diff --git a/Documentation/ABI/testing/sysfs-driver-hid b/Documentation/ABI/testing/sysfs-driver-hid index b6490e14fe83e8..48942cacb0bf49 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid +++ b/Documentation/ABI/testing/sysfs-driver-hid @@ -8,3 +8,13 @@ Description: When read, this file returns the device's raw binary HID report descriptor. This file cannot be written. Users: HIDAPI library (http://www.signal11.us/oss/hidapi) + +What: For USB devices : /sys/bus/usb/devices/-:./::./country + For BT devices : /sys/class/bluetooth/hci/::./country + Symlink : /sys/class/hidraw/hidraw/device/country +Date: February 2015 +KernelVersion: 3.19 +Contact: Olivier Gay +Description: When read, this file returns the hex integer value in ASCII + of the device's HID country code (e.g. 21 for US). + This file cannot be written. diff --git a/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff b/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff index 167d9032b970a7..b3f6a2ac500789 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff +++ b/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff @@ -5,3 +5,48 @@ Contact: Michal Malý Description: Display minimum, maximum and current range of the steering wheel. Writing a value within min and max boundaries sets the range of the wheel. + +What: /sys/bus/hid/drivers/logitech//alternate_modes +Date: Feb 2015 +KernelVersion: 4.1 +Contact: Michal Malý +Description: Displays a set of alternate modes supported by a wheel. Each + mode is listed as follows: + Tag: Mode Name + Currently active mode is marked with an asterisk. List also + contains an abstract item "native" which always denotes the + native mode of the wheel. Echoing the mode tag switches the + wheel into the corresponding mode. Depending on the exact model + of the wheel not all listed modes might always be selectable. + If a wheel cannot be switched into the desired mode, -EINVAL + is returned accompanied with an explanatory message in the + kernel log. + This entry is not created for devices that have only one mode. + + Currently supported mode switches: + Driving Force Pro: + DF-EX --> DFP + + G25: + DF-EX --> DFP --> G25 + + G27: + DF-EX <*> DFP <-> G25 <-> G27 + DF-EX <*--------> G25 <-> G27 + DF-EX <*----------------> G27 + + DFGT: + DF-EX <*> DFP <-> DFGT + DF-EX <*--------> DFGT + + * hid_logitech module must be loaded with lg4ff_no_autoswitch=1 + parameter set in order for the switch to DF-EX mode to work. + +What: /sys/bus/hid/drivers/logitech//real_id +Date: Feb 2015 +KernelVersion: 4.1 +Contact: Michal Malý +Description: Displays the real model of the wheel regardless of any + alternate mode the wheel might be switched to. + It is a read-only value. + This entry is not created for devices that have only one mode. diff --git a/Documentation/atomic_ops.txt b/Documentation/atomic_ops.txt index 183e41bdcb69b5..dab6da3382d9f0 100644 --- a/Documentation/atomic_ops.txt +++ b/Documentation/atomic_ops.txt @@ -201,11 +201,11 @@ These routines add 1 and subtract 1, respectively, from the given atomic_t and return the new counter value after the operation is performed. -Unlike the above routines, it is required that explicit memory -barriers are performed before and after the operation. It must be -done such that all memory operations before and after the atomic -operation calls are strongly ordered with respect to the atomic -operation itself. +Unlike the above routines, it is required that these primitives +include explicit memory barriers that are performed before and after +the operation. It must be done such that all memory operations before +and after the atomic operation calls are strongly ordered with respect +to the atomic operation itself. For example, it should behave as if a smp_mb() call existed both before and after the atomic operation. @@ -233,21 +233,21 @@ These two routines increment and decrement by 1, respectively, the given atomic counter. They return a boolean indicating whether the resulting counter value was zero or not. -It requires explicit memory barrier semantics around the operation as -above. +Again, these primitives provide explicit memory barrier semantics around +the atomic operation. int atomic_sub_and_test(int i, atomic_t *v); This is identical to atomic_dec_and_test() except that an explicit -decrement is given instead of the implicit "1". It requires explicit -memory barrier semantics around the operation. +decrement is given instead of the implicit "1". This primitive must +provide explicit memory barrier semantics around the operation. int atomic_add_negative(int i, atomic_t *v); -The given increment is added to the given atomic counter value. A -boolean is return which indicates whether the resulting counter value -is negative. It requires explicit memory barrier semantics around the -operation. +The given increment is added to the given atomic counter value. A boolean +is return which indicates whether the resulting counter value is negative. +This primitive must provide explicit memory barrier semantics around +the operation. Then: @@ -257,7 +257,7 @@ This performs an atomic exchange operation on the atomic variable v, setting the given new value. It returns the old value that the atomic variable v had just before the operation. -atomic_xchg requires explicit memory barriers around the operation. +atomic_xchg must provide explicit memory barriers around the operation. int atomic_cmpxchg(atomic_t *v, int old, int new); @@ -266,7 +266,7 @@ with the given old and new values. Like all atomic_xxx operations, atomic_cmpxchg will only satisfy its atomicity semantics as long as all other accesses of *v are performed through atomic_xxx operations. -atomic_cmpxchg requires explicit memory barriers around the operation. +atomic_cmpxchg must provide explicit memory barriers around the operation. The semantics for atomic_cmpxchg are the same as those defined for 'cas' below. @@ -279,8 +279,8 @@ If the atomic value v is not equal to u, this function adds a to v, and returns non zero. If v is equal to u then it returns zero. This is done as an atomic operation. -atomic_add_unless requires explicit memory barriers around the operation -unless it fails (returns 0). +atomic_add_unless must provide explicit memory barriers around the +operation unless it fails (returns 0). atomic_inc_not_zero, equivalent to atomic_add_unless(v, 1, 0) @@ -460,9 +460,9 @@ the return value into an int. There are other places where things like this occur as well. These routines, like the atomic_t counter operations returning values, -require explicit memory barrier semantics around their execution. All -memory operations before the atomic bit operation call must be made -visible globally before the atomic bit operation is made visible. +must provide explicit memory barrier semantics around their execution. +All memory operations before the atomic bit operation call must be +made visible globally before the atomic bit operation is made visible. Likewise, the atomic bit operation must be visible globally before any subsequent memory operation is made visible. For example: @@ -536,8 +536,9 @@ except that two underscores are prefixed to the interface name. These non-atomic variants also do not require any special memory barrier semantics. -The routines xchg() and cmpxchg() need the same exact memory barriers -as the atomic and bit operations returning values. +The routines xchg() and cmpxchg() must provide the same exact +memory-barrier semantics as the atomic and bit operations returning +values. Spinlocks and rwlocks have memory barrier expectations as well. The rule to follow is simple: diff --git a/Documentation/devicetree/bindings/i2c/i2c-davinci.txt b/Documentation/devicetree/bindings/i2c/i2c-davinci.txt index 2dc935b4113d22..a4e1cbc810c19e 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-davinci.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-davinci.txt @@ -10,6 +10,9 @@ Required properties: Recommended properties : - interrupts : standard interrupt property. - clock-frequency : desired I2C bus clock frequency in Hz. +- ti,has-pfunc: boolean; if defined, it indicates that SoC supports PFUNC + registers. PFUNC registers allow to switch I2C pins to function as + GPIOs, so they can by toggled manually. Example (enbw_cmc board): i2c@1c22000 { diff --git a/Documentation/devicetree/bindings/i2c/i2c-digicolor.txt b/Documentation/devicetree/bindings/i2c/i2c-digicolor.txt new file mode 100644 index 00000000000000..457a098d4f7e63 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-digicolor.txt @@ -0,0 +1,25 @@ +Conexant Digicolor I2C controller + +Required properties: + - compatible: must be "cnxt,cx92755-i2c" + - reg: physical address and length of the device registers + - interrupts: a single interrupt specifier + - clocks: clock for the device + - #address-cells: should be <1> + - #size-cells: should be <0> + +Optional properties: +- clock-frequency: the desired I2C bus clock frequency in Hz; in + absence of this property the default value is used (100 kHz). + +Example: + + i2c: i2c@f0000120 { + compatible = "cnxt,cx92755-i2c"; + reg = <0xf0000120 0x10>; + interrupts = <28>; + clocks = <&main_clk>; + clock-frequency = <100000>; + #address-cells = <1>; + #size-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-jz4780.txt b/Documentation/devicetree/bindings/i2c/i2c-jz4780.txt new file mode 100644 index 00000000000000..231e4cc4008cbc --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-jz4780.txt @@ -0,0 +1,35 @@ +* Ingenic JZ4780 I2C Bus controller + +Required properties: +- compatible: should be "ingenic,jz4780-i2c" +- reg: Should contain the address & size of the I2C controller registers. +- interrupts: Should specify the interrupt provided by parent. +- clocks: Should contain a single clock specifier for the JZ4780 I2C clock. +- clock-frequency: desired I2C bus clock frequency in Hz. + +Recommended properties: +- pinctrl-names: should be "default"; +- pinctrl-0: phandle to pinctrl function + +Optional properties: +- interrupt-parent: Should be the phandle of the interrupt controller that + delivers interrupts to the I2C block. + +Example + +/ { + i2c4: i2c4@0x10054000 { + compatible = "ingenic,jz4780-i2c"; + reg = <0x10054000 0x1000>; + + interrupt-parent = <&intc>; + interrupts = <56>; + + clocks = <&cgu JZ4780_CLK_SMB4>; + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pins_i2c4_data>; + + }; +}; + diff --git a/Documentation/devicetree/bindings/i2c/i2c-xlp9xx.txt b/Documentation/devicetree/bindings/i2c/i2c-xlp9xx.txt new file mode 100644 index 00000000000000..f818ef507ab7cd --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-xlp9xx.txt @@ -0,0 +1,22 @@ +Device tree configuration for the I2C controller on the XLP9xx/5xx SoC + +Required properties: +- compatible : should be "netlogic,xlp980-i2c" +- reg : bus address start and address range size of device +- interrupts : interrupt number + +Optional properties: +- clock-frequency : frequency of bus clock in Hz + Defaults to 100 KHz when the property is not specified + +Example: + +i2c0: i2c@113100 { + compatible = "netlogic,xlp980-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0 0x113100 0x100>; + clock-frequency = <400000>; + interrupts = <30>; + interrupt-parent = <&pic>; +}; diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index aaa8325004d23a..003bd77b4595f5 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt @@ -89,6 +89,7 @@ ricoh,rv5c386 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC samsung,24ad0xd1 S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power) sii,s35390a 2-wire CMOS real-time clock +skyworks,sky81452 Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply st-micro,24c256 i2c serial eeprom (24cxx) stm,m41t00 Serial Access TIMEKEEPER stm,m41t62 Serial real-time clock (RTC) with alarm diff --git a/Documentation/devicetree/bindings/input/brcm,bcm-keypad.txt b/Documentation/devicetree/bindings/input/brcm,bcm-keypad.txt new file mode 100644 index 00000000000000..b77f50bd640366 --- /dev/null +++ b/Documentation/devicetree/bindings/input/brcm,bcm-keypad.txt @@ -0,0 +1,108 @@ +* Broadcom Keypad Controller device tree bindings + +Broadcom Keypad controller is used to interface a SoC with a matrix-type +keypad device. The keypad controller supports multiple row and column lines. +A key can be placed at each intersection of a unique row and a unique column. +The keypad controller can sense a key-press and key-release and report the +event using a interrupt to the cpu. + +This binding is based on the matrix-keymap binding with the following +changes: + +keypad,num-rows and keypad,num-columns are required. + +Required SoC Specific Properties: +- compatible: should be "brcm,bcm-keypad" + +- reg: physical base address of the controller and length of memory mapped + region. + +- interrupts: The interrupt number to the cpu. + +Board Specific Properties: +- keypad,num-rows: Number of row lines connected to the keypad + controller. + +- keypad,num-columns: Number of column lines connected to the + keypad controller. + +- col-debounce-filter-period: The debounce period for the Column filter. + + KEYPAD_DEBOUNCE_1_ms = 0 + KEYPAD_DEBOUNCE_2_ms = 1 + KEYPAD_DEBOUNCE_4_ms = 2 + KEYPAD_DEBOUNCE_8_ms = 3 + KEYPAD_DEBOUNCE_16_ms = 4 + KEYPAD_DEBOUNCE_32_ms = 5 + KEYPAD_DEBOUNCE_64_ms = 6 + KEYPAD_DEBOUNCE_128_ms = 7 + +- status-debounce-filter-period: The debounce period for the Status filter. + + KEYPAD_DEBOUNCE_1_ms = 0 + KEYPAD_DEBOUNCE_2_ms = 1 + KEYPAD_DEBOUNCE_4_ms = 2 + KEYPAD_DEBOUNCE_8_ms = 3 + KEYPAD_DEBOUNCE_16_ms = 4 + KEYPAD_DEBOUNCE_32_ms = 5 + KEYPAD_DEBOUNCE_64_ms = 6 + KEYPAD_DEBOUNCE_128_ms = 7 + +- row-output-enabled: An optional property indicating whether the row or + column is being used as output. If specified the row is being used + as the output. Else defaults to column. + +- pull-up-enabled: An optional property indicating the Keypad scan mode. + If specified implies the keypad scan pull-up has been enabled. + +- autorepeat: Boolean, Enable auto repeat feature of Linux input + subsystem (optional). + +- linux,keymap: The keymap for keys as described in the binding document + devicetree/bindings/input/matrix-keymap.txt. + +Example: +#include "dt-bindings/input/input.h" + +/ { + keypad: keypad@180ac000 { + /* Required SoC specific properties */ + compatible = "brcm,bcm-keypad"; + + /* Required Board specific properties */ + keypad,num-rows = <5>; + keypad,num-columns = <5>; + status = "okay"; + + linux,keymap = ; + + /* Optional board specific properties */ + col-debounce-filter-period = <5>; + row-output-enabled; + pull-up-enabled; + + }; +}; diff --git a/Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt b/Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt new file mode 100644 index 00000000000000..07bf55f6e0b9a8 --- /dev/null +++ b/Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt @@ -0,0 +1,43 @@ +Qualcomm PM8941 PMIC Power Key + +PROPERTIES + +- compatible: + Usage: required + Value type: + Definition: must be one of: + "qcom,pm8941-pwrkey" + +- reg: + Usage: required + Value type: + Definition: base address of registers for block + +- interrupts: + Usage: required + Value type: + Definition: key change interrupt; The format of the specifier is + defined by the binding document describing the node's + interrupt parent. + +- debounce: + Usage: optional + Value type: + Definition: time in microseconds that key must be pressed or released + for state change interrupt to trigger. + +- bias-pull-up: + Usage: optional + Value type: + Definition: presence of this property indicates that the KPDPWR_N pin + should be configured for pull up. + +EXAMPLE + + pwrkey@800 { + compatible = "qcom,pm8941-pwrkey"; + reg = <0x800>; + interrupts = <0x0 0x8 0 IRQ_TYPE_EDGE_BOTH>; + debounce = <15625>; + bias-pull-up; + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/brcm,iproc-touchscreen.txt b/Documentation/devicetree/bindings/input/touchscreen/brcm,iproc-touchscreen.txt new file mode 100644 index 00000000000000..34e3382a065903 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/brcm,iproc-touchscreen.txt @@ -0,0 +1,76 @@ +* Broadcom's IPROC Touchscreen Controller + +Required properties: +- compatible: must be "brcm,iproc-touchscreen" +- reg: physical base address of the controller and length of memory mapped + region. +- clocks: The clock provided by the SOC to driver the tsc +- clock-name: name for the clock +- interrupts: The touchscreen controller's interrupt + +Optional properties: +- scanning_period: Time between scans. Each step is 1024 us. Valid 1-256. +- debounce_timeout: Each step is 512 us. Valid 0-255 +- settling_timeout: The settling duration (in ms) is the amount of time + the tsc waits to allow the voltage to settle after + turning on the drivers in detection mode. + Valid values: 0-11 + 0 = 0.008 ms + 1 = 0.01 ms + 2 = 0.02 ms + 3 = 0.04 ms + 4 = 0.08 ms + 5 = 0.16 ms + 6 = 0.32 ms + 7 = 0.64 ms + 8 = 1.28 ms + 9 = 2.56 ms + 10 = 5.12 ms + 11 = 10.24 ms +- touch_timeout: The continuous number of scan periods in which touch is + not detected before the controller returns to idle state. + Valid values 0-255. +- average_data: Number of data samples which are averaged before a final + data point is placed into the FIFO + Valid values 0-7 + 0 = 1 sample + 1 = 2 samples + 2 = 4 samples + 3 = 8 samples + 4 = 16 samples + 5 = 32 samples + 6 = 64 samples + 7 = 128 samples +- fifo_threshold: Interrupt is generated whenever the number of fifo + entries exceeds this value + Valid values 0-31 +- touchscreen-size-x: horizontal resolution of touchscreen (in pixels) +- touchscreen-size-y: vertical resolution of touchscreen (in pixels) +- touchscreen-fuzz-x: horizontal noise value of the absolute input + device (in pixels) +- touchscreen-fuzz-y: vertical noise value of the absolute input + device (in pixels) +- touchscreen-inverted-x: X axis is inverted (boolean) +- touchscreen-inverted-y: Y axis is inverted (boolean) + +Example: + + touchscreen: tsc@0x180A6000 { + compatible = "brcm,iproc-touchscreen"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x180A6000 0x40>; + clocks = <&adc_clk>; + clock-names = "tsc_clk"; + interrupts = ; + + scanning_period = <5>; + debounce_timeout = <40>; + settling_timeout = <7>; + touch_timeout = <10>; + average_data = <5>; + fifo_threshold = <1>; + /* Touchscreen is rotated 180 degrees. */ + touchscreen-inverted-x; + touchscreen-inverted-y; + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt b/Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt new file mode 100644 index 00000000000000..d11f8d615b5de8 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt @@ -0,0 +1,46 @@ +* ChipOne icn8318 I2C touchscreen controller + +Required properties: + - compatible : "chipone,icn8318" + - reg : I2C slave address of the chip (0x40) + - interrupt-parent : a phandle pointing to the interrupt controller + serving the interrupt for this chip + - interrupts : interrupt specification for the icn8318 interrupt + - wake-gpios : GPIO specification for the WAKE input + - touchscreen-size-x : horizontal resolution of touchscreen (in pixels) + - touchscreen-size-y : vertical resolution of touchscreen (in pixels) + +Optional properties: + - pinctrl-names : should be "default" + - pinctrl-0: : a phandle pointing to the pin settings for the + control gpios + - touchscreen-fuzz-x : horizontal noise value of the absolute input + device (in pixels) + - touchscreen-fuzz-y : vertical noise value of the absolute input + device (in pixels) + - touchscreen-inverted-x : X axis is inverted (boolean) + - touchscreen-inverted-y : Y axis is inverted (boolean) + - touchscreen-swapped-x-y : X and Y axis are swapped (boolean) + Swapping is done after inverting the axis + +Example: + +i2c@00000000 { + /* ... */ + + chipone_icn8318@40 { + compatible = "chipone,icn8318"; + reg = <0x40>; + interrupt-parent = <&pio>; + interrupts = <9 IRQ_TYPE_EDGE_FALLING>; /* EINT9 (PG9) */ + pinctrl-names = "default"; + pinctrl-0 = <&ts_wake_pin_p66>; + wake-gpios = <&pio 1 3 GPIO_ACTIVE_HIGH>; /* PB3 */ + touchscreen-size-x = <800>; + touchscreen-size-y = <480>; + touchscreen-inverted-x; + touchscreen-swapped-x-y; + }; + + /* ... */ +}; diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix.txt b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt new file mode 100644 index 00000000000000..8ba98eec765bc5 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/goodix.txt @@ -0,0 +1,29 @@ +Device tree bindings for Goodix GT9xx series touchscreen controller + +Required properties: + + - compatible : Should be "goodix,gt911" + or "goodix,gt9110" + or "goodix,gt912" + or "goodix,gt927" + or "goodix,gt9271" + or "goodix,gt928" + or "goodix,gt967" + - reg : I2C address of the chip. Should be 0x5d or 0x14 + - interrupt-parent : Interrupt controller to which the chip is connected + - interrupts : Interrupt to which the chip is connected + +Example: + + i2c@00000000 { + /* ... */ + + gt928@5d { + compatible = "goodix,gt928"; + reg = <0x5d>; + interrupt-parent = <&gpio>; + interrupts = <0 0>; + }; + + /* ... */ + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt index 433332d3b2ba77..89abecd938cb98 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt @@ -2,14 +2,27 @@ sun4i resistive touchscreen controller -------------------------------------- Required properties: - - compatible: "allwinner,sun4i-a10-ts" or "allwinner,sun6i-a31-ts" + - compatible: "allwinner,sun4i-a10-ts", "allwinner,sun5i-a13-ts" or + "allwinner,sun6i-a31-ts" - reg: mmio address range of the chip - interrupts: interrupt to which the chip is connected - #thermal-sensor-cells: shall be 0 Optional properties: - - allwinner,ts-attached: boolean indicating that an actual touchscreen is - attached to the controller + - allwinner,ts-attached : boolean indicating that an actual touchscreen + is attached to the controller + - allwinner,tp-sensitive-adjust : integer (4 bits) + adjust sensitivity of pen down detection + between 0 (least sensitive) and 15 + (defaults to 15) + - allwinner,filter-type : integer (2 bits) + select median and averaging filter + samples used for median / averaging filter + 0: 4/2 + 1: 5/3 + 2: 8/4 + 3: 16/8 + (defaults to 1) Example: @@ -19,4 +32,7 @@ Example: interrupts = <29>; allwinner,ts-attached; #thermal-sensor-cells = <0>; + /* sensitive/noisy touch panel */ + allwinner,tp-sensitive-adjust = <0>; + allwinner,filter-type = <3>; }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt new file mode 100644 index 00000000000000..5aaa6b3aa90c26 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/sx8654.txt @@ -0,0 +1,16 @@ +* Semtech SX8654 I2C Touchscreen Controller + +Required properties: +- compatible: must be "semtech,sx8654" +- reg: i2c slave address +- interrupt-parent: the phandle for the interrupt controller +- interrupts: touch controller interrupt + +Example: + + sx8654@48 { + compatible = "semtech,sx8654"; + reg = <0x48>; + interrupt-parent = <&gpio6>; + interrupts = <3 IRQ_TYPE_EDGE_FALLING>; + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt index d8e06163c54edd..ac23caf518ad07 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.txt @@ -16,6 +16,8 @@ Optional properties for Touchscreens: controller) - touchscreen-inverted-x : X axis is inverted (boolean) - touchscreen-inverted-y : Y axis is inverted (boolean) + - touchscreen-swapped-x-y : X and Y axis are swapped (boolean) + Swapping is done after inverting the axis Deprecated properties for Touchscreens: - x-size : deprecated name for touchscreen-size-x diff --git a/Documentation/devicetree/bindings/mfd/arizona.txt b/Documentation/devicetree/bindings/mfd/arizona.txt index 7bd1273f571a40..7665aa95979f0b 100644 --- a/Documentation/devicetree/bindings/mfd/arizona.txt +++ b/Documentation/devicetree/bindings/mfd/arizona.txt @@ -8,6 +8,7 @@ Required properties: - compatible : One of the following chip-specific strings: "wlf,wm5102" "wlf,wm5110" + "wlf,wm8280" "wlf,wm8997" - reg : I2C slave address when connected using I2C, chip select number when using SPI. @@ -26,21 +27,27 @@ Required properties: - #gpio-cells : Must be 2. The first cell is the pin number and the second cell is used to specify optional parameters (currently unused). - - AVDD-supply, DBVDD1-supply, DBVDD2-supply, DBVDD3-supply (wm5102, wm5110), - CPVDD-supply, SPKVDDL-supply (wm5102, wm5110), SPKVDDR-supply (wm5102, - wm5110), SPKVDD-supply (wm8997) : Power supplies for the device, as covered - in Documentation/devicetree/bindings/regulator/regulator.txt + - AVDD-supply, DBVDD1-supply, CPVDD-supply : Power supplies for the device, + as covered in Documentation/devicetree/bindings/regulator/regulator.txt + + - DBVDD2-supply, DBVDD3-supply : Additional databus power supplies (wm5102, + wm5110, wm8280) + + - SPKVDDL-supply, SPKVDDR-supply : Speaker driver power supplies (wm5102, + wm5110, wm8280) + + - SPKVDD-supply : Speaker driver power supply (wm8997) Optional properties: - wlf,reset : GPIO specifier for the GPIO controlling /RESET - wlf,ldoena : GPIO specifier for the GPIO controlling LDOENA - - wlf,gpio-defaults : A list of GPIO configuration register values. If - absent, no configuration of these registers is performed. If any - entry has a value that is out of range for a 16 bit register then - the chip default will be used. If present exactly five values must - be specified. + - wlf,gpio-defaults : A list of GPIO configuration register values. Defines + for the appropriate values can found in . If + absent, no configuration of these registers is performed. If any entry has + a value that is out of range for a 16 bit register then the chip default + will be used. If present exactly five values must be specified. - wlf,inmode : A list of INn_MODE register values, where n is the number of input signals. Valid values are 0 (Differential), 1 (Single-ended) and @@ -49,6 +56,12 @@ Optional properties: input singals. If values less than the number of input signals, elements that has not been specifed are set to 0 by default. + - wlf,dmic-ref : DMIC reference voltage source for each input, can be + selected from either MICVDD or one of the MICBIAS's, defines + (ARIZONA_DMIC_xxxx) are provided in . If + present, the number of values should be less than or equal to the + number of inputs, unspecified inputs will use the chip default. + - DCVDD-supply, MICVDD-supply : Power supplies, only need to be specified if they are being externally supplied. As covered in Documentation/devicetree/bindings/regulator/regulator.txt @@ -73,10 +86,10 @@ codec: wm5102@1a { #gpio-cells = <2>; wlf,gpio-defaults = < - 0x00000000 /* AIF1TXLRCLK */ - 0xffffffff - 0xffffffff - 0xffffffff - 0xffffffff + ARIZONA_GP_FN_TXLRCLK + ARIZONA_GP_DEFAULT + ARIZONA_GP_DEFAULT + ARIZONA_GP_DEFAULT + ARIZONA_GP_DEFAULT >; }; diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt new file mode 100644 index 00000000000000..98685f291a720f --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt @@ -0,0 +1,96 @@ +AXP202/AXP209 device tree bindings + +The axp20x family current members : +axp202 (X-Powers) +axp209 (X-Powers) + +Required properties: +- compatible: "x-powers,axp202" or "x-powers,axp209" +- reg: The I2C slave address for the AXP chip +- interrupt-parent: The parent interrupt controller +- interrupts: SoC NMI / GPIO interrupt connected to the PMIC's IRQ pin +- interrupt-controller: axp20x has its own internal IRQs +- #interrupt-cells: Should be set to 1 + +Optional properties: +- x-powers,dcdc-freq: defines the work frequency of DC-DC in KHz + (range: 750-1875). Default: 1.5MHz +- -supply: a phandle to the regulator supply node. May be omitted if + inputs are unregulated, such as using the IPSOUT output + from the PMIC. + +- regulators: A node that houses a sub-node for each regulator. Regulators + not used but preferred to be managed by the OS should be + listed as well. + See Documentation/devicetree/bindings/regulator/regulator.txt + for more information on standard regulator bindings. + +Optional properties for DCDC regulators: +- x-powers,dcdc-workmode: 1 for PWM mode, 0 for AUTO (PWM/PFM) mode + Default: Current hardware setting + The DCDC regulators work in a mixed PWM/PFM mode, + using PFM under light loads and switching to PWM + for heavier loads. Forcing PWM mode trades efficiency + under light loads for lower output noise. This + probably makes sense for HiFi audio related + applications that aren't battery constrained. + + +AXP202/AXP209 regulators, type, and corresponding input supply names: + +Regulator Type Supply Name Notes +--------- ---- ----------- ----- +DCDC2 : DC-DC buck : vin2-supply +DCDC3 : DC-DC buck : vin3-supply +LDO1 : LDO : acin-supply : always on +LDO2 : LDO : ldo24in-supply : shared supply +LDO3 : LDO : ldo3in-supply +LDO4 : LDO : ldo24in-supply : shared supply +LDO5 : LDO : ldo5in-supply + +Example: + +axp209: pmic@34 { + compatible = "x-powers,axp209"; + reg = <0x34>; + interrupt-parent = <&nmi_intc>; + interrupts = <0 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + #interrupt-cells = <1>; + + regulators { + x-powers,dcdc-freq = <1500>; + + vdd_cpu: dcdc2 { + regulator-always-on; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1450000>; + regulator-name = "vdd-cpu"; + }; + + vdd_int_dll: dcdc3 { + regulator-always-on; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1400000>; + regulator-name = "vdd-int-dll"; + }; + + vdd_rtc: ldo1 { + regulator-always-on; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1400000>; + regulator-name = "vdd-rtc"; + }; + + avcc: ldo2 { + regulator-always-on; + regulator-min-microvolt = <2700000>; + regulator-max-microvolt = <3300000>; + regulator-name = "avcc"; + }; + + ldo3 { + /* unused but preferred to be managed by OS */ + }; + }; +}; diff --git a/Documentation/devicetree/bindings/mfd/da9150.txt b/Documentation/devicetree/bindings/mfd/da9150.txt new file mode 100644 index 00000000000000..d0588eaa0d714d --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/da9150.txt @@ -0,0 +1,43 @@ +Dialog Semiconductor DA9150 Combined Charger/Fuel-Gauge MFD bindings + +DA9150 consists of a group of sub-devices: + +Device Description +------ ----------- +da9150-gpadc : General Purpose ADC +da9150-charger : Battery Charger + +====== + +Required properties: +- compatible : Should be "dlg,da9150" +- reg: Specifies the I2C slave address +- interrupt-parent: Specifies the phandle of the interrupt controller to which + the IRQs from da9150 are delivered to. +- interrupts: IRQ line info for da9150 chip. +- interrupt-controller: da9150 has internal IRQs (own IRQ domain). + (See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for + further information relating to interrupt properties) + +Sub-devices: +- da9150-gpadc: See Documentation/devicetree/bindings/iio/adc/da9150-gpadc.txt +- da9150-charger: See Documentation/devicetree/bindings/power/da9150-charger.txt + + +Example: + + charger_fg: da9150@58 { + compatible = "dlg,da9150"; + reg = <0x58>; + interrupt-parent = <&gpio6>; + interrupts = <11 IRQ_TYPE_LEVEL_LOW>; + interrupt-controller; + + gpadc: da9150-gpadc { + ... + }; + + da9150-charger { + ... + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/mt6397.txt b/Documentation/devicetree/bindings/mfd/mt6397.txt new file mode 100644 index 00000000000000..15043e6526990c --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/mt6397.txt @@ -0,0 +1,70 @@ +MediaTek MT6397 Multifunction Device Driver + +MT6397 is a multifunction device with the following sub modules: +- Regulator +- RTC +- Audio codec +- GPIO +- Clock + +It is interfaced to host controller using SPI interface by a proprietary hardware +called PMIC wrapper or pwrap. MT6397 MFD is a child device of pwrap. +See the following for pwarp node definitions: +Documentation/devicetree/bindings/soc/pwrap.txt + +This document describes the binding for MFD device and its sub module. + +Required properties: +compatible: "mediatek,mt6397" + +Optional subnodes: + +- rtc + Required properties: + - compatible: "mediatek,mt6397-rtc" +- regulators + Required properties: + - compatible: "mediatek,mt6397-regulator" + see Documentation/devicetree/bindings/regulator/mt6397-regulator.txt +- codec + Required properties: + - compatible: "mediatek,mt6397-codec" +- clk + Required properties: + - compatible: "mediatek,mt6397-clk" + +Example: + pwrap: pwrap@1000f000 { + compatible = "mediatek,mt8135-pwrap"; + + ... + + pmic { + compatible = "mediatek,mt6397"; + + codec: mt6397codec { + compatible = "mediatek,mt6397-codec"; + }; + + regulators { + compatible = "mediatek,mt6397-regulator"; + + mt6397_vpca15_reg: buck_vpca15 { + regulator-compatible = "buck_vpca15"; + regulator-name = "vpca15"; + regulator-min-microvolt = <850000>; + regulator-max-microvolt = <1400000>; + regulator-ramp-delay = <12500>; + regulator-always-on; + }; + + mt6397_vgp4_reg: ldo_vgp4 { + regulator-compatible = "ldo_vgp4"; + regulator-name = "vgp4"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3300000>; + regulator-enable-ramp-delay = <218>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.txt b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.txt index 7182b8857f57f3..6ac06c1b9aec89 100644 --- a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.txt +++ b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.txt @@ -15,10 +15,21 @@ each. A function can consume one or more of these fixed-size register regions. Required properties: - compatible: Should contain one of: - "qcom,pm8941" - "qcom,pm8841" - "qcom,pma8084" - or generalized "qcom,spmi-pmic". + "qcom,pm8941", + "qcom,pm8841", + "qcom,pma8084", + "qcom,pm8019", + "qcom,pm8226", + "qcom,pm8110", + "qcom,pma8084", + "qcom,pmi8962", + "qcom,pmd9635", + "qcom,pm8994", + "qcom,pmi8994", + "qcom,pm8916", + "qcom,pm8004", + "qcom,pm8909", + or generalized "qcom,spmi-pmic". - reg: Specifies the SPMI USID slave address for this device. For more information see: Documentation/devicetree/bindings/spmi/spmi.txt diff --git a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt index 85e31980017aa9..5e97a9593ad71b 100644 --- a/Documentation/devicetree/bindings/mfd/qcom-rpm.txt +++ b/Documentation/devicetree/bindings/mfd/qcom-rpm.txt @@ -12,6 +12,7 @@ frequencies. "qcom,rpm-apq8064" "qcom,rpm-msm8660" "qcom,rpm-msm8960" + "qcom,rpm-ipq8064" - reg: Usage: required @@ -31,16 +32,6 @@ frequencies. Value type: Definition: must be the three strings "ack", "err" and "wakeup", in order -- #address-cells: - Usage: required - Value type: - Definition: must be 1 - -- #size-cells: - Usage: required - Value type: - Definition: must be 0 - - qcom,ipc: Usage: required Value type: @@ -52,6 +43,188 @@ frequencies. - u32 representing the ipc bit within the register += SUBNODES + +The RPM exposes resources to its subnodes. The below bindings specify the set +of valid subnodes that can operate on these resources. + +== Regulators + +Regulator nodes are identified by their compatible: + +- compatible: + Usage: required + Value type: + Definition: must be one of: + "qcom,rpm-pm8058-regulators" + "qcom,rpm-pm8901-regulators" + "qcom,rpm-pm8921-regulators" + +- vdd_l0_l1_lvs-supply: +- vdd_l2_l11_l12-supply: +- vdd_l3_l4_l5-supply: +- vdd_l6_l7-supply: +- vdd_l8-supply: +- vdd_l9-supply: +- vdd_l10-supply: +- vdd_l13_l16-supply: +- vdd_l14_l15-supply: +- vdd_l17_l18-supply: +- vdd_l19_l20-supply: +- vdd_l21-supply: +- vdd_l22-supply: +- vdd_l23_l24_l25-supply: +- vdd_ncp-supply: +- vdd_s0-supply: +- vdd_s1-supply: +- vdd_s2-supply: +- vdd_s3-supply: +- vdd_s4-supply: + Usage: optional (pm8058 only) + Value type: + Definition: reference to regulator supplying the input pin, as + described in the data sheet + +- lvs0_in-supply: +- lvs1_in-supply: +- lvs2_in-supply: +- lvs3_in-supply: +- mvs_in-supply: +- vdd_l0-supply: +- vdd_l1-supply: +- vdd_l2-supply: +- vdd_l3-supply: +- vdd_l4-supply: +- vdd_l5-supply: +- vdd_l6-supply: +- vdd_s0-supply: +- vdd_s1-supply: +- vdd_s2-supply: +- vdd_s3-supply: +- vdd_s4-supply: + Usage: optional (pm8901 only) + Value type: + Definition: reference to regulator supplying the input pin, as + described in the data sheet + +- vdd_l1_l2_l12_l18-supply: +- vdd_l3_l15_l17-supply: +- vdd_l4_l14-supply: +- vdd_l5_l8_l16-supply: +- vdd_l6_l7-supply: +- vdd_l9_l11-supply: +- vdd_l10_l22-supply: +- vdd_l21_l23_l29-supply: +- vdd_l24-supply: +- vdd_l25-supply: +- vdd_l26-supply: +- vdd_l27-supply: +- vdd_l28-supply: +- vdd_ncp-supply: +- vdd_s1-supply: +- vdd_s2-supply: +- vdd_s4-supply: +- vdd_s5-supply: +- vdd_s6-supply: +- vdd_s7-supply: +- vdd_s8-supply: +- vin_5vs-supply: +- vin_lvs1_3_6-supply: +- vin_lvs2-supply: +- vin_lvs4_5_7-supply: + Usage: optional (pm8921 only) + Value type: + Definition: reference to regulator supplying the input pin, as + described in the data sheet + +The regulator node houses sub-nodes for each regulator within the device. Each +sub-node is identified using the node's name, with valid values listed for each +of the pmics below. + +pm8058: + l0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, + l16, l17, l18, l19, l20, l21, l22, l23, l24, l25, s0, s1, s2, s3, s4, + lvs0, lvs1, ncp + +pm8901: + l0, l1, l2, l3, l4, l5, l6, s0, s1, s2, s3, s4, lvs0, lvs1, lvs2, lvs3, + mvs + +pm8921: + s1, s2, s3, s4, s7, s8, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, + l12, l14, l15, l16, l17, l18, l21, l22, l23, l24, l25, l26, l27, l28, + l29, lvs1, lvs2, lvs3, lvs4, lvs5, lvs6, lvs7, usb-switch, hdmi-switch, + ncp + +The content of each sub-node is defined by the standard binding for regulators - +see regulator.txt - with additional custom properties described below: + +=== Switch-mode Power Supply regulator custom properties + +- bias-pull-down: + Usage: optional + Value type: + Definition: enable pull down of the regulator when inactive + +- qcom,switch-mode-frequency: + Usage: required + Value type: + Definition: Frequency (Hz) of the switch-mode power supply; + must be one of: + 19200000, 9600000, 6400000, 4800000, 3840000, 3200000, + 2740000, 2400000, 2130000, 1920000, 1750000, 1600000, + 1480000, 1370000, 1280000, 1200000 + +- qcom,force-mode: + Usage: optional (default if no other qcom,force-mode is specified) + Value type: + Defintion: indicates that the regulator should be forced to a + particular mode, valid values are: + QCOM_RPM_FORCE_MODE_NONE - do not force any mode + QCOM_RPM_FORCE_MODE_LPM - force into low power mode + QCOM_RPM_FORCE_MODE_HPM - force into high power mode + QCOM_RPM_FORCE_MODE_AUTO - allow regulator to automatically + select its own mode based on + realtime current draw, only for: + pm8921 smps and ftsmps + +- qcom,power-mode-hysteretic: + Usage: optional + Value type: + Definition: select that the power supply should operate in hysteretic + mode, instead of the default pwm mode + +=== Low-dropout regulator custom properties + +- bias-pull-down: + Usage: optional + Value type: + Definition: enable pull down of the regulator when inactive + +- qcom,force-mode: + Usage: optional + Value type: + Defintion: indicates that the regulator should not be forced to any + particular mode, valid values are: + QCOM_RPM_FORCE_MODE_NONE - do not force any mode + QCOM_RPM_FORCE_MODE_LPM - force into low power mode + QCOM_RPM_FORCE_MODE_HPM - force into high power mode + QCOM_RPM_FORCE_MODE_BYPASS - set regulator to use bypass + mode, i.e. to act as a switch + and not regulate, only for: + pm8921 pldo, nldo and nldo1200 + +=== Negative Charge Pump custom properties + +- qcom,switch-mode-frequency: + Usage: required + Value type: + Definition: Frequency (Hz) of the swith mode power supply; + must be one of: + 19200000, 9600000, 6400000, 4800000, 3840000, 3200000, + 2740000, 2400000, 2130000, 1920000, 1750000, 1600000, + 1480000, 1370000, 1280000, 1200000 + = EXAMPLE #include @@ -64,7 +237,28 @@ frequencies. interrupts = <0 19 0>, <0 21 0>, <0 22 0>; interrupt-names = "ack", "err", "wakeup"; - #address-cells = <1>; - #size-cells = <0>; + regulators { + compatible = "qcom,rpm-pm8921-regulators"; + vdd_l1_l2_l12_l18-supply = <&pm8921_s4>; + + s1 { + regulator-min-microvolt = <1225000>; + regulator-max-microvolt = <1225000>; + + bias-pull-down; + + qcom,switch-mode-frequency = <3200000>; + }; + + pm8921_s4: s4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + qcom,switch-mode-frequency = <1600000>; + bias-pull-down; + + qcom,force-mode = ; + }; + }; }; diff --git a/Documentation/devicetree/bindings/mfd/sky81452.txt b/Documentation/devicetree/bindings/mfd/sky81452.txt new file mode 100644 index 00000000000000..35181794aa241b --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/sky81452.txt @@ -0,0 +1,35 @@ +SKY81452 bindings + +Required properties: +- compatible : Must be "skyworks,sky81452" +- reg : I2C slave address + +Required child nodes: +- backlight : container node for backlight following the binding + in video/backlight/sky81452-backlight.txt +- regulator : container node for regulators following the binding + in regulator/sky81452-regulator.txt + +Example: + + sky81452@2c { + compatible = "skyworks,sky81452"; + reg = <0x2c>; + + backlight { + compatible = "skyworks,sky81452-backlight"; + name = "pwm-backlight"; + led-sources = <0 1 2 3 6>; + skyworks,ignore-pwm; + skyworks,phase-shift; + skyworks,current-limit = <2300>; + }; + + regulator { + lout { + regulator-name = "sky81452-lout"; + regulator-min-microvolt = <4500000>; + regulator-max-microvolt = <8000000>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-gpio.txt b/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-gpio.txt new file mode 100644 index 00000000000000..6540ca56be5eda --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-gpio.txt @@ -0,0 +1,98 @@ +Broadcom Cygnus GPIO/PINCONF Controller + +Required properties: + +- compatible: + Must be "brcm,cygnus-ccm-gpio", "brcm,cygnus-asiu-gpio", or + "brcm,cygnus-crmu-gpio" + +- reg: + Define the base and range of the I/O address space that contains the Cygnus +GPIO/PINCONF controller registers + +- #gpio-cells: + Must be two. The first cell is the GPIO pin number (within the +controller's pin space) and the second cell is used for the following: + bit[0]: polarity (0 for active high and 1 for active low) + +- gpio-controller: + Specifies that the node is a GPIO controller + +Optional properties: + +- interrupts: + Interrupt ID + +- interrupt-controller: + Specifies that the node is an interrupt controller + +- pinmux: + Specifies the phandle to the IOMUX device, where pins can be individually +muxed to GPIO + +Supported generic PINCONF properties in child nodes: + +- pins: + The list of pins (within the controller's own pin space) that properties +in the node apply to. Pin names are "gpio-" + +- bias-disable: + Disable pin bias + +- bias-pull-up: + Enable internal pull up resistor + +- bias-pull-down: + Enable internal pull down resistor + +- drive-strength: + Valid drive strength values include 2, 4, 6, 8, 10, 12, 14, 16 (mA) + +Example: + gpio_ccm: gpio@1800a000 { + compatible = "brcm,cygnus-ccm-gpio"; + reg = <0x1800a000 0x50>, + <0x0301d164 0x20>; + #gpio-cells = <2>; + gpio-controller; + interrupts = ; + interrupt-controller; + + touch_pins: touch_pins { + pwr: pwr { + pins = "gpio-0"; + drive-strength = <16>; + }; + + event: event { + pins = "gpio-1"; + bias-pull-up; + }; + }; + }; + + gpio_asiu: gpio@180a5000 { + compatible = "brcm,cygnus-asiu-gpio"; + reg = <0x180a5000 0x668>; + #gpio-cells = <2>; + gpio-controller; + interrupts = ; + interrupt-controller; + }; + + /* + * Touchscreen that uses the CCM GPIO 0 and 1 + */ + tsc { + ... + ... + gpio-pwr = <&gpio_ccm 0 0>; + gpio-event = <&gpio_ccm 1 0>; + }; + + /* Bluetooth that uses the ASIU GPIO 5, with polarity inverted */ + bluetooth { + ... + ... + bcm,rfkill-bank-sel = <&gpio_asiu 5 1> + } diff --git a/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinmux.txt b/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinmux.txt new file mode 100644 index 00000000000000..3600d5c6c4d7d5 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/brcm,cygnus-pinmux.txt @@ -0,0 +1,132 @@ +Broadcom Cygnus IOMUX Controller + +The Cygnus IOMUX controller supports group based mux configuration. In +addition, certain pins can be muxed to GPIO function individually. + +Required properties: + +- compatible: + Must be "brcm,cygnus-pinmux" + +- reg: + Define the base and range of the I/O address space that contains the Cygnus +IOMUX registers + +Properties in subnodes: + +- function: + The mux function to select + +- groups: + The list of groups to select with a given function + +For more details, refer to +Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt + +For example: + + pinmux: pinmux@0x0301d0c8 { + compatible = "brcm,cygnus-pinmux"; + reg = <0x0301d0c8 0x1b0>; + + pinctrl-names = "default"; + pinctrl-0 = <&i2s0_default>; + + i2s0_default: i2s0_default { + mux { + function = "i2s0"; + groups = "i2s0_0_grp", "i2s0_1_grp"; + }; + }; + }; + +List of supported functions and groups in Cygnus: + +"i2s0": "i2s0_0_grp", "i2s0_1_grp" + +"i2s1": "i2s1_0_grp", "i2s1_1_grp" + +"i2s2": "i2s2_0_grp", "i2s2_1_grp", "i2s2_2_grp", "i2s2_3_grp", "i2s2_4_grp" + +"spdif": "spdif_grp" + +"pwm0": "pwm0_grp" + +"pwm1": "pwm1_grp" + +"pwm2": "pwm2_grp" + +"pwm3": "pwm3_grp" + +"pwm4": "pwm4_grp" + +"pwm5": "pwm5_grp" + +"key": "key0_grp", "key1_grp", "key2_grp", "key3_grp", "key4_grp", "key5_grp", +"key6_grp", "key7_grp", "key8_grp", "key9_grp", "key10_grp", "key11_grp", +"key12_grp", "key13_grp", "key14_grp", "key15_grp" + +"audio_dte": "audio_dte0_grp", "audio_dte1_grp", "audio_dte2_grp", "audio_dte3_grp" + +"smart_card0": "smart_card0_grp", "smart_card0_fcb_grp" + +"smart_card1": "smart_card1_grp", "smart_card1_fcb_grp" + +"spi0": "spi0_grp" + +"spi1": "spi1_grp" + +"spi2": "spi2_grp" + +"spi3": "spi3_grp" + +"spi4": "spi4_0_grp", "spi4_1_grp" + +"spi5": "spi5_grp" + +"sw_led0": "sw_led0_0_grp", "sw_led0_1_grp" + +"sw_led1": "sw_led1_grp" + +"sw_led2": "sw_led2_0_grp", "sw_led2_1_grp" + +"d1w": "d1w_grp" + +"lcd": "lcd_grp" + +"sram": "sram_0_grp", "sram_1_grp" + +"uart0": "uart0_grp" + +"uart1": "uart1_grp", "uart1_dte_grp" + +"uart2": "uart2_grp" + +"uart3": "uart3_grp" + +"uart4": "uart4_grp" + +"qspi": "qspi_0_grp", "qspi_1_grp" + +"nand": "nand_grp" + +"sdio0": "sdio0_grp", "sdio0_cd_grp", "sdio0_mmc_grp" + +"sdio1": "sdio1_data_0_grp", "sdio1_data_1_grp", "sdio1_cd_grp", +"sdio1_led_grp", "sdio1_mmc_grp" + +"can0": "can0_grp" + +"can1": "can1_grp" + +"cam": "cam_led_grp", "cam_0_grp", "cam_1_grp" + +"bsc1": "bsc1_grp" + +"pcie_clkreq": "pcie_clkreq_grp" + +"usb0_oc": "usb0_oc_grp" + +"usb1_oc": "usb1_oc_grp" + +"usb2_oc": "usb2_oc_grp" diff --git a/Documentation/devicetree/bindings/pinctrl/marvell,armada-39x-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/marvell,armada-39x-pinctrl.txt new file mode 100644 index 00000000000000..5b1a9dc004f472 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/marvell,armada-39x-pinctrl.txt @@ -0,0 +1,78 @@ +* Marvell Armada 39x SoC pinctrl driver for mpp + +Please refer to marvell,mvebu-pinctrl.txt in this directory for common binding +part and usage. + +Required properties: +- compatible: "marvell,88f6920-pinctrl", "marvell,88f6928-pinctrl" + depending on the specific variant of the SoC being used. +- reg: register specifier of MPP registers + +Available mpp pins/groups and functions: +Note: brackets (x) are not part of the mpp name for marvell,function and given +only for more detailed description in this document. + +name pins functions +================================================================================ +mpp0 0 gpio, ua0(rxd) +mpp1 1 gpio, ua0(txd) +mpp2 2 gpio, i2c0(sck) +mpp3 3 gpio, i2c0(sda) +mpp4 4 gpio, ua1(txd), ua0(rts), smi(mdc) +mpp5 5 gpio, ua1(rxd), ua0(cts), smi(mdio) +mpp6 6 gpio, dev(cs3), xsmi(mdio) +mpp7 7 gpio, dev(ad9), xsmi(mdc) +mpp8 8 gpio, dev(ad10), ptp(trig) +mpp9 9 gpio, dev(ad11), ptp(clk) +mpp10 10 gpio, dev(ad12), ptp(event) +mpp11 11 gpio, dev(ad13), led(clk) +mpp12 12 gpio, pcie0(rstout), dev(ad14), led(stb) +mpp13 13 gpio, dev(ad15), led(data) +mpp14 14 gpio, m(vtt), dev(wen1), ua1(txd) +mpp15 15 gpio, pcie0(rstout), spi0(mosi), i2c1(sck) +mpp16 16 gpio, m(decc), spi0(miso), i2c1(sda) +mpp17 17 gpio, ua1(rxd), spi0(sck), smi(mdio) +mpp18 18 gpio, ua1(txd), spi0(cs0), i2c2(sck) +mpp19 19 gpio, sata1(present) [1], ua0(cts), ua1(rxd), i2c2(sda) +mpp20 20 gpio, sata0(present) [1], ua0(rts), ua1(txd), smi(mdc) +mpp21 21 gpio, spi0(cs1), sata0(present) [1], sd(cmd), dev(bootcs), ge(rxd0) +mpp22 22 gpio, spi0(mosi), dev(ad0) +mpp23 23 gpio, spi0(sck), dev(ad2) +mpp24 24 gpio, spi0(miso), ua0(cts), ua1(rxd), sd(d4), dev(readyn) +mpp25 25 gpio, spi0(cs0), ua0(rts), ua1(txd), sd(d5), dev(cs0) +mpp26 26 gpio, spi0(cs2), i2c1(sck), sd(d6), dev(cs1) +mpp27 27 gpio, spi0(cs3), i2c1(sda), sd(d7), dev(cs2), ge(txclkout) +mpp28 28 gpio, sd(clk), dev(ad5), ge(txd0) +mpp29 29 gpio, dev(ale0), ge(txd1) +mpp30 30 gpio, dev(oen), ge(txd2) +mpp31 31 gpio, dev(ale1), ge(txd3) +mpp32 32 gpio, dev(wen0), ge(txctl) +mpp33 33 gpio, m(decc), dev(ad3) +mpp34 34 gpio, dev(ad1) +mpp35 35 gpio, ref(clk), dev(a1) +mpp36 36 gpio, dev(a0) +mpp37 37 gpio, sd(d3), dev(ad8), ge(rxclk) +mpp38 38 gpio, ref(clk), sd(d0), dev(ad4), ge(rxd1) +mpp39 39 gpio, i2c1(sck), ua0(cts), sd(d1), dev(a2), ge(rxd2) +mpp40 40 gpio, i2c1(sda), ua0(rts), sd(d2), dev(ad6), ge(rxd3) +mpp41 41 gpio, ua1(rxd), ua0(cts), spi1(cs3), dev(burstn), nd(rbn0), ge(rxctl) +mpp42 42 gpio, ua1(txd), ua0(rts), dev(ad7) +mpp43 43 gpio, pcie0(clkreq), m(vtt), m(decc), spi1(cs2), dev(clkout), nd(rbn1) +mpp44 44 gpio, sata0(present) [1], sata1(present) [1], led(clk) +mpp45 45 gpio, ref(clk), pcie0(rstout), ua1(rxd) +mpp46 46 gpio, ref(clk), pcie0(rstout), ua1(txd), led(stb) +mpp47 47 gpio, sata0(present) [1], sata1(present) [1], led(data) +mpp48 48 gpio, sata0(present) [1], m(vtt), tdm(pclk) [1], audio(mclk) [1], sd(d4), pcie0(clkreq), ua1(txd) +mpp49 49 gpio, tdm(fsync) [1], audio(lrclk) [1], sd(d5), ua2(rxd) +mpp50 50 gpio, pcie0(rstout), tdm(drx) [1], audio(extclk) [1], sd(cmd), ua2(rxd) +mpp51 51 gpio, tdm(dtx) [1], audio(sdo) [1], m(decc), ua2(txd) +mpp52 52 gpio, pcie0(rstout), tdm(intn) [1], audio(sdi) [1], sd(d6), i2c3(sck) +mpp53 53 gpio, sata1(present) [1], sata0(present) [1], tdm(rstn) [1], audio(bclk) [1], sd(d7), i2c3(sda) +mpp54 54 gpio, sata0(present) [1], sata1(present) [1], pcie0(rstout), sd(d3), ua3(txd) +mpp55 55 gpio, ua1(cts), spi1(cs1), sd(d0), ua1(rxd), ua3(rxd) +mpp56 56 gpio, ua1(rts), m(decc), spi1(mosi), ua1(txd) +mpp57 57 gpio, spi1(sck), sd(clk), ua1(txd) +mpp58 58 gpio, i2c1(sck), pcie2(clkreq), spi1(miso), sd(d1), ua1(rxd) +mpp59 59 gpio, pcie0(rstout), i2c1(sda), spi1(cs0), sd(d2) + +[1]: only available on 88F6928 diff --git a/Documentation/devicetree/bindings/pinctrl/meson,pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/meson,pinctrl.txt index 17e7240c699879..3f6a524cc5ffce 100644 --- a/Documentation/devicetree/bindings/pinctrl/meson,pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/meson,pinctrl.txt @@ -1,7 +1,7 @@ == Amlogic Meson pinmux controller == Required properties for the root node: - - compatible: "amlogic,meson8-pinctrl" + - compatible: "amlogic,meson8-pinctrl" or "amlogic,meson8b-pinctrl" - reg: address and size of registers controlling irq functionality === GPIO sub-nodes === diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra210-pinmux.txt b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra210-pinmux.txt new file mode 100644 index 00000000000000..a62d82d5fbe927 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra210-pinmux.txt @@ -0,0 +1,166 @@ +NVIDIA Tegra210 pinmux controller + +Required properties: +- compatible: "nvidia,tegra210-pinmux" +- reg: Should contain a list of base address and size pairs for: + - first entry: The APB_MISC_GP_*_PADCTRL registers (pad control) + - second entry: The PINMUX_AUX_* registers (pinmux) + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +Tegra's pin configuration nodes act as a container for an arbitrary number of +subnodes. Each of these subnodes represents some desired configuration for a +pin, a group, or a list of pins or groups. This configuration can include the +mux function to select on those pin(s)/group(s), and various pin configuration +parameters, such as pull-up, tristate, drive strength, etc. + +The name of each subnode is not important; all subnodes should be enumerated +and processed purely based on their content. + +Each subnode only affects those parameters that are explicitly listed. In +other words, a subnode that lists a mux function but no pin configuration +parameters implies no information about any pin configuration parameters. +Similarly, a pin subnode that describes a pullup parameter implies no +information about e.g. the mux function or tristate parameter. For this +reason, even seemingly boolean values are actually tristates in this binding: +unspecified, off, or on. Unspecified is represented as an absent property, +and off/on are represented as integer values 0 and 1. + +See the TRM to determine which properties and values apply to each pin/group. +Macro values for property values are defined in +include/dt-binding/pinctrl/pinctrl-tegra.h. + +Required subnode-properties: +- nvidia,pins : An array of strings. Each string contains the name of a pin or + group. Valid values for these names are listed below. + +Optional subnode-properties: +- nvidia,function: A string containing the name of the function to mux to the + pin or group. +- nvidia,pull: Integer, representing the pull-down/up to apply to the pin. + 0: none, 1: down, 2: up. +- nvidia,tristate: Integer. + 0: drive, 1: tristate. +- nvidia,enable-input: Integer. Enable the pin's input path. + enable :TEGRA_PIN_ENABLE0 and + disable or output only: TEGRA_PIN_DISABLE. +- nvidia,open-drain: Integer. + enable: TEGRA_PIN_ENABLE. + disable: TEGRA_PIN_DISABLE. +- nvidia,lock: Integer. Lock the pin configuration against further changes + until reset. + enable: TEGRA_PIN_ENABLE. + disable: TEGRA_PIN_DISABLE. +- nvidia,io-hv: Integer. Select high-voltage receivers. + normal: TEGRA_PIN_DISABLE + high: TEGRA_PIN_ENABLE +- nvidia,high-speed-mode: Integer. Enable high speed mode the pins. + normal: TEGRA_PIN_DISABLE + high: TEGRA_PIN_ENABLE +- nvidia,schmitt: Integer. Enables Schmitt Trigger on the input. + normal: TEGRA_PIN_DISABLE + high: TEGRA_PIN_ENABLE +- nvidia,drive-type: Integer. Valid range 0...3. +- nvidia,pull-down-strength: Integer. Controls drive strength. 0 is weakest. + The range of valid values depends on the pingroup. See "CAL_DRVDN" in the + Tegra TRM. +- nvidia,pull-up-strength: Integer. Controls drive strength. 0 is weakest. + The range of valid values depends on the pingroup. See "CAL_DRVUP" in the + Tegra TRM. +- nvidia,slew-rate-rising: Integer. Controls rising signal slew rate. 0 is + fastest. The range of valid values depends on the pingroup. See + "DRVDN_SLWR" in the Tegra TRM. +- nvidia,slew-rate-falling: Integer. Controls falling signal slew rate. 0 is + fastest. The range of valid values depends on the pingroup. See + "DRVUP_SLWF" in the Tegra TRM. + +Valid values for pin and group names (nvidia,pin) are: + + Mux groups: + + These correspond to Tegra PINMUX_AUX_* (pinmux) registers. Any property + that exists in those registers may be set for the following pin names. + + In Tegra210, many pins also have a dedicated APB_MISC_GP_*_PADCTRL + register. Where that is true, and property that exists in that register + may also be set on the following pin names. + + als_prox_int_px3, ap_ready_pv5, ap_wake_bt_ph3, ap_wake_nfc_ph7, + aud_mclk_pbb0, batt_bcl, bt_rst_ph4, bt_wake_ap_ph5, button_home_py1, + button_power_on_px5, button_slide_sw_py0, button_vol_down_px7, + button_vol_up_px6, cam1_mclk_ps0, cam1_pwdn_ps7, cam1_strobe_pt1, + cam2_mclk_ps1, cam2_pwdn_pt0, cam_af_en_ps5, cam_flash_en_ps6, + cam_i2c_scl_ps2, cam_i2c_sda_ps3, cam_rst_ps4cam_rst_ps4, clk_32k_in, + clk_32k_out_py5, clk_req, core_pwr_req, cpu_pwr_req, dap1_din_pb1, + dap1_dout_pb2, dap1_fs_pb0, dap1_sclk_pb3, dap2_din_paa2, dap2_dout_paa3, + dap2_fs_paa0, dap2_sclk_paa1, dap4_din_pj5, dap4_dout_pj6, dap4_fs_pj4, + dap4_sclk_pj7, dmic1_clk_pe0, dmic1_dat_pe1, dmic2_clk_pe2, dmic2_dat_pe3, + dmic3_clk_pe4, dmic3_dat_pe5, dp_hpd0_pcc6, dvfs_clk_pbb2, dvfs_pwm_pbb1, + gen1_i2c_scl_pj1, gen1_i2c_sda_pj0, gen2_i2c_scl_pj2, gen2_i2c_sda_pj3, + gen3_i2c_scl_pf0, gen3_i2c_sda_pf1, gpio_x1_aud_pbb3, gpio_x3_aud_pbb4, + gps_en_pi2, gps_rst_pi3, hdmi_cec_pcc0, hdmi_int_dp_hpd_pcc1, jtag_rtck, + lcd_bl_en_pv1, lcd_bl_pwm_pv0, lcd_gpio1_pv3, lcd_gpio2_pv4, lcd_rst_pv2, + lcd_te_py2, modem_wake_ap_px0, motion_int_px2, nfc_en_pi0, nfc_int_pi1, + pa6, pcc7, pe6, pe7, pex_l0_clkreq_n_pa1, pex_l0_rst_n_pa0, + pex_l1_clkreq_n_pa4, pex_l1_rst_n_pa3, pex_wake_n_pa2, ph6, pk0, pk1, pk2, + pk3, pk4, pk5, pk6, pk7, pl0, pl1, pwr_i2c_scl_py3, pwr_i2c_sda_py4, + pwr_int_n, pz0, pz1, pz2, pz3, pz4, pz5, qspi_cs_n_pee1, qspi_io0_pee2, + qspi_io1_pee3, qspi_io2_pee4, qspi_io3_pee5, qspi_sck_pee0, + sata_led_active_pa5, sdmmc1_clk_pm0, sdmmc1_cmd_pm1, sdmmc1_dat0_pm5, + sdmmc1_dat1_pm4, sdmmc1_dat2_pm3, sdmmc1_dat3_pm2, sdmmc3_clk_pp0, + sdmmc3_cmd_pp1, sdmmc3_dat0_pp5, sdmmc3_dat1_pp4, sdmmc3_dat2_pp3, + sdmmc3_dat3_pp2, shutdown, spdif_in_pcc3, spdif_out_pcc2, spi1_cs0_pc3, + spi1_cs1_pc4, spi1_miso_pc1, spi1_mosi_pc0, spi1_sck_pc2, spi2_cs0_pb7, + spi2_cs1_pdd0, spi2_miso_pb5, spi2_mosi_pb4, spi2_sck_pb6, spi4_cs0_pc6, + spi4_miso_pd0, spi4_mosi_pc7, spi4_sck_pc5, temp_alert_px4, touch_clk_pv7, + touch_int_px1, touch_rst_pv6, uart1_cts_pu3, uart1_rts_pu2, uart1_rx_pu1, + uart1_tx_pu0, uart2_cts_pg3, uart2_rts_pg2, uart2_rx_pg1, uart2_tx_pg0, + uart3_cts_pd4, uart3_rts_pd3, uart3_rx_pd2, uart3_tx_pd1, uart4_cts_pi7, + uart4_rts_pi6, uart4_rx_pi5, uart4_tx_pi4, usb_vbus_en0_pcc4, + usb_vbus_en1_pcc5, wifi_en_ph0, wifi_rst_ph1, wifi_wake_ap_ph2 + + Drive groups: + + These correspond to the Tegra APB_MISC_GP_*_PADCTRL (pad control) + registers. Note that where one of these registers controls a single pin + for which a PINMUX_AUX_* exists, see the list above for the pin name to + use when configuring the pinmux. + + pa6, pcc7, pe6, pe7, ph6, pk0, pk1, pk2, pk3, pk4, pk5, pk6, pk7, pl0, pl1, + pz0, pz1, pz2, pz3, pz4, pz5, sdmmc1, sdmmc2, sdmmc3, sdmmc4 + +Valid values for nvidia,functions are: + + aud, bcl, blink, ccla, cec, cldvfs, clk, core, cpu, displaya, displayb, + dmic1, dmic2, dmic3, dp, dtv, extperiph3, i2c1, i2c2, i2c3, i2cpmu, i2cvi, + i2s1, i2s2, i2s3, i2s4a, i2s4b, i2s5a, i2s5b, iqc0, iqc1, jtag, pe, pe0, + pe1, pmi, pwm0, pwm1, pwm2, pwm3, qspi, rsvd0, rsvd1, rsvd2, rsvd3, sata, + sdmmc1, sdmmc3, shutdown, soc, sor0, sor1, spdif, spi1, spi2, spi3, spi4, + sys, touch, uart, uarta, uartb, uartc, uartd, usb, vgp1, vgp2, vgp3, vgp4, + vgp5, vgp6, vimclk, vimclk2 + +Example: + + pinmux: pinmux@70000800 { + compatible = "nvidia,tegra210-pinmux"; + reg = <0x0 0x700008d4 0x0 0x2a8>, /* Pad control registers */ + <0x0 0x70003000 0x0 0x1000>; /* Mux registers */ + + pinctrl-names = "boot"; + pinctrl-0 = <&state_boot>; + + state_boot: pinmux { + gen1_i2c_scl_pj1 { + nvidia,pins = "gen1_i2c_scl_pj1", + nvidia,function = "i2c1"; + nvidia,pull = ; + nvidia,tristate = ; + nvidia,enable-input = ; + nvidia,open-drain = ; + nvidia,io-hv = ; + }; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt index 47d84b6ee91b96..b73c96d24f5924 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt @@ -38,7 +38,7 @@ property exists to define the pin configuration. Each state may also be assigned a name. When names are used, another property exists to map from those names to the integer IDs. -Each client device's own binding determines the set of states the must be +Each client device's own binding determines the set of states that must be defined in its device tree node, and whether to define the set of state IDs that must be provided, or whether to define the set of state names that must be provided. @@ -133,16 +133,27 @@ pin multiplexing nodes: function - the mux function to select groups - the list of groups to select with this function + (either this or "pins" must be specified) +pins - the list of pins to select with this function (either + this or "groups" must be specified) Example: state_0_node_a { - function = "uart0"; - groups = "u0rxtx", "u0rtscts"; + uart0 { + function = "uart0"; + groups = "u0rxtx", "u0rtscts"; + }; }; state_1_node_a { - function = "spi0"; - groups = "spi0pins"; + spi0 { + function = "spi0"; + groups = "spi0pins"; + }; +}; +state_2_node_a { + function = "i2c0"; + pins = "mfio29", "mfio30"; }; == Generic pin configuration node content == @@ -188,16 +199,22 @@ slew-rate - set the slew rate For example: state_0_node_a { - pins = "GPIO0_AJ5", "GPIO2_AH4"; /* CTS+RXD */ - bias-pull-up; + cts_rxd { + pins = "GPIO0_AJ5", "GPIO2_AH4"; /* CTS+RXD */ + bias-pull-up; + }; }; state_1_node_a { - pins = "GPIO1_AJ3", "GPIO3_AH3"; /* RTS+TXD */ - output-high; + rts_txd { + pins = "GPIO1_AJ3", "GPIO3_AH3"; /* RTS+TXD */ + output-high; + }; }; state_2_node_a { - group = "foo-group"; - bias-pull-up; + foo { + group = "foo-group"; + bias-pull-up; + }; }; Some of the generic properties take arguments. For those that do, the diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt new file mode 100644 index 00000000000000..5868a0f7255d25 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-mt65xx.txt @@ -0,0 +1,145 @@ +* Mediatek MT65XX Pin Controller + +The Mediatek's Pin controller is used to control SoC pins. + +Required properties: +- compatible: value should be either of the following. + (a) "mediatek,mt8135-pinctrl", compatible with mt8135 pinctrl. +- mediatek,pctl-regmap: Should be a phandle of the syscfg node. +- pins-are-numbered: Specify the subnodes are using numbered pinmux to + specify pins. +- gpio-controller : Marks the device node as a gpio controller. +- #gpio-cells: number of cells in GPIO specifier. Since the generic GPIO + binding is used, the amount of cells must be specified as 2. See the below + mentioned gpio binding representation for description of particular cells. + + Eg: <&pio 6 0> + <[phandle of the gpio controller node] + [line number within the gpio controller] + [flags]> + + Values for gpio specifier: + - Line number: is a value between 0 to 202. + - Flags: bit field of flags, as defined in . + Only the following flags are supported: + 0 - GPIO_ACTIVE_HIGH + 1 - GPIO_ACTIVE_LOW +- reg: physicall address base for EINT registers +- interrupt-controller: Marks the device node as an interrupt controller +- #interrupt-cells: Should be two. +- interrupts : The interrupt outputs from the controller. + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices. + +Subnode format +A pinctrl node should contain at least one subnodes representing the +pinctrl groups available on the machine. Each subnode will list the +pins it needs, and how they should be configured, with regard to muxer +configuration, pullups, drive strength, input enable/disable and input schmitt. + + node { + pinmux = ; + GENERIC_PINCONFIG; + }; + +Required properties: +- pinmux: integer array, represents gpio pin number and mux setting. + Supported pin number and mux varies for different SoCs, and are defined + as macros in boot/dts/-pinfunc.h directly. + +Optional properties: +- GENERIC_PINCONFIG: is the generic pinconfig options to use, bias-disable, + bias-pull-down, bias-pull-up, input-enable, input-disable, output-low, output-high, + input-schmitt-enable, input-schmitt-disable and drive-strength are valid. + + Some special pins have extra pull up strength, there are R0 and R1 pull-up + resistors available, but for user, it's only need to set R1R0 as 00, 01, 10 or 11. + So when config bias-pull-up, it support arguments for those special pins. + Some macros have been defined for this usage, such as MTK_PUPD_SET_R1R0_00. + See dt-bindings/pinctrl/mt65xx.h. + + When config drive-strength, it can support some arguments, such as + MTK_DRIVE_4mA, MTK_DRIVE_6mA, etc. See dt-bindings/pinctrl/mt65xx.h. + +Examples: + +#include "mt8135-pinfunc.h" + +... +{ + syscfg_pctl_a: syscfg_pctl_a@10005000 { + compatible = "mediatek,mt8135-pctl-a-syscfg", "syscon"; + reg = <0 0x10005000 0 0x1000>; + }; + + syscfg_pctl_b: syscfg_pctl_b@1020C020 { + compatible = "mediatek,mt8135-pctl-b-syscfg", "syscon"; + reg = <0 0x1020C020 0 0x1000>; + }; + + pinctrl@01c20800 { + compatible = "mediatek,mt8135-pinctrl"; + reg = <0 0x1000B000 0 0x1000>; + mediatek,pctl-regmap = <&syscfg_pctl_a &syscfg_pctl_b>; + pins-are-numbered; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = , + , + ; + + i2c0_pins_a: i2c0@0 { + pins1 { + pinmux = , + ; + bias-disable; + }; + }; + + i2c1_pins_a: i2c1@0 { + pins { + pinmux = , + ; + bias-pull-up = <55>; + }; + }; + + i2c2_pins_a: i2c2@0 { + pins1 { + pinmux = ; + bias-pull-down; + }; + + pins2 { + pinmux = ; + bias-pull-up; + }; + }; + + i2c3_pins_a: i2c3@0 { + pins1 { + pinmux = , + ; + bias-pull-up = <55>; + }; + + pins2 { + pinmux = , + ; + output-low; + bias-pull-up = <55>; + }; + + pins3 { + pinmux = , + ; + drive-strength = <32>; + }; + }; + + ... + } +}; diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt index 7ed08048516a9c..1ae63c0acd40b2 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt +++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.txt @@ -10,6 +10,7 @@ PMIC's from Qualcomm. "qcom,pm8018-gpio" "qcom,pm8038-gpio" "qcom,pm8058-gpio" + "qcom,pm8916-gpio" "qcom,pm8917-gpio" "qcom,pm8921-gpio" "qcom,pm8941-gpio" @@ -74,6 +75,7 @@ to specify in a pin configuration subnode: gpio1-gpio6 for pm8018 gpio1-gpio12 for pm8038 gpio1-gpio40 for pm8058 + gpio1-gpio4 for pm8916 gpio1-gpio38 for pm8917 gpio1-gpio44 for pm8921 gpio1-gpio36 for pm8941 diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt index 854774b194ed70..ed19991aad35e2 100644 --- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt +++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.txt @@ -8,6 +8,7 @@ of PMIC's from Qualcomm. Value type: Definition: Should contain one of: "qcom,pm8841-mpp", + "qcom,pm8916-mpp", "qcom,pm8941-mpp", "qcom,pma8084-mpp", @@ -67,6 +68,7 @@ to specify in a pin configuration subnode: Definition: List of MPP pins affected by the properties specified in this subnode. Valid pins are: mpp1-mpp4 for pm8841 + mpp1-mpp4 for pm8916 mpp1-mpp8 for pm8941 mpp1-mpp4 for pma8084 diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index fae26d014aaf25..b13aa55b7a6f71 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -37,6 +37,7 @@ capella Capella Microsystems, Inc cavium Cavium, Inc. cdns Cadence Design Systems Inc. chipidea Chipidea, Inc +chipone ChipOne chipspark ChipSPARK chrp Common Hardware Reference Platform chunghwa Chunghwa Picture Tubes Ltd. @@ -78,6 +79,7 @@ geniatech Geniatech, Inc. giantplus Giantplus Technology Co., Ltd. globalscale Globalscale Technologies, Inc. gmt Global Mixed-mode Technology, Inc. +goodix Shenzhen Huiding Technology Co., Ltd. google Google, Inc. gumstix Gumstix, Inc. gw Gateworks Corporation @@ -125,6 +127,7 @@ mxicy Macronix International Co., Ltd. national National Semiconductor neonode Neonode Inc. netgear NETGEAR +netlogic Broadcom Corporation (formerly NetLogic Microsystems) newhaven Newhaven Display International nintendo Nintendo nokia Nokia @@ -167,6 +170,7 @@ sii Seiko Instruments, Inc. silergy Silergy Corp. sirf SiRF Technology, Inc. sitronix Sitronix Technology Corporation +skyworks Skyworks Solutions, Inc. smsc Standard Microsystems Corporation snps Synopsys, Inc. solidrun SolidRun @@ -194,6 +198,7 @@ voipac Voipac Technologies s.r.o. winbond Winbond Electronics corp. wlf Wolfson Microelectronics wm Wondermedia Technologies, Inc. +x-powers X-Powers xes Extreme Engineering Solutions (X-ES) xillybus Xillybus Ltd. xlnx Xilinx diff --git a/Documentation/devicetree/bindings/video/backlight/sky81452-backlight.txt b/Documentation/devicetree/bindings/video/backlight/sky81452-backlight.txt new file mode 100644 index 00000000000000..8bf2940f54bcfc --- /dev/null +++ b/Documentation/devicetree/bindings/video/backlight/sky81452-backlight.txt @@ -0,0 +1,29 @@ +SKY81452-backlight bindings + +Required properties: +- compatible : Must be "skyworks,sky81452-backlight" + +Optional properties: +- name : Name of backlight device. Default is 'lcd-backlight'. +- gpios : GPIO to use to EN pin. + See Documentation/devicetree/bindings/gpio/gpio.txt +- led-sources : List of enabled channels from 0 to 5. + See Documentation/devicetree/bindings/leds/common.txt +- skyworks,ignore-pwm : Ignore both PWM input +- skyworks,dpwm-mode : Enable DPWM dimming mode, otherwise Analog dimming. +- skyworks,phase-shift : Enable phase shift mode +- skyworks,short-detection-threshold-volt + : It should be one of 4, 5, 6 and 7V. +- skyworks,current-limit-mA + : It should be 2300mA or 2750mA. + +Example: + + backlight { + compatible = "skyworks,sky81452-backlight"; + name = "pwm-backlight"; + led-sources = <0 1 2 5>; + skyworks,ignore-pwm; + skyworks,phase-shift; + skyworks,current-limit-mA = <2300>; + }; diff --git a/Documentation/hid/hid-sensor.txt b/Documentation/hid/hid-sensor.txt index 948b0989c4335c..b287752a31cd56 100644 --- a/Documentation/hid/hid-sensor.txt +++ b/Documentation/hid/hid-sensor.txt @@ -138,3 +138,87 @@ accelerometer wants to poll X axis value, then it can call this function with the usage id of X axis. HID sensors can provide events, so this is not necessary to poll for any field. If there is some new sample, the core driver will call registered callback function to process the sample. + + +---------- + +HID Custom and generic Sensors + +HID Sensor specification defines two special sensor usage types. Since they +don't represent a standard sensor, it is not possible to define using Linux IIO +type interfaces. +The purpose of these sensors is to extend the functionality or provide a +way to obfuscate the data being communicated by a sensor. Without knowing the +mapping between the data and its encapsulated form, it is difficult for +an application/driver to determine what data is being communicated by the sensor. +This allows some differentiating use cases, where vendor can provide applications. +Some common use cases are debug other sensors or to provide some events like +keyboard attached/detached or lid open/close. + +To allow application to utilize these sensors, here they are exported uses sysfs +attribute groups, attributes and misc device interface. + +An example of this representation on sysfs: +/sys/devices/pci0000:00/INT33C2:00/i2c-0/i2c-INT33D1:00/0018:8086:09FA.0001/HID-SENSOR-2000e1.6.auto$ tree -R +. +????????? enable_sensor +????????? feature-0-200316 +??????? ????????? feature-0-200316-maximum +??????? ????????? feature-0-200316-minimum +??????? ????????? feature-0-200316-name +??????? ????????? feature-0-200316-size +??????? ????????? feature-0-200316-unit-expo +??????? ????????? feature-0-200316-units +??????? ????????? feature-0-200316-value +????????? feature-1-200201 +??????? ????????? feature-1-200201-maximum +??????? ????????? feature-1-200201-minimum +??????? ????????? feature-1-200201-name +??????? ????????? feature-1-200201-size +??????? ????????? feature-1-200201-unit-expo +??????? ????????? feature-1-200201-units +??????? ????????? feature-1-200201-value +????????? input-0-200201 +??????? ????????? input-0-200201-maximum +??????? ????????? input-0-200201-minimum +??????? ????????? input-0-200201-name +??????? ????????? input-0-200201-size +??????? ????????? input-0-200201-unit-expo +??????? ????????? input-0-200201-units +??????? ????????? input-0-200201-value +????????? input-1-200202 +??????? ????????? input-1-200202-maximum +??????? ????????? input-1-200202-minimum +??????? ????????? input-1-200202-name +??????? ????????? input-1-200202-size +??????? ????????? input-1-200202-unit-expo +??????? ????????? input-1-200202-units +??????? ????????? input-1-200202-value + +Here there is a custom sensors with four fields, two feature and two inputs. +Each field is represented by a set of attributes. All fields except the "value" +are read only. The value field is a RW field. +Example +/sys/bus/platform/devices/HID-SENSOR-2000e1.6.auto/feature-0-200316$ grep -r . * +feature-0-200316-maximum:6 +feature-0-200316-minimum:0 +feature-0-200316-name:property-reporting-state +feature-0-200316-size:1 +feature-0-200316-unit-expo:0 +feature-0-200316-units:25 +feature-0-200316-value:1 + +How to enable such sensor? +By default sensor can be power gated. To enable sysfs attribute "enable" can be +used. +$ echo 1 > enable_sensor + +Once enabled and powered on, sensor can report value using HID reports. +These reports are pushed using misc device interface in a FIFO order. +/dev$ tree | grep HID-SENSOR-2000e1.6.auto +??????? ????????? 10:53 -> ../HID-SENSOR-2000e1.6.auto +????????? HID-SENSOR-2000e1.6.auto + +Each reports can be of variable length preceded by a header. This header +consist of a 32 bit usage id, 64 bit time stamp and 32 bit length field of raw +data. diff --git a/Documentation/i2c/slave-eeprom-backend b/Documentation/i2c/slave-eeprom-backend new file mode 100644 index 00000000000000..c8444ef82acfa3 --- /dev/null +++ b/Documentation/i2c/slave-eeprom-backend @@ -0,0 +1,14 @@ +Linux I2C slave eeprom backend +============================== + +by Wolfram Sang in 2014-15 + +This is a proof-of-concept backend which acts like an EEPROM on the connected +I2C bus. The memory contents can be modified from userspace via this file +located in sysfs: + + /sys/bus/i2c/devices//slave-eeprom + +As of 2015, Linux doesn't support poll on binary sysfs files, so there is no +notfication when another master changed the content. + diff --git a/Documentation/i2c/slave-interface b/Documentation/i2c/slave-interface new file mode 100644 index 00000000000000..389bb5d618549e --- /dev/null +++ b/Documentation/i2c/slave-interface @@ -0,0 +1,179 @@ +Linux I2C slave interface description +===================================== + +by Wolfram Sang in 2014-15 + +Linux can also be an I2C slave in case I2C controllers have slave support. +Besides this HW requirement, one also needs a software backend providing the +actual functionality. An example for this is the slave-eeprom driver, which +acts as a dual memory driver. While another I2C master on the bus can access it +like a regular EEPROM, the Linux I2C slave can access the content via sysfs and +retrieve/provide information as needed. The software backend driver and the I2C +bus driver communicate via events. Here is a small graph visualizing the data +flow and the means by which data is transported. The dotted line marks only one +example. The backend could also use e.g. a character device, be in-kernel +only, or something completely different: + + + e.g. sysfs I2C slave events I/O registers + +-----------+ v +---------+ v +--------+ v +------------+ + | Userspace +........+ Backend +-----------+ Driver +-----+ Controller | + +-----------+ +---------+ +--------+ +------------+ + | | + ----------------------------------------------------------------+-- I2C + --------------------------------------------------------------+---- Bus + +Note: Technically, there is also the I2C core between the backend and the +driver. However, at this time of writing, the layer is transparent. + + +User manual +=========== + +I2C slave backends behave like standard I2C clients. So, you can instantiate +them like described in the document 'instantiating-devices'. A quick example +for instantiating the slave-eeprom driver from userspace: + + # echo 0-0064 > /sys/bus/i2c/drivers/i2c-slave-eeprom/bind + +Each backend should come with separate documentation to describe its specific +behaviour and setup. + + +Developer manual +================ + +I2C slave events +---------------- + +The bus driver sends an event to the backend using the following function: + + ret = i2c_slave_event(client, event, &val) + +'client' describes the i2c slave device. 'event' is one of the special event +types described hereafter. 'val' holds an u8 value for the data byte to be +read/written and is thus bidirectional. The pointer to val must always be +provided even if val is not used for an event, i.e. don't use NULL here. 'ret' +is the return value from the backend. Mandatory events must be provided by the +bus drivers and must be checked for by backend drivers. + +Event types: + +* I2C_SLAVE_WRITE_REQUESTED (mandatory) + +'val': unused +'ret': always 0 + +Another I2C master wants to write data to us. This event should be sent once +our own address and the write bit was detected. The data did not arrive yet, so +there is nothing to process or return. Wakeup or initialization probably needs +to be done, though. + +* I2C_SLAVE_READ_REQUESTED (mandatory) + +'val': backend returns first byte to be sent +'ret': always 0 + +Another I2C master wants to read data from us. This event should be sent once +our own address and the read bit was detected. After returning, the bus driver +should transmit the first byte. + +* I2C_SLAVE_WRITE_RECEIVED (mandatory) + +'val': bus driver delivers received byte +'ret': 0 if the byte should be acked, some errno if the byte should be nacked + +Another I2C master has sent a byte to us which needs to be set in 'val'. If 'ret' +is zero, the bus driver should ack this byte. If 'ret' is an errno, then the byte +should be nacked. + +* I2C_SLAVE_READ_PROCESSED (mandatory) + +'val': backend returns next byte to be sent +'ret': always 0 + +The bus driver requests the next byte to be sent to another I2C master in +'val'. Important: This does not mean that the previous byte has been acked, it +only means that the previous byte is shifted out to the bus! To ensure seamless +transmission, most hardware requests the next byte when the previous one is +still shifted out. If the master sends NACK and stops reading after the byte +currently shifted out, this byte requested here is never used. It very likely +needs to be sent again on the next I2C_SLAVE_READ_REQUEST, depending a bit on +your backend, though. + +* I2C_SLAVE_STOP (mandatory) + +'val': unused +'ret': always 0 + +A stop condition was received. This can happen anytime and the backend should +reset its state machine for I2C transfers to be able to receive new requests. + + +Software backends +----------------- + +If you want to write a software backend: + +* use a standard i2c_driver and its matching mechanisms +* write the slave_callback which handles the above slave events + (best using a state machine) +* register this callback via i2c_slave_register() + +Check the i2c-slave-eeprom driver as an example. + + +Bus driver support +------------------ + +If you want to add slave support to the bus driver: + +* implement calls to register/unregister the slave and add those to the + struct i2c_algorithm. When registering, you probably need to set the i2c + slave address and enable slave specific interrupts. If you use runtime pm, you + should use pm_runtime_forbid() because your device usually needs to be powered + on always to be able to detect its slave address. When unregistering, do the + inverse of the above. + +* Catch the slave interrupts and send appropriate i2c_slave_events to the backend. + +Check the i2c-rcar driver as an example. + + +About ACK/NACK +-------------- + +It is good behaviour to always ACK the address phase, so the master knows if a +device is basically present or if it mysteriously disappeared. Using NACK to +state being busy is troublesome. SMBus demands to always ACK the address phase, +while the I2C specification is more loose on that. Most I2C controllers also +automatically ACK when detecting their slave addresses, so there is no option +to NACK them. For those reasons, this API does not support NACK in the address +phase. + +Currently, there is no slave event to report if the master did ACK or NACK a +byte when it reads from us. We could make this an optional event if the need +arises. However, cases should be extremely rare because the master is expected +to send STOP after that and we have an event for that. Also, keep in mind not +all I2C controllers have the possibility to report that event. + + +About buffers +------------- + +During development of this API, the question of using buffers instead of just +bytes came up. Such an extension might be possible, usefulness is unclear at +this time of writing. Some points to keep in mind when using buffers: + +* Buffers should be opt-in and slave drivers will always have to support + byte-based transactions as the ultimate fallback because this is how the + majority of HW works. + +* For backends simulating hardware registers, buffers are not helpful because + on writes an action should be immediately triggered. For reads, the data in + the buffer might get stale. + +* A master can send STOP at any time. For partially transferred buffers, this + means additional code to handle this exception. Such code tends to be + error-prone. + diff --git a/Documentation/i2c/summary b/Documentation/i2c/summary index 13ab076dcd9248..809541ab352f03 100644 --- a/Documentation/i2c/summary +++ b/Documentation/i2c/summary @@ -41,7 +41,3 @@ integrated than Algorithm and Adapter. For a given configuration, you will need a driver for your I2C bus, and drivers for your I2C devices (usually one driver for each device). - -At this time, Linux only operates I2C (or SMBus) in master mode; you can't -use these APIs to make a Linux system behave as a slave/device, either to -speak a custom protocol or to emulate some other device. diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 8cc635f4c4f081..32755634975708 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2973,6 +2973,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Set maximum number of finished RCU callbacks to process in one batch. + rcutree.gp_init_delay= [KNL] + Set the number of jiffies to delay each step of + RCU grace-period initialization. This only has + effect when CONFIG_RCU_TORTURE_TEST_SLOW_INIT is + set. + rcutree.rcu_fanout_leaf= [KNL] Increase the number of CPUs assigned to each leaf rcu_node structure. Useful for very large @@ -2996,11 +3002,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted. value is one, and maximum value is HZ. rcutree.kthread_prio= [KNL,BOOT] - Set the SCHED_FIFO priority of the RCU - per-CPU kthreads (rcuc/N). This value is also - used for the priority of the RCU boost threads - (rcub/N). Valid values are 1-99 and the default - is 1 (the least-favored priority). + Set the SCHED_FIFO priority of the RCU per-CPU + kthreads (rcuc/N). This value is also used for + the priority of the RCU boost threads (rcub/N) + and for the RCU grace-period kthreads (rcu_bh, + rcu_preempt, and rcu_sched). If RCU_BOOST is + set, valid values are 1-99 and the default is 1 + (the least-favored priority). Otherwise, when + RCU_BOOST is not set, valid values are 0-99 and + the default is zero (non-realtime operation). rcutree.rcu_nocb_leader_stride= [KNL] Set the number of NOCB kthread groups, which diff --git a/Documentation/kernel-per-CPU-kthreads.txt b/Documentation/kernel-per-CPU-kthreads.txt index f3cd299fcc4120..f4cbfe0ba1085b 100644 --- a/Documentation/kernel-per-CPU-kthreads.txt +++ b/Documentation/kernel-per-CPU-kthreads.txt @@ -190,20 +190,24 @@ To reduce its OS jitter, do any of the following: on each CPU, including cs_dbs_timer() and od_dbs_timer(). WARNING: Please check your CPU specifications to make sure that this is safe on your particular system. - d. It is not possible to entirely get rid of OS jitter - from vmstat_update() on CONFIG_SMP=y systems, but you - can decrease its frequency by writing a large value - to /proc/sys/vm/stat_interval. The default value is - HZ, for an interval of one second. Of course, larger - values will make your virtual-memory statistics update - more slowly. Of course, you can also run your workload - at a real-time priority, thus preempting vmstat_update(), + d. As of v3.18, Christoph Lameter's on-demand vmstat workers + commit prevents OS jitter due to vmstat_update() on + CONFIG_SMP=y systems. Before v3.18, is not possible + to entirely get rid of the OS jitter, but you can + decrease its frequency by writing a large value to + /proc/sys/vm/stat_interval. The default value is HZ, + for an interval of one second. Of course, larger values + will make your virtual-memory statistics update more + slowly. Of course, you can also run your workload at + a real-time priority, thus preempting vmstat_update(), but if your workload is CPU-bound, this is a bad idea. However, there is an RFC patch from Christoph Lameter (based on an earlier one from Gilad Ben-Yossef) that reduces or even eliminates vmstat overhead for some workloads at https://lkml.org/lkml/2013/9/4/379. - e. If running on high-end powerpc servers, build with + e. Boot with "elevator=noop" to avoid workqueue use by + the block layer. + f. If running on high-end powerpc servers, build with CONFIG_PPC_RTAS_DAEMON=n. This prevents the RTAS daemon from running on each CPU every second or so. (This will require editing Kconfig files and will defeat @@ -211,12 +215,12 @@ To reduce its OS jitter, do any of the following: due to the rtas_event_scan() function. WARNING: Please check your CPU specifications to make sure that this is safe on your particular system. - f. If running on Cell Processor, build your kernel with + g. If running on Cell Processor, build your kernel with CBE_CPUFREQ_SPU_GOVERNOR=n to avoid OS jitter from spu_gov_work(). WARNING: Please check your CPU specifications to make sure that this is safe on your particular system. - g. If running on PowerMAC, build your kernel with + h. If running on PowerMAC, build your kernel with CONFIG_PMAC_RACKMETER=n to disable the CPU-meter, avoiding OS jitter from rackmeter_do_timer(). @@ -258,8 +262,12 @@ Purpose: Detect software lockups on each CPU. To reduce its OS jitter, do at least one of the following: 1. Build with CONFIG_LOCKUP_DETECTOR=n, which will prevent these kthreads from being created in the first place. -2. Echo a zero to /proc/sys/kernel/watchdog to disable the +2. Boot with "nosoftlockup=0", which will also prevent these kthreads + from being created. Other related watchdog and softlockup boot + parameters may be found in Documentation/kernel-parameters.txt + and Documentation/watchdog/watchdog-parameters.txt. +3. Echo a zero to /proc/sys/kernel/watchdog to disable the watchdog timer. -3. Echo a large number of /proc/sys/kernel/watchdog_thresh in +4. Echo a large number of /proc/sys/kernel/watchdog_thresh in order to reduce the frequency of OS jitter due to the watchdog timer down to a level that is acceptable for your workload. diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt index ca2387ef27ab0c..6974f1c2b4e1ed 100644 --- a/Documentation/memory-barriers.txt +++ b/Documentation/memory-barriers.txt @@ -592,9 +592,9 @@ See also the subsection on "Cache Coherency" for a more thorough example. CONTROL DEPENDENCIES -------------------- -A control dependency requires a full read memory barrier, not simply a data -dependency barrier to make it work correctly. Consider the following bit of -code: +A load-load control dependency requires a full read memory barrier, not +simply a data dependency barrier to make it work correctly. Consider the +following bit of code: q = ACCESS_ONCE(a); if (q) { @@ -615,14 +615,15 @@ case what's actually required is: } However, stores are not speculated. This means that ordering -is- provided -in the following example: +for load-store control dependencies, as in the following example: q = ACCESS_ONCE(a); if (q) { ACCESS_ONCE(b) = p; } -Please note that ACCESS_ONCE() is not optional! Without the +Control dependencies pair normally with other types of barriers. +That said, please note that ACCESS_ONCE() is not optional! Without the ACCESS_ONCE(), might combine the load from 'a' with other loads from 'a', and the store to 'b' with other stores to 'b', with possible highly counterintuitive effects on ordering. @@ -813,6 +814,8 @@ In summary: barrier() can help to preserve your control dependency. Please see the Compiler Barrier section for more information. + (*) Control dependencies pair normally with other types of barriers. + (*) Control dependencies do -not- provide transitivity. If you need transitivity, use smp_mb(). @@ -823,14 +826,14 @@ SMP BARRIER PAIRING When dealing with CPU-CPU interactions, certain types of memory barrier should always be paired. A lack of appropriate pairing is almost certainly an error. -General barriers pair with each other, though they also pair with -most other types of barriers, albeit without transitivity. An acquire -barrier pairs with a release barrier, but both may also pair with other -barriers, including of course general barriers. A write barrier pairs -with a data dependency barrier, an acquire barrier, a release barrier, -a read barrier, or a general barrier. Similarly a read barrier or a -data dependency barrier pairs with a write barrier, an acquire barrier, -a release barrier, or a general barrier: +General barriers pair with each other, though they also pair with most +other types of barriers, albeit without transitivity. An acquire barrier +pairs with a release barrier, but both may also pair with other barriers, +including of course general barriers. A write barrier pairs with a data +dependency barrier, a control dependency, an acquire barrier, a release +barrier, a read barrier, or a general barrier. Similarly a read barrier, +control dependency, or a data dependency barrier pairs with a write +barrier, an acquire barrier, a release barrier, or a general barrier: CPU 1 CPU 2 =============== =============== @@ -850,6 +853,19 @@ Or: y = *x; +Or even: + + CPU 1 CPU 2 + =============== =============================== + r1 = ACCESS_ONCE(y); + + ACCESS_ONCE(y) = 1; if (r2 = ACCESS_ONCE(x)) { + + ACCESS_ONCE(y) = 1; + } + + assert(r1 == 0 || r2 == 0); + Basically, the read barrier always has to be there, even though it can be of the "weaker" type. diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt index b8f2147b96dd90..a9b47163bb5d69 100644 --- a/Documentation/pinctrl.txt +++ b/Documentation/pinctrl.txt @@ -72,7 +72,6 @@ static struct pinctrl_desc foo_desc = { .name = "foo", .pins = foo_pins, .npins = ARRAY_SIZE(foo_pins), - .maxpin = 63, .owner = THIS_MODULE, }; @@ -164,8 +163,8 @@ static const char *foo_get_group_name(struct pinctrl_dev *pctldev, } static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, - unsigned ** const pins, - unsigned * const num_pins) + const unsigned **pins, + unsigned *num_pins) { *pins = (unsigned *) foo_groups[selector].pins; *num_pins = foo_groups[selector].num_pins; @@ -570,9 +569,8 @@ is possible to perform the requested mux setting, poke the hardware so that this happens. Pinmux drivers are required to supply a few callback functions, some are -optional. Usually the enable() and disable() functions are implemented, -writing values into some certain registers to activate a certain mux setting -for a certain pin. +optional. Usually the set_mux() function is implemented, writing values into +some certain registers to activate a certain mux setting for a certain pin. A simple driver for the above example will work by setting bits 0, 1, 2, 3 or 4 into some register named MUX to select a certain function with a certain @@ -683,12 +681,12 @@ static const struct foo_pmx_func foo_functions[] = { }, }; -int foo_get_functions_count(struct pinctrl_dev *pctldev) +static int foo_get_functions_count(struct pinctrl_dev *pctldev) { return ARRAY_SIZE(foo_functions); } -const char *foo_get_fname(struct pinctrl_dev *pctldev, unsigned selector) +static const char *foo_get_fname(struct pinctrl_dev *pctldev, unsigned selector) { return foo_functions[selector].name; } @@ -702,7 +700,7 @@ static int foo_get_groups(struct pinctrl_dev *pctldev, unsigned selector, return 0; } -int foo_set_mux(struct pinctrl_dev *pctldev, unsigned selector, +static int foo_set_mux(struct pinctrl_dev *pctldev, unsigned selector, unsigned group) { u8 regbit = (1 << selector + group); @@ -711,7 +709,7 @@ int foo_set_mux(struct pinctrl_dev *pctldev, unsigned selector, return 0; } -struct pinmux_ops foo_pmxops = { +static struct pinmux_ops foo_pmxops = { .get_functions_count = foo_get_functions_count, .get_function_name = foo_get_fname, .get_function_groups = foo_get_groups, @@ -1266,7 +1264,7 @@ The semantics of the pinctrl APIs are: 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 +the associated pins, whereas select_state pass on to the pin controller driver which takes care of activating and/or deactivating the mux setting by quickly poking some registers. @@ -1363,8 +1361,9 @@ function, but with different named in the mapping as described under "Advanced mapping" above. So that for an SPI device, we have two states named "pos-A" and "pos-B". -This snippet first muxes the function in the pins defined by group A, enables -it, disables and releases it, and muxes it in on the pins defined by group B: +This snippet first initializes a state object for both groups (in foo_probe()), +then muxes the function in the pins defined by group A, and finally muxes it in +on the pins defined by group B: #include diff --git a/Documentation/timers/NO_HZ.txt b/Documentation/timers/NO_HZ.txt index cca122f25120b3..6eaf576294f3bb 100644 --- a/Documentation/timers/NO_HZ.txt +++ b/Documentation/timers/NO_HZ.txt @@ -158,13 +158,9 @@ not come for free: to the need to inform kernel subsystems (such as RCU) about the change in mode. -3. POSIX CPU timers on adaptive-tick CPUs may miss their deadlines - (perhaps indefinitely) because they currently rely on - scheduling-tick interrupts. This will likely be fixed in - one of two ways: (1) Prevent CPUs with POSIX CPU timers from - entering adaptive-tick mode, or (2) Use hrtimers or other - adaptive-ticks-immune mechanism to cause the POSIX CPU timer to - fire properly. +3. POSIX CPU timers prevent CPUs from entering adaptive-tick mode. + Real-time applications needing to take actions based on CPU time + consumption need to use other means of doing so. 4. If there are more perf events pending than the hardware can accommodate, they are normally round-robined so as to collect diff --git a/MAINTAINERS b/MAINTAINERS index 38579ac581cebe..283d1145e6d847 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2525,6 +2525,13 @@ L: linux-usb@vger.kernel.org S: Maintained F: drivers/usb/chipidea/ +CHIPONE ICN8318 I2C TOUCHSCREEN DRIVER +M: Hans de Goede +L: linux-input@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt +F: drivers/input/touchscreen/chipone_icn8318.c + CHROME HARDWARE PLATFORM SUPPORT M: Olof Johansson S: Maintained @@ -4337,10 +4344,10 @@ F: scripts/get_maintainer.pl GFS2 FILE SYSTEM M: Steven Whitehouse +M: Bob Peterson L: cluster-devel@redhat.com W: http://sources.redhat.com/cluster/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/steve/gfs2-3.0-fixes.git -T: git git://git.kernel.org/pub/scm/linux/kernel/git/steve/gfs2-3.0-nmw.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2.git S: Supported F: Documentation/filesystems/gfs2*.txt F: fs/gfs2/ diff --git a/arch/arc/kernel/process.c b/arch/arc/kernel/process.c index 98c00a2d4dd9a5..f46efd14059d30 100644 --- a/arch/arc/kernel/process.c +++ b/arch/arc/kernel/process.c @@ -155,8 +155,6 @@ int copy_thread(unsigned long clone_flags, */ void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long usp) { - set_fs(USER_DS); /* user space */ - regs->sp = usp; regs->ret = pc; diff --git a/arch/arc/kernel/unwind.c b/arch/arc/kernel/unwind.c index e550b117ec4ffe..93c6ea52b6719e 100644 --- a/arch/arc/kernel/unwind.c +++ b/arch/arc/kernel/unwind.c @@ -841,7 +841,7 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, break; case DW_CFA_GNU_window_save: default: - unw_debug("UNKNOW OPCODE 0x%x\n", opcode); + unw_debug("UNKNOWN OPCODE 0x%x\n", opcode); result = 0; break; } diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index 7fc70ae21185b7..dc7d0a95bd3651 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c @@ -648,7 +648,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) * Per-cpu breakpoints are not supported by our stepping * mechanism. */ - if (!bp->hw.bp_target) + if (!bp->hw.target) return -EINVAL; /* diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index 98bbe06e469c91..e7d934d3afe028 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -527,7 +527,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) * Disallow per-task kernel breakpoints since these would * complicate the stepping code. */ - if (info->ctrl.privilege == AARCH64_BREAKPOINT_EL1 && bp->hw.bp_target) + if (info->ctrl.privilege == AARCH64_BREAKPOINT_EL1 && bp->hw.target) return -EINVAL; return 0; diff --git a/arch/blackfin/mach-common/smp.c b/arch/blackfin/mach-common/smp.c index 8ad3e90cc8fc4c..1c7259597395e5 100644 --- a/arch/blackfin/mach-common/smp.c +++ b/arch/blackfin/mach-common/smp.c @@ -413,16 +413,14 @@ int __cpu_disable(void) return 0; } -static DECLARE_COMPLETION(cpu_killed); - int __cpu_die(unsigned int cpu) { - return wait_for_completion_timeout(&cpu_killed, 5000); + return cpu_wait_death(cpu, 5); } void cpu_die(void) { - complete(&cpu_killed); + (void)cpu_report_death(); atomic_dec(&init_mm.mm_users); atomic_dec(&init_mm.mm_count); diff --git a/arch/c6x/kernel/process.c b/arch/c6x/kernel/process.c index 57d2ea8d197738..3ae9f5a166a058 100644 --- a/arch/c6x/kernel/process.c +++ b/arch/c6x/kernel/process.c @@ -101,7 +101,6 @@ void start_thread(struct pt_regs *regs, unsigned int pc, unsigned long usp) */ usp -= 8; - set_fs(USER_DS); regs->pc = pc; regs->sp = usp; regs->tsr |= 0x40; /* set user mode */ diff --git a/arch/frv/kernel/signal.c b/arch/frv/kernel/signal.c index 336713ab47454f..85ca6727ca075c 100644 --- a/arch/frv/kernel/signal.c +++ b/arch/frv/kernel/signal.c @@ -176,8 +176,6 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set) struct sigframe __user *frame; int rsig, sig = ksig->sig; - set_fs(USER_DS); - frame = get_sigframe(ksig, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) @@ -257,8 +255,6 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set) struct rt_sigframe __user *frame; int rsig, sig = ksig->sig; - set_fs(USER_DS); - frame = get_sigframe(ksig, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) diff --git a/arch/hexagon/kernel/process.c b/arch/hexagon/kernel/process.c index 0a0dd5c05b46af..a9ebd471823a66 100644 --- a/arch/hexagon/kernel/process.c +++ b/arch/hexagon/kernel/process.c @@ -37,8 +37,6 @@ */ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) { - /* Set to run with user-mode data segmentation */ - set_fs(USER_DS); /* We want to zero all data-containing registers. Is this overkill? */ memset(regs, 0, sizeof(*regs)); /* We might want to also zero all Processor registers here */ diff --git a/arch/m32r/kernel/signal.c b/arch/m32r/kernel/signal.c index 7736c6660a1580..8c25e0c8f6a5c7 100644 --- a/arch/m32r/kernel/signal.c +++ b/arch/m32r/kernel/signal.c @@ -214,8 +214,6 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, regs->r2 = (unsigned long)&frame->uc; regs->bpc = (unsigned long)ksig->ka.sa.sa_handler; - set_fs(USER_DS); - #if DEBUG_SIG printk("SIG deliver (%s:%d): sp=%p pc=%p\n", current->comm, current->pid, frame, regs->pc); diff --git a/arch/metag/include/asm/processor.h b/arch/metag/include/asm/processor.h index 13272fd5a5baec..0838ca69976466 100644 --- a/arch/metag/include/asm/processor.h +++ b/arch/metag/include/asm/processor.h @@ -111,7 +111,6 @@ struct thread_struct { */ #define start_thread(regs, pc, usp) do { \ unsigned int *argc = (unsigned int *) bprm->exec; \ - set_fs(USER_DS); \ current->thread.int_depth = 1; \ /* Force this process down to user land */ \ regs->ctx.SaveMask = TBICTX_PRIV_BIT; \ diff --git a/arch/metag/kernel/smp.c b/arch/metag/kernel/smp.c index f006d2276f40ab..ac3a199e33e714 100644 --- a/arch/metag/kernel/smp.c +++ b/arch/metag/kernel/smp.c @@ -261,7 +261,6 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle) } #ifdef CONFIG_HOTPLUG_CPU -static DECLARE_COMPLETION(cpu_killed); /* * __cpu_disable runs on the processor to be shutdown. @@ -299,7 +298,7 @@ int __cpu_disable(void) */ void __cpu_die(unsigned int cpu) { - if (!wait_for_completion_timeout(&cpu_killed, msecs_to_jiffies(1))) + if (!cpu_wait_death(cpu, 1)) pr_err("CPU%u: unable to kill\n", cpu); } @@ -314,7 +313,7 @@ void cpu_die(void) local_irq_disable(); idle_task_exit(); - complete(&cpu_killed); + (void)cpu_report_death(); asm ("XOR TXENABLE, D0Re0,D0Re0\n"); } diff --git a/arch/microblaze/kernel/signal.c b/arch/microblaze/kernel/signal.c index a1cbaf90e2ea47..20ccd4e2baa54c 100644 --- a/arch/microblaze/kernel/signal.c +++ b/arch/microblaze/kernel/signal.c @@ -236,8 +236,6 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, /* Offset to handle microblaze rtid r14, 0 */ regs->pc = (unsigned long)ksig->ka.sa.sa_handler; - set_fs(USER_DS); - #ifdef DEBUG_SIG pr_info("SIG deliver (%s:%d): sp=%p pc=%08lx\n", current->comm, current->pid, frame, regs->pc); diff --git a/arch/nios2/kernel/process.c b/arch/nios2/kernel/process.c index 0e075b5ad2a542..2f8c74f93e705a 100644 --- a/arch/nios2/kernel/process.c +++ b/arch/nios2/kernel/process.c @@ -94,7 +94,6 @@ void show_regs(struct pt_regs *regs) void flush_thread(void) { - set_fs(USER_DS); } int copy_thread(unsigned long clone_flags, diff --git a/arch/openrisc/kernel/process.c b/arch/openrisc/kernel/process.c index 386af258591dbe..7095dfe7666ba3 100644 --- a/arch/openrisc/kernel/process.c +++ b/arch/openrisc/kernel/process.c @@ -197,7 +197,6 @@ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) { unsigned long sr = mfspr(SPR_SR) & ~SPR_SR_SM; - set_fs(USER_DS); memset(regs, 0, sizeof(struct pt_regs)); regs->pc = pc; diff --git a/arch/powerpc/include/asm/kvm_book3s.h b/arch/powerpc/include/asm/kvm_book3s.h index 942c7b1678e3f7..993090422690c7 100644 --- a/arch/powerpc/include/asm/kvm_book3s.h +++ b/arch/powerpc/include/asm/kvm_book3s.h @@ -106,10 +106,6 @@ struct kvmppc_vcpu_book3s { spinlock_t mmu_lock; }; -#define CONTEXT_HOST 0 -#define CONTEXT_GUEST 1 -#define CONTEXT_GUEST_END 2 - #define VSID_REAL 0x07ffffffffc00000ULL #define VSID_BAT 0x07ffffffffb00000ULL #define VSID_64K 0x0800000000000000ULL diff --git a/arch/powerpc/include/asm/smu.h b/arch/powerpc/include/asm/smu.h index 6e909f3e6a4669..37d2da6feabf72 100644 --- a/arch/powerpc/include/asm/smu.h +++ b/arch/powerpc/include/asm/smu.h @@ -478,7 +478,7 @@ extern unsigned long smu_cmdbuf_abs; /* - * Kenrel asynchronous i2c interface + * Kernel asynchronous i2c interface */ #define SMU_I2C_READ_MAX 0x1d diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index b8e15c678960f3..308c5e15676b91 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -721,7 +721,7 @@ void __init early_init_devtree(void *params) */ of_scan_flat_dt(early_init_dt_scan_cpus, NULL); if (boot_cpuid < 0) { - printk("Failed to indentify boot CPU !\n"); + printk("Failed to identify boot CPU !\n"); BUG(); } diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 7c4f6690533a5e..7fd60dcb2cb0eb 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -124,7 +124,7 @@ static unsigned long ebb_switch_in(bool ebb, struct cpu_hw_events *cpuhw) static inline void power_pmu_bhrb_enable(struct perf_event *event) {} static inline void power_pmu_bhrb_disable(struct perf_event *event) {} -static void power_pmu_flush_branch_stack(void) {} +static void power_pmu_sched_task(struct perf_event_context *ctx, bool sched_in) {} static inline void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw) {} static void pmao_restore_workaround(bool ebb) { } #endif /* CONFIG_PPC32 */ @@ -350,6 +350,7 @@ static void power_pmu_bhrb_enable(struct perf_event *event) cpuhw->bhrb_context = event->ctx; } cpuhw->bhrb_users++; + perf_sched_cb_inc(event->ctx->pmu); } static void power_pmu_bhrb_disable(struct perf_event *event) @@ -361,6 +362,7 @@ static void power_pmu_bhrb_disable(struct perf_event *event) cpuhw->bhrb_users--; WARN_ON_ONCE(cpuhw->bhrb_users < 0); + perf_sched_cb_dec(event->ctx->pmu); if (!cpuhw->disabled && !cpuhw->bhrb_users) { /* BHRB cannot be turned off when other @@ -375,9 +377,12 @@ static void power_pmu_bhrb_disable(struct perf_event *event) /* Called from ctxsw to prevent one process's branch entries to * mingle with the other process's entries during context switch. */ -static void power_pmu_flush_branch_stack(void) +static void power_pmu_sched_task(struct perf_event_context *ctx, bool sched_in) { - if (ppmu->bhrb_nr) + if (!ppmu->bhrb_nr) + return; + + if (sched_in) power_pmu_bhrb_reset(); } /* Calculate the to address for a branch */ @@ -1901,7 +1906,7 @@ static struct pmu power_pmu = { .cancel_txn = power_pmu_cancel_txn, .commit_txn = power_pmu_commit_txn, .event_idx = power_pmu_event_idx, - .flush_branch_stack = power_pmu_flush_branch_stack, + .sched_task = power_pmu_sched_task, }; /* diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index 9445a824819ea7..abeb9ec0d11789 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -1126,7 +1126,7 @@ static int h_24x7_event_init(struct perf_event *event) /* Physical domains & other lpars require extra capabilities */ if (!caps.collect_privileged && (is_physical_domain(domain) || (event_get_lpar(event) != event_get_lpar_max()))) { - pr_devel("hv permisions disallow: is_physical_domain:%d, lpar=0x%llx\n", + pr_devel("hv permissions disallow: is_physical_domain:%d, lpar=0x%llx\n", is_physical_domain(domain), event_get_lpar(event)); return -EACCES; diff --git a/arch/powerpc/platforms/85xx/p1022_rdk.c b/arch/powerpc/platforms/85xx/p1022_rdk.c index 7a180f0308d55a..680232d6ba481c 100644 --- a/arch/powerpc/platforms/85xx/p1022_rdk.c +++ b/arch/powerpc/platforms/85xx/p1022_rdk.c @@ -50,14 +50,14 @@ void p1022rdk_set_pixel_clock(unsigned int pixclock) /* Map the global utilities registers. */ guts_np = of_find_compatible_node(NULL, NULL, "fsl,p1022-guts"); if (!guts_np) { - pr_err("p1022rdk: missing global utilties device node\n"); + pr_err("p1022rdk: missing global utilities device node\n"); return; } guts = of_iomap(guts_np, 0); of_node_put(guts_np); if (!guts) { - pr_err("p1022rdk: could not map global utilties device\n"); + pr_err("p1022rdk: could not map global utilities device\n"); return; } diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index ac2b75d74cd29d..6321fd8bf813b4 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -134,6 +134,7 @@ config S390 select HAVE_KPROBES select HAVE_KRETPROBES select HAVE_KVM if 64BIT + select HAVE_LIVEPATCH select HAVE_MEMBLOCK select HAVE_MEMBLOCK_NODE_MAP select HAVE_MEMBLOCK_PHYS_MAP @@ -165,6 +166,8 @@ source "init/Kconfig" source "kernel/Kconfig.freezer" +source "kernel/livepatch/Kconfig" + menu "Processor type and features" config HAVE_MARCH_Z900_FEATURES diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c index 99824ff8dd354e..df7d8cbee377a2 100644 --- a/arch/s390/hypfs/inode.c +++ b/arch/s390/hypfs/inode.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include "hypfs.h" diff --git a/arch/s390/include/asm/livepatch.h b/arch/s390/include/asm/livepatch.h new file mode 100644 index 00000000000000..7aa799134a1175 --- /dev/null +++ b/arch/s390/include/asm/livepatch.h @@ -0,0 +1,43 @@ +/* + * livepatch.h - s390-specific Kernel Live Patching Core + * + * Copyright (c) 2013-2015 SUSE + * Authors: Jiri Kosina + * Vojtech Pavlik + * Jiri Slaby + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#ifndef ASM_LIVEPATCH_H +#define ASM_LIVEPATCH_H + +#include + +#ifdef CONFIG_LIVEPATCH +static inline int klp_check_compiler_support(void) +{ + return 0; +} + +static inline int klp_write_module_reloc(struct module *mod, unsigned long + type, unsigned long loc, unsigned long value) +{ + /* not supported yet */ + return -ENOSYS; +} + +static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip) +{ + regs->psw.addr = ip; +} +#else +#error Live patching support is disabled; check CONFIG_LIVEPATCH +#endif + +#endif diff --git a/arch/s390/kvm/trace-s390.h b/arch/s390/kvm/trace-s390.h index 653a7ec09ef53a..3208d33a48cb62 100644 --- a/arch/s390/kvm/trace-s390.h +++ b/arch/s390/kvm/trace-s390.h @@ -9,6 +9,13 @@ #undef TRACE_INCLUDE_FILE #define TRACE_INCLUDE_FILE trace-s390 +/* + * The TRACE_SYSTEM_VAR defaults to TRACE_SYSTEM, but must be a + * legitimate C variable. It is not exported to user space. + */ +#undef TRACE_SYSTEM_VAR +#define TRACE_SYSTEM_VAR kvm_s390 + /* * Trace point for the creation of the kvm instance. */ diff --git a/arch/sh/kernel/signal_32.c b/arch/sh/kernel/signal_32.c index 0b34f2a704fe1d..97292890b51bc4 100644 --- a/arch/sh/kernel/signal_32.c +++ b/arch/sh/kernel/signal_32.c @@ -329,8 +329,6 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, if (err) return -EFAULT; - set_fs(USER_DS); - pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", current->comm, task_pid_nr(current), frame, regs->pc, regs->pr); @@ -408,8 +406,6 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, if (err) return -EFAULT; - set_fs(USER_DS); - pr_debug("SIG deliver (%s:%d): sp=%p pc=%08lx pr=%08lx\n", current->comm, task_pid_nr(current), frame, regs->pc, regs->pr); diff --git a/arch/sh/kernel/signal_64.c b/arch/sh/kernel/signal_64.c index 71993c6a7d94b0..0462995d4d7f66 100644 --- a/arch/sh/kernel/signal_64.c +++ b/arch/sh/kernel/signal_64.c @@ -457,8 +457,6 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs regs->pc = neff_sign_extend((unsigned long)ksig->ka.sa.sa_handler); - set_fs(USER_DS); - /* Broken %016Lx */ pr_debug("SIG deliver (#%d,%s:%d): sp=%p pc=%08Lx%08Lx link=%08Lx%08Lx\n", signal, current->comm, current->pid, frame, @@ -547,8 +545,6 @@ static int setup_rt_frame(struct ksignal *kig, sigset_t *set, regs->regs[REG_ARG3] = (unsigned long long)(unsigned long)(signed long)&frame->uc.uc_mcontext; regs->pc = neff_sign_extend((unsigned long)ksig->ka.sa.sa_handler); - set_fs(USER_DS); - pr_debug("SIG deliver (#%d,%s:%d): sp=%p pc=%08Lx%08Lx link=%08Lx%08Lx\n", signal, current->comm, current->pid, frame, regs->pc >> 32, regs->pc & 0xffffffff, diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h index d2b12988d2ed19..bf2caa1dedc5a3 100644 --- a/arch/x86/include/asm/cpu.h +++ b/arch/x86/include/asm/cpu.h @@ -34,8 +34,6 @@ extern int _debug_hotplug_cpu(int cpu, int action); #endif #endif -DECLARE_PER_CPU(int, cpu_state); - int mwait_usable(const struct cpuinfo_x86 *); #endif /* _ASM_X86_CPU_H */ diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h index 854c04b3c9c264..7ee9b94d9921bf 100644 --- a/arch/x86/include/asm/cpufeature.h +++ b/arch/x86/include/asm/cpufeature.h @@ -12,7 +12,7 @@ #include #endif -#define NCAPINTS 11 /* N 32-bit words worth of info */ +#define NCAPINTS 13 /* N 32-bit words worth of info */ #define NBUGINTS 1 /* N 32-bit bug flags */ /* @@ -195,6 +195,7 @@ #define X86_FEATURE_HWP_ACT_WINDOW ( 7*32+ 12) /* Intel HWP_ACT_WINDOW */ #define X86_FEATURE_HWP_EPP ( 7*32+13) /* Intel HWP_EPP */ #define X86_FEATURE_HWP_PKG_REQ ( 7*32+14) /* Intel HWP_PKG_REQ */ +#define X86_FEATURE_INTEL_PT ( 7*32+15) /* Intel Processor Trace */ /* Virtualization flags: Linux defined, word 8 */ #define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */ @@ -226,6 +227,7 @@ #define X86_FEATURE_ERMS ( 9*32+ 9) /* Enhanced REP MOVSB/STOSB */ #define X86_FEATURE_INVPCID ( 9*32+10) /* Invalidate Processor Context ID */ #define X86_FEATURE_RTM ( 9*32+11) /* Restricted Transactional Memory */ +#define X86_FEATURE_CQM ( 9*32+12) /* Cache QoS Monitoring */ #define X86_FEATURE_MPX ( 9*32+14) /* Memory Protection Extension */ #define X86_FEATURE_AVX512F ( 9*32+16) /* AVX-512 Foundation */ #define X86_FEATURE_RDSEED ( 9*32+18) /* The RDSEED instruction */ @@ -244,6 +246,12 @@ #define X86_FEATURE_XGETBV1 (10*32+ 2) /* XGETBV with ECX = 1 */ #define X86_FEATURE_XSAVES (10*32+ 3) /* XSAVES/XRSTORS */ +/* Intel-defined CPU QoS Sub-leaf, CPUID level 0x0000000F:0 (edx), word 11 */ +#define X86_FEATURE_CQM_LLC (11*32+ 1) /* LLC QoS if 1 */ + +/* Intel-defined CPU QoS Sub-leaf, CPUID level 0x0000000F:1 (edx), word 12 */ +#define X86_FEATURE_CQM_OCCUP_LLC (12*32+ 0) /* LLC occupancy monitoring if 1 */ + /* * BUG word(s) */ diff --git a/arch/x86/include/asm/livepatch.h b/arch/x86/include/asm/livepatch.h index a455a53d789a12..2d29197bd2fbfb 100644 --- a/arch/x86/include/asm/livepatch.h +++ b/arch/x86/include/asm/livepatch.h @@ -32,8 +32,8 @@ static inline int klp_check_compiler_support(void) #endif return 0; } -extern int klp_write_module_reloc(struct module *mod, unsigned long type, - unsigned long loc, unsigned long value); +int klp_write_module_reloc(struct module *mod, unsigned long type, + unsigned long loc, unsigned long value); static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip) { diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index d2203b5d9538ff..23ba6765b718c7 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -109,6 +109,9 @@ struct cpuinfo_x86 { /* in KB - valid for CPUS which support this call: */ int x86_cache_size; int x86_cache_alignment; /* In bytes */ + /* Cache QoS architectural values: */ + int x86_cache_max_rmid; /* max index */ + int x86_cache_occ_scale; /* scale to bytes */ int x86_power; unsigned long loops_per_jiffy; /* cpuid returned max cores value: */ diff --git a/arch/x86/include/asm/smp.h b/arch/x86/include/asm/smp.h index 81d02fc7dafa52..17a8dced12daef 100644 --- a/arch/x86/include/asm/smp.h +++ b/arch/x86/include/asm/smp.h @@ -150,13 +150,13 @@ static inline void arch_send_call_function_ipi_mask(const struct cpumask *mask) } void cpu_disable_common(void); -void cpu_die_common(unsigned int cpu); void native_smp_prepare_boot_cpu(void); void native_smp_prepare_cpus(unsigned int max_cpus); void native_smp_cpus_done(unsigned int max_cpus); void common_cpu_up(unsigned int cpunum, struct task_struct *tidle); int native_cpu_up(unsigned int cpunum, struct task_struct *tidle); int native_cpu_disable(void); +int common_cpu_die(unsigned int cpu); void native_cpu_die(unsigned int cpu); void native_play_dead(void); void play_dead_common(void); diff --git a/arch/x86/include/uapi/asm/msr-index.h b/arch/x86/include/uapi/asm/msr-index.h index 3ce079136c11ff..1a4eae695ca8ac 100644 --- a/arch/x86/include/uapi/asm/msr-index.h +++ b/arch/x86/include/uapi/asm/msr-index.h @@ -74,6 +74,24 @@ #define MSR_IA32_PERF_CAPABILITIES 0x00000345 #define MSR_PEBS_LD_LAT_THRESHOLD 0x000003f6 +#define MSR_IA32_RTIT_CTL 0x00000570 +#define RTIT_CTL_TRACEEN BIT(0) +#define RTIT_CTL_OS BIT(2) +#define RTIT_CTL_USR BIT(3) +#define RTIT_CTL_CR3EN BIT(7) +#define RTIT_CTL_TOPA BIT(8) +#define RTIT_CTL_TSC_EN BIT(10) +#define RTIT_CTL_DISRETC BIT(11) +#define RTIT_CTL_BRANCH_EN BIT(13) +#define MSR_IA32_RTIT_STATUS 0x00000571 +#define RTIT_STATUS_CONTEXTEN BIT(1) +#define RTIT_STATUS_TRIGGEREN BIT(2) +#define RTIT_STATUS_ERROR BIT(4) +#define RTIT_STATUS_STOPPED BIT(5) +#define MSR_IA32_RTIT_CR3_MATCH 0x00000572 +#define MSR_IA32_RTIT_OUTPUT_BASE 0x00000560 +#define MSR_IA32_RTIT_OUTPUT_MASK 0x00000561 + #define MSR_MTRRfix64K_00000 0x00000250 #define MSR_MTRRfix16K_80000 0x00000258 #define MSR_MTRRfix16K_A0000 0x00000259 diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index 80091ae54c2b09..9bff6879883623 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile @@ -39,7 +39,8 @@ obj-$(CONFIG_CPU_SUP_AMD) += perf_event_amd_iommu.o endif obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_p6.o perf_event_knc.o perf_event_p4.o obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_lbr.o perf_event_intel_ds.o perf_event_intel.o -obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_rapl.o +obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_rapl.o perf_event_intel_cqm.o +obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_pt.o perf_event_intel_bts.o obj-$(CONFIG_PERF_EVENTS_INTEL_UNCORE) += perf_event_intel_uncore.o \ perf_event_intel_uncore_snb.o \ diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 3f70538012e2d6..a62cf04dac8ae9 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -646,6 +646,30 @@ void get_cpu_cap(struct cpuinfo_x86 *c) c->x86_capability[10] = eax; } + /* Additional Intel-defined flags: level 0x0000000F */ + if (c->cpuid_level >= 0x0000000F) { + u32 eax, ebx, ecx, edx; + + /* QoS sub-leaf, EAX=0Fh, ECX=0 */ + cpuid_count(0x0000000F, 0, &eax, &ebx, &ecx, &edx); + c->x86_capability[11] = edx; + if (cpu_has(c, X86_FEATURE_CQM_LLC)) { + /* will be overridden if occupancy monitoring exists */ + c->x86_cache_max_rmid = ebx; + + /* QoS sub-leaf, EAX=0Fh, ECX=1 */ + cpuid_count(0x0000000F, 1, &eax, &ebx, &ecx, &edx); + c->x86_capability[12] = edx; + if (cpu_has(c, X86_FEATURE_CQM_OCCUP_LLC)) { + c->x86_cache_max_rmid = ecx; + c->x86_cache_occ_scale = ebx; + } + } else { + c->x86_cache_max_rmid = -1; + c->x86_cache_occ_scale = -1; + } + } + /* AMD-defined flags: level 0x80000001 */ xlvl = cpuid_eax(0x80000000); c->extended_cpuid_level = xlvl; @@ -834,6 +858,20 @@ static void generic_identify(struct cpuinfo_x86 *c) detect_nopl(c); } +static void x86_init_cache_qos(struct cpuinfo_x86 *c) +{ + /* + * The heavy lifting of max_rmid and cache_occ_scale are handled + * in get_cpu_cap(). Here we just set the max_rmid for the boot_cpu + * in case CQM bits really aren't there in this CPU. + */ + if (c != &boot_cpu_data) { + boot_cpu_data.x86_cache_max_rmid = + min(boot_cpu_data.x86_cache_max_rmid, + c->x86_cache_max_rmid); + } +} + /* * This does the hard work of actually picking apart the CPU stuff... */ @@ -923,6 +961,7 @@ static void identify_cpu(struct cpuinfo_x86 *c) init_hypervisor(c); x86_init_rdrand(c); + x86_init_cache_qos(c); /* * Clear/Set all flags overriden by options, need do it diff --git a/arch/x86/kernel/cpu/intel_pt.h b/arch/x86/kernel/cpu/intel_pt.h new file mode 100644 index 00000000000000..1c338b0eba050d --- /dev/null +++ b/arch/x86/kernel/cpu/intel_pt.h @@ -0,0 +1,131 @@ +/* + * Intel(R) Processor Trace PMU driver for perf + * Copyright (c) 2013-2014, Intel Corporation. + * + * 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. + * + * Intel PT is specified in the Intel Architecture Instruction Set Extensions + * Programming Reference: + * http://software.intel.com/en-us/intel-isa-extensions + */ + +#ifndef __INTEL_PT_H__ +#define __INTEL_PT_H__ + +/* + * Single-entry ToPA: when this close to region boundary, switch + * buffers to avoid losing data. + */ +#define TOPA_PMI_MARGIN 512 + +/* + * Table of Physical Addresses bits + */ +enum topa_sz { + TOPA_4K = 0, + TOPA_8K, + TOPA_16K, + TOPA_32K, + TOPA_64K, + TOPA_128K, + TOPA_256K, + TOPA_512K, + TOPA_1MB, + TOPA_2MB, + TOPA_4MB, + TOPA_8MB, + TOPA_16MB, + TOPA_32MB, + TOPA_64MB, + TOPA_128MB, + TOPA_SZ_END, +}; + +static inline unsigned int sizes(enum topa_sz tsz) +{ + return 1 << (tsz + 12); +}; + +struct topa_entry { + u64 end : 1; + u64 rsvd0 : 1; + u64 intr : 1; + u64 rsvd1 : 1; + u64 stop : 1; + u64 rsvd2 : 1; + u64 size : 4; + u64 rsvd3 : 2; + u64 base : 36; + u64 rsvd4 : 16; +}; + +#define TOPA_SHIFT 12 +#define PT_CPUID_LEAVES 2 + +enum pt_capabilities { + PT_CAP_max_subleaf = 0, + PT_CAP_cr3_filtering, + PT_CAP_topa_output, + PT_CAP_topa_multiple_entries, + PT_CAP_payloads_lip, +}; + +struct pt_pmu { + struct pmu pmu; + u32 caps[4 * PT_CPUID_LEAVES]; +}; + +/** + * struct pt_buffer - buffer configuration; one buffer per task_struct or + * cpu, depending on perf event configuration + * @cpu: cpu for per-cpu allocation + * @tables: list of ToPA tables in this buffer + * @first: shorthand for first topa table + * @last: shorthand for last topa table + * @cur: current topa table + * @nr_pages: buffer size in pages + * @cur_idx: current output region's index within @cur table + * @output_off: offset within the current output region + * @data_size: running total of the amount of data in this buffer + * @lost: if data was lost/truncated + * @head: logical write offset inside the buffer + * @snapshot: if this is for a snapshot/overwrite counter + * @stop_pos: STOP topa entry in the buffer + * @intr_pos: INT topa entry in the buffer + * @data_pages: array of pages from perf + * @topa_index: table of topa entries indexed by page offset + */ +struct pt_buffer { + int cpu; + struct list_head tables; + struct topa *first, *last, *cur; + unsigned int cur_idx; + size_t output_off; + unsigned long nr_pages; + local_t data_size; + local_t lost; + local64_t head; + bool snapshot; + unsigned long stop_pos, intr_pos; + void **data_pages; + struct topa_entry *topa_index[0]; +}; + +/** + * struct pt - per-cpu pt context + * @handle: perf output handle + * @handle_nmi: do handle PT PMI on this cpu, there's an active event + */ +struct pt { + struct perf_output_handle handle; + int handle_nmi; +}; + +#endif /* __INTEL_PT_H__ */ diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index e2888a3ad1e3c3..87848ebe2bb79a 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -263,6 +263,14 @@ static void hw_perf_event_destroy(struct perf_event *event) } } +void hw_perf_lbr_event_destroy(struct perf_event *event) +{ + hw_perf_event_destroy(event); + + /* undo the lbr/bts event accounting */ + x86_del_exclusive(x86_lbr_exclusive_lbr); +} + static inline int x86_pmu_initialized(void) { return x86_pmu.handle_irq != NULL; @@ -302,6 +310,35 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event) return x86_pmu_extra_regs(val, event); } +/* + * Check if we can create event of a certain type (that no conflicting events + * are present). + */ +int x86_add_exclusive(unsigned int what) +{ + int ret = -EBUSY, i; + + if (atomic_inc_not_zero(&x86_pmu.lbr_exclusive[what])) + return 0; + + mutex_lock(&pmc_reserve_mutex); + for (i = 0; i < ARRAY_SIZE(x86_pmu.lbr_exclusive); i++) + if (i != what && atomic_read(&x86_pmu.lbr_exclusive[i])) + goto out; + + atomic_inc(&x86_pmu.lbr_exclusive[what]); + ret = 0; + +out: + mutex_unlock(&pmc_reserve_mutex); + return ret; +} + +void x86_del_exclusive(unsigned int what) +{ + atomic_dec(&x86_pmu.lbr_exclusive[what]); +} + int x86_setup_perfctr(struct perf_event *event) { struct perf_event_attr *attr = &event->attr; @@ -346,6 +383,12 @@ int x86_setup_perfctr(struct perf_event *event) /* BTS is currently only allowed for user-mode. */ if (!attr->exclude_kernel) return -EOPNOTSUPP; + + /* disallow bts if conflicting events are present */ + if (x86_add_exclusive(x86_lbr_exclusive_lbr)) + return -EBUSY; + + event->destroy = hw_perf_lbr_event_destroy; } hwc->config |= config; @@ -399,39 +442,41 @@ int x86_pmu_hw_config(struct perf_event *event) if (event->attr.precise_ip > precise) return -EOPNOTSUPP; - /* - * check that PEBS LBR correction does not conflict with - * whatever the user is asking with attr->branch_sample_type - */ - if (event->attr.precise_ip > 1 && - x86_pmu.intel_cap.pebs_format < 2) { - u64 *br_type = &event->attr.branch_sample_type; - - if (has_branch_stack(event)) { - if (!precise_br_compat(event)) - return -EOPNOTSUPP; - - /* branch_sample_type is compatible */ - - } else { - /* - * user did not specify branch_sample_type - * - * For PEBS fixups, we capture all - * the branches at the priv level of the - * event. - */ - *br_type = PERF_SAMPLE_BRANCH_ANY; - - if (!event->attr.exclude_user) - *br_type |= PERF_SAMPLE_BRANCH_USER; - - if (!event->attr.exclude_kernel) - *br_type |= PERF_SAMPLE_BRANCH_KERNEL; - } + } + /* + * check that PEBS LBR correction does not conflict with + * whatever the user is asking with attr->branch_sample_type + */ + if (event->attr.precise_ip > 1 && x86_pmu.intel_cap.pebs_format < 2) { + u64 *br_type = &event->attr.branch_sample_type; + + if (has_branch_stack(event)) { + if (!precise_br_compat(event)) + return -EOPNOTSUPP; + + /* branch_sample_type is compatible */ + + } else { + /* + * user did not specify branch_sample_type + * + * For PEBS fixups, we capture all + * the branches at the priv level of the + * event. + */ + *br_type = PERF_SAMPLE_BRANCH_ANY; + + if (!event->attr.exclude_user) + *br_type |= PERF_SAMPLE_BRANCH_USER; + + if (!event->attr.exclude_kernel) + *br_type |= PERF_SAMPLE_BRANCH_KERNEL; } } + if (event->attr.branch_sample_type & PERF_SAMPLE_BRANCH_CALL_STACK) + event->attach_state |= PERF_ATTACH_TASK_DATA; + /* * Generate PMC IRQs: * (keep 'enabled' bit clear for now) @@ -449,6 +494,12 @@ int x86_pmu_hw_config(struct perf_event *event) if (event->attr.type == PERF_TYPE_RAW) event->hw.config |= event->attr.config & X86_RAW_EVENT_MASK; + if (event->attr.sample_period && x86_pmu.limit_period) { + if (x86_pmu.limit_period(event, event->attr.sample_period) > + event->attr.sample_period) + return -EINVAL; + } + return x86_setup_perfctr(event); } @@ -728,14 +779,17 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) struct event_constraint *c; unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; struct perf_event *e; - int i, wmin, wmax, num = 0; + int i, wmin, wmax, unsched = 0; struct hw_perf_event *hwc; bitmap_zero(used_mask, X86_PMC_IDX_MAX); + if (x86_pmu.start_scheduling) + x86_pmu.start_scheduling(cpuc); + for (i = 0, wmin = X86_PMC_IDX_MAX, wmax = 0; i < n; i++) { hwc = &cpuc->event_list[i]->hw; - c = x86_pmu.get_event_constraints(cpuc, cpuc->event_list[i]); + c = x86_pmu.get_event_constraints(cpuc, i, cpuc->event_list[i]); hwc->constraint = c; wmin = min(wmin, c->weight); @@ -768,24 +822,30 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) /* slow path */ if (i != n) - num = perf_assign_events(cpuc->event_list, n, wmin, - wmax, assign); + unsched = perf_assign_events(cpuc->event_list, n, wmin, + wmax, assign); /* - * Mark the event as committed, so we do not put_constraint() - * in case new events are added and fail scheduling. + * In case of success (unsched = 0), mark events as committed, + * so we do not put_constraint() in case new events are added + * and fail to be scheduled + * + * We invoke the lower level commit callback to lock the resource + * + * We do not need to do all of this in case we are called to + * validate an event group (assign == NULL) */ - if (!num && assign) { + if (!unsched && assign) { for (i = 0; i < n; i++) { e = cpuc->event_list[i]; e->hw.flags |= PERF_X86_EVENT_COMMITTED; + if (x86_pmu.commit_scheduling) + x86_pmu.commit_scheduling(cpuc, e, assign[i]); } } - /* - * scheduling failed or is just a simulation, - * free resources if necessary - */ - if (!assign || num) { + + if (!assign || unsched) { + for (i = 0; i < n; i++) { e = cpuc->event_list[i]; /* @@ -795,11 +855,18 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign) if ((e->hw.flags & PERF_X86_EVENT_COMMITTED)) continue; + /* + * release events that failed scheduling + */ if (x86_pmu.put_event_constraints) x86_pmu.put_event_constraints(cpuc, e); } } - return num ? -EINVAL : 0; + + if (x86_pmu.stop_scheduling) + x86_pmu.stop_scheduling(cpuc); + + return unsched ? -EINVAL : 0; } /* @@ -986,6 +1053,9 @@ int x86_perf_event_set_period(struct perf_event *event) if (left > x86_pmu.max_period) left = x86_pmu.max_period; + if (x86_pmu.limit_period) + left = x86_pmu.limit_period(event, left); + per_cpu(pmc_prev_left[idx], smp_processor_id()) = left; /* @@ -1033,7 +1103,6 @@ static int x86_pmu_add(struct perf_event *event, int flags) hwc = &event->hw; - perf_pmu_disable(event->pmu); n0 = cpuc->n_events; ret = n = collect_events(cpuc, event, false); if (ret < 0) @@ -1071,7 +1140,6 @@ static int x86_pmu_add(struct perf_event *event, int flags) ret = 0; out: - perf_pmu_enable(event->pmu); return ret; } @@ -1103,7 +1171,7 @@ static void x86_pmu_start(struct perf_event *event, int flags) void perf_event_print_debug(void) { u64 ctrl, status, overflow, pmc_ctrl, pmc_count, prev_left, fixed; - u64 pebs; + u64 pebs, debugctl; struct cpu_hw_events *cpuc; unsigned long flags; int cpu, idx; @@ -1121,14 +1189,20 @@ void perf_event_print_debug(void) rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status); rdmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, overflow); rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR_CTRL, fixed); - rdmsrl(MSR_IA32_PEBS_ENABLE, pebs); pr_info("\n"); pr_info("CPU#%d: ctrl: %016llx\n", cpu, ctrl); pr_info("CPU#%d: status: %016llx\n", cpu, status); pr_info("CPU#%d: overflow: %016llx\n", cpu, overflow); pr_info("CPU#%d: fixed: %016llx\n", cpu, fixed); - pr_info("CPU#%d: pebs: %016llx\n", cpu, pebs); + if (x86_pmu.pebs_constraints) { + rdmsrl(MSR_IA32_PEBS_ENABLE, pebs); + pr_info("CPU#%d: pebs: %016llx\n", cpu, pebs); + } + if (x86_pmu.lbr_nr) { + rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); + pr_info("CPU#%d: debugctl: %016llx\n", cpu, debugctl); + } } pr_info("CPU#%d: active: %016llx\n", cpu, *(u64 *)cpuc->active_mask); @@ -1321,11 +1395,12 @@ x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) { unsigned int cpu = (long)hcpu; struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); - int ret = NOTIFY_OK; + int i, ret = NOTIFY_OK; switch (action & ~CPU_TASKS_FROZEN) { case CPU_UP_PREPARE: - cpuc->kfree_on_online = NULL; + for (i = 0 ; i < X86_PERF_KFREE_MAX; i++) + cpuc->kfree_on_online[i] = NULL; if (x86_pmu.cpu_prepare) ret = x86_pmu.cpu_prepare(cpu); break; @@ -1336,7 +1411,10 @@ x86_pmu_notifier(struct notifier_block *self, unsigned long action, void *hcpu) break; case CPU_ONLINE: - kfree(cpuc->kfree_on_online); + for (i = 0 ; i < X86_PERF_KFREE_MAX; i++) { + kfree(cpuc->kfree_on_online[i]); + cpuc->kfree_on_online[i] = NULL; + } break; case CPU_DYING: @@ -1712,7 +1790,7 @@ static int validate_event(struct perf_event *event) if (IS_ERR(fake_cpuc)) return PTR_ERR(fake_cpuc); - c = x86_pmu.get_event_constraints(fake_cpuc, event); + c = x86_pmu.get_event_constraints(fake_cpuc, -1, event); if (!c || !c->weight) ret = -EINVAL; @@ -1914,10 +1992,10 @@ static const struct attribute_group *x86_pmu_attr_groups[] = { NULL, }; -static void x86_pmu_flush_branch_stack(void) +static void x86_pmu_sched_task(struct perf_event_context *ctx, bool sched_in) { - if (x86_pmu.flush_branch_stack) - x86_pmu.flush_branch_stack(); + if (x86_pmu.sched_task) + x86_pmu.sched_task(ctx, sched_in); } void perf_check_microcode(void) @@ -1949,7 +2027,8 @@ static struct pmu pmu = { .commit_txn = x86_pmu_commit_txn, .event_idx = x86_pmu_event_idx, - .flush_branch_stack = x86_pmu_flush_branch_stack, + .sched_task = x86_pmu_sched_task, + .task_ctx_size = sizeof(struct x86_perf_task_context), }; void arch_perf_update_userpage(struct perf_event *event, @@ -1968,13 +2047,23 @@ void arch_perf_update_userpage(struct perf_event *event, data = cyc2ns_read_begin(); + /* + * Internal timekeeping for enabled/running/stopped times + * is always in the local_clock domain. + */ userpg->cap_user_time = 1; userpg->time_mult = data->cyc2ns_mul; userpg->time_shift = data->cyc2ns_shift; userpg->time_offset = data->cyc2ns_offset - now; - userpg->cap_user_time_zero = 1; - userpg->time_zero = data->cyc2ns_offset; + /* + * cap_user_time_zero doesn't make sense when we're using a different + * time base for the records. + */ + if (event->clock == &local_clock) { + userpg->cap_user_time_zero = 1; + userpg->time_zero = data->cyc2ns_offset; + } cyc2ns_read_end(data); } diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index df525d2be1e814..329f0356ad4a0a 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h @@ -71,6 +71,8 @@ struct event_constraint { #define PERF_X86_EVENT_COMMITTED 0x8 /* event passed commit_txn */ #define PERF_X86_EVENT_PEBS_LD_HSW 0x10 /* haswell style datala, load */ #define PERF_X86_EVENT_PEBS_NA_HSW 0x20 /* haswell style datala, unknown */ +#define PERF_X86_EVENT_EXCL 0x40 /* HT exclusivity on counter */ +#define PERF_X86_EVENT_DYNAMIC 0x80 /* dynamic alloc'd constraint */ #define PERF_X86_EVENT_RDPMC_ALLOWED 0x40 /* grant rdpmc permission */ @@ -123,8 +125,37 @@ struct intel_shared_regs { unsigned core_id; /* per-core: core id */ }; +enum intel_excl_state_type { + INTEL_EXCL_UNUSED = 0, /* counter is unused */ + INTEL_EXCL_SHARED = 1, /* counter can be used by both threads */ + INTEL_EXCL_EXCLUSIVE = 2, /* counter can be used by one thread only */ +}; + +struct intel_excl_states { + enum intel_excl_state_type init_state[X86_PMC_IDX_MAX]; + enum intel_excl_state_type state[X86_PMC_IDX_MAX]; + int num_alloc_cntrs;/* #counters allocated */ + int max_alloc_cntrs;/* max #counters allowed */ + bool sched_started; /* true if scheduling has started */ +}; + +struct intel_excl_cntrs { + raw_spinlock_t lock; + + struct intel_excl_states states[2]; + + int refcnt; /* per-core: #HT threads */ + unsigned core_id; /* per-core: core id */ +}; + #define MAX_LBR_ENTRIES 16 +enum { + X86_PERF_KFREE_SHARED = 0, + X86_PERF_KFREE_EXCL = 1, + X86_PERF_KFREE_MAX +}; + struct cpu_hw_events { /* * Generic x86 PMC bits @@ -179,6 +210,12 @@ struct cpu_hw_events { * used on Intel NHM/WSM/SNB */ struct intel_shared_regs *shared_regs; + /* + * manage exclusive counter access between hyperthread + */ + struct event_constraint *constraint_list; /* in enable order */ + struct intel_excl_cntrs *excl_cntrs; + int excl_thread_id; /* 0 or 1 */ /* * AMD specific bits @@ -187,7 +224,7 @@ struct cpu_hw_events { /* Inverted mask of bits to clear in the perf_ctr ctrl registers */ u64 perf_ctr_virt_mask; - void *kfree_on_online; + void *kfree_on_online[X86_PERF_KFREE_MAX]; }; #define __EVENT_CONSTRAINT(c, n, m, w, o, f) {\ @@ -202,6 +239,10 @@ struct cpu_hw_events { #define EVENT_CONSTRAINT(c, n, m) \ __EVENT_CONSTRAINT(c, n, m, HWEIGHT(n), 0, 0) +#define INTEL_EXCLEVT_CONSTRAINT(c, n) \ + __EVENT_CONSTRAINT(c, n, ARCH_PERFMON_EVENTSEL_EVENT, HWEIGHT(n),\ + 0, PERF_X86_EVENT_EXCL) + /* * The overlap flag marks event constraints with overlapping counter * masks. This is the case if the counter mask of such an event is not @@ -259,6 +300,10 @@ struct cpu_hw_events { #define INTEL_FLAGS_UEVENT_CONSTRAINT(c, n) \ EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS) +#define INTEL_EXCLUEVT_CONSTRAINT(c, n) \ + __EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK, \ + HWEIGHT(n), 0, PERF_X86_EVENT_EXCL) + #define INTEL_PLD_CONSTRAINT(c, n) \ __EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \ HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LDLAT) @@ -283,22 +328,40 @@ struct cpu_hw_events { /* Check flags and event code, and set the HSW load flag */ #define INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD(code, n) \ - __EVENT_CONSTRAINT(code, n, \ + __EVENT_CONSTRAINT(code, n, \ ARCH_PERFMON_EVENTSEL_EVENT|X86_ALL_EVENT_FLAGS, \ HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LD_HSW) +#define INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_XLD(code, n) \ + __EVENT_CONSTRAINT(code, n, \ + ARCH_PERFMON_EVENTSEL_EVENT|X86_ALL_EVENT_FLAGS, \ + HWEIGHT(n), 0, \ + PERF_X86_EVENT_PEBS_LD_HSW|PERF_X86_EVENT_EXCL) + /* Check flags and event code/umask, and set the HSW store flag */ #define INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(code, n) \ __EVENT_CONSTRAINT(code, n, \ INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \ HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_ST_HSW) +#define INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XST(code, n) \ + __EVENT_CONSTRAINT(code, n, \ + INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \ + HWEIGHT(n), 0, \ + PERF_X86_EVENT_PEBS_ST_HSW|PERF_X86_EVENT_EXCL) + /* Check flags and event code/umask, and set the HSW load flag */ #define INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(code, n) \ __EVENT_CONSTRAINT(code, n, \ INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \ HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LD_HSW) +#define INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XLD(code, n) \ + __EVENT_CONSTRAINT(code, n, \ + INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS, \ + HWEIGHT(n), 0, \ + PERF_X86_EVENT_PEBS_LD_HSW|PERF_X86_EVENT_EXCL) + /* Check flags and event code/umask, and set the HSW N/A flag */ #define INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_NA(code, n) \ __EVENT_CONSTRAINT(code, n, \ @@ -408,6 +471,13 @@ union x86_pmu_config { #define X86_CONFIG(args...) ((union x86_pmu_config){.bits = {args}}).value +enum { + x86_lbr_exclusive_lbr, + x86_lbr_exclusive_bts, + x86_lbr_exclusive_pt, + x86_lbr_exclusive_max, +}; + /* * struct x86_pmu - generic x86 pmu */ @@ -443,14 +513,25 @@ struct x86_pmu { u64 max_period; struct event_constraint * (*get_event_constraints)(struct cpu_hw_events *cpuc, + int idx, struct perf_event *event); void (*put_event_constraints)(struct cpu_hw_events *cpuc, struct perf_event *event); + + void (*commit_scheduling)(struct cpu_hw_events *cpuc, + struct perf_event *event, + int cntr); + + void (*start_scheduling)(struct cpu_hw_events *cpuc); + + void (*stop_scheduling)(struct cpu_hw_events *cpuc); + struct event_constraint *event_constraints; struct x86_pmu_quirk *quirks; int perfctr_second_write; bool late_ack; + unsigned (*limit_period)(struct perf_event *event, unsigned l); /* * sysfs attrs @@ -472,7 +553,8 @@ struct x86_pmu { void (*cpu_dead)(int cpu); void (*check_microcode)(void); - void (*flush_branch_stack)(void); + void (*sched_task)(struct perf_event_context *ctx, + bool sched_in); /* * Intel Arch Perfmon v2+ @@ -503,11 +585,16 @@ struct x86_pmu { const int *lbr_sel_map; /* lbr_select mappings */ bool lbr_double_abort; /* duplicated lbr aborts */ + /* + * Intel PT/LBR/BTS are exclusive + */ + atomic_t lbr_exclusive[x86_lbr_exclusive_max]; + /* * Extra registers for events */ struct extra_reg *extra_regs; - unsigned int er_flags; + unsigned int flags; /* * Intel host/guest support (KVM) @@ -515,6 +602,13 @@ struct x86_pmu { struct perf_guest_switch_msr *(*guest_get_msrs)(int *nr); }; +struct x86_perf_task_context { + u64 lbr_from[MAX_LBR_ENTRIES]; + u64 lbr_to[MAX_LBR_ENTRIES]; + int lbr_callstack_users; + int lbr_stack_state; +}; + #define x86_add_quirk(func_) \ do { \ static struct x86_pmu_quirk __quirk __initdata = { \ @@ -524,8 +618,13 @@ do { \ x86_pmu.quirks = &__quirk; \ } while (0) -#define ERF_NO_HT_SHARING 1 -#define ERF_HAS_RSP_1 2 +/* + * x86_pmu flags + */ +#define PMU_FL_NO_HT_SHARING 0x1 /* no hyper-threading resource sharing */ +#define PMU_FL_HAS_RSP_1 0x2 /* has 2 equivalent offcore_rsp regs */ +#define PMU_FL_EXCL_CNTRS 0x4 /* has exclusive counter requirements */ +#define PMU_FL_EXCL_ENABLED 0x8 /* exclusive counter active */ #define EVENT_VAR(_id) event_attr_##_id #define EVENT_PTR(_id) &event_attr_##_id.attr.attr @@ -546,6 +645,12 @@ static struct perf_pmu_events_attr event_attr_##v = { \ extern struct x86_pmu x86_pmu __read_mostly; +static inline bool x86_pmu_has_lbr_callstack(void) +{ + return x86_pmu.lbr_sel_map && + x86_pmu.lbr_sel_map[PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT] > 0; +} + DECLARE_PER_CPU(struct cpu_hw_events, cpu_hw_events); int x86_perf_event_set_period(struct perf_event *event); @@ -588,6 +693,12 @@ static inline int x86_pmu_rdpmc_index(int index) return x86_pmu.rdpmc_index ? x86_pmu.rdpmc_index(index) : index; } +int x86_add_exclusive(unsigned int what); + +void x86_del_exclusive(unsigned int what); + +void hw_perf_lbr_event_destroy(struct perf_event *event); + int x86_setup_perfctr(struct perf_event *event); int x86_pmu_hw_config(struct perf_event *event); @@ -674,10 +785,34 @@ static inline int amd_pmu_init(void) #ifdef CONFIG_CPU_SUP_INTEL +static inline bool intel_pmu_needs_lbr_smpl(struct perf_event *event) +{ + /* user explicitly requested branch sampling */ + if (has_branch_stack(event)) + return true; + + /* implicit branch sampling to correct PEBS skid */ + if (x86_pmu.intel_cap.pebs_trap && event->attr.precise_ip > 1 && + x86_pmu.intel_cap.pebs_format < 2) + return true; + + return false; +} + +static inline bool intel_pmu_has_bts(struct perf_event *event) +{ + if (event->attr.config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS && + !event->attr.freq && event->hw.sample_period == 1) + return true; + + return false; +} + int intel_pmu_save_and_restart(struct perf_event *event); struct event_constraint * -x86_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event); +x86_get_event_constraints(struct cpu_hw_events *cpuc, int idx, + struct perf_event *event); struct intel_shared_regs *allocate_shared_regs(int cpu); @@ -727,13 +862,15 @@ void intel_pmu_pebs_disable_all(void); void intel_ds_init(void); +void intel_pmu_lbr_sched_task(struct perf_event_context *ctx, bool sched_in); + void intel_pmu_lbr_reset(void); void intel_pmu_lbr_enable(struct perf_event *event); void intel_pmu_lbr_disable(struct perf_event *event); -void intel_pmu_lbr_enable_all(void); +void intel_pmu_lbr_enable_all(bool pmi); void intel_pmu_lbr_disable_all(void); @@ -747,8 +884,18 @@ void intel_pmu_lbr_init_atom(void); void intel_pmu_lbr_init_snb(void); +void intel_pmu_lbr_init_hsw(void); + int intel_pmu_setup_lbr_filter(struct perf_event *event); +void intel_pt_interrupt(void); + +int intel_bts_interrupt(void); + +void intel_bts_enable_local(void); + +void intel_bts_disable_local(void); + int p4_pmu_init(void); int p6_pmu_init(void); @@ -758,6 +905,10 @@ int knc_pmu_init(void); ssize_t events_sysfs_show(struct device *dev, struct device_attribute *attr, char *page); +static inline int is_ht_workaround_enabled(void) +{ + return !!(x86_pmu.flags & PMU_FL_EXCL_ENABLED); +} #else /* CONFIG_CPU_SUP_INTEL */ static inline void reserve_ds_buffers(void) diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c index 28926311aac183..1cee5d2d7eceaf 100644 --- a/arch/x86/kernel/cpu/perf_event_amd.c +++ b/arch/x86/kernel/cpu/perf_event_amd.c @@ -382,6 +382,7 @@ static int amd_pmu_cpu_prepare(int cpu) static void amd_pmu_cpu_starting(int cpu) { struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); + void **onln = &cpuc->kfree_on_online[X86_PERF_KFREE_SHARED]; struct amd_nb *nb; int i, nb_id; @@ -399,7 +400,7 @@ static void amd_pmu_cpu_starting(int cpu) continue; if (nb->nb_id == nb_id) { - cpuc->kfree_on_online = cpuc->amd_nb; + *onln = cpuc->amd_nb; cpuc->amd_nb = nb; break; } @@ -429,7 +430,8 @@ static void amd_pmu_cpu_dead(int cpu) } static struct event_constraint * -amd_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) +amd_get_event_constraints(struct cpu_hw_events *cpuc, int idx, + struct perf_event *event) { /* * if not NB event or no NB, then no constraints @@ -537,7 +539,8 @@ static struct event_constraint amd_f15_PMC50 = EVENT_CONSTRAINT(0, 0x3F, 0); static struct event_constraint amd_f15_PMC53 = EVENT_CONSTRAINT(0, 0x38, 0); static struct event_constraint * -amd_get_event_constraints_f15h(struct cpu_hw_events *cpuc, struct perf_event *event) +amd_get_event_constraints_f15h(struct cpu_hw_events *cpuc, int idx, + struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; unsigned int event_code = amd_get_event_code(hwc); diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c index a61f5c6911daac..989d3c215d2bce 100644 --- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c +++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c @@ -796,7 +796,7 @@ static int setup_ibs_ctl(int ibs_eilvt_off) * the IBS interrupt vector is handled by perf_ibs_cpu_notifier that * is using the new offset. */ -static int force_ibs_eilvt_setup(void) +static void force_ibs_eilvt_setup(void) { int offset; int ret; @@ -811,26 +811,24 @@ static int force_ibs_eilvt_setup(void) if (offset == APIC_EILVT_NR_MAX) { printk(KERN_DEBUG "No EILVT entry available\n"); - return -EBUSY; + return; } ret = setup_ibs_ctl(offset); if (ret) goto out; - if (!ibs_eilvt_valid()) { - ret = -EFAULT; + if (!ibs_eilvt_valid()) goto out; - } pr_info("IBS: LVT offset %d assigned\n", offset); - return 0; + return; out: preempt_disable(); put_eilvt(offset); preempt_enable(); - return ret; + return; } static void ibs_eilvt_setup(void) diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 258990688a5e99..9da2400c2ec37b 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -113,6 +114,12 @@ static struct event_constraint intel_snb_event_constraints[] __read_mostly = INTEL_EVENT_CONSTRAINT(0xcd, 0x8), /* MEM_TRANS_RETIRED.LOAD_LATENCY */ INTEL_UEVENT_CONSTRAINT(0x04a3, 0xf), /* CYCLE_ACTIVITY.CYCLES_NO_DISPATCH */ INTEL_UEVENT_CONSTRAINT(0x02a3, 0x4), /* CYCLE_ACTIVITY.CYCLES_L1D_PENDING */ + + INTEL_EXCLEVT_CONSTRAINT(0xd0, 0xf), /* MEM_UOPS_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd3, 0xf), /* MEM_LOAD_UOPS_LLC_MISS_RETIRED.* */ + EVENT_CONSTRAINT_END }; @@ -131,15 +138,12 @@ static struct event_constraint intel_ivb_event_constraints[] __read_mostly = INTEL_UEVENT_CONSTRAINT(0x08a3, 0x4), /* CYCLE_ACTIVITY.CYCLES_L1D_PENDING */ INTEL_UEVENT_CONSTRAINT(0x0ca3, 0x4), /* CYCLE_ACTIVITY.STALLS_L1D_PENDING */ INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PREC_DIST */ - /* - * Errata BV98 -- MEM_*_RETIRED events can leak between counters of SMT - * siblings; disable these events because they can corrupt unrelated - * counters. - */ - INTEL_EVENT_CONSTRAINT(0xd0, 0x0), /* MEM_UOPS_RETIRED.* */ - INTEL_EVENT_CONSTRAINT(0xd1, 0x0), /* MEM_LOAD_UOPS_RETIRED.* */ - INTEL_EVENT_CONSTRAINT(0xd2, 0x0), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */ - INTEL_EVENT_CONSTRAINT(0xd3, 0x0), /* MEM_LOAD_UOPS_LLC_MISS_RETIRED.* */ + + INTEL_EXCLEVT_CONSTRAINT(0xd0, 0xf), /* MEM_UOPS_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd3, 0xf), /* MEM_LOAD_UOPS_LLC_MISS_RETIRED.* */ + EVENT_CONSTRAINT_END }; @@ -217,6 +221,21 @@ static struct event_constraint intel_hsw_event_constraints[] = { INTEL_UEVENT_CONSTRAINT(0x0ca3, 0x4), /* CYCLE_ACTIVITY.CYCLES_NO_EXECUTE */ INTEL_UEVENT_CONSTRAINT(0x04a3, 0xf), + + INTEL_EXCLEVT_CONSTRAINT(0xd0, 0xf), /* MEM_UOPS_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd3, 0xf), /* MEM_LOAD_UOPS_LLC_MISS_RETIRED.* */ + + EVENT_CONSTRAINT_END +}; + +struct event_constraint intel_bdw_event_constraints[] = { + FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */ + FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */ + FIXED_EVENT_CONSTRAINT(0x0300, 2), /* CPU_CLK_UNHALTED.REF */ + INTEL_UEVENT_CONSTRAINT(0x148, 0x4), /* L1D_PEND_MISS.PENDING */ + INTEL_EVENT_CONSTRAINT(0xa3, 0x4), /* CYCLE_ACTIVITY.* */ EVENT_CONSTRAINT_END }; @@ -415,6 +434,202 @@ static __initconst const u64 snb_hw_cache_event_ids }; +/* + * Notes on the events: + * - data reads do not include code reads (comparable to earlier tables) + * - data counts include speculative execution (except L1 write, dtlb, bpu) + * - remote node access includes remote memory, remote cache, remote mmio. + * - prefetches are not included in the counts because they are not + * reliably counted. + */ + +#define HSW_DEMAND_DATA_RD BIT_ULL(0) +#define HSW_DEMAND_RFO BIT_ULL(1) +#define HSW_ANY_RESPONSE BIT_ULL(16) +#define HSW_SUPPLIER_NONE BIT_ULL(17) +#define HSW_L3_MISS_LOCAL_DRAM BIT_ULL(22) +#define HSW_L3_MISS_REMOTE_HOP0 BIT_ULL(27) +#define HSW_L3_MISS_REMOTE_HOP1 BIT_ULL(28) +#define HSW_L3_MISS_REMOTE_HOP2P BIT_ULL(29) +#define HSW_L3_MISS (HSW_L3_MISS_LOCAL_DRAM| \ + HSW_L3_MISS_REMOTE_HOP0|HSW_L3_MISS_REMOTE_HOP1| \ + HSW_L3_MISS_REMOTE_HOP2P) +#define HSW_SNOOP_NONE BIT_ULL(31) +#define HSW_SNOOP_NOT_NEEDED BIT_ULL(32) +#define HSW_SNOOP_MISS BIT_ULL(33) +#define HSW_SNOOP_HIT_NO_FWD BIT_ULL(34) +#define HSW_SNOOP_HIT_WITH_FWD BIT_ULL(35) +#define HSW_SNOOP_HITM BIT_ULL(36) +#define HSW_SNOOP_NON_DRAM BIT_ULL(37) +#define HSW_ANY_SNOOP (HSW_SNOOP_NONE| \ + HSW_SNOOP_NOT_NEEDED|HSW_SNOOP_MISS| \ + HSW_SNOOP_HIT_NO_FWD|HSW_SNOOP_HIT_WITH_FWD| \ + HSW_SNOOP_HITM|HSW_SNOOP_NON_DRAM) +#define HSW_SNOOP_DRAM (HSW_ANY_SNOOP & ~HSW_SNOOP_NON_DRAM) +#define HSW_DEMAND_READ HSW_DEMAND_DATA_RD +#define HSW_DEMAND_WRITE HSW_DEMAND_RFO +#define HSW_L3_MISS_REMOTE (HSW_L3_MISS_REMOTE_HOP0|\ + HSW_L3_MISS_REMOTE_HOP1|HSW_L3_MISS_REMOTE_HOP2P) +#define HSW_LLC_ACCESS HSW_ANY_RESPONSE + +#define BDW_L3_MISS_LOCAL BIT(26) +#define BDW_L3_MISS (BDW_L3_MISS_LOCAL| \ + HSW_L3_MISS_REMOTE_HOP0|HSW_L3_MISS_REMOTE_HOP1| \ + HSW_L3_MISS_REMOTE_HOP2P) + + +static __initconst const u64 hsw_hw_cache_event_ids + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX] = +{ + [ C(L1D ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x81d0, /* MEM_UOPS_RETIRED.ALL_LOADS */ + [ C(RESULT_MISS) ] = 0x151, /* L1D.REPLACEMENT */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x82d0, /* MEM_UOPS_RETIRED.ALL_STORES */ + [ C(RESULT_MISS) ] = 0x0, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + [ C(RESULT_MISS) ] = 0x0, + }, + }, + [ C(L1I ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + [ C(RESULT_MISS) ] = 0x280, /* ICACHE.MISSES */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + [ C(RESULT_MISS) ] = 0x0, + }, + }, + [ C(LL ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x1b7, /* OFFCORE_RESPONSE */ + [ C(RESULT_MISS) ] = 0x1b7, /* OFFCORE_RESPONSE */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x1b7, /* OFFCORE_RESPONSE */ + [ C(RESULT_MISS) ] = 0x1b7, /* OFFCORE_RESPONSE */ + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + [ C(RESULT_MISS) ] = 0x0, + }, + }, + [ C(DTLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x81d0, /* MEM_UOPS_RETIRED.ALL_LOADS */ + [ C(RESULT_MISS) ] = 0x108, /* DTLB_LOAD_MISSES.MISS_CAUSES_A_WALK */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x82d0, /* MEM_UOPS_RETIRED.ALL_STORES */ + [ C(RESULT_MISS) ] = 0x149, /* DTLB_STORE_MISSES.MISS_CAUSES_A_WALK */ + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + [ C(RESULT_MISS) ] = 0x0, + }, + }, + [ C(ITLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x6085, /* ITLB_MISSES.STLB_HIT */ + [ C(RESULT_MISS) ] = 0x185, /* ITLB_MISSES.MISS_CAUSES_A_WALK */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, + [ C(BPU ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0xc4, /* BR_INST_RETIRED.ALL_BRANCHES */ + [ C(RESULT_MISS) ] = 0xc5, /* BR_MISP_RETIRED.ALL_BRANCHES */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, + [ C(NODE) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x1b7, /* OFFCORE_RESPONSE */ + [ C(RESULT_MISS) ] = 0x1b7, /* OFFCORE_RESPONSE */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x1b7, /* OFFCORE_RESPONSE */ + [ C(RESULT_MISS) ] = 0x1b7, /* OFFCORE_RESPONSE */ + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + [ C(RESULT_MISS) ] = 0x0, + }, + }, +}; + +static __initconst const u64 hsw_hw_cache_extra_regs + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX] = +{ + [ C(LL ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = HSW_DEMAND_READ| + HSW_LLC_ACCESS, + [ C(RESULT_MISS) ] = HSW_DEMAND_READ| + HSW_L3_MISS|HSW_ANY_SNOOP, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = HSW_DEMAND_WRITE| + HSW_LLC_ACCESS, + [ C(RESULT_MISS) ] = HSW_DEMAND_WRITE| + HSW_L3_MISS|HSW_ANY_SNOOP, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + [ C(RESULT_MISS) ] = 0x0, + }, + }, + [ C(NODE) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = HSW_DEMAND_READ| + HSW_L3_MISS_LOCAL_DRAM| + HSW_SNOOP_DRAM, + [ C(RESULT_MISS) ] = HSW_DEMAND_READ| + HSW_L3_MISS_REMOTE| + HSW_SNOOP_DRAM, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = HSW_DEMAND_WRITE| + HSW_L3_MISS_LOCAL_DRAM| + HSW_SNOOP_DRAM, + [ C(RESULT_MISS) ] = HSW_DEMAND_WRITE| + HSW_L3_MISS_REMOTE| + HSW_SNOOP_DRAM, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + [ C(RESULT_MISS) ] = 0x0, + }, + }, +}; + static __initconst const u64 westmere_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] @@ -1029,21 +1244,10 @@ static __initconst const u64 slm_hw_cache_event_ids }, }; -static inline bool intel_pmu_needs_lbr_smpl(struct perf_event *event) -{ - /* user explicitly requested branch sampling */ - if (has_branch_stack(event)) - return true; - - /* implicit branch sampling to correct PEBS skid */ - if (x86_pmu.intel_cap.pebs_trap && event->attr.precise_ip > 1 && - x86_pmu.intel_cap.pebs_format < 2) - return true; - - return false; -} - -static void intel_pmu_disable_all(void) +/* + * Use from PMIs where the LBRs are already disabled. + */ +static void __intel_pmu_disable_all(void) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); @@ -1051,17 +1255,24 @@ static void intel_pmu_disable_all(void) if (test_bit(INTEL_PMC_IDX_FIXED_BTS, cpuc->active_mask)) intel_pmu_disable_bts(); + else + intel_bts_disable_local(); intel_pmu_pebs_disable_all(); +} + +static void intel_pmu_disable_all(void) +{ + __intel_pmu_disable_all(); intel_pmu_lbr_disable_all(); } -static void intel_pmu_enable_all(int added) +static void __intel_pmu_enable_all(int added, bool pmi) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); intel_pmu_pebs_enable_all(); - intel_pmu_lbr_enable_all(); + intel_pmu_lbr_enable_all(pmi); wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl & ~cpuc->intel_ctrl_guest_mask); @@ -1073,7 +1284,13 @@ static void intel_pmu_enable_all(int added) return; intel_pmu_enable_bts(event->hw.config); - } + } else + intel_bts_enable_local(); +} + +static void intel_pmu_enable_all(int added) +{ + __intel_pmu_enable_all(added, false); } /* @@ -1207,7 +1424,7 @@ static void intel_pmu_disable_event(struct perf_event *event) * must disable before any actual event * because any event may be combined with LBR */ - if (intel_pmu_needs_lbr_smpl(event)) + if (needs_branch_stack(event)) intel_pmu_lbr_disable(event); if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { @@ -1268,7 +1485,7 @@ static void intel_pmu_enable_event(struct perf_event *event) * must enabled before any actual event * because any event may be combined with LBR */ - if (intel_pmu_needs_lbr_smpl(event)) + if (needs_branch_stack(event)) intel_pmu_lbr_enable(event); if (event->attr.exclude_host) @@ -1334,6 +1551,18 @@ static void intel_pmu_reset(void) if (ds) ds->bts_index = ds->bts_buffer_base; + /* Ack all overflows and disable fixed counters */ + if (x86_pmu.version >= 2) { + intel_pmu_ack_status(intel_pmu_get_status()); + wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); + } + + /* Reset LBRs and LBR freezing */ + if (x86_pmu.lbr_nr) { + update_debugctlmsr(get_debugctlmsr() & + ~(DEBUGCTLMSR_FREEZE_LBRS_ON_PMI|DEBUGCTLMSR_LBR)); + } + local_irq_restore(flags); } @@ -1357,8 +1586,9 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) */ if (!x86_pmu.late_ack) apic_write(APIC_LVTPC, APIC_DM_NMI); - intel_pmu_disable_all(); + __intel_pmu_disable_all(); handled = intel_pmu_drain_bts_buffer(); + handled += intel_bts_interrupt(); status = intel_pmu_get_status(); if (!status) goto done; @@ -1398,6 +1628,14 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) x86_pmu.drain_pebs(regs); } + /* + * Intel PT + */ + if (__test_and_clear_bit(55, (unsigned long *)&status)) { + handled++; + intel_pt_interrupt(); + } + /* * Checkpointed counters can lead to 'spurious' PMIs because the * rollback caused by the PMI will have cleared the overflow status @@ -1433,7 +1671,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) goto again; done: - intel_pmu_enable_all(0); + __intel_pmu_enable_all(0, true); /* * Only unmask the NMI after the overflow counters * have been reset. This avoids spurious NMIs on @@ -1464,7 +1702,7 @@ intel_bts_constraints(struct perf_event *event) static int intel_alt_er(int idx) { - if (!(x86_pmu.er_flags & ERF_HAS_RSP_1)) + if (!(x86_pmu.flags & PMU_FL_HAS_RSP_1)) return idx; if (idx == EXTRA_REG_RSP_0) @@ -1624,7 +1862,8 @@ intel_shared_regs_constraints(struct cpu_hw_events *cpuc, } struct event_constraint * -x86_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) +x86_get_event_constraints(struct cpu_hw_events *cpuc, int idx, + struct perf_event *event) { struct event_constraint *c; @@ -1641,7 +1880,8 @@ x86_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) } static struct event_constraint * -intel_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) +__intel_get_event_constraints(struct cpu_hw_events *cpuc, int idx, + struct perf_event *event) { struct event_constraint *c; @@ -1657,7 +1897,278 @@ intel_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event if (c) return c; - return x86_get_event_constraints(cpuc, event); + return x86_get_event_constraints(cpuc, idx, event); +} + +static void +intel_start_scheduling(struct cpu_hw_events *cpuc) +{ + struct intel_excl_cntrs *excl_cntrs = cpuc->excl_cntrs; + struct intel_excl_states *xl, *xlo; + int tid = cpuc->excl_thread_id; + int o_tid = 1 - tid; /* sibling thread */ + + /* + * nothing needed if in group validation mode + */ + if (cpuc->is_fake || !is_ht_workaround_enabled()) + return; + + /* + * no exclusion needed + */ + if (!excl_cntrs) + return; + + xlo = &excl_cntrs->states[o_tid]; + xl = &excl_cntrs->states[tid]; + + xl->sched_started = true; + xl->num_alloc_cntrs = 0; + /* + * lock shared state until we are done scheduling + * in stop_event_scheduling() + * makes scheduling appear as a transaction + */ + WARN_ON_ONCE(!irqs_disabled()); + raw_spin_lock(&excl_cntrs->lock); + + /* + * save initial state of sibling thread + */ + memcpy(xlo->init_state, xlo->state, sizeof(xlo->init_state)); +} + +static void +intel_stop_scheduling(struct cpu_hw_events *cpuc) +{ + struct intel_excl_cntrs *excl_cntrs = cpuc->excl_cntrs; + struct intel_excl_states *xl, *xlo; + int tid = cpuc->excl_thread_id; + int o_tid = 1 - tid; /* sibling thread */ + + /* + * nothing needed if in group validation mode + */ + if (cpuc->is_fake || !is_ht_workaround_enabled()) + return; + /* + * no exclusion needed + */ + if (!excl_cntrs) + return; + + xlo = &excl_cntrs->states[o_tid]; + xl = &excl_cntrs->states[tid]; + + /* + * make new sibling thread state visible + */ + memcpy(xlo->state, xlo->init_state, sizeof(xlo->state)); + + xl->sched_started = false; + /* + * release shared state lock (acquired in intel_start_scheduling()) + */ + raw_spin_unlock(&excl_cntrs->lock); +} + +static struct event_constraint * +intel_get_excl_constraints(struct cpu_hw_events *cpuc, struct perf_event *event, + int idx, struct event_constraint *c) +{ + struct event_constraint *cx; + struct intel_excl_cntrs *excl_cntrs = cpuc->excl_cntrs; + struct intel_excl_states *xl, *xlo; + int is_excl, i; + int tid = cpuc->excl_thread_id; + int o_tid = 1 - tid; /* alternate */ + + /* + * validating a group does not require + * enforcing cross-thread exclusion + */ + if (cpuc->is_fake || !is_ht_workaround_enabled()) + return c; + + /* + * no exclusion needed + */ + if (!excl_cntrs) + return c; + /* + * event requires exclusive counter access + * across HT threads + */ + is_excl = c->flags & PERF_X86_EVENT_EXCL; + + /* + * xl = state of current HT + * xlo = state of sibling HT + */ + xl = &excl_cntrs->states[tid]; + xlo = &excl_cntrs->states[o_tid]; + + /* + * do not allow scheduling of more than max_alloc_cntrs + * which is set to half the available generic counters. + * this helps avoid counter starvation of sibling thread + * by ensuring at most half the counters cannot be in + * exclusive mode. There is not designated counters for the + * limits. Any N/2 counters can be used. This helps with + * events with specifix counter constraints + */ + if (xl->num_alloc_cntrs++ == xl->max_alloc_cntrs) + return &emptyconstraint; + + cx = c; + + /* + * because we modify the constraint, we need + * to make a copy. Static constraints come + * from static const tables. + * + * only needed when constraint has not yet + * been cloned (marked dynamic) + */ + if (!(c->flags & PERF_X86_EVENT_DYNAMIC)) { + + /* sanity check */ + if (idx < 0) + return &emptyconstraint; + + /* + * grab pre-allocated constraint entry + */ + cx = &cpuc->constraint_list[idx]; + + /* + * initialize dynamic constraint + * with static constraint + */ + memcpy(cx, c, sizeof(*cx)); + + /* + * mark constraint as dynamic, so we + * can free it later on + */ + cx->flags |= PERF_X86_EVENT_DYNAMIC; + } + + /* + * From here on, the constraint is dynamic. + * Either it was just allocated above, or it + * was allocated during a earlier invocation + * of this function + */ + + /* + * Modify static constraint with current dynamic + * state of thread + * + * EXCLUSIVE: sibling counter measuring exclusive event + * SHARED : sibling counter measuring non-exclusive event + * UNUSED : sibling counter unused + */ + for_each_set_bit(i, cx->idxmsk, X86_PMC_IDX_MAX) { + /* + * exclusive event in sibling counter + * our corresponding counter cannot be used + * regardless of our event + */ + if (xl->state[i] == INTEL_EXCL_EXCLUSIVE) + __clear_bit(i, cx->idxmsk); + /* + * if measuring an exclusive event, sibling + * measuring non-exclusive, then counter cannot + * be used + */ + if (is_excl && xl->state[i] == INTEL_EXCL_SHARED) + __clear_bit(i, cx->idxmsk); + } + + /* + * recompute actual bit weight for scheduling algorithm + */ + cx->weight = hweight64(cx->idxmsk64); + + /* + * if we return an empty mask, then switch + * back to static empty constraint to avoid + * the cost of freeing later on + */ + if (cx->weight == 0) + cx = &emptyconstraint; + + return cx; +} + +static struct event_constraint * +intel_get_event_constraints(struct cpu_hw_events *cpuc, int idx, + struct perf_event *event) +{ + struct event_constraint *c1 = event->hw.constraint; + struct event_constraint *c2; + + /* + * first time only + * - static constraint: no change across incremental scheduling calls + * - dynamic constraint: handled by intel_get_excl_constraints() + */ + c2 = __intel_get_event_constraints(cpuc, idx, event); + if (c1 && (c1->flags & PERF_X86_EVENT_DYNAMIC)) { + bitmap_copy(c1->idxmsk, c2->idxmsk, X86_PMC_IDX_MAX); + c1->weight = c2->weight; + c2 = c1; + } + + if (cpuc->excl_cntrs) + return intel_get_excl_constraints(cpuc, event, idx, c2); + + return c2; +} + +static void intel_put_excl_constraints(struct cpu_hw_events *cpuc, + struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + struct intel_excl_cntrs *excl_cntrs = cpuc->excl_cntrs; + struct intel_excl_states *xlo, *xl; + unsigned long flags = 0; /* keep compiler happy */ + int tid = cpuc->excl_thread_id; + int o_tid = 1 - tid; + + /* + * nothing needed if in group validation mode + */ + if (cpuc->is_fake) + return; + + WARN_ON_ONCE(!excl_cntrs); + + if (!excl_cntrs) + return; + + xl = &excl_cntrs->states[tid]; + xlo = &excl_cntrs->states[o_tid]; + + /* + * put_constraint may be called from x86_schedule_events() + * which already has the lock held so here make locking + * conditional + */ + if (!xl->sched_started) + raw_spin_lock_irqsave(&excl_cntrs->lock, flags); + + /* + * if event was actually assigned, then mark the + * counter state as unused now + */ + if (hwc->idx >= 0) + xlo->state[hwc->idx] = INTEL_EXCL_UNUSED; + + if (!xl->sched_started) + raw_spin_unlock_irqrestore(&excl_cntrs->lock, flags); } static void @@ -1678,7 +2189,57 @@ intel_put_shared_regs_event_constraints(struct cpu_hw_events *cpuc, static void intel_put_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) { + struct event_constraint *c = event->hw.constraint; + intel_put_shared_regs_event_constraints(cpuc, event); + + /* + * is PMU has exclusive counter restrictions, then + * all events are subject to and must call the + * put_excl_constraints() routine + */ + if (c && cpuc->excl_cntrs) + intel_put_excl_constraints(cpuc, event); + + /* cleanup dynamic constraint */ + if (c && (c->flags & PERF_X86_EVENT_DYNAMIC)) + event->hw.constraint = NULL; +} + +static void intel_commit_scheduling(struct cpu_hw_events *cpuc, + struct perf_event *event, int cntr) +{ + struct intel_excl_cntrs *excl_cntrs = cpuc->excl_cntrs; + struct event_constraint *c = event->hw.constraint; + struct intel_excl_states *xlo, *xl; + int tid = cpuc->excl_thread_id; + int o_tid = 1 - tid; + int is_excl; + + if (cpuc->is_fake || !c) + return; + + is_excl = c->flags & PERF_X86_EVENT_EXCL; + + if (!(c->flags & PERF_X86_EVENT_DYNAMIC)) + return; + + WARN_ON_ONCE(!excl_cntrs); + + if (!excl_cntrs) + return; + + xl = &excl_cntrs->states[tid]; + xlo = &excl_cntrs->states[o_tid]; + + WARN_ON_ONCE(!raw_spin_is_locked(&excl_cntrs->lock)); + + if (cntr >= 0) { + if (is_excl) + xlo->init_state[cntr] = INTEL_EXCL_EXCLUSIVE; + else + xlo->init_state[cntr] = INTEL_EXCL_SHARED; + } } static void intel_pebs_aliases_core2(struct perf_event *event) @@ -1747,10 +2308,21 @@ static int intel_pmu_hw_config(struct perf_event *event) if (event->attr.precise_ip && x86_pmu.pebs_aliases) x86_pmu.pebs_aliases(event); - if (intel_pmu_needs_lbr_smpl(event)) { + if (needs_branch_stack(event)) { ret = intel_pmu_setup_lbr_filter(event); if (ret) return ret; + + /* + * BTS is set up earlier in this path, so don't account twice + */ + if (!intel_pmu_has_bts(event)) { + /* disallow lbr if conflicting events are present */ + if (x86_add_exclusive(x86_lbr_exclusive_lbr)) + return -EBUSY; + + event->destroy = hw_perf_lbr_event_destroy; + } } if (event->attr.type != PERF_TYPE_RAW) @@ -1891,9 +2463,12 @@ static struct event_constraint counter2_constraint = EVENT_CONSTRAINT(0, 0x4, 0); static struct event_constraint * -hsw_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) +hsw_get_event_constraints(struct cpu_hw_events *cpuc, int idx, + struct perf_event *event) { - struct event_constraint *c = intel_get_event_constraints(cpuc, event); + struct event_constraint *c; + + c = intel_get_event_constraints(cpuc, idx, event); /* Handle special quirk on in_tx_checkpointed only in counter 2 */ if (event->hw.config & HSW_IN_TX_CHECKPOINTED) { @@ -1905,6 +2480,32 @@ hsw_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) return c; } +/* + * Broadwell: + * + * The INST_RETIRED.ALL period always needs to have lowest 6 bits cleared + * (BDM55) and it must not use a period smaller than 100 (BDM11). We combine + * the two to enforce a minimum period of 128 (the smallest value that has bits + * 0-5 cleared and >= 100). + * + * Because of how the code in x86_perf_event_set_period() works, the truncation + * of the lower 6 bits is 'harmless' as we'll occasionally add a longer period + * to make up for the 'lost' events due to carrying the 'error' in period_left. + * + * Therefore the effective (average) period matches the requested period, + * despite coarser hardware granularity. + */ +static unsigned bdw_limit_period(struct perf_event *event, unsigned left) +{ + if ((event->hw.config & INTEL_ARCH_EVENT_MASK) == + X86_CONFIG(.event=0xc0, .umask=0x01)) { + if (left < 128) + left = 128; + left &= ~0x3fu; + } + return left; +} + PMU_FORMAT_ATTR(event, "config:0-7" ); PMU_FORMAT_ATTR(umask, "config:8-15" ); PMU_FORMAT_ATTR(edge, "config:18" ); @@ -1979,16 +2580,52 @@ struct intel_shared_regs *allocate_shared_regs(int cpu) return regs; } +static struct intel_excl_cntrs *allocate_excl_cntrs(int cpu) +{ + struct intel_excl_cntrs *c; + int i; + + c = kzalloc_node(sizeof(struct intel_excl_cntrs), + GFP_KERNEL, cpu_to_node(cpu)); + if (c) { + raw_spin_lock_init(&c->lock); + for (i = 0; i < X86_PMC_IDX_MAX; i++) { + c->states[0].state[i] = INTEL_EXCL_UNUSED; + c->states[0].init_state[i] = INTEL_EXCL_UNUSED; + + c->states[1].state[i] = INTEL_EXCL_UNUSED; + c->states[1].init_state[i] = INTEL_EXCL_UNUSED; + } + c->core_id = -1; + } + return c; +} + static int intel_pmu_cpu_prepare(int cpu) { struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); - if (!(x86_pmu.extra_regs || x86_pmu.lbr_sel_map)) - return NOTIFY_OK; + if (x86_pmu.extra_regs || x86_pmu.lbr_sel_map) { + cpuc->shared_regs = allocate_shared_regs(cpu); + if (!cpuc->shared_regs) + return NOTIFY_BAD; + } - cpuc->shared_regs = allocate_shared_regs(cpu); - if (!cpuc->shared_regs) - return NOTIFY_BAD; + if (x86_pmu.flags & PMU_FL_EXCL_CNTRS) { + size_t sz = X86_PMC_IDX_MAX * sizeof(struct event_constraint); + + cpuc->constraint_list = kzalloc(sz, GFP_KERNEL); + if (!cpuc->constraint_list) + return NOTIFY_BAD; + + cpuc->excl_cntrs = allocate_excl_cntrs(cpu); + if (!cpuc->excl_cntrs) { + kfree(cpuc->constraint_list); + kfree(cpuc->shared_regs); + return NOTIFY_BAD; + } + cpuc->excl_thread_id = 0; + } return NOTIFY_OK; } @@ -2010,13 +2647,15 @@ static void intel_pmu_cpu_starting(int cpu) if (!cpuc->shared_regs) return; - if (!(x86_pmu.er_flags & ERF_NO_HT_SHARING)) { + if (!(x86_pmu.flags & PMU_FL_NO_HT_SHARING)) { + void **onln = &cpuc->kfree_on_online[X86_PERF_KFREE_SHARED]; + for_each_cpu(i, topology_thread_cpumask(cpu)) { struct intel_shared_regs *pc; pc = per_cpu(cpu_hw_events, i).shared_regs; if (pc && pc->core_id == core_id) { - cpuc->kfree_on_online = cpuc->shared_regs; + *onln = cpuc->shared_regs; cpuc->shared_regs = pc; break; } @@ -2027,6 +2666,44 @@ static void intel_pmu_cpu_starting(int cpu) if (x86_pmu.lbr_sel_map) cpuc->lbr_sel = &cpuc->shared_regs->regs[EXTRA_REG_LBR]; + + if (x86_pmu.flags & PMU_FL_EXCL_CNTRS) { + int h = x86_pmu.num_counters >> 1; + + for_each_cpu(i, topology_thread_cpumask(cpu)) { + struct intel_excl_cntrs *c; + + c = per_cpu(cpu_hw_events, i).excl_cntrs; + if (c && c->core_id == core_id) { + cpuc->kfree_on_online[1] = cpuc->excl_cntrs; + cpuc->excl_cntrs = c; + cpuc->excl_thread_id = 1; + break; + } + } + cpuc->excl_cntrs->core_id = core_id; + cpuc->excl_cntrs->refcnt++; + /* + * set hard limit to half the number of generic counters + */ + cpuc->excl_cntrs->states[0].max_alloc_cntrs = h; + cpuc->excl_cntrs->states[1].max_alloc_cntrs = h; + } +} + +static void free_excl_cntrs(int cpu) +{ + struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); + struct intel_excl_cntrs *c; + + c = cpuc->excl_cntrs; + if (c) { + if (c->core_id == -1 || --c->refcnt == 0) + kfree(c); + cpuc->excl_cntrs = NULL; + kfree(cpuc->constraint_list); + cpuc->constraint_list = NULL; + } } static void intel_pmu_cpu_dying(int cpu) @@ -2041,19 +2718,9 @@ static void intel_pmu_cpu_dying(int cpu) cpuc->shared_regs = NULL; } - fini_debug_store_on_cpu(cpu); -} + free_excl_cntrs(cpu); -static void intel_pmu_flush_branch_stack(void) -{ - /* - * Intel LBR does not tag entries with the - * PID of the current task, then we need to - * flush it on ctxsw - * For now, we simply reset it - */ - if (x86_pmu.lbr_nr) - intel_pmu_lbr_reset(); + fini_debug_store_on_cpu(cpu); } PMU_FORMAT_ATTR(offcore_rsp, "config1:0-63"); @@ -2107,7 +2774,7 @@ static __initconst const struct x86_pmu intel_pmu = { .cpu_starting = intel_pmu_cpu_starting, .cpu_dying = intel_pmu_cpu_dying, .guest_get_msrs = intel_guest_get_msrs, - .flush_branch_stack = intel_pmu_flush_branch_stack, + .sched_task = intel_pmu_lbr_sched_task, }; static __init void intel_clovertown_quirk(void) @@ -2264,6 +2931,27 @@ static __init void intel_nehalem_quirk(void) } } +/* + * enable software workaround for errata: + * SNB: BJ122 + * IVB: BV98 + * HSW: HSD29 + * + * Only needed when HT is enabled. However detecting + * if HT is enabled is difficult (model specific). So instead, + * we enable the workaround in the early boot, and verify if + * it is needed in a later initcall phase once we have valid + * topology information to check if HT is actually enabled + */ +static __init void intel_ht_bug(void) +{ + x86_pmu.flags |= PMU_FL_EXCL_CNTRS | PMU_FL_EXCL_ENABLED; + + x86_pmu.commit_scheduling = intel_commit_scheduling; + x86_pmu.start_scheduling = intel_start_scheduling; + x86_pmu.stop_scheduling = intel_stop_scheduling; +} + EVENT_ATTR_STR(mem-loads, mem_ld_hsw, "event=0xcd,umask=0x1,ldlat=3"); EVENT_ATTR_STR(mem-stores, mem_st_hsw, "event=0xd0,umask=0x82") @@ -2443,7 +3131,7 @@ __init int intel_pmu_init(void) x86_pmu.event_constraints = intel_slm_event_constraints; x86_pmu.pebs_constraints = intel_slm_pebs_event_constraints; x86_pmu.extra_regs = intel_slm_extra_regs; - x86_pmu.er_flags |= ERF_HAS_RSP_1; + x86_pmu.flags |= PMU_FL_HAS_RSP_1; pr_cont("Silvermont events, "); break; @@ -2461,7 +3149,7 @@ __init int intel_pmu_init(void) x86_pmu.enable_all = intel_pmu_nhm_enable_all; x86_pmu.pebs_constraints = intel_westmere_pebs_event_constraints; x86_pmu.extra_regs = intel_westmere_extra_regs; - x86_pmu.er_flags |= ERF_HAS_RSP_1; + x86_pmu.flags |= PMU_FL_HAS_RSP_1; x86_pmu.cpu_events = nhm_events_attrs; @@ -2478,6 +3166,7 @@ __init int intel_pmu_init(void) case 42: /* 32nm SandyBridge */ case 45: /* 32nm SandyBridge-E/EN/EP */ x86_add_quirk(intel_sandybridge_quirk); + x86_add_quirk(intel_ht_bug); memcpy(hw_cache_event_ids, snb_hw_cache_event_ids, sizeof(hw_cache_event_ids)); memcpy(hw_cache_extra_regs, snb_hw_cache_extra_regs, @@ -2492,9 +3181,11 @@ __init int intel_pmu_init(void) x86_pmu.extra_regs = intel_snbep_extra_regs; else x86_pmu.extra_regs = intel_snb_extra_regs; + + /* all extra regs are per-cpu when HT is on */ - x86_pmu.er_flags |= ERF_HAS_RSP_1; - x86_pmu.er_flags |= ERF_NO_HT_SHARING; + x86_pmu.flags |= PMU_FL_HAS_RSP_1; + x86_pmu.flags |= PMU_FL_NO_HT_SHARING; x86_pmu.cpu_events = snb_events_attrs; @@ -2510,6 +3201,7 @@ __init int intel_pmu_init(void) case 58: /* 22nm IvyBridge */ case 62: /* 22nm IvyBridge-EP/EX */ + x86_add_quirk(intel_ht_bug); memcpy(hw_cache_event_ids, snb_hw_cache_event_ids, sizeof(hw_cache_event_ids)); /* dTLB-load-misses on IVB is different than SNB */ @@ -2528,8 +3220,8 @@ __init int intel_pmu_init(void) else x86_pmu.extra_regs = intel_snb_extra_regs; /* all extra regs are per-cpu when HT is on */ - x86_pmu.er_flags |= ERF_HAS_RSP_1; - x86_pmu.er_flags |= ERF_NO_HT_SHARING; + x86_pmu.flags |= PMU_FL_HAS_RSP_1; + x86_pmu.flags |= PMU_FL_NO_HT_SHARING; x86_pmu.cpu_events = snb_events_attrs; @@ -2545,19 +3237,20 @@ __init int intel_pmu_init(void) case 63: /* 22nm Haswell Server */ case 69: /* 22nm Haswell ULT */ case 70: /* 22nm Haswell + GT3e (Intel Iris Pro graphics) */ + x86_add_quirk(intel_ht_bug); x86_pmu.late_ack = true; - memcpy(hw_cache_event_ids, snb_hw_cache_event_ids, sizeof(hw_cache_event_ids)); - memcpy(hw_cache_extra_regs, snb_hw_cache_extra_regs, sizeof(hw_cache_extra_regs)); + memcpy(hw_cache_event_ids, hsw_hw_cache_event_ids, sizeof(hw_cache_event_ids)); + memcpy(hw_cache_extra_regs, hsw_hw_cache_extra_regs, sizeof(hw_cache_extra_regs)); - intel_pmu_lbr_init_snb(); + intel_pmu_lbr_init_hsw(); x86_pmu.event_constraints = intel_hsw_event_constraints; x86_pmu.pebs_constraints = intel_hsw_pebs_event_constraints; x86_pmu.extra_regs = intel_snbep_extra_regs; x86_pmu.pebs_aliases = intel_pebs_aliases_snb; /* all extra regs are per-cpu when HT is on */ - x86_pmu.er_flags |= ERF_HAS_RSP_1; - x86_pmu.er_flags |= ERF_NO_HT_SHARING; + x86_pmu.flags |= PMU_FL_HAS_RSP_1; + x86_pmu.flags |= PMU_FL_NO_HT_SHARING; x86_pmu.hw_config = hsw_hw_config; x86_pmu.get_event_constraints = hsw_get_event_constraints; @@ -2566,6 +3259,39 @@ __init int intel_pmu_init(void) pr_cont("Haswell events, "); break; + case 61: /* 14nm Broadwell Core-M */ + case 86: /* 14nm Broadwell Xeon D */ + x86_pmu.late_ack = true; + memcpy(hw_cache_event_ids, hsw_hw_cache_event_ids, sizeof(hw_cache_event_ids)); + memcpy(hw_cache_extra_regs, hsw_hw_cache_extra_regs, sizeof(hw_cache_extra_regs)); + + /* L3_MISS_LOCAL_DRAM is BIT(26) in Broadwell */ + hw_cache_extra_regs[C(LL)][C(OP_READ)][C(RESULT_MISS)] = HSW_DEMAND_READ | + BDW_L3_MISS|HSW_SNOOP_DRAM; + hw_cache_extra_regs[C(LL)][C(OP_WRITE)][C(RESULT_MISS)] = HSW_DEMAND_WRITE|BDW_L3_MISS| + HSW_SNOOP_DRAM; + hw_cache_extra_regs[C(NODE)][C(OP_READ)][C(RESULT_ACCESS)] = HSW_DEMAND_READ| + BDW_L3_MISS_LOCAL|HSW_SNOOP_DRAM; + hw_cache_extra_regs[C(NODE)][C(OP_WRITE)][C(RESULT_ACCESS)] = HSW_DEMAND_WRITE| + BDW_L3_MISS_LOCAL|HSW_SNOOP_DRAM; + + intel_pmu_lbr_init_snb(); + + x86_pmu.event_constraints = intel_bdw_event_constraints; + x86_pmu.pebs_constraints = intel_hsw_pebs_event_constraints; + x86_pmu.extra_regs = intel_snbep_extra_regs; + x86_pmu.pebs_aliases = intel_pebs_aliases_snb; + /* all extra regs are per-cpu when HT is on */ + x86_pmu.flags |= PMU_FL_HAS_RSP_1; + x86_pmu.flags |= PMU_FL_NO_HT_SHARING; + + x86_pmu.hw_config = hsw_hw_config; + x86_pmu.get_event_constraints = hsw_get_event_constraints; + x86_pmu.cpu_events = hsw_events_attrs; + x86_pmu.limit_period = bdw_limit_period; + pr_cont("Broadwell events, "); + break; + default: switch (x86_pmu.version) { case 1: @@ -2651,3 +3377,47 @@ __init int intel_pmu_init(void) return 0; } + +/* + * HT bug: phase 2 init + * Called once we have valid topology information to check + * whether or not HT is enabled + * If HT is off, then we disable the workaround + */ +static __init int fixup_ht_bug(void) +{ + int cpu = smp_processor_id(); + int w, c; + /* + * problem not present on this CPU model, nothing to do + */ + if (!(x86_pmu.flags & PMU_FL_EXCL_ENABLED)) + return 0; + + w = cpumask_weight(topology_thread_cpumask(cpu)); + if (w > 1) { + pr_info("PMU erratum BJ122, BV98, HSD29 worked around, HT is on\n"); + return 0; + } + + watchdog_nmi_disable_all(); + + x86_pmu.flags &= ~(PMU_FL_EXCL_CNTRS | PMU_FL_EXCL_ENABLED); + + x86_pmu.commit_scheduling = NULL; + x86_pmu.start_scheduling = NULL; + x86_pmu.stop_scheduling = NULL; + + watchdog_nmi_enable_all(); + + get_online_cpus(); + + for_each_online_cpu(c) { + free_excl_cntrs(c); + } + + put_online_cpus(); + pr_info("PMU erratum BJ122, BV98, HSD29 workaround disabled, HT off\n"); + return 0; +} +subsys_initcall(fixup_ht_bug) diff --git a/arch/x86/kernel/cpu/perf_event_intel_bts.c b/arch/x86/kernel/cpu/perf_event_intel_bts.c new file mode 100644 index 00000000000000..ac1f0c55f3796e --- /dev/null +++ b/arch/x86/kernel/cpu/perf_event_intel_bts.c @@ -0,0 +1,525 @@ +/* + * BTS PMU driver for perf + * Copyright (c) 2013-2014, Intel Corporation. + * + * 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. + */ + +#undef DEBUG + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "perf_event.h" + +struct bts_ctx { + struct perf_output_handle handle; + struct debug_store ds_back; + int started; +}; + +static DEFINE_PER_CPU(struct bts_ctx, bts_ctx); + +#define BTS_RECORD_SIZE 24 +#define BTS_SAFETY_MARGIN 4080 + +struct bts_phys { + struct page *page; + unsigned long size; + unsigned long offset; + unsigned long displacement; +}; + +struct bts_buffer { + size_t real_size; /* multiple of BTS_RECORD_SIZE */ + unsigned int nr_pages; + unsigned int nr_bufs; + unsigned int cur_buf; + bool snapshot; + local_t data_size; + local_t lost; + local_t head; + unsigned long end; + void **data_pages; + struct bts_phys buf[0]; +}; + +struct pmu bts_pmu; + +void intel_pmu_enable_bts(u64 config); +void intel_pmu_disable_bts(void); + +static size_t buf_size(struct page *page) +{ + return 1 << (PAGE_SHIFT + page_private(page)); +} + +static void * +bts_buffer_setup_aux(int cpu, void **pages, int nr_pages, bool overwrite) +{ + struct bts_buffer *buf; + struct page *page; + int node = (cpu == -1) ? cpu : cpu_to_node(cpu); + unsigned long offset; + size_t size = nr_pages << PAGE_SHIFT; + int pg, nbuf, pad; + + /* count all the high order buffers */ + for (pg = 0, nbuf = 0; pg < nr_pages;) { + page = virt_to_page(pages[pg]); + if (WARN_ON_ONCE(!PagePrivate(page) && nr_pages > 1)) + return NULL; + pg += 1 << page_private(page); + nbuf++; + } + + /* + * to avoid interrupts in overwrite mode, only allow one physical + */ + if (overwrite && nbuf > 1) + return NULL; + + buf = kzalloc_node(offsetof(struct bts_buffer, buf[nbuf]), GFP_KERNEL, node); + if (!buf) + return NULL; + + buf->nr_pages = nr_pages; + buf->nr_bufs = nbuf; + buf->snapshot = overwrite; + buf->data_pages = pages; + buf->real_size = size - size % BTS_RECORD_SIZE; + + for (pg = 0, nbuf = 0, offset = 0, pad = 0; nbuf < buf->nr_bufs; nbuf++) { + unsigned int __nr_pages; + + page = virt_to_page(pages[pg]); + __nr_pages = PagePrivate(page) ? 1 << page_private(page) : 1; + buf->buf[nbuf].page = page; + buf->buf[nbuf].offset = offset; + buf->buf[nbuf].displacement = (pad ? BTS_RECORD_SIZE - pad : 0); + buf->buf[nbuf].size = buf_size(page) - buf->buf[nbuf].displacement; + pad = buf->buf[nbuf].size % BTS_RECORD_SIZE; + buf->buf[nbuf].size -= pad; + + pg += __nr_pages; + offset += __nr_pages << PAGE_SHIFT; + } + + return buf; +} + +static void bts_buffer_free_aux(void *data) +{ + kfree(data); +} + +static unsigned long bts_buffer_offset(struct bts_buffer *buf, unsigned int idx) +{ + return buf->buf[idx].offset + buf->buf[idx].displacement; +} + +static void +bts_config_buffer(struct bts_buffer *buf) +{ + int cpu = raw_smp_processor_id(); + struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; + struct bts_phys *phys = &buf->buf[buf->cur_buf]; + unsigned long index, thresh = 0, end = phys->size; + struct page *page = phys->page; + + index = local_read(&buf->head); + + if (!buf->snapshot) { + if (buf->end < phys->offset + buf_size(page)) + end = buf->end - phys->offset - phys->displacement; + + index -= phys->offset + phys->displacement; + + if (end - index > BTS_SAFETY_MARGIN) + thresh = end - BTS_SAFETY_MARGIN; + else if (end - index > BTS_RECORD_SIZE) + thresh = end - BTS_RECORD_SIZE; + else + thresh = end; + } + + ds->bts_buffer_base = (u64)(long)page_address(page) + phys->displacement; + ds->bts_index = ds->bts_buffer_base + index; + ds->bts_absolute_maximum = ds->bts_buffer_base + end; + ds->bts_interrupt_threshold = !buf->snapshot + ? ds->bts_buffer_base + thresh + : ds->bts_absolute_maximum + BTS_RECORD_SIZE; +} + +static void bts_buffer_pad_out(struct bts_phys *phys, unsigned long head) +{ + unsigned long index = head - phys->offset; + + memset(page_address(phys->page) + index, 0, phys->size - index); +} + +static bool bts_buffer_is_full(struct bts_buffer *buf, struct bts_ctx *bts) +{ + if (buf->snapshot) + return false; + + if (local_read(&buf->data_size) >= bts->handle.size || + bts->handle.size - local_read(&buf->data_size) < BTS_RECORD_SIZE) + return true; + + return false; +} + +static void bts_update(struct bts_ctx *bts) +{ + int cpu = raw_smp_processor_id(); + struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; + struct bts_buffer *buf = perf_get_aux(&bts->handle); + unsigned long index = ds->bts_index - ds->bts_buffer_base, old, head; + + if (!buf) + return; + + head = index + bts_buffer_offset(buf, buf->cur_buf); + old = local_xchg(&buf->head, head); + + if (!buf->snapshot) { + if (old == head) + return; + + if (ds->bts_index >= ds->bts_absolute_maximum) + local_inc(&buf->lost); + + /* + * old and head are always in the same physical buffer, so we + * can subtract them to get the data size. + */ + local_add(head - old, &buf->data_size); + } else { + local_set(&buf->data_size, head); + } +} + +static void __bts_event_start(struct perf_event *event) +{ + struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); + struct bts_buffer *buf = perf_get_aux(&bts->handle); + u64 config = 0; + + if (!buf || bts_buffer_is_full(buf, bts)) + return; + + event->hw.state = 0; + + if (!buf->snapshot) + config |= ARCH_PERFMON_EVENTSEL_INT; + if (!event->attr.exclude_kernel) + config |= ARCH_PERFMON_EVENTSEL_OS; + if (!event->attr.exclude_user) + config |= ARCH_PERFMON_EVENTSEL_USR; + + bts_config_buffer(buf); + + /* + * local barrier to make sure that ds configuration made it + * before we enable BTS + */ + wmb(); + + intel_pmu_enable_bts(config); +} + +static void bts_event_start(struct perf_event *event, int flags) +{ + struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); + + __bts_event_start(event); + + /* PMI handler: this counter is running and likely generating PMIs */ + ACCESS_ONCE(bts->started) = 1; +} + +static void __bts_event_stop(struct perf_event *event) +{ + /* + * No extra synchronization is mandated by the documentation to have + * BTS data stores globally visible. + */ + intel_pmu_disable_bts(); + + if (event->hw.state & PERF_HES_STOPPED) + return; + + ACCESS_ONCE(event->hw.state) |= PERF_HES_STOPPED; +} + +static void bts_event_stop(struct perf_event *event, int flags) +{ + struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); + + /* PMI handler: don't restart this counter */ + ACCESS_ONCE(bts->started) = 0; + + __bts_event_stop(event); + + if (flags & PERF_EF_UPDATE) + bts_update(bts); +} + +void intel_bts_enable_local(void) +{ + struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); + + if (bts->handle.event && bts->started) + __bts_event_start(bts->handle.event); +} + +void intel_bts_disable_local(void) +{ + struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); + + if (bts->handle.event) + __bts_event_stop(bts->handle.event); +} + +static int +bts_buffer_reset(struct bts_buffer *buf, struct perf_output_handle *handle) +{ + unsigned long head, space, next_space, pad, gap, skip, wakeup; + unsigned int next_buf; + struct bts_phys *phys, *next_phys; + int ret; + + if (buf->snapshot) + return 0; + + head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1); + if (WARN_ON_ONCE(head != local_read(&buf->head))) + return -EINVAL; + + phys = &buf->buf[buf->cur_buf]; + space = phys->offset + phys->displacement + phys->size - head; + pad = space; + if (space > handle->size) { + space = handle->size; + space -= space % BTS_RECORD_SIZE; + } + if (space <= BTS_SAFETY_MARGIN) { + /* See if next phys buffer has more space */ + next_buf = buf->cur_buf + 1; + if (next_buf >= buf->nr_bufs) + next_buf = 0; + next_phys = &buf->buf[next_buf]; + gap = buf_size(phys->page) - phys->displacement - phys->size + + next_phys->displacement; + skip = pad + gap; + if (handle->size >= skip) { + next_space = next_phys->size; + if (next_space + skip > handle->size) { + next_space = handle->size - skip; + next_space -= next_space % BTS_RECORD_SIZE; + } + if (next_space > space || !space) { + if (pad) + bts_buffer_pad_out(phys, head); + ret = perf_aux_output_skip(handle, skip); + if (ret) + return ret; + /* Advance to next phys buffer */ + phys = next_phys; + space = next_space; + head = phys->offset + phys->displacement; + /* + * After this, cur_buf and head won't match ds + * anymore, so we must not be racing with + * bts_update(). + */ + buf->cur_buf = next_buf; + local_set(&buf->head, head); + } + } + } + + /* Don't go far beyond wakeup watermark */ + wakeup = BTS_SAFETY_MARGIN + BTS_RECORD_SIZE + handle->wakeup - + handle->head; + if (space > wakeup) { + space = wakeup; + space -= space % BTS_RECORD_SIZE; + } + + buf->end = head + space; + + /* + * If we have no space, the lost notification would have been sent when + * we hit absolute_maximum - see bts_update() + */ + if (!space) + return -ENOSPC; + + return 0; +} + +int intel_bts_interrupt(void) +{ + struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); + struct perf_event *event = bts->handle.event; + struct bts_buffer *buf; + s64 old_head; + int err; + + if (!event || !bts->started) + return 0; + + buf = perf_get_aux(&bts->handle); + /* + * Skip snapshot counters: they don't use the interrupt, but + * there's no other way of telling, because the pointer will + * keep moving + */ + if (!buf || buf->snapshot) + return 0; + + old_head = local_read(&buf->head); + bts_update(bts); + + /* no new data */ + if (old_head == local_read(&buf->head)) + return 0; + + perf_aux_output_end(&bts->handle, local_xchg(&buf->data_size, 0), + !!local_xchg(&buf->lost, 0)); + + buf = perf_aux_output_begin(&bts->handle, event); + if (!buf) + return 1; + + err = bts_buffer_reset(buf, &bts->handle); + if (err) + perf_aux_output_end(&bts->handle, 0, false); + + return 1; +} + +static void bts_event_del(struct perf_event *event, int mode) +{ + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); + struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); + struct bts_buffer *buf = perf_get_aux(&bts->handle); + + bts_event_stop(event, PERF_EF_UPDATE); + + if (buf) { + if (buf->snapshot) + bts->handle.head = + local_xchg(&buf->data_size, + buf->nr_pages << PAGE_SHIFT); + perf_aux_output_end(&bts->handle, local_xchg(&buf->data_size, 0), + !!local_xchg(&buf->lost, 0)); + } + + cpuc->ds->bts_index = bts->ds_back.bts_buffer_base; + cpuc->ds->bts_buffer_base = bts->ds_back.bts_buffer_base; + cpuc->ds->bts_absolute_maximum = bts->ds_back.bts_absolute_maximum; + cpuc->ds->bts_interrupt_threshold = bts->ds_back.bts_interrupt_threshold; +} + +static int bts_event_add(struct perf_event *event, int mode) +{ + struct bts_buffer *buf; + struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); + struct hw_perf_event *hwc = &event->hw; + int ret = -EBUSY; + + event->hw.state = PERF_HES_STOPPED; + + if (test_bit(INTEL_PMC_IDX_FIXED_BTS, cpuc->active_mask)) + return -EBUSY; + + if (bts->handle.event) + return -EBUSY; + + buf = perf_aux_output_begin(&bts->handle, event); + if (!buf) + return -EINVAL; + + ret = bts_buffer_reset(buf, &bts->handle); + if (ret) { + perf_aux_output_end(&bts->handle, 0, false); + return ret; + } + + bts->ds_back.bts_buffer_base = cpuc->ds->bts_buffer_base; + bts->ds_back.bts_absolute_maximum = cpuc->ds->bts_absolute_maximum; + bts->ds_back.bts_interrupt_threshold = cpuc->ds->bts_interrupt_threshold; + + if (mode & PERF_EF_START) { + bts_event_start(event, 0); + if (hwc->state & PERF_HES_STOPPED) { + bts_event_del(event, 0); + return -EBUSY; + } + } + + return 0; +} + +static void bts_event_destroy(struct perf_event *event) +{ + x86_del_exclusive(x86_lbr_exclusive_bts); +} + +static int bts_event_init(struct perf_event *event) +{ + if (event->attr.type != bts_pmu.type) + return -ENOENT; + + if (x86_add_exclusive(x86_lbr_exclusive_bts)) + return -EBUSY; + + event->destroy = bts_event_destroy; + + return 0; +} + +static void bts_event_read(struct perf_event *event) +{ +} + +static __init int bts_init(void) +{ + if (!boot_cpu_has(X86_FEATURE_DTES64) || !x86_pmu.bts) + return -ENODEV; + + bts_pmu.capabilities = PERF_PMU_CAP_AUX_NO_SG | PERF_PMU_CAP_ITRACE; + bts_pmu.task_ctx_nr = perf_sw_context; + bts_pmu.event_init = bts_event_init; + bts_pmu.add = bts_event_add; + bts_pmu.del = bts_event_del; + bts_pmu.start = bts_event_start; + bts_pmu.stop = bts_event_stop; + bts_pmu.read = bts_event_read; + bts_pmu.setup_aux = bts_buffer_setup_aux; + bts_pmu.free_aux = bts_buffer_free_aux; + + return perf_pmu_register(&bts_pmu, "intel_bts", -1); +} + +module_init(bts_init); diff --git a/arch/x86/kernel/cpu/perf_event_intel_cqm.c b/arch/x86/kernel/cpu/perf_event_intel_cqm.c new file mode 100644 index 00000000000000..e4d1b8b738fa8a --- /dev/null +++ b/arch/x86/kernel/cpu/perf_event_intel_cqm.c @@ -0,0 +1,1379 @@ +/* + * Intel Cache Quality-of-Service Monitoring (CQM) support. + * + * Based very, very heavily on work by Peter Zijlstra. + */ + +#include +#include +#include +#include "perf_event.h" + +#define MSR_IA32_PQR_ASSOC 0x0c8f +#define MSR_IA32_QM_CTR 0x0c8e +#define MSR_IA32_QM_EVTSEL 0x0c8d + +static unsigned int cqm_max_rmid = -1; +static unsigned int cqm_l3_scale; /* supposedly cacheline size */ + +struct intel_cqm_state { + raw_spinlock_t lock; + int rmid; + int cnt; +}; + +static DEFINE_PER_CPU(struct intel_cqm_state, cqm_state); + +/* + * Protects cache_cgroups and cqm_rmid_free_lru and cqm_rmid_limbo_lru. + * Also protects event->hw.cqm_rmid + * + * Hold either for stability, both for modification of ->hw.cqm_rmid. + */ +static DEFINE_MUTEX(cache_mutex); +static DEFINE_RAW_SPINLOCK(cache_lock); + +/* + * Groups of events that have the same target(s), one RMID per group. + */ +static LIST_HEAD(cache_groups); + +/* + * Mask of CPUs for reading CQM values. We only need one per-socket. + */ +static cpumask_t cqm_cpumask; + +#define RMID_VAL_ERROR (1ULL << 63) +#define RMID_VAL_UNAVAIL (1ULL << 62) + +#define QOS_L3_OCCUP_EVENT_ID (1 << 0) + +#define QOS_EVENT_MASK QOS_L3_OCCUP_EVENT_ID + +/* + * This is central to the rotation algorithm in __intel_cqm_rmid_rotate(). + * + * This rmid is always free and is guaranteed to have an associated + * near-zero occupancy value, i.e. no cachelines are tagged with this + * RMID, once __intel_cqm_rmid_rotate() returns. + */ +static unsigned int intel_cqm_rotation_rmid; + +#define INVALID_RMID (-1) + +/* + * Is @rmid valid for programming the hardware? + * + * rmid 0 is reserved by the hardware for all non-monitored tasks, which + * means that we should never come across an rmid with that value. + * Likewise, an rmid value of -1 is used to indicate "no rmid currently + * assigned" and is used as part of the rotation code. + */ +static inline bool __rmid_valid(unsigned int rmid) +{ + if (!rmid || rmid == INVALID_RMID) + return false; + + return true; +} + +static u64 __rmid_read(unsigned int rmid) +{ + u64 val; + + /* + * Ignore the SDM, this thing is _NOTHING_ like a regular perfcnt, + * it just says that to increase confusion. + */ + wrmsr(MSR_IA32_QM_EVTSEL, QOS_L3_OCCUP_EVENT_ID, rmid); + rdmsrl(MSR_IA32_QM_CTR, val); + + /* + * Aside from the ERROR and UNAVAIL bits, assume this thing returns + * the number of cachelines tagged with @rmid. + */ + return val; +} + +enum rmid_recycle_state { + RMID_YOUNG = 0, + RMID_AVAILABLE, + RMID_DIRTY, +}; + +struct cqm_rmid_entry { + unsigned int rmid; + enum rmid_recycle_state state; + struct list_head list; + unsigned long queue_time; +}; + +/* + * cqm_rmid_free_lru - A least recently used list of RMIDs. + * + * Oldest entry at the head, newest (most recently used) entry at the + * tail. This list is never traversed, it's only used to keep track of + * the lru order. That is, we only pick entries of the head or insert + * them on the tail. + * + * All entries on the list are 'free', and their RMIDs are not currently + * in use. To mark an RMID as in use, remove its entry from the lru + * list. + * + * + * cqm_rmid_limbo_lru - list of currently unused but (potentially) dirty RMIDs. + * + * This list is contains RMIDs that no one is currently using but that + * may have a non-zero occupancy value associated with them. The + * rotation worker moves RMIDs from the limbo list to the free list once + * the occupancy value drops below __intel_cqm_threshold. + * + * Both lists are protected by cache_mutex. + */ +static LIST_HEAD(cqm_rmid_free_lru); +static LIST_HEAD(cqm_rmid_limbo_lru); + +/* + * We use a simple array of pointers so that we can lookup a struct + * cqm_rmid_entry in O(1). This alleviates the callers of __get_rmid() + * and __put_rmid() from having to worry about dealing with struct + * cqm_rmid_entry - they just deal with rmids, i.e. integers. + * + * Once this array is initialized it is read-only. No locks are required + * to access it. + * + * All entries for all RMIDs can be looked up in the this array at all + * times. + */ +static struct cqm_rmid_entry **cqm_rmid_ptrs; + +static inline struct cqm_rmid_entry *__rmid_entry(int rmid) +{ + struct cqm_rmid_entry *entry; + + entry = cqm_rmid_ptrs[rmid]; + WARN_ON(entry->rmid != rmid); + + return entry; +} + +/* + * Returns < 0 on fail. + * + * We expect to be called with cache_mutex held. + */ +static int __get_rmid(void) +{ + struct cqm_rmid_entry *entry; + + lockdep_assert_held(&cache_mutex); + + if (list_empty(&cqm_rmid_free_lru)) + return INVALID_RMID; + + entry = list_first_entry(&cqm_rmid_free_lru, struct cqm_rmid_entry, list); + list_del(&entry->list); + + return entry->rmid; +} + +static void __put_rmid(unsigned int rmid) +{ + struct cqm_rmid_entry *entry; + + lockdep_assert_held(&cache_mutex); + + WARN_ON(!__rmid_valid(rmid)); + entry = __rmid_entry(rmid); + + entry->queue_time = jiffies; + entry->state = RMID_YOUNG; + + list_add_tail(&entry->list, &cqm_rmid_limbo_lru); +} + +static int intel_cqm_setup_rmid_cache(void) +{ + struct cqm_rmid_entry *entry; + unsigned int nr_rmids; + int r = 0; + + nr_rmids = cqm_max_rmid + 1; + cqm_rmid_ptrs = kmalloc(sizeof(struct cqm_rmid_entry *) * + nr_rmids, GFP_KERNEL); + if (!cqm_rmid_ptrs) + return -ENOMEM; + + for (; r <= cqm_max_rmid; r++) { + struct cqm_rmid_entry *entry; + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + goto fail; + + INIT_LIST_HEAD(&entry->list); + entry->rmid = r; + cqm_rmid_ptrs[r] = entry; + + list_add_tail(&entry->list, &cqm_rmid_free_lru); + } + + /* + * RMID 0 is special and is always allocated. It's used for all + * tasks that are not monitored. + */ + entry = __rmid_entry(0); + list_del(&entry->list); + + mutex_lock(&cache_mutex); + intel_cqm_rotation_rmid = __get_rmid(); + mutex_unlock(&cache_mutex); + + return 0; +fail: + while (r--) + kfree(cqm_rmid_ptrs[r]); + + kfree(cqm_rmid_ptrs); + return -ENOMEM; +} + +/* + * Determine if @a and @b measure the same set of tasks. + * + * If @a and @b measure the same set of tasks then we want to share a + * single RMID. + */ +static bool __match_event(struct perf_event *a, struct perf_event *b) +{ + /* Per-cpu and task events don't mix */ + if ((a->attach_state & PERF_ATTACH_TASK) != + (b->attach_state & PERF_ATTACH_TASK)) + return false; + +#ifdef CONFIG_CGROUP_PERF + if (a->cgrp != b->cgrp) + return false; +#endif + + /* If not task event, we're machine wide */ + if (!(b->attach_state & PERF_ATTACH_TASK)) + return true; + + /* + * Events that target same task are placed into the same cache group. + */ + if (a->hw.target == b->hw.target) + return true; + + /* + * Are we an inherited event? + */ + if (b->parent == a) + return true; + + return false; +} + +#ifdef CONFIG_CGROUP_PERF +static inline struct perf_cgroup *event_to_cgroup(struct perf_event *event) +{ + if (event->attach_state & PERF_ATTACH_TASK) + return perf_cgroup_from_task(event->hw.target); + + return event->cgrp; +} +#endif + +/* + * Determine if @a's tasks intersect with @b's tasks + * + * There are combinations of events that we explicitly prohibit, + * + * PROHIBITS + * system-wide -> cgroup and task + * cgroup -> system-wide + * -> task in cgroup + * task -> system-wide + * -> task in cgroup + * + * Call this function before allocating an RMID. + */ +static bool __conflict_event(struct perf_event *a, struct perf_event *b) +{ +#ifdef CONFIG_CGROUP_PERF + /* + * We can have any number of cgroups but only one system-wide + * event at a time. + */ + if (a->cgrp && b->cgrp) { + struct perf_cgroup *ac = a->cgrp; + struct perf_cgroup *bc = b->cgrp; + + /* + * This condition should have been caught in + * __match_event() and we should be sharing an RMID. + */ + WARN_ON_ONCE(ac == bc); + + if (cgroup_is_descendant(ac->css.cgroup, bc->css.cgroup) || + cgroup_is_descendant(bc->css.cgroup, ac->css.cgroup)) + return true; + + return false; + } + + if (a->cgrp || b->cgrp) { + struct perf_cgroup *ac, *bc; + + /* + * cgroup and system-wide events are mutually exclusive + */ + if ((a->cgrp && !(b->attach_state & PERF_ATTACH_TASK)) || + (b->cgrp && !(a->attach_state & PERF_ATTACH_TASK))) + return true; + + /* + * Ensure neither event is part of the other's cgroup + */ + ac = event_to_cgroup(a); + bc = event_to_cgroup(b); + if (ac == bc) + return true; + + /* + * Must have cgroup and non-intersecting task events. + */ + if (!ac || !bc) + return false; + + /* + * We have cgroup and task events, and the task belongs + * to a cgroup. Check for for overlap. + */ + if (cgroup_is_descendant(ac->css.cgroup, bc->css.cgroup) || + cgroup_is_descendant(bc->css.cgroup, ac->css.cgroup)) + return true; + + return false; + } +#endif + /* + * If one of them is not a task, same story as above with cgroups. + */ + if (!(a->attach_state & PERF_ATTACH_TASK) || + !(b->attach_state & PERF_ATTACH_TASK)) + return true; + + /* + * Must be non-overlapping. + */ + return false; +} + +struct rmid_read { + unsigned int rmid; + atomic64_t value; +}; + +static void __intel_cqm_event_count(void *info); + +/* + * Exchange the RMID of a group of events. + */ +static unsigned int +intel_cqm_xchg_rmid(struct perf_event *group, unsigned int rmid) +{ + struct perf_event *event; + unsigned int old_rmid = group->hw.cqm_rmid; + struct list_head *head = &group->hw.cqm_group_entry; + + lockdep_assert_held(&cache_mutex); + + /* + * If our RMID is being deallocated, perform a read now. + */ + if (__rmid_valid(old_rmid) && !__rmid_valid(rmid)) { + struct rmid_read rr = { + .value = ATOMIC64_INIT(0), + .rmid = old_rmid, + }; + + on_each_cpu_mask(&cqm_cpumask, __intel_cqm_event_count, + &rr, 1); + local64_set(&group->count, atomic64_read(&rr.value)); + } + + raw_spin_lock_irq(&cache_lock); + + group->hw.cqm_rmid = rmid; + list_for_each_entry(event, head, hw.cqm_group_entry) + event->hw.cqm_rmid = rmid; + + raw_spin_unlock_irq(&cache_lock); + + return old_rmid; +} + +/* + * If we fail to assign a new RMID for intel_cqm_rotation_rmid because + * cachelines are still tagged with RMIDs in limbo, we progressively + * increment the threshold until we find an RMID in limbo with <= + * __intel_cqm_threshold lines tagged. This is designed to mitigate the + * problem where cachelines tagged with an RMID are not steadily being + * evicted. + * + * On successful rotations we decrease the threshold back towards zero. + * + * __intel_cqm_max_threshold provides an upper bound on the threshold, + * and is measured in bytes because it's exposed to userland. + */ +static unsigned int __intel_cqm_threshold; +static unsigned int __intel_cqm_max_threshold; + +/* + * Test whether an RMID has a zero occupancy value on this cpu. + */ +static void intel_cqm_stable(void *arg) +{ + struct cqm_rmid_entry *entry; + + list_for_each_entry(entry, &cqm_rmid_limbo_lru, list) { + if (entry->state != RMID_AVAILABLE) + break; + + if (__rmid_read(entry->rmid) > __intel_cqm_threshold) + entry->state = RMID_DIRTY; + } +} + +/* + * If we have group events waiting for an RMID that don't conflict with + * events already running, assign @rmid. + */ +static bool intel_cqm_sched_in_event(unsigned int rmid) +{ + struct perf_event *leader, *event; + + lockdep_assert_held(&cache_mutex); + + leader = list_first_entry(&cache_groups, struct perf_event, + hw.cqm_groups_entry); + event = leader; + + list_for_each_entry_continue(event, &cache_groups, + hw.cqm_groups_entry) { + if (__rmid_valid(event->hw.cqm_rmid)) + continue; + + if (__conflict_event(event, leader)) + continue; + + intel_cqm_xchg_rmid(event, rmid); + return true; + } + + return false; +} + +/* + * Initially use this constant for both the limbo queue time and the + * rotation timer interval, pmu::hrtimer_interval_ms. + * + * They don't need to be the same, but the two are related since if you + * rotate faster than you recycle RMIDs, you may run out of available + * RMIDs. + */ +#define RMID_DEFAULT_QUEUE_TIME 250 /* ms */ + +static unsigned int __rmid_queue_time_ms = RMID_DEFAULT_QUEUE_TIME; + +/* + * intel_cqm_rmid_stabilize - move RMIDs from limbo to free list + * @nr_available: number of freeable RMIDs on the limbo list + * + * Quiescent state; wait for all 'freed' RMIDs to become unused, i.e. no + * cachelines are tagged with those RMIDs. After this we can reuse them + * and know that the current set of active RMIDs is stable. + * + * Return %true or %false depending on whether stabilization needs to be + * reattempted. + * + * If we return %true then @nr_available is updated to indicate the + * number of RMIDs on the limbo list that have been queued for the + * minimum queue time (RMID_AVAILABLE), but whose data occupancy values + * are above __intel_cqm_threshold. + */ +static bool intel_cqm_rmid_stabilize(unsigned int *available) +{ + struct cqm_rmid_entry *entry, *tmp; + + lockdep_assert_held(&cache_mutex); + + *available = 0; + list_for_each_entry(entry, &cqm_rmid_limbo_lru, list) { + unsigned long min_queue_time; + unsigned long now = jiffies; + + /* + * We hold RMIDs placed into limbo for a minimum queue + * time. Before the minimum queue time has elapsed we do + * not recycle RMIDs. + * + * The reasoning is that until a sufficient time has + * passed since we stopped using an RMID, any RMID + * placed onto the limbo list will likely still have + * data tagged in the cache, which means we'll probably + * fail to recycle it anyway. + * + * We can save ourselves an expensive IPI by skipping + * any RMIDs that have not been queued for the minimum + * time. + */ + min_queue_time = entry->queue_time + + msecs_to_jiffies(__rmid_queue_time_ms); + + if (time_after(min_queue_time, now)) + break; + + entry->state = RMID_AVAILABLE; + (*available)++; + } + + /* + * Fast return if none of the RMIDs on the limbo list have been + * sitting on the queue for the minimum queue time. + */ + if (!*available) + return false; + + /* + * Test whether an RMID is free for each package. + */ + on_each_cpu_mask(&cqm_cpumask, intel_cqm_stable, NULL, true); + + list_for_each_entry_safe(entry, tmp, &cqm_rmid_limbo_lru, list) { + /* + * Exhausted all RMIDs that have waited min queue time. + */ + if (entry->state == RMID_YOUNG) + break; + + if (entry->state == RMID_DIRTY) + continue; + + list_del(&entry->list); /* remove from limbo */ + + /* + * The rotation RMID gets priority if it's + * currently invalid. In which case, skip adding + * the RMID to the the free lru. + */ + if (!__rmid_valid(intel_cqm_rotation_rmid)) { + intel_cqm_rotation_rmid = entry->rmid; + continue; + } + + /* + * If we have groups waiting for RMIDs, hand + * them one now provided they don't conflict. + */ + if (intel_cqm_sched_in_event(entry->rmid)) + continue; + + /* + * Otherwise place it onto the free list. + */ + list_add_tail(&entry->list, &cqm_rmid_free_lru); + } + + + return __rmid_valid(intel_cqm_rotation_rmid); +} + +/* + * Pick a victim group and move it to the tail of the group list. + * @next: The first group without an RMID + */ +static void __intel_cqm_pick_and_rotate(struct perf_event *next) +{ + struct perf_event *rotor; + unsigned int rmid; + + lockdep_assert_held(&cache_mutex); + + rotor = list_first_entry(&cache_groups, struct perf_event, + hw.cqm_groups_entry); + + /* + * The group at the front of the list should always have a valid + * RMID. If it doesn't then no groups have RMIDs assigned and we + * don't need to rotate the list. + */ + if (next == rotor) + return; + + rmid = intel_cqm_xchg_rmid(rotor, INVALID_RMID); + __put_rmid(rmid); + + list_rotate_left(&cache_groups); +} + +/* + * Deallocate the RMIDs from any events that conflict with @event, and + * place them on the back of the group list. + */ +static void intel_cqm_sched_out_conflicting_events(struct perf_event *event) +{ + struct perf_event *group, *g; + unsigned int rmid; + + lockdep_assert_held(&cache_mutex); + + list_for_each_entry_safe(group, g, &cache_groups, hw.cqm_groups_entry) { + if (group == event) + continue; + + rmid = group->hw.cqm_rmid; + + /* + * Skip events that don't have a valid RMID. + */ + if (!__rmid_valid(rmid)) + continue; + + /* + * No conflict? No problem! Leave the event alone. + */ + if (!__conflict_event(group, event)) + continue; + + intel_cqm_xchg_rmid(group, INVALID_RMID); + __put_rmid(rmid); + } +} + +/* + * Attempt to rotate the groups and assign new RMIDs. + * + * We rotate for two reasons, + * 1. To handle the scheduling of conflicting events + * 2. To recycle RMIDs + * + * Rotating RMIDs is complicated because the hardware doesn't give us + * any clues. + * + * There's problems with the hardware interface; when you change the + * task:RMID map cachelines retain their 'old' tags, giving a skewed + * picture. In order to work around this, we must always keep one free + * RMID - intel_cqm_rotation_rmid. + * + * Rotation works by taking away an RMID from a group (the old RMID), + * and assigning the free RMID to another group (the new RMID). We must + * then wait for the old RMID to not be used (no cachelines tagged). + * This ensure that all cachelines are tagged with 'active' RMIDs. At + * this point we can start reading values for the new RMID and treat the + * old RMID as the free RMID for the next rotation. + * + * Return %true or %false depending on whether we did any rotating. + */ +static bool __intel_cqm_rmid_rotate(void) +{ + struct perf_event *group, *start = NULL; + unsigned int threshold_limit; + unsigned int nr_needed = 0; + unsigned int nr_available; + bool rotated = false; + + mutex_lock(&cache_mutex); + +again: + /* + * Fast path through this function if there are no groups and no + * RMIDs that need cleaning. + */ + if (list_empty(&cache_groups) && list_empty(&cqm_rmid_limbo_lru)) + goto out; + + list_for_each_entry(group, &cache_groups, hw.cqm_groups_entry) { + if (!__rmid_valid(group->hw.cqm_rmid)) { + if (!start) + start = group; + nr_needed++; + } + } + + /* + * We have some event groups, but they all have RMIDs assigned + * and no RMIDs need cleaning. + */ + if (!nr_needed && list_empty(&cqm_rmid_limbo_lru)) + goto out; + + if (!nr_needed) + goto stabilize; + + /* + * We have more event groups without RMIDs than available RMIDs, + * or we have event groups that conflict with the ones currently + * scheduled. + * + * We force deallocate the rmid of the group at the head of + * cache_groups. The first event group without an RMID then gets + * assigned intel_cqm_rotation_rmid. This ensures we always make + * forward progress. + * + * Rotate the cache_groups list so the previous head is now the + * tail. + */ + __intel_cqm_pick_and_rotate(start); + + /* + * If the rotation is going to succeed, reduce the threshold so + * that we don't needlessly reuse dirty RMIDs. + */ + if (__rmid_valid(intel_cqm_rotation_rmid)) { + intel_cqm_xchg_rmid(start, intel_cqm_rotation_rmid); + intel_cqm_rotation_rmid = __get_rmid(); + + intel_cqm_sched_out_conflicting_events(start); + + if (__intel_cqm_threshold) + __intel_cqm_threshold--; + } + + rotated = true; + +stabilize: + /* + * We now need to stablize the RMID we freed above (if any) to + * ensure that the next time we rotate we have an RMID with zero + * occupancy value. + * + * Alternatively, if we didn't need to perform any rotation, + * we'll have a bunch of RMIDs in limbo that need stabilizing. + */ + threshold_limit = __intel_cqm_max_threshold / cqm_l3_scale; + + while (intel_cqm_rmid_stabilize(&nr_available) && + __intel_cqm_threshold < threshold_limit) { + unsigned int steal_limit; + + /* + * Don't spin if nobody is actively waiting for an RMID, + * the rotation worker will be kicked as soon as an + * event needs an RMID anyway. + */ + if (!nr_needed) + break; + + /* Allow max 25% of RMIDs to be in limbo. */ + steal_limit = (cqm_max_rmid + 1) / 4; + + /* + * We failed to stabilize any RMIDs so our rotation + * logic is now stuck. In order to make forward progress + * we have a few options: + * + * 1. rotate ("steal") another RMID + * 2. increase the threshold + * 3. do nothing + * + * We do both of 1. and 2. until we hit the steal limit. + * + * The steal limit prevents all RMIDs ending up on the + * limbo list. This can happen if every RMID has a + * non-zero occupancy above threshold_limit, and the + * occupancy values aren't dropping fast enough. + * + * Note that there is prioritisation at work here - we'd + * rather increase the number of RMIDs on the limbo list + * than increase the threshold, because increasing the + * threshold skews the event data (because we reuse + * dirty RMIDs) - threshold bumps are a last resort. + */ + if (nr_available < steal_limit) + goto again; + + __intel_cqm_threshold++; + } + +out: + mutex_unlock(&cache_mutex); + return rotated; +} + +static void intel_cqm_rmid_rotate(struct work_struct *work); + +static DECLARE_DELAYED_WORK(intel_cqm_rmid_work, intel_cqm_rmid_rotate); + +static struct pmu intel_cqm_pmu; + +static void intel_cqm_rmid_rotate(struct work_struct *work) +{ + unsigned long delay; + + __intel_cqm_rmid_rotate(); + + delay = msecs_to_jiffies(intel_cqm_pmu.hrtimer_interval_ms); + schedule_delayed_work(&intel_cqm_rmid_work, delay); +} + +/* + * Find a group and setup RMID. + * + * If we're part of a group, we use the group's RMID. + */ +static void intel_cqm_setup_event(struct perf_event *event, + struct perf_event **group) +{ + struct perf_event *iter; + unsigned int rmid; + bool conflict = false; + + list_for_each_entry(iter, &cache_groups, hw.cqm_groups_entry) { + rmid = iter->hw.cqm_rmid; + + if (__match_event(iter, event)) { + /* All tasks in a group share an RMID */ + event->hw.cqm_rmid = rmid; + *group = iter; + return; + } + + /* + * We only care about conflicts for events that are + * actually scheduled in (and hence have a valid RMID). + */ + if (__conflict_event(iter, event) && __rmid_valid(rmid)) + conflict = true; + } + + if (conflict) + rmid = INVALID_RMID; + else + rmid = __get_rmid(); + + event->hw.cqm_rmid = rmid; +} + +static void intel_cqm_event_read(struct perf_event *event) +{ + unsigned long flags; + unsigned int rmid; + u64 val; + + /* + * Task events are handled by intel_cqm_event_count(). + */ + if (event->cpu == -1) + return; + + raw_spin_lock_irqsave(&cache_lock, flags); + rmid = event->hw.cqm_rmid; + + if (!__rmid_valid(rmid)) + goto out; + + val = __rmid_read(rmid); + + /* + * Ignore this reading on error states and do not update the value. + */ + if (val & (RMID_VAL_ERROR | RMID_VAL_UNAVAIL)) + goto out; + + local64_set(&event->count, val); +out: + raw_spin_unlock_irqrestore(&cache_lock, flags); +} + +static void __intel_cqm_event_count(void *info) +{ + struct rmid_read *rr = info; + u64 val; + + val = __rmid_read(rr->rmid); + + if (val & (RMID_VAL_ERROR | RMID_VAL_UNAVAIL)) + return; + + atomic64_add(val, &rr->value); +} + +static inline bool cqm_group_leader(struct perf_event *event) +{ + return !list_empty(&event->hw.cqm_groups_entry); +} + +static u64 intel_cqm_event_count(struct perf_event *event) +{ + unsigned long flags; + struct rmid_read rr = { + .value = ATOMIC64_INIT(0), + }; + + /* + * We only need to worry about task events. System-wide events + * are handled like usual, i.e. entirely with + * intel_cqm_event_read(). + */ + if (event->cpu != -1) + return __perf_event_count(event); + + /* + * Only the group leader gets to report values. This stops us + * reporting duplicate values to userspace, and gives us a clear + * rule for which task gets to report the values. + * + * Note that it is impossible to attribute these values to + * specific packages - we forfeit that ability when we create + * task events. + */ + if (!cqm_group_leader(event)) + return 0; + + /* + * Notice that we don't perform the reading of an RMID + * atomically, because we can't hold a spin lock across the + * IPIs. + * + * Speculatively perform the read, since @event might be + * assigned a different (possibly invalid) RMID while we're + * busying performing the IPI calls. It's therefore necessary to + * check @event's RMID afterwards, and if it has changed, + * discard the result of the read. + */ + rr.rmid = ACCESS_ONCE(event->hw.cqm_rmid); + + if (!__rmid_valid(rr.rmid)) + goto out; + + on_each_cpu_mask(&cqm_cpumask, __intel_cqm_event_count, &rr, 1); + + raw_spin_lock_irqsave(&cache_lock, flags); + if (event->hw.cqm_rmid == rr.rmid) + local64_set(&event->count, atomic64_read(&rr.value)); + raw_spin_unlock_irqrestore(&cache_lock, flags); +out: + return __perf_event_count(event); +} + +static void intel_cqm_event_start(struct perf_event *event, int mode) +{ + struct intel_cqm_state *state = this_cpu_ptr(&cqm_state); + unsigned int rmid = event->hw.cqm_rmid; + unsigned long flags; + + if (!(event->hw.cqm_state & PERF_HES_STOPPED)) + return; + + event->hw.cqm_state &= ~PERF_HES_STOPPED; + + raw_spin_lock_irqsave(&state->lock, flags); + + if (state->cnt++) + WARN_ON_ONCE(state->rmid != rmid); + else + WARN_ON_ONCE(state->rmid); + + state->rmid = rmid; + wrmsrl(MSR_IA32_PQR_ASSOC, state->rmid); + + raw_spin_unlock_irqrestore(&state->lock, flags); +} + +static void intel_cqm_event_stop(struct perf_event *event, int mode) +{ + struct intel_cqm_state *state = this_cpu_ptr(&cqm_state); + unsigned long flags; + + if (event->hw.cqm_state & PERF_HES_STOPPED) + return; + + event->hw.cqm_state |= PERF_HES_STOPPED; + + raw_spin_lock_irqsave(&state->lock, flags); + intel_cqm_event_read(event); + + if (!--state->cnt) { + state->rmid = 0; + wrmsrl(MSR_IA32_PQR_ASSOC, 0); + } else { + WARN_ON_ONCE(!state->rmid); + } + + raw_spin_unlock_irqrestore(&state->lock, flags); +} + +static int intel_cqm_event_add(struct perf_event *event, int mode) +{ + unsigned long flags; + unsigned int rmid; + + raw_spin_lock_irqsave(&cache_lock, flags); + + event->hw.cqm_state = PERF_HES_STOPPED; + rmid = event->hw.cqm_rmid; + + if (__rmid_valid(rmid) && (mode & PERF_EF_START)) + intel_cqm_event_start(event, mode); + + raw_spin_unlock_irqrestore(&cache_lock, flags); + + return 0; +} + +static void intel_cqm_event_del(struct perf_event *event, int mode) +{ + intel_cqm_event_stop(event, mode); +} + +static void intel_cqm_event_destroy(struct perf_event *event) +{ + struct perf_event *group_other = NULL; + + mutex_lock(&cache_mutex); + + /* + * If there's another event in this group... + */ + if (!list_empty(&event->hw.cqm_group_entry)) { + group_other = list_first_entry(&event->hw.cqm_group_entry, + struct perf_event, + hw.cqm_group_entry); + list_del(&event->hw.cqm_group_entry); + } + + /* + * And we're the group leader.. + */ + if (cqm_group_leader(event)) { + /* + * If there was a group_other, make that leader, otherwise + * destroy the group and return the RMID. + */ + if (group_other) { + list_replace(&event->hw.cqm_groups_entry, + &group_other->hw.cqm_groups_entry); + } else { + unsigned int rmid = event->hw.cqm_rmid; + + if (__rmid_valid(rmid)) + __put_rmid(rmid); + list_del(&event->hw.cqm_groups_entry); + } + } + + mutex_unlock(&cache_mutex); +} + +static int intel_cqm_event_init(struct perf_event *event) +{ + struct perf_event *group = NULL; + bool rotate = false; + + if (event->attr.type != intel_cqm_pmu.type) + return -ENOENT; + + if (event->attr.config & ~QOS_EVENT_MASK) + return -EINVAL; + + /* unsupported modes and filters */ + if (event->attr.exclude_user || + event->attr.exclude_kernel || + event->attr.exclude_hv || + event->attr.exclude_idle || + event->attr.exclude_host || + event->attr.exclude_guest || + event->attr.sample_period) /* no sampling */ + return -EINVAL; + + INIT_LIST_HEAD(&event->hw.cqm_group_entry); + INIT_LIST_HEAD(&event->hw.cqm_groups_entry); + + event->destroy = intel_cqm_event_destroy; + + mutex_lock(&cache_mutex); + + /* Will also set rmid */ + intel_cqm_setup_event(event, &group); + + if (group) { + list_add_tail(&event->hw.cqm_group_entry, + &group->hw.cqm_group_entry); + } else { + list_add_tail(&event->hw.cqm_groups_entry, + &cache_groups); + + /* + * All RMIDs are either in use or have recently been + * used. Kick the rotation worker to clean/free some. + * + * We only do this for the group leader, rather than for + * every event in a group to save on needless work. + */ + if (!__rmid_valid(event->hw.cqm_rmid)) + rotate = true; + } + + mutex_unlock(&cache_mutex); + + if (rotate) + schedule_delayed_work(&intel_cqm_rmid_work, 0); + + return 0; +} + +EVENT_ATTR_STR(llc_occupancy, intel_cqm_llc, "event=0x01"); +EVENT_ATTR_STR(llc_occupancy.per-pkg, intel_cqm_llc_pkg, "1"); +EVENT_ATTR_STR(llc_occupancy.unit, intel_cqm_llc_unit, "Bytes"); +EVENT_ATTR_STR(llc_occupancy.scale, intel_cqm_llc_scale, NULL); +EVENT_ATTR_STR(llc_occupancy.snapshot, intel_cqm_llc_snapshot, "1"); + +static struct attribute *intel_cqm_events_attr[] = { + EVENT_PTR(intel_cqm_llc), + EVENT_PTR(intel_cqm_llc_pkg), + EVENT_PTR(intel_cqm_llc_unit), + EVENT_PTR(intel_cqm_llc_scale), + EVENT_PTR(intel_cqm_llc_snapshot), + NULL, +}; + +static struct attribute_group intel_cqm_events_group = { + .name = "events", + .attrs = intel_cqm_events_attr, +}; + +PMU_FORMAT_ATTR(event, "config:0-7"); +static struct attribute *intel_cqm_formats_attr[] = { + &format_attr_event.attr, + NULL, +}; + +static struct attribute_group intel_cqm_format_group = { + .name = "format", + .attrs = intel_cqm_formats_attr, +}; + +static ssize_t +max_recycle_threshold_show(struct device *dev, struct device_attribute *attr, + char *page) +{ + ssize_t rv; + + mutex_lock(&cache_mutex); + rv = snprintf(page, PAGE_SIZE-1, "%u\n", __intel_cqm_max_threshold); + mutex_unlock(&cache_mutex); + + return rv; +} + +static ssize_t +max_recycle_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int bytes, cachelines; + int ret; + + ret = kstrtouint(buf, 0, &bytes); + if (ret) + return ret; + + mutex_lock(&cache_mutex); + + __intel_cqm_max_threshold = bytes; + cachelines = bytes / cqm_l3_scale; + + /* + * The new maximum takes effect immediately. + */ + if (__intel_cqm_threshold > cachelines) + __intel_cqm_threshold = cachelines; + + mutex_unlock(&cache_mutex); + + return count; +} + +static DEVICE_ATTR_RW(max_recycle_threshold); + +static struct attribute *intel_cqm_attrs[] = { + &dev_attr_max_recycle_threshold.attr, + NULL, +}; + +static const struct attribute_group intel_cqm_group = { + .attrs = intel_cqm_attrs, +}; + +static const struct attribute_group *intel_cqm_attr_groups[] = { + &intel_cqm_events_group, + &intel_cqm_format_group, + &intel_cqm_group, + NULL, +}; + +static struct pmu intel_cqm_pmu = { + .hrtimer_interval_ms = RMID_DEFAULT_QUEUE_TIME, + .attr_groups = intel_cqm_attr_groups, + .task_ctx_nr = perf_sw_context, + .event_init = intel_cqm_event_init, + .add = intel_cqm_event_add, + .del = intel_cqm_event_del, + .start = intel_cqm_event_start, + .stop = intel_cqm_event_stop, + .read = intel_cqm_event_read, + .count = intel_cqm_event_count, +}; + +static inline void cqm_pick_event_reader(int cpu) +{ + int phys_id = topology_physical_package_id(cpu); + int i; + + for_each_cpu(i, &cqm_cpumask) { + if (phys_id == topology_physical_package_id(i)) + return; /* already got reader for this socket */ + } + + cpumask_set_cpu(cpu, &cqm_cpumask); +} + +static void intel_cqm_cpu_prepare(unsigned int cpu) +{ + struct intel_cqm_state *state = &per_cpu(cqm_state, cpu); + struct cpuinfo_x86 *c = &cpu_data(cpu); + + raw_spin_lock_init(&state->lock); + state->rmid = 0; + state->cnt = 0; + + WARN_ON(c->x86_cache_max_rmid != cqm_max_rmid); + WARN_ON(c->x86_cache_occ_scale != cqm_l3_scale); +} + +static void intel_cqm_cpu_exit(unsigned int cpu) +{ + int phys_id = topology_physical_package_id(cpu); + int i; + + /* + * Is @cpu a designated cqm reader? + */ + if (!cpumask_test_and_clear_cpu(cpu, &cqm_cpumask)) + return; + + for_each_online_cpu(i) { + if (i == cpu) + continue; + + if (phys_id == topology_physical_package_id(i)) { + cpumask_set_cpu(i, &cqm_cpumask); + break; + } + } +} + +static int intel_cqm_cpu_notifier(struct notifier_block *nb, + unsigned long action, void *hcpu) +{ + unsigned int cpu = (unsigned long)hcpu; + + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_UP_PREPARE: + intel_cqm_cpu_prepare(cpu); + break; + case CPU_DOWN_PREPARE: + intel_cqm_cpu_exit(cpu); + break; + case CPU_STARTING: + cqm_pick_event_reader(cpu); + break; + } + + return NOTIFY_OK; +} + +static const struct x86_cpu_id intel_cqm_match[] = { + { .vendor = X86_VENDOR_INTEL, .feature = X86_FEATURE_CQM_OCCUP_LLC }, + {} +}; + +static int __init intel_cqm_init(void) +{ + char *str, scale[20]; + int i, cpu, ret; + + if (!x86_match_cpu(intel_cqm_match)) + return -ENODEV; + + cqm_l3_scale = boot_cpu_data.x86_cache_occ_scale; + + /* + * It's possible that not all resources support the same number + * of RMIDs. Instead of making scheduling much more complicated + * (where we have to match a task's RMID to a cpu that supports + * that many RMIDs) just find the minimum RMIDs supported across + * all cpus. + * + * Also, check that the scales match on all cpus. + */ + cpu_notifier_register_begin(); + + for_each_online_cpu(cpu) { + struct cpuinfo_x86 *c = &cpu_data(cpu); + + if (c->x86_cache_max_rmid < cqm_max_rmid) + cqm_max_rmid = c->x86_cache_max_rmid; + + if (c->x86_cache_occ_scale != cqm_l3_scale) { + pr_err("Multiple LLC scale values, disabling\n"); + ret = -EINVAL; + goto out; + } + } + + /* + * A reasonable upper limit on the max threshold is the number + * of lines tagged per RMID if all RMIDs have the same number of + * lines tagged in the LLC. + * + * For a 35MB LLC and 56 RMIDs, this is ~1.8% of the LLC. + */ + __intel_cqm_max_threshold = + boot_cpu_data.x86_cache_size * 1024 / (cqm_max_rmid + 1); + + snprintf(scale, sizeof(scale), "%u", cqm_l3_scale); + str = kstrdup(scale, GFP_KERNEL); + if (!str) { + ret = -ENOMEM; + goto out; + } + + event_attr_intel_cqm_llc_scale.event_str = str; + + ret = intel_cqm_setup_rmid_cache(); + if (ret) + goto out; + + for_each_online_cpu(i) { + intel_cqm_cpu_prepare(i); + cqm_pick_event_reader(i); + } + + __perf_cpu_notifier(intel_cqm_cpu_notifier); + + ret = perf_pmu_register(&intel_cqm_pmu, "intel_cqm", -1); + if (ret) + pr_err("Intel CQM perf registration failed: %d\n", ret); + else + pr_info("Intel CQM monitoring enabled\n"); + +out: + cpu_notifier_register_done(); + + return ret; +} +device_initcall(intel_cqm_init); diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 07398339836426..ca69ea56c712b5 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -461,7 +461,8 @@ void intel_pmu_enable_bts(u64 config) debugctlmsr |= DEBUGCTLMSR_TR; debugctlmsr |= DEBUGCTLMSR_BTS; - debugctlmsr |= DEBUGCTLMSR_BTINT; + if (config & ARCH_PERFMON_EVENTSEL_INT) + debugctlmsr |= DEBUGCTLMSR_BTINT; if (!(config & ARCH_PERFMON_EVENTSEL_OS)) debugctlmsr |= DEBUGCTLMSR_BTS_OFF_OS; @@ -611,6 +612,10 @@ struct event_constraint intel_snb_pebs_event_constraints[] = { INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */ /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf), + INTEL_EXCLEVT_CONSTRAINT(0xd0, 0xf), /* MEM_UOP_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd3, 0xf), /* MEM_LOAD_UOPS_LLC_MISS_RETIRED.* */ /* Allow all events as PEBS with no flags */ INTEL_ALL_EVENT_CONSTRAINT(0, 0xf), EVENT_CONSTRAINT_END @@ -622,6 +627,10 @@ struct event_constraint intel_ivb_pebs_event_constraints[] = { INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */ /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf), + INTEL_EXCLEVT_CONSTRAINT(0xd0, 0xf), /* MEM_UOP_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */ + INTEL_EXCLEVT_CONSTRAINT(0xd3, 0xf), /* MEM_LOAD_UOPS_LLC_MISS_RETIRED.* */ /* Allow all events as PEBS with no flags */ INTEL_ALL_EVENT_CONSTRAINT(0, 0xf), EVENT_CONSTRAINT_END @@ -633,16 +642,16 @@ struct event_constraint intel_hsw_pebs_event_constraints[] = { /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf), INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_NA(0x01c2, 0xf), /* UOPS_RETIRED.ALL */ - INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x11d0, 0xf), /* MEM_UOPS_RETIRED.STLB_MISS_LOADS */ - INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x21d0, 0xf), /* MEM_UOPS_RETIRED.LOCK_LOADS */ - INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x41d0, 0xf), /* MEM_UOPS_RETIRED.SPLIT_LOADS */ - INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x81d0, 0xf), /* MEM_UOPS_RETIRED.ALL_LOADS */ - INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x12d0, 0xf), /* MEM_UOPS_RETIRED.STLB_MISS_STORES */ - INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x42d0, 0xf), /* MEM_UOPS_RETIRED.SPLIT_STORES */ - INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x82d0, 0xf), /* MEM_UOPS_RETIRED.ALL_STORES */ - INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */ - INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD(0xd2, 0xf), /* MEM_LOAD_UOPS_L3_HIT_RETIRED.* */ - INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_LD(0xd3, 0xf), /* MEM_LOAD_UOPS_L3_MISS_RETIRED.* */ + INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XLD(0x11d0, 0xf), /* MEM_UOPS_RETIRED.STLB_MISS_LOADS */ + INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XLD(0x21d0, 0xf), /* MEM_UOPS_RETIRED.LOCK_LOADS */ + INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XLD(0x41d0, 0xf), /* MEM_UOPS_RETIRED.SPLIT_LOADS */ + INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XLD(0x81d0, 0xf), /* MEM_UOPS_RETIRED.ALL_LOADS */ + INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XST(0x12d0, 0xf), /* MEM_UOPS_RETIRED.STLB_MISS_STORES */ + INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XST(0x42d0, 0xf), /* MEM_UOPS_RETIRED.SPLIT_STORES */ + INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XST(0x82d0, 0xf), /* MEM_UOPS_RETIRED.ALL_STORES */ + INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_XLD(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */ + INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_XLD(0xd2, 0xf), /* MEM_LOAD_UOPS_L3_HIT_RETIRED.* */ + INTEL_FLAGS_EVENT_CONSTRAINT_DATALA_XLD(0xd3, 0xf), /* MEM_LOAD_UOPS_L3_MISS_RETIRED.* */ /* Allow all events as PEBS with no flags */ INTEL_ALL_EVENT_CONSTRAINT(0, 0xf), EVENT_CONSTRAINT_END diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index 58f1a94beaf09f..94e5b506caa6d1 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -39,6 +39,7 @@ static enum { #define LBR_IND_JMP_BIT 6 /* do not capture indirect jumps */ #define LBR_REL_JMP_BIT 7 /* do not capture relative jumps */ #define LBR_FAR_BIT 8 /* do not capture far branches */ +#define LBR_CALL_STACK_BIT 9 /* enable call stack */ #define LBR_KERNEL (1 << LBR_KERNEL_BIT) #define LBR_USER (1 << LBR_USER_BIT) @@ -49,6 +50,7 @@ static enum { #define LBR_REL_JMP (1 << LBR_REL_JMP_BIT) #define LBR_IND_JMP (1 << LBR_IND_JMP_BIT) #define LBR_FAR (1 << LBR_FAR_BIT) +#define LBR_CALL_STACK (1 << LBR_CALL_STACK_BIT) #define LBR_PLM (LBR_KERNEL | LBR_USER) @@ -69,33 +71,31 @@ static enum { #define LBR_FROM_FLAG_IN_TX (1ULL << 62) #define LBR_FROM_FLAG_ABORT (1ULL << 61) -#define for_each_branch_sample_type(x) \ - for ((x) = PERF_SAMPLE_BRANCH_USER; \ - (x) < PERF_SAMPLE_BRANCH_MAX; (x) <<= 1) - /* * x86control flow change classification * x86control flow changes include branches, interrupts, traps, faults */ enum { - X86_BR_NONE = 0, /* unknown */ - - X86_BR_USER = 1 << 0, /* branch target is user */ - X86_BR_KERNEL = 1 << 1, /* branch target is kernel */ - - X86_BR_CALL = 1 << 2, /* call */ - X86_BR_RET = 1 << 3, /* return */ - X86_BR_SYSCALL = 1 << 4, /* syscall */ - X86_BR_SYSRET = 1 << 5, /* syscall return */ - X86_BR_INT = 1 << 6, /* sw interrupt */ - X86_BR_IRET = 1 << 7, /* return from interrupt */ - X86_BR_JCC = 1 << 8, /* conditional */ - X86_BR_JMP = 1 << 9, /* jump */ - X86_BR_IRQ = 1 << 10,/* hw interrupt or trap or fault */ - X86_BR_IND_CALL = 1 << 11,/* indirect calls */ - X86_BR_ABORT = 1 << 12,/* transaction abort */ - X86_BR_IN_TX = 1 << 13,/* in transaction */ - X86_BR_NO_TX = 1 << 14,/* not in transaction */ + X86_BR_NONE = 0, /* unknown */ + + X86_BR_USER = 1 << 0, /* branch target is user */ + X86_BR_KERNEL = 1 << 1, /* branch target is kernel */ + + X86_BR_CALL = 1 << 2, /* call */ + X86_BR_RET = 1 << 3, /* return */ + X86_BR_SYSCALL = 1 << 4, /* syscall */ + X86_BR_SYSRET = 1 << 5, /* syscall return */ + X86_BR_INT = 1 << 6, /* sw interrupt */ + X86_BR_IRET = 1 << 7, /* return from interrupt */ + X86_BR_JCC = 1 << 8, /* conditional */ + X86_BR_JMP = 1 << 9, /* jump */ + X86_BR_IRQ = 1 << 10,/* hw interrupt or trap or fault */ + X86_BR_IND_CALL = 1 << 11,/* indirect calls */ + X86_BR_ABORT = 1 << 12,/* transaction abort */ + X86_BR_IN_TX = 1 << 13,/* in transaction */ + X86_BR_NO_TX = 1 << 14,/* not in transaction */ + X86_BR_ZERO_CALL = 1 << 15,/* zero length call */ + X86_BR_CALL_STACK = 1 << 16,/* call stack */ }; #define X86_BR_PLM (X86_BR_USER | X86_BR_KERNEL) @@ -112,13 +112,15 @@ enum { X86_BR_JMP |\ X86_BR_IRQ |\ X86_BR_ABORT |\ - X86_BR_IND_CALL) + X86_BR_IND_CALL |\ + X86_BR_ZERO_CALL) #define X86_BR_ALL (X86_BR_PLM | X86_BR_ANY) #define X86_BR_ANY_CALL \ (X86_BR_CALL |\ X86_BR_IND_CALL |\ + X86_BR_ZERO_CALL |\ X86_BR_SYSCALL |\ X86_BR_IRQ |\ X86_BR_INT) @@ -130,17 +132,32 @@ static void intel_pmu_lbr_filter(struct cpu_hw_events *cpuc); * otherwise it becomes near impossible to get a reliable stack. */ -static void __intel_pmu_lbr_enable(void) +static void __intel_pmu_lbr_enable(bool pmi) { - u64 debugctl; struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); + u64 debugctl, lbr_select = 0, orig_debugctl; - if (cpuc->lbr_sel) - wrmsrl(MSR_LBR_SELECT, cpuc->lbr_sel->config); + /* + * No need to reprogram LBR_SELECT in a PMI, as it + * did not change. + */ + if (cpuc->lbr_sel && !pmi) { + lbr_select = cpuc->lbr_sel->config; + wrmsrl(MSR_LBR_SELECT, lbr_select); + } rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); - debugctl |= (DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI); - wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); + orig_debugctl = debugctl; + debugctl |= DEBUGCTLMSR_LBR; + /* + * LBR callstack does not work well with FREEZE_LBRS_ON_PMI. + * If FREEZE_LBRS_ON_PMI is set, PMI near call/return instructions + * may cause superfluous increase/decrease of LBR_TOS. + */ + if (!(lbr_select & LBR_CALL_STACK)) + debugctl |= DEBUGCTLMSR_FREEZE_LBRS_ON_PMI; + if (orig_debugctl != debugctl) + wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); } static void __intel_pmu_lbr_disable(void) @@ -181,9 +198,116 @@ void intel_pmu_lbr_reset(void) intel_pmu_lbr_reset_64(); } +/* + * TOS = most recently recorded branch + */ +static inline u64 intel_pmu_lbr_tos(void) +{ + u64 tos; + + rdmsrl(x86_pmu.lbr_tos, tos); + return tos; +} + +enum { + LBR_NONE, + LBR_VALID, +}; + +static void __intel_pmu_lbr_restore(struct x86_perf_task_context *task_ctx) +{ + int i; + unsigned lbr_idx, mask; + u64 tos; + + if (task_ctx->lbr_callstack_users == 0 || + task_ctx->lbr_stack_state == LBR_NONE) { + intel_pmu_lbr_reset(); + return; + } + + mask = x86_pmu.lbr_nr - 1; + tos = intel_pmu_lbr_tos(); + for (i = 0; i < x86_pmu.lbr_nr; i++) { + lbr_idx = (tos - i) & mask; + wrmsrl(x86_pmu.lbr_from + lbr_idx, task_ctx->lbr_from[i]); + wrmsrl(x86_pmu.lbr_to + lbr_idx, task_ctx->lbr_to[i]); + } + task_ctx->lbr_stack_state = LBR_NONE; +} + +static void __intel_pmu_lbr_save(struct x86_perf_task_context *task_ctx) +{ + int i; + unsigned lbr_idx, mask; + u64 tos; + + if (task_ctx->lbr_callstack_users == 0) { + task_ctx->lbr_stack_state = LBR_NONE; + return; + } + + mask = x86_pmu.lbr_nr - 1; + tos = intel_pmu_lbr_tos(); + for (i = 0; i < x86_pmu.lbr_nr; i++) { + lbr_idx = (tos - i) & mask; + rdmsrl(x86_pmu.lbr_from + lbr_idx, task_ctx->lbr_from[i]); + rdmsrl(x86_pmu.lbr_to + lbr_idx, task_ctx->lbr_to[i]); + } + task_ctx->lbr_stack_state = LBR_VALID; +} + +void intel_pmu_lbr_sched_task(struct perf_event_context *ctx, bool sched_in) +{ + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); + struct x86_perf_task_context *task_ctx; + + if (!x86_pmu.lbr_nr) + return; + + /* + * If LBR callstack feature is enabled and the stack was saved when + * the task was scheduled out, restore the stack. Otherwise flush + * the LBR stack. + */ + task_ctx = ctx ? ctx->task_ctx_data : NULL; + if (task_ctx) { + if (sched_in) { + __intel_pmu_lbr_restore(task_ctx); + cpuc->lbr_context = ctx; + } else { + __intel_pmu_lbr_save(task_ctx); + } + return; + } + + /* + * When sampling the branck stack in system-wide, it may be + * necessary to flush the stack on context switch. This happens + * when the branch stack does not tag its entries with the pid + * of the current task. Otherwise it becomes impossible to + * associate a branch entry with a task. This ambiguity is more + * likely to appear when the branch stack supports priv level + * filtering and the user sets it to monitor only at the user + * level (which could be a useful measurement in system-wide + * mode). In that case, the risk is high of having a branch + * stack with branch from multiple tasks. + */ + if (sched_in) { + intel_pmu_lbr_reset(); + cpuc->lbr_context = ctx; + } +} + +static inline bool branch_user_callstack(unsigned br_sel) +{ + return (br_sel & X86_BR_USER) && (br_sel & X86_BR_CALL_STACK); +} + void intel_pmu_lbr_enable(struct perf_event *event) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); + struct x86_perf_task_context *task_ctx; if (!x86_pmu.lbr_nr) return; @@ -198,18 +322,33 @@ void intel_pmu_lbr_enable(struct perf_event *event) } cpuc->br_sel = event->hw.branch_reg.reg; + if (branch_user_callstack(cpuc->br_sel) && event->ctx && + event->ctx->task_ctx_data) { + task_ctx = event->ctx->task_ctx_data; + task_ctx->lbr_callstack_users++; + } + cpuc->lbr_users++; + perf_sched_cb_inc(event->ctx->pmu); } void intel_pmu_lbr_disable(struct perf_event *event) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); + struct x86_perf_task_context *task_ctx; if (!x86_pmu.lbr_nr) return; + if (branch_user_callstack(cpuc->br_sel) && event->ctx && + event->ctx->task_ctx_data) { + task_ctx = event->ctx->task_ctx_data; + task_ctx->lbr_callstack_users--; + } + cpuc->lbr_users--; WARN_ON_ONCE(cpuc->lbr_users < 0); + perf_sched_cb_dec(event->ctx->pmu); if (cpuc->enabled && !cpuc->lbr_users) { __intel_pmu_lbr_disable(); @@ -218,12 +357,12 @@ void intel_pmu_lbr_disable(struct perf_event *event) } } -void intel_pmu_lbr_enable_all(void) +void intel_pmu_lbr_enable_all(bool pmi) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); if (cpuc->lbr_users) - __intel_pmu_lbr_enable(); + __intel_pmu_lbr_enable(pmi); } void intel_pmu_lbr_disable_all(void) @@ -234,18 +373,6 @@ void intel_pmu_lbr_disable_all(void) __intel_pmu_lbr_disable(); } -/* - * TOS = most recently recorded branch - */ -static inline u64 intel_pmu_lbr_tos(void) -{ - u64 tos; - - rdmsrl(x86_pmu.lbr_tos, tos); - - return tos; -} - static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc) { unsigned long mask = x86_pmu.lbr_nr - 1; @@ -350,7 +477,7 @@ void intel_pmu_lbr_read(void) * - in case there is no HW filter * - in case the HW filter has errata or limitations */ -static void intel_pmu_setup_sw_lbr_filter(struct perf_event *event) +static int intel_pmu_setup_sw_lbr_filter(struct perf_event *event) { u64 br_type = event->attr.branch_sample_type; int mask = 0; @@ -387,11 +514,21 @@ static void intel_pmu_setup_sw_lbr_filter(struct perf_event *event) if (br_type & PERF_SAMPLE_BRANCH_COND) mask |= X86_BR_JCC; + if (br_type & PERF_SAMPLE_BRANCH_CALL_STACK) { + if (!x86_pmu_has_lbr_callstack()) + return -EOPNOTSUPP; + if (mask & ~(X86_BR_USER | X86_BR_KERNEL)) + return -EINVAL; + mask |= X86_BR_CALL | X86_BR_IND_CALL | X86_BR_RET | + X86_BR_CALL_STACK; + } + /* * stash actual user request into reg, it may * be used by fixup code for some CPU */ event->hw.branch_reg.reg = mask; + return 0; } /* @@ -403,14 +540,14 @@ static int intel_pmu_setup_hw_lbr_filter(struct perf_event *event) { struct hw_perf_event_extra *reg; u64 br_type = event->attr.branch_sample_type; - u64 mask = 0, m; - u64 v; + u64 mask = 0, v; + int i; - for_each_branch_sample_type(m) { - if (!(br_type & m)) + for (i = 0; i < PERF_SAMPLE_BRANCH_MAX_SHIFT; i++) { + if (!(br_type & (1ULL << i))) continue; - v = x86_pmu.lbr_sel_map[m]; + v = x86_pmu.lbr_sel_map[i]; if (v == LBR_NOT_SUPP) return -EOPNOTSUPP; @@ -420,8 +557,12 @@ static int intel_pmu_setup_hw_lbr_filter(struct perf_event *event) reg = &event->hw.branch_reg; reg->idx = EXTRA_REG_LBR; - /* LBR_SELECT operates in suppress mode so invert mask */ - reg->config = ~mask & x86_pmu.lbr_sel_mask; + /* + * The first 9 bits (LBR_SEL_MASK) in LBR_SELECT operate + * in suppress mode. So LBR_SELECT should be set to + * (~mask & LBR_SEL_MASK) | (mask & ~LBR_SEL_MASK) + */ + reg->config = mask ^ x86_pmu.lbr_sel_mask; return 0; } @@ -439,7 +580,9 @@ int intel_pmu_setup_lbr_filter(struct perf_event *event) /* * setup SW LBR filter */ - intel_pmu_setup_sw_lbr_filter(event); + ret = intel_pmu_setup_sw_lbr_filter(event); + if (ret) + return ret; /* * setup HW LBR filter, if any @@ -568,6 +711,12 @@ static int branch_type(unsigned long from, unsigned long to, int abort) ret = X86_BR_INT; break; case 0xe8: /* call near rel */ + insn_get_immediate(&insn); + if (insn.immediate1.value == 0) { + /* zero length call */ + ret = X86_BR_ZERO_CALL; + break; + } case 0x9a: /* call far absolute */ ret = X86_BR_CALL; break; @@ -678,35 +827,49 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc) /* * Map interface branch filters onto LBR filters */ -static const int nhm_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX] = { - [PERF_SAMPLE_BRANCH_ANY] = LBR_ANY, - [PERF_SAMPLE_BRANCH_USER] = LBR_USER, - [PERF_SAMPLE_BRANCH_KERNEL] = LBR_KERNEL, - [PERF_SAMPLE_BRANCH_HV] = LBR_IGN, - [PERF_SAMPLE_BRANCH_ANY_RETURN] = LBR_RETURN | LBR_REL_JMP - | LBR_IND_JMP | LBR_FAR, +static const int nhm_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = { + [PERF_SAMPLE_BRANCH_ANY_SHIFT] = LBR_ANY, + [PERF_SAMPLE_BRANCH_USER_SHIFT] = LBR_USER, + [PERF_SAMPLE_BRANCH_KERNEL_SHIFT] = LBR_KERNEL, + [PERF_SAMPLE_BRANCH_HV_SHIFT] = LBR_IGN, + [PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT] = LBR_RETURN | LBR_REL_JMP + | LBR_IND_JMP | LBR_FAR, /* * NHM/WSM erratum: must include REL_JMP+IND_JMP to get CALL branches */ - [PERF_SAMPLE_BRANCH_ANY_CALL] = + [PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT] = LBR_REL_CALL | LBR_IND_CALL | LBR_REL_JMP | LBR_IND_JMP | LBR_FAR, /* * NHM/WSM erratum: must include IND_JMP to capture IND_CALL */ - [PERF_SAMPLE_BRANCH_IND_CALL] = LBR_IND_CALL | LBR_IND_JMP, - [PERF_SAMPLE_BRANCH_COND] = LBR_JCC, + [PERF_SAMPLE_BRANCH_IND_CALL_SHIFT] = LBR_IND_CALL | LBR_IND_JMP, + [PERF_SAMPLE_BRANCH_COND_SHIFT] = LBR_JCC, }; -static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX] = { - [PERF_SAMPLE_BRANCH_ANY] = LBR_ANY, - [PERF_SAMPLE_BRANCH_USER] = LBR_USER, - [PERF_SAMPLE_BRANCH_KERNEL] = LBR_KERNEL, - [PERF_SAMPLE_BRANCH_HV] = LBR_IGN, - [PERF_SAMPLE_BRANCH_ANY_RETURN] = LBR_RETURN | LBR_FAR, - [PERF_SAMPLE_BRANCH_ANY_CALL] = LBR_REL_CALL | LBR_IND_CALL - | LBR_FAR, - [PERF_SAMPLE_BRANCH_IND_CALL] = LBR_IND_CALL, - [PERF_SAMPLE_BRANCH_COND] = LBR_JCC, +static const int snb_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = { + [PERF_SAMPLE_BRANCH_ANY_SHIFT] = LBR_ANY, + [PERF_SAMPLE_BRANCH_USER_SHIFT] = LBR_USER, + [PERF_SAMPLE_BRANCH_KERNEL_SHIFT] = LBR_KERNEL, + [PERF_SAMPLE_BRANCH_HV_SHIFT] = LBR_IGN, + [PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT] = LBR_RETURN | LBR_FAR, + [PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT] = LBR_REL_CALL | LBR_IND_CALL + | LBR_FAR, + [PERF_SAMPLE_BRANCH_IND_CALL_SHIFT] = LBR_IND_CALL, + [PERF_SAMPLE_BRANCH_COND_SHIFT] = LBR_JCC, +}; + +static const int hsw_lbr_sel_map[PERF_SAMPLE_BRANCH_MAX_SHIFT] = { + [PERF_SAMPLE_BRANCH_ANY_SHIFT] = LBR_ANY, + [PERF_SAMPLE_BRANCH_USER_SHIFT] = LBR_USER, + [PERF_SAMPLE_BRANCH_KERNEL_SHIFT] = LBR_KERNEL, + [PERF_SAMPLE_BRANCH_HV_SHIFT] = LBR_IGN, + [PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT] = LBR_RETURN | LBR_FAR, + [PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT] = LBR_REL_CALL | LBR_IND_CALL + | LBR_FAR, + [PERF_SAMPLE_BRANCH_IND_CALL_SHIFT] = LBR_IND_CALL, + [PERF_SAMPLE_BRANCH_COND_SHIFT] = LBR_JCC, + [PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT] = LBR_REL_CALL | LBR_IND_CALL + | LBR_RETURN | LBR_CALL_STACK, }; /* core */ @@ -765,6 +928,20 @@ void __init intel_pmu_lbr_init_snb(void) pr_cont("16-deep LBR, "); } +/* haswell */ +void intel_pmu_lbr_init_hsw(void) +{ + x86_pmu.lbr_nr = 16; + x86_pmu.lbr_tos = MSR_LBR_TOS; + x86_pmu.lbr_from = MSR_LBR_NHM_FROM; + x86_pmu.lbr_to = MSR_LBR_NHM_TO; + + x86_pmu.lbr_sel_mask = LBR_SEL_MASK; + x86_pmu.lbr_sel_map = hsw_lbr_sel_map; + + pr_cont("16-deep LBR, "); +} + /* atom */ void __init intel_pmu_lbr_init_atom(void) { diff --git a/arch/x86/kernel/cpu/perf_event_intel_pt.c b/arch/x86/kernel/cpu/perf_event_intel_pt.c new file mode 100644 index 00000000000000..f2770641c0fd21 --- /dev/null +++ b/arch/x86/kernel/cpu/perf_event_intel_pt.c @@ -0,0 +1,1103 @@ +/* + * Intel(R) Processor Trace PMU driver for perf + * Copyright (c) 2013-2014, Intel Corporation. + * + * 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. + * + * Intel PT is specified in the Intel Architecture Instruction Set Extensions + * Programming Reference: + * http://software.intel.com/en-us/intel-isa-extensions + */ + +#undef DEBUG + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +#include +#include +#include + +#include "perf_event.h" +#include "intel_pt.h" + +static DEFINE_PER_CPU(struct pt, pt_ctx); + +static struct pt_pmu pt_pmu; + +enum cpuid_regs { + CR_EAX = 0, + CR_ECX, + CR_EDX, + CR_EBX +}; + +/* + * Capabilities of Intel PT hardware, such as number of address bits or + * supported output schemes, are cached and exported to userspace as "caps" + * attribute group of pt pmu device + * (/sys/bus/event_source/devices/intel_pt/caps/) so that userspace can store + * relevant bits together with intel_pt traces. + * + * These are necessary for both trace decoding (payloads_lip, contains address + * width encoded in IP-related packets), and event configuration (bitmasks with + * permitted values for certain bit fields). + */ +#define PT_CAP(_n, _l, _r, _m) \ + [PT_CAP_ ## _n] = { .name = __stringify(_n), .leaf = _l, \ + .reg = _r, .mask = _m } + +static struct pt_cap_desc { + const char *name; + u32 leaf; + u8 reg; + u32 mask; +} pt_caps[] = { + PT_CAP(max_subleaf, 0, CR_EAX, 0xffffffff), + PT_CAP(cr3_filtering, 0, CR_EBX, BIT(0)), + PT_CAP(topa_output, 0, CR_ECX, BIT(0)), + PT_CAP(topa_multiple_entries, 0, CR_ECX, BIT(1)), + PT_CAP(payloads_lip, 0, CR_ECX, BIT(31)), +}; + +static u32 pt_cap_get(enum pt_capabilities cap) +{ + struct pt_cap_desc *cd = &pt_caps[cap]; + u32 c = pt_pmu.caps[cd->leaf * 4 + cd->reg]; + unsigned int shift = __ffs(cd->mask); + + return (c & cd->mask) >> shift; +} + +static ssize_t pt_cap_show(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + struct dev_ext_attribute *ea = + container_of(attr, struct dev_ext_attribute, attr); + enum pt_capabilities cap = (long)ea->var; + + return snprintf(buf, PAGE_SIZE, "%x\n", pt_cap_get(cap)); +} + +static struct attribute_group pt_cap_group = { + .name = "caps", +}; + +PMU_FORMAT_ATTR(tsc, "config:10" ); +PMU_FORMAT_ATTR(noretcomp, "config:11" ); + +static struct attribute *pt_formats_attr[] = { + &format_attr_tsc.attr, + &format_attr_noretcomp.attr, + NULL, +}; + +static struct attribute_group pt_format_group = { + .name = "format", + .attrs = pt_formats_attr, +}; + +static const struct attribute_group *pt_attr_groups[] = { + &pt_cap_group, + &pt_format_group, + NULL, +}; + +static int __init pt_pmu_hw_init(void) +{ + struct dev_ext_attribute *de_attrs; + struct attribute **attrs; + size_t size; + int ret; + long i; + + attrs = NULL; + ret = -ENODEV; + if (!test_cpu_cap(&boot_cpu_data, X86_FEATURE_INTEL_PT)) + goto fail; + + for (i = 0; i < PT_CPUID_LEAVES; i++) { + cpuid_count(20, i, + &pt_pmu.caps[CR_EAX + i*4], + &pt_pmu.caps[CR_EBX + i*4], + &pt_pmu.caps[CR_ECX + i*4], + &pt_pmu.caps[CR_EDX + i*4]); + } + + ret = -ENOMEM; + size = sizeof(struct attribute *) * (ARRAY_SIZE(pt_caps)+1); + attrs = kzalloc(size, GFP_KERNEL); + if (!attrs) + goto fail; + + size = sizeof(struct dev_ext_attribute) * (ARRAY_SIZE(pt_caps)+1); + de_attrs = kzalloc(size, GFP_KERNEL); + if (!de_attrs) + goto fail; + + for (i = 0; i < ARRAY_SIZE(pt_caps); i++) { + struct dev_ext_attribute *de_attr = de_attrs + i; + + de_attr->attr.attr.name = pt_caps[i].name; + + sysfs_attr_init(&de_attrs->attr.attr); + + de_attr->attr.attr.mode = S_IRUGO; + de_attr->attr.show = pt_cap_show; + de_attr->var = (void *)i; + + attrs[i] = &de_attr->attr.attr; + } + + pt_cap_group.attrs = attrs; + + return 0; + +fail: + kfree(attrs); + + return ret; +} + +#define PT_CONFIG_MASK (RTIT_CTL_TSC_EN | RTIT_CTL_DISRETC) + +static bool pt_event_valid(struct perf_event *event) +{ + u64 config = event->attr.config; + + if ((config & PT_CONFIG_MASK) != config) + return false; + + return true; +} + +/* + * PT configuration helpers + * These all are cpu affine and operate on a local PT + */ + +static bool pt_is_running(void) +{ + u64 ctl; + + rdmsrl(MSR_IA32_RTIT_CTL, ctl); + + return !!(ctl & RTIT_CTL_TRACEEN); +} + +static void pt_config(struct perf_event *event) +{ + u64 reg; + + reg = RTIT_CTL_TOPA | RTIT_CTL_BRANCH_EN | RTIT_CTL_TRACEEN; + + if (!event->attr.exclude_kernel) + reg |= RTIT_CTL_OS; + if (!event->attr.exclude_user) + reg |= RTIT_CTL_USR; + + reg |= (event->attr.config & PT_CONFIG_MASK); + + wrmsrl(MSR_IA32_RTIT_CTL, reg); +} + +static void pt_config_start(bool start) +{ + u64 ctl; + + rdmsrl(MSR_IA32_RTIT_CTL, ctl); + if (start) + ctl |= RTIT_CTL_TRACEEN; + else + ctl &= ~RTIT_CTL_TRACEEN; + wrmsrl(MSR_IA32_RTIT_CTL, ctl); + + /* + * A wrmsr that disables trace generation serializes other PT + * registers and causes all data packets to be written to memory, + * but a fence is required for the data to become globally visible. + * + * The below WMB, separating data store and aux_head store matches + * the consumer's RMB that separates aux_head load and data load. + */ + if (!start) + wmb(); +} + +static void pt_config_buffer(void *buf, unsigned int topa_idx, + unsigned int output_off) +{ + u64 reg; + + wrmsrl(MSR_IA32_RTIT_OUTPUT_BASE, virt_to_phys(buf)); + + reg = 0x7f | ((u64)topa_idx << 7) | ((u64)output_off << 32); + + wrmsrl(MSR_IA32_RTIT_OUTPUT_MASK, reg); +} + +/* + * Keep ToPA table-related metadata on the same page as the actual table, + * taking up a few words from the top + */ + +#define TENTS_PER_PAGE (((PAGE_SIZE - 40) / sizeof(struct topa_entry)) - 1) + +/** + * struct topa - page-sized ToPA table with metadata at the top + * @table: actual ToPA table entries, as understood by PT hardware + * @list: linkage to struct pt_buffer's list of tables + * @phys: physical address of this page + * @offset: offset of the first entry in this table in the buffer + * @size: total size of all entries in this table + * @last: index of the last initialized entry in this table + */ +struct topa { + struct topa_entry table[TENTS_PER_PAGE]; + struct list_head list; + u64 phys; + u64 offset; + size_t size; + int last; +}; + +/* make -1 stand for the last table entry */ +#define TOPA_ENTRY(t, i) ((i) == -1 ? &(t)->table[(t)->last] : &(t)->table[(i)]) + +/** + * topa_alloc() - allocate page-sized ToPA table + * @cpu: CPU on which to allocate. + * @gfp: Allocation flags. + * + * Return: On success, return the pointer to ToPA table page. + */ +static struct topa *topa_alloc(int cpu, gfp_t gfp) +{ + int node = cpu_to_node(cpu); + struct topa *topa; + struct page *p; + + p = alloc_pages_node(node, gfp | __GFP_ZERO, 0); + if (!p) + return NULL; + + topa = page_address(p); + topa->last = 0; + topa->phys = page_to_phys(p); + + /* + * In case of singe-entry ToPA, always put the self-referencing END + * link as the 2nd entry in the table + */ + if (!pt_cap_get(PT_CAP_topa_multiple_entries)) { + TOPA_ENTRY(topa, 1)->base = topa->phys >> TOPA_SHIFT; + TOPA_ENTRY(topa, 1)->end = 1; + } + + return topa; +} + +/** + * topa_free() - free a page-sized ToPA table + * @topa: Table to deallocate. + */ +static void topa_free(struct topa *topa) +{ + free_page((unsigned long)topa); +} + +/** + * topa_insert_table() - insert a ToPA table into a buffer + * @buf: PT buffer that's being extended. + * @topa: New topa table to be inserted. + * + * If it's the first table in this buffer, set up buffer's pointers + * accordingly; otherwise, add a END=1 link entry to @topa to the current + * "last" table and adjust the last table pointer to @topa. + */ +static void topa_insert_table(struct pt_buffer *buf, struct topa *topa) +{ + struct topa *last = buf->last; + + list_add_tail(&topa->list, &buf->tables); + + if (!buf->first) { + buf->first = buf->last = buf->cur = topa; + return; + } + + topa->offset = last->offset + last->size; + buf->last = topa; + + if (!pt_cap_get(PT_CAP_topa_multiple_entries)) + return; + + BUG_ON(last->last != TENTS_PER_PAGE - 1); + + TOPA_ENTRY(last, -1)->base = topa->phys >> TOPA_SHIFT; + TOPA_ENTRY(last, -1)->end = 1; +} + +/** + * topa_table_full() - check if a ToPA table is filled up + * @topa: ToPA table. + */ +static bool topa_table_full(struct topa *topa) +{ + /* single-entry ToPA is a special case */ + if (!pt_cap_get(PT_CAP_topa_multiple_entries)) + return !!topa->last; + + return topa->last == TENTS_PER_PAGE - 1; +} + +/** + * topa_insert_pages() - create a list of ToPA tables + * @buf: PT buffer being initialized. + * @gfp: Allocation flags. + * + * This initializes a list of ToPA tables with entries from + * the data_pages provided by rb_alloc_aux(). + * + * Return: 0 on success or error code. + */ +static int topa_insert_pages(struct pt_buffer *buf, gfp_t gfp) +{ + struct topa *topa = buf->last; + int order = 0; + struct page *p; + + p = virt_to_page(buf->data_pages[buf->nr_pages]); + if (PagePrivate(p)) + order = page_private(p); + + if (topa_table_full(topa)) { + topa = topa_alloc(buf->cpu, gfp); + if (!topa) + return -ENOMEM; + + topa_insert_table(buf, topa); + } + + TOPA_ENTRY(topa, -1)->base = page_to_phys(p) >> TOPA_SHIFT; + TOPA_ENTRY(topa, -1)->size = order; + if (!buf->snapshot && !pt_cap_get(PT_CAP_topa_multiple_entries)) { + TOPA_ENTRY(topa, -1)->intr = 1; + TOPA_ENTRY(topa, -1)->stop = 1; + } + + topa->last++; + topa->size += sizes(order); + + buf->nr_pages += 1ul << order; + + return 0; +} + +/** + * pt_topa_dump() - print ToPA tables and their entries + * @buf: PT buffer. + */ +static void pt_topa_dump(struct pt_buffer *buf) +{ + struct topa *topa; + + list_for_each_entry(topa, &buf->tables, list) { + int i; + + pr_debug("# table @%p (%016Lx), off %llx size %zx\n", topa->table, + topa->phys, topa->offset, topa->size); + for (i = 0; i < TENTS_PER_PAGE; i++) { + pr_debug("# entry @%p (%lx sz %u %c%c%c) raw=%16llx\n", + &topa->table[i], + (unsigned long)topa->table[i].base << TOPA_SHIFT, + sizes(topa->table[i].size), + topa->table[i].end ? 'E' : ' ', + topa->table[i].intr ? 'I' : ' ', + topa->table[i].stop ? 'S' : ' ', + *(u64 *)&topa->table[i]); + if ((pt_cap_get(PT_CAP_topa_multiple_entries) && + topa->table[i].stop) || + topa->table[i].end) + break; + } + } +} + +/** + * pt_buffer_advance() - advance to the next output region + * @buf: PT buffer. + * + * Advance the current pointers in the buffer to the next ToPA entry. + */ +static void pt_buffer_advance(struct pt_buffer *buf) +{ + buf->output_off = 0; + buf->cur_idx++; + + if (buf->cur_idx == buf->cur->last) { + if (buf->cur == buf->last) + buf->cur = buf->first; + else + buf->cur = list_entry(buf->cur->list.next, struct topa, + list); + buf->cur_idx = 0; + } +} + +/** + * pt_update_head() - calculate current offsets and sizes + * @pt: Per-cpu pt context. + * + * Update buffer's current write pointer position and data size. + */ +static void pt_update_head(struct pt *pt) +{ + struct pt_buffer *buf = perf_get_aux(&pt->handle); + u64 topa_idx, base, old; + + /* offset of the first region in this table from the beginning of buf */ + base = buf->cur->offset + buf->output_off; + + /* offset of the current output region within this table */ + for (topa_idx = 0; topa_idx < buf->cur_idx; topa_idx++) + base += sizes(buf->cur->table[topa_idx].size); + + if (buf->snapshot) { + local_set(&buf->data_size, base); + } else { + old = (local64_xchg(&buf->head, base) & + ((buf->nr_pages << PAGE_SHIFT) - 1)); + if (base < old) + base += buf->nr_pages << PAGE_SHIFT; + + local_add(base - old, &buf->data_size); + } +} + +/** + * pt_buffer_region() - obtain current output region's address + * @buf: PT buffer. + */ +static void *pt_buffer_region(struct pt_buffer *buf) +{ + return phys_to_virt(buf->cur->table[buf->cur_idx].base << TOPA_SHIFT); +} + +/** + * pt_buffer_region_size() - obtain current output region's size + * @buf: PT buffer. + */ +static size_t pt_buffer_region_size(struct pt_buffer *buf) +{ + return sizes(buf->cur->table[buf->cur_idx].size); +} + +/** + * pt_handle_status() - take care of possible status conditions + * @pt: Per-cpu pt context. + */ +static void pt_handle_status(struct pt *pt) +{ + struct pt_buffer *buf = perf_get_aux(&pt->handle); + int advance = 0; + u64 status; + + rdmsrl(MSR_IA32_RTIT_STATUS, status); + + if (status & RTIT_STATUS_ERROR) { + pr_err_ratelimited("ToPA ERROR encountered, trying to recover\n"); + pt_topa_dump(buf); + status &= ~RTIT_STATUS_ERROR; + } + + if (status & RTIT_STATUS_STOPPED) { + status &= ~RTIT_STATUS_STOPPED; + + /* + * On systems that only do single-entry ToPA, hitting STOP + * means we are already losing data; need to let the decoder + * know. + */ + if (!pt_cap_get(PT_CAP_topa_multiple_entries) || + buf->output_off == sizes(TOPA_ENTRY(buf->cur, buf->cur_idx)->size)) { + local_inc(&buf->lost); + advance++; + } + } + + /* + * Also on single-entry ToPA implementations, interrupt will come + * before the output reaches its output region's boundary. + */ + if (!pt_cap_get(PT_CAP_topa_multiple_entries) && !buf->snapshot && + pt_buffer_region_size(buf) - buf->output_off <= TOPA_PMI_MARGIN) { + void *head = pt_buffer_region(buf); + + /* everything within this margin needs to be zeroed out */ + memset(head + buf->output_off, 0, + pt_buffer_region_size(buf) - + buf->output_off); + advance++; + } + + if (advance) + pt_buffer_advance(buf); + + wrmsrl(MSR_IA32_RTIT_STATUS, status); +} + +/** + * pt_read_offset() - translate registers into buffer pointers + * @buf: PT buffer. + * + * Set buffer's output pointers from MSR values. + */ +static void pt_read_offset(struct pt_buffer *buf) +{ + u64 offset, base_topa; + + rdmsrl(MSR_IA32_RTIT_OUTPUT_BASE, base_topa); + buf->cur = phys_to_virt(base_topa); + + rdmsrl(MSR_IA32_RTIT_OUTPUT_MASK, offset); + /* offset within current output region */ + buf->output_off = offset >> 32; + /* index of current output region within this table */ + buf->cur_idx = (offset & 0xffffff80) >> 7; +} + +/** + * pt_topa_next_entry() - obtain index of the first page in the next ToPA entry + * @buf: PT buffer. + * @pg: Page offset in the buffer. + * + * When advancing to the next output region (ToPA entry), given a page offset + * into the buffer, we need to find the offset of the first page in the next + * region. + */ +static unsigned int pt_topa_next_entry(struct pt_buffer *buf, unsigned int pg) +{ + struct topa_entry *te = buf->topa_index[pg]; + + /* one region */ + if (buf->first == buf->last && buf->first->last == 1) + return pg; + + do { + pg++; + pg &= buf->nr_pages - 1; + } while (buf->topa_index[pg] == te); + + return pg; +} + +/** + * pt_buffer_reset_markers() - place interrupt and stop bits in the buffer + * @buf: PT buffer. + * @handle: Current output handle. + * + * Place INT and STOP marks to prevent overwriting old data that the consumer + * hasn't yet collected. + */ +static int pt_buffer_reset_markers(struct pt_buffer *buf, + struct perf_output_handle *handle) + +{ + unsigned long idx, npages, end; + + if (buf->snapshot) + return 0; + + /* can't stop in the middle of an output region */ + if (buf->output_off + handle->size + 1 < + sizes(TOPA_ENTRY(buf->cur, buf->cur_idx)->size)) + return -EINVAL; + + + /* single entry ToPA is handled by marking all regions STOP=1 INT=1 */ + if (!pt_cap_get(PT_CAP_topa_multiple_entries)) + return 0; + + /* clear STOP and INT from current entry */ + buf->topa_index[buf->stop_pos]->stop = 0; + buf->topa_index[buf->intr_pos]->intr = 0; + + if (pt_cap_get(PT_CAP_topa_multiple_entries)) { + npages = (handle->size + 1) >> PAGE_SHIFT; + end = (local64_read(&buf->head) >> PAGE_SHIFT) + npages; + /*if (end > handle->wakeup >> PAGE_SHIFT) + end = handle->wakeup >> PAGE_SHIFT;*/ + idx = end & (buf->nr_pages - 1); + buf->stop_pos = idx; + idx = (local64_read(&buf->head) >> PAGE_SHIFT) + npages - 1; + idx &= buf->nr_pages - 1; + buf->intr_pos = idx; + } + + buf->topa_index[buf->stop_pos]->stop = 1; + buf->topa_index[buf->intr_pos]->intr = 1; + + return 0; +} + +/** + * pt_buffer_setup_topa_index() - build topa_index[] table of regions + * @buf: PT buffer. + * + * topa_index[] references output regions indexed by offset into the + * buffer for purposes of quick reverse lookup. + */ +static void pt_buffer_setup_topa_index(struct pt_buffer *buf) +{ + struct topa *cur = buf->first, *prev = buf->last; + struct topa_entry *te_cur = TOPA_ENTRY(cur, 0), + *te_prev = TOPA_ENTRY(prev, prev->last - 1); + int pg = 0, idx = 0, ntopa = 0; + + while (pg < buf->nr_pages) { + int tidx; + + /* pages within one topa entry */ + for (tidx = 0; tidx < 1 << te_cur->size; tidx++, pg++) + buf->topa_index[pg] = te_prev; + + te_prev = te_cur; + + if (idx == cur->last - 1) { + /* advance to next topa table */ + idx = 0; + cur = list_entry(cur->list.next, struct topa, list); + ntopa++; + } else + idx++; + te_cur = TOPA_ENTRY(cur, idx); + } + +} + +/** + * pt_buffer_reset_offsets() - adjust buffer's write pointers from aux_head + * @buf: PT buffer. + * @head: Write pointer (aux_head) from AUX buffer. + * + * Find the ToPA table and entry corresponding to given @head and set buffer's + * "current" pointers accordingly. + */ +static void pt_buffer_reset_offsets(struct pt_buffer *buf, unsigned long head) +{ + int pg; + + if (buf->snapshot) + head &= (buf->nr_pages << PAGE_SHIFT) - 1; + + pg = (head >> PAGE_SHIFT) & (buf->nr_pages - 1); + pg = pt_topa_next_entry(buf, pg); + + buf->cur = (struct topa *)((unsigned long)buf->topa_index[pg] & PAGE_MASK); + buf->cur_idx = ((unsigned long)buf->topa_index[pg] - + (unsigned long)buf->cur) / sizeof(struct topa_entry); + buf->output_off = head & (sizes(buf->cur->table[buf->cur_idx].size) - 1); + + local64_set(&buf->head, head); + local_set(&buf->data_size, 0); +} + +/** + * pt_buffer_fini_topa() - deallocate ToPA structure of a buffer + * @buf: PT buffer. + */ +static void pt_buffer_fini_topa(struct pt_buffer *buf) +{ + struct topa *topa, *iter; + + list_for_each_entry_safe(topa, iter, &buf->tables, list) { + /* + * right now, this is in free_aux() path only, so + * no need to unlink this table from the list + */ + topa_free(topa); + } +} + +/** + * pt_buffer_init_topa() - initialize ToPA table for pt buffer + * @buf: PT buffer. + * @size: Total size of all regions within this ToPA. + * @gfp: Allocation flags. + */ +static int pt_buffer_init_topa(struct pt_buffer *buf, unsigned long nr_pages, + gfp_t gfp) +{ + struct topa *topa; + int err; + + topa = topa_alloc(buf->cpu, gfp); + if (!topa) + return -ENOMEM; + + topa_insert_table(buf, topa); + + while (buf->nr_pages < nr_pages) { + err = topa_insert_pages(buf, gfp); + if (err) { + pt_buffer_fini_topa(buf); + return -ENOMEM; + } + } + + pt_buffer_setup_topa_index(buf); + + /* link last table to the first one, unless we're double buffering */ + if (pt_cap_get(PT_CAP_topa_multiple_entries)) { + TOPA_ENTRY(buf->last, -1)->base = buf->first->phys >> TOPA_SHIFT; + TOPA_ENTRY(buf->last, -1)->end = 1; + } + + pt_topa_dump(buf); + return 0; +} + +/** + * pt_buffer_setup_aux() - set up topa tables for a PT buffer + * @cpu: Cpu on which to allocate, -1 means current. + * @pages: Array of pointers to buffer pages passed from perf core. + * @nr_pages: Number of pages in the buffer. + * @snapshot: If this is a snapshot/overwrite counter. + * + * This is a pmu::setup_aux callback that sets up ToPA tables and all the + * bookkeeping for an AUX buffer. + * + * Return: Our private PT buffer structure. + */ +static void * +pt_buffer_setup_aux(int cpu, void **pages, int nr_pages, bool snapshot) +{ + struct pt_buffer *buf; + int node, ret; + + if (!nr_pages) + return NULL; + + if (cpu == -1) + cpu = raw_smp_processor_id(); + node = cpu_to_node(cpu); + + buf = kzalloc_node(offsetof(struct pt_buffer, topa_index[nr_pages]), + GFP_KERNEL, node); + if (!buf) + return NULL; + + buf->cpu = cpu; + buf->snapshot = snapshot; + buf->data_pages = pages; + + INIT_LIST_HEAD(&buf->tables); + + ret = pt_buffer_init_topa(buf, nr_pages, GFP_KERNEL); + if (ret) { + kfree(buf); + return NULL; + } + + return buf; +} + +/** + * pt_buffer_free_aux() - perf AUX deallocation path callback + * @data: PT buffer. + */ +static void pt_buffer_free_aux(void *data) +{ + struct pt_buffer *buf = data; + + pt_buffer_fini_topa(buf); + kfree(buf); +} + +/** + * pt_buffer_is_full() - check if the buffer is full + * @buf: PT buffer. + * @pt: Per-cpu pt handle. + * + * If the user hasn't read data from the output region that aux_head + * points to, the buffer is considered full: the user needs to read at + * least this region and update aux_tail to point past it. + */ +static bool pt_buffer_is_full(struct pt_buffer *buf, struct pt *pt) +{ + if (buf->snapshot) + return false; + + if (local_read(&buf->data_size) >= pt->handle.size) + return true; + + return false; +} + +/** + * intel_pt_interrupt() - PT PMI handler + */ +void intel_pt_interrupt(void) +{ + struct pt *pt = this_cpu_ptr(&pt_ctx); + struct pt_buffer *buf; + struct perf_event *event = pt->handle.event; + + /* + * There may be a dangling PT bit in the interrupt status register + * after PT has been disabled by pt_event_stop(). Make sure we don't + * do anything (particularly, re-enable) for this event here. + */ + if (!ACCESS_ONCE(pt->handle_nmi)) + return; + + pt_config_start(false); + + if (!event) + return; + + buf = perf_get_aux(&pt->handle); + if (!buf) + return; + + pt_read_offset(buf); + + pt_handle_status(pt); + + pt_update_head(pt); + + perf_aux_output_end(&pt->handle, local_xchg(&buf->data_size, 0), + local_xchg(&buf->lost, 0)); + + if (!event->hw.state) { + int ret; + + buf = perf_aux_output_begin(&pt->handle, event); + if (!buf) { + event->hw.state = PERF_HES_STOPPED; + return; + } + + pt_buffer_reset_offsets(buf, pt->handle.head); + ret = pt_buffer_reset_markers(buf, &pt->handle); + if (ret) { + perf_aux_output_end(&pt->handle, 0, true); + return; + } + + pt_config_buffer(buf->cur->table, buf->cur_idx, + buf->output_off); + wrmsrl(MSR_IA32_RTIT_STATUS, 0); + pt_config(event); + } +} + +/* + * PMU callbacks + */ + +static void pt_event_start(struct perf_event *event, int mode) +{ + struct pt *pt = this_cpu_ptr(&pt_ctx); + struct pt_buffer *buf = perf_get_aux(&pt->handle); + + if (pt_is_running() || !buf || pt_buffer_is_full(buf, pt)) { + event->hw.state = PERF_HES_STOPPED; + return; + } + + ACCESS_ONCE(pt->handle_nmi) = 1; + event->hw.state = 0; + + pt_config_buffer(buf->cur->table, buf->cur_idx, + buf->output_off); + wrmsrl(MSR_IA32_RTIT_STATUS, 0); + pt_config(event); +} + +static void pt_event_stop(struct perf_event *event, int mode) +{ + struct pt *pt = this_cpu_ptr(&pt_ctx); + + /* + * Protect against the PMI racing with disabling wrmsr, + * see comment in intel_pt_interrupt(). + */ + ACCESS_ONCE(pt->handle_nmi) = 0; + pt_config_start(false); + + if (event->hw.state == PERF_HES_STOPPED) + return; + + event->hw.state = PERF_HES_STOPPED; + + if (mode & PERF_EF_UPDATE) { + struct pt *pt = this_cpu_ptr(&pt_ctx); + struct pt_buffer *buf = perf_get_aux(&pt->handle); + + if (!buf) + return; + + if (WARN_ON_ONCE(pt->handle.event != event)) + return; + + pt_read_offset(buf); + + pt_handle_status(pt); + + pt_update_head(pt); + } +} + +static void pt_event_del(struct perf_event *event, int mode) +{ + struct pt *pt = this_cpu_ptr(&pt_ctx); + struct pt_buffer *buf; + + pt_event_stop(event, PERF_EF_UPDATE); + + buf = perf_get_aux(&pt->handle); + + if (buf) { + if (buf->snapshot) + pt->handle.head = + local_xchg(&buf->data_size, + buf->nr_pages << PAGE_SHIFT); + perf_aux_output_end(&pt->handle, local_xchg(&buf->data_size, 0), + local_xchg(&buf->lost, 0)); + } +} + +static int pt_event_add(struct perf_event *event, int mode) +{ + struct pt_buffer *buf; + struct pt *pt = this_cpu_ptr(&pt_ctx); + struct hw_perf_event *hwc = &event->hw; + int ret = -EBUSY; + + if (pt->handle.event) + goto out; + + buf = perf_aux_output_begin(&pt->handle, event); + if (!buf) { + ret = -EINVAL; + goto out; + } + + pt_buffer_reset_offsets(buf, pt->handle.head); + if (!buf->snapshot) { + ret = pt_buffer_reset_markers(buf, &pt->handle); + if (ret) { + perf_aux_output_end(&pt->handle, 0, true); + goto out; + } + } + + if (mode & PERF_EF_START) { + pt_event_start(event, 0); + if (hwc->state == PERF_HES_STOPPED) { + pt_event_del(event, 0); + ret = -EBUSY; + } + } else { + hwc->state = PERF_HES_STOPPED; + } + + ret = 0; +out: + + if (ret) + hwc->state = PERF_HES_STOPPED; + + return ret; +} + +static void pt_event_read(struct perf_event *event) +{ +} + +static void pt_event_destroy(struct perf_event *event) +{ + x86_del_exclusive(x86_lbr_exclusive_pt); +} + +static int pt_event_init(struct perf_event *event) +{ + if (event->attr.type != pt_pmu.pmu.type) + return -ENOENT; + + if (!pt_event_valid(event)) + return -EINVAL; + + if (x86_add_exclusive(x86_lbr_exclusive_pt)) + return -EBUSY; + + event->destroy = pt_event_destroy; + + return 0; +} + +static __init int pt_init(void) +{ + int ret, cpu, prior_warn = 0; + + BUILD_BUG_ON(sizeof(struct topa) > PAGE_SIZE); + get_online_cpus(); + for_each_online_cpu(cpu) { + u64 ctl; + + ret = rdmsrl_safe_on_cpu(cpu, MSR_IA32_RTIT_CTL, &ctl); + if (!ret && (ctl & RTIT_CTL_TRACEEN)) + prior_warn++; + } + put_online_cpus(); + + if (prior_warn) { + x86_add_exclusive(x86_lbr_exclusive_pt); + pr_warn("PT is enabled at boot time, doing nothing\n"); + + return -EBUSY; + } + + ret = pt_pmu_hw_init(); + if (ret) + return ret; + + if (!pt_cap_get(PT_CAP_topa_output)) { + pr_warn("ToPA output is not supported on this CPU\n"); + return -ENODEV; + } + + if (!pt_cap_get(PT_CAP_topa_multiple_entries)) + pt_pmu.pmu.capabilities = + PERF_PMU_CAP_AUX_NO_SG | PERF_PMU_CAP_AUX_SW_DOUBLEBUF; + + pt_pmu.pmu.capabilities |= PERF_PMU_CAP_EXCLUSIVE | PERF_PMU_CAP_ITRACE; + pt_pmu.pmu.attr_groups = pt_attr_groups; + pt_pmu.pmu.task_ctx_nr = perf_sw_context; + pt_pmu.pmu.event_init = pt_event_init; + pt_pmu.pmu.add = pt_event_add; + pt_pmu.pmu.del = pt_event_del; + pt_pmu.pmu.start = pt_event_start; + pt_pmu.pmu.stop = pt_event_stop; + pt_pmu.pmu.read = pt_event_read; + pt_pmu.pmu.setup_aux = pt_buffer_setup_aux; + pt_pmu.pmu.free_aux = pt_buffer_free_aux; + ret = perf_pmu_register(&pt_pmu.pmu, "intel_pt", -1); + + return ret; +} + +module_init(pt_init); diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c index 21af6149edf2e7..12d9548457e719 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c @@ -1132,8 +1132,7 @@ static int snbep_pci2phy_map_init(int devid) } } - if (ubox_dev) - pci_dev_put(ubox_dev); + pci_dev_put(ubox_dev); return err ? pcibios_err_to_errno(err) : 0; } diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c index 60639093d536a9..3d423a101fae05 100644 --- a/arch/x86/kernel/cpu/scattered.c +++ b/arch/x86/kernel/cpu/scattered.c @@ -41,6 +41,7 @@ void init_scattered_cpuid_features(struct cpuinfo_x86 *c) { X86_FEATURE_HWP_ACT_WINDOW, CR_EAX, 9, 0x00000006, 0 }, { X86_FEATURE_HWP_EPP, CR_EAX,10, 0x00000006, 0 }, { X86_FEATURE_HWP_PKG_REQ, CR_EAX,11, 0x00000006, 0 }, + { X86_FEATURE_INTEL_PT, CR_EBX,25, 0x00000007, 0 }, { X86_FEATURE_APERFMPERF, CR_ECX, 0, 0x00000006, 0 }, { X86_FEATURE_EPB, CR_ECX, 3, 0x00000006, 0 }, { X86_FEATURE_HW_PSTATE, CR_EDX, 7, 0x80000007, 0 }, diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c index 24d079604fd53a..1deffe6cc87367 100644 --- a/arch/x86/kernel/kprobes/core.c +++ b/arch/x86/kernel/kprobes/core.c @@ -354,6 +354,7 @@ int __copy_instruction(u8 *dest, u8 *src) { struct insn insn; kprobe_opcode_t buf[MAX_INSN_SIZE]; + int length; unsigned long recovered_insn = recover_probed_instruction(buf, (unsigned long)src); @@ -361,16 +362,18 @@ int __copy_instruction(u8 *dest, u8 *src) return 0; kernel_insn_init(&insn, (void *)recovered_insn, MAX_INSN_SIZE); insn_get_length(&insn); + length = insn.length; + /* Another subsystem puts a breakpoint, failed to recover */ if (insn.opcode.bytes[0] == BREAKPOINT_INSTRUCTION) return 0; - memcpy(dest, insn.kaddr, insn.length); + memcpy(dest, insn.kaddr, length); #ifdef CONFIG_X86_64 if (insn_rip_relative(&insn)) { s64 newdisp; u8 *disp; - kernel_insn_init(&insn, dest, insn.length); + kernel_insn_init(&insn, dest, length); insn_get_displacement(&insn); /* * The copied instruction uses the %rip-relative addressing @@ -394,7 +397,7 @@ int __copy_instruction(u8 *dest, u8 *src) *(s32 *) disp = (s32) newdisp; } #endif - return insn.length; + return length; } static int arch_copy_kprobe(struct kprobe *p) diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 7035f6b21c3f99..50e547eac8cd4b 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -77,9 +77,6 @@ #include #include -/* State of each CPU */ -DEFINE_PER_CPU(int, cpu_state) = { 0 }; - /* Number of siblings per CPU package */ int smp_num_siblings = 1; EXPORT_SYMBOL(smp_num_siblings); @@ -257,7 +254,7 @@ static void notrace start_secondary(void *unused) lock_vector_lock(); set_cpu_online(smp_processor_id(), true); unlock_vector_lock(); - per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE; + cpu_set_state_online(smp_processor_id()); x86_platform.nmi_init(); /* enable local interrupts */ @@ -954,7 +951,10 @@ int native_cpu_up(unsigned int cpu, struct task_struct *tidle) */ mtrr_save_state(); - per_cpu(cpu_state, cpu) = CPU_UP_PREPARE; + /* x86 CPUs take themselves offline, so delayed offline is OK. */ + err = cpu_check_up_prepare(cpu); + if (err && err != -EBUSY) + return err; /* the FPU context is blank, nobody can own it */ __cpu_disable_lazy_restore(cpu); @@ -1197,7 +1197,7 @@ void __init native_smp_prepare_boot_cpu(void) switch_to_new_gdt(me); /* already set me in cpu_online_mask in boot_cpu_init() */ cpumask_set_cpu(me, cpu_callout_mask); - per_cpu(cpu_state, me) = CPU_ONLINE; + cpu_set_state_online(me); } void __init native_smp_cpus_done(unsigned int max_cpus) @@ -1324,14 +1324,10 @@ static void __ref remove_cpu_from_maps(int cpu) numa_remove_cpu(cpu); } -static DEFINE_PER_CPU(struct completion, die_complete); - void cpu_disable_common(void) { int cpu = smp_processor_id(); - init_completion(&per_cpu(die_complete, smp_processor_id())); - remove_siblinginfo(cpu); /* It's now safe to remove this processor from the online map */ @@ -1355,24 +1351,27 @@ int native_cpu_disable(void) return 0; } -void cpu_die_common(unsigned int cpu) +int common_cpu_die(unsigned int cpu) { - wait_for_completion_timeout(&per_cpu(die_complete, cpu), HZ); -} + int ret = 0; -void native_cpu_die(unsigned int cpu) -{ /* We don't do anything here: idle task is faking death itself. */ - cpu_die_common(cpu); - /* They ack this in play_dead() by setting CPU_DEAD */ - if (per_cpu(cpu_state, cpu) == CPU_DEAD) { + if (cpu_wait_death(cpu, 5)) { if (system_state == SYSTEM_RUNNING) pr_info("CPU %u is now offline\n", cpu); } else { pr_err("CPU %u didn't die...\n", cpu); + ret = -1; } + + return ret; +} + +void native_cpu_die(unsigned int cpu) +{ + common_cpu_die(cpu); } void play_dead_common(void) @@ -1381,10 +1380,8 @@ void play_dead_common(void) reset_lazy_tlbstate(); amd_e400_remove_cpu(raw_smp_processor_id()); - mb(); /* Ack it */ - __this_cpu_write(cpu_state, CPU_DEAD); - complete(&per_cpu(die_complete, smp_processor_id())); + (void)cpu_report_death(); /* * With physical CPU hotplug, we should halt the cpu diff --git a/arch/x86/kernel/test_rodata.c b/arch/x86/kernel/test_rodata.c index b79133abda48d9..5ecbfe5099dad6 100644 --- a/arch/x86/kernel/test_rodata.c +++ b/arch/x86/kernel/test_rodata.c @@ -57,7 +57,7 @@ int rodata_test(void) /* test 3: check the value hasn't changed */ /* If this test fails, we managed to overwrite the data */ if (!rodata_test_data) { - printk(KERN_ERR "rodata_test: Test 3 failes (end data)\n"); + printk(KERN_ERR "rodata_test: Test 3 fails (end data)\n"); return -ENODEV; } /* test 4: check if the rodata section is 4Kb aligned */ diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index f4fa991406cd78..324ab524768756 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -123,7 +123,7 @@ enum ctx_state ist_enter(struct pt_regs *regs) * but we need to notify RCU. */ rcu_nmi_enter(); - prev_state = IN_KERNEL; /* the value is irrelevant. */ + prev_state = CONTEXT_KERNEL; /* the value is irrelevant. */ } /* diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index 7413ee3706d025..86484384492e97 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -90,14 +90,10 @@ static void cpu_bringup(void) set_cpu_online(cpu, true); - this_cpu_write(cpu_state, CPU_ONLINE); - - wmb(); + cpu_set_state_online(cpu); /* Implies full memory barrier. */ /* We can take interrupts now: we're officially "up". */ local_irq_enable(); - - wmb(); /* make sure everything is out */ } /* @@ -451,7 +447,13 @@ static int xen_cpu_up(unsigned int cpu, struct task_struct *idle) xen_setup_timer(cpu); xen_init_lock_cpu(cpu); - per_cpu(cpu_state, cpu) = CPU_UP_PREPARE; + /* + * PV VCPUs are always successfully taken down (see 'while' loop + * in xen_cpu_die()), so -EBUSY is an error. + */ + rc = cpu_check_up_prepare(cpu); + if (rc) + return rc; /* make sure interrupts start blocked */ per_cpu(xen_vcpu, cpu)->evtchn_upcall_mask = 1; @@ -467,10 +469,8 @@ static int xen_cpu_up(unsigned int cpu, struct task_struct *idle) rc = HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL); BUG_ON(rc); - while(per_cpu(cpu_state, cpu) != CPU_ONLINE) { + while (cpu_report_state(cpu) != CPU_ONLINE) HYPERVISOR_sched_op(SCHEDOP_yield, NULL); - barrier(); - } return 0; } @@ -499,11 +499,11 @@ static void xen_cpu_die(unsigned int cpu) schedule_timeout(HZ/10); } - cpu_die_common(cpu); - - xen_smp_intr_free(cpu); - xen_uninit_lock_cpu(cpu); - xen_teardown_timer(cpu); + if (common_cpu_die(cpu) == 0) { + xen_smp_intr_free(cpu); + xen_uninit_lock_cpu(cpu); + xen_teardown_timer(cpu); + } } static void xen_play_dead(void) /* used only with HOTPLUG_CPU */ @@ -735,6 +735,16 @@ static void __init xen_hvm_smp_prepare_cpus(unsigned int max_cpus) static int xen_hvm_cpu_up(unsigned int cpu, struct task_struct *tidle) { int rc; + + /* + * This can happen if CPU was offlined earlier and + * offlining timed out in common_cpu_die(). + */ + if (cpu_report_state(cpu) == CPU_DEAD_FROZEN) { + xen_smp_intr_free(cpu); + xen_uninit_lock_cpu(cpu); + } + /* * xen_smp_intr_init() needs to run before native_cpu_up() * so that IPI vectors are set up on the booting CPU before @@ -756,12 +766,6 @@ static int xen_hvm_cpu_up(unsigned int cpu, struct task_struct *tidle) return rc; } -static void xen_hvm_cpu_die(unsigned int cpu) -{ - xen_cpu_die(cpu); - native_cpu_die(cpu); -} - void __init xen_hvm_smp_init(void) { if (!xen_have_vector_callback) @@ -769,7 +773,7 @@ void __init xen_hvm_smp_init(void) smp_ops.smp_prepare_cpus = xen_hvm_smp_prepare_cpus; smp_ops.smp_send_reschedule = xen_smp_send_reschedule; smp_ops.cpu_up = xen_hvm_cpu_up; - smp_ops.cpu_die = xen_hvm_cpu_die; + smp_ops.cpu_die = xen_cpu_die; smp_ops.send_call_func_ipi = xen_smp_send_call_function_ipi; smp_ops.send_call_func_single_ipi = xen_smp_send_call_function_single_ipi; smp_ops.smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu; diff --git a/arch/xtensa/kernel/signal.c b/arch/xtensa/kernel/signal.c index 3d733ba16f28a2..6b3790445cbed4 100644 --- a/arch/xtensa/kernel/signal.c +++ b/arch/xtensa/kernel/signal.c @@ -405,11 +405,6 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, regs->areg[8] = (unsigned long) &frame->uc; regs->threadptr = tp; - /* Set access mode to USER_DS. Nomenclature is outdated, but - * functionality is used in uaccess.h - */ - set_fs(USER_DS); - #if DEBUG_SIG printk("SIG rt deliver (%s:%d): signal=%d sp=%p pc=%08x\n", current->comm, current->pid, signal, frame, regs->pc); diff --git a/block/blk-map.c b/block/blk-map.c index b8d2725324a6b8..da310a10542997 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -124,10 +124,10 @@ int blk_rq_map_user(struct request_queue *q, struct request *rq, { struct iovec iov; struct iov_iter i; + int ret = import_single_range(rq_data_dir(rq), ubuf, len, &iov, &i); - iov.iov_base = ubuf; - iov.iov_len = len; - iov_iter_init(&i, rq_data_dir(rq), &iov, 1, len); + if (unlikely(ret < 0)) + return ret; return blk_rq_map_user_iov(q, rq, map_data, &i, gfp_mask); } diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c index e1f71c3961934b..55b6f15dac900a 100644 --- a/block/scsi_ioctl.c +++ b/block/scsi_ioctl.c @@ -335,16 +335,14 @@ static int sg_io(struct request_queue *q, struct gendisk *bd_disk, struct iov_iter i; struct iovec *iov = NULL; - ret = rw_copy_check_uvector(-1, hdr->dxferp, hdr->iovec_count, - 0, NULL, &iov); - if (ret < 0) { - kfree(iov); + ret = import_iovec(rq_data_dir(rq), + hdr->dxferp, hdr->iovec_count, + 0, &iov, &i); + if (ret < 0) goto out_free_cdb; - } /* SG_IO howto says that the shorter of the two wins */ - iov_iter_init(&i, rq_data_dir(rq), iov, hdr->iovec_count, - min_t(unsigned, ret, hdr->dxfer_len)); + iov_iter_truncate(&i, hdr->dxfer_len); ret = blk_rq_map_user_iov(q, rq, NULL, &i, GFP_KERNEL); kfree(iov); diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 297110c12635d3..9c4fd7a8e2e5c4 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include diff --git a/drivers/char/tile-srom.c b/drivers/char/tile-srom.c index 02e76ac6d282d5..69f6b4acc37714 100644 --- a/drivers/char/tile-srom.c +++ b/drivers/char/tile-srom.c @@ -27,7 +27,6 @@ #include /* size_t */ #include #include /* O_ACCMODE */ -#include #include #include #include diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 63f01c42aed486..6b5e795f3fe201 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -1149,6 +1149,7 @@ static int arizona_extcon_probe(struct platform_device *pdev) } break; case WM5110: + case WM8280: switch (arizona->rev) { case 0 ... 2: break; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c1e2ca3d9a51b1..dc1aaa83a3474c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -669,6 +669,7 @@ config GPIO_STP_XWAY config GPIO_TC3589X bool "TC3589X GPIOs" depends on MFD_TC3589X + depends on OF_GPIO select GPIOLIB_IRQCHIP help This enables support for the GPIOs found on the TC3589X diff --git a/drivers/gpio/gpio-arizona.c b/drivers/gpio/gpio-arizona.c index fe369f5c7fa6d9..9665d0aa4ebbf5 100644 --- a/drivers/gpio/gpio-arizona.c +++ b/drivers/gpio/gpio-arizona.c @@ -116,6 +116,7 @@ static int arizona_gpio_probe(struct platform_device *pdev) switch (arizona->type) { case WM5102: case WM5110: + case WM8280: case WM8997: arizona_gpio->gpio_chip.ngpio = 5; break; diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 11aed267106550..31b244cffabb2f 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -260,10 +260,7 @@ static int tc3589x_gpio_probe(struct platform_device *pdev) tc3589x_gpio->chip.ngpio = tc3589x->num_gpio; tc3589x_gpio->chip.dev = &pdev->dev; tc3589x_gpio->chip.base = -1; - -#ifdef CONFIG_OF_GPIO tc3589x_gpio->chip.of_node = np; -#endif /* Bring the GPIO module out of reset */ ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, diff --git a/drivers/gpu/drm/drm_trace.h b/drivers/gpu/drm/drm_trace.h index 27cc95f3638125..ce3c42813fbbf4 100644 --- a/drivers/gpu/drm/drm_trace.h +++ b/drivers/gpu/drm/drm_trace.h @@ -7,7 +7,6 @@ #undef TRACE_SYSTEM #define TRACE_SYSTEM drm -#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM) #define TRACE_INCLUDE_FILE drm_trace TRACE_EVENT(drm_vblank_event, diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index 6058a01b444334..d776621c8521aa 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -12,7 +12,6 @@ #undef TRACE_SYSTEM #define TRACE_SYSTEM i915 -#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM) #define TRACE_INCLUDE_FILE i915_trace /* pipe updates */ diff --git a/drivers/gpu/drm/radeon/radeon_trace.h b/drivers/gpu/drm/radeon/radeon_trace.h index ce075cb08cb2b7..fdce4062901fe9 100644 --- a/drivers/gpu/drm/radeon/radeon_trace.h +++ b/drivers/gpu/drm/radeon/radeon_trace.h @@ -9,7 +9,6 @@ #undef TRACE_SYSTEM #define TRACE_SYSTEM radeon -#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM) #define TRACE_INCLUDE_FILE radeon_trace TRACE_EVENT(radeon_bo_create, diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 152b006833cda2..15338afdf7f9ab 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -92,7 +92,7 @@ menu "Special HID drivers" depends on HID config HID_A4TECH - tristate "A4 tech mice" if EXPERT + tristate "A4 tech mice" depends on HID default !EXPERT ---help--- @@ -113,7 +113,7 @@ config HID_ACRUX_FF game controllers. config HID_APPLE - tristate "Apple {i,Power,Mac}Books" if EXPERT + tristate "Apple {i,Power,Mac}Books" depends on HID default !EXPERT ---help--- @@ -141,7 +141,7 @@ config HID_AUREAL Support for Aureal Cy se W-01RN Remote Controller and other Aureal derived remotes. config HID_BELKIN - tristate "Belkin Flip KVM and Wireless keyboard" if EXPERT + tristate "Belkin Flip KVM and Wireless keyboard" depends on HID default !EXPERT ---help--- @@ -158,14 +158,14 @@ config HID_BETOP_FF - BETOP 2185 PC & BFM MODE config HID_CHERRY - tristate "Cherry Cymotion keyboard" if EXPERT + tristate "Cherry Cymotion keyboard" depends on HID default !EXPERT ---help--- Support for Cherry Cymotion keyboard. config HID_CHICONY - tristate "Chicony Tactical pad" if EXPERT + tristate "Chicony Tactical pad" depends on HID default !EXPERT ---help--- @@ -196,7 +196,7 @@ config HID_CP2112 customizable USB descriptor fields are exposed as sysfs attributes. config HID_CYPRESS - tristate "Cypress mouse and barcode readers" if EXPERT + tristate "Cypress mouse and barcode readers" depends on HID default !EXPERT ---help--- @@ -245,7 +245,7 @@ config HID_ELO different devices than those handled by CONFIG_TOUCHSCREEN_USB_ELO. config HID_EZKEY - tristate "Ezkey BTC 8193 keyboard" if EXPERT + tristate "Ezkey BTC 8193 keyboard" depends on HID default !EXPERT ---help--- @@ -286,12 +286,6 @@ config HID_GT683R Currently the following devices are know to be supported: - MSI GT683R -config HID_HUION - tristate "Huion tablets" - depends on USB_HID - ---help--- - Support for Huion 580 tablet. - config HID_KEYTOUCH tristate "Keytouch HID devices" depends on HID @@ -312,9 +306,9 @@ config HID_KYE config HID_UCLOGIC tristate "UC-Logic" - depends on HID + depends on USB_HID ---help--- - Support for UC-Logic tablets. + Support for UC-Logic and Huion tablets. config HID_WALTOP tristate "Waltop" @@ -344,7 +338,7 @@ config HID_TWINHAN Support for Twinhan IR remote control. config HID_KENSINGTON - tristate "Kensington Slimblade Trackball" if EXPERT + tristate "Kensington Slimblade Trackball" depends on HID default !EXPERT ---help--- @@ -372,7 +366,7 @@ config HID_LENOVO - ThinkPad Compact USB Keyboard with TrackPoint (supports Fn keys) config HID_LOGITECH - tristate "Logitech devices" if EXPERT + tristate "Logitech devices" depends on HID default !EXPERT ---help--- @@ -461,14 +455,14 @@ config HID_MAGICMOUSE Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad. config HID_MICROSOFT - tristate "Microsoft non-fully HID-compliant devices" if EXPERT + tristate "Microsoft non-fully HID-compliant devices" depends on HID default !EXPERT ---help--- Support for Microsoft devices that are not fully compliant with HID standard. config HID_MONTEREY - tristate "Monterey Genius KB29E keyboard" if EXPERT + tristate "Monterey Genius KB29E keyboard" depends on HID default !EXPERT ---help--- @@ -638,7 +632,6 @@ config HID_PICOLCD_CIR config HID_PLANTRONICS tristate "Plantronics USB HID Driver" - default !EXPERT depends on HID ---help--- Provides HID support for Plantronics telephony devices. @@ -885,6 +878,21 @@ config HID_SENSOR_HUB for events and handle data streams. Each sensor driver can format data and present to user mode using input or IIO interface. +config HID_SENSOR_CUSTOM_SENSOR + tristate "HID Sensors hub custom sensor support" + depends on HID_SENSOR_HUB + default n + ---help--- + HID Sensor hub specification allows definition of some custom and + generic sensors. Unlike other HID sensors, they can't be exported + via Linux IIO because of custom fields. This is up to the manufacturer + to decide how to interpret these special sensor ids and process in + the user space. Currently some manufacturers are using these ids for + sensor calibration and debugging other sensors. Manufacturers + should't use these special custom sensor ids to export any of the + standard sensors. + Select this config option for custom/generic sensor support. + endmenu endif # HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 6f19958dfc3876..e4a21dfd7ef301 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -41,7 +41,6 @@ obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o -obj-$(CONFIG_HID_HUION) += hid-huion.o obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o obj-$(CONFIG_HID_ICADE) += hid-icade.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o @@ -101,6 +100,7 @@ obj-$(CONFIG_HID_WACOM) += wacom.o obj-$(CONFIG_HID_WALTOP) += hid-waltop.o obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o +obj-$(CONFIG_HID_SENSOR_CUSTOM_SENSOR) += hid-sensor-custom.o obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 56ce8c2b5530db..722a925795a288 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1562,12 +1562,26 @@ read_report_descriptor(struct file *filp, struct kobject *kobj, return count; } +static ssize_t +show_country(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + + return sprintf(buf, "%02x\n", hdev->country & 0xff); +} + static struct bin_attribute dev_bin_attr_report_desc = { .attr = { .name = "report_descriptor", .mode = 0444 }, .read = read_report_descriptor, .size = HID_MAX_DESCRIPTOR_SIZE, }; +static struct device_attribute dev_attr_country = { + .attr = { .name = "country", .mode = 0444 }, + .show = show_country, +}; + int hid_connect(struct hid_device *hdev, unsigned int connect_mask) { static const char *types[] = { "Device", "Pointer", "Mouse", "Device", @@ -1646,6 +1660,11 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) bus = ""; } + ret = device_create_file(&hdev->dev, &dev_attr_country); + if (ret) + hid_warn(hdev, + "can't create sysfs country code attribute err: %d\n", ret); + ret = device_create_bin_file(&hdev->dev, &dev_bin_attr_report_desc); if (ret) hid_warn(hdev, @@ -1661,6 +1680,7 @@ EXPORT_SYMBOL_GPL(hid_connect); void hid_disconnect(struct hid_device *hdev) { + device_remove_file(&hdev->dev, &dev_attr_country); device_remove_bin_file(&hdev->dev, &dev_bin_attr_report_desc); if (hdev->claimed & HID_CLAIMED_INPUT) hidinput_disconnect(hdev); @@ -1824,6 +1844,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, #if IS_ENABLED(CONFIG_HID_LENOVO) diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 8bf61d295ffd7e..2886b645ced738 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -165,6 +165,7 @@ static const struct hid_usage_entry hid_usage_table[] = { {0, 0x53, "DeviceIndex"}, {0, 0x54, "ContactCount"}, {0, 0x55, "ContactMaximumNumber"}, + {0, 0x59, "ButtonType"}, {0, 0x5A, "SecondaryBarrelSwitch"}, {0, 0x5B, "TransducerSerialNumber"}, { 15, 0, "PhysicalInterfaceDevice" }, @@ -814,7 +815,7 @@ static const char *keys[KEY_MAX + 1] = { [KEY_DELETEFILE] = "DeleteFile", [KEY_XFER] = "X-fer", [KEY_PROG1] = "Prog1", [KEY_PROG2] = "Prog2", [KEY_WWW] = "WWW", [KEY_MSDOS] = "MSDOS", - [KEY_COFFEE] = "Coffee", [KEY_DIRECTION] = "Direction", + [KEY_COFFEE] = "Coffee", [KEY_ROTATE_DISPLAY] = "RotateDisplay", [KEY_CYCLEWINDOWS] = "CycleWindows", [KEY_MAIL] = "Mail", [KEY_BOOKMARKS] = "Bookmarks", [KEY_COMPUTER] = "Computer", [KEY_BACK] = "Back", [KEY_FORWARD] = "Forward", @@ -1127,7 +1128,8 @@ static ssize_t hid_debug_events_read(struct file *file, char __user *buffer, if (!list->hdev || !list->hdev->debug) { ret = -EIO; - break; + set_current_state(TASK_RUNNING); + goto out; } /* allow O_NONBLOCK from other threads */ diff --git a/drivers/hid/hid-huion.c b/drivers/hid/hid-huion.c deleted file mode 100644 index 61b68ca27790d6..00000000000000 --- a/drivers/hid/hid-huion.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * HID driver for Huion devices not fully compliant with HID standard - * - * Copyright (c) 2013 Martin Rusko - * Copyright (c) 2014 Nikolai Kondrashov - */ - -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - */ - -#include -#include -#include -#include -#include -#include "usbhid/usbhid.h" - -#include "hid-ids.h" - -/* Report descriptor template placeholder head */ -#define HUION_PH_HEAD 0xFE, 0xED, 0x1D - -/* Report descriptor template placeholder IDs */ -enum huion_ph_id { - HUION_PH_ID_X_LM, - HUION_PH_ID_X_PM, - HUION_PH_ID_Y_LM, - HUION_PH_ID_Y_PM, - HUION_PH_ID_PRESSURE_LM, - HUION_PH_ID_NUM -}; - -/* Report descriptor template placeholder */ -#define HUION_PH(_ID) HUION_PH_HEAD, HUION_PH_ID_##_ID - -/* Fixed report descriptor template */ -static const __u8 huion_tablet_rdesc_template[] = { - 0x05, 0x0D, /* Usage Page (Digitizer), */ - 0x09, 0x02, /* Usage (Pen), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x07, /* Report ID (7), */ - 0x09, 0x20, /* Usage (Stylus), */ - 0xA0, /* Collection (Physical), */ - 0x14, /* Logical Minimum (0), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x75, 0x01, /* Report Size (1), */ - 0x09, 0x42, /* Usage (Tip Switch), */ - 0x09, 0x44, /* Usage (Barrel Switch), */ - 0x09, 0x46, /* Usage (Tablet Pick), */ - 0x95, 0x03, /* Report Count (3), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x03, /* Report Count (3), */ - 0x81, 0x03, /* Input (Constant, Variable), */ - 0x09, 0x32, /* Usage (In Range), */ - 0x95, 0x01, /* Report Count (1), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x01, /* Report Count (1), */ - 0x81, 0x03, /* Input (Constant, Variable), */ - 0x75, 0x10, /* Report Size (16), */ - 0x95, 0x01, /* Report Count (1), */ - 0xA4, /* Push, */ - 0x05, 0x01, /* Usage Page (Desktop), */ - 0x65, 0x13, /* Unit (Inch), */ - 0x55, 0xFD, /* Unit Exponent (-3), */ - 0x34, /* Physical Minimum (0), */ - 0x09, 0x30, /* Usage (X), */ - 0x27, HUION_PH(X_LM), /* Logical Maximum (PLACEHOLDER), */ - 0x47, HUION_PH(X_PM), /* Physical Maximum (PLACEHOLDER), */ - 0x81, 0x02, /* Input (Variable), */ - 0x09, 0x31, /* Usage (Y), */ - 0x27, HUION_PH(Y_LM), /* Logical Maximum (PLACEHOLDER), */ - 0x47, HUION_PH(Y_PM), /* Physical Maximum (PLACEHOLDER), */ - 0x81, 0x02, /* Input (Variable), */ - 0xB4, /* Pop, */ - 0x09, 0x30, /* Usage (Tip Pressure), */ - 0x27, - HUION_PH(PRESSURE_LM), /* Logical Maximum (PLACEHOLDER), */ - 0x81, 0x02, /* Input (Variable), */ - 0xC0, /* End Collection, */ - 0xC0 /* End Collection */ -}; - -/* Parameter indices */ -enum huion_prm { - HUION_PRM_X_LM = 1, - HUION_PRM_Y_LM = 2, - HUION_PRM_PRESSURE_LM = 4, - HUION_PRM_RESOLUTION = 5, - HUION_PRM_NUM -}; - -/* Driver data */ -struct huion_drvdata { - __u8 *rdesc; - unsigned int rsize; -}; - -static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) -{ - struct huion_drvdata *drvdata = hid_get_drvdata(hdev); - switch (hdev->product) { - case USB_DEVICE_ID_HUION_TABLET: - if (drvdata->rdesc != NULL) { - rdesc = drvdata->rdesc; - *rsize = drvdata->rsize; - } - break; - } - return rdesc; -} - -/** - * Enable fully-functional tablet mode and determine device parameters. - * - * @hdev: HID device - */ -static int huion_tablet_enable(struct hid_device *hdev) -{ - int rc; - struct usb_device *usb_dev = hid_to_usb_dev(hdev); - struct huion_drvdata *drvdata = hid_get_drvdata(hdev); - __le16 *buf = NULL; - size_t len; - s32 params[HUION_PH_ID_NUM]; - s32 resolution; - __u8 *p; - s32 v; - - /* - * Read string descriptor containing tablet parameters. The specific - * string descriptor and data were discovered by sniffing the Windows - * driver traffic. - * NOTE: This enables fully-functional tablet mode. - */ - len = HUION_PRM_NUM * sizeof(*buf); - buf = kmalloc(len, GFP_KERNEL); - if (buf == NULL) { - hid_err(hdev, "failed to allocate parameter buffer\n"); - rc = -ENOMEM; - goto cleanup; - } - rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, - (USB_DT_STRING << 8) + 0x64, - 0x0409, buf, len, - USB_CTRL_GET_TIMEOUT); - if (rc == -EPIPE) { - hid_err(hdev, "device parameters not found\n"); - rc = -ENODEV; - goto cleanup; - } else if (rc < 0) { - hid_err(hdev, "failed to get device parameters: %d\n", rc); - rc = -ENODEV; - goto cleanup; - } else if (rc != len) { - hid_err(hdev, "invalid device parameters\n"); - rc = -ENODEV; - goto cleanup; - } - - /* Extract device parameters */ - params[HUION_PH_ID_X_LM] = le16_to_cpu(buf[HUION_PRM_X_LM]); - params[HUION_PH_ID_Y_LM] = le16_to_cpu(buf[HUION_PRM_Y_LM]); - params[HUION_PH_ID_PRESSURE_LM] = - le16_to_cpu(buf[HUION_PRM_PRESSURE_LM]); - resolution = le16_to_cpu(buf[HUION_PRM_RESOLUTION]); - if (resolution == 0) { - params[HUION_PH_ID_X_PM] = 0; - params[HUION_PH_ID_Y_PM] = 0; - } else { - params[HUION_PH_ID_X_PM] = params[HUION_PH_ID_X_LM] * - 1000 / resolution; - params[HUION_PH_ID_Y_PM] = params[HUION_PH_ID_Y_LM] * - 1000 / resolution; - } - - /* Allocate fixed report descriptor */ - drvdata->rdesc = devm_kmalloc(&hdev->dev, - sizeof(huion_tablet_rdesc_template), - GFP_KERNEL); - if (drvdata->rdesc == NULL) { - hid_err(hdev, "failed to allocate fixed rdesc\n"); - rc = -ENOMEM; - goto cleanup; - } - drvdata->rsize = sizeof(huion_tablet_rdesc_template); - - /* Format fixed report descriptor */ - memcpy(drvdata->rdesc, huion_tablet_rdesc_template, - drvdata->rsize); - for (p = drvdata->rdesc; - p <= drvdata->rdesc + drvdata->rsize - 4;) { - if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D && - p[3] < sizeof(params)) { - v = params[p[3]]; - put_unaligned(cpu_to_le32(v), (s32 *)p); - p += 4; - } else { - p++; - } - } - - rc = 0; - -cleanup: - kfree(buf); - return rc; -} - -static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - int rc; - struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - struct huion_drvdata *drvdata; - - /* Allocate and assign driver data */ - drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); - if (drvdata == NULL) { - hid_err(hdev, "failed to allocate driver data\n"); - return -ENOMEM; - } - hid_set_drvdata(hdev, drvdata); - - switch (id->product) { - case USB_DEVICE_ID_HUION_TABLET: - /* If this is the pen interface */ - if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { - rc = huion_tablet_enable(hdev); - if (rc) { - hid_err(hdev, "tablet enabling failed\n"); - return rc; - } - } - break; - } - - rc = hid_parse(hdev); - if (rc) { - hid_err(hdev, "parse failed\n"); - return rc; - } - - rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (rc) { - hid_err(hdev, "hw start failed\n"); - return rc; - } - - return 0; -} - -static int huion_raw_event(struct hid_device *hdev, struct hid_report *report, - u8 *data, int size) -{ - struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - - /* If this is a pen input report */ - if (intf->cur_altsetting->desc.bInterfaceNumber == 0 && - report->type == HID_INPUT_REPORT && - report->id == 0x07 && size >= 2) - /* Invert the in-range bit */ - data[1] ^= 0x40; - - return 0; -} - -static const struct hid_device_id huion_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, - { } -}; -MODULE_DEVICE_TABLE(hid, huion_devices); - -static struct hid_driver huion_driver = { - .name = "huion", - .id_table = huion_devices, - .probe = huion_probe, - .report_fixup = huion_report_fixup, - .raw_event = huion_raw_event, -}; -module_hid_driver(huion_driver); - -MODULE_AUTHOR("Martin Rusko"); -MODULE_DESCRIPTION("Huion HID driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 9c4786759f16f4..41f167e4d75fde 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -459,6 +459,11 @@ #define USB_DEVICE_ID_UGCI_FLYING 0x0020 #define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 +#define USB_VENDOR_ID_HP 0x03f0 +#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A 0x0a4a +#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A 0x0b4a +#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE 0x134a + #define USB_VENDOR_ID_HUION 0x256c #define USB_DEVICE_ID_HUION_TABLET 0x006e @@ -533,6 +538,7 @@ #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011 #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2 0x501a #define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013 +#define USB_DEVICE_ID_KYE_PENSKETCH_M912 0x5015 #define USB_VENDOR_ID_LABTEC 0x1020 #define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006 @@ -591,6 +597,9 @@ #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f #define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306 +#define USB_DEVICE_ID_LOGITECH_MOUSE_C01A 0xc01a +#define USB_DEVICE_ID_LOGITECH_MOUSE_C05A 0xc05a +#define USB_DEVICE_ID_LOGITECH_MOUSE_C06A 0xc06a #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211 #define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 @@ -1022,6 +1031,7 @@ #define USB_DEVICE_ID_ZYTRONIC_ZXY100 0x0005 #define USB_VENDOR_ID_PRIMAX 0x0461 +#define USB_DEVICE_ID_PRIMAX_MOUSE_4D22 0x4d22 #define USB_DEVICE_ID_PRIMAX_KEYBOARD 0x4e05 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 32c2da49bd5b3f..008e89bf6f3c3d 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -720,6 +720,29 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } break; + case HID_UP_TELEPHONY: + switch (usage->hid & HID_USAGE) { + case 0x2f: map_key_clear(KEY_MICMUTE); break; + case 0xb0: map_key_clear(KEY_NUMERIC_0); break; + case 0xb1: map_key_clear(KEY_NUMERIC_1); break; + case 0xb2: map_key_clear(KEY_NUMERIC_2); break; + case 0xb3: map_key_clear(KEY_NUMERIC_3); break; + case 0xb4: map_key_clear(KEY_NUMERIC_4); break; + case 0xb5: map_key_clear(KEY_NUMERIC_5); break; + case 0xb6: map_key_clear(KEY_NUMERIC_6); break; + case 0xb7: map_key_clear(KEY_NUMERIC_7); break; + case 0xb8: map_key_clear(KEY_NUMERIC_8); break; + case 0xb9: map_key_clear(KEY_NUMERIC_9); break; + case 0xba: map_key_clear(KEY_NUMERIC_STAR); break; + case 0xbb: map_key_clear(KEY_NUMERIC_POUND); break; + case 0xbc: map_key_clear(KEY_NUMERIC_A); break; + case 0xbd: map_key_clear(KEY_NUMERIC_B); break; + case 0xbe: map_key_clear(KEY_NUMERIC_C); break; + case 0xbf: map_key_clear(KEY_NUMERIC_D); break; + default: goto ignore; + } + break; + case HID_UP_CONSUMER: /* USB HUT v1.12, pages 75-84 */ switch (usage->hid & HID_USAGE) { case 0x000: goto ignore; diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index 158fcf577fae57..32e6d8d9ded0ed 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -268,6 +268,137 @@ static __u8 easypen_m610x_rdesc_fixed[] = { 0xC0 /* End Collection */ }; + +/* Original PenSketch M912 report descriptor size */ +#define PENSKETCH_M912_RDESC_ORIG_SIZE 482 + +/* Fixed PenSketch M912 report descriptor */ +static __u8 pensketch_m912_rdesc_fixed[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x08, /* Usage (00h), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x05, /* Report ID (5), */ + 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ + 0x09, 0x01, /* Usage (01h), */ + 0x15, 0x81, /* Logical Minimum (-127), */ + 0x25, 0x7F, /* Logical Maximum (127), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x07, /* Report Count (7), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0xC0, /* End Collection, */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x10, /* Report ID (16), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x04, /* Report Count (4), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0xA4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x55, 0xFD, /* Unit Exponent (-3), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x14, /* Logical Minimum (0), */ + 0x34, /* Physical Minimum (0), */ + 0x09, 0x30, /* Usage (X), */ + 0x27, 0x00, 0xF0, 0x00, 0x00, /* Logical Maximum (61440), */ + 0x46, 0xE0, 0x2E, /* Physical Maximum (12000), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x27, 0x00, 0xB4, 0x00, 0x00, /* Logical Maximum (46080), */ + 0x46, 0x28, 0x23, /* Physical Maximum (9000), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x14, /* Logical Minimum (0), */ + 0x26, 0xFF, 0x07, /* Logical Maximum (2047), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xC0, /* End Collection, */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x21, /* Usage (Puck), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x11, /* Report ID (17), */ + 0x09, 0x21, /* Usage (Puck), */ + 0xA0, /* Collection (Physical), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x75, 0x01, /* Report Size (1), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x03, /* Usage Maximum (03h), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x04, /* Report Count (4), */ + 0x81, 0x01, /* Input (Constant), */ + 0x95, 0x01, /* Report Count (1), */ + 0x0B, 0x32, 0x00, 0x0D, 0x00, /* Usage (Digitizer In Range), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0xA4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0x55, 0xFD, /* Unit Exponent (-3), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x14, /* Logical Minimum (0), */ + 0x34, /* Physical Minimum (0), */ + 0x09, 0x30, /* Usage (X), */ + 0x27, 0x00, 0xF0, 0x00, 0x00, /* Logical Maximum (61440), */ + 0x46, 0xE0, 0x2E, /* Physical Maximum (12000), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x27, 0x00, 0xB4, 0x00, 0x00, /* Logical Maximum (46080), */ + 0x46, 0x28, 0x23, /* Physical Maximum (9000), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x38, /* Usage (Wheel), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x15, 0xFF, /* Logical Minimum (-1), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x34, /* Physical Minimum (0), */ + 0x44, /* Physical Maximum (0), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0xB4, /* Pop, */ + 0xC0, /* End Collection, */ + 0xC0, /* End Collection, */ + 0x05, 0x0C, /* Usage Page (Consumer), */ + 0x09, 0x01, /* Usage (Consumer Control), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x12, /* Report ID (18), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x08, /* Report Count (8), */ + 0x05, 0x0C, /* Usage Page (Consumer), */ + 0x0A, 0x6A, 0x02, /* Usage (AC Delete), */ + 0x0A, 0x1A, 0x02, /* Usage (AC Undo), */ + 0x0A, 0x01, 0x02, /* Usage (AC New), */ + 0x0A, 0x2F, 0x02, /* Usage (AC Zoom), */ + 0x0A, 0x25, 0x02, /* Usage (AC Forward), */ + 0x0A, 0x24, 0x02, /* Usage (AC Back), */ + 0x0A, 0x2D, 0x02, /* Usage (AC Zoom In), */ + 0x0A, 0x2E, 0x02, /* Usage (AC Zoom Out), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x30, /* Report Count (48), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0xC0 /* End Collection */ +}; + static __u8 *kye_consumer_control_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize, int offset, const char *device_name) { /* @@ -335,6 +466,12 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, *rsize = sizeof(easypen_m610x_rdesc_fixed); } break; + case USB_DEVICE_ID_KYE_PENSKETCH_M912: + if (*rsize == PENSKETCH_M912_RDESC_ORIG_SIZE) { + rdesc = pensketch_m912_rdesc_fixed; + *rsize = sizeof(pensketch_m912_rdesc_fixed); + } + break; case USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE: rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 104, "Genius Gila Gaming Mouse"); @@ -418,6 +555,7 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id) case USB_DEVICE_ID_KYE_MOUSEPEN_I608X: case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2: case USB_DEVICE_ID_KYE_EASYPEN_M610X: + case USB_DEVICE_ID_KYE_PENSKETCH_M912: ret = kye_tablet_enable(hdev); if (ret) { hid_err(hdev, "tablet enabling failed\n"); @@ -457,6 +595,8 @@ static const struct hid_device_id kye_devices[] = { USB_DEVICE_ID_GENIUS_GX_IMPERATOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_MANTICORE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, + USB_DEVICE_ID_KYE_PENSKETCH_M912) }, { } }; MODULE_DEVICE_TABLE(hid, kye_devices); diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index f91ff145db9a07..b86c18e651ed36 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -27,6 +27,7 @@ #include "usbhid/usbhid.h" #include "hid-ids.h" #include "hid-lg.h" +#include "hid-lg4ff.h" #define LG_RDESC 0x001 #define LG_BAD_RELATIVE_KEYS 0x002 @@ -818,4 +819,10 @@ static struct hid_driver lg_driver = { }; module_hid_driver(lg_driver); +#ifdef CONFIG_LOGIWHEELS_FF +int lg4ff_no_autoswitch = 0; +module_param_named(lg4ff_no_autoswitch, lg4ff_no_autoswitch, int, S_IRUGO); +MODULE_PARM_DESC(lg4ff_no_autoswitch, "Do not switch multimode wheels to their native mode automatically"); +#endif + MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h index 142ce3f5f055f0..10dd8f02413525 100644 --- a/drivers/hid/hid-lg.h +++ b/drivers/hid/hid-lg.h @@ -24,16 +24,4 @@ int lg3ff_init(struct hid_device *hdev); static inline int lg3ff_init(struct hid_device *hdev) { return -1; } #endif -#ifdef CONFIG_LOGIWHEELS_FF -int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, - struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data); -int lg4ff_init(struct hid_device *hdev); -int lg4ff_deinit(struct hid_device *hdev); -#else -static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, - struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) { return 0; } -static inline int lg4ff_init(struct hid_device *hdev) { return -1; } -static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; } -#endif - #endif diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index db0dd9b17e538d..1232210b1cc586 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -30,23 +30,44 @@ #include "usbhid/usbhid.h" #include "hid-lg.h" +#include "hid-lg4ff.h" #include "hid-ids.h" -#define DFGT_REV_MAJ 0x13 -#define DFGT_REV_MIN 0x22 -#define DFGT2_REV_MIN 0x26 -#define DFP_REV_MAJ 0x11 -#define DFP_REV_MIN 0x06 -#define FFEX_REV_MAJ 0x21 -#define FFEX_REV_MIN 0x00 -#define G25_REV_MAJ 0x12 -#define G25_REV_MIN 0x22 -#define G27_REV_MAJ 0x12 -#define G27_REV_MIN 0x38 -#define G27_2_REV_MIN 0x39 - #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev) +#define LG4FF_MMODE_IS_MULTIMODE 0 +#define LG4FF_MMODE_SWITCHED 1 +#define LG4FF_MMODE_NOT_MULTIMODE 2 + +#define LG4FF_MODE_NATIVE_IDX 0 +#define LG4FF_MODE_DFEX_IDX 1 +#define LG4FF_MODE_DFP_IDX 2 +#define LG4FF_MODE_G25_IDX 3 +#define LG4FF_MODE_DFGT_IDX 4 +#define LG4FF_MODE_G27_IDX 5 +#define LG4FF_MODE_MAX_IDX 6 + +#define LG4FF_MODE_NATIVE BIT(LG4FF_MODE_NATIVE_IDX) +#define LG4FF_MODE_DFEX BIT(LG4FF_MODE_DFEX_IDX) +#define LG4FF_MODE_DFP BIT(LG4FF_MODE_DFP_IDX) +#define LG4FF_MODE_G25 BIT(LG4FF_MODE_G25_IDX) +#define LG4FF_MODE_DFGT BIT(LG4FF_MODE_DFGT_IDX) +#define LG4FF_MODE_G27 BIT(LG4FF_MODE_G27_IDX) + +#define LG4FF_DFEX_TAG "DF-EX" +#define LG4FF_DFEX_NAME "Driving Force / Formula EX" +#define LG4FF_DFP_TAG "DFP" +#define LG4FF_DFP_NAME "Driving Force Pro" +#define LG4FF_G25_TAG "G25" +#define LG4FF_G25_NAME "G25 Racing Wheel" +#define LG4FF_G27_TAG "G27" +#define LG4FF_G27_NAME "G27 Racing Wheel" +#define LG4FF_DFGT_TAG "DFGT" +#define LG4FF_DFGT_NAME "Driving Force GT" + +#define LG4FF_FFEX_REV_MAJ 0x21 +#define LG4FF_FFEX_REV_MIN 0x00 + static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range); static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range); @@ -59,6 +80,10 @@ struct lg4ff_device_entry { __u8 led_state; struct led_classdev *led[5]; #endif + u32 alternate_modes; + const char *real_tag; + const char *real_name; + u16 real_product_id; struct list_head list; void (*set_range)(struct hid_device *hid, u16 range); }; @@ -77,6 +102,35 @@ struct lg4ff_wheel { void (*set_range)(struct hid_device *hid, u16 range); }; +struct lg4ff_compat_mode_switch { + const __u8 cmd_count; /* Number of commands to send */ + const __u8 cmd[]; +}; + +struct lg4ff_wheel_ident_info { + const u16 mask; + const u16 result; + const u16 real_product_id; +}; + +struct lg4ff_wheel_ident_checklist { + const u32 count; + const struct lg4ff_wheel_ident_info *models[]; +}; + +struct lg4ff_multimode_wheel { + const u16 product_id; + const u32 alternate_modes; + const char *real_tag; + const char *real_name; +}; + +struct lg4ff_alternate_mode { + const u16 product_id; + const char *tag; + const char *name; +}; + static const struct lg4ff_wheel lg4ff_devices[] = { {USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, @@ -88,46 +142,106 @@ static const struct lg4ff_wheel lg4ff_devices[] = { {USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270, NULL} }; -struct lg4ff_native_cmd { - const __u8 cmd_num; /* Number of commands to send */ - const __u8 cmd[]; +static const struct lg4ff_multimode_wheel lg4ff_multimode_wheels[] = { + {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, + LG4FF_MODE_NATIVE | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, + LG4FF_DFP_TAG, LG4FF_DFP_NAME}, + {USB_DEVICE_ID_LOGITECH_G25_WHEEL, + LG4FF_MODE_NATIVE | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, + LG4FF_G25_TAG, LG4FF_G25_NAME}, + {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, + LG4FF_MODE_NATIVE | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, + LG4FF_DFGT_TAG, LG4FF_DFGT_NAME}, + {USB_DEVICE_ID_LOGITECH_G27_WHEEL, + LG4FF_MODE_NATIVE | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, + LG4FF_G27_TAG, LG4FF_G27_NAME}, }; -struct lg4ff_usb_revision { - const __u16 rev_maj; - const __u16 rev_min; - const struct lg4ff_native_cmd *command; +static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = { + [LG4FF_MODE_NATIVE_IDX] = {0, "native", ""}, + [LG4FF_MODE_DFEX_IDX] = {USB_DEVICE_ID_LOGITECH_WHEEL, LG4FF_DFEX_TAG, LG4FF_DFEX_NAME}, + [LG4FF_MODE_DFP_IDX] = {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, LG4FF_DFP_TAG, LG4FF_DFP_NAME}, + [LG4FF_MODE_G25_IDX] = {USB_DEVICE_ID_LOGITECH_G25_WHEEL, LG4FF_G25_TAG, LG4FF_G25_NAME}, + [LG4FF_MODE_DFGT_IDX] = {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, LG4FF_DFGT_TAG, LG4FF_DFGT_NAME}, + [LG4FF_MODE_G27_IDX] = {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_G27_TAG, LG4FF_G27_NAME} }; -static const struct lg4ff_native_cmd native_dfp = { - 1, - {0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} +/* Multimode wheel identificators */ +static const struct lg4ff_wheel_ident_info lg4ff_dfp_ident_info = { + 0xf000, + 0x1000, + USB_DEVICE_ID_LOGITECH_DFP_WHEEL +}; + +static const struct lg4ff_wheel_ident_info lg4ff_g25_ident_info = { + 0xff00, + 0x1200, + USB_DEVICE_ID_LOGITECH_G25_WHEEL +}; + +static const struct lg4ff_wheel_ident_info lg4ff_g27_ident_info = { + 0xfff0, + 0x1230, + USB_DEVICE_ID_LOGITECH_G27_WHEEL }; -static const struct lg4ff_native_cmd native_dfgt = { +static const struct lg4ff_wheel_ident_info lg4ff_dfgt_ident_info = { + 0xff00, + 0x1300, + USB_DEVICE_ID_LOGITECH_DFGT_WHEEL +}; + +/* Multimode wheel identification checklists */ +static const struct lg4ff_wheel_ident_checklist lg4ff_main_checklist = { + 4, + {&lg4ff_dfgt_ident_info, + &lg4ff_g27_ident_info, + &lg4ff_g25_ident_info, + &lg4ff_dfp_ident_info} +}; + +/* Compatibility mode switching commands */ +/* EXT_CMD9 - Understood by G27 and DFGT */ +static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfex = { 2, - {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1st command */ - 0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00} /* 2nd command */ + {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */ + 0xf8, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DF-EX with detach */ }; -static const struct lg4ff_native_cmd native_g25 = { - 1, - {0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00} +static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfp = { + 2, + {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */ + 0xf8, 0x09, 0x01, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DFP with detach */ +}; + +static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g25 = { + 2, + {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */ + 0xf8, 0x09, 0x02, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G25 with detach */ +}; + +static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfgt = { + 2, + {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */ + 0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DFGT with detach */ }; -static const struct lg4ff_native_cmd native_g27 = { +static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g27 = { 2, - {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1st command */ - 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* 2nd command */ + {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */ + 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G27 with detach */ }; -static const struct lg4ff_usb_revision lg4ff_revs[] = { - {DFGT_REV_MAJ, DFGT_REV_MIN, &native_dfgt}, /* Driving Force GT */ - {DFGT_REV_MAJ, DFGT2_REV_MIN, &native_dfgt}, /* Driving Force GT v2 */ - {DFP_REV_MAJ, DFP_REV_MIN, &native_dfp}, /* Driving Force Pro */ - {G25_REV_MAJ, G25_REV_MIN, &native_g25}, /* G25 */ - {G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */ - {G27_REV_MAJ, G27_2_REV_MIN, &native_g27}, /* G27 v2 */ +/* EXT_CMD1 - Understood by DFP, G25, G27 and DFGT */ +static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext01_dfp = { + 1, + {0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} +}; + +/* EXT_CMD16 - Understood by G25 and G27 */ +static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext16_g25 = { + 1, + {0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00} }; /* Recalculates X axis value accordingly to currently selected range */ @@ -396,20 +510,216 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range) hid_hw_request(hid, report, HID_REQ_SET_REPORT); } -static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_native_cmd *cmd) +static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(const u16 real_product_id, const u16 target_product_id) +{ + switch (real_product_id) { + case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: + switch (target_product_id) { + case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: + return &lg4ff_mode_switch_ext01_dfp; + /* DFP can only be switched to its native mode */ + default: + return NULL; + } + break; + case USB_DEVICE_ID_LOGITECH_G25_WHEEL: + switch (target_product_id) { + case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: + return &lg4ff_mode_switch_ext01_dfp; + case USB_DEVICE_ID_LOGITECH_G25_WHEEL: + return &lg4ff_mode_switch_ext16_g25; + /* G25 can only be switched to DFP mode or its native mode */ + default: + return NULL; + } + break; + case USB_DEVICE_ID_LOGITECH_G27_WHEEL: + switch (target_product_id) { + case USB_DEVICE_ID_LOGITECH_WHEEL: + return &lg4ff_mode_switch_ext09_dfex; + case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: + return &lg4ff_mode_switch_ext09_dfp; + case USB_DEVICE_ID_LOGITECH_G25_WHEEL: + return &lg4ff_mode_switch_ext09_g25; + case USB_DEVICE_ID_LOGITECH_G27_WHEEL: + return &lg4ff_mode_switch_ext09_g27; + /* G27 can only be switched to DF-EX, DFP, G25 or its native mode */ + default: + return NULL; + } + break; + case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: + switch (target_product_id) { + case USB_DEVICE_ID_LOGITECH_WHEEL: + return &lg4ff_mode_switch_ext09_dfex; + case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: + return &lg4ff_mode_switch_ext09_dfp; + case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: + return &lg4ff_mode_switch_ext09_dfgt; + /* DFGT can only be switched to DF-EX, DFP or its native mode */ + default: + return NULL; + } + break; + /* No other wheels have multiple modes */ + default: + return NULL; + } +} + +static int lg4ff_switch_compatibility_mode(struct hid_device *hid, const struct lg4ff_compat_mode_switch *s) { struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct hid_report *report = list_entry(report_list->next, struct hid_report, list); - __u8 i, j; + __s32 *value = report->field[0]->value; + u8 i; - j = 0; - while (j < 7*cmd->cmd_num) { - for (i = 0; i < 7; i++) - report->field[0]->value[i] = cmd->cmd[j++]; + for (i = 0; i < s->cmd_count; i++) { + u8 j; + + for (j = 0; j < 7; j++) + value[j] = s->cmd[j + (7*i)]; hid_hw_request(hid, report, HID_REQ_SET_REPORT); } + hid_hw_wait(hid); + return 0; +} + +static ssize_t lg4ff_alternate_modes_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct hid_device *hid = to_hid_device(dev); + struct lg4ff_device_entry *entry; + struct lg_drv_data *drv_data; + ssize_t count = 0; + int i; + + drv_data = hid_get_drvdata(hid); + if (!drv_data) { + hid_err(hid, "Private driver data not found!\n"); + return 0; + } + + entry = drv_data->device_props; + if (!entry) { + hid_err(hid, "Device properties not found!\n"); + return 0; + } + + if (!entry->real_name) { + hid_err(hid, "NULL pointer to string\n"); + return 0; + } + + for (i = 0; i < LG4FF_MODE_MAX_IDX; i++) { + if (entry->alternate_modes & BIT(i)) { + /* Print tag and full name */ + count += scnprintf(buf + count, PAGE_SIZE - count, "%s: %s", + lg4ff_alternate_modes[i].tag, + !lg4ff_alternate_modes[i].product_id ? entry->real_name : lg4ff_alternate_modes[i].name); + if (count >= PAGE_SIZE - 1) + return count; + + /* Mark the currently active mode with an asterisk */ + if (lg4ff_alternate_modes[i].product_id == entry->product_id || + (lg4ff_alternate_modes[i].product_id == 0 && entry->product_id == entry->real_product_id)) + count += scnprintf(buf + count, PAGE_SIZE - count, " *\n"); + else + count += scnprintf(buf + count, PAGE_SIZE - count, "\n"); + + if (count >= PAGE_SIZE - 1) + return count; + } + } + + return count; +} + +static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct hid_device *hid = to_hid_device(dev); + struct lg4ff_device_entry *entry; + struct lg_drv_data *drv_data; + const struct lg4ff_compat_mode_switch *s; + u16 target_product_id = 0; + int i, ret; + char *lbuf; + + drv_data = hid_get_drvdata(hid); + if (!drv_data) { + hid_err(hid, "Private driver data not found!\n"); + return -EINVAL; + } + + entry = drv_data->device_props; + if (!entry) { + hid_err(hid, "Device properties not found!\n"); + return -EINVAL; + } + + /* Allow \n at the end of the input parameter */ + lbuf = kasprintf(GFP_KERNEL, "%s", buf); + if (!lbuf) + return -ENOMEM; + + i = strlen(lbuf); + if (lbuf[i-1] == '\n') { + if (i == 1) { + kfree(lbuf); + return -EINVAL; + } + lbuf[i-1] = '\0'; + } + + for (i = 0; i < LG4FF_MODE_MAX_IDX; i++) { + const u16 mode_product_id = lg4ff_alternate_modes[i].product_id; + const char *tag = lg4ff_alternate_modes[i].tag; + + if (entry->alternate_modes & BIT(i)) { + if (!strcmp(tag, lbuf)) { + if (!mode_product_id) + target_product_id = entry->real_product_id; + else + target_product_id = mode_product_id; + break; + } + } + } + + if (i == LG4FF_MODE_MAX_IDX) { + hid_info(hid, "Requested mode \"%s\" is not supported by the device\n", lbuf); + kfree(lbuf); + return -EINVAL; + } + kfree(lbuf); /* Not needed anymore */ + + if (target_product_id == entry->product_id) /* Nothing to do */ + return count; + + /* Automatic switching has to be disabled for the switch to DF-EX mode to work correctly */ + if (target_product_id == USB_DEVICE_ID_LOGITECH_WHEEL && !lg4ff_no_autoswitch) { + hid_info(hid, "\"%s\" cannot be switched to \"DF-EX\" mode. Load the \"hid_logitech\" module with \"lg4ff_no_autoswitch=1\" parameter set and try again\n", + entry->real_name); + return -EINVAL; + } + + /* Take care of hardware limitations */ + if ((entry->real_product_id == USB_DEVICE_ID_LOGITECH_DFP_WHEEL || entry->real_product_id == USB_DEVICE_ID_LOGITECH_G25_WHEEL) && + entry->product_id > target_product_id) { + hid_info(hid, "\"%s\" cannot be switched back into \"%s\" mode\n", entry->real_name, lg4ff_alternate_modes[i].name); + return -EINVAL; + } + + s = lg4ff_get_mode_switch_command(entry->real_product_id, target_product_id); + if (!s) { + hid_err(hid, "Invalid target product ID %X\n", target_product_id); + return -EINVAL; + } + + ret = lg4ff_switch_compatibility_mode(hid, s); + return (ret == 0 ? count : ret); } +static DEVICE_ATTR(alternate_modes, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_alternate_modes_show, lg4ff_alternate_modes_store); /* Read current range and display it in terminal */ static ssize_t range_show(struct device *dev, struct device_attribute *attr, @@ -472,6 +782,41 @@ static ssize_t range_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RW(range); +static ssize_t lg4ff_real_id_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct hid_device *hid = to_hid_device(dev); + struct lg4ff_device_entry *entry; + struct lg_drv_data *drv_data; + size_t count; + + drv_data = hid_get_drvdata(hid); + if (!drv_data) { + hid_err(hid, "Private driver data not found!\n"); + return 0; + } + + entry = drv_data->device_props; + if (!entry) { + hid_err(hid, "Device properties not found!\n"); + return 0; + } + + if (!entry->real_tag || !entry->real_name) { + hid_err(hid, "NULL pointer to string\n"); + return 0; + } + + count = scnprintf(buf, PAGE_SIZE, "%s: %s\n", entry->real_tag, entry->real_name); + return count; +} + +static ssize_t lg4ff_real_id_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + /* Real ID is a read-only value */ + return -EPERM; +} +static DEVICE_ATTR(real_id, S_IRUGO, lg4ff_real_id_show, lg4ff_real_id_store); + #ifdef CONFIG_LEDS_CLASS static void lg4ff_set_leds(struct hid_device *hid, __u8 leds) { @@ -555,20 +900,119 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde } #endif +static u16 lg4ff_identify_multimode_wheel(struct hid_device *hid, const u16 reported_product_id, const u16 bcdDevice) +{ + const struct lg4ff_wheel_ident_checklist *checklist; + int i, from_idx, to_idx; + + switch (reported_product_id) { + case USB_DEVICE_ID_LOGITECH_WHEEL: + case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: + checklist = &lg4ff_main_checklist; + from_idx = 0; + to_idx = checklist->count - 1; + break; + case USB_DEVICE_ID_LOGITECH_G25_WHEEL: + checklist = &lg4ff_main_checklist; + from_idx = 0; + to_idx = checklist->count - 2; /* End identity check at G25 */ + break; + case USB_DEVICE_ID_LOGITECH_G27_WHEEL: + checklist = &lg4ff_main_checklist; + from_idx = 1; /* Start identity check at G27 */ + to_idx = checklist->count - 3; /* End identity check at G27 */ + break; + case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: + checklist = &lg4ff_main_checklist; + from_idx = 0; + to_idx = checklist->count - 4; /* End identity check at DFGT */ + break; + default: + return 0; + } + + for (i = from_idx; i <= to_idx; i++) { + const u16 mask = checklist->models[i]->mask; + const u16 result = checklist->models[i]->result; + const u16 real_product_id = checklist->models[i]->real_product_id; + + if ((bcdDevice & mask) == result) { + dbg_hid("Found wheel with real PID %X whose reported PID is %X\n", real_product_id, reported_product_id); + return real_product_id; + } + } + + /* No match found. This is either Driving Force or an unknown + * wheel model, do not touch it */ + dbg_hid("Wheel with bcdDevice %X was not recognized as multimode wheel, leaving in its current mode\n", bcdDevice); + return 0; +} + +static int lg4ff_handle_multimode_wheel(struct hid_device *hid, u16 *real_product_id, const u16 bcdDevice) +{ + const u16 reported_product_id = hid->product; + int ret; + + *real_product_id = lg4ff_identify_multimode_wheel(hid, reported_product_id, bcdDevice); + /* Probed wheel is not a multimode wheel */ + if (!*real_product_id) { + *real_product_id = reported_product_id; + dbg_hid("Wheel is not a multimode wheel\n"); + return LG4FF_MMODE_NOT_MULTIMODE; + } + + /* Switch from "Driving Force" mode to native mode automatically. + * Otherwise keep the wheel in its current mode */ + if (reported_product_id == USB_DEVICE_ID_LOGITECH_WHEEL && + reported_product_id != *real_product_id && + !lg4ff_no_autoswitch) { + const struct lg4ff_compat_mode_switch *s = lg4ff_get_mode_switch_command(*real_product_id, *real_product_id); + + if (!s) { + hid_err(hid, "Invalid product id %X\n", *real_product_id); + return LG4FF_MMODE_NOT_MULTIMODE; + } + + ret = lg4ff_switch_compatibility_mode(hid, s); + if (ret) { + /* Wheel could not have been switched to native mode, + * leave it in "Driving Force" mode and continue */ + hid_err(hid, "Unable to switch wheel mode, errno %d\n", ret); + return LG4FF_MMODE_IS_MULTIMODE; + } + return LG4FF_MMODE_SWITCHED; + } + + return LG4FF_MMODE_IS_MULTIMODE; +} + + int lg4ff_init(struct hid_device *hid) { struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); struct input_dev *dev = hidinput->input; + const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor); + const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice); struct lg4ff_device_entry *entry; struct lg_drv_data *drv_data; - struct usb_device_descriptor *udesc; int error, i, j; - __u16 bcdDevice, rev_maj, rev_min; + int mmode_ret, mmode_idx = -1; + u16 real_product_id; /* Check that the report looks ok */ if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7)) return -1; + /* Check if a multimode wheel has been connected and + * handle it appropriately */ + mmode_ret = lg4ff_handle_multimode_wheel(hid, &real_product_id, bcdDevice); + + /* Wheel has been told to switch to native mode. There is no point in going on + * with the initialization as the wheel will do a USB reset when it switches mode + */ + if (mmode_ret == LG4FF_MMODE_SWITCHED) + return 0; + /* Check what wheel has been connected */ for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) { if (hid->product == lg4ff_devices[i].product_id) { @@ -583,25 +1027,15 @@ int lg4ff_init(struct hid_device *hid) return -1; } - /* Attempt to switch wheel to native mode when applicable */ - udesc = &(hid_to_usb_dev(hid)->descriptor); - if (!udesc) { - hid_err(hid, "NULL USB device descriptor\n"); - return -1; - } - bcdDevice = le16_to_cpu(udesc->bcdDevice); - rev_maj = bcdDevice >> 8; - rev_min = bcdDevice & 0xff; - - if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_WHEEL) { - dbg_hid("Generic wheel detected, can it do native?\n"); - dbg_hid("USB revision: %2x.%02x\n", rev_maj, rev_min); + if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) { + for (mmode_idx = 0; mmode_idx < ARRAY_SIZE(lg4ff_multimode_wheels); mmode_idx++) { + if (real_product_id == lg4ff_multimode_wheels[mmode_idx].product_id) + break; + } - for (j = 0; j < ARRAY_SIZE(lg4ff_revs); j++) { - if (lg4ff_revs[j].rev_maj == rev_maj && lg4ff_revs[j].rev_min == rev_min) { - hid_lg4ff_switch_native(hid, lg4ff_revs[j].command); - hid_info(hid, "Switched to native mode\n"); - } + if (mmode_idx == ARRAY_SIZE(lg4ff_multimode_wheels)) { + hid_err(hid, "Device product ID %X is not listed as a multimode wheel", real_product_id); + return -1; } } @@ -630,14 +1064,23 @@ int lg4ff_init(struct hid_device *hid) drv_data->device_props = entry; entry->product_id = lg4ff_devices[i].product_id; + entry->real_product_id = real_product_id; entry->min_range = lg4ff_devices[i].min_range; entry->max_range = lg4ff_devices[i].max_range; entry->set_range = lg4ff_devices[i].set_range; + if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) { + BUG_ON(mmode_idx == -1); + entry->alternate_modes = lg4ff_multimode_wheels[mmode_idx].alternate_modes; + entry->real_tag = lg4ff_multimode_wheels[mmode_idx].real_tag; + entry->real_name = lg4ff_multimode_wheels[mmode_idx].real_name; + } /* Check if autocentering is available and * set the centering force to zero by default */ if (test_bit(FF_AUTOCENTER, dev->ffbit)) { - if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */ + /* Formula Force EX expects different autocentering command */ + if ((bcdDevice >> 8) == LG4FF_FFEX_REV_MAJ && + (bcdDevice & 0xff) == LG4FF_FFEX_REV_MIN) dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex; else dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default; @@ -649,6 +1092,14 @@ int lg4ff_init(struct hid_device *hid) error = device_create_file(&hid->dev, &dev_attr_range); if (error) return error; + if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) { + error = device_create_file(&hid->dev, &dev_attr_real_id); + if (error) + return error; + error = device_create_file(&hid->dev, &dev_attr_alternate_modes); + if (error) + return error; + } dbg_hid("sysfs interface created\n"); /* Set the maximum range to start with */ @@ -711,24 +1162,26 @@ int lg4ff_init(struct hid_device *hid) return 0; } - - int lg4ff_deinit(struct hid_device *hid) { struct lg4ff_device_entry *entry; struct lg_drv_data *drv_data; - device_remove_file(&hid->dev, &dev_attr_range); - drv_data = hid_get_drvdata(hid); if (!drv_data) { hid_err(hid, "Error while deinitializing device, no private driver data.\n"); return -1; } entry = drv_data->device_props; - if (!entry) { - hid_err(hid, "Error while deinitializing device, no device properties data.\n"); - return -1; + if (!entry) + goto out; /* Nothing more to do */ + + device_remove_file(&hid->dev, &dev_attr_range); + + /* Multimode devices will have at least the "MODE_NATIVE" bit set */ + if (entry->alternate_modes) { + device_remove_file(&hid->dev, &dev_attr_real_id); + device_remove_file(&hid->dev, &dev_attr_alternate_modes); } #ifdef CONFIG_LEDS_CLASS @@ -752,6 +1205,7 @@ int lg4ff_deinit(struct hid_device *hid) /* Deallocate memory */ kfree(entry); +out: dbg_hid("Device successfully unregistered\n"); return 0; } diff --git a/drivers/hid/hid-lg4ff.h b/drivers/hid/hid-lg4ff.h new file mode 100644 index 00000000000000..5b6a5086c47fb8 --- /dev/null +++ b/drivers/hid/hid-lg4ff.h @@ -0,0 +1,18 @@ +#ifndef __HID_LG4FF_H +#define __HID_LG4FF_H + +#ifdef CONFIG_LOGIWHEELS_FF +extern int lg4ff_no_autoswitch; /* From hid-lg.c */ + +int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data); +int lg4ff_init(struct hid_device *hdev); +int lg4ff_deinit(struct hid_device *hdev); +#else +static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) { return 0; } +static inline int lg4ff_init(struct hid_device *hdev) { return -1; } +static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; } +#endif + +#endif diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index e77658cd037ca8..b3cf6fd4be9647 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -28,6 +28,11 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Benjamin Tissoires "); MODULE_AUTHOR("Nestor Lopez Casado "); +static bool disable_raw_mode; +module_param(disable_raw_mode, bool, 0644); +MODULE_PARM_DESC(disable_raw_mode, + "Disable Raw mode reporting for touchpads and keep firmware gestures."); + #define REPORT_ID_HIDPP_SHORT 0x10 #define REPORT_ID_HIDPP_LONG 0x11 @@ -1188,6 +1193,11 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) hidpp->quirks = id->driver_data; + if (disable_raw_mode) { + hidpp->quirks &= ~HIDPP_QUIRK_CLASS_WTP; + hidpp->quirks &= ~HIDPP_QUIRK_DELAYED_INIT; + } + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) { ret = wtp_allocate(hdev, id); if (ret) @@ -1210,6 +1220,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) connected = hidpp_is_connected(hidpp); if (id->group != HID_GROUP_LOGITECH_DJ_DEVICE) { if (!connected) { + ret = -ENODEV; hid_err(hdev, "Device not connected"); hid_device_io_stop(hdev); goto hid_parse_fail; diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index f65e78b4699999..6a9b05b328a9d4 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include @@ -72,6 +71,8 @@ MODULE_LICENSE("GPL"); #define MT_INPUTMODE_TOUCHSCREEN 0x02 #define MT_INPUTMODE_TOUCHPAD 0x03 +#define MT_BUTTONTYPE_CLICKPAD 0 + struct mt_slot { __s32 x, y, cx, cy, p, w, h; __s32 contactid; /* the device ContactID assigned to this slot */ @@ -116,6 +117,8 @@ struct mt_device { __u8 touches_by_report; /* how many touches are present in one report: * 1 means we should use a serial protocol * > 1 means hybrid (multitouch) protocol */ + __u8 buttons_count; /* number of physical buttons per touchpad */ + bool is_buttonpad; /* is this device a button pad? */ bool serial_maybe; /* need to check for serial protocol */ bool curvalid; /* is the current contact valid? */ unsigned mt_flags; /* flags to pass to input-mt */ @@ -333,6 +336,16 @@ static void mt_feature_mapping(struct hid_device *hdev, /* check if the maxcontacts is given by the class */ td->maxcontacts = td->mtclass.maxcontacts; + break; + case HID_DG_BUTTONTYPE: + if (usage->usage_index >= field->report_count) { + dev_err(&hdev->dev, "HID_DG_BUTTONTYPE out of range\n"); + break; + } + + if (field->value[usage->usage_index] == MT_BUTTONTYPE_CLICKPAD) + td->is_buttonpad = true; + break; } } @@ -379,6 +392,10 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, td->inputmode_value = MT_INPUTMODE_TOUCHPAD; } + /* count the buttons on touchpads */ + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) + td->buttons_count++; + if (usage->usage_index) prev_usage = &field->usage[usage->usage_index - 1]; @@ -728,6 +745,13 @@ static void mt_touch_input_configured(struct hid_device *hdev, if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) td->mt_flags |= INPUT_MT_DROP_UNUSED; + /* check for clickpads */ + if ((td->mt_flags & INPUT_MT_POINTER) && (td->buttons_count == 1)) + td->is_buttonpad = true; + + if (td->is_buttonpad) + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + input_mt_init_slots(input, td->maxcontacts, td->mt_flags); td->mt_flags = 0; diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 49d4fe4f5987e2..368ffdf2c0a3af 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -104,6 +104,7 @@ struct rmi_data { unsigned long flags; + struct rmi_function f01; struct rmi_function f11; struct rmi_function f30; @@ -124,6 +125,7 @@ struct rmi_data { struct hid_device *hdev; unsigned long device_flags; + unsigned long firmware_id; }; #define RMI_PAGE(addr) (((addr) >> 8) & 0xff) @@ -272,6 +274,46 @@ static inline int rmi_read(struct hid_device *hdev, u16 addr, void *buf) return rmi_read_block(hdev, addr, buf, 1); } +static int rmi_write_block(struct hid_device *hdev, u16 addr, void *buf, + const int len) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + int ret; + + mutex_lock(&data->page_mutex); + + if (RMI_PAGE(addr) != data->page) { + ret = rmi_set_page(hdev, RMI_PAGE(addr)); + if (ret < 0) + goto exit; + } + + data->writeReport[0] = RMI_WRITE_REPORT_ID; + data->writeReport[1] = len; + data->writeReport[2] = addr & 0xFF; + data->writeReport[3] = (addr >> 8) & 0xFF; + memcpy(&data->writeReport[4], buf, len); + + ret = rmi_write_report(hdev, data->writeReport, + data->output_report_size); + if (ret < 0) { + dev_err(&hdev->dev, + "failed to write request output report (%d)\n", + ret); + goto exit; + } + ret = 0; + +exit: + mutex_unlock(&data->page_mutex); + return ret; +} + +static inline int rmi_write(struct hid_device *hdev, u16 addr, void *buf) +{ + return rmi_write_block(hdev, addr, buf, 1); +} + static void rmi_f11_process_touch(struct rmi_data *hdata, int slot, u8 finger_state, u8 *touch_data) { @@ -532,6 +574,9 @@ static void rmi_register_function(struct rmi_data *data, u16 page_base = page << 8; switch (pdt_entry->function_number) { + case 0x01: + f = &data->f01; + break; case 0x11: f = &data->f11; break; @@ -604,6 +649,92 @@ static int rmi_scan_pdt(struct hid_device *hdev) return retval; } +#define RMI_DEVICE_F01_BASIC_QUERY_LEN 11 + +static int rmi_populate_f01(struct hid_device *hdev) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + u8 basic_queries[RMI_DEVICE_F01_BASIC_QUERY_LEN]; + u8 info[3]; + int ret; + bool has_query42; + bool has_lts; + bool has_sensor_id; + bool has_ds4_queries = false; + bool has_build_id_query = false; + bool has_package_id_query = false; + u16 query_offset = data->f01.query_base_addr; + u16 prod_info_addr; + u8 ds4_query_len; + + ret = rmi_read_block(hdev, query_offset, basic_queries, + RMI_DEVICE_F01_BASIC_QUERY_LEN); + if (ret) { + hid_err(hdev, "Can not read basic queries from Function 0x1.\n"); + return ret; + } + + has_lts = !!(basic_queries[0] & BIT(2)); + has_sensor_id = !!(basic_queries[1] & BIT(3)); + has_query42 = !!(basic_queries[1] & BIT(7)); + + query_offset += 11; + prod_info_addr = query_offset + 6; + query_offset += 10; + + if (has_lts) + query_offset += 20; + + if (has_sensor_id) + query_offset++; + + if (has_query42) { + ret = rmi_read(hdev, query_offset, info); + if (ret) { + hid_err(hdev, "Can not read query42.\n"); + return ret; + } + has_ds4_queries = !!(info[0] & BIT(0)); + query_offset++; + } + + if (has_ds4_queries) { + ret = rmi_read(hdev, query_offset, &ds4_query_len); + if (ret) { + hid_err(hdev, "Can not read DS4 Query length.\n"); + return ret; + } + query_offset++; + + if (ds4_query_len > 0) { + ret = rmi_read(hdev, query_offset, info); + if (ret) { + hid_err(hdev, "Can not read DS4 query.\n"); + return ret; + } + + has_package_id_query = !!(info[0] & BIT(0)); + has_build_id_query = !!(info[0] & BIT(1)); + } + } + + if (has_package_id_query) + prod_info_addr++; + + if (has_build_id_query) { + ret = rmi_read_block(hdev, prod_info_addr, info, 3); + if (ret) { + hid_err(hdev, "Can not read product info.\n"); + return ret; + } + + data->firmware_id = info[1] << 8 | info[0]; + data->firmware_id += info[2] * 65536; + } + + return 0; +} + static int rmi_populate_f11(struct hid_device *hdev) { struct rmi_data *data = hid_get_drvdata(hdev); @@ -620,6 +751,8 @@ static int rmi_populate_f11(struct hid_device *hdev) bool has_gestures; bool has_rel; bool has_data40 = false; + bool has_dribble = false; + bool has_palm_detect = false; unsigned x_size, y_size; u16 query_offset; @@ -661,6 +794,14 @@ static int rmi_populate_f11(struct hid_device *hdev) has_rel = !!(buf[0] & BIT(3)); has_gestures = !!(buf[0] & BIT(5)); + ret = rmi_read(hdev, data->f11.query_base_addr + 5, buf); + if (ret) { + hid_err(hdev, "can not get absolute data sources: %d.\n", ret); + return ret; + } + + has_dribble = !!(buf[0] & BIT(4)); + /* * At least 4 queries are guaranteed to be present in F11 * +1 for query 5 which is present since absolute events are @@ -680,6 +821,7 @@ static int rmi_populate_f11(struct hid_device *hdev) ret); return ret; } + has_palm_detect = !!(buf[0] & BIT(0)); has_query10 = !!(buf[0] & BIT(2)); query_offset += 2; /* query 7 and 8 are present */ @@ -766,17 +908,38 @@ static int rmi_populate_f11(struct hid_device *hdev) * retrieve the ctrl registers * the ctrl register has a size of 20 but a fw bug split it into 16 + 4, * and there is no way to know if the first 20 bytes are here or not. - * We use only the first 10 bytes, so get only them. + * We use only the first 12 bytes, so get only them. */ - ret = rmi_read_block(hdev, data->f11.control_base_addr, buf, 10); + ret = rmi_read_block(hdev, data->f11.control_base_addr, buf, 12); if (ret) { - hid_err(hdev, "can not read ctrl block of size 10: %d.\n", ret); + hid_err(hdev, "can not read ctrl block of size 11: %d.\n", ret); return ret; } data->max_x = buf[6] | (buf[7] << 8); data->max_y = buf[8] | (buf[9] << 8); + if (has_dribble) { + buf[0] = buf[0] & ~BIT(6); + ret = rmi_write(hdev, data->f11.control_base_addr, buf); + if (ret) { + hid_err(hdev, "can not write to control reg 0: %d.\n", + ret); + return ret; + } + } + + if (has_palm_detect) { + buf[11] = buf[11] & ~BIT(0); + ret = rmi_write(hdev, data->f11.control_base_addr + 11, + &buf[11]); + if (ret) { + hid_err(hdev, "can not write to control reg 11: %d.\n", + ret); + return ret; + } + } + return 0; } @@ -858,6 +1021,12 @@ static int rmi_populate(struct hid_device *hdev) return ret; } + ret = rmi_populate_f01(hdev); + if (ret) { + hid_err(hdev, "Error while initializing F01 (%d).\n", ret); + return ret; + } + ret = rmi_populate_f11(hdev); if (ret) { hid_err(hdev, "Error while initializing F11 (%d).\n", ret); @@ -907,6 +1076,8 @@ static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) if (ret) goto exit; + hid_info(hdev, "firmware id: %ld\n", data->firmware_id); + __set_bit(EV_ABS, input->evbit); input_set_abs_params(input, ABS_MT_POSITION_X, 1, data->max_x, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 1, data->max_y, 0, 0); diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c new file mode 100644 index 00000000000000..5614fee82347f3 --- /dev/null +++ b/drivers/hid/hid-sensor-custom.c @@ -0,0 +1,849 @@ +/* + * hid-sensor-custom.c + * Copyright (c) 2015, Intel Corporation. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HID_CUSTOM_NAME_LENGTH 64 +#define HID_CUSTOM_MAX_CORE_ATTRS 10 +#define HID_CUSTOM_TOTAL_ATTRS (HID_CUSTOM_MAX_CORE_ATTRS + 1) +#define HID_CUSTOM_FIFO_SIZE 4096 +#define HID_CUSTOM_MAX_FEATURE_BYTES 64 + +struct hid_sensor_custom_field { + int report_id; + char group_name[HID_CUSTOM_NAME_LENGTH]; + struct hid_sensor_hub_attribute_info attribute; + struct device_attribute sd_attrs[HID_CUSTOM_MAX_CORE_ATTRS]; + char attr_name[HID_CUSTOM_TOTAL_ATTRS][HID_CUSTOM_NAME_LENGTH]; + struct attribute *attrs[HID_CUSTOM_TOTAL_ATTRS]; + struct attribute_group hid_custom_attribute_group; +}; + +struct hid_sensor_custom { + struct mutex mutex; + struct platform_device *pdev; + struct hid_sensor_hub_device *hsdev; + struct hid_sensor_hub_callbacks callbacks; + int sensor_field_count; + struct hid_sensor_custom_field *fields; + int input_field_count; + int input_report_size; + int input_report_recd_size; + bool input_skip_sample; + bool enable; + struct hid_sensor_custom_field *power_state; + struct hid_sensor_custom_field *report_state; + struct miscdevice custom_dev; + struct kfifo data_fifo; + unsigned long misc_opened; + wait_queue_head_t wait; +}; + +/* Header for each sample to user space via dev interface */ +struct hid_sensor_sample { + u32 usage_id; + u64 timestamp; + u32 raw_len; +} __packed; + +static struct attribute hid_custom_attrs[] = { + {.name = "name", .mode = S_IRUGO}, + {.name = "units", .mode = S_IRUGO}, + {.name = "unit-expo", .mode = S_IRUGO}, + {.name = "minimum", .mode = S_IRUGO}, + {.name = "maximum", .mode = S_IRUGO}, + {.name = "size", .mode = S_IRUGO}, + {.name = "value", .mode = S_IWUSR | S_IRUGO}, + {.name = NULL} +}; + +static const struct hid_custom_usage_desc { + int usage_id; + char *desc; +} hid_custom_usage_desc_table[] = { + {0x200201, "event-sensor-state"}, + {0x200202, "event-sensor-event"}, + {0x200301, "property-friendly-name"}, + {0x200302, "property-persistent-unique-id"}, + {0x200303, "property-sensor-status"}, + {0x200304, "property-min-report-interval"}, + {0x200305, "property-sensor-manufacturer"}, + {0x200306, "property-sensor-model"}, + {0x200307, "property-sensor-serial-number"}, + {0x200308, "property-sensor-description"}, + {0x200309, "property-sensor-connection-type"}, + {0x20030A, "property-sensor-device-path"}, + {0x20030B, "property-hardware-revision"}, + {0x20030C, "property-firmware-version"}, + {0x20030D, "property-release-date"}, + {0x20030E, "property-report-interval"}, + {0x20030F, "property-change-sensitivity-absolute"}, + {0x200310, "property-change-sensitivity-percent-range"}, + {0x200311, "property-change-sensitivity-percent-relative"}, + {0x200312, "property-accuracy"}, + {0x200313, "property-resolution"}, + {0x200314, "property-maximum"}, + {0x200315, "property-minimum"}, + {0x200316, "property-reporting-state"}, + {0x200317, "property-sampling-rate"}, + {0x200318, "property-response-curve"}, + {0x200319, "property-power-state"}, + {0x200540, "data-field-custom"}, + {0x200541, "data-field-custom-usage"}, + {0x200542, "data-field-custom-boolean-array"}, + {0x200543, "data-field-custom-value"}, + {0x200544, "data-field-custom-value_1"}, + {0x200545, "data-field-custom-value_2"}, + {0x200546, "data-field-custom-value_3"}, + {0x200547, "data-field-custom-value_4"}, + {0x200548, "data-field-custom-value_5"}, + {0x200549, "data-field-custom-value_6"}, + {0x20054A, "data-field-custom-value_7"}, + {0x20054B, "data-field-custom-value_8"}, + {0x20054C, "data-field-custom-value_9"}, + {0x20054D, "data-field-custom-value_10"}, + {0x20054E, "data-field-custom-value_11"}, + {0x20054F, "data-field-custom-value_12"}, + {0x200550, "data-field-custom-value_13"}, + {0x200551, "data-field-custom-value_14"}, + {0x200552, "data-field-custom-value_15"}, + {0x200553, "data-field-custom-value_16"}, + {0x200554, "data-field-custom-value_17"}, + {0x200555, "data-field-custom-value_18"}, + {0x200556, "data-field-custom-value_19"}, + {0x200557, "data-field-custom-value_20"}, + {0x200558, "data-field-custom-value_21"}, + {0x200559, "data-field-custom-value_22"}, + {0x20055A, "data-field-custom-value_23"}, + {0x20055B, "data-field-custom-value_24"}, + {0x20055C, "data-field-custom-value_25"}, + {0x20055D, "data-field-custom-value_26"}, + {0x20055E, "data-field-custom-value_27"}, + {0x20055F, "data-field-custom-value_28"}, +}; + +static int usage_id_cmp(const void *p1, const void *p2) +{ + if (*(int *)p1 < *(int *)p2) + return -1; + + if (*(int *)p1 > *(int *)p2) + return 1; + + return 0; +} + +static ssize_t enable_sensor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", sensor_inst->enable); +} + +static int set_power_report_state(struct hid_sensor_custom *sensor_inst, + bool state) +{ + int power_val = -1; + int report_val = -1; + u32 power_state_usage_id; + u32 report_state_usage_id; + int ret; + + /* + * It is possible that the power/report state ids are not present. + * In this case this function will return success. But if the + * ids are present, then it will return error if set fails. + */ + if (state) { + power_state_usage_id = + HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM; + report_state_usage_id = + HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM; + } else { + power_state_usage_id = + HID_USAGE_SENSOR_PROP_POWER_STATE_D4_POWER_OFF_ENUM; + report_state_usage_id = + HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM; + } + + if (sensor_inst->power_state) + power_val = hid_sensor_get_usage_index(sensor_inst->hsdev, + sensor_inst->power_state->attribute.report_id, + sensor_inst->power_state->attribute.index, + power_state_usage_id); + if (sensor_inst->report_state) + report_val = hid_sensor_get_usage_index(sensor_inst->hsdev, + sensor_inst->report_state->attribute.report_id, + sensor_inst->report_state->attribute.index, + report_state_usage_id); + + if (power_val >= 0) { + power_val += + sensor_inst->power_state->attribute.logical_minimum; + ret = sensor_hub_set_feature(sensor_inst->hsdev, + sensor_inst->power_state->attribute.report_id, + sensor_inst->power_state->attribute.index, + sizeof(power_val), + &power_val); + if (ret) { + hid_err(sensor_inst->hsdev->hdev, + "Set power state failed\n"); + return ret; + } + } + + if (report_val >= 0) { + report_val += + sensor_inst->report_state->attribute.logical_minimum; + ret = sensor_hub_set_feature(sensor_inst->hsdev, + sensor_inst->report_state->attribute.report_id, + sensor_inst->report_state->attribute.index, + sizeof(report_val), + &report_val); + if (ret) { + hid_err(sensor_inst->hsdev->hdev, + "Set report state failed\n"); + return ret; + } + } + + return 0; +} + +static ssize_t enable_sensor_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev); + int value; + int ret = -EINVAL; + + if (kstrtoint(buf, 0, &value) != 0) + return -EINVAL; + + mutex_lock(&sensor_inst->mutex); + if (value && !sensor_inst->enable) { + ret = sensor_hub_device_open(sensor_inst->hsdev); + if (ret) + goto unlock_state; + + ret = set_power_report_state(sensor_inst, true); + if (ret) { + sensor_hub_device_close(sensor_inst->hsdev); + goto unlock_state; + } + sensor_inst->enable = true; + } else if (!value && sensor_inst->enable) { + ret = set_power_report_state(sensor_inst, false); + sensor_hub_device_close(sensor_inst->hsdev); + sensor_inst->enable = false; + } +unlock_state: + mutex_unlock(&sensor_inst->mutex); + if (ret < 0) + return ret; + + return count; +} +static DEVICE_ATTR_RW(enable_sensor); + +static struct attribute *enable_sensor_attrs[] = { + &dev_attr_enable_sensor.attr, + NULL, +}; + +static struct attribute_group enable_sensor_attr_group = { + .attrs = enable_sensor_attrs, +}; + +static ssize_t show_value(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev); + struct hid_sensor_hub_attribute_info *attribute; + int index, usage, field_index; + char name[HID_CUSTOM_NAME_LENGTH]; + bool feature = false; + bool input = false; + int value = 0; + + if (sscanf(attr->attr.name, "feature-%d-%x-%s", &index, &usage, + name) == 3) { + feature = true; + field_index = index + sensor_inst->input_field_count; + } else if (sscanf(attr->attr.name, "input-%d-%x-%s", &index, &usage, + name) == 3) { + input = true; + field_index = index; + } else + return -EINVAL; + + if (!strncmp(name, "value", strlen("value"))) { + u32 report_id; + int ret; + + attribute = &sensor_inst->fields[field_index].attribute; + report_id = attribute->report_id; + if (feature) { + u8 values[HID_CUSTOM_MAX_FEATURE_BYTES]; + int len = 0; + u64 value = 0; + int i = 0; + + ret = sensor_hub_get_feature(sensor_inst->hsdev, + report_id, + index, + sizeof(values), values); + if (ret < 0) + return ret; + + while (i < ret) { + if (i + attribute->size > ret) { + len += snprintf(&buf[len], + PAGE_SIZE - len, + "%d ", values[i]); + break; + } + switch (attribute->size) { + case 2: + value = (u64) *(u16 *)&values[i]; + i += attribute->size; + break; + case 4: + value = (u64) *(u32 *)&values[i]; + i += attribute->size; + break; + case 8: + value = *(u64 *)&values[i]; + i += attribute->size; + break; + default: + value = (u64) values[i]; + ++i; + break; + } + len += snprintf(&buf[len], PAGE_SIZE - len, + "%lld ", value); + } + len += snprintf(&buf[len], PAGE_SIZE - len, "\n"); + + return len; + } else if (input) + value = sensor_hub_input_attr_get_raw_value( + sensor_inst->hsdev, + sensor_inst->hsdev->usage, + usage, report_id, + SENSOR_HUB_SYNC); + } else if (!strncmp(name, "units", strlen("units"))) + value = sensor_inst->fields[field_index].attribute.units; + else if (!strncmp(name, "unit-expo", strlen("unit-expo"))) + value = sensor_inst->fields[field_index].attribute.unit_expo; + else if (!strncmp(name, "size", strlen("size"))) + value = sensor_inst->fields[field_index].attribute.size; + else if (!strncmp(name, "minimum", strlen("minimum"))) + value = sensor_inst->fields[field_index].attribute. + logical_minimum; + else if (!strncmp(name, "maximum", strlen("maximum"))) + value = sensor_inst->fields[field_index].attribute. + logical_maximum; + else if (!strncmp(name, "name", strlen("name"))) { + struct hid_custom_usage_desc *usage_desc; + + usage_desc = bsearch(&usage, hid_custom_usage_desc_table, + ARRAY_SIZE(hid_custom_usage_desc_table), + sizeof(struct hid_custom_usage_desc), + usage_id_cmp); + if (usage_desc) + return snprintf(buf, PAGE_SIZE, "%s\n", + usage_desc->desc); + else + return sprintf(buf, "not-specified\n"); + } else + return -EINVAL; + + return sprintf(buf, "%d\n", value); +} + +static ssize_t store_value(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev); + int index, field_index, usage; + char name[HID_CUSTOM_NAME_LENGTH]; + int value; + + if (sscanf(attr->attr.name, "feature-%d-%x-%s", &index, &usage, + name) == 3) { + field_index = index + sensor_inst->input_field_count; + } else + return -EINVAL; + + if (!strncmp(name, "value", strlen("value"))) { + u32 report_id; + int ret; + + if (kstrtoint(buf, 0, &value) != 0) + return -EINVAL; + + report_id = sensor_inst->fields[field_index].attribute. + report_id; + ret = sensor_hub_set_feature(sensor_inst->hsdev, report_id, + index, sizeof(value), &value); + } else + return -EINVAL; + + return count; +} + +static int hid_sensor_capture_sample(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, size_t raw_len, + char *raw_data, void *priv) +{ + struct hid_sensor_custom *sensor_inst = platform_get_drvdata(priv); + struct hid_sensor_sample header; + + /* If any error occurs in a sample, rest of the fields are ignored */ + if (sensor_inst->input_skip_sample) { + hid_err(sensor_inst->hsdev->hdev, "Skipped remaining data\n"); + return 0; + } + + hid_dbg(sensor_inst->hsdev->hdev, "%s received %d of %d\n", __func__, + (int) (sensor_inst->input_report_recd_size + raw_len), + sensor_inst->input_report_size); + + if (!test_bit(0, &sensor_inst->misc_opened)) + return 0; + + if (!sensor_inst->input_report_recd_size) { + int required_size = sizeof(struct hid_sensor_sample) + + sensor_inst->input_report_size; + header.usage_id = hsdev->usage; + header.raw_len = sensor_inst->input_report_size; + header.timestamp = ktime_get_real_ns(); + if (kfifo_avail(&sensor_inst->data_fifo) >= required_size) { + kfifo_in(&sensor_inst->data_fifo, + (unsigned char *)&header, + sizeof(header)); + } else + sensor_inst->input_skip_sample = true; + } + if (kfifo_avail(&sensor_inst->data_fifo) >= raw_len) + kfifo_in(&sensor_inst->data_fifo, (unsigned char *)raw_data, + raw_len); + + sensor_inst->input_report_recd_size += raw_len; + + return 0; +} + +static int hid_sensor_send_event(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, void *priv) +{ + struct hid_sensor_custom *sensor_inst = platform_get_drvdata(priv); + + if (!test_bit(0, &sensor_inst->misc_opened)) + return 0; + + sensor_inst->input_report_recd_size = 0; + sensor_inst->input_skip_sample = false; + + wake_up(&sensor_inst->wait); + + return 0; +} + +static int hid_sensor_custom_add_field(struct hid_sensor_custom *sensor_inst, + int index, int report_type, + struct hid_report *report, + struct hid_field *field) +{ + struct hid_sensor_custom_field *sensor_field; + void *fields; + + fields = krealloc(sensor_inst->fields, + (sensor_inst->sensor_field_count + 1) * + sizeof(struct hid_sensor_custom_field), GFP_KERNEL); + if (!fields) { + kfree(sensor_inst->fields); + return -ENOMEM; + } + sensor_inst->fields = fields; + sensor_field = &sensor_inst->fields[sensor_inst->sensor_field_count]; + sensor_field->attribute.usage_id = sensor_inst->hsdev->usage; + if (field->logical) + sensor_field->attribute.attrib_id = field->logical; + else + sensor_field->attribute.attrib_id = field->usage[0].hid; + + sensor_field->attribute.index = index; + sensor_field->attribute.report_id = report->id; + sensor_field->attribute.units = field->unit; + sensor_field->attribute.unit_expo = field->unit_exponent; + sensor_field->attribute.size = (field->report_size / 8); + sensor_field->attribute.logical_minimum = field->logical_minimum; + sensor_field->attribute.logical_maximum = field->logical_maximum; + + if (report_type == HID_FEATURE_REPORT) + snprintf(sensor_field->group_name, + sizeof(sensor_field->group_name), "feature-%x-%x", + sensor_field->attribute.index, + sensor_field->attribute.attrib_id); + else if (report_type == HID_INPUT_REPORT) { + snprintf(sensor_field->group_name, + sizeof(sensor_field->group_name), + "input-%x-%x", sensor_field->attribute.index, + sensor_field->attribute.attrib_id); + sensor_inst->input_field_count++; + sensor_inst->input_report_size += (field->report_size * + field->report_count) / 8; + } + + memset(&sensor_field->hid_custom_attribute_group, 0, + sizeof(struct attribute_group)); + sensor_inst->sensor_field_count++; + + return 0; +} + +static int hid_sensor_custom_add_fields(struct hid_sensor_custom *sensor_inst, + struct hid_report_enum *report_enum, + int report_type) +{ + int i; + int ret; + struct hid_report *report; + struct hid_field *field; + struct hid_sensor_hub_device *hsdev = sensor_inst->hsdev; + + list_for_each_entry(report, &report_enum->report_list, list) { + for (i = 0; i < report->maxfield; ++i) { + field = report->field[i]; + if (field->maxusage && + ((field->usage[0].collection_index >= + hsdev->start_collection_index) && + (field->usage[0].collection_index < + hsdev->end_collection_index))) { + + ret = hid_sensor_custom_add_field(sensor_inst, + i, + report_type, + report, + field); + if (ret) + return ret; + + } + } + } + + return 0; +} + +static int hid_sensor_custom_add_attributes(struct hid_sensor_custom + *sensor_inst) +{ + struct hid_sensor_hub_device *hsdev = sensor_inst->hsdev; + struct hid_device *hdev = hsdev->hdev; + int ret = -1; + int i, j; + + for (j = 0; j < HID_REPORT_TYPES; ++j) { + if (j == HID_OUTPUT_REPORT) + continue; + + ret = hid_sensor_custom_add_fields(sensor_inst, + &hdev->report_enum[j], j); + if (ret) + return ret; + + } + + /* Create sysfs attributes */ + for (i = 0; i < sensor_inst->sensor_field_count; ++i) { + j = 0; + while (j < HID_CUSTOM_TOTAL_ATTRS && + hid_custom_attrs[j].name) { + struct device_attribute *device_attr; + + device_attr = &sensor_inst->fields[i].sd_attrs[j]; + + snprintf((char *)&sensor_inst->fields[i].attr_name[j], + HID_CUSTOM_NAME_LENGTH, "%s-%s", + sensor_inst->fields[i].group_name, + hid_custom_attrs[j].name); + sysfs_attr_init(&device_attr->attr); + device_attr->attr.name = + (char *)&sensor_inst->fields[i].attr_name[j]; + device_attr->attr.mode = hid_custom_attrs[j].mode; + device_attr->show = show_value; + if (hid_custom_attrs[j].mode & S_IWUSR) + device_attr->store = store_value; + sensor_inst->fields[i].attrs[j] = &device_attr->attr; + ++j; + } + sensor_inst->fields[i].attrs[j] = NULL; + sensor_inst->fields[i].hid_custom_attribute_group.attrs = + sensor_inst->fields[i].attrs; + sensor_inst->fields[i].hid_custom_attribute_group.name = + sensor_inst->fields[i].group_name; + ret = sysfs_create_group(&sensor_inst->pdev->dev.kobj, + &sensor_inst->fields[i]. + hid_custom_attribute_group); + if (ret) + break; + + /* For power or report field store indexes */ + if (sensor_inst->fields[i].attribute.attrib_id == + HID_USAGE_SENSOR_PROY_POWER_STATE) + sensor_inst->power_state = &sensor_inst->fields[i]; + else if (sensor_inst->fields[i].attribute.attrib_id == + HID_USAGE_SENSOR_PROP_REPORT_STATE) + sensor_inst->report_state = &sensor_inst->fields[i]; + } + + return ret; +} + +static void hid_sensor_custom_remove_attributes(struct hid_sensor_custom * + sensor_inst) +{ + int i; + + for (i = 0; i < sensor_inst->sensor_field_count; ++i) + sysfs_remove_group(&sensor_inst->pdev->dev.kobj, + &sensor_inst->fields[i]. + hid_custom_attribute_group); + + kfree(sensor_inst->fields); +} + +static ssize_t hid_sensor_custom_read(struct file *file, char __user *buf, + size_t count, loff_t *f_ps) +{ + struct hid_sensor_custom *sensor_inst; + unsigned int copied; + int ret; + + sensor_inst = container_of(file->private_data, + struct hid_sensor_custom, custom_dev); + + if (count < sizeof(struct hid_sensor_sample)) + return -EINVAL; + + do { + if (kfifo_is_empty(&sensor_inst->data_fifo)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(sensor_inst->wait, + !kfifo_is_empty(&sensor_inst->data_fifo)); + if (ret) + return ret; + } + ret = kfifo_to_user(&sensor_inst->data_fifo, buf, count, + &copied); + if (ret) + return ret; + + } while (copied == 0); + + return copied; +} + +static int hid_sensor_custom_release(struct inode *inode, struct file *file) +{ + struct hid_sensor_custom *sensor_inst; + + sensor_inst = container_of(file->private_data, + struct hid_sensor_custom, custom_dev); + + clear_bit(0, &sensor_inst->misc_opened); + + return 0; +} + +static int hid_sensor_custom_open(struct inode *inode, struct file *file) +{ + struct hid_sensor_custom *sensor_inst; + + sensor_inst = container_of(file->private_data, + struct hid_sensor_custom, custom_dev); + /* We essentially have single reader and writer */ + if (test_and_set_bit(0, &sensor_inst->misc_opened)) + return -EBUSY; + + return nonseekable_open(inode, file); +} + +static unsigned int hid_sensor_custom_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct hid_sensor_custom *sensor_inst; + unsigned int mask = 0; + + sensor_inst = container_of(file->private_data, + struct hid_sensor_custom, custom_dev); + + poll_wait(file, &sensor_inst->wait, wait); + + if (!kfifo_is_empty(&sensor_inst->data_fifo)) + mask = POLLIN | POLLRDNORM; + + return mask; +} + +static const struct file_operations hid_sensor_custom_fops = { + .open = hid_sensor_custom_open, + .read = hid_sensor_custom_read, + .release = hid_sensor_custom_release, + .poll = hid_sensor_custom_poll, + .llseek = noop_llseek, +}; + +static int hid_sensor_custom_dev_if_add(struct hid_sensor_custom *sensor_inst) +{ + int ret; + + ret = kfifo_alloc(&sensor_inst->data_fifo, HID_CUSTOM_FIFO_SIZE, + GFP_KERNEL); + if (ret) + return ret; + + init_waitqueue_head(&sensor_inst->wait); + + sensor_inst->custom_dev.minor = MISC_DYNAMIC_MINOR; + sensor_inst->custom_dev.name = dev_name(&sensor_inst->pdev->dev); + sensor_inst->custom_dev.fops = &hid_sensor_custom_fops, + ret = misc_register(&sensor_inst->custom_dev); + if (ret) { + kfifo_free(&sensor_inst->data_fifo); + return ret; + } + return 0; +} + +static void hid_sensor_custom_dev_if_remove(struct hid_sensor_custom + *sensor_inst) +{ + wake_up(&sensor_inst->wait); + misc_deregister(&sensor_inst->custom_dev); + kfifo_free(&sensor_inst->data_fifo); + +} + +static int hid_sensor_custom_probe(struct platform_device *pdev) +{ + struct hid_sensor_custom *sensor_inst; + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + int ret; + + sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst), + GFP_KERNEL); + if (!sensor_inst) + return -ENOMEM; + + sensor_inst->callbacks.capture_sample = hid_sensor_capture_sample; + sensor_inst->callbacks.send_event = hid_sensor_send_event; + sensor_inst->callbacks.pdev = pdev; + sensor_inst->hsdev = hsdev; + sensor_inst->pdev = pdev; + mutex_init(&sensor_inst->mutex); + platform_set_drvdata(pdev, sensor_inst); + ret = sensor_hub_register_callback(hsdev, hsdev->usage, + &sensor_inst->callbacks); + if (ret < 0) { + dev_err(&pdev->dev, "callback reg failed\n"); + return ret; + } + + ret = sysfs_create_group(&sensor_inst->pdev->dev.kobj, + &enable_sensor_attr_group); + if (ret) + goto err_remove_callback; + + ret = hid_sensor_custom_add_attributes(sensor_inst); + if (ret) + goto err_remove_group; + + ret = hid_sensor_custom_dev_if_add(sensor_inst); + if (ret) + goto err_remove_attributes; + + return 0; + +err_remove_attributes: + hid_sensor_custom_remove_attributes(sensor_inst); +err_remove_group: + sysfs_remove_group(&sensor_inst->pdev->dev.kobj, + &enable_sensor_attr_group); +err_remove_callback: + sensor_hub_remove_callback(hsdev, hsdev->usage); + + return ret; +} + +static int hid_sensor_custom_remove(struct platform_device *pdev) +{ + struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev); + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + + hid_sensor_custom_dev_if_remove(sensor_inst); + hid_sensor_custom_remove_attributes(sensor_inst); + sysfs_remove_group(&sensor_inst->pdev->dev.kobj, + &enable_sensor_attr_group); + sensor_hub_remove_callback(hsdev, hsdev->usage); + + return 0; +} + +static struct platform_device_id hid_sensor_custom_ids[] = { + { + .name = "HID-SENSOR-2000e1", + }, + { + .name = "HID-SENSOR-2000e2", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, hid_sensor_custom_ids); + +static struct platform_driver hid_sensor_custom_platform_driver = { + .id_table = hid_sensor_custom_ids, + .driver = { + .name = KBUILD_MODNAME, + }, + .probe = hid_sensor_custom_probe, + .remove = hid_sensor_custom_remove, +}; +module_platform_driver(hid_sensor_custom_platform_driver); + +MODULE_DESCRIPTION("HID Sensor Custom and Generic sensor Driver"); +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index e54ce1097e2cc5..c3f6f1e311ea0d 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -28,30 +28,11 @@ #define HID_SENSOR_HUB_ENUM_QUIRK 0x01 -/** - * struct sensor_hub_pending - Synchronous read pending information - * @status: Pending status true/false. - * @ready: Completion synchronization data. - * @usage_id: Usage id for physical device, E.g. Gyro usage id. - * @attr_usage_id: Usage Id of a field, E.g. X-AXIS for a gyro. - * @raw_size: Response size for a read request. - * @raw_data: Place holder for received response. - */ -struct sensor_hub_pending { - bool status; - struct completion ready; - u32 usage_id; - u32 attr_usage_id; - int raw_size; - u8 *raw_data; -}; - /** * struct sensor_hub_data - Hold a instance data for a HID hub device * @hsdev: Stored hid instance for current hub device. * @mutex: Mutex to serialize synchronous request. * @lock: Spin lock to protect pending request structure. - * @pending: Holds information of pending sync read request. * @dyn_callback_list: Holds callback function * @dyn_callback_lock: spin lock to protect callback list * @hid_sensor_hub_client_devs: Stores all MFD cells for a hub instance. @@ -61,7 +42,6 @@ struct sensor_hub_pending { struct sensor_hub_data { struct mutex mutex; spinlock_t lock; - struct sensor_hub_pending pending; struct list_head dyn_callback_list; spinlock_t dyn_callback_lock; struct mfd_cell *hid_sensor_hub_client_devs; @@ -106,7 +86,8 @@ static int sensor_hub_get_physical_device_count(struct hid_device *hdev) for (i = 0; i < hdev->maxcollection; ++i) { struct hid_collection *collection = &hdev->collection[i]; - if (collection->type == HID_COLLECTION_PHYSICAL) + if (collection->type == HID_COLLECTION_PHYSICAL || + collection->type == HID_COLLECTION_APPLICATION) ++count; } @@ -139,7 +120,8 @@ static struct hid_sensor_hub_callbacks *sensor_hub_get_callback( spin_lock_irqsave(&pdata->dyn_callback_lock, flags); list_for_each_entry(callback, &pdata->dyn_callback_list, list) - if (callback->usage_id == usage_id && + if ((callback->usage_id == usage_id || + callback->usage_id == HID_USAGE_SENSOR_COLLECTION) && (collection_index >= callback->hsdev->start_collection_index) && (collection_index < @@ -179,7 +161,18 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev, callback->usage_callback = usage_callback; callback->usage_id = usage_id; callback->priv = NULL; - list_add_tail(&callback->list, &pdata->dyn_callback_list); + /* + * If there is a handler registered for the collection type, then + * it will handle all reports for sensors in this collection. If + * there is also an individual sensor handler registration, then + * we want to make sure that the reports are directed to collection + * handler, as this may be a fusion sensor. So add collection handlers + * to the beginning of the list, so that they are matched first. + */ + if (usage_id == HID_USAGE_SENSOR_COLLECTION) + list_add(&callback->list, &pdata->dyn_callback_list); + else + list_add_tail(&callback->list, &pdata->dyn_callback_list); spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags); return 0; @@ -208,10 +201,14 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev, EXPORT_SYMBOL_GPL(sensor_hub_remove_callback); int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, - u32 field_index, s32 value) + u32 field_index, int buffer_size, void *buffer) { struct hid_report *report; struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + __s32 *buf32 = buffer; + int i = 0; + int remaining_bytes; + __s32 value; int ret = 0; mutex_lock(&data->mutex); @@ -220,7 +217,21 @@ int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, ret = -EINVAL; goto done_proc; } - hid_set_field(report->field[field_index], 0, value); + + remaining_bytes = do_div(buffer_size, sizeof(__s32)); + if (buffer_size) { + for (i = 0; i < buffer_size; ++i) { + hid_set_field(report->field[field_index], i, + (__force __s32)cpu_to_le32(*buf32)); + ++buf32; + } + } + if (remaining_bytes) { + value = 0; + memcpy(&value, (u8 *)buf32, remaining_bytes); + hid_set_field(report->field[field_index], i, + (__force __s32)cpu_to_le32(value)); + } hid_hw_request(hsdev->hdev, report, HID_REQ_SET_REPORT); hid_hw_wait(hsdev->hdev); @@ -232,10 +243,11 @@ int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, EXPORT_SYMBOL_GPL(sensor_hub_set_feature); int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, - u32 field_index, s32 *value) + u32 field_index, int buffer_size, void *buffer) { struct hid_report *report; struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); + int report_size; int ret = 0; mutex_lock(&data->mutex); @@ -247,7 +259,17 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, } hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); hid_hw_wait(hsdev->hdev); - *value = report->field[field_index]->value[0]; + + /* calculate number of bytes required to read this field */ + report_size = DIV_ROUND_UP(report->field[field_index]->report_size, + 8) * + report->field[field_index]->report_count; + if (!report_size) { + ret = -EINVAL; + goto done_proc; + } + ret = min(report_size, buffer_size); + memcpy(buffer, report->field[field_index]->value, ret); done_proc: mutex_unlock(&data->mutex); @@ -259,47 +281,54 @@ EXPORT_SYMBOL_GPL(sensor_hub_get_feature); int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, u32 usage_id, - u32 attr_usage_id, u32 report_id) + u32 attr_usage_id, u32 report_id, + enum sensor_hub_read_flags flag) { struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev); unsigned long flags; struct hid_report *report; int ret_val = 0; - mutex_lock(&data->mutex); - memset(&data->pending, 0, sizeof(data->pending)); - init_completion(&data->pending.ready); - data->pending.usage_id = usage_id; - data->pending.attr_usage_id = attr_usage_id; - data->pending.raw_size = 0; - - spin_lock_irqsave(&data->lock, flags); - data->pending.status = true; - spin_unlock_irqrestore(&data->lock, flags); - report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT); + report = sensor_hub_report(report_id, hsdev->hdev, + HID_INPUT_REPORT); if (!report) - goto err_free; - - hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); - wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5); - switch (data->pending.raw_size) { - case 1: - ret_val = *(u8 *)data->pending.raw_data; - break; - case 2: - ret_val = *(u16 *)data->pending.raw_data; - break; - case 4: - ret_val = *(u32 *)data->pending.raw_data; - break; - default: - ret_val = 0; + return -EINVAL; + + mutex_lock(&hsdev->mutex); + if (flag == SENSOR_HUB_SYNC) { + memset(&hsdev->pending, 0, sizeof(hsdev->pending)); + init_completion(&hsdev->pending.ready); + hsdev->pending.usage_id = usage_id; + hsdev->pending.attr_usage_id = attr_usage_id; + hsdev->pending.raw_size = 0; + + spin_lock_irqsave(&data->lock, flags); + hsdev->pending.status = true; + spin_unlock_irqrestore(&data->lock, flags); } - kfree(data->pending.raw_data); - -err_free: - data->pending.status = false; + mutex_lock(&data->mutex); + hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); mutex_unlock(&data->mutex); + if (flag == SENSOR_HUB_SYNC) { + wait_for_completion_interruptible_timeout( + &hsdev->pending.ready, HZ*5); + switch (hsdev->pending.raw_size) { + case 1: + ret_val = *(u8 *)hsdev->pending.raw_data; + break; + case 2: + ret_val = *(u16 *)hsdev->pending.raw_data; + break; + case 4: + ret_val = *(u32 *)hsdev->pending.raw_data; + break; + default: + ret_val = 0; + } + kfree(hsdev->pending.raw_data); + hsdev->pending.status = false; + } + mutex_unlock(&hsdev->mutex); return ret_val; } @@ -455,16 +484,6 @@ static int sensor_hub_raw_event(struct hid_device *hdev, report->field[i]->report_count)/8); sz = (report->field[i]->report_size * report->field[i]->report_count)/8; - if (pdata->pending.status && pdata->pending.attr_usage_id == - report->field[i]->usage->hid) { - hid_dbg(hdev, "data was pending ...\n"); - pdata->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC); - if (pdata->pending.raw_data) - pdata->pending.raw_size = sz; - else - pdata->pending.raw_size = 0; - complete(&pdata->pending.ready); - } collection = &hdev->collection[ report->field[i]->usage->collection_index]; hid_dbg(hdev, "collection->usage %x\n", @@ -474,8 +493,23 @@ static int sensor_hub_raw_event(struct hid_device *hdev, report->field[i]->physical, report->field[i]->usage[0].collection_index, &hsdev, &priv); - - if (callback && callback->capture_sample) { + if (!callback) { + ptr += sz; + continue; + } + if (hsdev->pending.status && (hsdev->pending.attr_usage_id == + report->field[i]->usage->hid || + hsdev->pending.attr_usage_id == + report->field[i]->logical)) { + hid_dbg(hdev, "data was pending ...\n"); + hsdev->pending.raw_data = kmemdup(ptr, sz, GFP_ATOMIC); + if (hsdev->pending.raw_data) + hsdev->pending.raw_size = sz; + else + hsdev->pending.raw_size = 0; + complete(&hsdev->pending.ready); + } + if (callback->capture_sample) { if (report->field[i]->logical) callback->capture_sample(hsdev, report->field[i]->logical, sz, ptr, @@ -572,6 +606,7 @@ static int sensor_hub_probe(struct hid_device *hdev, int dev_cnt; struct hid_sensor_hub_device *hsdev; struct hid_sensor_hub_device *last_hsdev = NULL; + struct hid_sensor_hub_device *collection_hsdev = NULL; sd = devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL); if (!sd) { @@ -618,7 +653,8 @@ static int sensor_hub_probe(struct hid_device *hdev, for (i = 0; i < hdev->maxcollection; ++i) { struct hid_collection *collection = &hdev->collection[i]; - if (collection->type == HID_COLLECTION_PHYSICAL) { + if (collection->type == HID_COLLECTION_PHYSICAL || + collection->type == HID_COLLECTION_APPLICATION) { hsdev = devm_kzalloc(&hdev->dev, sizeof(*hsdev), GFP_KERNEL); @@ -630,6 +666,8 @@ static int sensor_hub_probe(struct hid_device *hdev, hsdev->hdev = hdev; hsdev->vendor_id = hdev->vendor; hsdev->product_id = hdev->product; + hsdev->usage = collection->usage; + mutex_init(&hsdev->mutex); hsdev->start_collection_index = i; if (last_hsdev) last_hsdev->end_collection_index = i; @@ -653,10 +691,17 @@ static int sensor_hub_probe(struct hid_device *hdev, hid_dbg(hdev, "Adding %s:%d\n", name, hsdev->start_collection_index); sd->hid_sensor_client_cnt++; + if (collection_hsdev) + collection_hsdev->end_collection_index = i; + if (collection->type == HID_COLLECTION_APPLICATION && + collection->usage == HID_USAGE_SENSOR_COLLECTION) + collection_hsdev = hsdev; } } if (last_hsdev) last_hsdev->end_collection_index = i; + if (collection_hsdev) + collection_hsdev->end_collection_index = i; ret = mfd_add_hotplug_devices(&hdev->dev, sd->hid_sensor_hub_client_devs, @@ -676,13 +721,18 @@ static void sensor_hub_remove(struct hid_device *hdev) { struct sensor_hub_data *data = hid_get_drvdata(hdev); unsigned long flags; + int i; hid_dbg(hdev, " hardware removed\n"); hid_hw_close(hdev); hid_hw_stop(hdev); spin_lock_irqsave(&data->lock, flags); - if (data->pending.status) - complete(&data->pending.ready); + for (i = 0; i < data->hid_sensor_client_cnt; ++i) { + struct hid_sensor_hub_device *hsdev = + data->hid_sensor_hub_client_devs[i].platform_data; + if (hsdev->pending.status) + complete(&hsdev->pending.ready); + } spin_unlock_irqrestore(&data->lock, flags); mfd_remove_devices(&hdev->dev); hid_set_drvdata(hdev, NULL); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index c906300cf667da..6ca96cebb44ce1 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -802,7 +802,8 @@ union sixaxis_output_report_01 { #define DS4_REPORT_0x05_SIZE 32 #define DS4_REPORT_0x11_SIZE 78 #define DS4_REPORT_0x81_SIZE 7 -#define SIXAXIS_REPORT_0xF2_SIZE 18 +#define SIXAXIS_REPORT_0xF2_SIZE 17 +#define SIXAXIS_REPORT_0xF5_SIZE 8 static DEFINE_SPINLOCK(sony_dev_list_lock); static LIST_HEAD(sony_device_list); @@ -1131,18 +1132,38 @@ static void sony_input_configured(struct hid_device *hdev, */ static int sixaxis_set_operational_usb(struct hid_device *hdev) { + const int buf_size = + max(SIXAXIS_REPORT_0xF2_SIZE, SIXAXIS_REPORT_0xF5_SIZE); + __u8 *buf; int ret; - char *buf = kmalloc(18, GFP_KERNEL); + buf = kmalloc(buf_size, GFP_KERNEL); if (!buf) return -ENOMEM; - ret = hid_hw_raw_request(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT, - HID_REQ_GET_REPORT); + ret = hid_hw_raw_request(hdev, 0xf2, buf, SIXAXIS_REPORT_0xF2_SIZE, + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret < 0) { + hid_err(hdev, "can't set operational mode: step 1\n"); + goto out; + } + + /* + * Some compatible controllers like the Speedlink Strike FX and + * Gasia need another query plus an USB interrupt to get operational. + */ + ret = hid_hw_raw_request(hdev, 0xf5, buf, SIXAXIS_REPORT_0xF5_SIZE, + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret < 0) { + hid_err(hdev, "can't set operational mode: step 2\n"); + goto out; + } + ret = hid_hw_output_report(hdev, buf, 1); if (ret < 0) - hid_err(hdev, "can't set operational mode\n"); + hid_err(hdev, "can't set operational mode: step 3\n"); +out: kfree(buf); return ret; diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index 29f328f411fb54..3edd4ac364942b 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -12,7 +12,6 @@ */ #include -#include #include #include diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c index fb8b516ff0ed5f..94167310e15a4c 100644 --- a/drivers/hid/hid-uclogic.c +++ b/drivers/hid/hid-uclogic.c @@ -1,7 +1,8 @@ /* * HID driver for UC-Logic devices not fully compliant with HID standard * - * Copyright (c) 2010 Nikolai Kondrashov + * Copyright (c) 2010-2014 Nikolai Kondrashov + * Copyright (c) 2013 Martin Rusko */ /* @@ -15,6 +16,8 @@ #include #include #include +#include +#include "usbhid/usbhid.h" #include "hid-ids.h" @@ -546,11 +549,93 @@ static __u8 twha60_rdesc_fixed1[] = { 0xC0 /* End Collection */ }; +/* Report descriptor template placeholder head */ +#define UCLOGIC_PH_HEAD 0xFE, 0xED, 0x1D + +/* Report descriptor template placeholder IDs */ +enum uclogic_ph_id { + UCLOGIC_PH_ID_X_LM, + UCLOGIC_PH_ID_X_PM, + UCLOGIC_PH_ID_Y_LM, + UCLOGIC_PH_ID_Y_PM, + UCLOGIC_PH_ID_PRESSURE_LM, + UCLOGIC_PH_ID_NUM +}; + +/* Report descriptor template placeholder */ +#define UCLOGIC_PH(_ID) UCLOGIC_PH_HEAD, UCLOGIC_PH_ID_##_ID +#define UCLOGIC_PEN_REPORT_ID 0x07 + +/* Fixed report descriptor template */ +static const __u8 uclogic_tablet_rdesc_template[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x07, /* Report ID (7), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0xA4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x55, 0xFD, /* Unit Exponent (-3), */ + 0x34, /* Physical Minimum (0), */ + 0x09, 0x30, /* Usage (X), */ + 0x27, UCLOGIC_PH(X_LM), /* Logical Maximum (PLACEHOLDER), */ + 0x47, UCLOGIC_PH(X_PM), /* Physical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x27, UCLOGIC_PH(Y_LM), /* Logical Maximum (PLACEHOLDER), */ + 0x47, UCLOGIC_PH(Y_PM), /* Physical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x27, + UCLOGIC_PH(PRESSURE_LM),/* Logical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +/* Parameter indices */ +enum uclogic_prm { + UCLOGIC_PRM_X_LM = 1, + UCLOGIC_PRM_Y_LM = 2, + UCLOGIC_PRM_PRESSURE_LM = 4, + UCLOGIC_PRM_RESOLUTION = 5, + UCLOGIC_PRM_NUM +}; + +/* Driver data */ +struct uclogic_drvdata { + __u8 *rdesc; + unsigned int rsize; + bool invert_pen_inrange; + bool ignore_pen_usage; +}; + static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { struct usb_interface *iface = to_usb_interface(hdev->dev.parent); __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; + struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); switch (hdev->product) { case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209: @@ -621,11 +706,241 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, break; } break; + default: + if (drvdata->rdesc != NULL) { + rdesc = drvdata->rdesc; + *rsize = drvdata->rsize; + } } return rdesc; } +static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + + /* discard the unused pen interface */ + if ((drvdata->ignore_pen_usage) && + (field->application == HID_DG_PEN)) + return -1; + + /* let hid-core decide what to do */ + return 0; +} + +static void uclogic_input_configured(struct hid_device *hdev, + struct hid_input *hi) +{ + char *name; + const char *suffix = NULL; + struct hid_field *field; + size_t len; + + /* no report associated (HID_QUIRK_MULTI_INPUT not set) */ + if (!hi->report) + return; + + field = hi->report->field[0]; + + switch (field->application) { + case HID_GD_KEYBOARD: + suffix = "Keyboard"; + break; + case HID_GD_MOUSE: + suffix = "Mouse"; + break; + case HID_GD_KEYPAD: + suffix = "Pad"; + break; + case HID_DG_PEN: + suffix = "Pen"; + break; + case HID_CP_CONSUMER_CONTROL: + suffix = "Consumer Control"; + break; + case HID_GD_SYSTEM_CONTROL: + suffix = "System Control"; + break; + } + + if (suffix) { + len = strlen(hdev->name) + 2 + strlen(suffix); + name = devm_kzalloc(&hi->input->dev, len, GFP_KERNEL); + if (name) { + snprintf(name, len, "%s %s", hdev->name, suffix); + hi->input->name = name; + } + } +} + +/** + * Enable fully-functional tablet mode and determine device parameters. + * + * @hdev: HID device + */ +static int uclogic_tablet_enable(struct hid_device *hdev) +{ + int rc; + struct usb_device *usb_dev = hid_to_usb_dev(hdev); + struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + __le16 *buf = NULL; + size_t len; + s32 params[UCLOGIC_PH_ID_NUM]; + s32 resolution; + __u8 *p; + s32 v; + + /* + * Read string descriptor containing tablet parameters. The specific + * string descriptor and data were discovered by sniffing the Windows + * driver traffic. + * NOTE: This enables fully-functional tablet mode. + */ + len = UCLOGIC_PRM_NUM * sizeof(*buf); + buf = kmalloc(len, GFP_KERNEL); + if (buf == NULL) { + hid_err(hdev, "failed to allocate parameter buffer\n"); + rc = -ENOMEM; + goto cleanup; + } + rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (USB_DT_STRING << 8) + 0x64, + 0x0409, buf, len, + USB_CTRL_GET_TIMEOUT); + if (rc == -EPIPE) { + hid_err(hdev, "device parameters not found\n"); + rc = -ENODEV; + goto cleanup; + } else if (rc < 0) { + hid_err(hdev, "failed to get device parameters: %d\n", rc); + rc = -ENODEV; + goto cleanup; + } else if (rc != len) { + hid_err(hdev, "invalid device parameters\n"); + rc = -ENODEV; + goto cleanup; + } + + /* Extract device parameters */ + params[UCLOGIC_PH_ID_X_LM] = le16_to_cpu(buf[UCLOGIC_PRM_X_LM]); + params[UCLOGIC_PH_ID_Y_LM] = le16_to_cpu(buf[UCLOGIC_PRM_Y_LM]); + params[UCLOGIC_PH_ID_PRESSURE_LM] = + le16_to_cpu(buf[UCLOGIC_PRM_PRESSURE_LM]); + resolution = le16_to_cpu(buf[UCLOGIC_PRM_RESOLUTION]); + if (resolution == 0) { + params[UCLOGIC_PH_ID_X_PM] = 0; + params[UCLOGIC_PH_ID_Y_PM] = 0; + } else { + params[UCLOGIC_PH_ID_X_PM] = params[UCLOGIC_PH_ID_X_LM] * + 1000 / resolution; + params[UCLOGIC_PH_ID_Y_PM] = params[UCLOGIC_PH_ID_Y_LM] * + 1000 / resolution; + } + + /* Allocate fixed report descriptor */ + drvdata->rdesc = devm_kzalloc(&hdev->dev, + sizeof(uclogic_tablet_rdesc_template), + GFP_KERNEL); + if (drvdata->rdesc == NULL) { + hid_err(hdev, "failed to allocate fixed rdesc\n"); + rc = -ENOMEM; + goto cleanup; + } + drvdata->rsize = sizeof(uclogic_tablet_rdesc_template); + + /* Format fixed report descriptor */ + memcpy(drvdata->rdesc, uclogic_tablet_rdesc_template, + drvdata->rsize); + for (p = drvdata->rdesc; + p <= drvdata->rdesc + drvdata->rsize - 4;) { + if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D && + p[3] < sizeof(params)) { + v = params[p[3]]; + put_unaligned(cpu_to_le32(v), (s32 *)p); + p += 4; + } else { + p++; + } + } + + rc = 0; + +cleanup: + kfree(buf); + return rc; +} + +static int uclogic_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int rc; + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct uclogic_drvdata *drvdata; + + /* + * libinput requires the pad interface to be on a different node + * than the pen, so use QUIRK_MULTI_INPUT for all tablets. + */ + hdev->quirks |= HID_QUIRK_MULTI_INPUT; + hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT; + + /* Allocate and assign driver data */ + drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (drvdata == NULL) + return -ENOMEM; + + hid_set_drvdata(hdev, drvdata); + + switch (id->product) { + case USB_DEVICE_ID_HUION_TABLET: + /* If this is the pen interface */ + if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { + rc = uclogic_tablet_enable(hdev); + if (rc) { + hid_err(hdev, "tablet enabling failed\n"); + return rc; + } + drvdata->invert_pen_inrange = true; + } else { + drvdata->ignore_pen_usage = true; + } + break; + } + + rc = hid_parse(hdev); + if (rc) { + hid_err(hdev, "parse failed\n"); + return rc; + } + + rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (rc) { + hid_err(hdev, "hw start failed\n"); + return rc; + } + + return 0; +} + +static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + + if ((drvdata->invert_pen_inrange) && + (report->type == HID_INPUT_REPORT) && + (report->id == UCLOGIC_PEN_REPORT_ID) && + (size >= 2)) + /* Invert the in-range bit */ + data[1] ^= 0x40; + + return 0; +} + static const struct hid_device_id uclogic_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) }, @@ -641,6 +956,8 @@ static const struct hid_device_id uclogic_devices[] = { USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, { } }; MODULE_DEVICE_TABLE(hid, uclogic_devices); @@ -648,8 +965,14 @@ MODULE_DEVICE_TABLE(hid, uclogic_devices); static struct hid_driver uclogic_driver = { .name = "uclogic", .id_table = uclogic_devices, + .probe = uclogic_probe, .report_fixup = uclogic_report_fixup, + .raw_event = uclogic_raw_event, + .input_mapping = uclogic_input_mapping, + .input_configured = uclogic_input_configured, }; module_hid_driver(uclogic_driver); +MODULE_AUTHOR("Martin Rusko"); +MODULE_AUTHOR("Nikolai Kondrashov"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 36053f33d6d93e..ab4dd952b6ba65 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -37,6 +37,7 @@ #include #include #include +#include #include @@ -144,6 +145,8 @@ struct i2c_hid { unsigned long flags; /* device flags */ wait_queue_head_t wait; /* For waiting the interrupt */ + struct gpio_desc *desc; + int irq; struct i2c_hid_platform_data pdata; }; @@ -785,16 +788,16 @@ static int i2c_hid_init_irq(struct i2c_client *client) struct i2c_hid *ihid = i2c_get_clientdata(client); int ret; - dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq); + dev_dbg(&client->dev, "Requesting IRQ: %d\n", ihid->irq); - ret = request_threaded_irq(client->irq, NULL, i2c_hid_irq, + ret = request_threaded_irq(ihid->irq, NULL, i2c_hid_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, ihid); if (ret < 0) { dev_warn(&client->dev, "Could not register for %s interrupt, irq = %d," " ret = %d\n", - client->name, client->irq, ret); + client->name, ihid->irq, ret); return ret; } @@ -841,6 +844,14 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) } #ifdef CONFIG_ACPI + +/* Default GPIO mapping */ +static const struct acpi_gpio_params i2c_hid_irq_gpio = { 0, 0, true }; +static const struct acpi_gpio_mapping i2c_hid_acpi_gpios[] = { + { "gpios", &i2c_hid_irq_gpio, 1 }, + { }, +}; + static int i2c_hid_acpi_pdata(struct i2c_client *client, struct i2c_hid_platform_data *pdata) { @@ -866,7 +877,7 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client, pdata->hid_descriptor_address = obj->integer.value; ACPI_FREE(obj); - return 0; + return acpi_dev_add_driver_gpios(adev, i2c_hid_acpi_gpios); } static const struct acpi_device_id i2c_hid_acpi_match[] = { @@ -930,12 +941,6 @@ static int i2c_hid_probe(struct i2c_client *client, dbg_hid("HID probe called for i2c 0x%02x\n", client->addr); - if (!client->irq) { - dev_err(&client->dev, - "HID over i2c has not been provided an Int IRQ\n"); - return -EINVAL; - } - ihid = kzalloc(sizeof(struct i2c_hid), GFP_KERNEL); if (!ihid) return -ENOMEM; @@ -955,6 +960,23 @@ static int i2c_hid_probe(struct i2c_client *client, ihid->pdata = *platform_data; } + if (client->irq > 0) { + ihid->irq = client->irq; + } else if (ACPI_COMPANION(&client->dev)) { + ihid->desc = gpiod_get(&client->dev, NULL, GPIOD_IN); + if (IS_ERR(ihid->desc)) { + dev_err(&client->dev, "Failed to get GPIO interrupt\n"); + return PTR_ERR(ihid->desc); + } + + ihid->irq = gpiod_to_irq(ihid->desc); + if (ihid->irq < 0) { + gpiod_put(ihid->desc); + dev_err(&client->dev, "Failed to convert GPIO to IRQ\n"); + return ihid->irq; + } + } + i2c_set_clientdata(client, ihid); ihid->client = client; @@ -1017,13 +1039,16 @@ static int i2c_hid_probe(struct i2c_client *client, hid_destroy_device(hid); err_irq: - free_irq(client->irq, ihid); + free_irq(ihid->irq, ihid); err_pm: pm_runtime_put_noidle(&client->dev); pm_runtime_disable(&client->dev); err: + if (ihid->desc) + gpiod_put(ihid->desc); + i2c_hid_free_buffers(ihid); kfree(ihid); return ret; @@ -1042,13 +1067,18 @@ static int i2c_hid_remove(struct i2c_client *client) hid = ihid->hid; hid_destroy_device(hid); - free_irq(client->irq, ihid); + free_irq(ihid->irq, ihid); if (ihid->bufsize) i2c_hid_free_buffers(ihid); + if (ihid->desc) + gpiod_put(ihid->desc); + kfree(ihid); + acpi_dev_remove_driver_gpios(ACPI_COMPANION(&client->dev)); + return 0; } @@ -1060,9 +1090,9 @@ static int i2c_hid_suspend(struct device *dev) struct hid_device *hid = ihid->hid; int ret = 0; - disable_irq(client->irq); + disable_irq(ihid->irq); if (device_may_wakeup(&client->dev)) - enable_irq_wake(client->irq); + enable_irq_wake(ihid->irq); if (hid->driver && hid->driver->suspend) ret = hid->driver->suspend(hid, PMSG_SUSPEND); @@ -1080,13 +1110,13 @@ static int i2c_hid_resume(struct device *dev) struct i2c_hid *ihid = i2c_get_clientdata(client); struct hid_device *hid = ihid->hid; - enable_irq(client->irq); + enable_irq(ihid->irq); ret = i2c_hid_hwreset(client); if (ret) return ret; if (device_may_wakeup(&client->dev)) - disable_irq_wake(client->irq); + disable_irq_wake(ihid->irq); if (hid->driver && hid->driver->reset_resume) { ret = hid->driver->reset_resume(hid); @@ -1101,17 +1131,19 @@ static int i2c_hid_resume(struct device *dev) static int i2c_hid_runtime_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); + struct i2c_hid *ihid = i2c_get_clientdata(client); i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); - disable_irq(client->irq); + disable_irq(ihid->irq); return 0; } static int i2c_hid_runtime_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); + struct i2c_hid *ihid = i2c_get_clientdata(client); - enable_irq(client->irq); + enable_irq(ihid->irq); i2c_hid_set_power(client, I2C_HID_PWR_ON); return 0; } diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index 0b531c6a76a588..08174d341f4a10 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -568,6 +568,12 @@ static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect, int type_id; int error; + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0; + if (old) { + pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = + pidff->pid_id[effect->id]; + } + switch (effect->type) { case FF_CONSTANT: if (!old) { diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index a8212775346117..a775143e6265e3 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -78,7 +78,13 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A, HID_QUIRK_ALWAYS_POLL }, + { USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A, HID_QUIRK_ALWAYS_POLL }, + { USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C077, HID_QUIRK_ALWAYS_POLL }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C01A, HID_QUIRK_ALWAYS_POLL }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C05A, HID_QUIRK_ALWAYS_POLL }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C06A, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP, HID_QUIRK_NO_INIT_REPORTS }, @@ -92,6 +98,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_MOUSE_4D22, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET }, { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET }, { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET }, @@ -107,12 +114,8 @@ static const struct hid_blacklist { { USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_2, HID_QUIRK_NOGET }, { USB_VENDOR_ID_TPV, USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN, HID_QUIRK_NOGET }, { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209, HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_KNA5, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWA60, HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U, HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET, HID_QUIRK_MULTI_INPUT }, @@ -128,6 +131,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS1, HID_QUIRK_NO_INIT_REPORTS }, diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h index 0d0d0dd89d17cc..024f4d89d5792a 100644 --- a/drivers/hid/wacom.h +++ b/drivers/hid/wacom.h @@ -131,13 +131,6 @@ static inline void wacom_schedule_work(struct wacom_wac *wacom_wac) schedule_work(&wacom->work); } -static inline void wacom_notify_battery(struct wacom_wac *wacom_wac) -{ - struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); - - power_supply_changed(wacom->battery); -} - extern const struct hid_device_id wacom_ids[]; void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); @@ -151,4 +144,5 @@ void wacom_wac_usage_mapping(struct hid_device *hdev, int wacom_wac_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value); void wacom_wac_report(struct hid_device *hdev, struct hid_report *report); +void wacom_battery_work(struct work_struct *work); #endif diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index ba9af470bea06f..e8607d09613846 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -406,6 +406,9 @@ static int wacom_query_tablet_data(struct hid_device *hdev, else if (features->type == WACOM_27QHDT) { return wacom_set_device_mode(hdev, 131, 3, 2); } + else if (features->type == BAMBOO_PAD) { + return wacom_set_device_mode(hdev, 2, 2, 2); + } } else if (features->device_type == BTN_TOOL_PEN) { if (features->type <= BAMBOO_PT && features->type != WIRELESS) { return wacom_set_device_mode(hdev, 2, 2, 2); @@ -524,6 +527,11 @@ static int wacom_add_shared_data(struct hid_device *hdev) wacom_wac->shared = &data->shared; + if (wacom_wac->features.device_type == BTN_TOOL_FINGER) + wacom_wac->shared->touch = hdev; + else if (wacom_wac->features.device_type == BTN_TOOL_PEN) + wacom_wac->shared->pen = hdev; + out: mutex_unlock(&wacom_udev_list_lock); return retval; @@ -541,14 +549,22 @@ static void wacom_release_shared_data(struct kref *kref) kfree(data); } -static void wacom_remove_shared_data(struct wacom_wac *wacom) +static void wacom_remove_shared_data(struct wacom *wacom) { struct wacom_hdev_data *data; + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + + if (wacom_wac->shared) { + data = container_of(wacom_wac->shared, struct wacom_hdev_data, + shared); + + if (wacom_wac->shared->touch == wacom->hdev) + wacom_wac->shared->touch = NULL; + else if (wacom_wac->shared->pen == wacom->hdev) + wacom_wac->shared->pen = NULL; - if (wacom->shared) { - data = container_of(wacom->shared, struct wacom_hdev_data, shared); kref_put(&data->kref, wacom_release_shared_data); - wacom->shared = NULL; + wacom_wac->shared = NULL; } } @@ -929,6 +945,7 @@ static void wacom_destroy_leds(struct wacom *wacom) } static enum power_supply_property wacom_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_CAPACITY @@ -948,6 +965,9 @@ static int wacom_battery_get_property(struct power_supply *psy, int ret = 0; switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = wacom->wacom_wac.bat_connected; + break; case POWER_SUPPLY_PROP_SCOPE: val->intval = POWER_SUPPLY_SCOPE_DEVICE; break; @@ -961,6 +981,8 @@ static int wacom_battery_get_property(struct power_supply *psy, else if (wacom->wacom_wac.battery_capacity == 100 && wacom->wacom_wac.ps_connected) val->intval = POWER_SUPPLY_STATUS_FULL; + else if (wacom->wacom_wac.ps_connected) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; else val->intval = POWER_SUPPLY_STATUS_DISCHARGING; break; @@ -1045,8 +1067,7 @@ static int wacom_initialize_battery(struct wacom *wacom) static void wacom_destroy_battery(struct wacom *wacom) { - if ((wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) && - wacom->battery) { + if (wacom->battery) { power_supply_unregister(wacom->battery); wacom->battery = NULL; power_supply_unregister(wacom->ac); @@ -1317,6 +1338,20 @@ static void wacom_wireless_work(struct work_struct *work) return; } +void wacom_battery_work(struct work_struct *work) +{ + struct wacom *wacom = container_of(work, struct wacom, work); + + if ((wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) && + !wacom->battery) { + wacom_initialize_battery(wacom); + } + else if (!(wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) && + wacom->battery) { + wacom_destroy_battery(wacom); + } +} + /* * Not all devices report physical dimensions from HID. * Compute the default from hardcoded logical dimension @@ -1377,6 +1412,9 @@ static int wacom_probe(struct hid_device *hdev, hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; + /* hid-core sets this quirk for the boot interface */ + hdev->quirks &= ~HID_QUIRK_NOGET; + wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); if (!wacom) return -ENOMEM; @@ -1416,6 +1454,21 @@ static int wacom_probe(struct hid_device *hdev, goto fail_allocate_inputs; } + /* + * Bamboo Pad has a generic hid handling for the Pen, and we switch it + * into debug mode for the touch part. + * We ignore the other interfaces. + */ + if (features->type == BAMBOO_PAD) { + if (features->pktlen == WACOM_PKGLEN_PENABLED) { + features->type = HID_GENERIC; + } else if ((features->pktlen != WACOM_PKGLEN_BPAD_TOUCH) && + (features->pktlen != WACOM_PKGLEN_BPAD_TOUCH_USB)) { + error = -ENODEV; + goto fail_shared_data; + } + } + /* set the default size in case we do not get them from hid */ wacom_set_default_phy(features); @@ -1450,6 +1503,12 @@ static int wacom_probe(struct hid_device *hdev, features->y_max = 4096; } + /* + * Same thing for Bamboo PAD + */ + if (features->type == BAMBOO_PAD) + features->device_type = BTN_TOOL_FINGER; + if (hdev->bus == BUS_BLUETOOTH) features->quirks |= WACOM_QUIRK_BATTERY; @@ -1466,19 +1525,17 @@ static int wacom_probe(struct hid_device *hdev, snprintf(wacom_wac->pad_name, sizeof(wacom_wac->pad_name), "%s Pad", features->name); - if (features->quirks & WACOM_QUIRK_MULTI_INPUT) { - /* Append the device type to the name */ - if (features->device_type != BTN_TOOL_FINGER) - strlcat(wacom_wac->name, " Pen", WACOM_NAME_MAX); - else if (features->touch_max) - strlcat(wacom_wac->name, " Finger", WACOM_NAME_MAX); - else - strlcat(wacom_wac->name, " Pad", WACOM_NAME_MAX); + /* Append the device type to the name */ + if (features->device_type != BTN_TOOL_FINGER) + strlcat(wacom_wac->name, " Pen", WACOM_NAME_MAX); + else if (features->touch_max) + strlcat(wacom_wac->name, " Finger", WACOM_NAME_MAX); + else + strlcat(wacom_wac->name, " Pad", WACOM_NAME_MAX); - error = wacom_add_shared_data(hdev); - if (error) - goto fail_shared_data; - } + error = wacom_add_shared_data(hdev); + if (error) + goto fail_shared_data; if (!(features->quirks & WACOM_QUIRK_MONITOR) && (features->quirks & WACOM_QUIRK_BATTERY)) { @@ -1531,7 +1588,7 @@ static int wacom_probe(struct hid_device *hdev, wacom_clean_inputs(wacom); wacom_destroy_battery(wacom); fail_battery: - wacom_remove_shared_data(wacom_wac); + wacom_remove_shared_data(wacom); fail_shared_data: wacom_clean_inputs(wacom); fail_allocate_inputs: @@ -1554,7 +1611,7 @@ static void wacom_remove(struct hid_device *hdev) if (hdev->bus == BUS_BLUETOOTH) device_remove_file(&hdev->dev, &dev_attr_speed); wacom_destroy_battery(wacom); - wacom_remove_shared_data(&wacom->wacom_wac); + wacom_remove_shared_data(wacom); hid_set_drvdata(hdev, NULL); kfree(wacom); diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index bbe32d66e50001..fa54d329065945 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -45,6 +45,27 @@ static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 }; */ static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 }; +static void wacom_notify_battery(struct wacom_wac *wacom_wac, + int bat_capacity, bool bat_charging, bool bat_connected, + bool ps_connected) +{ + struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); + bool changed = wacom_wac->battery_capacity != bat_capacity || + wacom_wac->bat_charging != bat_charging || + wacom_wac->bat_connected != bat_connected || + wacom_wac->ps_connected != ps_connected; + + if (changed) { + wacom_wac->battery_capacity = bat_capacity; + wacom_wac->bat_charging = bat_charging; + wacom_wac->bat_connected = bat_connected; + wacom_wac->ps_connected = ps_connected; + + if (wacom->battery) + power_supply_changed(wacom->battery); + } +} + static int wacom_penpartner_irq(struct wacom_wac *wacom) { unsigned char *data = wacom->data; @@ -419,17 +440,26 @@ static int wacom_graphire_irq(struct wacom_wac *wacom) rw = (data[7] >> 2 & 0x07); battery_capacity = batcap_gr[rw]; ps_connected = rw == 7; - if ((wacom->battery_capacity != battery_capacity) || - (wacom->ps_connected != ps_connected)) { - wacom->battery_capacity = battery_capacity; - wacom->ps_connected = ps_connected; - wacom_notify_battery(wacom); - } + wacom_notify_battery(wacom, battery_capacity, ps_connected, + 1, ps_connected); } exit: return retval; } +static void wacom_intuos_schedule_prox_event(struct wacom_wac *wacom_wac) +{ + struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); + struct hid_report *r; + struct hid_report_enum *re; + + re = &(wacom->hdev->report_enum[HID_FEATURE_REPORT]); + r = re->report_id_hash[WACOM_REPORT_INTUOSREAD]; + if (r) { + hid_hw_request(wacom->hdev, r, HID_REQ_GET_REPORT); + } +} + static int wacom_intuos_inout(struct wacom_wac *wacom) { struct wacom_features *features = &wacom->features; @@ -551,12 +581,9 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) (features->type == CINTIQ && !(data[1] & 0x40))) return 1; - if (wacom->shared) { - wacom->shared->stylus_in_proximity = true; - - if (wacom->shared->touch_down) - return 1; - } + wacom->shared->stylus_in_proximity = true; + if (wacom->shared->touch_down) + return 1; /* in Range while exiting */ if (((data[1] & 0xfe) == 0x20) && wacom->reporting_data) { @@ -568,8 +595,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) /* Exit report */ if ((data[1] & 0xfe) == 0x80) { - if (features->quirks & WACOM_QUIRK_MULTI_INPUT) - wacom->shared->stylus_in_proximity = false; + wacom->shared->stylus_in_proximity = false; wacom->reporting_data = false; /* don't report exit if we don't know the ID */ @@ -610,8 +636,11 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) } /* don't report other events if we don't know the ID */ - if (!wacom->id[idx]) + if (!wacom->id[idx]) { + /* but reschedule a read of the current tool */ + wacom_intuos_schedule_prox_event(wacom); return 1; + } return 0; } @@ -1023,15 +1052,9 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len) bat_charging = (power_raw & 0x08) ? 1 : 0; ps_connected = (power_raw & 0x10) ? 1 : 0; battery_capacity = batcap_i4[power_raw & 0x07]; - if ((wacom->battery_capacity != battery_capacity) || - (wacom->bat_charging != bat_charging) || - (wacom->ps_connected != ps_connected)) { - wacom->battery_capacity = battery_capacity; - wacom->bat_charging = bat_charging; - wacom->ps_connected = ps_connected; - wacom_notify_battery(wacom); - } - + wacom_notify_battery(wacom, battery_capacity, bat_charging, + battery_capacity || bat_charging, + ps_connected); break; default: dev_dbg(wacom->input->dev.parent, @@ -1042,6 +1065,28 @@ static int wacom_intuos_bt_irq(struct wacom_wac *wacom, size_t len) return 0; } +static int wacom_wac_finger_count_touches(struct wacom_wac *wacom) +{ + struct input_dev *input = wacom->input; + unsigned touch_max = wacom->features.touch_max; + int count = 0; + int i; + + /* non-HID_GENERIC single touch input doesn't call this routine */ + if ((touch_max == 1) && (wacom->features.type == HID_GENERIC)) + return wacom->hid_data.tipswitch && + !wacom->shared->stylus_in_proximity; + + for (i = 0; i < input->mt->num_slots; i++) { + struct input_mt_slot *ps = &input->mt->slots[i]; + int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID); + if (id >= 0) + count++; + } + + return count; +} + static int wacom_24hdt_irq(struct wacom_wac *wacom) { struct input_dev *input = wacom->input; @@ -1052,7 +1097,6 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) int num_contacts_left = 4; /* maximum contacts per packet */ int byte_per_packet = WACOM_BYTES_PER_24HDT_PACKET; int y_offset = 2; - static int contact_with_no_pen_down_count = 0; if (wacom->features.type == WACOM_27QHDT) { current_num_contacts = data[63]; @@ -1065,10 +1109,8 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) * First packet resets the counter since only the first * packet in series will have non-zero current_num_contacts. */ - if (current_num_contacts) { + if (current_num_contacts) wacom->num_contacts_left = current_num_contacts; - contact_with_no_pen_down_count = 0; - } contacts_to_send = min(num_contacts_left, wacom->num_contacts_left); @@ -1101,15 +1143,14 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h)); input_report_abs(input, ABS_MT_ORIENTATION, w > h); } - contact_with_no_pen_down_count++; } } - input_mt_report_pointer_emulation(input, true); + input_mt_sync_frame(input); wacom->num_contacts_left -= contacts_to_send; if (wacom->num_contacts_left <= 0) { wacom->num_contacts_left = 0; - wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); + wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom); } return 1; } @@ -1122,7 +1163,6 @@ static int wacom_mt_touch(struct wacom_wac *wacom) int current_num_contacts = data[2]; int contacts_to_send = 0; int x_offset = 0; - static int contact_with_no_pen_down_count = 0; /* MTTPC does not support Height and Width */ if (wacom->features.type == MTTPC || wacom->features.type == MTTPC_B) @@ -1132,10 +1172,8 @@ static int wacom_mt_touch(struct wacom_wac *wacom) * First packet resets the counter since only the first * packet in series will have non-zero current_num_contacts. */ - if (current_num_contacts) { + if (current_num_contacts) wacom->num_contacts_left = current_num_contacts; - contact_with_no_pen_down_count = 0; - } /* There are at most 5 contacts per packet */ contacts_to_send = min(5, wacom->num_contacts_left); @@ -1156,15 +1194,14 @@ static int wacom_mt_touch(struct wacom_wac *wacom) int y = get_unaligned_le16(&data[offset + x_offset + 9]); input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); - contact_with_no_pen_down_count++; } } - input_mt_report_pointer_emulation(input, true); + input_mt_sync_frame(input); wacom->num_contacts_left -= contacts_to_send; if (wacom->num_contacts_left <= 0) { wacom->num_contacts_left = 0; - wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); + wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom); } return 1; } @@ -1173,7 +1210,6 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom) { struct input_dev *input = wacom->input; unsigned char *data = wacom->data; - int contact_with_no_pen_down_count = 0; int i; for (i = 0; i < 2; i++) { @@ -1188,13 +1224,12 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom) input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); - contact_with_no_pen_down_count++; } } - input_mt_report_pointer_emulation(input, true); + input_mt_sync_frame(input); /* keep touch state for pen event */ - wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); + wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom); return 1; } @@ -1522,29 +1557,6 @@ static int wacom_wac_finger_event(struct hid_device *hdev, return 0; } -static int wacom_wac_finger_count_touches(struct hid_device *hdev) -{ - struct wacom *wacom = hid_get_drvdata(hdev); - struct wacom_wac *wacom_wac = &wacom->wacom_wac; - struct input_dev *input = wacom_wac->input; - unsigned touch_max = wacom_wac->features.touch_max; - int count = 0; - int i; - - if (touch_max == 1) - return wacom_wac->hid_data.tipswitch && - !wacom_wac->shared->stylus_in_proximity; - - for (i = 0; i < input->mt->num_slots; i++) { - struct input_mt_slot *ps = &input->mt->slots[i]; - int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID); - if (id >= 0) - count++; - } - - return count; -} - static void wacom_wac_finger_report(struct hid_device *hdev, struct hid_report *report) { @@ -1559,7 +1571,7 @@ static void wacom_wac_finger_report(struct hid_device *hdev, input_sync(input); /* keep touch state for pen event */ - wacom_wac->shared->touch_down = wacom_wac_finger_count_touches(hdev); + wacom_wac->shared->touch_down = wacom_wac_finger_count_touches(wacom_wac); } void wacom_wac_usage_mapping(struct hid_device *hdev, @@ -1619,7 +1631,6 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) struct input_dev *pad_input = wacom->pad_input; unsigned char *data = wacom->data; int i; - int contact_with_no_pen_down_count = 0; if (data[0] != 0x02) return 0; @@ -1647,22 +1658,21 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) } input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); - contact_with_no_pen_down_count++; } } - input_mt_report_pointer_emulation(input, true); + input_mt_sync_frame(input); input_report_key(pad_input, BTN_LEFT, (data[1] & 0x08) != 0); input_report_key(pad_input, BTN_FORWARD, (data[1] & 0x04) != 0); input_report_key(pad_input, BTN_BACK, (data[1] & 0x02) != 0); input_report_key(pad_input, BTN_RIGHT, (data[1] & 0x01) != 0); - wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); + wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom); return 1; } -static int wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data, int last_touch_count) +static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) { struct wacom_features *features = &wacom->features; struct input_dev *input = wacom->input; @@ -1670,7 +1680,7 @@ static int wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data, in int slot = input_mt_get_slot_by_key(input, data[0]); if (slot < 0) - return 0; + return; touch = touch && !wacom->shared->stylus_in_proximity; @@ -1702,9 +1712,7 @@ static int wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data, in input_report_abs(input, ABS_MT_POSITION_Y, y); input_report_abs(input, ABS_MT_TOUCH_MAJOR, width); input_report_abs(input, ABS_MT_TOUCH_MINOR, height); - last_touch_count++; } - return last_touch_count; } static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data) @@ -1729,7 +1737,6 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom) unsigned char *data = wacom->data; int count = data[1] & 0x07; int i; - int contact_with_no_pen_down_count = 0; if (data[0] != 0x02) return 0; @@ -1740,15 +1747,13 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom) int msg_id = data[offset]; if (msg_id >= 2 && msg_id <= 17) - contact_with_no_pen_down_count = - wacom_bpt3_touch_msg(wacom, data + offset, - contact_with_no_pen_down_count); + wacom_bpt3_touch_msg(wacom, data + offset); else if (msg_id == 128) wacom_bpt3_button_msg(wacom, data + offset); } - input_mt_report_pointer_emulation(input, true); - wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); + input_mt_sync_frame(input); + wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom); return 1; } @@ -1760,23 +1765,9 @@ static int wacom_bpt_pen(struct wacom_wac *wacom) unsigned char *data = wacom->data; int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0; - if (data[0] != WACOM_REPORT_PENABLED && data[0] != WACOM_REPORT_USB) + if (data[0] != WACOM_REPORT_PENABLED) return 0; - if (data[0] == WACOM_REPORT_USB) { - if (features->type == INTUOSHT && - wacom->shared->touch_input && - features->touch_max) { - input_report_switch(wacom->shared->touch_input, - SW_MUTE_DEVICE, data[8] & 0x40); - input_sync(wacom->shared->touch_input); - } - return 0; - } - - if (wacom->shared->touch_down) - return 0; - prox = (data[1] & 0x20) == 0x20; /* @@ -1789,17 +1780,21 @@ static int wacom_bpt_pen(struct wacom_wac *wacom) * * Hardware does report zero in most out-of-prox cases but not all. */ - if (prox) { - if (!wacom->shared->stylus_in_proximity) { - if (data[1] & 0x08) { - wacom->tool[0] = BTN_TOOL_RUBBER; - wacom->id[0] = ERASER_DEVICE_ID; - } else { - wacom->tool[0] = BTN_TOOL_PEN; - wacom->id[0] = STYLUS_DEVICE_ID; - } - wacom->shared->stylus_in_proximity = true; + if (!wacom->shared->stylus_in_proximity) { + if (data[1] & 0x08) { + wacom->tool[0] = BTN_TOOL_RUBBER; + wacom->id[0] = ERASER_DEVICE_ID; + } else { + wacom->tool[0] = BTN_TOOL_PEN; + wacom->id[0] = STYLUS_DEVICE_ID; } + } + + wacom->shared->stylus_in_proximity = prox; + if (wacom->shared->touch_down) + return 0; + + if (prox) { x = le16_to_cpup((__le16 *)&data[2]); y = le16_to_cpup((__le16 *)&data[4]); p = le16_to_cpup((__le16 *)&data[6]); @@ -1815,6 +1810,8 @@ static int wacom_bpt_pen(struct wacom_wac *wacom) pen = data[1] & 0x01; btn1 = data[1] & 0x02; btn2 = data[1] & 0x04; + } else { + wacom->id[0] = 0; } input_report_key(input, BTN_TOUCH, pen); @@ -1826,11 +1823,6 @@ static int wacom_bpt_pen(struct wacom_wac *wacom) input_report_abs(input, ABS_PRESSURE, p); input_report_abs(input, ABS_DISTANCE, d); - if (!prox) { - wacom->id[0] = 0; - wacom->shared->stylus_in_proximity = false; - } - input_report_key(input, wacom->tool[0], prox); /* PEN or RUBBER */ input_report_abs(input, ABS_MISC, wacom->id[0]); /* TOOL ID */ @@ -1849,6 +1841,91 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len) return 0; } +static void wacom_bamboo_pad_pen_event(struct wacom_wac *wacom, + unsigned char *data) +{ + unsigned char prefix; + + /* + * We need to reroute the event from the debug interface to the + * pen interface. + * We need to add the report ID to the actual pen report, so we + * temporary overwrite the first byte to prevent having to kzalloc/kfree + * and memcpy the report. + */ + prefix = data[0]; + data[0] = WACOM_REPORT_BPAD_PEN; + + /* + * actually reroute the event. + * No need to check if wacom->shared->pen is valid, hid_input_report() + * will check for us. + */ + hid_input_report(wacom->shared->pen, HID_INPUT_REPORT, data, + WACOM_PKGLEN_PENABLED, 1); + + data[0] = prefix; +} + +static int wacom_bamboo_pad_touch_event(struct wacom_wac *wacom, + unsigned char *data) +{ + struct input_dev *input = wacom->input; + unsigned char *finger_data, prefix; + unsigned id; + int x, y; + bool valid; + + prefix = data[0]; + + for (id = 0; id < wacom->features.touch_max; id++) { + valid = !!(prefix & BIT(id)) && + !wacom->shared->stylus_in_proximity; + + input_mt_slot(input, id); + input_mt_report_slot_state(input, MT_TOOL_FINGER, valid); + + if (!valid) + continue; + + finger_data = data + 1 + id * 3; + x = finger_data[0] | ((finger_data[1] & 0x0f) << 8); + y = (finger_data[2] << 4) | (finger_data[1] >> 4); + + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + } + + input_mt_sync_frame(input); + + input_report_key(input, BTN_LEFT, prefix & 0x40); + input_report_key(input, BTN_RIGHT, prefix & 0x80); + + /* keep touch state for pen event */ + wacom->shared->touch_down = !!prefix && + !wacom->shared->stylus_in_proximity; + + return 1; +} + +static int wacom_bamboo_pad_irq(struct wacom_wac *wacom, size_t len) +{ + unsigned char *data = wacom->data; + + if (!((len == WACOM_PKGLEN_BPAD_TOUCH) || + (len == WACOM_PKGLEN_BPAD_TOUCH_USB)) || + (data[0] != WACOM_REPORT_BPAD_TOUCH)) + return 0; + + if (data[1] & 0x01) + wacom_bamboo_pad_pen_event(wacom, &data[1]); + + if (data[1] & 0x02) + return wacom_bamboo_pad_touch_event(wacom, &data[9]); + + return 0; +} + static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) { unsigned char *data = wacom->data; @@ -1859,7 +1936,7 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) connected = data[1] & 0x01; if (connected) { - int pid, battery, ps_connected; + int pid, battery, charging; if ((wacom->shared->type == INTUOSHT) && wacom->shared->touch_input && @@ -1871,30 +1948,63 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) pid = get_unaligned_be16(&data[6]); battery = (data[5] & 0x3f) * 100 / 31; - ps_connected = !!(data[5] & 0x80); + charging = !!(data[5] & 0x80); if (wacom->pid != pid) { wacom->pid = pid; wacom_schedule_work(wacom); } - if (wacom->shared->type && - (battery != wacom->battery_capacity || - ps_connected != wacom->ps_connected)) { - wacom->battery_capacity = battery; - wacom->ps_connected = ps_connected; - wacom->bat_charging = ps_connected && - wacom->battery_capacity < 100; - wacom_notify_battery(wacom); - } + if (wacom->shared->type) + wacom_notify_battery(wacom, battery, charging, 1, 0); + } else if (wacom->pid != 0) { /* disconnected while previously connected */ wacom->pid = 0; wacom_schedule_work(wacom); - wacom->battery_capacity = 0; - wacom->bat_charging = 0; - wacom->ps_connected = 0; + wacom_notify_battery(wacom, 0, 0, 0, 0); + } + + return 0; +} + +static int wacom_status_irq(struct wacom_wac *wacom_wac, size_t len) +{ + struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); + struct wacom_features *features = &wacom_wac->features; + unsigned char *data = wacom_wac->data; + + if (data[0] != WACOM_REPORT_USB) + return 0; + + if (features->type == INTUOSHT && + wacom_wac->shared->touch_input && + features->touch_max) { + input_report_switch(wacom_wac->shared->touch_input, + SW_MUTE_DEVICE, data[8] & 0x40); + input_sync(wacom_wac->shared->touch_input); } + if (data[9] & 0x02) { /* wireless module is attached */ + int battery = (data[8] & 0x3f) * 100 / 31; + bool charging = !!(data[8] & 0x80); + + wacom_notify_battery(wacom_wac, battery, charging, + battery || charging, 1); + + if (!wacom->battery && + !(features->quirks & WACOM_QUIRK_BATTERY)) { + features->quirks |= WACOM_QUIRK_BATTERY; + INIT_WORK(&wacom->work, wacom_battery_work); + wacom_schedule_work(wacom_wac); + } + } + else if ((features->quirks & WACOM_QUIRK_BATTERY) && + wacom->battery) { + features->quirks &= ~WACOM_QUIRK_BATTERY; + INIT_WORK(&wacom->work, wacom_battery_work); + wacom_schedule_work(wacom_wac); + wacom_notify_battery(wacom_wac, 0, 0, 0, 0); + } return 0; } @@ -1967,6 +2077,8 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) case INTUOSPL: if (len == WACOM_PKGLEN_BBTOUCH3) sync = wacom_bpt3_touch(wacom_wac); + else if (wacom_wac->data[0] == WACOM_REPORT_USB) + sync = wacom_status_irq(wacom_wac, len); else sync = wacom_intuos_irq(wacom_wac); break; @@ -1982,7 +2094,14 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) case BAMBOO_PT: case INTUOSHT: - sync = wacom_bpt_irq(wacom_wac, len); + if (wacom_wac->data[0] == WACOM_REPORT_USB) + sync = wacom_status_irq(wacom_wac, len); + else + sync = wacom_bpt_irq(wacom_wac, len); + break; + + case BAMBOO_PAD: + sync = wacom_bamboo_pad_irq(wacom_wac, len); break; case WIRELESS: @@ -2054,12 +2173,6 @@ void wacom_setup_device_quirks(struct wacom_features *features) features->y_max = 1023; } - /* these device have multiple inputs */ - if (features->type >= WIRELESS || - (features->type >= INTUOS5S && features->type <= INTUOSHT) || - (features->oVid && features->oPid)) - features->quirks |= WACOM_QUIRK_MULTI_INPUT; - /* quirk for bamboo touch with 2 low res touches */ if (features->type == BAMBOO_PT && features->pktlen == WACOM_PKGLEN_BBTOUCH) { @@ -2323,6 +2436,13 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, 0, 0); } break; + case BAMBOO_PAD: + __clear_bit(ABS_MISC, input_dev->absbit); + input_mt_init_slots(input_dev, features->touch_max, + INPUT_MT_POINTER); + __set_bit(BTN_LEFT, input_dev->keybit); + __set_bit(BTN_RIGHT, input_dev->keybit); + break; } return 0; } @@ -2772,6 +2892,15 @@ static const struct wacom_features wacom_features_0x304 = { "Wacom Cintiq 13HD", 59152, 33448, 1023, 63, WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET }; +static const struct wacom_features wacom_features_0x333 = + { "Wacom Cintiq 13HD touch", 59152, 33448, 2047, 63, + WACOM_13HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x335 }; +static const struct wacom_features wacom_features_0x335 = + { "Wacom Cintiq 13HD touch", .type = WACOM_24HDT, /* Touch */ + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x333, .touch_max = 10, + .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; static const struct wacom_features wacom_features_0xC7 = { "Wacom DTU1931", 37832, 30305, 511, 0, PL, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; @@ -2976,6 +3105,12 @@ static const struct wacom_features wacom_features_0x30C = { "Wacom ISDv5 30C", .type = WACOM_24HDT, /* Touch */ .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x30A, .touch_max = 10, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x318 = + { "Wacom USB Bamboo PAD", 4095, 4095, /* Touch */ + .type = BAMBOO_PAD, 35, 48, .touch_max = 4 }; +static const struct wacom_features wacom_features_0x319 = + { "Wacom Wireless Bamboo PAD", 4095, 4095, /* Touch */ + .type = BAMBOO_PAD, 35, 48, .touch_max = 4 }; static const struct wacom_features wacom_features_0x323 = { "Wacom Intuos P M", 21600, 13500, 1023, 31, INTUOSHT, WACOM_INTUOS_RES, WACOM_INTUOS_RES, @@ -2992,6 +3127,10 @@ static const struct wacom_features wacom_features_HID_ANY_ID = HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\ .driver_data = (kernel_ulong_t)&wacom_features_##prod +#define I2C_DEVICE_WACOM(prod) \ + HID_DEVICE(BUS_I2C, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\ + .driver_data = (kernel_ulong_t)&wacom_features_##prod + #define USB_DEVICE_LENOVO(prod) \ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \ .driver_data = (kernel_ulong_t)&wacom_features_##prod @@ -3124,11 +3263,15 @@ const struct hid_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x314) }, { USB_DEVICE_WACOM(0x315) }, { USB_DEVICE_WACOM(0x317) }, + { USB_DEVICE_WACOM(0x318) }, + { USB_DEVICE_WACOM(0x319) }, { USB_DEVICE_WACOM(0x323) }, { USB_DEVICE_WACOM(0x32A) }, { USB_DEVICE_WACOM(0x32B) }, { USB_DEVICE_WACOM(0x32C) }, { USB_DEVICE_WACOM(0x32F) }, + { USB_DEVICE_WACOM(0x333) }, + { USB_DEVICE_WACOM(0x335) }, { USB_DEVICE_WACOM(0x4001) }, { USB_DEVICE_WACOM(0x4004) }, { USB_DEVICE_WACOM(0x5000) }, @@ -3136,6 +3279,7 @@ const struct hid_device_id wacom_ids[] = { { USB_DEVICE_LENOVO(0x6004) }, { USB_DEVICE_WACOM(HID_ANY_ID) }, + { I2C_DEVICE_WACOM(HID_ANY_ID) }, { } }; MODULE_DEVICE_TABLE(hid, wacom_ids); diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 021ee1c1980a83..4700ac994a3b37 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -33,6 +33,8 @@ #define WACOM_PKGLEN_MTTPC 40 #define WACOM_PKGLEN_DTUS 68 #define WACOM_PKGLEN_PENABLED 8 +#define WACOM_PKGLEN_BPAD_TOUCH 32 +#define WACOM_PKGLEN_BPAD_TOUCH_USB 64 /* wacom data size per MT contact */ #define WACOM_BYTES_PER_MT_PACKET 11 @@ -67,13 +69,14 @@ #define WACOM_REPORT_24HDT 1 #define WACOM_REPORT_WL 128 #define WACOM_REPORT_USB 192 +#define WACOM_REPORT_BPAD_PEN 3 +#define WACOM_REPORT_BPAD_TOUCH 16 /* device quirks */ -#define WACOM_QUIRK_MULTI_INPUT 0x0001 -#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002 -#define WACOM_QUIRK_NO_INPUT 0x0004 -#define WACOM_QUIRK_MONITOR 0x0008 -#define WACOM_QUIRK_BATTERY 0x0010 +#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0001 +#define WACOM_QUIRK_NO_INPUT 0x0002 +#define WACOM_QUIRK_MONITOR 0x0004 +#define WACOM_QUIRK_BATTERY 0x0008 #define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \ ((f)->physical == HID_DG_STYLUS) || \ @@ -122,6 +125,7 @@ enum { BAMBOO_PT, WACOM_24HDT, WACOM_27QHDT, + BAMBOO_PAD, TABLETPC, /* add new TPC below */ TABLETPCE, TABLETPC2FG, @@ -169,6 +173,8 @@ struct wacom_shared { unsigned touch_max; int type; struct input_dev *touch_input; + struct hid_device *pen; + struct hid_device *touch; }; struct hid_data { @@ -205,6 +211,7 @@ struct wacom_wac { int battery_capacity; int num_contacts_left; int bat_charging; + int bat_connected; int ps_connected; u8 bt_features; u8 bt_high_speed; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 22da9c2ffa2250..2255af23b9c70e 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -485,6 +485,15 @@ config I2C_DESIGNWARE_BAYTRAIL the platform firmware controlling it. You should say Y if running on a BayTrail system using the AXP288. +config I2C_DIGICOLOR + tristate "Conexant Digicolor I2C driver" + depends on ARCH_DIGICOLOR + help + Support for Conexant Digicolor SoCs (CX92755) I2C controller driver. + + This driver can also be built as a module. If so, the module + will be called i2c-digicolor. + config I2C_EFM32 tristate "EFM32 I2C controller" depends on ARCH_EFM32 || COMPILE_TEST @@ -574,6 +583,15 @@ config I2C_IOP3XX This driver can also be built as a module. If so, the module will be called i2c-iop3xx. +config I2C_JZ4780 + tristate "JZ4780 I2C controller interface support" + depends on MACH_JZ4780 || COMPILE_TEST + help + If you say yes to this option, support will be included for the + Ingenic JZ4780 I2C controller. + + If you don't know what to do here, say N. + config I2C_KEMPLD tristate "Kontron COM I2C Controller" depends on MFD_KEMPLD @@ -898,6 +916,16 @@ config I2C_XLR This driver can also be built as a module. If so, the module will be called i2c-xlr. +config I2C_XLP9XX + tristate "XLP9XX I2C support" + depends on CPU_XLP || COMPILE_TEST + help + This driver enables support for the on-chip I2C interface of + the Broadcom XLP9xx/XLP5xx MIPS processors. + + This driver can also be built as a module. If so, the module will + be called i2c-xlp9xx. + config I2C_RCAR tristate "Renesas R-Car I2C Controller" depends on ARCH_SHMOBILE || COMPILE_TEST diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 3638feb6677e1d..cdf941da91c65e 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -45,6 +45,7 @@ i2c-designware-platform-objs := i2c-designware-platdrv.o i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_BAYTRAIL) += i2c-designware-baytrail.o obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o i2c-designware-pci-objs := i2c-designware-pcidrv.o +obj-$(CONFIG_I2C_DIGICOLOR) += i2c-digicolor.o obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o @@ -55,6 +56,7 @@ obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o obj-$(CONFIG_I2C_IMG) += i2c-img-scb.o obj-$(CONFIG_I2C_IMX) += i2c-imx.o obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o +obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o obj-$(CONFIG_I2C_MESON) += i2c-meson.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o @@ -87,6 +89,7 @@ obj-$(CONFIG_I2C_WMT) += i2c-wmt.o obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o +obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o # External I2C/SMBus adapter drivers diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 636fd2efad8850..ff23d1bdd23072 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -381,6 +381,7 @@ static irqreturn_t atmel_twi_interrupt(int irq, void *dev_id) static int at91_do_twi_transfer(struct at91_twi_dev *dev) { int ret; + unsigned long time_left; bool has_unre_flag = dev->pdata->has_unre_flag; dev_dbg(dev->dev, "transfer: %s %d bytes.\n", @@ -436,9 +437,9 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) } } - ret = wait_for_completion_timeout(&dev->cmd_complete, - dev->adapter.timeout); - if (ret == 0) { + time_left = wait_for_completion_timeout(&dev->cmd_complete, + dev->adapter.timeout); + if (time_left == 0) { dev_err(dev->dev, "controller timed out\n"); at91_init_twi_bus(dev); ret = -ETIMEDOUT; @@ -487,30 +488,10 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num) if (ret < 0) goto out; - /* - * The hardware can handle at most two messages concatenated by a - * repeated start via it's internal address feature. - */ - if (num > 2) { - dev_err(dev->dev, - "cannot handle more than two concatenated messages.\n"); - ret = 0; - goto out; - } else if (num == 2) { + if (num == 2) { int internal_address = 0; int i; - if (msg->flags & I2C_M_RD) { - dev_err(dev->dev, "first transfer must be write.\n"); - ret = -EINVAL; - goto out; - } - if (msg->len > 3) { - dev_err(dev->dev, "first message size must be <= 3.\n"); - ret = -EINVAL; - goto out; - } - /* 1st msg is put into the internal address, start with 2nd */ m_start = &msg[1]; for (i = 0; i < msg->len; ++i) { @@ -540,6 +521,15 @@ static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num) return ret; } +/* + * The hardware can handle at most two messages concatenated by a + * repeated start via it's internal address feature. + */ +static struct i2c_adapter_quirks at91_twi_quirks = { + .flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | I2C_AQ_COMB_SAME_ADDR, + .max_comb_1st_msg_len = 3, +}; + static u32 at91_twi_func(struct i2c_adapter *adapter) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL @@ -777,6 +767,7 @@ static int at91_twi_probe(struct platform_device *pdev) dev->adapter.owner = THIS_MODULE; dev->adapter.class = I2C_CLASS_DEPRECATED; dev->adapter.algo = &at91_twi_algorithm; + dev->adapter.quirks = &at91_twi_quirks; dev->adapter.dev.parent = dev->dev; dev->adapter.nr = pdev->id; dev->adapter.timeout = AT91_I2C_TIMEOUT; diff --git a/drivers/i2c/busses/i2c-axxia.c b/drivers/i2c/busses/i2c-axxia.c index 768a598d8d03ad..32d88349086314 100644 --- a/drivers/i2c/busses/i2c-axxia.c +++ b/drivers/i2c/busses/i2c-axxia.c @@ -334,12 +334,7 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg) u32 int_mask = MST_STATUS_ERR | MST_STATUS_SNS; u32 rx_xfer, tx_xfer; u32 addr_1, addr_2; - int ret; - - if (msg->len > 255) { - dev_warn(idev->dev, "unsupported length %u\n", msg->len); - return -EINVAL; - } + unsigned long time_left; idev->msg = msg; idev->msg_xfrd = 0; @@ -388,15 +383,15 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg) i2c_int_enable(idev, int_mask); - ret = wait_for_completion_timeout(&idev->msg_complete, - I2C_XFER_TIMEOUT); + time_left = wait_for_completion_timeout(&idev->msg_complete, + I2C_XFER_TIMEOUT); i2c_int_disable(idev, int_mask); if (readl(idev->base + MST_COMMAND) & CMD_BUSY) dev_warn(idev->dev, "busy after xfer\n"); - if (ret == 0) + if (time_left == 0) idev->msg_err = -ETIMEDOUT; if (unlikely(idev->msg_err) && idev->msg_err != -ENXIO) @@ -408,17 +403,17 @@ static int axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg) static int axxia_i2c_stop(struct axxia_i2c_dev *idev) { u32 int_mask = MST_STATUS_ERR | MST_STATUS_SCC; - int ret; + unsigned long time_left; reinit_completion(&idev->msg_complete); /* Issue stop */ writel(0xb, idev->base + MST_COMMAND); i2c_int_enable(idev, int_mask); - ret = wait_for_completion_timeout(&idev->msg_complete, - I2C_STOP_TIMEOUT); + time_left = wait_for_completion_timeout(&idev->msg_complete, + I2C_STOP_TIMEOUT); i2c_int_disable(idev, int_mask); - if (ret == 0) + if (time_left == 0) return -ETIMEDOUT; if (readl(idev->base + MST_COMMAND) & CMD_BUSY) @@ -454,6 +449,11 @@ static const struct i2c_algorithm axxia_i2c_algo = { .functionality = axxia_i2c_func, }; +static struct i2c_adapter_quirks axxia_i2c_quirks = { + .max_read_len = 255, + .max_write_len = 255, +}; + static int axxia_i2c_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -511,6 +511,7 @@ static int axxia_i2c_probe(struct platform_device *pdev) strlcpy(idev->adapter.name, pdev->name, sizeof(idev->adapter.name)); idev->adapter.owner = THIS_MODULE; idev->adapter.algo = &axxia_i2c_algo; + idev->adapter.quirks = &axxia_i2c_quirks; idev->adapter.dev.parent = &pdev->dev; idev->adapter.dev.of_node = pdev->dev.of_node; diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index d3c89157b33774..f9f2c2082151e2 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -160,14 +160,6 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, u32 val; unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); - /* need to reserve one byte in the FIFO for the slave address */ - if (msg->len > M_TX_RX_FIFO_SIZE - 1) { - dev_err(iproc_i2c->device, - "only support data length up to %u bytes\n", - M_TX_RX_FIFO_SIZE - 1); - return -EOPNOTSUPP; - } - /* check if bus is busy */ if (!!(readl(iproc_i2c->base + M_CMD_OFFSET) & BIT(M_CMD_START_BUSY_SHIFT))) { @@ -287,6 +279,12 @@ static const struct i2c_algorithm bcm_iproc_algo = { .functionality = bcm_iproc_i2c_functionality, }; +static struct i2c_adapter_quirks bcm_iproc_i2c_quirks = { + /* need to reserve one byte in the FIFO for the slave address */ + .max_read_len = M_TX_RX_FIFO_SIZE - 1, + .max_write_len = M_TX_RX_FIFO_SIZE - 1, +}; + static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) { unsigned int bus_speed; @@ -413,6 +411,7 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev) i2c_set_adapdata(adap, iproc_i2c); strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); adap->algo = &bcm_iproc_algo; + adap->quirks = &bcm_iproc_i2c_quirks; adap->dev.parent = &pdev->dev; adap->dev.of_node = pdev->dev.of_node; diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c index 5d6feb937b9dc4..c9336a3202d562 100644 --- a/drivers/i2c/busses/i2c-bcm2835.c +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -147,7 +147,7 @@ static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev *i2c_dev, struct i2c_msg *msg) { u32 c; - int time_left; + unsigned long time_left; i2c_dev->msg_buf = msg->buf; i2c_dev->msg_buf_remaining = msg->len; diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 7d7a14cdadfb18..2ee78e099d3047 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -475,7 +475,7 @@ static void cdns_i2c_master_reset(struct i2c_adapter *adap) static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg, struct i2c_adapter *adap) { - int ret; + unsigned long time_left; u32 reg; id->p_msg = msg; @@ -501,8 +501,8 @@ static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg, cdns_i2c_msend(id); /* Wait for the signal of completion */ - ret = wait_for_completion_timeout(&id->xfer_done, adap->timeout); - if (!ret) { + time_left = wait_for_completion_timeout(&id->xfer_done, adap->timeout); + if (time_left == 0) { cdns_i2c_master_reset(adap); dev_err(id->adap.dev.parent, "timeout waiting on completion\n"); diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index 2d466538b2e2c9..714bdc837769fd 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -308,22 +308,12 @@ static int cpm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) struct i2c_reg __iomem *i2c_reg = cpm->i2c_reg; struct i2c_ram __iomem *i2c_ram = cpm->i2c_ram; struct i2c_msg *pmsg; - int ret, i; + int ret; int tptr; int rptr; cbd_t __iomem *tbdf; cbd_t __iomem *rbdf; - if (num > CPM_MAXBD) - return -EINVAL; - - /* Check if we have any oversized READ requests */ - for (i = 0; i < num; i++) { - pmsg = &msgs[i]; - if (pmsg->len >= CPM_MAX_READ) - return -EINVAL; - } - /* Reset to use first buffer */ out_be16(&i2c_ram->rbptr, in_be16(&i2c_ram->rbase)); out_be16(&i2c_ram->tbptr, in_be16(&i2c_ram->tbase)); @@ -424,10 +414,18 @@ static const struct i2c_algorithm cpm_i2c_algo = { .functionality = cpm_i2c_func, }; +/* CPM_MAX_READ is also limiting writes according to the code! */ +static struct i2c_adapter_quirks cpm_i2c_quirks = { + .max_num_msgs = CPM_MAXBD, + .max_read_len = CPM_MAX_READ, + .max_write_len = CPM_MAX_READ, +}; + static const struct i2c_adapter cpm_ops = { .owner = THIS_MODULE, .name = "i2c-cpm", .algo = &cpm_i2c_algo, + .quirks = &cpm_i2c_quirks, }; static int cpm_i2c_setup(struct cpm_i2c *cpm) diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 6dc7ff5d3d9a05..4788a32afb8618 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -60,6 +60,12 @@ #define DAVINCI_I2C_IVR_REG 0x28 #define DAVINCI_I2C_EMDR_REG 0x2c #define DAVINCI_I2C_PSC_REG 0x30 +#define DAVINCI_I2C_FUNC_REG 0x48 +#define DAVINCI_I2C_DIR_REG 0x4c +#define DAVINCI_I2C_DIN_REG 0x50 +#define DAVINCI_I2C_DOUT_REG 0x54 +#define DAVINCI_I2C_DSET_REG 0x58 +#define DAVINCI_I2C_DCLR_REG 0x5c #define DAVINCI_I2C_IVR_AAS 0x07 #define DAVINCI_I2C_IVR_SCD 0x06 @@ -93,6 +99,29 @@ #define DAVINCI_I2C_IMR_NACK BIT(1) #define DAVINCI_I2C_IMR_AL BIT(0) +/* set SDA and SCL as GPIO */ +#define DAVINCI_I2C_FUNC_PFUNC0 BIT(0) + +/* set SCL as output when used as GPIO*/ +#define DAVINCI_I2C_DIR_PDIR0 BIT(0) +/* set SDA as output when used as GPIO*/ +#define DAVINCI_I2C_DIR_PDIR1 BIT(1) + +/* read SCL GPIO level */ +#define DAVINCI_I2C_DIN_PDIN0 BIT(0) +/* read SDA GPIO level */ +#define DAVINCI_I2C_DIN_PDIN1 BIT(1) + +/*set the SCL GPIO high */ +#define DAVINCI_I2C_DSET_PDSET0 BIT(0) +/*set the SDA GPIO high */ +#define DAVINCI_I2C_DSET_PDSET1 BIT(1) + +/* set the SCL GPIO low */ +#define DAVINCI_I2C_DCLR_PDCLR0 BIT(0) +/* set the SDA GPIO low */ +#define DAVINCI_I2C_DCLR_PDCLR1 BIT(1) + struct davinci_i2c_dev { struct device *dev; void __iomem *base; @@ -129,43 +158,6 @@ static inline u16 davinci_i2c_read_reg(struct davinci_i2c_dev *i2c_dev, int reg) return readw_relaxed(i2c_dev->base + reg); } -/* Generate a pulse on the i2c clock pin. */ -static void davinci_i2c_clock_pulse(unsigned int scl_pin) -{ - u16 i; - - if (scl_pin) { - /* Send high and low on the SCL line */ - for (i = 0; i < 9; i++) { - gpio_set_value(scl_pin, 0); - udelay(20); - gpio_set_value(scl_pin, 1); - udelay(20); - } - } -} - -/* This routine does i2c bus recovery as specified in the - * i2c protocol Rev. 03 section 3.16 titled "Bus clear" - */ -static void davinci_i2c_recover_bus(struct davinci_i2c_dev *dev) -{ - u32 flag = 0; - struct davinci_i2c_platform_data *pdata = dev->pdata; - - dev_err(dev->dev, "initiating i2c bus recovery\n"); - /* Send NACK to the slave */ - flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); - flag |= DAVINCI_I2C_MDR_NACK; - /* write the data into mode register */ - davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); - davinci_i2c_clock_pulse(pdata->scl_pin); - /* Send STOP */ - flag = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG); - flag |= DAVINCI_I2C_MDR_STP; - davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); -} - static inline void davinci_i2c_reset_ctrl(struct davinci_i2c_dev *i2c_dev, int val) { @@ -262,6 +254,99 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev) return 0; } +/* + * This routine does i2c bus recovery by using i2c_generic_gpio_recovery + * which is provided by I2C Bus recovery infrastructure. + */ +static void davinci_i2c_prepare_recovery(struct i2c_adapter *adap) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + + /* Disable interrupts */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, 0); + + /* put I2C into reset */ + davinci_i2c_reset_ctrl(dev, 0); +} + +static void davinci_i2c_unprepare_recovery(struct i2c_adapter *adap) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + + i2c_davinci_init(dev); +} + +static struct i2c_bus_recovery_info davinci_i2c_gpio_recovery_info = { + .recover_bus = i2c_generic_gpio_recovery, + .prepare_recovery = davinci_i2c_prepare_recovery, + .unprepare_recovery = davinci_i2c_unprepare_recovery, +}; + +static void davinci_i2c_set_scl(struct i2c_adapter *adap, int val) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + + if (val) + davinci_i2c_write_reg(dev, DAVINCI_I2C_DSET_REG, + DAVINCI_I2C_DSET_PDSET0); + else + davinci_i2c_write_reg(dev, DAVINCI_I2C_DCLR_REG, + DAVINCI_I2C_DCLR_PDCLR0); +} + +static int davinci_i2c_get_scl(struct i2c_adapter *adap) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + int val; + + /* read the state of SCL */ + val = davinci_i2c_read_reg(dev, DAVINCI_I2C_DIN_REG); + return val & DAVINCI_I2C_DIN_PDIN0; +} + +static int davinci_i2c_get_sda(struct i2c_adapter *adap) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + int val; + + /* read the state of SDA */ + val = davinci_i2c_read_reg(dev, DAVINCI_I2C_DIN_REG); + return val & DAVINCI_I2C_DIN_PDIN1; +} + +static void davinci_i2c_scl_prepare_recovery(struct i2c_adapter *adap) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + + davinci_i2c_prepare_recovery(adap); + + /* SCL output, SDA input */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_DIR_REG, DAVINCI_I2C_DIR_PDIR0); + + /* change to GPIO mode */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_FUNC_REG, + DAVINCI_I2C_FUNC_PFUNC0); +} + +static void davinci_i2c_scl_unprepare_recovery(struct i2c_adapter *adap) +{ + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); + + /* change back to I2C mode */ + davinci_i2c_write_reg(dev, DAVINCI_I2C_FUNC_REG, 0); + + davinci_i2c_unprepare_recovery(adap); +} + +static struct i2c_bus_recovery_info davinci_i2c_scl_recovery_info = { + .recover_bus = i2c_generic_scl_recovery, + .set_scl = davinci_i2c_set_scl, + .get_scl = davinci_i2c_get_scl, + .get_sda = davinci_i2c_get_sda, + .prepare_recovery = davinci_i2c_scl_prepare_recovery, + .unprepare_recovery = davinci_i2c_scl_unprepare_recovery, +}; + /* * Waiting for bus not busy */ @@ -282,8 +367,7 @@ static int i2c_davinci_wait_bus_not_busy(struct davinci_i2c_dev *dev, return -ETIMEDOUT; } else { to_cnt = 0; - davinci_i2c_recover_bus(dev); - i2c_davinci_init(dev); + i2c_recover_bus(&dev->adapter); } } if (allow_sleep) @@ -304,7 +388,7 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) struct davinci_i2c_platform_data *pdata = dev->pdata; u32 flag; u16 w; - int r; + unsigned long time_left; /* Introduce a delay, required for some boards (e.g Davinci EVM) */ if (pdata->bus_delay) @@ -368,11 +452,11 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) flag |= DAVINCI_I2C_MDR_STP; davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag); - r = wait_for_completion_timeout(&dev->cmd_complete, dev->adapter.timeout); - if (r == 0) { + time_left = wait_for_completion_timeout(&dev->cmd_complete, + dev->adapter.timeout); + if (!time_left) { dev_err(dev->dev, "controller timed out\n"); - davinci_i2c_recover_bus(dev); - i2c_davinci_init(dev); + i2c_recover_bus(adap); dev->buf_len = 0; return -ETIMEDOUT; } @@ -380,17 +464,13 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) /* This should be 0 if all bytes were transferred * or dev->cmd_err denotes an error. */ - if (r >= 0) { - dev_err(dev->dev, "abnormal termination buf_len=%i\n", - dev->buf_len); - r = -EREMOTEIO; - } + dev_err(dev->dev, "abnormal termination buf_len=%i\n", + dev->buf_len); dev->terminate = 1; wmb(); dev->buf_len = 0; + return -EREMOTEIO; } - if (r < 0) - return r; /* no error */ if (likely(!dev->cmd_err)) @@ -674,6 +754,10 @@ static int davinci_i2c_probe(struct platform_device *pdev) if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency", &prop)) dev->pdata->bus_freq = prop / 1000; + + dev->pdata->has_pfunc = + of_property_read_bool(pdev->dev.of_node, + "ti,has-pfunc"); } else if (!dev->pdata) { dev->pdata = &davinci_i2c_platform_data_default; } @@ -715,6 +799,14 @@ static int davinci_i2c_probe(struct platform_device *pdev) adap->timeout = DAVINCI_I2C_TIMEOUT; adap->dev.of_node = pdev->dev.of_node; + if (dev->pdata->has_pfunc) + adap->bus_recovery_info = &davinci_i2c_scl_recovery_info; + else if (dev->pdata->scl_pin) { + adap->bus_recovery_info = &davinci_i2c_gpio_recovery_info; + adap->bus_recovery_info->scl_gpio = dev->pdata->scl_pin; + adap->bus_recovery_info->sda_gpio = dev->pdata->sda_pin; + } + adap->nr = pdev->id; r = i2c_add_numbered_adapter(adap); if (r) { diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index 6e25c010e69037..6f19a33773fe79 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -656,8 +656,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) i2c_dw_xfer_init(dev); /* wait for tx to complete */ - ret = wait_for_completion_timeout(&dev->cmd_complete, HZ); - if (ret == 0) { + if (!wait_for_completion_timeout(&dev->cmd_complete, HZ)) { dev_err(dev->dev, "controller timed out\n"); /* i2c_dw_init implicitly disables the adapter */ i2c_dw_init(dev); diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index c270f5f9a8f9af..fa4e2b521f0d6f 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -143,10 +143,8 @@ static int dw_i2c_probe(struct platform_device *pdev) u32 clk_freq, ht = 0; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no irq resource?\n"); - return irq; /* -ENXIO */ - } + if (irq < 0) + return irq; dev = devm_kzalloc(&pdev->dev, sizeof(struct dw_i2c_dev), GFP_KERNEL); if (!dev) diff --git a/drivers/i2c/busses/i2c-digicolor.c b/drivers/i2c/busses/i2c-digicolor.c new file mode 100644 index 00000000000000..03f1e5549896dc --- /dev/null +++ b/drivers/i2c/busses/i2c-digicolor.c @@ -0,0 +1,385 @@ +/* + * I2C bus driver for Conexant Digicolor SoCs + * + * Author: Baruch Siach + * + * Copyright (C) 2015 Paradox Innovation Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_FREQ 100000 +#define TIMEOUT_MS 100 + +#define II_CONTROL 0x0 +#define II_CONTROL_LOCAL_RESET BIT(0) + +#define II_CLOCKTIME 0x1 + +#define II_COMMAND 0x2 +#define II_CMD_START 1 +#define II_CMD_RESTART 2 +#define II_CMD_SEND_ACK 3 +#define II_CMD_GET_ACK 6 +#define II_CMD_GET_NOACK 7 +#define II_CMD_STOP 10 +#define II_COMMAND_GO BIT(7) +#define II_COMMAND_COMPLETION_STATUS(r) (((r) >> 5) & 3) +#define II_CMD_STATUS_NORMAL 0 +#define II_CMD_STATUS_ACK_GOOD 1 +#define II_CMD_STATUS_ACK_BAD 2 +#define II_CMD_STATUS_ABORT 3 + +#define II_DATA 0x3 +#define II_INTFLAG_CLEAR 0x8 +#define II_INTENABLE 0xa + +struct dc_i2c { + struct i2c_adapter adap; + struct device *dev; + void __iomem *regs; + struct clk *clk; + unsigned int frequency; + + struct i2c_msg *msg; + unsigned int msgbuf_ptr; + int last; + spinlock_t lock; + struct completion done; + int state; + int error; +}; + +enum { + STATE_IDLE, + STATE_START, + STATE_ADDR, + STATE_WRITE, + STATE_READ, + STATE_STOP, +}; + +static void dc_i2c_cmd(struct dc_i2c *i2c, u8 cmd) +{ + writeb_relaxed(cmd | II_COMMAND_GO, i2c->regs + II_COMMAND); +} + +static u8 dc_i2c_addr_cmd(struct i2c_msg *msg) +{ + u8 addr = (msg->addr & 0x7f) << 1; + + if (msg->flags & I2C_M_RD) + addr |= 1; + + return addr; +} + +static void dc_i2c_data(struct dc_i2c *i2c, u8 data) +{ + writeb_relaxed(data, i2c->regs + II_DATA); +} + +static void dc_i2c_write_byte(struct dc_i2c *i2c, u8 byte) +{ + dc_i2c_data(i2c, byte); + dc_i2c_cmd(i2c, II_CMD_SEND_ACK); +} + +static void dc_i2c_write_buf(struct dc_i2c *i2c) +{ + dc_i2c_write_byte(i2c, i2c->msg->buf[i2c->msgbuf_ptr++]); +} + +static void dc_i2c_next_read(struct dc_i2c *i2c) +{ + bool last = (i2c->msgbuf_ptr + 1 == i2c->msg->len); + + dc_i2c_cmd(i2c, last ? II_CMD_GET_NOACK : II_CMD_GET_ACK); +} + +static void dc_i2c_stop(struct dc_i2c *i2c) +{ + i2c->state = STATE_STOP; + if (i2c->last) + dc_i2c_cmd(i2c, II_CMD_STOP); + else + complete(&i2c->done); +} + +static u8 dc_i2c_read_byte(struct dc_i2c *i2c) +{ + return readb_relaxed(i2c->regs + II_DATA); +} + +static void dc_i2c_read_buf(struct dc_i2c *i2c) +{ + i2c->msg->buf[i2c->msgbuf_ptr++] = dc_i2c_read_byte(i2c); + dc_i2c_next_read(i2c); +} + +static void dc_i2c_set_irq(struct dc_i2c *i2c, int enable) +{ + if (enable) + writeb_relaxed(1, i2c->regs + II_INTFLAG_CLEAR); + writeb_relaxed(!!enable, i2c->regs + II_INTENABLE); +} + +static int dc_i2c_cmd_status(struct dc_i2c *i2c) +{ + u8 cmd = readb_relaxed(i2c->regs + II_COMMAND); + + return II_COMMAND_COMPLETION_STATUS(cmd); +} + +static void dc_i2c_start_msg(struct dc_i2c *i2c, int first) +{ + struct i2c_msg *msg = i2c->msg; + + if (!(msg->flags & I2C_M_NOSTART)) { + i2c->state = STATE_START; + dc_i2c_cmd(i2c, first ? II_CMD_START : II_CMD_RESTART); + } else if (msg->flags & I2C_M_RD) { + i2c->state = STATE_READ; + dc_i2c_next_read(i2c); + } else { + i2c->state = STATE_WRITE; + dc_i2c_write_buf(i2c); + } +} + +static irqreturn_t dc_i2c_irq(int irq, void *dev_id) +{ + struct dc_i2c *i2c = dev_id; + int cmd_status = dc_i2c_cmd_status(i2c); + unsigned long flags; + u8 addr_cmd; + + writeb_relaxed(1, i2c->regs + II_INTFLAG_CLEAR); + + spin_lock_irqsave(&i2c->lock, flags); + + if (cmd_status == II_CMD_STATUS_ACK_BAD + || cmd_status == II_CMD_STATUS_ABORT) { + i2c->error = -EIO; + complete(&i2c->done); + goto out; + } + + switch (i2c->state) { + case STATE_START: + addr_cmd = dc_i2c_addr_cmd(i2c->msg); + dc_i2c_write_byte(i2c, addr_cmd); + i2c->state = STATE_ADDR; + break; + case STATE_ADDR: + if (i2c->msg->flags & I2C_M_RD) { + dc_i2c_next_read(i2c); + i2c->state = STATE_READ; + break; + } + i2c->state = STATE_WRITE; + /* fall through */ + case STATE_WRITE: + if (i2c->msgbuf_ptr < i2c->msg->len) + dc_i2c_write_buf(i2c); + else + dc_i2c_stop(i2c); + break; + case STATE_READ: + if (i2c->msgbuf_ptr < i2c->msg->len) + dc_i2c_read_buf(i2c); + else + dc_i2c_stop(i2c); + break; + case STATE_STOP: + i2c->state = STATE_IDLE; + complete(&i2c->done); + break; + } + +out: + spin_unlock_irqrestore(&i2c->lock, flags); + return IRQ_HANDLED; +} + +static int dc_i2c_xfer_msg(struct dc_i2c *i2c, struct i2c_msg *msg, int first, + int last) +{ + unsigned long timeout = msecs_to_jiffies(TIMEOUT_MS); + unsigned long flags; + + spin_lock_irqsave(&i2c->lock, flags); + i2c->msg = msg; + i2c->msgbuf_ptr = 0; + i2c->last = last; + i2c->error = 0; + + reinit_completion(&i2c->done); + dc_i2c_set_irq(i2c, 1); + dc_i2c_start_msg(i2c, first); + spin_unlock_irqrestore(&i2c->lock, flags); + + timeout = wait_for_completion_timeout(&i2c->done, timeout); + dc_i2c_set_irq(i2c, 0); + if (timeout == 0) { + i2c->state = STATE_IDLE; + return -ETIMEDOUT; + } + + if (i2c->error) + return i2c->error; + + return 0; +} + +static int dc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct dc_i2c *i2c = adap->algo_data; + int i, ret; + + for (i = 0; i < num; i++) { + ret = dc_i2c_xfer_msg(i2c, &msgs[i], i == 0, i == num - 1); + if (ret) + return ret; + } + + return num; +} + +static int dc_i2c_init_hw(struct dc_i2c *i2c) +{ + unsigned long clk_rate = clk_get_rate(i2c->clk); + unsigned int clocktime; + + writeb_relaxed(II_CONTROL_LOCAL_RESET, i2c->regs + II_CONTROL); + udelay(100); + writeb_relaxed(0, i2c->regs + II_CONTROL); + udelay(100); + + clocktime = DIV_ROUND_UP(clk_rate, 64 * i2c->frequency); + if (clocktime < 1 || clocktime > 0xff) { + dev_err(i2c->dev, "can't set bus speed of %u Hz\n", + i2c->frequency); + return -EINVAL; + } + writeb_relaxed(clocktime - 1, i2c->regs + II_CLOCKTIME); + + return 0; +} + +static u32 dc_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART; +} + +static const struct i2c_algorithm dc_i2c_algorithm = { + .master_xfer = dc_i2c_xfer, + .functionality = dc_i2c_func, +}; + +static int dc_i2c_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct dc_i2c *i2c; + struct resource *r; + int ret = 0, irq; + + i2c = devm_kzalloc(&pdev->dev, sizeof(struct dc_i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + if (of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &i2c->frequency)) + i2c->frequency = DEFAULT_FREQ; + + i2c->dev = &pdev->dev; + platform_set_drvdata(pdev, i2c); + + spin_lock_init(&i2c->lock); + init_completion(&i2c->done); + + i2c->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(i2c->clk)) + return PTR_ERR(i2c->clk); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2c->regs = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(i2c->regs)) + return PTR_ERR(i2c->regs); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, dc_i2c_irq, 0, + dev_name(&pdev->dev), i2c); + if (ret < 0) + return ret; + + strlcpy(i2c->adap.name, "Conexant Digicolor I2C adapter", + sizeof(i2c->adap.name)); + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &dc_i2c_algorithm; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = np; + i2c->adap.algo_data = i2c; + + ret = dc_i2c_init_hw(i2c); + if (ret) + return ret; + + ret = clk_prepare_enable(i2c->clk); + if (ret < 0) + return ret; + + ret = i2c_add_adapter(&i2c->adap); + if (ret < 0) { + clk_unprepare(i2c->clk); + return ret; + } + + return 0; +} + +static int dc_i2c_remove(struct platform_device *pdev) +{ + struct dc_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); + clk_disable_unprepare(i2c->clk); + + return 0; +} + +static const struct of_device_id dc_i2c_match[] = { + { .compatible = "cnxt,cx92755-i2c" }, + { }, +}; + +static struct platform_driver dc_i2c_driver = { + .probe = dc_i2c_probe, + .remove = dc_i2c_remove, + .driver = { + .name = "digicolor-i2c", + .of_match_table = dc_i2c_match, + }, +}; +module_platform_driver(dc_i2c_driver); + +MODULE_AUTHOR("Baruch Siach "); +MODULE_DESCRIPTION("Conexant Digicolor I2C master driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-dln2.c b/drivers/i2c/busses/i2c-dln2.c index b3fb86af4cbb14..1600edd57ce9d7 100644 --- a/drivers/i2c/busses/i2c-dln2.c +++ b/drivers/i2c/busses/i2c-dln2.c @@ -144,7 +144,6 @@ static int dln2_i2c_xfer(struct i2c_adapter *adapter, { struct dln2_i2c *dln2 = i2c_get_adapdata(adapter); struct i2c_msg *pmsg; - struct device *dev = &dln2->adapter.dev; int i; for (i = 0; i < num; i++) { @@ -152,11 +151,6 @@ static int dln2_i2c_xfer(struct i2c_adapter *adapter, pmsg = &msgs[i]; - if (pmsg->len > DLN2_I2C_MAX_XFER_SIZE) { - dev_warn(dev, "maximum transfer size exceeded\n"); - return -EOPNOTSUPP; - } - if (pmsg->flags & I2C_M_RD) { ret = dln2_i2c_read(dln2, pmsg->addr, pmsg->buf, pmsg->len); @@ -187,6 +181,11 @@ static const struct i2c_algorithm dln2_i2c_usb_algorithm = { .functionality = dln2_i2c_func, }; +static struct i2c_adapter_quirks dln2_i2c_quirks = { + .max_read_len = DLN2_I2C_MAX_XFER_SIZE, + .max_write_len = DLN2_I2C_MAX_XFER_SIZE, +}; + static int dln2_i2c_probe(struct platform_device *pdev) { int ret; @@ -209,7 +208,9 @@ static int dln2_i2c_probe(struct platform_device *pdev) dln2->adapter.owner = THIS_MODULE; dln2->adapter.class = I2C_CLASS_HWMON; dln2->adapter.algo = &dln2_i2c_usb_algorithm; + dln2->adapter.quirks = &dln2_i2c_quirks; dln2->adapter.dev.parent = dev; + dln2->adapter.dev.of_node = dev->of_node; i2c_set_adapdata(&dln2->adapter, dln2); snprintf(dln2->adapter.name, sizeof(dln2->adapter.name), "%s-%s-%d", "dln2-i2c", dev_name(pdev->dev.parent), dln2->port); diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 8fafb254e42a55..5ecbb3fdc27ee2 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -223,8 +223,6 @@ struct i801_priv { #endif }; -static struct pci_driver i801_driver; - #define FEATURE_SMBUS_PEC (1 << 0) #define FEATURE_BLOCK_BUFFER (1 << 1) #define FEATURE_BLOCK_PROC (1 << 2) @@ -1140,7 +1138,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) int err, i; struct i801_priv *priv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1182,34 +1180,35 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) } priv->features &= ~disable_features; - err = pci_enable_device(dev); + err = pcim_enable_device(dev); if (err) { dev_err(&dev->dev, "Failed to enable SMBus PCI device (%d)\n", err); - goto exit; + return err; } + pcim_pin_device(dev); /* Determine the address of the SMBus area */ priv->smba = pci_resource_start(dev, SMBBAR); if (!priv->smba) { - dev_err(&dev->dev, "SMBus base address uninitialized, " - "upgrade BIOS\n"); - err = -ENODEV; - goto exit; + dev_err(&dev->dev, + "SMBus base address uninitialized, upgrade BIOS\n"); + return -ENODEV; } err = acpi_check_resource_conflict(&dev->resource[SMBBAR]); if (err) { - err = -ENODEV; - goto exit; + return -ENODEV; } - err = pci_request_region(dev, SMBBAR, i801_driver.name); + err = pcim_iomap_regions(dev, 1 << SMBBAR, + dev_driver_string(&dev->dev)); if (err) { - dev_err(&dev->dev, "Failed to request SMBus region " - "0x%lx-0x%Lx\n", priv->smba, + dev_err(&dev->dev, + "Failed to request SMBus region 0x%lx-0x%Lx\n", + priv->smba, (unsigned long long)pci_resource_end(dev, SMBBAR)); - goto exit; + return err; } pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &temp); @@ -1254,8 +1253,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) if (priv->features & FEATURE_IRQ) { init_waitqueue_head(&priv->waitq); - err = request_irq(dev->irq, i801_isr, IRQF_SHARED, - i801_driver.name, priv); + err = devm_request_irq(&dev->dev, dev->irq, i801_isr, + IRQF_SHARED, + dev_driver_string(&dev->dev), priv); if (err) { dev_err(&dev->dev, "Failed to allocate irq %d: %d\n", dev->irq, err); @@ -1276,7 +1276,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) err = i2c_add_adapter(&priv->adapter); if (err) { dev_err(&dev->dev, "Failed to add SMBus adapter\n"); - goto exit_free_irq; + return err; } i801_probe_optional_slaves(priv); @@ -1286,14 +1286,6 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) pci_set_drvdata(dev, priv); return 0; - -exit_free_irq: - if (priv->features & FEATURE_IRQ) - free_irq(dev->irq, priv); - pci_release_region(dev, SMBBAR); -exit: - kfree(priv); - return err; } static void i801_remove(struct pci_dev *dev) @@ -1304,11 +1296,6 @@ static void i801_remove(struct pci_dev *dev) i2c_del_adapter(&priv->adapter); pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg); - if (priv->features & FEATURE_IRQ) - free_irq(dev->irq, priv); - pci_release_region(dev, SMBBAR); - - kfree(priv); /* * do not call pci_disable_device(dev) since it can cause hard hangs on * some systems during power-off (eg. Fujitsu-Siemens Lifebook E8010) @@ -1330,7 +1317,7 @@ static int i801_resume(struct pci_dev *dev) { pci_set_power_state(dev, PCI_D0); pci_restore_state(dev); - return pci_enable_device(dev); + return 0; } #else #define i801_suspend NULL diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c index 0fcc1694c6072a..00ffd661368069 100644 --- a/drivers/i2c/busses/i2c-img-scb.c +++ b/drivers/i2c/busses/i2c-img-scb.c @@ -988,15 +988,16 @@ static irqreturn_t img_i2c_isr(int irq, void *dev_id) static int img_i2c_reset_bus(struct img_i2c *i2c) { unsigned long flags; - int ret; + unsigned long time_left; spin_lock_irqsave(&i2c->lock, flags); reinit_completion(&i2c->msg_complete); img_i2c_reset_start(i2c); spin_unlock_irqrestore(&i2c->lock, flags); - ret = wait_for_completion_timeout(&i2c->msg_complete, IMG_I2C_TIMEOUT); - if (ret == 0) + time_left = wait_for_completion_timeout(&i2c->msg_complete, + IMG_I2C_TIMEOUT); + if (time_left == 0) return -ETIMEDOUT; return 0; } @@ -1007,6 +1008,7 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, struct img_i2c *i2c = i2c_get_adapdata(adap); bool atomic = false; int i, ret; + unsigned long time_left; if (i2c->mode == MODE_SUSPEND) { WARN(1, "refusing to service transaction in suspended state\n"); @@ -1068,11 +1070,11 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, img_i2c_write(i2c); spin_unlock_irqrestore(&i2c->lock, flags); - ret = wait_for_completion_timeout(&i2c->msg_complete, - IMG_I2C_TIMEOUT); + time_left = wait_for_completion_timeout(&i2c->msg_complete, + IMG_I2C_TIMEOUT); del_timer_sync(&i2c->check_timer); - if (ret == 0) { + if (time_left == 0) { dev_err(adap->dev.parent, "i2c transfer timed out\n"); i2c->msg_status = -ETIMEDOUT; break; diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index d7b26fc6f43200..a53a7dd6694545 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -601,6 +601,7 @@ static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs) { int result; + unsigned long time_left; unsigned int temp = 0; unsigned long orig_jiffies = jiffies; struct imx_i2c_dma *dma = i2c_imx->dma; @@ -624,10 +625,10 @@ static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx, */ imx_i2c_write_reg(msgs->addr << 1, i2c_imx, IMX_I2C_I2DR); reinit_completion(&i2c_imx->dma->cmd_complete); - result = wait_for_completion_timeout( + time_left = wait_for_completion_timeout( &i2c_imx->dma->cmd_complete, msecs_to_jiffies(DMA_TIMEOUT)); - if (result == 0) { + if (time_left == 0) { dmaengine_terminate_all(dma->chan_using); return -ETIMEDOUT; } @@ -663,6 +664,7 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bool is_lastmsg) { int result; + unsigned long time_left; unsigned int temp; unsigned long orig_jiffies = jiffies; struct imx_i2c_dma *dma = i2c_imx->dma; @@ -682,10 +684,10 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx, return result; reinit_completion(&i2c_imx->dma->cmd_complete); - result = wait_for_completion_timeout( + time_left = wait_for_completion_timeout( &i2c_imx->dma->cmd_complete, msecs_to_jiffies(DMA_TIMEOUT)); - if (result == 0) { + if (time_left == 0) { dmaengine_terminate_all(dma->chan_using); return -ETIMEDOUT; } diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c index f2b0ff011631c9..f994712d090473 100644 --- a/drivers/i2c/busses/i2c-ismt.c +++ b/drivers/i2c/busses/i2c-ismt.c @@ -380,6 +380,7 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr, int size, union i2c_smbus_data *data) { int ret; + unsigned long time_left; dma_addr_t dma_addr = 0; /* address of the data buffer */ u8 dma_size = 0; enum dma_data_direction dma_direction = 0; @@ -578,13 +579,13 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr, ismt_submit_desc(priv); /* Now we wait for interrupt completion, 1s */ - ret = wait_for_completion_timeout(&priv->cmp, HZ*1); + time_left = wait_for_completion_timeout(&priv->cmp, HZ*1); /* unmap the data buffer */ if (dma_size != 0) dma_unmap_single(&adap->dev, dma_addr, dma_size, dma_direction); - if (unlikely(!ret)) { + if (unlikely(!time_left)) { dev_err(dev, "completion wait timed out\n"); ret = -ETIMEDOUT; goto out; diff --git a/drivers/i2c/busses/i2c-jz4780.c b/drivers/i2c/busses/i2c-jz4780.c new file mode 100644 index 00000000000000..ce1d6932416990 --- /dev/null +++ b/drivers/i2c/busses/i2c-jz4780.c @@ -0,0 +1,832 @@ +/* + * Ingenic JZ4780 I2C bus driver + * + * Copyright (C) 2006 - 2009 Ingenic Semiconductor Inc. + * Copyright (C) 2015 Imagination Technologies + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JZ4780_I2C_CTRL 0x00 +#define JZ4780_I2C_TAR 0x04 +#define JZ4780_I2C_SAR 0x08 +#define JZ4780_I2C_DC 0x10 +#define JZ4780_I2C_SHCNT 0x14 +#define JZ4780_I2C_SLCNT 0x18 +#define JZ4780_I2C_FHCNT 0x1C +#define JZ4780_I2C_FLCNT 0x20 +#define JZ4780_I2C_INTST 0x2C +#define JZ4780_I2C_INTM 0x30 +#define JZ4780_I2C_RXTL 0x38 +#define JZ4780_I2C_TXTL 0x3C +#define JZ4780_I2C_CINTR 0x40 +#define JZ4780_I2C_CRXUF 0x44 +#define JZ4780_I2C_CRXOF 0x48 +#define JZ4780_I2C_CTXOF 0x4C +#define JZ4780_I2C_CRXREQ 0x50 +#define JZ4780_I2C_CTXABRT 0x54 +#define JZ4780_I2C_CRXDONE 0x58 +#define JZ4780_I2C_CACT 0x5C +#define JZ4780_I2C_CSTP 0x60 +#define JZ4780_I2C_CSTT 0x64 +#define JZ4780_I2C_CGC 0x68 +#define JZ4780_I2C_ENB 0x6C +#define JZ4780_I2C_STA 0x70 +#define JZ4780_I2C_TXABRT 0x80 +#define JZ4780_I2C_DMACR 0x88 +#define JZ4780_I2C_DMATDLR 0x8C +#define JZ4780_I2C_DMARDLR 0x90 +#define JZ4780_I2C_SDASU 0x94 +#define JZ4780_I2C_ACKGC 0x98 +#define JZ4780_I2C_ENSTA 0x9C +#define JZ4780_I2C_SDAHD 0xD0 + +#define JZ4780_I2C_CTRL_STPHLD BIT(7) +#define JZ4780_I2C_CTRL_SLVDIS BIT(6) +#define JZ4780_I2C_CTRL_REST BIT(5) +#define JZ4780_I2C_CTRL_MATP BIT(4) +#define JZ4780_I2C_CTRL_SATP BIT(3) +#define JZ4780_I2C_CTRL_SPDF BIT(2) +#define JZ4780_I2C_CTRL_SPDS BIT(1) +#define JZ4780_I2C_CTRL_MD BIT(0) + +#define JZ4780_I2C_STA_SLVACT BIT(6) +#define JZ4780_I2C_STA_MSTACT BIT(5) +#define JZ4780_I2C_STA_RFF BIT(4) +#define JZ4780_I2C_STA_RFNE BIT(3) +#define JZ4780_I2C_STA_TFE BIT(2) +#define JZ4780_I2C_STA_TFNF BIT(1) +#define JZ4780_I2C_STA_ACT BIT(0) + +static const char * const jz4780_i2c_abrt_src[] = { + "ABRT_7B_ADDR_NOACK", + "ABRT_10ADDR1_NOACK", + "ABRT_10ADDR2_NOACK", + "ABRT_XDATA_NOACK", + "ABRT_GCALL_NOACK", + "ABRT_GCALL_READ", + "ABRT_HS_ACKD", + "SBYTE_ACKDET", + "ABRT_HS_NORSTRT", + "SBYTE_NORSTRT", + "ABRT_10B_RD_NORSTRT", + "ABRT_MASTER_DIS", + "ARB_LOST", + "SLVFLUSH_TXFIFO", + "SLV_ARBLOST", + "SLVRD_INTX", +}; + +#define JZ4780_I2C_INTST_IGC BIT(11) +#define JZ4780_I2C_INTST_ISTT BIT(10) +#define JZ4780_I2C_INTST_ISTP BIT(9) +#define JZ4780_I2C_INTST_IACT BIT(8) +#define JZ4780_I2C_INTST_RXDN BIT(7) +#define JZ4780_I2C_INTST_TXABT BIT(6) +#define JZ4780_I2C_INTST_RDREQ BIT(5) +#define JZ4780_I2C_INTST_TXEMP BIT(4) +#define JZ4780_I2C_INTST_TXOF BIT(3) +#define JZ4780_I2C_INTST_RXFL BIT(2) +#define JZ4780_I2C_INTST_RXOF BIT(1) +#define JZ4780_I2C_INTST_RXUF BIT(0) + +#define JZ4780_I2C_INTM_MIGC BIT(11) +#define JZ4780_I2C_INTM_MISTT BIT(10) +#define JZ4780_I2C_INTM_MISTP BIT(9) +#define JZ4780_I2C_INTM_MIACT BIT(8) +#define JZ4780_I2C_INTM_MRXDN BIT(7) +#define JZ4780_I2C_INTM_MTXABT BIT(6) +#define JZ4780_I2C_INTM_MRDREQ BIT(5) +#define JZ4780_I2C_INTM_MTXEMP BIT(4) +#define JZ4780_I2C_INTM_MTXOF BIT(3) +#define JZ4780_I2C_INTM_MRXFL BIT(2) +#define JZ4780_I2C_INTM_MRXOF BIT(1) +#define JZ4780_I2C_INTM_MRXUF BIT(0) + +#define JZ4780_I2C_DC_READ BIT(8) + +#define JZ4780_I2C_SDAHD_HDENB BIT(8) + +#define JZ4780_I2C_ENB_I2C BIT(0) + +#define JZ4780_I2CSHCNT_ADJUST(n) (((n) - 8) < 6 ? 6 : ((n) - 8)) +#define JZ4780_I2CSLCNT_ADJUST(n) (((n) - 1) < 8 ? 8 : ((n) - 1)) +#define JZ4780_I2CFHCNT_ADJUST(n) (((n) - 8) < 6 ? 6 : ((n) - 8)) +#define JZ4780_I2CFLCNT_ADJUST(n) (((n) - 1) < 8 ? 8 : ((n) - 1)) + +#define JZ4780_I2C_FIFO_LEN 16 +#define TX_LEVEL 3 +#define RX_LEVEL (JZ4780_I2C_FIFO_LEN - TX_LEVEL - 1) + +#define JZ4780_I2C_TIMEOUT 300 + +#define BUFSIZE 200 + +struct jz4780_i2c { + void __iomem *iomem; + int irq; + struct clk *clk; + struct i2c_adapter adap; + + /* lock to protect rbuf and wbuf between xfer_rd/wr and irq handler */ + spinlock_t lock; + + /* beginning of lock scope */ + unsigned char *rbuf; + int rd_total_len; + int rd_data_xfered; + int rd_cmd_xfered; + + unsigned char *wbuf; + int wt_len; + + int is_write; + int stop_hold; + int speed; + + int data_buf[BUFSIZE]; + int cmd_buf[BUFSIZE]; + int cmd; + + /* end of lock scope */ + struct completion trans_waitq; +}; + +static inline unsigned short jz4780_i2c_readw(struct jz4780_i2c *i2c, + unsigned long offset) +{ + return readw(i2c->iomem + offset); +} + +static inline void jz4780_i2c_writew(struct jz4780_i2c *i2c, + unsigned long offset, unsigned short val) +{ + writew(val, i2c->iomem + offset); +} + +static int jz4780_i2c_disable(struct jz4780_i2c *i2c) +{ + unsigned short regval; + unsigned long loops = 5; + + jz4780_i2c_writew(i2c, JZ4780_I2C_ENB, 0); + + do { + regval = jz4780_i2c_readw(i2c, JZ4780_I2C_ENSTA); + if (!(regval & JZ4780_I2C_ENB_I2C)) + return 0; + + usleep_range(5000, 15000); + } while (--loops); + + dev_err(&i2c->adap.dev, "disable failed: ENSTA=0x%04x\n", regval); + return -ETIMEDOUT; +} + +static int jz4780_i2c_enable(struct jz4780_i2c *i2c) +{ + unsigned short regval; + unsigned long loops = 5; + + jz4780_i2c_writew(i2c, JZ4780_I2C_ENB, 1); + + do { + regval = jz4780_i2c_readw(i2c, JZ4780_I2C_ENSTA); + if (regval & JZ4780_I2C_ENB_I2C) + return 0; + + usleep_range(5000, 15000); + } while (--loops); + + dev_err(&i2c->adap.dev, "enable failed: ENSTA=0x%04x\n", regval); + return -ETIMEDOUT; +} + +static int jz4780_i2c_set_target(struct jz4780_i2c *i2c, unsigned char address) +{ + unsigned short regval; + unsigned long loops = 5; + + do { + regval = jz4780_i2c_readw(i2c, JZ4780_I2C_STA); + if ((regval & JZ4780_I2C_STA_TFE) && + !(regval & JZ4780_I2C_STA_MSTACT)) + break; + + usleep_range(5000, 15000); + } while (--loops); + + if (loops) { + jz4780_i2c_writew(i2c, JZ4780_I2C_TAR, address); + return 0; + } + + dev_err(&i2c->adap.dev, + "set device to address 0x%02x failed, STA=0x%04x\n", + address, regval); + + return -ENXIO; +} + +static int jz4780_i2c_set_speed(struct jz4780_i2c *i2c) +{ + int dev_clk_khz = clk_get_rate(i2c->clk) / 1000; + int cnt_high = 0; /* HIGH period count of the SCL clock */ + int cnt_low = 0; /* LOW period count of the SCL clock */ + int cnt_period = 0; /* period count of the SCL clock */ + int setup_time = 0; + int hold_time = 0; + unsigned short tmp = 0; + int i2c_clk = i2c->speed; + + if (jz4780_i2c_disable(i2c)) + dev_dbg(&i2c->adap.dev, "i2c not disabled\n"); + + /* + * 1 JZ4780_I2C cycle equals to cnt_period PCLK(i2c_clk) + * standard mode, min LOW and HIGH period are 4700 ns and 4000 ns + * fast mode, min LOW and HIGH period are 1300 ns and 600 ns + */ + cnt_period = dev_clk_khz / i2c_clk; + + if (i2c_clk <= 100) + cnt_high = (cnt_period * 4000) / (4700 + 4000); + else + cnt_high = (cnt_period * 600) / (1300 + 600); + + cnt_low = cnt_period - cnt_high; + + /* + * NOTE: JZ4780_I2C_CTRL_REST can't set when i2c enabled, because + * normal read are 2 messages, we cannot disable i2c controller + * between these two messages, this means that we must always set + * JZ4780_I2C_CTRL_REST when init JZ4780_I2C_CTRL + * + */ + if (i2c_clk <= 100) { + tmp = JZ4780_I2C_CTRL_SPDS | JZ4780_I2C_CTRL_REST + | JZ4780_I2C_CTRL_SLVDIS | JZ4780_I2C_CTRL_MD; + jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp); + + jz4780_i2c_writew(i2c, JZ4780_I2C_SHCNT, + JZ4780_I2CSHCNT_ADJUST(cnt_high)); + jz4780_i2c_writew(i2c, JZ4780_I2C_SLCNT, + JZ4780_I2CSLCNT_ADJUST(cnt_low)); + } else { + tmp = JZ4780_I2C_CTRL_SPDF | JZ4780_I2C_CTRL_REST + | JZ4780_I2C_CTRL_SLVDIS | JZ4780_I2C_CTRL_MD; + jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp); + + jz4780_i2c_writew(i2c, JZ4780_I2C_FHCNT, + JZ4780_I2CFHCNT_ADJUST(cnt_high)); + jz4780_i2c_writew(i2c, JZ4780_I2C_FLCNT, + JZ4780_I2CFLCNT_ADJUST(cnt_low)); + } + + /* + * a i2c device must internally provide a hold time at least 300ns + * tHD:DAT + * Standard Mode: min=300ns, max=3450ns + * Fast Mode: min=0ns, max=900ns + * tSU:DAT + * Standard Mode: min=250ns, max=infinite + * Fast Mode: min=100(250ns is recommended), max=infinite + * + * 1i2c_clk = 10^6 / dev_clk_khz + * on FPGA, dev_clk_khz = 12000, so 1i2c_clk = 1000/12 = 83ns + * on Pisces(1008M), dev_clk_khz=126000, so 1i2c_clk = 1000 / 126 = 8ns + * + * The actual hold time is (SDAHD + 1) * (i2c_clk period). + * + * Length of setup time calculated using (SDASU - 1) * (ic_clk_period) + * + */ + if (i2c_clk <= 100) { /* standard mode */ + setup_time = 300; + hold_time = 400; + } else { + setup_time = 450; + hold_time = 450; + } + + hold_time = ((hold_time * dev_clk_khz) / 1000000) - 1; + setup_time = ((setup_time * dev_clk_khz) / 1000000) + 1; + + if (setup_time > 255) + setup_time = 255; + + if (setup_time <= 0) + setup_time = 1; + + jz4780_i2c_writew(i2c, JZ4780_I2C_SDASU, setup_time); + + if (hold_time > 255) + hold_time = 255; + + if (hold_time >= 0) { + /*i2c hold time enable */ + hold_time |= JZ4780_I2C_SDAHD_HDENB; + jz4780_i2c_writew(i2c, JZ4780_I2C_SDAHD, hold_time); + } else { + /* disable hold time */ + jz4780_i2c_writew(i2c, JZ4780_I2C_SDAHD, 0); + } + + return 0; +} + +static int jz4780_i2c_cleanup(struct jz4780_i2c *i2c) +{ + int ret; + unsigned long flags; + unsigned short tmp; + + spin_lock_irqsave(&i2c->lock, flags); + + /* can send stop now if need */ + tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL); + tmp &= ~JZ4780_I2C_CTRL_STPHLD; + jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp); + + /* disable all interrupts first */ + jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, 0); + + /* then clear all interrupts */ + jz4780_i2c_readw(i2c, JZ4780_I2C_CTXABRT); + jz4780_i2c_readw(i2c, JZ4780_I2C_CINTR); + + /* then disable the controller */ + tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL); + tmp &= ~JZ4780_I2C_ENB_I2C; + jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp); + udelay(10); + tmp |= JZ4780_I2C_ENB_I2C; + jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp); + + spin_unlock_irqrestore(&i2c->lock, flags); + + ret = jz4780_i2c_disable(i2c); + if (ret) + dev_err(&i2c->adap.dev, + "unable to disable device during cleanup!\n"); + + if (unlikely(jz4780_i2c_readw(i2c, JZ4780_I2C_INTM) + & jz4780_i2c_readw(i2c, JZ4780_I2C_INTST))) + dev_err(&i2c->adap.dev, + "device has interrupts after a complete cleanup!\n"); + + return ret; +} + +static int jz4780_i2c_prepare(struct jz4780_i2c *i2c) +{ + jz4780_i2c_set_speed(i2c); + return jz4780_i2c_enable(i2c); +} + +static void jz4780_i2c_send_rcmd(struct jz4780_i2c *i2c, int cmd_count) +{ + int i; + + for (i = 0; i < cmd_count; i++) + jz4780_i2c_writew(i2c, JZ4780_I2C_DC, JZ4780_I2C_DC_READ); +} + +static void jz4780_i2c_trans_done(struct jz4780_i2c *i2c) +{ + jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, 0); + complete(&i2c->trans_waitq); +} + +static irqreturn_t jz4780_i2c_irq(int irqno, void *dev_id) +{ + unsigned short tmp; + unsigned short intst; + unsigned short intmsk; + struct jz4780_i2c *i2c = dev_id; + unsigned long flags; + + spin_lock_irqsave(&i2c->lock, flags); + intmsk = jz4780_i2c_readw(i2c, JZ4780_I2C_INTM); + intst = jz4780_i2c_readw(i2c, JZ4780_I2C_INTST); + + intst &= intmsk; + + if (intst & JZ4780_I2C_INTST_TXABT) { + jz4780_i2c_trans_done(i2c); + goto done; + } + + if (intst & JZ4780_I2C_INTST_RXOF) { + dev_dbg(&i2c->adap.dev, "received fifo overflow!\n"); + jz4780_i2c_trans_done(i2c); + goto done; + } + + /* + * When reading, always drain RX FIFO before we send more Read + * Commands to avoid fifo overrun + */ + if (i2c->is_write == 0) { + int rd_left; + + while ((jz4780_i2c_readw(i2c, JZ4780_I2C_STA) + & JZ4780_I2C_STA_RFNE)) { + *(i2c->rbuf++) = jz4780_i2c_readw(i2c, JZ4780_I2C_DC) + & 0xff; + i2c->rd_data_xfered++; + if (i2c->rd_data_xfered == i2c->rd_total_len) { + jz4780_i2c_trans_done(i2c); + goto done; + } + } + + rd_left = i2c->rd_total_len - i2c->rd_data_xfered; + + if (rd_left <= JZ4780_I2C_FIFO_LEN) + jz4780_i2c_writew(i2c, JZ4780_I2C_RXTL, rd_left - 1); + } + + if (intst & JZ4780_I2C_INTST_TXEMP) { + if (i2c->is_write == 0) { + int cmd_left = i2c->rd_total_len - i2c->rd_cmd_xfered; + int max_send = (JZ4780_I2C_FIFO_LEN - 1) + - (i2c->rd_cmd_xfered + - i2c->rd_data_xfered); + int cmd_to_send = min(cmd_left, max_send); + + if (i2c->rd_cmd_xfered != 0) + cmd_to_send = min(cmd_to_send, + JZ4780_I2C_FIFO_LEN + - TX_LEVEL - 1); + + if (cmd_to_send) { + jz4780_i2c_send_rcmd(i2c, cmd_to_send); + i2c->rd_cmd_xfered += cmd_to_send; + } + + cmd_left = i2c->rd_total_len - i2c->rd_cmd_xfered; + if (cmd_left == 0) { + intmsk = jz4780_i2c_readw(i2c, JZ4780_I2C_INTM); + intmsk &= ~JZ4780_I2C_INTM_MTXEMP; + jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, intmsk); + + tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL); + tmp &= ~JZ4780_I2C_CTRL_STPHLD; + jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp); + } + } else { + unsigned short data; + unsigned short i2c_sta; + + i2c_sta = jz4780_i2c_readw(i2c, JZ4780_I2C_STA); + + while ((i2c_sta & JZ4780_I2C_STA_TFNF) && + (i2c->wt_len > 0)) { + i2c_sta = jz4780_i2c_readw(i2c, JZ4780_I2C_STA); + data = *i2c->wbuf; + data &= ~JZ4780_I2C_DC_READ; + jz4780_i2c_writew(i2c, JZ4780_I2C_DC, + data); + i2c->wbuf++; + i2c->wt_len--; + } + + if (i2c->wt_len == 0) { + if (!i2c->stop_hold) { + tmp = jz4780_i2c_readw(i2c, + JZ4780_I2C_CTRL); + tmp &= ~JZ4780_I2C_CTRL_STPHLD; + jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, + tmp); + } + + jz4780_i2c_trans_done(i2c); + goto done; + } + } + } + +done: + spin_unlock_irqrestore(&i2c->lock, flags); + return IRQ_HANDLED; +} + +static void jz4780_i2c_txabrt(struct jz4780_i2c *i2c, int src) +{ + int i; + + dev_err(&i2c->adap.dev, "txabrt: 0x%08x\n", src); + dev_err(&i2c->adap.dev, "device addr=%x\n", + jz4780_i2c_readw(i2c, JZ4780_I2C_TAR)); + dev_err(&i2c->adap.dev, "send cmd count:%d %d\n", + i2c->cmd, i2c->cmd_buf[i2c->cmd]); + dev_err(&i2c->adap.dev, "receive data count:%d %d\n", + i2c->cmd, i2c->data_buf[i2c->cmd]); + + for (i = 0; i < 16; i++) { + if (src & BIT(i)) + dev_dbg(&i2c->adap.dev, "I2C TXABRT[%d]=%s\n", + i, jz4780_i2c_abrt_src[i]); + } +} + +static inline int jz4780_i2c_xfer_read(struct jz4780_i2c *i2c, + unsigned char *buf, int len, int cnt, + int idx) +{ + int ret = 0; + long timeout; + int wait_time = JZ4780_I2C_TIMEOUT * (len + 5); + unsigned short tmp; + unsigned long flags; + + memset(buf, 0, len); + + spin_lock_irqsave(&i2c->lock, flags); + + i2c->stop_hold = 0; + i2c->is_write = 0; + i2c->rbuf = buf; + i2c->rd_total_len = len; + i2c->rd_data_xfered = 0; + i2c->rd_cmd_xfered = 0; + + if (len <= JZ4780_I2C_FIFO_LEN) + jz4780_i2c_writew(i2c, JZ4780_I2C_RXTL, len - 1); + else + jz4780_i2c_writew(i2c, JZ4780_I2C_RXTL, RX_LEVEL); + + jz4780_i2c_writew(i2c, JZ4780_I2C_TXTL, TX_LEVEL); + + jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, + JZ4780_I2C_INTM_MRXFL | JZ4780_I2C_INTM_MTXEMP + | JZ4780_I2C_INTM_MTXABT | JZ4780_I2C_INTM_MRXOF); + + tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL); + tmp |= JZ4780_I2C_CTRL_STPHLD; + jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp); + + spin_unlock_irqrestore(&i2c->lock, flags); + + timeout = wait_for_completion_timeout(&i2c->trans_waitq, + msecs_to_jiffies(wait_time)); + + if (!timeout) { + dev_err(&i2c->adap.dev, "irq read timeout\n"); + dev_dbg(&i2c->adap.dev, "send cmd count:%d %d\n", + i2c->cmd, i2c->cmd_buf[i2c->cmd]); + dev_dbg(&i2c->adap.dev, "receive data count:%d %d\n", + i2c->cmd, i2c->data_buf[i2c->cmd]); + ret = -EIO; + } + + tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_TXABRT); + if (tmp) { + jz4780_i2c_txabrt(i2c, tmp); + ret = -EIO; + } + + return ret; +} + +static inline int jz4780_i2c_xfer_write(struct jz4780_i2c *i2c, + unsigned char *buf, int len, + int cnt, int idx) +{ + int ret = 0; + int wait_time = JZ4780_I2C_TIMEOUT * (len + 5); + long timeout; + unsigned short tmp; + unsigned long flags; + + spin_lock_irqsave(&i2c->lock, flags); + + if (idx < (cnt - 1)) + i2c->stop_hold = 1; + else + i2c->stop_hold = 0; + + i2c->is_write = 1; + i2c->wbuf = buf; + i2c->wt_len = len; + + jz4780_i2c_writew(i2c, JZ4780_I2C_TXTL, TX_LEVEL); + + jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, JZ4780_I2C_INTM_MTXEMP + | JZ4780_I2C_INTM_MTXABT); + + tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL); + tmp |= JZ4780_I2C_CTRL_STPHLD; + jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp); + + spin_unlock_irqrestore(&i2c->lock, flags); + + timeout = wait_for_completion_timeout(&i2c->trans_waitq, + msecs_to_jiffies(wait_time)); + if (timeout && !i2c->stop_hold) { + unsigned short i2c_sta; + int write_in_process; + + timeout = JZ4780_I2C_TIMEOUT * 100; + for (; timeout > 0; timeout--) { + i2c_sta = jz4780_i2c_readw(i2c, JZ4780_I2C_STA); + + write_in_process = (i2c_sta & JZ4780_I2C_STA_MSTACT) || + !(i2c_sta & JZ4780_I2C_STA_TFE); + if (!write_in_process) + break; + udelay(10); + } + } + + if (!timeout) { + dev_err(&i2c->adap.dev, "write wait timeout\n"); + ret = -EIO; + } + + tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_TXABRT); + if (tmp) { + jz4780_i2c_txabrt(i2c, tmp); + ret = -EIO; + } + + return ret; +} + +static int jz4780_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, + int count) +{ + int i = -EIO; + int ret = 0; + struct jz4780_i2c *i2c = adap->algo_data; + + ret = jz4780_i2c_prepare(i2c); + if (ret) { + dev_err(&i2c->adap.dev, "I2C prepare failed\n"); + goto out; + } + + if (msg->addr != jz4780_i2c_readw(i2c, JZ4780_I2C_TAR)) { + ret = jz4780_i2c_set_target(i2c, msg->addr); + if (ret) + goto out; + } + for (i = 0; i < count; i++, msg++) { + if (msg->flags & I2C_M_RD) + ret = jz4780_i2c_xfer_read(i2c, msg->buf, msg->len, + count, i); + else + ret = jz4780_i2c_xfer_write(i2c, msg->buf, msg->len, + count, i); + + if (ret) + goto out; + } + + ret = i; + +out: + jz4780_i2c_cleanup(i2c); + return ret; +} + +static u32 jz4780_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm jz4780_i2c_algorithm = { + .master_xfer = jz4780_i2c_xfer, + .functionality = jz4780_i2c_functionality, +}; + +static const struct of_device_id jz4780_i2c_of_matches[] = { + { .compatible = "ingenic,jz4780-i2c", }, + { /* sentinel */ } +}; + +static int jz4780_i2c_probe(struct platform_device *pdev) +{ + int ret = 0; + unsigned int clk_freq = 0; + unsigned short tmp; + struct resource *r; + struct jz4780_i2c *i2c; + + i2c = devm_kzalloc(&pdev->dev, sizeof(struct jz4780_i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &jz4780_i2c_algorithm; + i2c->adap.algo_data = i2c; + i2c->adap.retries = 5; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = pdev->dev.of_node; + sprintf(i2c->adap.name, "%s", pdev->name); + + init_completion(&i2c->trans_waitq); + spin_lock_init(&i2c->lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2c->iomem = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(i2c->iomem)) + return PTR_ERR(i2c->iomem); + + platform_set_drvdata(pdev, i2c); + + i2c->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(i2c->clk)) + return PTR_ERR(i2c->clk); + + clk_prepare_enable(i2c->clk); + + if (of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &clk_freq)) { + dev_err(&pdev->dev, "clock-frequency not specified in DT"); + return clk_freq; + } + + i2c->speed = clk_freq / 1000; + jz4780_i2c_set_speed(i2c); + + dev_info(&pdev->dev, "Bus frequency is %d KHz\n", i2c->speed); + + tmp = jz4780_i2c_readw(i2c, JZ4780_I2C_CTRL); + tmp &= ~JZ4780_I2C_CTRL_STPHLD; + jz4780_i2c_writew(i2c, JZ4780_I2C_CTRL, tmp); + + jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, 0x0); + + i2c->cmd = 0; + memset(i2c->cmd_buf, 0, BUFSIZE); + memset(i2c->data_buf, 0, BUFSIZE); + + i2c->irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(&pdev->dev, i2c->irq, jz4780_i2c_irq, 0, + dev_name(&pdev->dev), i2c); + if (ret) { + ret = -ENODEV; + goto err; + } + + ret = i2c_add_adapter(&i2c->adap); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to add bus\n"); + goto err; + } + + return 0; + +err: + clk_disable_unprepare(i2c->clk); + return ret; +} + +static int jz4780_i2c_remove(struct platform_device *pdev) +{ + struct jz4780_i2c *i2c = platform_get_drvdata(pdev); + + clk_disable_unprepare(i2c->clk); + i2c_del_adapter(&i2c->adap); + return 0; +} + +static struct platform_driver jz4780_i2c_driver = { + .probe = jz4780_i2c_probe, + .remove = jz4780_i2c_remove, + .driver = { + .name = "jz4780-i2c", + .of_match_table = of_match_ptr(jz4780_i2c_of_matches), + }, +}; + +module_platform_driver(jz4780_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ztyan"); +MODULE_DESCRIPTION("i2c driver for JZ4780 SoCs"); diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index c74cc2be613b67..48ecffecc0eda3 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -29,6 +29,7 @@ #include #include +#include #include #define DRV_NAME "mpc-i2c" @@ -95,8 +96,9 @@ static irqreturn_t mpc_i2c_isr(int irq, void *dev_id) i2c->interrupt = readb(i2c->base + MPC_I2C_SR); writeb(0, i2c->base + MPC_I2C_SR); wake_up(&i2c->queue); + return IRQ_HANDLED; } - return IRQ_HANDLED; + return IRQ_NONE; } /* Sometimes 9th clock pulse isn't generated, and slave doesn't release @@ -346,6 +348,33 @@ static u32 mpc_i2c_get_sec_cfg_8xxx(void) return val; } +static u32 mpc_i2c_get_prescaler_8xxx(void) +{ + /* mpc83xx and mpc82xx all have prescaler 1 */ + u32 prescaler = 1; + + /* mpc85xx */ + if (pvr_version_is(PVR_VER_E500V1) || pvr_version_is(PVR_VER_E500V2) + || pvr_version_is(PVR_VER_E500MC) + || pvr_version_is(PVR_VER_E5500) + || pvr_version_is(PVR_VER_E6500)) { + unsigned int svr = mfspr(SPRN_SVR); + + if ((SVR_SOC_VER(svr) == SVR_8540) + || (SVR_SOC_VER(svr) == SVR_8541) + || (SVR_SOC_VER(svr) == SVR_8560) + || (SVR_SOC_VER(svr) == SVR_8555) + || (SVR_SOC_VER(svr) == SVR_8610)) + /* the above 85xx SoCs have prescaler 1 */ + prescaler = 1; + else + /* all the other 85xx have prescaler 2 */ + prescaler = 2; + } + + return prescaler; +} + static int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock, u32 prescaler, u32 *real_clk) { @@ -363,7 +392,7 @@ static int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock, if (of_device_is_compatible(node, "fsl,mpc8544-i2c")) prescaler = mpc_i2c_get_sec_cfg_8xxx() ? 3 : 2; if (!prescaler) - prescaler = 1; + prescaler = mpc_i2c_get_prescaler_8xxx(); divider = fsl_get_sys_freq() / clock / prescaler; diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index ff8b12c8d25f50..56fceff6ba1422 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -568,6 +568,7 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int ret; int flags; int use_pio = 0; + unsigned long time_left; flags = stop ? MXS_I2C_CTRL0_POST_SEND_STOP : 0; @@ -599,9 +600,9 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, if (ret) return ret; - ret = wait_for_completion_timeout(&i2c->cmd_complete, + time_left = wait_for_completion_timeout(&i2c->cmd_complete, msecs_to_jiffies(1000)); - if (ret == 0) + if (!time_left) goto timeout; ret = i2c->cmd_err; diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 97998946c4f64b..bcd17e8cbcb4b3 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -446,9 +446,9 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev) */ static int read_i2c(struct nmk_i2c_dev *dev, u16 flags) { - u32 status = 0; + int status = 0; u32 mcr, irq_mask; - int timeout; + unsigned long timeout; mcr = load_i2c_mcr_reg(dev, flags); writel(mcr, dev->virtbase + I2C_MCR); @@ -517,7 +517,7 @@ static int write_i2c(struct nmk_i2c_dev *dev, u16 flags) { u32 status = 0; u32 mcr, irq_mask; - int timeout; + unsigned long timeout; mcr = load_i2c_mcr_reg(dev, flags); diff --git a/drivers/i2c/busses/i2c-opal.c b/drivers/i2c/busses/i2c-opal.c index 16f90b1a750894..75dd6d04124128 100644 --- a/drivers/i2c/busses/i2c-opal.c +++ b/drivers/i2c/busses/i2c-opal.c @@ -104,18 +104,8 @@ static int i2c_opal_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, req.buffer_ra = cpu_to_be64(__pa(msgs[0].buf)); break; case 2: - /* For two messages, we basically support only simple - * smbus transactions of a write plus a read. We might - * want to allow also two writes but we'd have to bounce - * the data into a single buffer. - */ - if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD)) - return -EOPNOTSUPP; - if (msgs[0].len > 4) - return -EOPNOTSUPP; - if (msgs[0].addr != msgs[1].addr) - return -EOPNOTSUPP; - req.type = OPAL_I2C_SM_READ; + req.type = (msgs[1].flags & I2C_M_RD) ? + OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE; req.addr = cpu_to_be16(msgs[0].addr); req.subaddr_sz = msgs[0].len; for (i = 0; i < msgs[0].len; i++) @@ -210,6 +200,15 @@ static const struct i2c_algorithm i2c_opal_algo = { .functionality = i2c_opal_func, }; +/* + * For two messages, we basically support simple smbus transactions of a + * write-then-anything. + */ +static struct i2c_adapter_quirks i2c_opal_quirks = { + .flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | I2C_AQ_COMB_SAME_ADDR, + .max_comb_1st_msg_len = 4, +}; + static int i2c_opal_probe(struct platform_device *pdev) { struct i2c_adapter *adapter; @@ -232,6 +231,7 @@ static int i2c_opal_probe(struct platform_device *pdev) adapter->algo = &i2c_opal_algo; adapter->algo_data = (void *)(unsigned long)opal_id; + adapter->quirks = &i2c_opal_quirks; adapter->dev.parent = &pdev->dev; adapter->dev.of_node = of_node_get(pdev->dev.of_node); pname = of_get_property(pdev->dev.of_node, "ibm,port-name", NULL); diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c index d37d9db6681e7b..2c40edbf6224eb 100644 --- a/drivers/i2c/busses/i2c-pmcmsp.c +++ b/drivers/i2c/busses/i2c-pmcmsp.c @@ -456,14 +456,6 @@ static enum pmcmsptwi_xfer_result pmcmsptwi_xfer_cmd( return -EINVAL; } - if (cmd->read_len > MSP_MAX_BYTES_PER_RW || - cmd->write_len > MSP_MAX_BYTES_PER_RW) { - dev_err(&pmcmsptwi_adapter.dev, - "%s: Cannot transfer more than %d bytes\n", - __func__, MSP_MAX_BYTES_PER_RW); - return -EINVAL; - } - mutex_lock(&data->lock); dev_dbg(&pmcmsptwi_adapter.dev, "Setting address to 0x%04x\n", cmd->addr); @@ -520,25 +512,14 @@ static int pmcmsptwi_master_xfer(struct i2c_adapter *adap, struct pmcmsptwi_cfg oldcfg, newcfg; int ret; - if (num > 2) { - dev_dbg(&adap->dev, "%d messages unsupported\n", num); - return -EINVAL; - } else if (num == 2) { - /* Check for a dual write-then-read command */ + if (num == 2) { struct i2c_msg *nextmsg = msg + 1; - if (!(msg->flags & I2C_M_RD) && - (nextmsg->flags & I2C_M_RD) && - msg->addr == nextmsg->addr) { - cmd.type = MSP_TWI_CMD_WRITE_READ; - cmd.write_len = msg->len; - cmd.write_data = msg->buf; - cmd.read_len = nextmsg->len; - cmd.read_data = nextmsg->buf; - } else { - dev_dbg(&adap->dev, - "Non write-read dual messages unsupported\n"); - return -EINVAL; - } + + cmd.type = MSP_TWI_CMD_WRITE_READ; + cmd.write_len = msg->len; + cmd.write_data = msg->buf; + cmd.read_len = nextmsg->len; + cmd.read_data = nextmsg->buf; } else if (msg->flags & I2C_M_RD) { cmd.type = MSP_TWI_CMD_READ; cmd.read_len = msg->len; @@ -598,6 +579,14 @@ static u32 pmcmsptwi_i2c_func(struct i2c_adapter *adapter) I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL; } +static struct i2c_adapter_quirks pmcmsptwi_i2c_quirks = { + .flags = I2C_AQ_COMB_WRITE_THEN_READ, + .max_write_len = MSP_MAX_BYTES_PER_RW, + .max_read_len = MSP_MAX_BYTES_PER_RW, + .max_comb_1st_msg_len = MSP_MAX_BYTES_PER_RW, + .max_comb_2nd_msg_len = MSP_MAX_BYTES_PER_RW, +}; + /* -- Initialization -- */ static struct i2c_algorithm pmcmsptwi_algo = { @@ -609,6 +598,7 @@ static struct i2c_adapter pmcmsptwi_adapter = { .owner = THIS_MODULE, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &pmcmsptwi_algo, + .quirks = &pmcmsptwi_i2c_quirks, .name = DRV_NAME, }; diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 60a53c169ed2b3..6abcf696e3594b 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -153,12 +153,6 @@ static int i2c_powermac_master_xfer( struct i2c_adapter *adap, int read; int addrdir; - if (num != 1) { - dev_err(&adap->dev, - "Multi-message I2C transactions not supported\n"); - return -EOPNOTSUPP; - } - if (msgs->flags & I2C_M_TEN) return -EINVAL; read = (msgs->flags & I2C_M_RD) != 0; @@ -205,6 +199,9 @@ static const struct i2c_algorithm i2c_powermac_algorithm = { .functionality = i2c_powermac_func, }; +static struct i2c_adapter_quirks i2c_powermac_quirks = { + .max_num_msgs = 1, +}; static int i2c_powermac_remove(struct platform_device *dev) { @@ -434,6 +431,7 @@ static int i2c_powermac_probe(struct platform_device *dev) platform_set_drvdata(dev, adapter); adapter->algo = &i2c_powermac_algorithm; + adapter->quirks = &i2c_powermac_quirks; i2c_set_adapdata(adapter, bus); adapter->dev.parent = &dev->dev; diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index 4dad23bdffbe90..fdcbdab808e9fc 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -412,17 +412,6 @@ static int qup_i2c_read_one(struct qup_i2c_dev *qup, struct i2c_msg *msg) unsigned long left; int ret; - /* - * The QUP block will issue a NACK and STOP on the bus when reaching - * the end of the read, the length of the read is specified as one byte - * which limits the possible read to 256 (QUP_READ_LIMIT) bytes. - */ - if (msg->len > QUP_READ_LIMIT) { - dev_err(qup->dev, "HW not capable of reads over %d bytes\n", - QUP_READ_LIMIT); - return -EINVAL; - } - qup->msg = msg; qup->pos = 0; @@ -534,6 +523,15 @@ static const struct i2c_algorithm qup_i2c_algo = { .functionality = qup_i2c_func, }; +/* + * The QUP block will issue a NACK and STOP on the bus when reaching + * the end of the read, the length of the read is specified as one byte + * which limits the possible read to 256 (QUP_READ_LIMIT) bytes. + */ +static struct i2c_adapter_quirks qup_i2c_quirks = { + .max_read_len = QUP_READ_LIMIT, +}; + static void qup_i2c_enable_clocks(struct qup_i2c_dev *qup) { clk_prepare_enable(qup->clk); @@ -670,6 +668,7 @@ static int qup_i2c_probe(struct platform_device *pdev) i2c_set_adapdata(&qup->adap, qup); qup->adap.algo = &qup_i2c_algo; + qup->adap.quirks = &qup_i2c_quirks; qup->adap.dev.parent = qup->dev; qup->adap.dev.of_node = pdev->dev.of_node; strlcpy(qup->adap.name, "QUP I2C adapter", sizeof(qup->adap.name)); diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 71a6e07eb7ab7c..5a84bea5b84514 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -382,11 +382,11 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv) if (ssr_filtered & SAR) { /* read or write request */ if (ssr_raw & STM) { - i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_START, &value); + i2c_slave_event(priv->slave, I2C_SLAVE_READ_REQUESTED, &value); rcar_i2c_write(priv, ICRXTX, value); rcar_i2c_write(priv, ICSIER, SDE | SSR | SAR); } else { - i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_START, &value); + i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_REQUESTED, &value); rcar_i2c_read(priv, ICRXTX); /* dummy read */ rcar_i2c_write(priv, ICSIER, SDR | SSR | SAR); } @@ -406,17 +406,15 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv) int ret; value = rcar_i2c_read(priv, ICRXTX); - ret = i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_END, &value); + ret = i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_RECEIVED, &value); /* Send NACK in case of error */ rcar_i2c_write(priv, ICSCR, SIE | SDBS | (ret < 0 ? FNA : 0)); - i2c_slave_event(priv->slave, I2C_SLAVE_REQ_WRITE_START, &value); rcar_i2c_write(priv, ICSSR, ~SDR & 0xff); } /* master wants to read from us */ if (ssr_filtered & SDE) { - i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_END, &value); - i2c_slave_event(priv->slave, I2C_SLAVE_REQ_READ_START, &value); + i2c_slave_event(priv->slave, I2C_SLAVE_READ_PROCESSED, &value); rcar_i2c_write(priv, ICRXTX, value); rcar_i2c_write(priv, ICSSR, ~SDE & 0xff); } diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 29f14331dd9d01..1bcd75ea0b4cfd 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -532,7 +532,7 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, { u32 packet_header; u32 int_mask; - int ret; + unsigned long time_left; tegra_i2c_flush_fifos(i2c_dev); @@ -585,18 +585,20 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, dev_dbg(i2c_dev->dev, "unmasked irq: %02x\n", i2c_readl(i2c_dev, I2C_INT_MASK)); - ret = wait_for_completion_timeout(&i2c_dev->msg_complete, TEGRA_I2C_TIMEOUT); + time_left = wait_for_completion_timeout(&i2c_dev->msg_complete, + TEGRA_I2C_TIMEOUT); tegra_i2c_mask_irq(i2c_dev, int_mask); - if (ret == 0) { + if (time_left == 0) { dev_err(i2c_dev->dev, "i2c transfer timed out\n"); tegra_i2c_init(i2c_dev); return -ETIMEDOUT; } - dev_dbg(i2c_dev->dev, "transfer complete: %d %d %d\n", - ret, completion_done(&i2c_dev->msg_complete), i2c_dev->msg_err); + dev_dbg(i2c_dev->dev, "transfer complete: %lu %d %d\n", + time_left, completion_done(&i2c_dev->msg_complete), + i2c_dev->msg_err); if (likely(i2c_dev->msg_err == I2C_ERR_NONE)) return 0; diff --git a/drivers/i2c/busses/i2c-viperboard.c b/drivers/i2c/busses/i2c-viperboard.c index 7533fa34d73711..47e88adf2011e1 100644 --- a/drivers/i2c/busses/i2c-viperboard.c +++ b/drivers/i2c/busses/i2c-viperboard.c @@ -288,10 +288,6 @@ static int vprbrd_i2c_xfer(struct i2c_adapter *i2c, struct i2c_msg *msgs, i, pmsg->flags & I2C_M_RD ? "read" : "write", pmsg->flags, pmsg->len, pmsg->addr); - /* msgs longer than 2048 bytes are not supported by adapter */ - if (pmsg->len > 2048) - return -EINVAL; - mutex_lock(&vb->lock); /* directly send the message */ if (pmsg->flags & I2C_M_RD) { @@ -358,6 +354,11 @@ static const struct i2c_algorithm vprbrd_algorithm = { .functionality = vprbrd_i2c_func, }; +static struct i2c_adapter_quirks vprbrd_quirks = { + .max_read_len = 2048, + .max_write_len = 2048, +}; + static int vprbrd_i2c_probe(struct platform_device *pdev) { struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent); @@ -373,6 +374,7 @@ static int vprbrd_i2c_probe(struct platform_device *pdev) vb_i2c->i2c.owner = THIS_MODULE; vb_i2c->i2c.class = I2C_CLASS_HWMON; vb_i2c->i2c.algo = &vprbrd_algorithm; + vb_i2c->i2c.quirks = &vprbrd_quirks; vb_i2c->i2c.algo_data = vb; /* save the param in usb capabable memory */ vb_i2c->bus_freq_param = i2c_bus_param; diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c index 82ea349254899c..e1e3a85596c562 100644 --- a/drivers/i2c/busses/i2c-wmt.c +++ b/drivers/i2c/busses/i2c-wmt.c @@ -128,7 +128,8 @@ static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg, { struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap); u16 val, tcr_val; - int ret, wait_result; + int ret; + unsigned long wait_result; int xfer_len = 0; if (!(pmsg->flags & I2C_M_NOSTART)) { @@ -177,7 +178,7 @@ static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg, while (xfer_len < pmsg->len) { wait_result = wait_for_completion_timeout(&i2c_dev->complete, - 500 * HZ / 1000); + msecs_to_jiffies(500)); if (wait_result == 0) return -ETIMEDOUT; @@ -218,7 +219,8 @@ static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg, { struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap); u16 val, tcr_val; - int ret, wait_result; + int ret; + unsigned long wait_result; u32 xfer_len = 0; if (!(pmsg->flags & I2C_M_NOSTART)) { @@ -266,7 +268,7 @@ static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg, while (xfer_len < pmsg->len) { wait_result = wait_for_completion_timeout(&i2c_dev->complete, - 500 * HZ / 1000); + msecs_to_jiffies(500)); if (!wait_result) return -ETIMEDOUT; diff --git a/drivers/i2c/busses/i2c-xlp9xx.c b/drivers/i2c/busses/i2c-xlp9xx.c new file mode 100644 index 00000000000000..c941418f06f562 --- /dev/null +++ b/drivers/i2c/busses/i2c-xlp9xx.c @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2003-2015 Broadcom Corporation + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define XLP9XX_I2C_DIV 0x0 +#define XLP9XX_I2C_CTRL 0x1 +#define XLP9XX_I2C_CMD 0x2 +#define XLP9XX_I2C_STATUS 0x3 +#define XLP9XX_I2C_MTXFIFO 0x4 +#define XLP9XX_I2C_MRXFIFO 0x5 +#define XLP9XX_I2C_MFIFOCTRL 0x6 +#define XLP9XX_I2C_STXFIFO 0x7 +#define XLP9XX_I2C_SRXFIFO 0x8 +#define XLP9XX_I2C_SFIFOCTRL 0x9 +#define XLP9XX_I2C_SLAVEADDR 0xA +#define XLP9XX_I2C_OWNADDR 0xB +#define XLP9XX_I2C_FIFOWCNT 0xC +#define XLP9XX_I2C_INTEN 0xD +#define XLP9XX_I2C_INTST 0xE +#define XLP9XX_I2C_WAITCNT 0xF +#define XLP9XX_I2C_TIMEOUT 0X10 +#define XLP9XX_I2C_GENCALLADDR 0x11 + +#define XLP9XX_I2C_CMD_START BIT(7) +#define XLP9XX_I2C_CMD_STOP BIT(6) +#define XLP9XX_I2C_CMD_READ BIT(5) +#define XLP9XX_I2C_CMD_WRITE BIT(4) +#define XLP9XX_I2C_CMD_ACK BIT(3) + +#define XLP9XX_I2C_CTRL_MCTLEN_SHIFT 16 +#define XLP9XX_I2C_CTRL_MCTLEN_MASK 0xffff0000 +#define XLP9XX_I2C_CTRL_RST BIT(8) +#define XLP9XX_I2C_CTRL_EN BIT(6) +#define XLP9XX_I2C_CTRL_MASTER BIT(4) +#define XLP9XX_I2C_CTRL_FIFORD BIT(1) +#define XLP9XX_I2C_CTRL_ADDMODE BIT(0) + +#define XLP9XX_I2C_INTEN_NACKADDR BIT(25) +#define XLP9XX_I2C_INTEN_SADDR BIT(13) +#define XLP9XX_I2C_INTEN_DATADONE BIT(12) +#define XLP9XX_I2C_INTEN_ARLOST BIT(11) +#define XLP9XX_I2C_INTEN_MFIFOFULL BIT(4) +#define XLP9XX_I2C_INTEN_MFIFOEMTY BIT(3) +#define XLP9XX_I2C_INTEN_MFIFOHI BIT(2) +#define XLP9XX_I2C_INTEN_BUSERR BIT(0) + +#define XLP9XX_I2C_MFIFOCTRL_HITH_SHIFT 8 +#define XLP9XX_I2C_MFIFOCTRL_LOTH_SHIFT 0 +#define XLP9XX_I2C_MFIFOCTRL_RST BIT(16) + +#define XLP9XX_I2C_SLAVEADDR_RW BIT(0) +#define XLP9XX_I2C_SLAVEADDR_ADDR_SHIFT 1 + +#define XLP9XX_I2C_IP_CLK_FREQ 133000000UL +#define XLP9XX_I2C_DEFAULT_FREQ 100000 +#define XLP9XX_I2C_HIGH_FREQ 400000 +#define XLP9XX_I2C_FIFO_SIZE 0x80U +#define XLP9XX_I2C_TIMEOUT_MS 1000 + +#define XLP9XX_I2C_FIFO_WCNT_MASK 0xff +#define XLP9XX_I2C_STATUS_ERRMASK (XLP9XX_I2C_INTEN_ARLOST | \ + XLP9XX_I2C_INTEN_NACKADDR | XLP9XX_I2C_INTEN_BUSERR) + +struct xlp9xx_i2c_dev { + struct device *dev; + struct i2c_adapter adapter; + struct completion msg_complete; + int irq; + bool msg_read; + u32 __iomem *base; + u32 msg_buf_remaining; + u32 msg_len; + u32 clk_hz; + u32 msg_err; + u8 *msg_buf; +}; + +static inline void xlp9xx_write_i2c_reg(struct xlp9xx_i2c_dev *priv, + unsigned long reg, u32 val) +{ + writel(val, priv->base + reg); +} + +static inline u32 xlp9xx_read_i2c_reg(struct xlp9xx_i2c_dev *priv, + unsigned long reg) +{ + return readl(priv->base + reg); +} + +static void xlp9xx_i2c_mask_irq(struct xlp9xx_i2c_dev *priv, u32 mask) +{ + u32 inten; + + inten = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_INTEN) & ~mask; + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, inten); +} + +static void xlp9xx_i2c_unmask_irq(struct xlp9xx_i2c_dev *priv, u32 mask) +{ + u32 inten; + + inten = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_INTEN) | mask; + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, inten); +} + +static void xlp9xx_i2c_update_rx_fifo_thres(struct xlp9xx_i2c_dev *priv) +{ + u32 thres; + + thres = min(priv->msg_buf_remaining, XLP9XX_I2C_FIFO_SIZE); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_MFIFOCTRL, + thres << XLP9XX_I2C_MFIFOCTRL_HITH_SHIFT); +} + +static void xlp9xx_i2c_fill_tx_fifo(struct xlp9xx_i2c_dev *priv) +{ + u32 len, i; + u8 *buf = priv->msg_buf; + + len = min(priv->msg_buf_remaining, XLP9XX_I2C_FIFO_SIZE); + for (i = 0; i < len; i++) + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_MTXFIFO, buf[i]); + priv->msg_buf_remaining -= len; + priv->msg_buf += len; +} + +static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv) +{ + u32 len, i; + u8 *buf = priv->msg_buf; + + len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) & + XLP9XX_I2C_FIFO_WCNT_MASK; + len = min(priv->msg_buf_remaining, len); + for (i = 0; i < len; i++, buf++) + *buf = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO); + + priv->msg_buf_remaining -= len; + priv->msg_buf = buf; + + if (priv->msg_buf_remaining) + xlp9xx_i2c_update_rx_fifo_thres(priv); +} + +static irqreturn_t xlp9xx_i2c_isr(int irq, void *dev_id) +{ + struct xlp9xx_i2c_dev *priv = dev_id; + u32 status; + + status = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_INTST); + if (status == 0) + return IRQ_NONE; + + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTST, status); + if (status & XLP9XX_I2C_STATUS_ERRMASK) { + priv->msg_err = status; + goto xfer_done; + } + + /* SADDR ACK for SMBUS_QUICK */ + if ((status & XLP9XX_I2C_INTEN_SADDR) && (priv->msg_len == 0)) + goto xfer_done; + + if (!priv->msg_read) { + if (status & XLP9XX_I2C_INTEN_MFIFOEMTY) { + /* TX FIFO got empty, fill it up again */ + if (priv->msg_buf_remaining) + xlp9xx_i2c_fill_tx_fifo(priv); + else + xlp9xx_i2c_mask_irq(priv, + XLP9XX_I2C_INTEN_MFIFOEMTY); + } + } else { + if (status & (XLP9XX_I2C_INTEN_DATADONE | + XLP9XX_I2C_INTEN_MFIFOHI)) { + /* data is in FIFO, read it */ + if (priv->msg_buf_remaining) + xlp9xx_i2c_drain_rx_fifo(priv); + } + } + + /* Transfer complete */ + if (status & XLP9XX_I2C_INTEN_DATADONE) + goto xfer_done; + + return IRQ_HANDLED; + +xfer_done: + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, 0); + complete(&priv->msg_complete); + return IRQ_HANDLED; +} + +static int xlp9xx_i2c_init(struct xlp9xx_i2c_dev *priv) +{ + u32 prescale; + + /* + * The controller uses 5 * SCL clock internally. + * So prescale value should be divided by 5. + */ + prescale = DIV_ROUND_UP(XLP9XX_I2C_IP_CLK_FREQ, priv->clk_hz); + prescale = ((prescale - 8) / 5) - 1; + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, XLP9XX_I2C_CTRL_RST); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, XLP9XX_I2C_CTRL_EN | + XLP9XX_I2C_CTRL_MASTER); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_DIV, prescale); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, 0); + + return 0; +} + +static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg, + int last_msg) +{ + unsigned long timeleft; + u32 intr_mask, cmd, val; + + priv->msg_buf = msg->buf; + priv->msg_buf_remaining = priv->msg_len = msg->len; + priv->msg_err = 0; + priv->msg_read = (msg->flags & I2C_M_RD); + reinit_completion(&priv->msg_complete); + + /* Reset FIFO */ + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_MFIFOCTRL, + XLP9XX_I2C_MFIFOCTRL_RST); + + /* set FIFO threshold if reading */ + if (priv->msg_read) + xlp9xx_i2c_update_rx_fifo_thres(priv); + + /* set slave addr */ + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_SLAVEADDR, + (msg->addr << XLP9XX_I2C_SLAVEADDR_ADDR_SHIFT) | + (priv->msg_read ? XLP9XX_I2C_SLAVEADDR_RW : 0)); + + /* Build control word for transfer */ + val = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_CTRL); + if (!priv->msg_read) + val &= ~XLP9XX_I2C_CTRL_FIFORD; + else + val |= XLP9XX_I2C_CTRL_FIFORD; /* read */ + + if (msg->flags & I2C_M_TEN) + val |= XLP9XX_I2C_CTRL_ADDMODE; /* 10-bit address mode*/ + else + val &= ~XLP9XX_I2C_CTRL_ADDMODE; + + /* set data length to be transferred */ + val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) | + (msg->len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val); + + /* fill fifo during tx */ + if (!priv->msg_read) + xlp9xx_i2c_fill_tx_fifo(priv); + + /* set interrupt mask */ + intr_mask = (XLP9XX_I2C_INTEN_ARLOST | XLP9XX_I2C_INTEN_BUSERR | + XLP9XX_I2C_INTEN_NACKADDR | XLP9XX_I2C_INTEN_DATADONE); + + if (priv->msg_read) { + intr_mask |= XLP9XX_I2C_INTEN_MFIFOHI; + if (msg->len == 0) + intr_mask |= XLP9XX_I2C_INTEN_SADDR; + } else { + if (msg->len == 0) + intr_mask |= XLP9XX_I2C_INTEN_SADDR; + else + intr_mask |= XLP9XX_I2C_INTEN_MFIFOEMTY; + } + xlp9xx_i2c_unmask_irq(priv, intr_mask); + + /* set cmd reg */ + cmd = XLP9XX_I2C_CMD_START; + cmd |= (priv->msg_read ? XLP9XX_I2C_CMD_READ : XLP9XX_I2C_CMD_WRITE); + if (last_msg) + cmd |= XLP9XX_I2C_CMD_STOP; + + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CMD, cmd); + + timeleft = msecs_to_jiffies(XLP9XX_I2C_TIMEOUT_MS); + timeleft = wait_for_completion_timeout(&priv->msg_complete, timeleft); + + if (priv->msg_err) { + dev_dbg(priv->dev, "transfer error %x!\n", priv->msg_err); + if (priv->msg_err & XLP9XX_I2C_INTEN_BUSERR) + xlp9xx_i2c_init(priv); + return -EIO; + } + + if (timeleft == 0) { + dev_dbg(priv->dev, "i2c transfer timed out!\n"); + xlp9xx_i2c_init(priv); + return -ETIMEDOUT; + } + + return 0; +} + +static int xlp9xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + int i, ret; + struct xlp9xx_i2c_dev *priv = i2c_get_adapdata(adap); + + for (i = 0; i < num; i++) { + ret = xlp9xx_i2c_xfer_msg(priv, &msgs[i], i == num - 1); + if (ret != 0) + return ret; + } + + return num; +} + +static u32 xlp9xx_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C | + I2C_FUNC_10BIT_ADDR; +} + +static struct i2c_algorithm xlp9xx_i2c_algo = { + .master_xfer = xlp9xx_i2c_xfer, + .functionality = xlp9xx_i2c_functionality, +}; + +static int xlp9xx_i2c_get_frequency(struct platform_device *pdev, + struct xlp9xx_i2c_dev *priv) +{ + struct device_node *np = pdev->dev.of_node; + u32 freq; + int err; + + err = of_property_read_u32(np, "clock-frequency", &freq); + if (err) { + freq = XLP9XX_I2C_DEFAULT_FREQ; + dev_dbg(&pdev->dev, "using default frequency %u\n", freq); + } else if (freq == 0 || freq > XLP9XX_I2C_HIGH_FREQ) { + dev_warn(&pdev->dev, "invalid frequency %u, using default\n", + freq); + freq = XLP9XX_I2C_DEFAULT_FREQ; + } + priv->clk_hz = freq; + + return 0; +} + +static int xlp9xx_i2c_probe(struct platform_device *pdev) +{ + struct xlp9xx_i2c_dev *priv; + struct resource *res; + int err = 0; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq <= 0) { + dev_err(&pdev->dev, "invalid irq!\n"); + return priv->irq; + } + + xlp9xx_i2c_get_frequency(pdev, priv); + xlp9xx_i2c_init(priv); + + err = devm_request_irq(&pdev->dev, priv->irq, xlp9xx_i2c_isr, 0, + pdev->name, priv); + if (err) { + dev_err(&pdev->dev, "IRQ request failed!\n"); + return err; + } + + init_completion(&priv->msg_complete); + priv->adapter.dev.parent = &pdev->dev; + priv->adapter.algo = &xlp9xx_i2c_algo; + priv->adapter.dev.of_node = pdev->dev.of_node; + priv->dev = &pdev->dev; + + snprintf(priv->adapter.name, sizeof(priv->adapter.name), "xlp9xx-i2c"); + i2c_set_adapdata(&priv->adapter, priv); + + err = i2c_add_adapter(&priv->adapter); + if (err) { + dev_err(&pdev->dev, "failed to add I2C adapter!\n"); + return err; + } + + platform_set_drvdata(pdev, priv); + dev_dbg(&pdev->dev, "I2C bus:%d added\n", priv->adapter.nr); + + return 0; +} + +static int xlp9xx_i2c_remove(struct platform_device *pdev) +{ + struct xlp9xx_i2c_dev *priv; + + priv = platform_get_drvdata(pdev); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, 0); + synchronize_irq(priv->irq); + i2c_del_adapter(&priv->adapter); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, 0); + + return 0; +} + +static const struct of_device_id xlp9xx_i2c_of_match[] = { + { .compatible = "netlogic,xlp980-i2c", }, + { /* sentinel */ }, +}; + +static struct platform_driver xlp9xx_i2c_driver = { + .probe = xlp9xx_i2c_probe, + .remove = xlp9xx_i2c_remove, + .driver = { + .name = "xlp9xx-i2c", + .of_match_table = xlp9xx_i2c_of_match, + }, +}; + +module_platform_driver(xlp9xx_i2c_driver); + +MODULE_AUTHOR("Subhendu Sekhar Behera "); +MODULE_DESCRIPTION("XLP9XX/5XX I2C Bus Controller Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index edf274cabe8172..68f687733379e5 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -561,7 +561,7 @@ static int i2c_generic_recovery(struct i2c_adapter *adap) int i = 0, val = 1, ret = 0; if (bri->prepare_recovery) - bri->prepare_recovery(bri); + bri->prepare_recovery(adap); /* * By this time SCL is high, as we need to give 9 falling-rising edges @@ -586,7 +586,7 @@ static int i2c_generic_recovery(struct i2c_adapter *adap) } if (bri->unprepare_recovery) - bri->unprepare_recovery(bri); + bri->unprepare_recovery(adap); return ret; } @@ -1875,6 +1875,13 @@ static int __init i2c_init(void) { int retval; + retval = of_alias_get_highest_id("i2c"); + + down_write(&__i2c_board_lock); + if (retval >= __i2c_first_dynamic_bus_num) + __i2c_first_dynamic_bus_num = retval + 1; + up_write(&__i2c_board_lock); + retval = bus_register(&i2c_bus_type); if (retval) return retval; @@ -1926,6 +1933,65 @@ module_exit(i2c_exit); * ---------------------------------------------------- */ +/* Check if val is exceeding the quirk IFF quirk is non 0 */ +#define i2c_quirk_exceeded(val, quirk) ((quirk) && ((val) > (quirk))) + +static int i2c_quirk_error(struct i2c_adapter *adap, struct i2c_msg *msg, char *err_msg) +{ + dev_err_ratelimited(&adap->dev, "adapter quirk: %s (addr 0x%04x, size %u, %s)\n", + err_msg, msg->addr, msg->len, + msg->flags & I2C_M_RD ? "read" : "write"); + return -EOPNOTSUPP; +} + +static int i2c_check_for_quirks(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + const struct i2c_adapter_quirks *q = adap->quirks; + int max_num = q->max_num_msgs, i; + bool do_len_check = true; + + if (q->flags & I2C_AQ_COMB) { + max_num = 2; + + /* special checks for combined messages */ + if (num == 2) { + if (q->flags & I2C_AQ_COMB_WRITE_FIRST && msgs[0].flags & I2C_M_RD) + return i2c_quirk_error(adap, &msgs[0], "1st comb msg must be write"); + + if (q->flags & I2C_AQ_COMB_READ_SECOND && !(msgs[1].flags & I2C_M_RD)) + return i2c_quirk_error(adap, &msgs[1], "2nd comb msg must be read"); + + if (q->flags & I2C_AQ_COMB_SAME_ADDR && msgs[0].addr != msgs[1].addr) + return i2c_quirk_error(adap, &msgs[0], "comb msg only to same addr"); + + if (i2c_quirk_exceeded(msgs[0].len, q->max_comb_1st_msg_len)) + return i2c_quirk_error(adap, &msgs[0], "msg too long"); + + if (i2c_quirk_exceeded(msgs[1].len, q->max_comb_2nd_msg_len)) + return i2c_quirk_error(adap, &msgs[1], "msg too long"); + + do_len_check = false; + } + } + + if (i2c_quirk_exceeded(num, max_num)) + return i2c_quirk_error(adap, &msgs[0], "too many messages"); + + for (i = 0; i < num; i++) { + u16 len = msgs[i].len; + + if (msgs[i].flags & I2C_M_RD) { + if (do_len_check && i2c_quirk_exceeded(len, q->max_read_len)) + return i2c_quirk_error(adap, &msgs[i], "msg too long"); + } else { + if (do_len_check && i2c_quirk_exceeded(len, q->max_write_len)) + return i2c_quirk_error(adap, &msgs[i], "msg too long"); + } + } + + return 0; +} + /** * __i2c_transfer - unlocked flavor of i2c_transfer * @adap: Handle to I2C bus @@ -1943,6 +2009,9 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) unsigned long orig_jiffies; int ret, try; + if (adap->quirks && i2c_check_for_quirks(adap, msgs, num)) + return -EOPNOTSUPP; + /* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets * enabled. This is an efficient way of keeping the for-loop from * being executed when not needed. diff --git a/drivers/i2c/i2c-slave-eeprom.c b/drivers/i2c/i2c-slave-eeprom.c index cf9b09db092f4e..8223746546093c 100644 --- a/drivers/i2c/i2c-slave-eeprom.c +++ b/drivers/i2c/i2c-slave-eeprom.c @@ -36,7 +36,7 @@ static int i2c_slave_eeprom_slave_cb(struct i2c_client *client, struct eeprom_data *eeprom = i2c_get_clientdata(client); switch (event) { - case I2C_SLAVE_REQ_WRITE_END: + case I2C_SLAVE_WRITE_RECEIVED: if (eeprom->first_write) { eeprom->buffer_idx = *val; eeprom->first_write = false; @@ -47,17 +47,23 @@ static int i2c_slave_eeprom_slave_cb(struct i2c_client *client, } break; - case I2C_SLAVE_REQ_READ_START: + case I2C_SLAVE_READ_PROCESSED: + /* The previous byte made it to the bus, get next one */ + eeprom->buffer_idx++; + /* fallthrough */ + case I2C_SLAVE_READ_REQUESTED: spin_lock(&eeprom->buffer_lock); *val = eeprom->buffer[eeprom->buffer_idx]; spin_unlock(&eeprom->buffer_lock); - break; - - case I2C_SLAVE_REQ_READ_END: - eeprom->buffer_idx++; + /* + * Do not increment buffer_idx here, because we don't know if + * this byte will be actually used. Read Linux I2C slave docs + * for details. + */ break; case I2C_SLAVE_STOP: + case I2C_SLAVE_WRITE_REQUESTED: eeprom->first_write = true; break; diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index f5798eb4076b5c..70db99264339ef 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -76,10 +76,9 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux, return -ENODEV; } adapter = of_find_i2c_adapter_by_node(adapter_np); - if (!adapter) { - dev_err(&pdev->dev, "Cannot find parent bus\n"); + if (!adapter) return -EPROBE_DEFER; - } + mux->data.parent = i2c_adapter_id(adapter); put_device(&adapter->dev); @@ -177,11 +176,8 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev) } parent = i2c_get_adapter(mux->data.parent); - if (!parent) { - dev_err(&pdev->dev, "Parent adapter (%d) not found\n", - mux->data.parent); + if (!parent) return -EPROBE_DEFER; - } mux->parent = parent; mux->gpio_base = gpio_base; diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index 3d8f4fe2e47e52..bea0d2de299384 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -204,9 +204,9 @@ static int pca954x_probe(struct i2c_client *client, i2c_set_clientdata(client, data); /* Get the mux out of reset if a reset GPIO is specified. */ - gpio = devm_gpiod_get(&client->dev, "reset"); - if (!IS_ERR(gpio)) - gpiod_direction_output(gpio, 0); + gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); /* Write the mux register at addr to verify * that the mux is in fact present. This also diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index df6a593bd4bdbd..2b4fad6998c150 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -123,7 +123,8 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev, *val = sensor_hub_input_attr_get_raw_value( accel_state->common_attributes.hsdev, HID_USAGE_SENSOR_ACCEL_3D, address, - report_id); + report_id, + SENSOR_HUB_SYNC); else { *val = 0; hid_sensor_power_state(&accel_state->common_attributes, diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 87ee1c7d0b5424..44bf815adb6c0d 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -436,7 +436,7 @@ static int max1027_probe(struct spi_device *spi) indio_dev->num_channels * 2, GFP_KERNEL); if (st->buffer == NULL) { - dev_err(&indio_dev->dev, "Can't allocate bufffer\n"); + dev_err(&indio_dev->dev, "Can't allocate buffer\n"); return -ENOMEM; } diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index 25b01e156d8264..e81f434760f4c7 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -153,8 +153,8 @@ s32 hid_sensor_read_poll_value(struct hid_sensor_common *st) int ret; ret = sensor_hub_get_feature(st->hsdev, - st->poll.report_id, - st->poll.index, &value); + st->poll.report_id, + st->poll.index, sizeof(value), &value); if (ret < 0 || value < 0) { return -EINVAL; @@ -174,8 +174,8 @@ int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, int ret; ret = sensor_hub_get_feature(st->hsdev, - st->poll.report_id, - st->poll.index, &value); + st->poll.report_id, + st->poll.index, sizeof(value), &value); if (ret < 0 || value < 0) { *val1 = *val2 = 0; return -EINVAL; @@ -212,9 +212,8 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st, else value = 0; } - ret = sensor_hub_set_feature(st->hsdev, - st->poll.report_id, - st->poll.index, value); + ret = sensor_hub_set_feature(st->hsdev, st->poll.report_id, + st->poll.index, sizeof(value), &value); if (ret < 0 || value < 0) ret = -EINVAL; @@ -229,8 +228,9 @@ int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st, int ret; ret = sensor_hub_get_feature(st->hsdev, - st->sensitivity.report_id, - st->sensitivity.index, &value); + st->sensitivity.report_id, + st->sensitivity.index, sizeof(value), + &value); if (ret < 0 || value < 0) { *val1 = *val2 = 0; return -EINVAL; @@ -253,9 +253,9 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, value = convert_to_vtf_format(st->sensitivity.size, st->sensitivity.unit_expo, val1, val2); - ret = sensor_hub_set_feature(st->hsdev, - st->sensitivity.report_id, - st->sensitivity.index, value); + ret = sensor_hub_set_feature(st->hsdev, st->sensitivity.report_id, + st->sensitivity.index, sizeof(value), + &value); if (ret < 0 || value < 0) ret = -EINVAL; diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 2f1d535b94c45d..610fc98f88efa4 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -68,20 +68,21 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state) if (state_val >= 0) { state_val += st->power_state.logical_minimum; sensor_hub_set_feature(st->hsdev, st->power_state.report_id, - st->power_state.index, - (s32)state_val); + st->power_state.index, sizeof(state_val), + &state_val); } if (report_val >= 0) { report_val += st->report_state.logical_minimum; sensor_hub_set_feature(st->hsdev, st->report_state.report_id, - st->report_state.index, - (s32)report_val); + st->report_state.index, + sizeof(report_val), + &report_val); } sensor_hub_get_feature(st->hsdev, st->power_state.report_id, - st->power_state.index, - &state_val); + st->power_state.index, + sizeof(state_val), &state_val); if (state && poll_value) msleep_interruptible(poll_value * 2); diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index a3c3e19de52758..b5883b6f4e5044 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -123,7 +123,8 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev, *val = sensor_hub_input_attr_get_raw_value( gyro_state->common_attributes.hsdev, HID_USAGE_SENSOR_GYRO_3D, address, - report_id); + report_id, + SENSOR_HUB_SYNC); else { *val = 0; hid_sensor_power_state(&gyro_state->common_attributes, diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 948acfc38b8cb3..1609ecdd01b07a 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -101,7 +101,8 @@ static int als_read_raw(struct iio_dev *indio_dev, *val = sensor_hub_input_attr_get_raw_value( als_state->common_attributes.hsdev, HID_USAGE_SENSOR_ALS, address, - report_id); + report_id, + SENSOR_HUB_SYNC); hid_sensor_power_state(&als_state->common_attributes, false); } else { diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index 3ecf79ed08ac89..91ecc46ffeaa0b 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -96,7 +96,8 @@ static int prox_read_raw(struct iio_dev *indio_dev, *val = sensor_hub_input_attr_get_raw_value( prox_state->common_attributes.hsdev, HID_USAGE_SENSOR_PROX, address, - report_id); + report_id, + SENSOR_HUB_SYNC); hid_sensor_power_state(&prox_state->common_attributes, false); } else { diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index d22993b4066a0d..4f9c0be244518d 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -170,7 +170,8 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev, *val = sensor_hub_input_attr_get_raw_value( magn_state->common_attributes.hsdev, HID_USAGE_SENSOR_COMPASS_3D, address, - report_id); + report_id, + SENSOR_HUB_SYNC); else { *val = 0; hid_sensor_power_state(&magn_state->common_attributes, diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index 73854460bb2cff..5930fa32a2ab19 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -124,7 +124,8 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev, *val = sensor_hub_input_attr_get_raw_value( incl_state->common_attributes.hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D, address, - report_id); + report_id, + SENSOR_HUB_SYNC); else { hid_sensor_power_state(&incl_state->common_attributes, false); diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index 1af314926ebd1f..7bb8d4c1f7df49 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -100,7 +100,8 @@ static int press_read_raw(struct iio_dev *indio_dev, *val = sensor_hub_input_attr_get_raw_value( press_state->common_attributes.hsdev, HID_USAGE_SENSOR_PRESSURE, address, - report_id); + report_id, + SENSOR_HUB_SYNC); hid_sensor_power_state(&press_state->common_attributes, false); } else { diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c index 6d7f453b4d05ef..aed8afee56da16 100644 --- a/drivers/infiniband/hw/ipath/ipath_file_ops.c +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c index 41937c6f888af1..14046f5a37fa33 100644 --- a/drivers/infiniband/hw/qib/qib_file_ops.c +++ b/drivers/infiniband/hw/qib/qib_file_ops.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index cb150a1dbaff9c..54fce56c802368 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -88,10 +88,13 @@ int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots, goto err_mem; } - /* Mark slots as 'unused' */ + /* Mark slots as 'inactive' */ for (i = 0; i < num_slots; i++) input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1); + /* Mark slots as 'unused' */ + mt->frame = 1; + dev->mt = mt; return 0; err_mem: @@ -365,27 +368,35 @@ static void input_mt_set_slots(struct input_mt *mt, int *slots, int num_pos) { struct input_mt_slot *s; - int *w = mt->red, *p; + int *w = mt->red, j; - for (p = slots; p != slots + num_pos; p++) - *p = -1; + for (j = 0; j != num_pos; j++) + slots[j] = -1; for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { if (!input_mt_is_active(s)) continue; - for (p = slots; p != slots + num_pos; p++) - if (*w++ < 0) - *p = s - mt->slots; + + for (j = 0; j != num_pos; j++) { + if (w[j] < 0) { + slots[j] = s - mt->slots; + break; + } + } + + w += num_pos; } for (s = mt->slots; s != mt->slots + mt->num_slots; s++) { if (input_mt_is_active(s)) continue; - for (p = slots; p != slots + num_pos; p++) - if (*p < 0) { - *p = s - mt->slots; + + for (j = 0; j != num_pos; j++) { + if (slots[j] < 0) { + slots[j] = s - mt->slots; break; } + } } } @@ -439,6 +450,8 @@ EXPORT_SYMBOL(input_mt_assign_slots); * set the key on the first unused slot and return. * * If no available slot can be found, -1 is returned. + * Note that for this function to work properly, input_mt_sync_frame() has + * to be called at each frame. */ int input_mt_get_slot_by_key(struct input_dev *dev, int key) { @@ -453,7 +466,7 @@ int input_mt_get_slot_by_key(struct input_dev *dev, int key) return s - mt->slots; for (s = mt->slots; s != mt->slots + mt->num_slots; s++) - if (!input_mt_is_active(s)) { + if (!input_mt_is_active(s) && !input_mt_is_used(mt, s)) { s->key = key; return s - mt->slots; } diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index a89ba7cb96f1ec..106fbac7f8c5b0 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -588,6 +588,16 @@ config KEYBOARD_DAVINCI To compile this driver as a module, choose M here: the module will be called davinci_keyscan. +config KEYBOARD_IPAQ_MICRO + tristate "Buttons on Micro SoC (iPaq h3100,h3600,h3700)" + depends on MFD_IPAQ_MICRO + help + Say Y to enable support for the buttons attached to + Micro peripheral controller on iPAQ h3100/h3600/h3700 + + To compile this driver as a module, choose M here: the + module will be called ipaq-micro-keys. + config KEYBOARD_OMAP tristate "TI OMAP keypad support" depends on ARCH_OMAP1 @@ -686,4 +696,15 @@ config KEYBOARD_CAP11XX To compile this driver as a module, choose M here: the module will be called cap11xx. +config KEYBOARD_BCM + tristate "Broadcom keypad driver" + depends on OF && HAVE_CLK + select INPUT_MATRIXKMAP + default ARCH_BCM_CYGNUS + help + Say Y here if you want to use Broadcom keypad. + + To compile this driver as a module, choose M here: the + module will be called bcm-keypad. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 470767884bd8c3..df28d5553c05c4 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o +obj-$(CONFIG_KEYBOARD_BCM) += bcm-keypad.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_CAP11XX) += cap11xx.o obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o @@ -23,6 +24,7 @@ obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o obj-$(CONFIG_KEYBOARD_TCA8418) += tca8418_keypad.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o +obj-$(CONFIG_KEYBOARD_IPAQ_MICRO) += ipaq-micro-keys.o obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 387c51f4b4e429..ec876b5b1382ff 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -1653,6 +1653,12 @@ static int __init atkbd_deactivate_fixup(const struct dmi_system_id *id) return 1; } +/* + * NOTE: do not add any more "force release" quirks to this table. The + * task of adjusting list of keys that should be "released" automatically + * by the driver is now delegated to userspace tools, such as udev, so + * submit such quirks there. + */ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = { { .matches = { diff --git a/drivers/input/keyboard/bcm-keypad.c b/drivers/input/keyboard/bcm-keypad.c new file mode 100644 index 00000000000000..86a8b723ae1533 --- /dev/null +++ b/drivers/input/keyboard/bcm-keypad.c @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_CLK_HZ 31250 +#define MAX_ROWS 8 +#define MAX_COLS 8 + +/* Register/field definitions */ +#define KPCR_OFFSET 0x00000080 +#define KPCR_MODE 0x00000002 +#define KPCR_MODE_SHIFT 1 +#define KPCR_MODE_MASK 1 +#define KPCR_ENABLE 0x00000001 +#define KPCR_STATUSFILTERENABLE 0x00008000 +#define KPCR_STATUSFILTERTYPE_SHIFT 12 +#define KPCR_COLFILTERENABLE 0x00000800 +#define KPCR_COLFILTERTYPE_SHIFT 8 +#define KPCR_ROWWIDTH_SHIFT 20 +#define KPCR_COLUMNWIDTH_SHIFT 16 + +#define KPIOR_OFFSET 0x00000084 +#define KPIOR_ROWOCONTRL_SHIFT 24 +#define KPIOR_ROWOCONTRL_MASK 0xFF000000 +#define KPIOR_COLUMNOCONTRL_SHIFT 16 +#define KPIOR_COLUMNOCONTRL_MASK 0x00FF0000 +#define KPIOR_COLUMN_IO_DATA_SHIFT 0 + +#define KPEMR0_OFFSET 0x00000090 +#define KPEMR1_OFFSET 0x00000094 +#define KPEMR2_OFFSET 0x00000098 +#define KPEMR3_OFFSET 0x0000009C +#define KPEMR_EDGETYPE_BOTH 3 + +#define KPSSR0_OFFSET 0x000000A0 +#define KPSSR1_OFFSET 0x000000A4 +#define KPSSRN_OFFSET(reg_n) (KPSSR0_OFFSET + 4 * (reg_n)) +#define KPIMR0_OFFSET 0x000000B0 +#define KPIMR1_OFFSET 0x000000B4 +#define KPICR0_OFFSET 0x000000B8 +#define KPICR1_OFFSET 0x000000BC +#define KPICRN_OFFSET(reg_n) (KPICR0_OFFSET + 4 * (reg_n)) +#define KPISR0_OFFSET 0x000000C0 +#define KPISR1_OFFSET 0x000000C4 + +#define KPCR_STATUSFILTERTYPE_MAX 7 +#define KPCR_COLFILTERTYPE_MAX 7 + +/* Macros to determine the row/column from a bit that is set in SSR0/1. */ +#define BIT_TO_ROW_SSRN(bit_nr, reg_n) (((bit_nr) >> 3) + 4 * (reg_n)) +#define BIT_TO_COL(bit_nr) ((bit_nr) % 8) + +/* Structure representing various run-time entities */ +struct bcm_kp { + void __iomem *base; + int irq; + struct clk *clk; + struct input_dev *input_dev; + unsigned long last_state[2]; + unsigned int n_rows; + unsigned int n_cols; + u32 kpcr; + u32 kpior; + u32 kpemr; + u32 imr0_val; + u32 imr1_val; +}; + +/* + * Returns the keycode from the input device keymap given the row and + * column. + */ +static int bcm_kp_get_keycode(struct bcm_kp *kp, int row, int col) +{ + unsigned int row_shift = get_count_order(kp->n_cols); + unsigned short *keymap = kp->input_dev->keycode; + + return keymap[MATRIX_SCAN_CODE(row, col, row_shift)]; +} + +static void bcm_kp_report_keys(struct bcm_kp *kp, int reg_num, int pull_mode) +{ + unsigned long state, change; + int bit_nr; + int key_press; + int row, col; + unsigned int keycode; + + /* Clear interrupts */ + writel(0xFFFFFFFF, kp->base + KPICRN_OFFSET(reg_num)); + + state = readl(kp->base + KPSSRN_OFFSET(reg_num)); + change = kp->last_state[reg_num] ^ state; + kp->last_state[reg_num] = state; + + for_each_set_bit(bit_nr, &change, BITS_PER_LONG) { + key_press = state & BIT(bit_nr); + /* The meaning of SSR register depends on pull mode. */ + key_press = pull_mode ? !key_press : key_press; + row = BIT_TO_ROW_SSRN(bit_nr, reg_num); + col = BIT_TO_COL(bit_nr); + keycode = bcm_kp_get_keycode(kp, row, col); + input_report_key(kp->input_dev, keycode, key_press); + } +} + +static irqreturn_t bcm_kp_isr_thread(int irq, void *dev_id) +{ + struct bcm_kp *kp = dev_id; + int pull_mode = (kp->kpcr >> KPCR_MODE_SHIFT) & KPCR_MODE_MASK; + int reg_num; + + for (reg_num = 0; reg_num <= 1; reg_num++) + bcm_kp_report_keys(kp, reg_num, pull_mode); + + input_sync(kp->input_dev); + + return IRQ_HANDLED; +} + +static int bcm_kp_start(struct bcm_kp *kp) +{ + int error; + + if (kp->clk) { + error = clk_prepare_enable(kp->clk); + if (error) + return error; + } + + writel(kp->kpior, kp->base + KPIOR_OFFSET); + + writel(kp->imr0_val, kp->base + KPIMR0_OFFSET); + writel(kp->imr1_val, kp->base + KPIMR1_OFFSET); + + writel(kp->kpemr, kp->base + KPEMR0_OFFSET); + writel(kp->kpemr, kp->base + KPEMR1_OFFSET); + writel(kp->kpemr, kp->base + KPEMR2_OFFSET); + writel(kp->kpemr, kp->base + KPEMR3_OFFSET); + + writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET); + writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET); + + kp->last_state[0] = readl(kp->base + KPSSR0_OFFSET); + kp->last_state[0] = readl(kp->base + KPSSR1_OFFSET); + + writel(kp->kpcr | KPCR_ENABLE, kp->base + KPCR_OFFSET); + + return 0; +} + +static void bcm_kp_stop(const struct bcm_kp *kp) +{ + u32 val; + + val = readl(kp->base + KPCR_OFFSET); + val &= ~KPCR_ENABLE; + writel(0, kp->base + KPCR_OFFSET); + writel(0, kp->base + KPIMR0_OFFSET); + writel(0, kp->base + KPIMR1_OFFSET); + writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET); + writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET); + + if (kp->clk) + clk_disable_unprepare(kp->clk); +} + +static int bcm_kp_open(struct input_dev *dev) +{ + struct bcm_kp *kp = input_get_drvdata(dev); + + return bcm_kp_start(kp); +} + +static void bcm_kp_close(struct input_dev *dev) +{ + struct bcm_kp *kp = input_get_drvdata(dev); + + bcm_kp_stop(kp); +} + +static int bcm_kp_matrix_key_parse_dt(struct bcm_kp *kp) +{ + struct device *dev = kp->input_dev->dev.parent; + struct device_node *np = dev->of_node; + int error; + unsigned int dt_val; + unsigned int i; + unsigned int num_rows, col_mask, rows_set; + + /* Initialize the KPCR Keypad Configuration Register */ + kp->kpcr = KPCR_STATUSFILTERENABLE | KPCR_COLFILTERENABLE; + + error = matrix_keypad_parse_of_params(dev, &kp->n_rows, &kp->n_cols); + if (error) { + dev_err(dev, "failed to parse kp params\n"); + return error; + } + + /* Set row width for the ASIC block. */ + kp->kpcr |= (kp->n_rows - 1) << KPCR_ROWWIDTH_SHIFT; + + /* Set column width for the ASIC block. */ + kp->kpcr |= (kp->n_cols - 1) << KPCR_COLUMNWIDTH_SHIFT; + + /* Configure the IMR registers */ + + /* + * IMR registers contain interrupt enable bits for 8x8 matrix + * IMR0 register format: + * IMR1 register format: + */ + col_mask = (1 << (kp->n_cols)) - 1; + num_rows = kp->n_rows; + + /* Set column bits in rows 0 to 3 in IMR0 */ + kp->imr0_val = col_mask; + + rows_set = 1; + while (--num_rows && rows_set++ < 4) + kp->imr0_val |= kp->imr0_val << MAX_COLS; + + /* Set column bits in rows 4 to 7 in IMR1 */ + kp->imr1_val = 0; + if (num_rows) { + kp->imr1_val = col_mask; + while (--num_rows) + kp->imr1_val |= kp->imr1_val << MAX_COLS; + } + + /* Initialize the KPEMR Keypress Edge Mode Registers */ + /* Trigger on both edges */ + kp->kpemr = 0; + for (i = 0; i <= 30; i += 2) + kp->kpemr |= (KPEMR_EDGETYPE_BOTH << i); + + /* + * Obtain the Status filter debounce value and verify against the + * possible values specified in the DT binding. + */ + of_property_read_u32(np, "status-debounce-filter-period", &dt_val); + + if (dt_val > KPCR_STATUSFILTERTYPE_MAX) { + dev_err(dev, "Invalid Status filter debounce value %d\n", + dt_val); + return -EINVAL; + } + + kp->kpcr |= dt_val << KPCR_STATUSFILTERTYPE_SHIFT; + + /* + * Obtain the Column filter debounce value and verify against the + * possible values specified in the DT binding. + */ + of_property_read_u32(np, "col-debounce-filter-period", &dt_val); + + if (dt_val > KPCR_COLFILTERTYPE_MAX) { + dev_err(dev, "Invalid Column filter debounce value %d\n", + dt_val); + return -EINVAL; + } + + kp->kpcr |= dt_val << KPCR_COLFILTERTYPE_SHIFT; + + /* + * Determine between the row and column, + * which should be configured as output. + */ + if (of_property_read_bool(np, "row-output-enabled")) { + /* + * Set RowOContrl or ColumnOContrl in KPIOR + * to the number of pins to drive as outputs + */ + kp->kpior = ((1 << kp->n_rows) - 1) << + KPIOR_ROWOCONTRL_SHIFT; + } else { + kp->kpior = ((1 << kp->n_cols) - 1) << + KPIOR_COLUMNOCONTRL_SHIFT; + } + + /* + * Determine if the scan pull up needs to be enabled + */ + if (of_property_read_bool(np, "pull-up-enabled")) + kp->kpcr |= KPCR_MODE; + + dev_dbg(dev, "n_rows=%d n_col=%d kpcr=%x kpior=%x kpemr=%x\n", + kp->n_rows, kp->n_cols, + kp->kpcr, kp->kpior, kp->kpemr); + + return 0; +} + + +static int bcm_kp_probe(struct platform_device *pdev) +{ + struct bcm_kp *kp; + struct input_dev *input_dev; + struct resource *res; + int error; + + kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL); + if (!kp) + return -ENOMEM; + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) { + dev_err(&pdev->dev, "failed to allocate the input device\n"); + return -ENOMEM; + } + + __set_bit(EV_KEY, input_dev->evbit); + + /* Enable auto repeat feature of Linux input subsystem */ + if (of_property_read_bool(pdev->dev.of_node, "autorepeat")) + __set_bit(EV_REP, input_dev->evbit); + + input_dev->name = pdev->name; + input_dev->phys = "keypad/input0"; + input_dev->dev.parent = &pdev->dev; + input_dev->open = bcm_kp_open; + input_dev->close = bcm_kp_close; + + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + + input_set_drvdata(input_dev, kp); + + kp->input_dev = input_dev; + + platform_set_drvdata(pdev, kp); + + error = bcm_kp_matrix_key_parse_dt(kp); + if (error) + return error; + + error = matrix_keypad_build_keymap(NULL, NULL, + kp->n_rows, kp->n_cols, + NULL, input_dev); + if (error) { + dev_err(&pdev->dev, "failed to build keymap\n"); + return error; + } + + /* Get the KEYPAD base address */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Missing keypad base address resource\n"); + return -ENODEV; + } + + kp->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(kp->base)) + return PTR_ERR(kp->base); + + /* Enable clock */ + kp->clk = devm_clk_get(&pdev->dev, "peri_clk"); + if (IS_ERR(kp->clk)) { + error = PTR_ERR(kp->clk); + if (error != -ENOENT) { + if (error != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to get clock\n"); + return error; + } + dev_dbg(&pdev->dev, + "No clock specified. Assuming it's enabled\n"); + kp->clk = NULL; + } else { + unsigned int desired_rate; + long actual_rate; + + error = of_property_read_u32(pdev->dev.of_node, + "clock-frequency", &desired_rate); + if (error < 0) + desired_rate = DEFAULT_CLK_HZ; + + actual_rate = clk_round_rate(kp->clk, desired_rate); + if (actual_rate <= 0) + return -EINVAL; + + error = clk_set_rate(kp->clk, actual_rate); + if (error) + return error; + + error = clk_prepare_enable(kp->clk); + if (error) + return error; + } + + /* Put the kp into a known sane state */ + bcm_kp_stop(kp); + + kp->irq = platform_get_irq(pdev, 0); + if (kp->irq < 0) { + dev_err(&pdev->dev, "no IRQ specified\n"); + return -EINVAL; + } + + error = devm_request_threaded_irq(&pdev->dev, kp->irq, + NULL, bcm_kp_isr_thread, + IRQF_ONESHOT, pdev->name, kp); + if (error) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + return error; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + return error; + } + + return 0; +} + +static const struct of_device_id bcm_kp_of_match[] = { + { .compatible = "brcm,bcm-keypad" }, + { }, +}; +MODULE_DEVICE_TABLE(of, bcm_kp_of_match); + +static struct platform_driver bcm_kp_device_driver = { + .probe = bcm_kp_probe, + .driver = { + .name = "bcm-keypad", + .of_match_table = of_match_ptr(bcm_kp_of_match), + } +}; + +module_platform_driver(bcm_kp_device_driver); + +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_DESCRIPTION("BCM Keypad Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index ffa989f2c78510..64b9b59ad4cbf4 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -338,7 +338,7 @@ static int cros_ec_keyb_resume(struct device *dev) * wake source (e.g. the lid is open and the user might press a key to * wake) then the key scan buffer should be preserved. */ - if (ckdev->ec->was_wake_device) + if (!ckdev->ec->was_wake_device) cros_ec_keyb_clear_keyboard(ckdev); return 0; diff --git a/drivers/input/keyboard/ipaq-micro-keys.c b/drivers/input/keyboard/ipaq-micro-keys.c new file mode 100644 index 00000000000000..602900d1f93788 --- /dev/null +++ b/drivers/input/keyboard/ipaq-micro-keys.c @@ -0,0 +1,168 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * h3600 atmel micro companion support, key subdevice + * based on previous kernel 2.4 version + * Author : Alessandro Gardich + * Author : Linus Walleij + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ipaq_micro_keys { + struct ipaq_micro *micro; + struct input_dev *input; + u16 *codes; +}; + +static const u16 micro_keycodes[] = { + KEY_RECORD, /* 1: Record button */ + KEY_CALENDAR, /* 2: Calendar */ + KEY_ADDRESSBOOK, /* 3: Contacts (looks like Outlook) */ + KEY_MAIL, /* 4: Envelope (Q on older iPAQs) */ + KEY_HOMEPAGE, /* 5: Start (looks like swoopy arrow) */ + KEY_UP, /* 6: Up */ + KEY_RIGHT, /* 7: Right */ + KEY_LEFT, /* 8: Left */ + KEY_DOWN, /* 9: Down */ +}; + +static void micro_key_receive(void *data, int len, unsigned char *msg) +{ + struct ipaq_micro_keys *keys = data; + int key, down; + + down = 0x80 & msg[0]; + key = 0x7f & msg[0]; + + if (key < ARRAY_SIZE(micro_keycodes)) { + input_report_key(keys->input, keys->codes[key], down); + input_sync(keys->input); + } +} + +static void micro_key_start(struct ipaq_micro_keys *keys) +{ + spin_lock(&keys->micro->lock); + keys->micro->key = micro_key_receive; + keys->micro->key_data = keys; + spin_unlock(&keys->micro->lock); +} + +static void micro_key_stop(struct ipaq_micro_keys *keys) +{ + spin_lock(&keys->micro->lock); + keys->micro->key = NULL; + keys->micro->key_data = NULL; + spin_unlock(&keys->micro->lock); +} + +static int micro_key_open(struct input_dev *input) +{ + struct ipaq_micro_keys *keys = input_get_drvdata(input); + + micro_key_start(keys); + + return 0; +} + +static void micro_key_close(struct input_dev *input) +{ + struct ipaq_micro_keys *keys = input_get_drvdata(input); + + micro_key_stop(keys); +} + +static int micro_key_probe(struct platform_device *pdev) +{ + struct ipaq_micro_keys *keys; + int error; + int i; + + keys = devm_kzalloc(&pdev->dev, sizeof(*keys), GFP_KERNEL); + if (!keys) + return -ENOMEM; + + keys->micro = dev_get_drvdata(pdev->dev.parent); + + keys->input = devm_input_allocate_device(&pdev->dev); + if (!keys->input) + return -ENOMEM; + + keys->input->keycodesize = sizeof(micro_keycodes[0]); + keys->input->keycodemax = ARRAY_SIZE(micro_keycodes); + keys->codes = devm_kmemdup(&pdev->dev, micro_keycodes, + keys->input->keycodesize * keys->input->keycodemax, + GFP_KERNEL); + keys->input->keycode = keys->codes; + + __set_bit(EV_KEY, keys->input->evbit); + for (i = 0; i < ARRAY_SIZE(micro_keycodes); i++) + __set_bit(micro_keycodes[i], keys->input->keybit); + + keys->input->name = "h3600 micro keys"; + keys->input->open = micro_key_open; + keys->input->close = micro_key_close; + input_set_drvdata(keys->input, keys); + + error = input_register_device(keys->input); + if (error) + return error; + + platform_set_drvdata(pdev, keys); + return 0; +} + +static int __maybe_unused micro_key_suspend(struct device *dev) +{ + struct ipaq_micro_keys *keys = dev_get_drvdata(dev); + + micro_key_stop(keys); + + return 0; +} + +static int __maybe_unused micro_key_resume(struct device *dev) +{ + struct ipaq_micro_keys *keys = dev_get_drvdata(dev); + struct input_dev *input = keys->input; + + mutex_lock(&input->mutex); + + if (input->users) + micro_key_start(keys); + + mutex_unlock(&input->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(micro_key_dev_pm_ops, + micro_key_suspend, micro_key_resume); + +static struct platform_driver micro_key_device_driver = { + .driver = { + .name = "ipaq-micro-keys", + .pm = µ_key_dev_pm_ops, + }, + .probe = micro_key_probe, +}; +module_platform_driver(micro_key_device_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("driver for iPAQ Atmel micro keys"); +MODULE_ALIAS("platform:ipaq-micro-keys"); diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c index 563932500ff1bb..31c606a4dd310c 100644 --- a/drivers/input/keyboard/tc3589x-keypad.c +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -69,6 +69,28 @@ #define TC3589x_EVT_INT_CLR 0x2 #define TC3589x_KBD_INT_CLR 0x1 +/** + * struct tc35893_keypad_platform_data - platform specific keypad data + * @keymap_data: matrix scan code table for keycodes + * @krow: mask for available rows, value is 0xFF + * @kcol: mask for available columns, value is 0xFF + * @debounce_period: platform specific debounce time + * @settle_time: platform specific settle down time + * @irqtype: type of interrupt, falling or rising edge + * @enable_wakeup: specifies if keypad event can wake up system from sleep + * @no_autorepeat: flag for auto repetition + */ +struct tc3589x_keypad_platform_data { + const struct matrix_keymap_data *keymap_data; + u8 krow; + u8 kcol; + u8 debounce_period; + u8 settle_time; + unsigned long irqtype; + bool enable_wakeup; + bool no_autorepeat; +}; + /** * struct tc_keypad - data structure used by keypad driver * @tc3589x: pointer to tc35893 @@ -296,7 +318,6 @@ static void tc3589x_keypad_close(struct input_dev *input) tc3589x_keypad_disable(keypad); } -#ifdef CONFIG_OF static const struct tc3589x_keypad_platform_data * tc3589x_keypad_of_probe(struct device *dev) { @@ -346,14 +367,6 @@ tc3589x_keypad_of_probe(struct device *dev) return plat; } -#else -static inline const struct tc3589x_keypad_platform_data * -tc3589x_keypad_of_probe(struct device *dev) -{ - return ERR_PTR(-ENODEV); -} -#endif - static int tc3589x_keypad_probe(struct platform_device *pdev) { @@ -363,13 +376,10 @@ static int tc3589x_keypad_probe(struct platform_device *pdev) const struct tc3589x_keypad_platform_data *plat; int error, irq; - plat = tc3589x->pdata->keypad; - if (!plat) { - plat = tc3589x_keypad_of_probe(&pdev->dev); - if (IS_ERR(plat)) { - dev_err(&pdev->dev, "invalid keypad platform data\n"); - return PTR_ERR(plat); - } + plat = tc3589x_keypad_of_probe(&pdev->dev); + if (IS_ERR(plat)) { + dev_err(&pdev->dev, "invalid keypad platform data\n"); + return PTR_ERR(plat); } irq = platform_get_irq(pdev, 0); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 6deb8dae320511..4436ab1b9735d6 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -115,6 +115,18 @@ config INPUT_PCSPKR To compile this driver as a module, choose M here: the module will be called pcspkr. +config INPUT_PM8941_PWRKEY + tristate "Qualcomm PM8941 power key support" + depends on MFD_SPMI_PMIC + help + Say Y here if you want support for the power key usually found + on boards using a Qualcomm PM8941 compatible PMIC. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the module + will be called pm8941-pwrkey. + config INPUT_PM8XXX_VIBRATOR tristate "Qualcomm PM8XXX vibrator support" depends on MFD_PM8XXX @@ -165,6 +177,18 @@ config INPUT_MAX77693_HAPTIC To compile this driver as module, choose M here: the module will be called max77693-haptic. +config INPUT_MAX77843_HAPTIC + tristate "MAXIM MAX77843 haptic controller support" + depends on MFD_MAX77843 && REGULATOR + select INPUT_FF_MEMLESS + help + This option enables support for the haptic controller on + MAXIM MAX77843 chip. The driver supports ff-memless interface + from input framework. + + To compile this driver as module, choose M here: the + module will be called max77843-haptic. + config INPUT_MAX8925_ONKEY tristate "MAX8925 ONKEY support" depends on MFD_MAX8925 diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 403a1a54a76c30..78ba4c1b8532c5 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o +obj-$(CONFIG_INPUT_MAX77843_HAPTIC) += max77843-haptic.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o @@ -49,6 +50,7 @@ obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o +obj-$(CONFIG_INPUT_PM8941_PWRKEY) += pm8941-pwrkey.o obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o diff --git a/drivers/input/misc/max77843-haptic.c b/drivers/input/misc/max77843-haptic.c new file mode 100644 index 00000000000000..dccbb465a055eb --- /dev/null +++ b/drivers/input/misc/max77843-haptic.c @@ -0,0 +1,358 @@ +/* + * MAXIM MAX77693 Haptic device driver + * + * Copyright (C) 2015 Samsung Electronics + * Author: Jaewon Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_MAGNITUDE_SHIFT 16 + +enum max77843_haptic_motor_type { + MAX77843_HAPTIC_ERM = 0, + MAX77843_HAPTIC_LRA, +}; + +enum max77843_haptic_pwm_divisor { + MAX77843_HAPTIC_PWM_DIVISOR_32 = 0, + MAX77843_HAPTIC_PWM_DIVISOR_64, + MAX77843_HAPTIC_PWM_DIVISOR_128, + MAX77843_HAPTIC_PWM_DIVISOR_256, +}; + +struct max77843_haptic { + struct regmap *regmap_haptic; + struct device *dev; + struct input_dev *input_dev; + struct pwm_device *pwm_dev; + struct regulator *motor_reg; + struct work_struct work; + struct mutex mutex; + + unsigned int magnitude; + unsigned int pwm_duty; + + bool active; + bool suspended; + + enum max77843_haptic_motor_type type; + enum max77843_haptic_pwm_divisor pwm_divisor; +}; + +static int max77843_haptic_set_duty_cycle(struct max77843_haptic *haptic) +{ + int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2; + int error; + + error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period); + if (error) { + dev_err(haptic->dev, "failed to configure pwm: %d\n", error); + return error; + } + + return 0; +} + +static int max77843_haptic_bias(struct max77843_haptic *haptic, bool on) +{ + int error; + + error = regmap_update_bits(haptic->regmap_haptic, + MAX77843_SYS_REG_MAINCTRL1, + MAX77843_MAINCTRL1_BIASEN_MASK, + on << MAINCTRL1_BIASEN_SHIFT); + if (error) { + dev_err(haptic->dev, "failed to %s bias: %d\n", + on ? "enable" : "disable", error); + return error; + } + + return 0; +} + +static int max77843_haptic_config(struct max77843_haptic *haptic, bool enable) +{ + unsigned int value; + int error; + + value = (haptic->type << MCONFIG_MODE_SHIFT) | + (enable << MCONFIG_MEN_SHIFT) | + (haptic->pwm_divisor << MCONFIG_PDIV_SHIFT); + + error = regmap_write(haptic->regmap_haptic, + MAX77843_HAP_REG_MCONFIG, value); + if (error) { + dev_err(haptic->dev, + "failed to update haptic config: %d\n", error); + return error; + } + + return 0; +} + +static int max77843_haptic_enable(struct max77843_haptic *haptic) +{ + int error; + + if (haptic->active) + return 0; + + error = pwm_enable(haptic->pwm_dev); + if (error) { + dev_err(haptic->dev, + "failed to enable pwm device: %d\n", error); + return error; + } + + error = max77843_haptic_config(haptic, true); + if (error) + goto err_config; + + haptic->active = true; + + return 0; + +err_config: + pwm_disable(haptic->pwm_dev); + + return error; +} + +static int max77843_haptic_disable(struct max77843_haptic *haptic) +{ + int error; + + if (!haptic->active) + return 0; + + error = max77843_haptic_config(haptic, false); + if (error) + return error; + + pwm_disable(haptic->pwm_dev); + + haptic->active = false; + + return 0; +} + +static void max77843_haptic_play_work(struct work_struct *work) +{ + struct max77843_haptic *haptic = + container_of(work, struct max77843_haptic, work); + int error; + + mutex_lock(&haptic->mutex); + + if (haptic->suspended) + goto out_unlock; + + if (haptic->magnitude) { + error = max77843_haptic_set_duty_cycle(haptic); + if (error) { + dev_err(haptic->dev, + "failed to set duty cycle: %d\n", error); + goto out_unlock; + } + + error = max77843_haptic_enable(haptic); + if (error) + dev_err(haptic->dev, + "cannot enable haptic: %d\n", error); + } else { + error = max77843_haptic_disable(haptic); + if (error) + dev_err(haptic->dev, + "cannot disable haptic: %d\n", error); + } + +out_unlock: + mutex_unlock(&haptic->mutex); +} + +static int max77843_haptic_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct max77843_haptic *haptic = input_get_drvdata(dev); + u64 period_mag_multi; + + haptic->magnitude = effect->u.rumble.strong_magnitude; + if (!haptic->magnitude) + haptic->magnitude = effect->u.rumble.weak_magnitude; + + period_mag_multi = (u64)haptic->pwm_dev->period * haptic->magnitude; + haptic->pwm_duty = (unsigned int)(period_mag_multi >> + MAX_MAGNITUDE_SHIFT); + + schedule_work(&haptic->work); + + return 0; +} + +static int max77843_haptic_open(struct input_dev *dev) +{ + struct max77843_haptic *haptic = input_get_drvdata(dev); + int error; + + error = max77843_haptic_bias(haptic, true); + if (error) + return error; + + error = regulator_enable(haptic->motor_reg); + if (error) { + dev_err(haptic->dev, + "failed to enable regulator: %d\n", error); + return error; + } + + return 0; +} + +static void max77843_haptic_close(struct input_dev *dev) +{ + struct max77843_haptic *haptic = input_get_drvdata(dev); + int error; + + cancel_work_sync(&haptic->work); + max77843_haptic_disable(haptic); + + error = regulator_disable(haptic->motor_reg); + if (error) + dev_err(haptic->dev, + "failed to disable regulator: %d\n", error); + + max77843_haptic_bias(haptic, false); +} + +static int max77843_haptic_probe(struct platform_device *pdev) +{ + struct max77843 *max77843 = dev_get_drvdata(pdev->dev.parent); + struct max77843_haptic *haptic; + int error; + + haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL); + if (!haptic) + return -ENOMEM; + + haptic->regmap_haptic = max77843->regmap; + haptic->dev = &pdev->dev; + haptic->type = MAX77843_HAPTIC_LRA; + haptic->pwm_divisor = MAX77843_HAPTIC_PWM_DIVISOR_128; + + INIT_WORK(&haptic->work, max77843_haptic_play_work); + mutex_init(&haptic->mutex); + + haptic->pwm_dev = devm_pwm_get(&pdev->dev, NULL); + if (IS_ERR(haptic->pwm_dev)) { + dev_err(&pdev->dev, "failed to get pwm device\n"); + return PTR_ERR(haptic->pwm_dev); + } + + haptic->motor_reg = devm_regulator_get_exclusive(&pdev->dev, "haptic"); + if (IS_ERR(haptic->motor_reg)) { + dev_err(&pdev->dev, "failed to get regulator\n"); + return PTR_ERR(haptic->motor_reg); + } + + haptic->input_dev = devm_input_allocate_device(&pdev->dev); + if (!haptic->input_dev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + haptic->input_dev->name = "max77843-haptic"; + haptic->input_dev->id.version = 1; + haptic->input_dev->dev.parent = &pdev->dev; + haptic->input_dev->open = max77843_haptic_open; + haptic->input_dev->close = max77843_haptic_close; + input_set_drvdata(haptic->input_dev, haptic); + input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(haptic->input_dev, NULL, + max77843_haptic_play_effect); + if (error) { + dev_err(&pdev->dev, "failed to create force-feedback\n"); + return error; + } + + error = input_register_device(haptic->input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + return error; + } + + platform_set_drvdata(pdev, haptic); + + return 0; +} + +static int __maybe_unused max77843_haptic_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max77843_haptic *haptic = platform_get_drvdata(pdev); + int error; + + error = mutex_lock_interruptible(&haptic->mutex); + if (error) + return error; + + max77843_haptic_disable(haptic); + + haptic->suspended = true; + + mutex_unlock(&haptic->mutex); + + return 0; +} + +static int __maybe_unused max77843_haptic_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max77843_haptic *haptic = platform_get_drvdata(pdev); + unsigned int magnitude; + + mutex_lock(&haptic->mutex); + + haptic->suspended = false; + + magnitude = ACCESS_ONCE(haptic->magnitude); + if (magnitude) + max77843_haptic_enable(haptic); + + mutex_unlock(&haptic->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(max77843_haptic_pm_ops, + max77843_haptic_suspend, max77843_haptic_resume); + +static struct platform_driver max77843_haptic_driver = { + .driver = { + .name = "max77843-haptic", + .pm = &max77843_haptic_pm_ops, + }, + .probe = max77843_haptic_probe, +}; +module_platform_driver(max77843_haptic_driver); + +MODULE_AUTHOR("Jaewon Kim "); +MODULE_DESCRIPTION("MAXIM MAX77843 Haptic driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c index 98228773a1118b..19c73574458e82 100644 --- a/drivers/input/misc/mma8450.c +++ b/drivers/input/misc/mma8450.c @@ -174,12 +174,13 @@ static int mma8450_probe(struct i2c_client *c, struct mma8450 *m; int err; - m = kzalloc(sizeof(struct mma8450), GFP_KERNEL); - idev = input_allocate_polled_device(); - if (!m || !idev) { - err = -ENOMEM; - goto err_free_mem; - } + m = devm_kzalloc(&c->dev, sizeof(*m), GFP_KERNEL); + if (!m) + return -ENOMEM; + + idev = devm_input_allocate_polled_device(&c->dev); + if (!idev) + return -ENOMEM; m->client = c; m->idev = idev; @@ -187,7 +188,6 @@ static int mma8450_probe(struct i2c_client *c, idev->private = m; idev->input->name = MMA8450_DRV_NAME; idev->input->id.bustype = BUS_I2C; - idev->input->dev.parent = &c->dev; idev->poll = mma8450_poll; idev->poll_interval = POLL_INTERVAL; idev->poll_interval_max = POLL_INTERVAL_MAX; @@ -202,28 +202,11 @@ static int mma8450_probe(struct i2c_client *c, err = input_register_polled_device(idev); if (err) { dev_err(&c->dev, "failed to register polled input device\n"); - goto err_free_mem; + return err; } i2c_set_clientdata(c, m); - return 0; - -err_free_mem: - input_free_polled_device(idev); - kfree(m); - return err; -} - -static int mma8450_remove(struct i2c_client *c) -{ - struct mma8450 *m = i2c_get_clientdata(c); - struct input_polled_dev *idev = m->idev; - - input_unregister_polled_device(idev); - input_free_polled_device(idev); - kfree(m); - return 0; } @@ -242,11 +225,9 @@ MODULE_DEVICE_TABLE(of, mma8450_dt_ids); static struct i2c_driver mma8450_driver = { .driver = { .name = MMA8450_DRV_NAME, - .owner = THIS_MODULE, .of_match_table = mma8450_dt_ids, }, .probe = mma8450_probe, - .remove = mma8450_remove, .id_table = mma8450_id, }; diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c index 1f9b5ee927460c..1e1baed6392908 100644 --- a/drivers/input/misc/palmas-pwrbutton.c +++ b/drivers/input/misc/palmas-pwrbutton.c @@ -304,7 +304,7 @@ static SIMPLE_DEV_PM_OPS(palmas_pwron_pm, palmas_pwron_suspend, palmas_pwron_resume); #ifdef CONFIG_OF -static struct of_device_id of_palmas_pwr_match[] = { +static const struct of_device_id of_palmas_pwr_match[] = { { .compatible = "ti,palmas-pwrbutton" }, { }, }; diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c new file mode 100644 index 00000000000000..867db8a9137201 --- /dev/null +++ b/drivers/input/misc/pm8941-pwrkey.c @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2014, Sony Mobile Communications Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PON_REV2 0x01 + +#define PON_RT_STS 0x10 +#define PON_KPDPWR_N_SET BIT(0) + +#define PON_PS_HOLD_RST_CTL 0x5a +#define PON_PS_HOLD_RST_CTL2 0x5b +#define PON_PS_HOLD_ENABLE BIT(7) +#define PON_PS_HOLD_TYPE_MASK 0x0f +#define PON_PS_HOLD_TYPE_SHUTDOWN 4 +#define PON_PS_HOLD_TYPE_HARD_RESET 7 + +#define PON_PULL_CTL 0x70 +#define PON_KPDPWR_PULL_UP BIT(1) + +#define PON_DBC_CTL 0x71 +#define PON_DBC_DELAY_MASK 0x7 + + +struct pm8941_pwrkey { + struct device *dev; + int irq; + u32 baseaddr; + struct regmap *regmap; + struct input_dev *input; + + unsigned int revision; + struct notifier_block reboot_notifier; +}; + +static int pm8941_reboot_notify(struct notifier_block *nb, + unsigned long code, void *unused) +{ + struct pm8941_pwrkey *pwrkey = container_of(nb, struct pm8941_pwrkey, + reboot_notifier); + unsigned int enable_reg; + unsigned int reset_type; + int error; + + /* PMICs with revision 0 have the enable bit in same register as ctrl */ + if (pwrkey->revision == 0) + enable_reg = PON_PS_HOLD_RST_CTL; + else + enable_reg = PON_PS_HOLD_RST_CTL2; + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + enable_reg, + PON_PS_HOLD_ENABLE, + 0); + if (error) + dev_err(pwrkey->dev, + "unable to clear ps hold reset enable: %d\n", + error); + + /* + * Updates of PON_PS_HOLD_ENABLE requires 3 sleep cycles between + * writes. + */ + usleep_range(100, 1000); + + switch (code) { + case SYS_HALT: + case SYS_POWER_OFF: + reset_type = PON_PS_HOLD_TYPE_SHUTDOWN; + break; + case SYS_RESTART: + default: + reset_type = PON_PS_HOLD_TYPE_HARD_RESET; + break; + }; + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + PON_PS_HOLD_RST_CTL, + PON_PS_HOLD_TYPE_MASK, + reset_type); + if (error) + dev_err(pwrkey->dev, "unable to set ps hold reset type: %d\n", + error); + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + enable_reg, + PON_PS_HOLD_ENABLE, + PON_PS_HOLD_ENABLE); + if (error) + dev_err(pwrkey->dev, "unable to re-set enable: %d\n", error); + + return NOTIFY_DONE; +} + +static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data) +{ + struct pm8941_pwrkey *pwrkey = _data; + unsigned int sts; + int error; + + error = regmap_read(pwrkey->regmap, + pwrkey->baseaddr + PON_RT_STS, &sts); + if (error) + return IRQ_HANDLED; + + input_report_key(pwrkey->input, KEY_POWER, !!(sts & PON_KPDPWR_N_SET)); + input_sync(pwrkey->input); + + return IRQ_HANDLED; +} + +static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev) +{ + struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(pwrkey->irq); + + return 0; +} + +static int __maybe_unused pm8941_pwrkey_resume(struct device *dev) +{ + struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(pwrkey->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops, + pm8941_pwrkey_suspend, pm8941_pwrkey_resume); + +static int pm8941_pwrkey_probe(struct platform_device *pdev) +{ + struct pm8941_pwrkey *pwrkey; + bool pull_up; + u32 req_delay; + int error; + + if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay)) + req_delay = 15625; + + if (req_delay > 2000000 || req_delay == 0) { + dev_err(&pdev->dev, "invalid debounce time: %u\n", req_delay); + return -EINVAL; + } + + pull_up = of_property_read_bool(pdev->dev.of_node, "bias-pull-up"); + + pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL); + if (!pwrkey) + return -ENOMEM; + + pwrkey->dev = &pdev->dev; + + pwrkey->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!pwrkey->regmap) { + dev_err(&pdev->dev, "failed to locate regmap\n"); + return -ENODEV; + } + + pwrkey->irq = platform_get_irq(pdev, 0); + if (pwrkey->irq < 0) { + dev_err(&pdev->dev, "failed to get irq\n"); + return pwrkey->irq; + } + + error = of_property_read_u32(pdev->dev.of_node, "reg", + &pwrkey->baseaddr); + if (error) + return error; + + error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2, + &pwrkey->revision); + if (error) { + dev_err(&pdev->dev, "failed to set debounce: %d\n", error); + return error; + } + + pwrkey->input = devm_input_allocate_device(&pdev->dev); + if (!pwrkey->input) { + dev_dbg(&pdev->dev, "unable to allocate input device\n"); + return -ENOMEM; + } + + input_set_capability(pwrkey->input, EV_KEY, KEY_POWER); + + pwrkey->input->name = "pm8941_pwrkey"; + pwrkey->input->phys = "pm8941_pwrkey/input0"; + + req_delay = (req_delay << 6) / USEC_PER_SEC; + req_delay = ilog2(req_delay); + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + PON_DBC_CTL, + PON_DBC_DELAY_MASK, + req_delay); + if (error) { + dev_err(&pdev->dev, "failed to set debounce: %d\n", error); + return error; + } + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + PON_PULL_CTL, + PON_KPDPWR_PULL_UP, + pull_up ? PON_KPDPWR_PULL_UP : 0); + if (error) { + dev_err(&pdev->dev, "failed to set pull: %d\n", error); + return error; + } + + error = devm_request_threaded_irq(&pdev->dev, pwrkey->irq, + NULL, pm8941_pwrkey_irq, + IRQF_ONESHOT, + "pm8941_pwrkey", pwrkey); + if (error) { + dev_err(&pdev->dev, "failed requesting IRQ: %d\n", error); + return error; + } + + error = input_register_device(pwrkey->input); + if (error) { + dev_err(&pdev->dev, "failed to register input device: %d\n", + error); + return error; + } + + pwrkey->reboot_notifier.notifier_call = pm8941_reboot_notify, + error = register_reboot_notifier(&pwrkey->reboot_notifier); + if (error) { + dev_err(&pdev->dev, "failed to register reboot notifier: %d\n", + error); + return error; + } + + platform_set_drvdata(pdev, pwrkey); + device_init_wakeup(&pdev->dev, 1); + + return 0; +} + +static int pm8941_pwrkey_remove(struct platform_device *pdev) +{ + struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev); + + device_init_wakeup(&pdev->dev, 0); + unregister_reboot_notifier(&pwrkey->reboot_notifier); + + return 0; +} + +static const struct of_device_id pm8941_pwr_key_id_table[] = { + { .compatible = "qcom,pm8941-pwrkey" }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table); + +static struct platform_driver pm8941_pwrkey_driver = { + .probe = pm8941_pwrkey_probe, + .remove = pm8941_pwrkey_remove, + .driver = { + .name = "pm8941-pwrkey", + .pm = &pm8941_pwr_key_pm_ops, + .of_match_table = of_match_ptr(pm8941_pwr_key_id_table), + }, +}; +module_platform_driver(pm8941_pwrkey_driver); + +MODULE_DESCRIPTION("PM8941 Power Key driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index a28ee70ff15856..e82edf810d1f3e 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -50,7 +50,6 @@ static int pwm_beeper_event(struct input_dev *input, } if (value == 0) { - pwm_config(beeper->pwm, 0, 0); pwm_disable(beeper->pwm); } else { period = HZ_TO_NANOSECONDS(value); @@ -169,12 +168,6 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, pwm_beeper_suspend, pwm_beeper_resume); -#ifdef CONFIG_PM_SLEEP -#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops) -#else -#define PWM_BEEPER_PM_OPS NULL -#endif - #ifdef CONFIG_OF static const struct of_device_id pwm_beeper_match[] = { { .compatible = "pwm-beeper", }, @@ -187,7 +180,7 @@ static struct platform_driver pwm_beeper_driver = { .remove = pwm_beeper_remove, .driver = { .name = "pwm-beeper", - .pm = PWM_BEEPER_PM_OPS, + .pm = &pwm_beeper_pm_ops, .of_match_table = of_match_ptr(pwm_beeper_match), }, }; diff --git a/drivers/input/misc/regulator-haptic.c b/drivers/input/misc/regulator-haptic.c index 132eb914ea3e0e..6bf3f1082f71ea 100644 --- a/drivers/input/misc/regulator-haptic.c +++ b/drivers/input/misc/regulator-haptic.c @@ -245,7 +245,7 @@ static int __maybe_unused regulator_haptic_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops, regulator_haptic_suspend, regulator_haptic_resume); -static struct of_device_id regulator_haptic_dt_match[] = { +static const struct of_device_id regulator_haptic_dt_match[] = { { .compatible = "regulator-haptic" }, { /* sentinel */ }, }; diff --git a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c index 54508dec4eb308..a39b62651a4b33 100644 --- a/drivers/input/misc/tps65218-pwrbutton.c +++ b/drivers/input/misc/tps65218-pwrbutton.c @@ -106,7 +106,7 @@ static int tps65218_pwron_probe(struct platform_device *pdev) return 0; } -static struct of_device_id of_tps65218_pwr_match[] = { +static const struct of_device_id of_tps65218_pwr_match[] = { { .compatible = "ti,tps65218-pwrbutton" }, { }, }; diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 27bcdbc950c9fc..e6708f6efb4db7 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -153,10 +153,18 @@ static const struct alps_protocol_info alps_v7_protocol_data = { ALPS_PROTO_V7, 0x48, 0x48, ALPS_DUALPOINT }; +static const struct alps_protocol_info alps_v8_protocol_data = { + ALPS_PROTO_V8, 0x18, 0x18, 0 +}; + static void alps_set_abs_params_st(struct alps_data *priv, struct input_dev *dev1); static void alps_set_abs_params_mt(struct alps_data *priv, struct input_dev *dev1); +static void alps_set_abs_params_v7(struct alps_data *priv, + struct input_dev *dev1); +static void alps_set_abs_params_ss4_v2(struct alps_data *priv, + struct input_dev *dev1); /* Packet formats are described in Documentation/input/alps.txt */ @@ -243,6 +251,14 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) return; } + /* Non interleaved V2 dualpoint has separate stick button bits */ + if (priv->proto_version == ALPS_PROTO_V2 && + priv->flags == (ALPS_PASS | ALPS_DUALPOINT)) { + left |= packet[0] & 1; + right |= packet[0] & 2; + middle |= packet[0] & 4; + } + alps_report_buttons(dev, dev2, left, right, middle); /* Convert hardware tap to a reasonable Z value */ @@ -1085,6 +1101,176 @@ static void alps_process_packet_v7(struct psmouse *psmouse) alps_process_touchpad_packet_v7(psmouse); } +static unsigned char alps_get_pkt_id_ss4_v2(unsigned char *byte) +{ + unsigned char pkt_id = SS4_PACKET_ID_IDLE; + + if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 && + (byte[3] & 0x88) == 0x08 && byte[4] == 0x10 && byte[5] == 0x00) { + pkt_id = SS4_PACKET_ID_IDLE; + } else if (!(byte[3] & 0x10)) { + pkt_id = SS4_PACKET_ID_ONE; + } else if (!(byte[3] & 0x20)) { + pkt_id = SS4_PACKET_ID_TWO; + } else { + pkt_id = SS4_PACKET_ID_MULTI; + } + + return pkt_id; +} + +static int alps_decode_ss4_v2(struct alps_fields *f, + unsigned char *p, struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + unsigned char pkt_id; + unsigned int no_data_x, no_data_y; + + pkt_id = alps_get_pkt_id_ss4_v2(p); + + /* Current packet is 1Finger coordinate packet */ + switch (pkt_id) { + case SS4_PACKET_ID_ONE: + f->mt[0].x = SS4_1F_X_V2(p); + f->mt[0].y = SS4_1F_Y_V2(p); + f->pressure = ((SS4_1F_Z_V2(p)) * 2) & 0x7f; + f->fingers = 1; + f->first_mp = 0; + f->is_mp = 0; + break; + + case SS4_PACKET_ID_TWO: + if (priv->flags & ALPS_BUTTONPAD) { + f->mt[0].x = SS4_BTL_MF_X_V2(p, 0); + f->mt[0].y = SS4_BTL_MF_Y_V2(p, 0); + f->mt[1].x = SS4_BTL_MF_X_V2(p, 1); + f->mt[1].y = SS4_BTL_MF_Y_V2(p, 1); + } else { + f->mt[0].x = SS4_STD_MF_X_V2(p, 0); + f->mt[0].y = SS4_STD_MF_Y_V2(p, 0); + f->mt[1].x = SS4_STD_MF_X_V2(p, 1); + f->mt[1].y = SS4_STD_MF_Y_V2(p, 1); + } + f->pressure = SS4_MF_Z_V2(p, 0) ? 0x30 : 0; + + if (SS4_IS_MF_CONTINUE(p)) { + f->first_mp = 1; + } else { + f->fingers = 2; + f->first_mp = 0; + } + f->is_mp = 0; + + break; + + case SS4_PACKET_ID_MULTI: + if (priv->flags & ALPS_BUTTONPAD) { + f->mt[2].x = SS4_BTL_MF_X_V2(p, 0); + f->mt[2].y = SS4_BTL_MF_Y_V2(p, 0); + f->mt[3].x = SS4_BTL_MF_X_V2(p, 1); + f->mt[3].y = SS4_BTL_MF_Y_V2(p, 1); + no_data_x = SS4_MFPACKET_NO_AX_BL; + no_data_y = SS4_MFPACKET_NO_AY_BL; + } else { + f->mt[2].x = SS4_STD_MF_X_V2(p, 0); + f->mt[2].y = SS4_STD_MF_Y_V2(p, 0); + f->mt[3].x = SS4_STD_MF_X_V2(p, 1); + f->mt[3].y = SS4_STD_MF_Y_V2(p, 1); + no_data_x = SS4_MFPACKET_NO_AX; + no_data_y = SS4_MFPACKET_NO_AY; + } + + f->first_mp = 0; + f->is_mp = 1; + + if (SS4_IS_5F_DETECTED(p)) { + f->fingers = 5; + } else if (f->mt[3].x == no_data_x && + f->mt[3].y == no_data_y) { + f->mt[3].x = 0; + f->mt[3].y = 0; + f->fingers = 3; + } else { + f->fingers = 4; + } + break; + + case SS4_PACKET_ID_IDLE: + default: + memset(f, 0, sizeof(struct alps_fields)); + break; + } + + f->left = !!(SS4_BTN_V2(p) & 0x01); + if (!(priv->flags & ALPS_BUTTONPAD)) { + f->right = !!(SS4_BTN_V2(p) & 0x02); + f->middle = !!(SS4_BTN_V2(p) & 0x04); + } + + return 0; +} + +static void alps_process_packet_ss4_v2(struct psmouse *psmouse) +{ + struct alps_data *priv = psmouse->private; + unsigned char *packet = psmouse->packet; + struct input_dev *dev = psmouse->dev; + struct alps_fields *f = &priv->f; + + memset(f, 0, sizeof(struct alps_fields)); + priv->decode_fields(f, packet, psmouse); + if (priv->multi_packet) { + /* + * Sometimes the first packet will indicate a multi-packet + * sequence, but sometimes the next multi-packet would not + * come. Check for this, and when it happens process the + * position packet as usual. + */ + if (f->is_mp) { + /* Now process the 1st packet */ + priv->decode_fields(f, priv->multi_data, psmouse); + } else { + priv->multi_packet = 0; + } + } + + /* + * "f.is_mp" would always be '0' after merging the 1st and 2nd packet. + * When it is set, it means 2nd packet comes without 1st packet come. + */ + if (f->is_mp) + return; + + /* Save the first packet */ + if (!priv->multi_packet && f->first_mp) { + priv->multi_packet = 1; + memcpy(priv->multi_data, packet, sizeof(priv->multi_data)); + return; + } + + priv->multi_packet = 0; + + alps_report_mt_data(psmouse, (f->fingers <= 4) ? f->fingers : 4); + + input_mt_report_finger_count(dev, f->fingers); + + input_report_key(dev, BTN_LEFT, f->left); + input_report_key(dev, BTN_RIGHT, f->right); + input_report_key(dev, BTN_MIDDLE, f->middle); + + input_report_abs(dev, ABS_PRESSURE, f->pressure); + input_sync(dev); +} + +static bool alps_is_valid_package_ss4_v2(struct psmouse *psmouse) +{ + if (psmouse->pktcnt == 4 && ((psmouse->packet[3] & 0x08) != 0x08)) + return false; + if (psmouse->pktcnt == 6 && ((psmouse->packet[5] & 0x10) != 0x0)) + return false; + return true; +} + static DEFINE_MUTEX(alps_mutex); static void alps_register_bare_ps2_mouse(struct work_struct *work) @@ -1159,13 +1345,14 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse, bool report_buttons) { struct alps_data *priv = psmouse->private; - struct input_dev *dev; + struct input_dev *dev, *dev2 = NULL; /* Figure out which device to use to report the bare packet */ if (priv->proto_version == ALPS_PROTO_V2 && (priv->flags & ALPS_DUALPOINT)) { /* On V2 devices the DualPoint Stick reports bare packets */ dev = priv->dev2; + dev2 = psmouse->dev; } else if (unlikely(IS_ERR_OR_NULL(priv->dev3))) { /* Register dev3 mouse if we received PS/2 packet first time */ if (!IS_ERR(priv->dev3)) @@ -1177,7 +1364,7 @@ static void alps_report_bare_ps2_packet(struct psmouse *psmouse, } if (report_buttons) - alps_report_buttons(dev, NULL, + alps_report_buttons(dev, dev2, packet[0] & 1, packet[0] & 2, packet[0] & 4); input_report_rel(dev, REL_X, @@ -1305,8 +1492,12 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) * a device connected to the external PS/2 port. Because bare PS/2 * protocol does not have enough constant bits to self-synchronize * properly we only do this if the device is fully synchronized. + * Can not distinguish V8's first byte from PS/2 packet's */ - if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) { + if (priv->proto_version != ALPS_PROTO_V8 && + !psmouse->out_of_sync_cnt && + (psmouse->packet[0] & 0xc8) == 0x08) { + if (psmouse->pktcnt == 3) { alps_report_bare_ps2_packet(psmouse, psmouse->packet, true); @@ -1354,8 +1545,10 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) return PSMOUSE_BAD_DATA; } - if (priv->proto_version == ALPS_PROTO_V7 && - !alps_is_valid_package_v7(psmouse)) { + if ((priv->proto_version == ALPS_PROTO_V7 && + !alps_is_valid_package_v7(psmouse)) || + (priv->proto_version == ALPS_PROTO_V8 && + !alps_is_valid_package_ss4_v2(psmouse))) { psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", psmouse->pktcnt - 1, psmouse->packet[psmouse->pktcnt - 1]); @@ -2130,6 +2323,88 @@ static int alps_hw_init_v4(struct psmouse *psmouse) return -1; } +static int alps_get_otp_values_ss4_v2(struct psmouse *psmouse, + unsigned char index, unsigned char otp[]) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + + switch (index) { + case 0: + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) || + ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO)) + return -1; + + break; + + case 1: + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETPOLL) || + ps2_command(ps2dev, otp, PSMOUSE_CMD_GETINFO)) + return -1; + + break; + } + + return 0; +} + +static int alps_update_device_area_ss4_v2(unsigned char otp[][4], + struct alps_data *priv) +{ + int num_x_electrode; + int num_y_electrode; + int x_pitch, y_pitch, x_phys, y_phys; + + num_x_electrode = SS4_NUMSENSOR_XOFFSET + (otp[1][0] & 0x0F); + num_y_electrode = SS4_NUMSENSOR_YOFFSET + ((otp[1][0] >> 4) & 0x0F); + + priv->x_max = (num_x_electrode - 1) * SS4_COUNT_PER_ELECTRODE; + priv->y_max = (num_y_electrode - 1) * SS4_COUNT_PER_ELECTRODE; + + x_pitch = ((otp[1][2] >> 2) & 0x07) + SS4_MIN_PITCH_MM; + y_pitch = ((otp[1][2] >> 5) & 0x07) + SS4_MIN_PITCH_MM; + + x_phys = x_pitch * (num_x_electrode - 1); /* In 0.1 mm units */ + y_phys = y_pitch * (num_y_electrode - 1); /* In 0.1 mm units */ + + priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */ + priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */ + + return 0; +} + +static int alps_update_btn_info_ss4_v2(unsigned char otp[][4], + struct alps_data *priv) +{ + unsigned char is_btnless; + + is_btnless = (otp[1][1] >> 3) & 0x01; + + if (is_btnless) + priv->flags |= ALPS_BUTTONPAD; + + return 0; +} + +static int alps_set_defaults_ss4_v2(struct psmouse *psmouse, + struct alps_data *priv) +{ + unsigned char otp[2][4]; + + memset(otp, 0, sizeof(otp)); + + if (alps_get_otp_values_ss4_v2(psmouse, 0, &otp[0][0]) || + alps_get_otp_values_ss4_v2(psmouse, 1, &otp[1][0])) + return -1; + + alps_update_device_area_ss4_v2(otp, priv); + + alps_update_btn_info_ss4_v2(otp, priv); + + return 0; +} + static int alps_dolphin_get_device_area(struct psmouse *psmouse, struct alps_data *priv) { @@ -2222,6 +2497,35 @@ static int alps_hw_init_v7(struct psmouse *psmouse) return ret; } +static int alps_hw_init_ss4_v2(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + char param[2] = {0x64, 0x28}; + int ret = -1; + + /* enter absolute mode */ + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM) || + ps2_command(ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) || + ps2_command(ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE)) { + goto error; + } + + /* T.B.D. Decread noise packet number, delete in the future */ + if (alps_exit_command_mode(psmouse) || + alps_enter_command_mode(psmouse) || + alps_command_mode_write_reg(psmouse, 0x001D, 0x20)) { + goto error; + } + alps_exit_command_mode(psmouse); + + return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); + +error: + alps_exit_command_mode(psmouse); + return ret; +} + static int alps_set_protocol(struct psmouse *psmouse, struct alps_data *priv, const struct alps_protocol_info *protocol) @@ -2311,7 +2615,7 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->hw_init = alps_hw_init_v7; priv->process_packet = alps_process_packet_v7; priv->decode_fields = alps_decode_packet_v7; - priv->set_abs_params = alps_set_abs_params_mt; + priv->set_abs_params = alps_set_abs_params_v7; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; priv->x_max = 0xfff; @@ -2321,6 +2625,19 @@ static int alps_set_protocol(struct psmouse *psmouse, priv->flags |= ALPS_BUTTONPAD; break; + + case ALPS_PROTO_V8: + priv->hw_init = alps_hw_init_ss4_v2; + priv->process_packet = alps_process_packet_ss4_v2; + priv->decode_fields = alps_decode_ss4_v2; + priv->set_abs_params = alps_set_abs_params_ss4_v2; + priv->nibble_commands = alps_v3_nibble_commands; + priv->addr_command = PSMOUSE_CMD_RESET_WRAP; + + if (alps_set_defaults_ss4_v2(psmouse, priv)) + return -EIO; + + break; } return 0; @@ -2389,6 +2706,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) } else if (ec[0] == 0x88 && ec[1] == 0x07 && ec[2] >= 0x90 && ec[2] <= 0x9d) { protocol = &alps_v3_protocol_data; + } else if (e7[0] == 0x73 && e7[1] == 0x03 && + e7[2] == 0x14 && ec[1] == 0x02) { + protocol = &alps_v8_protocol_data; } else { psmouse_dbg(psmouse, "Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec); @@ -2437,10 +2757,11 @@ static void alps_set_abs_params_st(struct alps_data *priv, { input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0); input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0); + input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); } -static void alps_set_abs_params_mt(struct alps_data *priv, - struct input_dev *dev1) +static void alps_set_abs_params_mt_common(struct alps_data *priv, + struct input_dev *dev1) { input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0); input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0); @@ -2448,15 +2769,44 @@ static void alps_set_abs_params_mt(struct alps_data *priv, input_abs_set_res(dev1, ABS_MT_POSITION_X, priv->x_res); input_abs_set_res(dev1, ABS_MT_POSITION_Y, priv->y_res); - input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER | - INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK | INPUT_MT_SEMI_MT); - set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); set_bit(BTN_TOOL_QUADTAP, dev1->keybit); +} + +static void alps_set_abs_params_mt(struct alps_data *priv, + struct input_dev *dev1) +{ + alps_set_abs_params_mt_common(priv, dev1); + input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); + + input_mt_init_slots(dev1, MAX_TOUCHES, + INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | + INPUT_MT_TRACK | INPUT_MT_SEMI_MT); +} + +static void alps_set_abs_params_v7(struct alps_data *priv, + struct input_dev *dev1) +{ + alps_set_abs_params_mt_common(priv, dev1); + set_bit(BTN_TOOL_QUINTTAP, dev1->keybit); + + input_mt_init_slots(dev1, MAX_TOUCHES, + INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | + INPUT_MT_TRACK); + + set_bit(BTN_TOOL_QUINTTAP, dev1->keybit); +} + +static void alps_set_abs_params_ss4_v2(struct alps_data *priv, + struct input_dev *dev1) +{ + alps_set_abs_params_mt_common(priv, dev1); + input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); + set_bit(BTN_TOOL_QUINTTAP, dev1->keybit); - /* V7 is real multi-touch */ - if (priv->proto_version == ALPS_PROTO_V7) - clear_bit(INPUT_PROP_SEMI_MT, dev1->propbit); + input_mt_init_slots(dev1, MAX_TOUCHES, + INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | + INPUT_MT_TRACK); } int alps_init(struct psmouse *psmouse) @@ -2489,9 +2839,6 @@ int alps_init(struct psmouse *psmouse) dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); priv->set_abs_params(priv, dev1); - /* No pressure on V7 */ - if (priv->proto_version != ALPS_PROTO_V7) - input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); if (priv->flags & ALPS_WHEEL) { dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL); diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 02513c0502fc13..6dfdccc3a7c6e8 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -22,13 +22,89 @@ #define ALPS_PROTO_V5 0x500 #define ALPS_PROTO_V6 0x600 #define ALPS_PROTO_V7 0x700 /* t3btl t4s */ +#define ALPS_PROTO_V8 0x800 /* SS4btl SS4s */ -#define MAX_TOUCHES 2 +#define MAX_TOUCHES 4 #define DOLPHIN_COUNT_PER_ELECTRODE 64 #define DOLPHIN_PROFILE_XOFFSET 8 /* x-electrode offset */ #define DOLPHIN_PROFILE_YOFFSET 1 /* y-electrode offset */ +/* + * enum SS4_PACKET_ID - defines the packet type for V8 + * SS4_PACKET_ID_IDLE: There's no finger and no button activity. + * SS4_PACKET_ID_ONE: There's one finger on touchpad + * or there's button activities. + * SS4_PACKET_ID_TWO: There's two or more fingers on touchpad + * SS4_PACKET_ID_MULTI: There's three or more fingers on touchpad +*/ +enum SS4_PACKET_ID { + SS4_PACKET_ID_IDLE = 0, + SS4_PACKET_ID_ONE, + SS4_PACKET_ID_TWO, + SS4_PACKET_ID_MULTI, +}; + +#define SS4_COUNT_PER_ELECTRODE 256 +#define SS4_NUMSENSOR_XOFFSET 7 +#define SS4_NUMSENSOR_YOFFSET 7 +#define SS4_MIN_PITCH_MM 50 + +#define SS4_MASK_NORMAL_BUTTONS 0x07 + +#define SS4_1F_X_V2(_b) ((_b[0] & 0x0007) | \ + ((_b[1] << 3) & 0x0078) | \ + ((_b[1] << 2) & 0x0380) | \ + ((_b[2] << 5) & 0x1C00) \ + ) + +#define SS4_1F_Y_V2(_b) (((_b[2]) & 0x000F) | \ + ((_b[3] >> 2) & 0x0030) | \ + ((_b[4] << 6) & 0x03C0) | \ + ((_b[4] << 5) & 0x0C00) \ + ) + +#define SS4_1F_Z_V2(_b) (((_b[5]) & 0x0F) | \ + ((_b[5] >> 1) & 0x70) | \ + ((_b[4]) & 0x80) \ + ) + +#define SS4_1F_LFB_V2(_b) (((_b[2] >> 4) & 0x01) == 0x01) + +#define SS4_MF_LF_V2(_b, _i) ((_b[1 + (_i) * 3] & 0x0004) == 0x0004) + +#define SS4_BTN_V2(_b) ((_b[0] >> 5) & SS4_MASK_NORMAL_BUTTONS) + +#define SS4_STD_MF_X_V2(_b, _i) (((_b[0 + (_i) * 3] << 5) & 0x00E0) | \ + ((_b[1 + _i * 3] << 5) & 0x1F00) \ + ) + +#define SS4_STD_MF_Y_V2(_b, _i) (((_b[1 + (_i) * 3] << 3) & 0x0010) | \ + ((_b[2 + (_i) * 3] << 5) & 0x01E0) | \ + ((_b[2 + (_i) * 3] << 4) & 0x0E00) \ + ) + +#define SS4_BTL_MF_X_V2(_b, _i) (SS4_STD_MF_X_V2(_b, _i) | \ + ((_b[0 + (_i) * 3] >> 3) & 0x0010) \ + ) + +#define SS4_BTL_MF_Y_V2(_b, _i) (SS4_STD_MF_Y_V2(_b, _i) | \ + ((_b[0 + (_i) * 3] >> 3) & 0x0008) \ + ) + +#define SS4_MF_Z_V2(_b, _i) (((_b[1 + (_i) * 3]) & 0x0001) | \ + ((_b[1 + (_i) * 3] >> 1) & 0x0002) \ + ) + +#define SS4_IS_MF_CONTINUE(_b) ((_b[2] & 0x10) == 0x10) +#define SS4_IS_5F_DETECTED(_b) ((_b[2] & 0x10) == 0x10) + + +#define SS4_MFPACKET_NO_AX 8160 /* X-Coordinate value */ +#define SS4_MFPACKET_NO_AY 4080 /* Y-Coordinate value */ +#define SS4_MFPACKET_NO_AX_BL 8176 /* Buttonless X-Coordinate value */ +#define SS4_MFPACKET_NO_AY_BL 4088 /* Buttonless Y-Coordinate value */ + /* * enum V7_PACKET_ID - defines the packet type for V7 * V7_PACKET_ID_IDLE: There's no finger and no button activity. diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h index e100c1b31597d7..9b2dc015f20c8a 100644 --- a/drivers/input/mouse/elan_i2c.h +++ b/drivers/input/mouse/elan_i2c.h @@ -17,7 +17,7 @@ */ #ifndef _ELAN_I2C_H -#define _ELAN_i2C_H +#define _ELAN_I2C_H #include diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 7ce8bfe22d7eeb..375d98f47483d3 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -99,7 +99,7 @@ static int elan_enable_power(struct elan_tp_data *data) error = regulator_enable(data->vcc); if (error) { dev_err(&data->client->dev, - "Failed to enable regulator: %d\n", error); + "failed to enable regulator: %d\n", error); return error; } @@ -111,6 +111,7 @@ static int elan_enable_power(struct elan_tp_data *data) msleep(30); } while (--repeat > 0); + dev_err(&data->client->dev, "failed to enable power: %d\n", error); return error; } @@ -125,7 +126,7 @@ static int elan_disable_power(struct elan_tp_data *data) error = regulator_disable(data->vcc); if (error) { dev_err(&data->client->dev, - "Failed to disable regulator: %d\n", + "failed to disable regulator: %d\n", error); /* Attempt to power the chip back up */ data->ops->power_control(data->client, true); @@ -138,6 +139,7 @@ static int elan_disable_power(struct elan_tp_data *data) msleep(30); } while (--repeat > 0); + dev_err(&data->client->dev, "failed to disable power: %d\n", error); return error; } @@ -196,7 +198,6 @@ static int elan_initialize(struct elan_tp_data *data) if (!error) return 0; - repeat--; msleep(30); } while (--repeat > 0); @@ -1084,16 +1085,18 @@ static int __maybe_unused elan_resume(struct device *dev) } error = elan_enable_power(data); - if (error) + if (error) { dev_err(dev, "power up when resuming failed: %d\n", error); + goto err; + } error = elan_initialize(data); if (error) dev_err(dev, "initialize when resuming failed: %d\n", error); +err: enable_irq(data->client->irq); - - return 0; + return error; } static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume); diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c index 029941f861afd6..6cf0def6d35ee7 100644 --- a/drivers/input/mouse/elan_i2c_i2c.c +++ b/drivers/input/mouse/elan_i2c_i2c.c @@ -117,7 +117,15 @@ static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd) int ret; ret = i2c_transfer(client->adapter, &msg, 1); - return ret == 1 ? 0 : (ret < 0 ? ret : -EIO); + if (ret != 1) { + if (ret >= 0) + ret = -EIO; + dev_err(&client->dev, "writing cmd (0x%04x) failed: %d\n", + reg, ret); + return ret; + } + + return 0; } static int elan_i2c_initialize(struct i2c_client *client) diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 6e22682c8255cf..991dc6b20a5859 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -892,6 +892,21 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) return PSMOUSE_FULL_PACKET; } +/* + * This writes the reg_07 value again to the hardware at the end of every + * set_rate call because the register loses its value. reg_07 allows setting + * absolute mode on v4 hardware + */ +static void elantech_set_rate_restore_reg_07(struct psmouse *psmouse, + unsigned int rate) +{ + struct elantech_data *etd = psmouse->private; + + etd->original_set_rate(psmouse, rate); + if (elantech_write_reg(psmouse, 0x07, etd->reg_07)) + psmouse_err(psmouse, "restoring reg_07 failed\n"); +} + /* * Put the touchpad into absolute mode */ @@ -1094,6 +1109,8 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse, * Asus K53SV 0x450f01 78, 15, 0c 2 hw buttons * Asus G46VW 0x460f02 00, 18, 0c 2 hw buttons * Asus G750JX 0x360f00 00, 16, 0c 2 hw buttons + * Asus TP500LN 0x381f17 10, 14, 0e clickpad + * Asus X750JN 0x381f17 10, 14, 0e clickpad * Asus UX31 0x361f00 20, 15, 0e clickpad * Asus UX32VD 0x361f02 00, 15, 0e clickpad * Avatar AVIU-145A2 0x361f00 ? clickpad @@ -1635,6 +1652,11 @@ int elantech_init(struct psmouse *psmouse) goto init_fail; } + if (etd->fw_version == 0x381f17) { + etd->original_set_rate = psmouse->set_rate; + psmouse->set_rate = elantech_set_rate_restore_reg_07; + } + if (elantech_set_input_params(psmouse)) { psmouse_err(psmouse, "failed to query touchpad range.\n"); goto init_fail; diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h index 6f3afec02f03ee..f965d1569cc338 100644 --- a/drivers/input/mouse/elantech.h +++ b/drivers/input/mouse/elantech.h @@ -142,6 +142,7 @@ struct elantech_data { struct finger_pos mt[ETP_MAX_FINGERS]; unsigned char parity[256]; int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param); + void (*original_set_rate)(struct psmouse *psmouse, unsigned int rate); }; #ifdef CONFIG_MOUSE_PS2_ELANTECH diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c index 23222dd5a66fb1..e5ed216824e917 100644 --- a/drivers/input/mouse/lifebook.c +++ b/drivers/input/mouse/lifebook.c @@ -256,8 +256,8 @@ static void lifebook_disconnect(struct psmouse *psmouse) int lifebook_detect(struct psmouse *psmouse, bool set_properties) { - if (!lifebook_present) - return -1; + if (!lifebook_present) + return -1; if (desired_serio_phys && strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys)) @@ -268,7 +268,7 @@ int lifebook_detect(struct psmouse *psmouse, bool set_properties) psmouse->name = "Lifebook TouchScreen"; } - return 0; + return 0; } static int lifebook_create_relative_device(struct psmouse *psmouse) diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 8bc61237bc1b1c..27057df7ba74ee 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -474,19 +474,45 @@ static int psmouse_poll(struct psmouse *psmouse) PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); } +static bool psmouse_check_pnp_id(const char *id, const char * const ids[]) +{ + int i; + + for (i = 0; ids[i]; i++) + if (!strcasecmp(id, ids[i])) + return true; + + return false; +} + /* * psmouse_matches_pnp_id - check if psmouse matches one of the passed in ids. */ bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]) { - int i; - - if (!strncmp(psmouse->ps2dev.serio->firmware_id, "PNP:", 4)) - for (i = 0; ids[i]; i++) - if (strstr(psmouse->ps2dev.serio->firmware_id, ids[i])) - return true; + struct serio *serio = psmouse->ps2dev.serio; + char *p, *fw_id_copy, *save_ptr; + bool found = false; + + if (strncmp(serio->firmware_id, "PNP: ", 5)) + return false; + + fw_id_copy = kstrndup(&serio->firmware_id[5], + sizeof(serio->firmware_id) - 5, + GFP_KERNEL); + if (!fw_id_copy) + return false; + + save_ptr = fw_id_copy; + while ((p = strsep(&fw_id_copy, " ")) != NULL) { + if (psmouse_check_pnp_id(p, ids)) { + found = true; + break; + } + } - return false; + kfree(save_ptr); + return found; } /* diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 3b06c8a360b661..630af73e98c488 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -67,6 +67,9 @@ #define X_MAX_POSITIVE 8176 #define Y_MAX_POSITIVE 8176 +/* maximum ABS_MT_POSITION displacement (in mm) */ +#define DMAX 10 + /***************************************************************************** * Stuff we need even when we do not want native Synaptics support ****************************************************************************/ @@ -203,6 +206,13 @@ static const char * const topbuttonpad_pnp_ids[] = { NULL }; +/* This list has been kindly provided by Synaptics. */ +static const char * const forcepad_pnp_ids[] = { + "SYN300D", + "SYN3014", + NULL +}; + /***************************************************************************** * Synaptics communications functions ****************************************************************************/ @@ -687,8 +697,6 @@ static void synaptics_parse_ext_buttons(const unsigned char buf[], hw->ext_buttons |= (buf[5] & ext_mask) << ext_bits; } -static bool is_forcepad; - static int synaptics_parse_hw_state(const unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw) @@ -718,7 +726,7 @@ static int synaptics_parse_hw_state(const unsigned char buf[], hw->left = (buf[0] & 0x01) ? 1 : 0; hw->right = (buf[0] & 0x02) ? 1 : 0; - if (is_forcepad) { + if (priv->is_forcepad) { /* * ForcePads, like Clickpads, use middle button * bits to report primary button clicks. @@ -917,7 +925,7 @@ static void synaptics_report_mt_data(struct psmouse *psmouse, pos[i].y = synaptics_invert_y(hw[i]->y); } - input_mt_assign_slots(dev, slot, pos, nsemi, 0); + input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res); for (i = 0; i < nsemi; i++) { input_mt_slot(dev, slot[i]); @@ -1186,7 +1194,7 @@ static void set_input_params(struct psmouse *psmouse, ABS_MT_POSITION_Y); /* Image sensors can report per-contact pressure */ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); - input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK); + input_mt_init_slots(dev, 3, INPUT_MT_POINTER | INPUT_MT_TRACK); /* Image sensors can signal 4 and 5 finger clicks */ __set_bit(BTN_TOOL_QUADTAP, dev->keybit); @@ -1418,29 +1426,11 @@ static const struct dmi_system_id __initconst cr48_dmi_table[] = { { } }; -static const struct dmi_system_id forcepad_dmi_table[] __initconst = { -#if defined(CONFIG_DMI) && defined(CONFIG_X86) - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook Folio 1040 G1"), - }, - }, -#endif - { } -}; - void __init synaptics_module_init(void) { impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); broken_olpc_ec = dmi_check_system(olpc_dmi_table); cr48_profile_sensor = dmi_check_system(cr48_dmi_table); - - /* - * Unfortunately ForcePad capability is not exported over PS/2, - * so we have to resort to checking DMI. - */ - is_forcepad = dmi_check_system(forcepad_dmi_table); } static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) @@ -1475,6 +1465,12 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) if (SYN_ID_DISGEST_SUPPORTED(priv->identity)) priv->disable_gesture = true; + /* + * Unfortunately ForcePad capability is not exported over PS/2, + * so we have to resort to checking PNP IDs. + */ + priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids); + if (synaptics_set_mode(psmouse)) { psmouse_err(psmouse, "Unable to initialize device.\n"); goto init_fail; diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index ee4bd0d12b26fa..56faa7ec443480 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -196,6 +196,7 @@ struct synaptics_data { unsigned long press_start; bool press; bool report_press; + bool is_forcepad; }; void synaptics_module_init(void); diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c index 94ab494a6ade8d..ecba666afadb7c 100644 --- a/drivers/input/serio/gscps2.c +++ b/drivers/input/serio/gscps2.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 986a71c614b046..cb5ece77fd7d8c 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -1162,13 +1162,32 @@ static int i8042_controller_resume(bool force_reset) static int i8042_pm_suspend(struct device *dev) { + int i; + i8042_controller_reset(true); + /* Set up serio interrupts for system wakeup. */ + for (i = 0; i < I8042_NUM_PORTS; i++) { + struct serio *serio = i8042_ports[i].serio; + + if (serio && device_may_wakeup(&serio->dev)) + enable_irq_wake(i8042_ports[i].irq); + } + return 0; } static int i8042_pm_resume(struct device *dev) { + int i; + + for (i = 0; i < I8042_NUM_PORTS; i++) { + struct serio *serio = i8042_ports[i].serio; + + if (serio && device_may_wakeup(&serio->dev)) + disable_irq_wake(i8042_ports[i].irq); + } + /* * On resume from S2R we always try to reset the controller * to bring it in a sane state. (In case of S2D we expect @@ -1300,13 +1319,16 @@ static void __init i8042_register_ports(void) int i; for (i = 0; i < I8042_NUM_PORTS; i++) { - if (i8042_ports[i].serio) { + struct serio *serio = i8042_ports[i].serio; + + if (serio) { printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n", - i8042_ports[i].serio->name, + serio->name, (unsigned long) I8042_DATA_REG, (unsigned long) I8042_COMMAND_REG, i8042_ports[i].irq); - serio_register_port(i8042_ports[i].serio); + serio_register_port(serio); + device_set_wakeup_capable(&serio->dev, true); } } } diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 6261fd6d7c3c4d..547f67d653728a 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -140,6 +140,19 @@ config TOUCHSCREEN_BU21013 To compile this driver as a module, choose M here: the module will be called bu21013_ts. +config TOUCHSCREEN_CHIPONE_ICN8318 + tristate "chipone icn8318 touchscreen controller" + depends on GPIOLIB + depends on I2C + depends on OF + help + Say Y here if you have a ChipOne icn8318 based I2C touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called chipone_icn8318. + config TOUCHSCREEN_CY8CTMG110 tristate "cy8ctmg110 touchscreen" depends on I2C @@ -297,11 +310,12 @@ config TOUCHSCREEN_FUJITSU config TOUCHSCREEN_GOODIX tristate "Goodix I2C touchscreen" - depends on I2C && ACPI + depends on I2C help Say Y here if you have the Goodix touchscreen (such as one installed in Onda v975w tablets) connected to your - system. + system. It also supports 5-finger chip models, which can be + found on ARM tablets, like Wexler TAB7200 and MSI Primo73. If unsure, say N. @@ -323,6 +337,18 @@ config TOUCHSCREEN_ILI210X To compile this driver as a module, choose M here: the module will be called ili210x. +config TOUCHSCREEN_IPROC + tristate "IPROC touch panel driver support" + depends on ARCH_BCM_IPROC || COMPILE_TEST + help + Say Y here if you want to add support for the IPROC touch + controller to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called bcm_iproc_tsc. + config TOUCHSCREEN_S3C2410 tristate "Samsung S3C2410/generic touchscreen input driver" depends on ARCH_S3C24XX || SAMSUNG_DEV_TS @@ -962,6 +988,17 @@ config TOUCHSCREEN_SUR40 To compile this driver as a module, choose M here: the module will be called sur40. +config TOUCHSCREEN_SX8654 + tristate "Semtech SX8654 touchscreen" + depends on I2C + help + Say Y here if you have a Semtech SX8654 touchscreen controller. + + If unsure, say N + + To compile this driver as a module, choose M here: the + module will be called sx8654. + config TOUCHSCREEN_TPS6507X tristate "TPS6507x based touchscreens" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 0242fea2102ac7..44deea743d0225 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o +obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o @@ -39,6 +40,7 @@ obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o +obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o @@ -79,5 +81,6 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o +obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c index ba30578e296e4e..f0b954d46a2597 100644 --- a/drivers/input/touchscreen/ar1021_i2c.c +++ b/drivers/input/touchscreen/ar1021_i2c.c @@ -157,7 +157,7 @@ static const struct i2c_device_id ar1021_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id); -static struct of_device_id ar1021_i2c_of_match[] = { +static const struct of_device_id ar1021_i2c_of_match[] = { { .compatible = "microchip,ar1021-i2c", }, { } }; diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 95ee92a91bd213..2875ddf3728952 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -25,6 +25,7 @@ #include #include #include +#include /* Version */ #define MXT_VER_20 20 @@ -79,6 +80,7 @@ #define MXT_SPT_DIGITIZER_T43 43 #define MXT_SPT_MESSAGECOUNT_T44 44 #define MXT_SPT_CTECONFIG_T46 46 +#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100 /* MXT_GEN_MESSAGE_T5 object */ #define MXT_RPTID_NOMSG 0xff @@ -185,6 +187,36 @@ struct t9_range { #define MXT_RESET_VALUE 0x01 #define MXT_BACKUP_VALUE 0x55 +/* T100 Multiple Touch Touchscreen */ +#define MXT_T100_CTRL 0 +#define MXT_T100_CFG1 1 +#define MXT_T100_TCHAUX 3 +#define MXT_T100_XRANGE 13 +#define MXT_T100_YRANGE 24 + +#define MXT_T100_CFG_SWITCHXY BIT(5) + +#define MXT_T100_TCHAUX_VECT BIT(0) +#define MXT_T100_TCHAUX_AMPL BIT(1) +#define MXT_T100_TCHAUX_AREA BIT(2) + +#define MXT_T100_DETECT BIT(7) +#define MXT_T100_TYPE_MASK 0x70 + +enum t100_type { + MXT_T100_TYPE_FINGER = 1, + MXT_T100_TYPE_PASSIVE_STYLUS = 2, + MXT_T100_TYPE_HOVERING_FINGER = 4, + MXT_T100_TYPE_GLOVE = 5, + MXT_T100_TYPE_LARGE_TOUCH = 6, +}; + +#define MXT_DISTANCE_ACTIVE_TOUCH 0 +#define MXT_DISTANCE_HOVERING 1 + +#define MXT_TOUCH_MAJOR_DEFAULT 1 +#define MXT_PRESSURE_DEFAULT 1 + /* Delay times */ #define MXT_BACKUP_TIME 50 /* msec */ #define MXT_RESET_TIME 200 /* msec */ @@ -244,6 +276,9 @@ struct mxt_data { unsigned int max_y; bool in_bootloader; u16 mem_size; + u8 t100_aux_ampl; + u8 t100_aux_area; + u8 t100_aux_vect; u8 max_reportid; u32 config_crc; u32 info_crc; @@ -253,6 +288,7 @@ struct mxt_data { bool update_input; u8 last_message_count; u8 num_touchids; + u8 multitouch; /* Cached parameters from object table */ u16 T5_address; @@ -264,6 +300,8 @@ struct mxt_data { u8 T9_reportid_max; u8 T19_reportid; u16 T44_address; + u8 T100_reportid_min; + u8 T100_reportid_max; /* for fw update in bootloader */ struct completion bl_completion; @@ -771,6 +809,114 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) data->update_input = true; } +static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev = data->input_dev; + int id; + u8 status; + u8 type = 0; + u16 x; + u16 y; + int distance = 0; + int tool = 0; + u8 major = 0; + u8 pressure = 0; + u8 orientation = 0; + + id = message[0] - data->T100_reportid_min - 2; + + /* ignore SCRSTATUS events */ + if (id < 0) + return; + + status = message[1]; + x = get_unaligned_le16(&message[2]); + y = get_unaligned_le16(&message[4]); + + if (status & MXT_T100_DETECT) { + type = (status & MXT_T100_TYPE_MASK) >> 4; + + switch (type) { + case MXT_T100_TYPE_HOVERING_FINGER: + tool = MT_TOOL_FINGER; + distance = MXT_DISTANCE_HOVERING; + + if (data->t100_aux_vect) + orientation = message[data->t100_aux_vect]; + + break; + + case MXT_T100_TYPE_FINGER: + case MXT_T100_TYPE_GLOVE: + tool = MT_TOOL_FINGER; + distance = MXT_DISTANCE_ACTIVE_TOUCH; + + if (data->t100_aux_area) + major = message[data->t100_aux_area]; + + if (data->t100_aux_ampl) + pressure = message[data->t100_aux_ampl]; + + if (data->t100_aux_vect) + orientation = message[data->t100_aux_vect]; + + break; + + case MXT_T100_TYPE_PASSIVE_STYLUS: + tool = MT_TOOL_PEN; + + /* + * Passive stylus is reported with size zero so + * hardcode. + */ + major = MXT_TOUCH_MAJOR_DEFAULT; + + if (data->t100_aux_ampl) + pressure = message[data->t100_aux_ampl]; + + break; + + case MXT_T100_TYPE_LARGE_TOUCH: + /* Ignore suppressed touch */ + break; + + default: + dev_dbg(dev, "Unexpected T100 type\n"); + return; + } + } + + /* + * Values reported should be non-zero if tool is touching the + * device + */ + if (!pressure && type != MXT_T100_TYPE_HOVERING_FINGER) + pressure = MXT_PRESSURE_DEFAULT; + + input_mt_slot(input_dev, id); + + if (status & MXT_T100_DETECT) { + dev_dbg(dev, "[%u] type:%u x:%u y:%u a:%02X p:%02X v:%02X\n", + id, type, x, y, major, pressure, orientation); + + input_mt_report_slot_state(input_dev, tool, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major); + input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); + input_report_abs(input_dev, ABS_MT_DISTANCE, distance); + input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation); + } else { + dev_dbg(dev, "[%u] release\n", id); + + /* close out slot */ + input_mt_report_slot_state(input_dev, 0, 0); + } + + data->update_input = true; +} + static int mxt_proc_message(struct mxt_data *data, u8 *message) { u8 report_id = message[0]; @@ -786,9 +932,12 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) * is not yet registered. */ mxt_dump_message(data, message); - } else if (report_id >= data->T9_reportid_min - && report_id <= data->T9_reportid_max) { + } else if (report_id >= data->T9_reportid_min && + report_id <= data->T9_reportid_max) { mxt_proc_t9_message(data, message); + } else if (report_id >= data->T100_reportid_min && + report_id <= data->T100_reportid_max) { + mxt_proc_t100_message(data, message); } else if (report_id == data->T19_reportid) { mxt_input_button(data, message); data->update_input = true; @@ -1411,6 +1560,8 @@ static void mxt_free_object_table(struct mxt_data *data) data->T9_reportid_max = 0; data->T19_reportid = 0; data->T44_address = 0; + data->T100_reportid_min = 0; + data->T100_reportid_max = 0; data->max_reportid = 0; } @@ -1487,6 +1638,7 @@ static int mxt_get_object_table(struct mxt_data *data) data->T7_address = object->start_address; break; case MXT_TOUCH_MULTI_T9: + data->multitouch = MXT_TOUCH_MULTI_T9; data->T9_reportid_min = min_id; data->T9_reportid_max = max_id; data->num_touchids = object->num_report_ids @@ -1498,6 +1650,13 @@ static int mxt_get_object_table(struct mxt_data *data) case MXT_SPT_GPIOPWM_T19: data->T19_reportid = min_id; break; + case MXT_TOUCH_MULTITOUCHSCREEN_T100: + data->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100; + data->T100_reportid_min = min_id; + data->T100_reportid_max = max_id; + /* first two report IDs reserved */ + data->num_touchids = object->num_report_ids - 2; + break; } end_address = object->start_address @@ -1582,22 +1741,138 @@ static int mxt_read_t9_resolution(struct mxt_data *data) return 0; } +static int mxt_read_t100_config(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + struct mxt_object *object; + u16 range_x, range_y; + u8 cfg, tchaux; + u8 aux; + + object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100); + if (!object) + return -EINVAL; + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_XRANGE, + sizeof(range_x), &range_x); + if (error) + return error; + + le16_to_cpus(&range_x); + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_YRANGE, + sizeof(range_y), &range_y); + if (error) + return error; + + le16_to_cpus(&range_y); + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_CFG1, + 1, &cfg); + if (error) + return error; + + error = __mxt_read_reg(client, + object->start_address + MXT_T100_TCHAUX, + 1, &tchaux); + if (error) + return error; + + /* Handle default values */ + if (range_x == 0) + range_x = 1023; + + if (range_y == 0) + range_y = 1023; + + if (cfg & MXT_T100_CFG_SWITCHXY) { + data->max_x = range_y; + data->max_y = range_x; + } else { + data->max_x = range_x; + data->max_y = range_y; + } + + /* allocate aux bytes */ + aux = 6; + + if (tchaux & MXT_T100_TCHAUX_VECT) + data->t100_aux_vect = aux++; + + if (tchaux & MXT_T100_TCHAUX_AMPL) + data->t100_aux_ampl = aux++; + + if (tchaux & MXT_T100_TCHAUX_AREA) + data->t100_aux_area = aux++; + + dev_dbg(&client->dev, + "T100 aux mappings vect:%u ampl:%u area:%u\n", + data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area); + + dev_info(&client->dev, + "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y); + + return 0; +} + static int mxt_input_open(struct input_dev *dev); static void mxt_input_close(struct input_dev *dev); -static int mxt_initialize_t9_input_device(struct mxt_data *data) +static void mxt_set_up_as_touchpad(struct input_dev *input_dev, + struct mxt_data *data) { - struct device *dev = &data->client->dev; const struct mxt_platform_data *pdata = data->pdata; + int i; + + input_dev->name = "Atmel maXTouch Touchpad"; + + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + + input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_X, + MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_Y, + MXT_PIXELS_PER_MM); + + for (i = 0; i < pdata->t19_num_keys; i++) + if (pdata->t19_keymap[i] != KEY_RESERVED) + input_set_capability(input_dev, EV_KEY, + pdata->t19_keymap[i]); +} + +static int mxt_initialize_input_device(struct mxt_data *data) +{ + const struct mxt_platform_data *pdata = data->pdata; + struct device *dev = &data->client->dev; struct input_dev *input_dev; int error; unsigned int num_mt_slots; unsigned int mt_flags = 0; - int i; - error = mxt_read_t9_resolution(data); - if (error) - dev_warn(dev, "Failed to initialize T9 resolution\n"); + switch (data->multitouch) { + case MXT_TOUCH_MULTI_T9: + num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; + error = mxt_read_t9_resolution(data); + if (error) + dev_warn(dev, "Failed to initialize T9 resolution\n"); + break; + + case MXT_TOUCH_MULTITOUCHSCREEN_T100: + num_mt_slots = data->num_touchids; + error = mxt_read_t100_config(data); + if (error) + dev_warn(dev, "Failed to read T100 config\n"); + break; + + default: + dev_err(dev, "Invalid multitouch object\n"); + return -EINVAL; + } input_dev = input_allocate_device(); if (!input_dev) { @@ -1612,54 +1887,76 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data) input_dev->open = mxt_input_open; input_dev->close = mxt_input_close; - __set_bit(EV_ABS, input_dev->evbit); - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(BTN_TOUCH, input_dev->keybit); + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); - if (pdata->t19_num_keys) { - __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + /* For single touch */ + input_set_abs_params(input_dev, ABS_X, 0, data->max_x, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, data->max_y, 0, 0); - for (i = 0; i < pdata->t19_num_keys; i++) - if (pdata->t19_keymap[i] != KEY_RESERVED) - input_set_capability(input_dev, EV_KEY, - pdata->t19_keymap[i]); + if (data->multitouch == MXT_TOUCH_MULTI_T9 || + (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_ampl)) { + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); + } + /* If device has buttons we assume it is a touchpad */ + if (pdata->t19_num_keys) { + mxt_set_up_as_touchpad(input_dev, data); mt_flags |= INPUT_MT_POINTER; - - input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_MT_POSITION_X, - MXT_PIXELS_PER_MM); - input_abs_set_res(input_dev, ABS_MT_POSITION_Y, - MXT_PIXELS_PER_MM); - - input_dev->name = "Atmel maXTouch Touchpad"; } - /* For single touch */ - input_set_abs_params(input_dev, ABS_X, - 0, data->max_x, 0, 0); - input_set_abs_params(input_dev, ABS_Y, - 0, data->max_y, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, - 0, 255, 0, 0); - /* For multi touch */ - num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); if (error) { dev_err(dev, "Error %d initialising slots\n", error); goto err_free_mem; } - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, MXT_MAX_AREA, 0, 0); + if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100) { + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_MT_DISTANCE, + MXT_DISTANCE_ACTIVE_TOUCH, + MXT_DISTANCE_HOVERING, + 0, 0); + } + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, data->max_x, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, data->max_y, 0, 0); - input_set_abs_params(input_dev, ABS_MT_PRESSURE, - 0, 255, 0, 0); + + if (data->multitouch == MXT_TOUCH_MULTI_T9 || + (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_area)) { + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, MXT_MAX_AREA, 0, 0); + } + + if (data->multitouch == MXT_TOUCH_MULTI_T9 || + (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_ampl)) { + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, 255, 0, 0); + } + + if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_vect) { + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, + 0, 255, 0, 0); + } + + if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_ampl) { + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, 255, 0, 0); + } + + if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_vect) { + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, + 0, 255, 0, 0); + } input_set_drvdata(input_dev, data); @@ -1765,9 +2062,13 @@ static int mxt_configure_objects(struct mxt_data *data, dev_warn(dev, "Error %d updating config\n", error); } - error = mxt_initialize_t9_input_device(data); - if (error) - return error; + if (data->multitouch) { + error = mxt_initialize_input_device(data); + if (error) + return error; + } else { + dev_warn(dev, "No touch object detected\n"); + } dev_info(dev, "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", @@ -2044,15 +2345,13 @@ static const struct attribute_group mxt_attr_group = { static void mxt_start(struct mxt_data *data) { /* Touch enable */ - mxt_write_object(data, - MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83); + mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0x83); } static void mxt_stop(struct mxt_data *data) { /* Touch disable */ - mxt_write_object(data, - MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0); + mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0); } static int mxt_input_open(struct input_dev *dev) diff --git a/drivers/input/touchscreen/bcm_iproc_tsc.c b/drivers/input/touchscreen/bcm_iproc_tsc.c new file mode 100644 index 00000000000000..ae460a5c93d5e3 --- /dev/null +++ b/drivers/input/touchscreen/bcm_iproc_tsc.c @@ -0,0 +1,522 @@ +/* +* Copyright (C) 2015 Broadcom Corporation +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation version 2. +* +* This program is distributed "as is" WITHOUT ANY WARRANTY of any +* kind, whether express or implied; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPROC_TS_NAME "iproc-ts" + +#define PEN_DOWN_STATUS 1 +#define PEN_UP_STATUS 0 + +#define X_MIN 0 +#define Y_MIN 0 +#define X_MAX 0xFFF +#define Y_MAX 0xFFF + +/* Value given by controller for invalid coordinate. */ +#define INVALID_COORD 0xFFFFFFFF + +/* Register offsets */ +#define REGCTL1 0x00 +#define REGCTL2 0x04 +#define INTERRUPT_THRES 0x08 +#define INTERRUPT_MASK 0x0c + +#define INTERRUPT_STATUS 0x10 +#define CONTROLLER_STATUS 0x14 +#define FIFO_DATA 0x18 +#define FIFO_DATA_X_Y_MASK 0xFFFF +#define ANALOG_CONTROL 0x1c + +#define AUX_DATA 0x20 +#define DEBOUNCE_CNTR_STAT 0x24 +#define SCAN_CNTR_STAT 0x28 +#define REM_CNTR_STAT 0x2c + +#define SETTLING_TIMER_STAT 0x30 +#define SPARE_REG 0x34 +#define SOFT_BYPASS_CONTROL 0x38 +#define SOFT_BYPASS_DATA 0x3c + + +/* Bit values for INTERRUPT_MASK and INTERRUPT_STATUS regs */ +#define TS_PEN_INTR_MASK BIT(0) +#define TS_FIFO_INTR_MASK BIT(2) + +/* Bit values for CONTROLLER_STATUS reg1 */ +#define TS_PEN_DOWN BIT(0) + +/* Shift values for control reg1 */ +#define SCANNING_PERIOD_SHIFT 24 +#define DEBOUNCE_TIMEOUT_SHIFT 16 +#define SETTLING_TIMEOUT_SHIFT 8 +#define TOUCH_TIMEOUT_SHIFT 0 + +/* Shift values for coordinates from fifo */ +#define X_COORD_SHIFT 0 +#define Y_COORD_SHIFT 16 + +/* Bit values for REGCTL2 */ +#define TS_CONTROLLER_EN_BIT BIT(16) +#define TS_CONTROLLER_AVGDATA_SHIFT 8 +#define TS_CONTROLLER_AVGDATA_MASK (0x7 << TS_CONTROLLER_AVGDATA_SHIFT) +#define TS_CONTROLLER_PWR_LDO BIT(5) +#define TS_CONTROLLER_PWR_ADC BIT(4) +#define TS_CONTROLLER_PWR_BGP BIT(3) +#define TS_CONTROLLER_PWR_TS BIT(2) +#define TS_WIRE_MODE_BIT BIT(1) + +#define dbg_reg(dev, priv, reg) \ + dev_dbg(dev, "%20s= 0x%08x\n", #reg, readl((priv)->regs + reg)) + +struct tsc_param { + /* Each step is 1024 us. Valid 1-256 */ + u32 scanning_period; + + /* Each step is 512 us. Valid 0-255 */ + u32 debounce_timeout; + + /* + * The settling duration (in ms) is the amount of time the tsc + * waits to allow the voltage to settle after turning on the + * drivers in detection mode. Valid values: 0-11 + * 0 = 0.008 ms + * 1 = 0.01 ms + * 2 = 0.02 ms + * 3 = 0.04 ms + * 4 = 0.08 ms + * 5 = 0.16 ms + * 6 = 0.32 ms + * 7 = 0.64 ms + * 8 = 1.28 ms + * 9 = 2.56 ms + * 10 = 5.12 ms + * 11 = 10.24 ms + */ + u32 settling_timeout; + + /* touch timeout in sample counts */ + u32 touch_timeout; + + /* + * Number of data samples which are averaged before a final data point + * is placed into the FIFO + */ + u32 average_data; + + /* FIFO threshold */ + u32 fifo_threshold; + + /* Optional standard touchscreen properties. */ + u32 max_x; + u32 max_y; + u32 fuzz_x; + u32 fuzz_y; + bool invert_x; + bool invert_y; +}; + +struct iproc_ts_priv { + struct platform_device *pdev; + struct input_dev *idev; + + void __iomem *regs; + struct clk *tsc_clk; + + int pen_status; + struct tsc_param cfg_params; +}; + +/* + * Set default values the same as hardware reset values + * except for fifo_threshold with is set to 1. + */ +static const struct tsc_param iproc_default_config = { + .scanning_period = 0x5, /* 1 to 256 */ + .debounce_timeout = 0x28, /* 0 to 255 */ + .settling_timeout = 0x7, /* 0 to 11 */ + .touch_timeout = 0xa, /* 0 to 255 */ + .average_data = 5, /* entry 5 = 32 pts */ + .fifo_threshold = 1, /* 0 to 31 */ + .max_x = X_MAX, + .max_y = Y_MAX, +}; + +static void ts_reg_dump(struct iproc_ts_priv *priv) +{ + struct device *dev = &priv->pdev->dev; + + dbg_reg(dev, priv, REGCTL1); + dbg_reg(dev, priv, REGCTL2); + dbg_reg(dev, priv, INTERRUPT_THRES); + dbg_reg(dev, priv, INTERRUPT_MASK); + dbg_reg(dev, priv, INTERRUPT_STATUS); + dbg_reg(dev, priv, CONTROLLER_STATUS); + dbg_reg(dev, priv, FIFO_DATA); + dbg_reg(dev, priv, ANALOG_CONTROL); + dbg_reg(dev, priv, AUX_DATA); + dbg_reg(dev, priv, DEBOUNCE_CNTR_STAT); + dbg_reg(dev, priv, SCAN_CNTR_STAT); + dbg_reg(dev, priv, REM_CNTR_STAT); + dbg_reg(dev, priv, SETTLING_TIMER_STAT); + dbg_reg(dev, priv, SPARE_REG); + dbg_reg(dev, priv, SOFT_BYPASS_CONTROL); + dbg_reg(dev, priv, SOFT_BYPASS_DATA); +} + +static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data) +{ + struct platform_device *pdev = data; + struct iproc_ts_priv *priv = platform_get_drvdata(pdev); + u32 intr_status; + u32 raw_coordinate; + u16 x; + u16 y; + int i; + bool needs_sync = false; + + intr_status = readl(priv->regs + INTERRUPT_STATUS); + intr_status &= TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; + if (intr_status == 0) + return IRQ_NONE; + + /* Clear all interrupt status bits, write-1-clear */ + writel(intr_status, priv->regs + INTERRUPT_STATUS); + + /* Pen up/down */ + if (intr_status & TS_PEN_INTR_MASK) { + if (readl(priv->regs + CONTROLLER_STATUS) & TS_PEN_DOWN) + priv->pen_status = PEN_DOWN_STATUS; + else + priv->pen_status = PEN_UP_STATUS; + + input_report_key(priv->idev, BTN_TOUCH, priv->pen_status); + needs_sync = true; + + dev_dbg(&priv->pdev->dev, + "pen up-down (%d)\n", priv->pen_status); + } + + /* coordinates in FIFO exceed the theshold */ + if (intr_status & TS_FIFO_INTR_MASK) { + for (i = 0; i < priv->cfg_params.fifo_threshold; i++) { + raw_coordinate = readl(priv->regs + FIFO_DATA); + if (raw_coordinate == INVALID_COORD) + continue; + + /* + * The x and y coordinate are 16 bits each + * with the x in the lower 16 bits and y in the + * upper 16 bits. + */ + x = (raw_coordinate >> X_COORD_SHIFT) & + FIFO_DATA_X_Y_MASK; + y = (raw_coordinate >> Y_COORD_SHIFT) & + FIFO_DATA_X_Y_MASK; + + /* We only want to retain the 12 msb of the 16 */ + x = (x >> 4) & 0x0FFF; + y = (y >> 4) & 0x0FFF; + + /* adjust x y according to lcd tsc mount angle */ + if (priv->cfg_params.invert_x) + x = priv->cfg_params.max_x - x; + + if (priv->cfg_params.invert_y) + y = priv->cfg_params.max_y - y; + + input_report_abs(priv->idev, ABS_X, x); + input_report_abs(priv->idev, ABS_Y, y); + needs_sync = true; + + dev_dbg(&priv->pdev->dev, "xy (0x%x 0x%x)\n", x, y); + } + } + + if (needs_sync) + input_sync(priv->idev); + + return IRQ_HANDLED; +} + +static int iproc_ts_start(struct input_dev *idev) +{ + struct iproc_ts_priv *priv = input_get_drvdata(idev); + u32 val; + int error; + + /* Enable clock */ + error = clk_prepare_enable(priv->tsc_clk); + if (error) { + dev_err(&priv->pdev->dev, "%s clk_prepare_enable failed %d\n", + __func__, error); + return error; + } + + /* + * Interrupt is generated when: + * FIFO reaches the int_th value, and pen event(up/down) + */ + val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK; + writel(val, priv->regs + INTERRUPT_MASK); + + writel(priv->cfg_params.fifo_threshold, priv->regs + INTERRUPT_THRES); + + /* Initialize control reg1 */ + val = 0; + val |= priv->cfg_params.scanning_period << SCANNING_PERIOD_SHIFT; + val |= priv->cfg_params.debounce_timeout << DEBOUNCE_TIMEOUT_SHIFT; + val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT; + val |= priv->cfg_params.touch_timeout << TOUCH_TIMEOUT_SHIFT; + writel(val, priv->regs + REGCTL1); + + /* Try to clear all interrupt status */ + val = readl(priv->regs + INTERRUPT_STATUS); + val |= TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK; + writel(val, priv->regs + INTERRUPT_STATUS); + + /* Initialize control reg2 */ + val = readl(priv->regs + REGCTL2); + val |= TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT; + + val &= ~TS_CONTROLLER_AVGDATA_MASK; + val |= priv->cfg_params.average_data << TS_CONTROLLER_AVGDATA_SHIFT; + + val &= ~(TS_CONTROLLER_PWR_LDO | /* PWR up LDO */ + TS_CONTROLLER_PWR_ADC | /* PWR up ADC */ + TS_CONTROLLER_PWR_BGP | /* PWR up BGP */ + TS_CONTROLLER_PWR_TS); /* PWR up TS */ + + writel(val, priv->regs + REGCTL2); + + ts_reg_dump(priv); + + return 0; +} + +static void iproc_ts_stop(struct input_dev *dev) +{ + u32 val; + struct iproc_ts_priv *priv = input_get_drvdata(dev); + + writel(0, priv->regs + INTERRUPT_MASK); /* Disable all interrupts */ + + /* Only power down touch screen controller */ + val = readl(priv->regs + REGCTL2); + val |= TS_CONTROLLER_PWR_TS; + writel(val, priv->regs + REGCTL2); + + clk_disable(priv->tsc_clk); +} + +static int iproc_get_tsc_config(struct device *dev, struct iproc_ts_priv *priv) +{ + struct device_node *np = dev->of_node; + u32 val; + + priv->cfg_params = iproc_default_config; + + if (!np) + return 0; + + if (of_property_read_u32(np, "scanning_period", &val) >= 0) { + if (val < 1 || val > 256) { + dev_err(dev, "scanning_period (%u) must be [1-256]\n", + val); + return -EINVAL; + } + priv->cfg_params.scanning_period = val; + } + + if (of_property_read_u32(np, "debounce_timeout", &val) >= 0) { + if (val > 255) { + dev_err(dev, "debounce_timeout (%u) must be [0-255]\n", + val); + return -EINVAL; + } + priv->cfg_params.debounce_timeout = val; + } + + if (of_property_read_u32(np, "settling_timeout", &val) >= 0) { + if (val > 11) { + dev_err(dev, "settling_timeout (%u) must be [0-11]\n", + val); + return -EINVAL; + } + priv->cfg_params.settling_timeout = val; + } + + if (of_property_read_u32(np, "touch_timeout", &val) >= 0) { + if (val > 255) { + dev_err(dev, "touch_timeout (%u) must be [0-255]\n", + val); + return -EINVAL; + } + priv->cfg_params.touch_timeout = val; + } + + if (of_property_read_u32(np, "average_data", &val) >= 0) { + if (val > 8) { + dev_err(dev, "average_data (%u) must be [0-8]\n", val); + return -EINVAL; + } + priv->cfg_params.average_data = val; + } + + if (of_property_read_u32(np, "fifo_threshold", &val) >= 0) { + if (val > 31) { + dev_err(dev, "fifo_threshold (%u)) must be [0-31]\n", + val); + return -EINVAL; + } + priv->cfg_params.fifo_threshold = val; + } + + /* Parse optional properties. */ + of_property_read_u32(np, "touchscreen-size-x", &priv->cfg_params.max_x); + of_property_read_u32(np, "touchscreen-size-y", &priv->cfg_params.max_y); + + of_property_read_u32(np, "touchscreen-fuzz-x", + &priv->cfg_params.fuzz_x); + of_property_read_u32(np, "touchscreen-fuzz-y", + &priv->cfg_params.fuzz_y); + + priv->cfg_params.invert_x = + of_property_read_bool(np, "touchscreen-inverted-x"); + priv->cfg_params.invert_y = + of_property_read_bool(np, "touchscreen-inverted-y"); + + return 0; +} + +static int iproc_ts_probe(struct platform_device *pdev) +{ + struct iproc_ts_priv *priv; + struct input_dev *idev; + struct resource *res; + int irq; + int error; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* touchscreen controller memory mapped regs */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->regs)) { + error = PTR_ERR(priv->regs); + dev_err(&pdev->dev, "unable to map I/O memory: %d\n", error); + return error; + } + + priv->tsc_clk = devm_clk_get(&pdev->dev, "tsc_clk"); + if (IS_ERR(priv->tsc_clk)) { + error = PTR_ERR(priv->tsc_clk); + dev_err(&pdev->dev, + "failed getting clock tsc_clk: %d\n", error); + return error; + } + + priv->pdev = pdev; + error = iproc_get_tsc_config(&pdev->dev, priv); + if (error) { + dev_err(&pdev->dev, "get_tsc_config failed: %d\n", error); + return error; + } + + idev = devm_input_allocate_device(&pdev->dev); + if (!idev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + priv->idev = idev; + priv->pen_status = PEN_UP_STATUS; + + /* Set input device info */ + idev->name = IPROC_TS_NAME; + idev->dev.parent = &pdev->dev; + + idev->id.bustype = BUS_HOST; + idev->id.vendor = SERIO_UNKNOWN; + idev->id.product = 0; + idev->id.version = 0; + + idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + __set_bit(BTN_TOUCH, idev->keybit); + + input_set_abs_params(idev, ABS_X, X_MIN, priv->cfg_params.max_x, + priv->cfg_params.fuzz_x, 0); + input_set_abs_params(idev, ABS_Y, Y_MIN, priv->cfg_params.max_y, + priv->cfg_params.fuzz_y, 0); + + idev->open = iproc_ts_start; + idev->close = iproc_ts_stop; + + input_set_drvdata(idev, priv); + platform_set_drvdata(pdev, priv); + + /* get interrupt */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "platform_get_irq failed: %d\n", irq); + return irq; + } + + error = devm_request_irq(&pdev->dev, irq, + iproc_touchscreen_interrupt, + IRQF_SHARED, IPROC_TS_NAME, pdev); + if (error) + return error; + + error = input_register_device(priv->idev); + if (error) { + dev_err(&pdev->dev, + "failed to register input device: %d\n", error); + return error; + } + + return 0; +} + +static const struct of_device_id iproc_ts_of_match[] = { + {.compatible = "brcm,iproc-touchscreen", }, + { }, +}; +MODULE_DEVICE_TABLE(of, iproc_ts_of_match); + +static struct platform_driver iproc_ts_driver = { + .probe = iproc_ts_probe, + .driver = { + .name = IPROC_TS_NAME, + .of_match_table = of_match_ptr(iproc_ts_of_match), + }, +}; + +module_platform_driver(iproc_ts_driver); + +MODULE_DESCRIPTION("IPROC Touchscreen driver"); +MODULE_AUTHOR("Broadcom"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/chipone_icn8318.c b/drivers/input/touchscreen/chipone_icn8318.c new file mode 100644 index 00000000000000..32e9db0e04bfd0 --- /dev/null +++ b/drivers/input/touchscreen/chipone_icn8318.c @@ -0,0 +1,316 @@ +/* + * Driver for ChipOne icn8318 i2c touchscreen controller + * + * Copyright (c) 2015 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Red Hat authors: + * Hans de Goede + */ + +#include +#include +#include +#include +#include +#include +#include + +#define ICN8318_REG_POWER 4 +#define ICN8318_REG_TOUCHDATA 16 + +#define ICN8318_POWER_ACTIVE 0 +#define ICN8318_POWER_MONITOR 1 +#define ICN8318_POWER_HIBERNATE 2 + +#define ICN8318_MAX_TOUCHES 5 + +struct icn8318_touch { + __u8 slot; + __be16 x; + __be16 y; + __u8 pressure; /* Seems more like finger width then pressure really */ + __u8 event; +/* The difference between 2 and 3 is unclear */ +#define ICN8318_EVENT_NO_DATA 1 /* No finger seen yet since wakeup */ +#define ICN8318_EVENT_UPDATE1 2 /* New or updated coordinates */ +#define ICN8318_EVENT_UPDATE2 3 /* New or updated coordinates */ +#define ICN8318_EVENT_END 4 /* Finger lifted */ +} __packed; + +struct icn8318_touch_data { + __u8 softbutton; + __u8 touch_count; + struct icn8318_touch touches[ICN8318_MAX_TOUCHES]; +} __packed; + +struct icn8318_data { + struct i2c_client *client; + struct input_dev *input; + struct gpio_desc *wake_gpio; + u32 max_x; + u32 max_y; + bool invert_x; + bool invert_y; + bool swap_x_y; +}; + +static int icn8318_read_touch_data(struct i2c_client *client, + struct icn8318_touch_data *touch_data) +{ + u8 reg = ICN8318_REG_TOUCHDATA; + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .len = 1, + .buf = ® + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = sizeof(struct icn8318_touch_data), + .buf = (u8 *)touch_data + } + }; + + return i2c_transfer(client->adapter, msg, 2); +} + +static inline bool icn8318_touch_active(u8 event) +{ + return (event == ICN8318_EVENT_UPDATE1) || + (event == ICN8318_EVENT_UPDATE2); +} + +static irqreturn_t icn8318_irq(int irq, void *dev_id) +{ + struct icn8318_data *data = dev_id; + struct device *dev = &data->client->dev; + struct icn8318_touch_data touch_data; + int i, ret, x, y; + + ret = icn8318_read_touch_data(data->client, &touch_data); + if (ret < 0) { + dev_err(dev, "Error reading touch data: %d\n", ret); + return IRQ_HANDLED; + } + + if (touch_data.softbutton) { + /* + * Other data is invalid when a softbutton is pressed. + * This needs some extra devicetree bindings to map the icn8318 + * softbutton codes to evdev codes. Currently no known devices + * use this. + */ + return IRQ_HANDLED; + } + + if (touch_data.touch_count > ICN8318_MAX_TOUCHES) { + dev_warn(dev, "Too much touches %d > %d\n", + touch_data.touch_count, ICN8318_MAX_TOUCHES); + touch_data.touch_count = ICN8318_MAX_TOUCHES; + } + + for (i = 0; i < touch_data.touch_count; i++) { + struct icn8318_touch *touch = &touch_data.touches[i]; + bool act = icn8318_touch_active(touch->event); + + input_mt_slot(data->input, touch->slot); + input_mt_report_slot_state(data->input, MT_TOOL_FINGER, act); + if (!act) + continue; + + x = be16_to_cpu(touch->x); + y = be16_to_cpu(touch->y); + + if (data->invert_x) + x = data->max_x - x; + + if (data->invert_y) + y = data->max_y - y; + + if (!data->swap_x_y) { + input_event(data->input, EV_ABS, ABS_MT_POSITION_X, x); + input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, y); + } else { + input_event(data->input, EV_ABS, ABS_MT_POSITION_X, y); + input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, x); + } + } + + input_mt_sync_frame(data->input); + input_sync(data->input); + + return IRQ_HANDLED; +} + +static int icn8318_start(struct input_dev *dev) +{ + struct icn8318_data *data = input_get_drvdata(dev); + + enable_irq(data->client->irq); + gpiod_set_value_cansleep(data->wake_gpio, 1); + + return 0; +} + +static void icn8318_stop(struct input_dev *dev) +{ + struct icn8318_data *data = input_get_drvdata(dev); + + disable_irq(data->client->irq); + i2c_smbus_write_byte_data(data->client, ICN8318_REG_POWER, + ICN8318_POWER_HIBERNATE); + gpiod_set_value_cansleep(data->wake_gpio, 0); +} + +#ifdef CONFIG_PM_SLEEP +static int icn8318_suspend(struct device *dev) +{ + struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev)); + + mutex_lock(&data->input->mutex); + if (data->input->users) + icn8318_stop(data->input); + mutex_unlock(&data->input->mutex); + + return 0; +} + +static int icn8318_resume(struct device *dev) +{ + struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev)); + + mutex_lock(&data->input->mutex); + if (data->input->users) + icn8318_start(data->input); + mutex_unlock(&data->input->mutex); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(icn8318_pm_ops, icn8318_suspend, icn8318_resume); + +static int icn8318_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *np = dev->of_node; + struct icn8318_data *data; + struct input_dev *input; + u32 fuzz_x = 0, fuzz_y = 0; + int error; + + if (!client->irq) { + dev_err(dev, "Error no irq specified\n"); + return -EINVAL; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->wake_gpio = devm_gpiod_get(dev, "wake", GPIOD_OUT_LOW); + if (IS_ERR(data->wake_gpio)) { + error = PTR_ERR(data->wake_gpio); + if (error != -EPROBE_DEFER) + dev_err(dev, "Error getting wake gpio: %d\n", error); + return error; + } + + if (of_property_read_u32(np, "touchscreen-size-x", &data->max_x) || + of_property_read_u32(np, "touchscreen-size-y", &data->max_y)) { + dev_err(dev, "Error touchscreen-size-x and/or -y missing\n"); + return -EINVAL; + } + + /* Optional */ + of_property_read_u32(np, "touchscreen-fuzz-x", &fuzz_x); + of_property_read_u32(np, "touchscreen-fuzz-y", &fuzz_y); + data->invert_x = of_property_read_bool(np, "touchscreen-inverted-x"); + data->invert_y = of_property_read_bool(np, "touchscreen-inverted-y"); + data->swap_x_y = of_property_read_bool(np, "touchscreen-swapped-x-y"); + + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + input->name = client->name; + input->id.bustype = BUS_I2C; + input->open = icn8318_start; + input->close = icn8318_stop; + input->dev.parent = dev; + + if (!data->swap_x_y) { + input_set_abs_params(input, ABS_MT_POSITION_X, 0, + data->max_x, fuzz_x, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, + data->max_y, fuzz_y, 0); + } else { + input_set_abs_params(input, ABS_MT_POSITION_X, 0, + data->max_y, fuzz_y, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, + data->max_x, fuzz_x, 0); + } + + error = input_mt_init_slots(input, ICN8318_MAX_TOUCHES, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) + return error; + + data->client = client; + data->input = input; + input_set_drvdata(input, data); + + error = devm_request_threaded_irq(dev, client->irq, NULL, icn8318_irq, + IRQF_ONESHOT, client->name, data); + if (error) { + dev_err(dev, "Error requesting irq: %d\n", error); + return error; + } + + /* Stop device till opened */ + icn8318_stop(data->input); + + error = input_register_device(input); + if (error) + return error; + + i2c_set_clientdata(client, data); + + return 0; +} + +static const struct of_device_id icn8318_of_match[] = { + { .compatible = "chipone,icn8318" }, + { } +}; +MODULE_DEVICE_TABLE(of, icn8318_of_match); + +/* This is useless for OF-enabled devices, but it is needed by I2C subsystem */ +static const struct i2c_device_id icn8318_i2c_id[] = { + { }, +}; +MODULE_DEVICE_TABLE(i2c, icn8318_i2c_id); + +static struct i2c_driver icn8318_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "chipone_icn8318", + .pm = &icn8318_pm_ops, + .of_match_table = icn8318_of_match, + }, + .probe = icn8318_probe, + .id_table = icn8318_i2c_id, +}; + +module_i2c_driver(icn8318_driver); + +MODULE_DESCRIPTION("ChipOne icn8318 I2C Touchscreen Driver"); +MODULE_AUTHOR("Hans de Goede "); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index d4c24fb7704f5e..e6aef3e48bd920 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #define MAX_SUPPORT_POINTS 5 @@ -1034,7 +1035,6 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, input->id.bustype = BUS_I2C; input->dev.parent = &client->dev; - __set_bit(EV_SYN, input->evbit); __set_bit(EV_KEY, input->evbit); __set_bit(EV_ABS, input->evbit); __set_bit(BTN_TOUCH, input->keybit); @@ -1044,6 +1044,10 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, 0, tsdata->num_x * 64 - 1, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, 0, tsdata->num_y * 64 - 1, 0, 0); + + if (!pdata) + touchscreen_parse_of_params(input); + error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0); if (error) { dev_err(&client->dev, "Unable to init MT slots.\n"); diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 926c58e540c089..43b3c9c2d788ed 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -98,7 +98,6 @@ #define MAX_FW_UPDATE_RETRIES 30 #define ELAN_FW_PAGESIZE 132 -#define ELAN_FW_FILENAME "elants_i2c.bin" /* calibration timeout definition */ #define ELAN_CALI_TIMEOUT_MSEC 10000 @@ -697,12 +696,19 @@ static int elants_i2c_fw_update(struct elants_data *ts) { struct i2c_client *client = ts->client; const struct firmware *fw; + char *fw_name; int error; - error = request_firmware(&fw, ELAN_FW_FILENAME, &client->dev); + fw_name = kasprintf(GFP_KERNEL, "elants_i2c_%4x.bin", ts->hw_version); + if (!fw_name) + return -ENOMEM; + + dev_info(&client->dev, "requesting fw name = %s\n", fw_name); + error = request_firmware(&fw, fw_name, &client->dev); + kfree(fw_name); if (error) { - dev_err(&client->dev, "failed to request firmware %s: %d\n", - ELAN_FW_FILENAME, error); + dev_err(&client->dev, "failed to request firmware: %d\n", + error); return error; } diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index ca196689f02522..3af16984d57c90 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include struct goodix_ts_data { @@ -48,6 +50,7 @@ struct goodix_ts_data { #define GOODIX_REG_VERSION 0x8140 #define RESOLUTION_LOC 1 +#define MAX_CONTACTS_LOC 5 #define TRIGGER_LOC 6 static const unsigned long goodix_irq_flags[] = { @@ -99,7 +102,7 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) } touch_num = data[0] & 0x0f; - if (touch_num > GOODIX_MAX_CONTACTS) + if (touch_num > ts->max_touch_num) return -EPROTO; if (touch_num > 1) { @@ -141,7 +144,7 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) */ static void goodix_process_events(struct goodix_ts_data *ts) { - u8 point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS]; + u8 point_data[1 + GOODIX_CONTACT_SIZE * ts->max_touch_num]; int touch_num; int i; @@ -202,21 +205,23 @@ static void goodix_read_config(struct goodix_ts_data *ts) ts->abs_x_max = GOODIX_MAX_WIDTH; ts->abs_y_max = GOODIX_MAX_HEIGHT; ts->int_trigger_type = GOODIX_INT_TRIGGER; + ts->max_touch_num = GOODIX_MAX_CONTACTS; return; } ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]); ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); - ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03; - if (!ts->abs_x_max || !ts->abs_y_max) { + ts->int_trigger_type = config[TRIGGER_LOC] & 0x03; + ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f; + if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) { dev_err(&ts->client->dev, "Invalid config, using defaults\n"); ts->abs_x_max = GOODIX_MAX_WIDTH; ts->abs_y_max = GOODIX_MAX_HEIGHT; + ts->max_touch_num = GOODIX_MAX_CONTACTS; } } - /** * goodix_read_version - Read goodix touchscreen version * @@ -295,7 +300,7 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts) input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); - input_mt_init_slots(ts->input_dev, GOODIX_MAX_CONTACTS, + input_mt_init_slots(ts->input_dev, ts->max_touch_num, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); ts->input_dev->name = "Goodix Capacitive TouchScreen"; @@ -372,11 +377,27 @@ static const struct i2c_device_id goodix_ts_id[] = { { } }; +#ifdef CONFIG_ACPI static const struct acpi_device_id goodix_acpi_match[] = { { "GDIX1001", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, goodix_acpi_match); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id goodix_of_match[] = { + { .compatible = "goodix,gt911" }, + { .compatible = "goodix,gt9110" }, + { .compatible = "goodix,gt912" }, + { .compatible = "goodix,gt927" }, + { .compatible = "goodix,gt9271" }, + { .compatible = "goodix,gt928" }, + { .compatible = "goodix,gt967" }, + { } +}; +MODULE_DEVICE_TABLE(of, goodix_of_match); +#endif static struct i2c_driver goodix_ts_driver = { .probe = goodix_ts_probe, @@ -384,7 +405,8 @@ static struct i2c_driver goodix_ts_driver = { .driver = { .name = "Goodix-TS", .owner = THIS_MODULE, - .acpi_match_table = goodix_acpi_match, + .acpi_match_table = ACPI_PTR(goodix_acpi_match), + .of_match_table = of_match_ptr(goodix_of_match), }, }; module_i2c_driver(goodix_ts_driver); diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c index f8f9b84230b1f5..b82b5207c78bdd 100644 --- a/drivers/input/touchscreen/of_touchscreen.c +++ b/drivers/input/touchscreen/of_touchscreen.c @@ -11,8 +11,41 @@ #include #include +#include #include +static u32 of_get_optional_u32(struct device_node *np, + const char *property) +{ + u32 val = 0; + + of_property_read_u32(np, property, &val); + + return val; +} + +static void touchscreen_set_params(struct input_dev *dev, + unsigned long axis, + int max, int fuzz) +{ + struct input_absinfo *absinfo; + + if (!test_bit(axis, dev->absbit)) { + /* + * Emit a warning only if the axis is not a multitouch + * axis, which might not be set by the driver. + */ + if (!input_is_mt_axis(axis)) + dev_warn(&dev->dev, + "DT specifies parameters but the axis is not set up\n"); + return; + } + + absinfo = &dev->absinfo[axis]; + absinfo->maximum = max; + absinfo->fuzz = fuzz; +} + /** * touchscreen_parse_of_params - parse common touchscreen DT properties * @dev: device that should be parsed @@ -24,22 +57,31 @@ void touchscreen_parse_of_params(struct input_dev *dev) { struct device_node *np = dev->dev.parent->of_node; - struct input_absinfo *absinfo; + u32 maximum, fuzz; input_alloc_absinfo(dev); if (!dev->absinfo) return; - absinfo = &dev->absinfo[ABS_X]; - of_property_read_u32(np, "touchscreen-size-x", &absinfo->maximum); - of_property_read_u32(np, "touchscreen-fuzz-x", &absinfo->fuzz); + maximum = of_get_optional_u32(np, "touchscreen-size-x"); + fuzz = of_get_optional_u32(np, "touchscreen-fuzz-x"); + if (maximum || fuzz) { + touchscreen_set_params(dev, ABS_X, maximum, fuzz); + touchscreen_set_params(dev, ABS_MT_POSITION_X, maximum, fuzz); + } - absinfo = &dev->absinfo[ABS_Y]; - of_property_read_u32(np, "touchscreen-size-y", &absinfo->maximum); - of_property_read_u32(np, "touchscreen-fuzz-y", &absinfo->fuzz); + maximum = of_get_optional_u32(np, "touchscreen-size-y"); + fuzz = of_get_optional_u32(np, "touchscreen-fuzz-y"); + if (maximum || fuzz) { + touchscreen_set_params(dev, ABS_Y, maximum, fuzz); + touchscreen_set_params(dev, ABS_MT_POSITION_Y, maximum, fuzz); + } - absinfo = &dev->absinfo[ABS_PRESSURE]; - of_property_read_u32(np, "touchscreen-max-pressure", &absinfo->maximum); - of_property_read_u32(np, "touchscreen-fuzz-pressure", &absinfo->fuzz); + maximum = of_get_optional_u32(np, "touchscreen-max-pressure"); + fuzz = of_get_optional_u32(np, "touchscreen-fuzz-pressure"); + if (maximum || fuzz) { + touchscreen_set_params(dev, ABS_PRESSURE, maximum, fuzz); + touchscreen_set_params(dev, ABS_MT_PRESSURE, maximum, fuzz); + } } EXPORT_SYMBOL(touchscreen_parse_of_params); diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c index b93a28b955fda3..c0116994067d5c 100644 --- a/drivers/input/touchscreen/sun4i-ts.c +++ b/drivers/input/touchscreen/sun4i-ts.c @@ -30,6 +30,10 @@ * These kinds of heuristics are just asking for trouble (and don't belong * in the kernel). So this driver offers straight forward, reliable single * touch functionality only. + * + * s.a. A20 User Manual "1.15 TP" (Documentation/arm/sunxi/README) + * (looks like the description in the A20 User Manual v1.3 is better + * than the one in the A10 User Manual v.1.5) */ #include @@ -193,7 +197,7 @@ static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp) if (ts->temp_data == -1) return -EAGAIN; - *temp = (ts->temp_data - ts->temp_offset) * ts->temp_step; + *temp = ts->temp_data * ts->temp_step - ts->temp_offset; return 0; } @@ -246,6 +250,8 @@ static int sun4i_ts_probe(struct platform_device *pdev) int error; u32 reg; bool ts_attached; + u32 tp_sensitive_adjust = 15; + u32 filter_type = 1; ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL); if (!ts) @@ -255,22 +261,31 @@ static int sun4i_ts_probe(struct platform_device *pdev) ts->ignore_fifo_data = true; ts->temp_data = -1; if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) { - /* Allwinner SDK has temperature = -271 + (value / 6) (C) */ - ts->temp_offset = 1626; + /* Allwinner SDK has temperature (C) = (value / 6) - 271 */ + ts->temp_offset = 271000; ts->temp_step = 167; + } else if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) { + /* + * The A10 temperature sensor has quite a wide spread, these + * parameters are based on the averaging of the calibration + * results of 4 completely different boards, with a spread of + * temp_step from 0.096 - 0.170 and temp_offset from 176 - 331. + */ + ts->temp_offset = 257000; + ts->temp_step = 133; } else { /* * The user manuals do not contain the formula for calculating * the temperature. The formula used here is from the AXP209, * which is designed by X-Powers, an affiliate of Allwinner: * - * temperature = -144.7 + (value * 0.1) + * temperature (C) = (value * 0.1) - 144.7 * * Allwinner does not have any documentation whatsoever for * this hardware. Moreover, it is claimed that the sensor * is inaccurate and cannot work properly. */ - ts->temp_offset = 1447; + ts->temp_offset = 144700; ts->temp_step = 100; } @@ -313,14 +328,20 @@ static int sun4i_ts_probe(struct platform_device *pdev) ts->base + TP_CTRL0); /* - * sensitive_adjust = 15 : max, which is not all that sensitive, + * tp_sensitive_adjust is an optional property * tp_mode = 0 : only x and y coordinates, as we don't use dual touch */ - writel(TP_SENSITIVE_ADJUST(15) | TP_MODE_SELECT(0), + of_property_read_u32(np, "allwinner,tp-sensitive-adjust", + &tp_sensitive_adjust); + writel(TP_SENSITIVE_ADJUST(tp_sensitive_adjust) | TP_MODE_SELECT(0), ts->base + TP_CTRL2); - /* Enable median filter, type 1 : 5/3 */ - writel(FILTER_EN(1) | FILTER_TYPE(1), ts->base + TP_CTRL3); + /* + * Enable median and averaging filter, optional property for + * filter type. + */ + of_property_read_u32(np, "allwinner,filter-type", &filter_type); + writel(FILTER_EN(1) | FILTER_TYPE(filter_type), ts->base + TP_CTRL3); /* Enable temperature measurement, period 1953 (2 seconds) */ writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR); @@ -330,10 +351,10 @@ static int sun4i_ts_probe(struct platform_device *pdev) * finally enable tp mode. */ reg = STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1); - if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) - reg |= TP_MODE_EN(1); - else + if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) reg |= SUN6I_TP_MODE_EN(1); + else + reg |= TP_MODE_EN(1); writel(reg, ts->base + TP_CTRL1); /* @@ -383,6 +404,7 @@ static int sun4i_ts_remove(struct platform_device *pdev) static const struct of_device_id sun4i_ts_of_match[] = { { .compatible = "allwinner,sun4i-a10-ts", }, + { .compatible = "allwinner,sun5i-a13-ts", }, { .compatible = "allwinner,sun6i-a31-ts", }, { /* sentinel */ } }; diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c new file mode 100644 index 00000000000000..aecb9ad2e70168 --- /dev/null +++ b/drivers/input/touchscreen/sx8654.c @@ -0,0 +1,286 @@ +/* + * Driver for Semtech SX8654 I2C touchscreen controller. + * + * Copyright (c) 2015 Armadeus Systems + * Sébastien Szymanski + * + * Using code from: + * - sx865x.c + * Copyright (c) 2013 U-MoBo Srl + * Pierluigi Passaro + * - sx8650.c + * Copyright (c) 2009 Wayne Roberts + * - tsc2007.c + * Copyright (c) 2008 Kwangwoo Lee + * - ads7846.c + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Nokia Corporation + * - corgi_ts.c + * Copyright (C) 2004-2005 Richard Purdie + * - omap_ts.[hc], ads7846.h, ts_osk.c + * Copyright (C) 2002 MontaVista Software + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2005 Dirk Behme + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +/* register addresses */ +#define I2C_REG_TOUCH0 0x00 +#define I2C_REG_TOUCH1 0x01 +#define I2C_REG_CHANMASK 0x04 +#define I2C_REG_IRQMASK 0x22 +#define I2C_REG_IRQSRC 0x23 +#define I2C_REG_SOFTRESET 0x3f + +/* commands */ +#define CMD_READ_REGISTER 0x40 +#define CMD_MANUAL 0xc0 +#define CMD_PENTRG 0xe0 + +/* value for I2C_REG_SOFTRESET */ +#define SOFTRESET_VALUE 0xde + +/* bits for I2C_REG_IRQSRC */ +#define IRQ_PENTOUCH_TOUCHCONVDONE 0x08 +#define IRQ_PENRELEASE 0x04 + +/* bits for RegTouch1 */ +#define CONDIRQ 0x20 +#define FILT_7SA 0x03 + +/* bits for I2C_REG_CHANMASK */ +#define CONV_X 0x80 +#define CONV_Y 0x40 + +/* coordinates rate: higher nibble of CTRL0 register */ +#define RATE_MANUAL 0x00 +#define RATE_5000CPS 0xf0 + +/* power delay: lower nibble of CTRL0 register */ +#define POWDLY_1_1MS 0x0b + +#define MAX_12BIT ((1 << 12) - 1) + +struct sx8654 { + struct input_dev *input; + struct i2c_client *client; +}; + +static irqreturn_t sx8654_irq(int irq, void *handle) +{ + struct sx8654 *sx8654 = handle; + int irqsrc; + u8 data[4]; + unsigned int x, y; + int retval; + + irqsrc = i2c_smbus_read_byte_data(sx8654->client, + CMD_READ_REGISTER | I2C_REG_IRQSRC); + dev_dbg(&sx8654->client->dev, "irqsrc = 0x%x", irqsrc); + + if (irqsrc < 0) + goto out; + + if (irqsrc & IRQ_PENRELEASE) { + dev_dbg(&sx8654->client->dev, "pen release interrupt"); + + input_report_key(sx8654->input, BTN_TOUCH, 0); + input_sync(sx8654->input); + } + + if (irqsrc & IRQ_PENTOUCH_TOUCHCONVDONE) { + dev_dbg(&sx8654->client->dev, "pen touch interrupt"); + + retval = i2c_master_recv(sx8654->client, data, sizeof(data)); + if (retval != sizeof(data)) + goto out; + + /* invalid data */ + if (unlikely(data[0] & 0x80 || data[2] & 0x80)) + goto out; + + x = ((data[0] & 0xf) << 8) | (data[1]); + y = ((data[2] & 0xf) << 8) | (data[3]); + + input_report_abs(sx8654->input, ABS_X, x); + input_report_abs(sx8654->input, ABS_Y, y); + input_report_key(sx8654->input, BTN_TOUCH, 1); + input_sync(sx8654->input); + + dev_dbg(&sx8654->client->dev, "point(%4d,%4d)\n", x, y); + } + +out: + return IRQ_HANDLED; +} + +static int sx8654_open(struct input_dev *dev) +{ + struct sx8654 *sx8654 = input_get_drvdata(dev); + struct i2c_client *client = sx8654->client; + int error; + + /* enable pen trigger mode */ + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, + RATE_5000CPS | POWDLY_1_1MS); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed"); + return error; + } + + error = i2c_smbus_write_byte(client, CMD_PENTRG); + if (error) { + dev_err(&client->dev, "writing command CMD_PENTRG failed"); + return error; + } + + enable_irq(client->irq); + + return 0; +} + +static void sx8654_close(struct input_dev *dev) +{ + struct sx8654 *sx8654 = input_get_drvdata(dev); + struct i2c_client *client = sx8654->client; + int error; + + disable_irq(client->irq); + + /* enable manual mode mode */ + error = i2c_smbus_write_byte(client, CMD_MANUAL); + if (error) { + dev_err(&client->dev, "writing command CMD_MANUAL failed"); + return; + } + + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed"); + return; + } +} + +static int sx8654_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sx8654 *sx8654; + struct input_dev *input; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -ENXIO; + + sx8654 = devm_kzalloc(&client->dev, sizeof(*sx8654), GFP_KERNEL); + if (!sx8654) + return -ENOMEM; + + input = devm_input_allocate_device(&client->dev); + if (!sx8654) + return -ENOMEM; + + input->name = "SX8654 I2C Touchscreen"; + input->id.bustype = BUS_I2C; + input->dev.parent = &client->dev; + input->open = sx8654_open; + input->close = sx8654_close; + + __set_bit(INPUT_PROP_DIRECT, input->propbit); + input_set_capability(input, EV_KEY, BTN_TOUCH); + input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0); + + sx8654->client = client; + sx8654->input = input; + + input_set_drvdata(sx8654->input, sx8654); + + error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET, + SOFTRESET_VALUE); + if (error) { + dev_err(&client->dev, "writing softreset value failed"); + return error; + } + + error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK, + CONV_X | CONV_Y); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed"); + return error; + } + + error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK, + IRQ_PENTOUCH_TOUCHCONVDONE | + IRQ_PENRELEASE); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed"); + return error; + } + + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1, + CONDIRQ | FILT_7SA); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed"); + return error; + } + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, sx8654_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->name, sx8654); + if (error) { + dev_err(&client->dev, + "Failed to enable IRQ %d, error: %d\n", + client->irq, error); + return error; + } + + /* Disable the IRQ, we'll enable it in sx8654_open() */ + disable_irq(client->irq); + + error = input_register_device(sx8654->input); + if (error) + return error; + + i2c_set_clientdata(client, sx8654); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id sx8654_of_match[] = { + { .compatible = "semtech,sx8654", }, + { }, +}; +MODULE_DEVICE_TABLE(of, sx8654_of_match); +#endif + +static const struct i2c_device_id sx8654_id_table[] = { + { "semtech_sx8654", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, sx8654_id_table); + +static struct i2c_driver sx8654_driver = { + .driver = { + .name = "sx8654", + .of_match_table = of_match_ptr(sx8654_of_match), + }, + .id_table = sx8654_id_table, + .probe = sx8654_probe, +}; +module_i2c_driver(sx8654_driver); + +MODULE_AUTHOR("Sébastien Szymanski "); +MODULE_DESCRIPTION("Semtech SX8654 I2C Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index 1bf9906b5a3fc1..ccc8aa61570968 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c @@ -75,7 +75,7 @@ struct tsc2007 { u16 model; u16 x_plate_ohms; u16 max_rt; - unsigned long poll_period; + unsigned long poll_period; /* in jiffies */ int fuzzx; int fuzzy; int fuzzz; @@ -214,8 +214,7 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle) dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); } - wait_event_timeout(ts->wait, ts->stopped, - msecs_to_jiffies(ts->poll_period)); + wait_event_timeout(ts->wait, ts->stopped, ts->poll_period); } dev_dbg(&ts->client->dev, "UP\n"); @@ -314,9 +313,9 @@ static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts) ts->fuzzz = val32; if (!of_property_read_u64(np, "ti,poll-period", &val64)) - ts->poll_period = val64; + ts->poll_period = msecs_to_jiffies(val64); else - ts->poll_period = 1; + ts->poll_period = msecs_to_jiffies(1); if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) { ts->x_plate_ohms = val32; @@ -350,7 +349,7 @@ static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts, ts->model = pdata->model; ts->x_plate_ohms = pdata->x_plate_ohms; ts->max_rt = pdata->max_rt ? : MAX_12BIT; - ts->poll_period = pdata->poll_period ? : 1; + ts->poll_period = msecs_to_jiffies(pdata->poll_period ? : 1); ts->get_pendown_state = pdata->get_pendown_state; ts->clear_penirq = pdata->clear_penirq; ts->fuzzx = pdata->fuzzx; diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index a0966331a89bb1..f2c6c352c55af2 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -132,6 +132,7 @@ enum { DEVTYPE_GUNZE, DEVTYPE_DMC_TSC10, DEVTYPE_IRTOUCH, + DEVTYPE_IRTOUCH_HIRES, DEVTYPE_IDEALTEK, DEVTYPE_GENERAL_TOUCH, DEVTYPE_GOTOP, @@ -198,6 +199,7 @@ static const struct usb_device_id usbtouch_devices[] = { #ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH {USB_DEVICE(0x595a, 0x0001), .driver_info = DEVTYPE_IRTOUCH}, {USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH}, + {USB_DEVICE(0x6615, 0x0012), .driver_info = DEVTYPE_IRTOUCH_HIRES}, #endif #ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK @@ -1177,6 +1179,15 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { .rept_size = 8, .read_data = irtouch_read_data, }, + + [DEVTYPE_IRTOUCH_HIRES] = { + .min_xc = 0x0, + .max_xc = 0x7fff, + .min_yc = 0x0, + .max_yc = 0x7fff, + .rept_size = 8, + .read_data = irtouch_read_data, + }, #endif #ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c index c1493f4162fbca..d5bdbaf93a1afe 100644 --- a/drivers/isdn/hardware/mISDN/mISDNinfineon.c +++ b/drivers/isdn/hardware/mISDN/mISDNinfineon.c @@ -1092,7 +1092,7 @@ inf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } card->ci = get_card_info(ent->driver_data); if (!card->ci) { - pr_info("mISDN: do not have informations about adapter at %s\n", + pr_info("mISDN: do not have information about adapter at %s\n", pci_name(pdev)); kfree(card); pci_disable_device(pdev); diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c index 87f7dff20ff66b..52c43821f746e0 100644 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -295,7 +295,7 @@ dsp_cmx_del_conf_member(struct dsp *dsp) } } printk(KERN_WARNING - "%s: dsp is not present in its own conf_meber list.\n", + "%s: dsp is not present in its own conf_member list.\n", __func__); return -EINVAL; diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c index 77025f5cb57df7..0222b1a35a2dd3 100644 --- a/drivers/isdn/mISDN/dsp_core.c +++ b/drivers/isdn/mISDN/dsp_core.c @@ -460,7 +460,7 @@ dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb) } if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: enable mixing of " - "tx-data with conf mebers\n", __func__); + "tx-data with conf members\n", __func__); dsp->tx_mix = 1; dsp_cmx_hardware(dsp->conf, dsp); dsp_rx_off(dsp); @@ -474,7 +474,7 @@ dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb) } if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: disable mixing of " - "tx-data with conf mebers\n", __func__); + "tx-data with conf members\n", __func__); dsp->tx_mix = 0; dsp_cmx_hardware(dsp->conf, dsp); dsp_rx_off(dsp); diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index ba4ee0b4883440..d3d928e1c0cece 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -630,7 +630,7 @@ static int m88ds3103_init(struct dvb_frontend *fe) /* request the firmware, this will block and timeout */ ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent); if (ret) { - dev_err(&priv->i2c->dev, "%s: firmare file '%s' not found\n", + dev_err(&priv->i2c->dev, "%s: firmware file '%s' not found\n", KBUILD_MODNAME, fw_file); goto err; } diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index aadd1367673fed..d7efce8043edb4 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -40,7 +40,7 @@ struct si2168_dev { bool ts_clock_inv; }; -/* firmare command struct */ +/* firmware command struct */ #define SI2168_ARGLEN 30 struct si2168_cmd { u8 args[SI2168_ARGLEN]; diff --git a/drivers/media/dvb-frontends/tda10071_priv.h b/drivers/media/dvb-frontends/tda10071_priv.h index 42048619273682..03f839c431e934 100644 --- a/drivers/media/dvb-frontends/tda10071_priv.h +++ b/drivers/media/dvb-frontends/tda10071_priv.h @@ -99,7 +99,7 @@ struct tda10071_reg_val_mask { #define CMD_BER_CONTROL 0x3e #define CMD_BER_UPDATE_COUNTERS 0x3f -/* firmare command struct */ +/* firmware command struct */ #define TDA10071_ARGLEN 30 struct tda10071_cmd { u8 args[TDA10071_ARGLEN]; diff --git a/drivers/media/tuners/msi001.c b/drivers/media/tuners/msi001.c index 26019e73199314..74cfc3c98edbd3 100644 --- a/drivers/media/tuners/msi001.c +++ b/drivers/media/tuners/msi001.c @@ -408,7 +408,7 @@ static int msi001_s_ctrl(struct v4l2_ctrl *ctrl) s->mixer_gain->cur.val, s->if_gain->val); break; default: - dev_dbg(&s->spi->dev, "unkown control %d\n", ctrl->id); + dev_dbg(&s->spi->dev, "unknown control %d\n", ctrl->id); ret = -EINVAL; } diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 38356e39adba0b..d5ad04dad081df 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -283,6 +283,18 @@ config HTC_I2CPLD This device provides input and output GPIOs through an I2C interface to one or more sub-chips. +config MFD_INTEL_QUARK_I2C_GPIO + tristate "Intel Quark MFD I2C GPIO" + depends on PCI + depends on X86 + depends on COMMON_CLK + select MFD_CORE + help + This MFD provides support for I2C and GPIO that exist only + in a single PCI device. It splits the 2 IO devices to + their respective IO driver. + The GPIO exports a total amount of 8 interrupt-capable GPIOs. + config LPC_ICH tristate "Intel ICH LPC" depends on PCI @@ -364,6 +376,7 @@ config MFD_KEMPLD * COMe-bIP# * COMe-bPC2 (ETXexpress-PC) * COMe-bSC# (ETXexpress-SC T#) + * COMe-cBL6 * COMe-cBT6 * COMe-cCT6 * COMe-cDC2 (microETXexpress-DC) @@ -455,6 +468,20 @@ config MFD_MAX77693 additional drivers must be enabled in order to use the functionality of the device. +config MFD_MAX77843 + bool "Maxim Semiconductor MAX77843 PMIC Support" + depends on I2C=y + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + help + Say yes here to add support for Maxim Semiconductor MAX77843. + This is companion Power Management IC with LEDs, Haptic, Charger, + Fuel Gauge, MUIC(Micro USB Interface Controller) controls on chip. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. + config MFD_MAX8907 tristate "Maxim Semiconductor MAX8907 PMIC Support" select MFD_CORE @@ -502,6 +529,16 @@ config MFD_MAX8998 additional drivers must be enabled in order to use the functionality of the device. +config MFD_MT6397 + tristate "MediaTek MT6397 PMIC Support" + select MFD_CORE + select IRQ_DOMAIN + help + Say yes here to add support for MediaTek MT6397 PMIC. This is + a Power Management IC. This driver provides common support for + accessing the device; additional drivers must be enabled in order + to use the functionality of the device. + config MFD_MENF21BMC tristate "MEN 14F021P00 Board Management Controller Support" depends on I2C @@ -655,6 +692,7 @@ config MFD_RT5033 depends on I2C=y select MFD_CORE select REGMAP_I2C + select REGMAP_IRQ help This driver provides for the Richtek RT5033 Power Management IC, which includes the I2C driver and the Core APIs. This driver provides @@ -753,6 +791,18 @@ config MFD_SM501_GPIO lines on the SM501. The platform data is used to supply the base number for the first GPIO line to register. +config MFD_SKY81452 + tristate "Skyworks Solutions SKY81452" + select MFD_CORE + select REGMAP_I2C + depends on I2C + help + This is the core driver for the Skyworks SKY81452 backlight and + voltage regulator device. + + This driver can also be built as a module. If so, the module + will be called sky81452. + config MFD_SMSC bool "SMSC ECE1099 series chips" depends on I2C=y @@ -1210,6 +1260,7 @@ config MFD_TIMBERDALE config MFD_TC3589X bool "Toshiba TC35892 and variants" depends on I2C=y + depends on OF select MFD_CORE help Support for the Toshiba TC35892 and variants I/O Expander. @@ -1289,10 +1340,11 @@ config MFD_WM5102 Support for Wolfson Microelectronics WM5102 low power audio SoC config MFD_WM5110 - bool "Wolfson Microelectronics WM5110" + bool "Wolfson Microelectronics WM5110 and WM8280/WM8281" depends on MFD_ARIZONA help - Support for Wolfson Microelectronics WM5110 low power audio SoC + Support for Wolfson Microelectronics WM5110 and WM8280/WM8281 + low power audio SoC config MFD_WM8997 bool "Wolfson Microelectronics WM8997" @@ -1362,7 +1414,7 @@ config MFD_WM8994 depends on I2C help The WM8994 is a highly integrated hi-fi CODEC designed for - smartphone applicatiosn. As well as audio functionality it + smartphone applications. As well as audio functionality it has on board GPIO and regulator functionality which is supported via the relevant subsystems. This driver provides core support for the WM8994, in order to use the actual diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 19f3d744e3bdad..0e5cfeba107ca4 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -13,7 +13,7 @@ obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o -rtsx_pci-objs := rtsx_pcr.o rtsx_gops.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o +rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o obj-$(CONFIG_MFD_RTSX_USB) += rtsx_usb.o @@ -117,6 +117,7 @@ obj-$(CONFIG_MFD_DA9150) += da9150-core.o obj-$(CONFIG_MFD_MAX14577) += max14577.o obj-$(CONFIG_MFD_MAX77686) += max77686.o obj-$(CONFIG_MFD_MAX77693) += max77693.o +obj-$(CONFIG_MFD_MAX77843) += max77843.o obj-$(CONFIG_MFD_MAX8907) += max8907.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o @@ -138,6 +139,7 @@ obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o obj-$(CONFIG_PMIC_ADP5520) += adp5520.o obj-$(CONFIG_MFD_KEMPLD) += kempld-core.o +obj-$(CONFIG_MFD_INTEL_QUARK_I2C_GPIO) += intel_quark_i2c_gpio.o obj-$(CONFIG_LPC_SCH) += lpc_sch.o obj-$(CONFIG_LPC_ICH) += lpc_ich.o obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o @@ -178,6 +180,8 @@ obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o obj-$(CONFIG_MFD_DLN2) += dln2.o obj-$(CONFIG_MFD_RT5033) += rt5033.o +obj-$(CONFIG_MFD_SKY81452) += sky81452.o intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o +obj-$(CONFIG_MFD_MT6397) += mt6397-core.o diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 9a8e185f11dfaf..cdd6f3d63314c9 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -1283,7 +1283,7 @@ static irqreturn_t ab8500_debug_handler(int irq, void *data) /* Prints to seq_file or log_buf */ static int ab8500_registers_print(struct device *dev, u32 bank, - struct seq_file *s) + struct seq_file *s) { unsigned int i; @@ -1304,20 +1304,19 @@ static int ab8500_registers_print(struct device *dev, u32 bank, } if (s) { - err = seq_printf(s, - " [0x%02X/0x%02X]: 0x%02X\n", - bank, reg, value); - if (err < 0) { - /* Error is not returned here since - * the output is wanted in any case */ + seq_printf(s, " [0x%02X/0x%02X]: 0x%02X\n", + bank, reg, value); + /* Error is not returned here since + * the output is wanted in any case */ + if (seq_has_overflowed(s)) return 0; - } } else { dev_info(dev, " [0x%02X/0x%02X]: 0x%02X\n", bank, reg, value); } } } + return 0; } @@ -1330,8 +1329,7 @@ static int ab8500_print_bank_registers(struct seq_file *s, void *p) seq_printf(s, " bank 0x%02X:\n", bank); - ab8500_registers_print(dev, bank, s); - return 0; + return ab8500_registers_print(dev, bank, s); } static int ab8500_registers_open(struct inode *inode, struct file *file) @@ -1355,9 +1353,12 @@ static int ab8500_print_all_banks(struct seq_file *s, void *p) seq_puts(s, AB8500_NAME_STRING " register values:\n"); for (i = 0; i < AB8500_NUM_BANKS; i++) { - seq_printf(s, " bank 0x%02X:\n", i); + int err; - ab8500_registers_print(dev, i, s); + seq_printf(s, " bank 0x%02X:\n", i); + err = ab8500_registers_print(dev, i, s); + if (err) + return err; } return 0; } @@ -1458,7 +1459,8 @@ static const struct file_operations ab8500_all_banks_fops = { static int ab8500_bank_print(struct seq_file *s, void *p) { - return seq_printf(s, "0x%02X\n", debug_bank); + seq_printf(s, "0x%02X\n", debug_bank); + return 0; } static int ab8500_bank_open(struct inode *inode, struct file *file) @@ -1490,7 +1492,8 @@ static ssize_t ab8500_bank_write(struct file *file, static int ab8500_address_print(struct seq_file *s, void *p) { - return seq_printf(s, "0x%02X\n", debug_address); + seq_printf(s, "0x%02X\n", debug_address); + return 0; } static int ab8500_address_open(struct inode *inode, struct file *file) @@ -1598,7 +1601,8 @@ static int ab8500_interrupts_print(struct seq_file *s, void *p) for (line = 0; line < num_interrupt_lines; line++) { struct irq_desc *desc = irq_to_desc(line + irq_first); - seq_printf(s, "%3i: %6i %4i", line, + seq_printf(s, "%3i: %6i %4i", + line, num_interrupts[line], num_wake_interrupts[line]); @@ -1705,8 +1709,7 @@ static int ab8500_print_modem_registers(struct seq_file *s, void *p) dev_err(dev, "ab->read fail %d\n", err); return err; } - err = seq_printf(s, " [0x%02X/0x%02X]: 0x%02X\n", - bank, reg, value); + seq_printf(s, " [0x%02X/0x%02X]: 0x%02X\n", bank, reg, value); } err = abx500_set_register_interruptible(dev, AB8500_REGU_CTRL1, AB8500_SUPPLY_CONTROL_REG, orig_value); @@ -1743,8 +1746,9 @@ static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p) bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc, BAT_CTRL, bat_ctrl_raw); - return seq_printf(s, "%d,0x%X\n", - bat_ctrl_convert, bat_ctrl_raw); + seq_printf(s, "%d,0x%X\n", bat_ctrl_convert, bat_ctrl_raw); + + return 0; } static int ab8500_gpadc_bat_ctrl_open(struct inode *inode, struct file *file) @@ -1773,8 +1777,9 @@ static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p) btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL, btemp_ball_raw); - return seq_printf(s, - "%d,0x%X\n", btemp_ball_convert, btemp_ball_raw); + seq_printf(s, "%d,0x%X\n", btemp_ball_convert, btemp_ball_raw); + + return 0; } static int ab8500_gpadc_btemp_ball_open(struct inode *inode, @@ -1804,8 +1809,9 @@ static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p) main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_CHARGER_V, main_charger_v_raw); - return seq_printf(s, "%d,0x%X\n", - main_charger_v_convert, main_charger_v_raw); + seq_printf(s, "%d,0x%X\n", main_charger_v_convert, main_charger_v_raw); + + return 0; } static int ab8500_gpadc_main_charger_v_open(struct inode *inode, @@ -1835,8 +1841,9 @@ static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p) acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1, acc_detect1_raw); - return seq_printf(s, "%d,0x%X\n", - acc_detect1_convert, acc_detect1_raw); + seq_printf(s, "%d,0x%X\n", acc_detect1_convert, acc_detect1_raw); + + return 0; } static int ab8500_gpadc_acc_detect1_open(struct inode *inode, @@ -1866,8 +1873,9 @@ static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p) acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT2, acc_detect2_raw); - return seq_printf(s, "%d,0x%X\n", - acc_detect2_convert, acc_detect2_raw); + seq_printf(s, "%d,0x%X\n", acc_detect2_convert, acc_detect2_raw); + + return 0; } static int ab8500_gpadc_acc_detect2_open(struct inode *inode, @@ -1897,8 +1905,9 @@ static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p) aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1, aux1_raw); - return seq_printf(s, "%d,0x%X\n", - aux1_convert, aux1_raw); + seq_printf(s, "%d,0x%X\n", aux1_convert, aux1_raw); + + return 0; } static int ab8500_gpadc_aux1_open(struct inode *inode, struct file *file) @@ -1926,8 +1935,9 @@ static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p) aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2, aux2_raw); - return seq_printf(s, "%d,0x%X\n", - aux2_convert, aux2_raw); + seq_printf(s, "%d,0x%X\n", aux2_convert, aux2_raw); + + return 0; } static int ab8500_gpadc_aux2_open(struct inode *inode, struct file *file) @@ -1955,8 +1965,9 @@ static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p) main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V, main_bat_v_raw); - return seq_printf(s, "%d,0x%X\n", - main_bat_v_convert, main_bat_v_raw); + seq_printf(s, "%d,0x%X\n", main_bat_v_convert, main_bat_v_raw); + + return 0; } static int ab8500_gpadc_main_bat_v_open(struct inode *inode, @@ -1986,8 +1997,9 @@ static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p) vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V, vbus_v_raw); - return seq_printf(s, "%d,0x%X\n", - vbus_v_convert, vbus_v_raw); + seq_printf(s, "%d,0x%X\n", vbus_v_convert, vbus_v_raw); + + return 0; } static int ab8500_gpadc_vbus_v_open(struct inode *inode, struct file *file) @@ -2015,8 +2027,9 @@ static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p) main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_CHARGER_C, main_charger_c_raw); - return seq_printf(s, "%d,0x%X\n", - main_charger_c_convert, main_charger_c_raw); + seq_printf(s, "%d,0x%X\n", main_charger_c_convert, main_charger_c_raw); + + return 0; } static int ab8500_gpadc_main_charger_c_open(struct inode *inode, @@ -2046,8 +2059,9 @@ static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p) usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc, USB_CHARGER_C, usb_charger_c_raw); - return seq_printf(s, "%d,0x%X\n", - usb_charger_c_convert, usb_charger_c_raw); + seq_printf(s, "%d,0x%X\n", usb_charger_c_convert, usb_charger_c_raw); + + return 0; } static int ab8500_gpadc_usb_charger_c_open(struct inode *inode, @@ -2077,8 +2091,9 @@ static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p) bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, BK_BAT_V, bk_bat_v_raw); - return seq_printf(s, "%d,0x%X\n", - bk_bat_v_convert, bk_bat_v_raw); + seq_printf(s, "%d,0x%X\n", bk_bat_v_convert, bk_bat_v_raw); + + return 0; } static int ab8500_gpadc_bk_bat_v_open(struct inode *inode, struct file *file) @@ -2107,8 +2122,9 @@ static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p) die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP, die_temp_raw); - return seq_printf(s, "%d,0x%X\n", - die_temp_convert, die_temp_raw); + seq_printf(s, "%d,0x%X\n", die_temp_convert, die_temp_raw); + + return 0; } static int ab8500_gpadc_die_temp_open(struct inode *inode, struct file *file) @@ -2137,8 +2153,9 @@ static int ab8500_gpadc_usb_id_print(struct seq_file *s, void *p) usb_id_convert = ab8500_gpadc_ad_to_voltage(gpadc, USB_ID, usb_id_raw); - return seq_printf(s, "%d,0x%X\n", - usb_id_convert, usb_id_raw); + seq_printf(s, "%d,0x%X\n", usb_id_convert, usb_id_raw); + + return 0; } static int ab8500_gpadc_usb_id_open(struct inode *inode, struct file *file) @@ -2166,8 +2183,9 @@ static int ab8540_gpadc_xtal_temp_print(struct seq_file *s, void *p) xtal_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, XTAL_TEMP, xtal_temp_raw); - return seq_printf(s, "%d,0x%X\n", - xtal_temp_convert, xtal_temp_raw); + seq_printf(s, "%d,0x%X\n", xtal_temp_convert, xtal_temp_raw); + + return 0; } static int ab8540_gpadc_xtal_temp_open(struct inode *inode, struct file *file) @@ -2197,8 +2215,9 @@ static int ab8540_gpadc_vbat_true_meas_print(struct seq_file *s, void *p) ab8500_gpadc_ad_to_voltage(gpadc, VBAT_TRUE_MEAS, vbat_true_meas_raw); - return seq_printf(s, "%d,0x%X\n", - vbat_true_meas_convert, vbat_true_meas_raw); + seq_printf(s, "%d,0x%X\n", vbat_true_meas_convert, vbat_true_meas_raw); + + return 0; } static int ab8540_gpadc_vbat_true_meas_open(struct inode *inode, @@ -2233,9 +2252,13 @@ static int ab8540_gpadc_bat_ctrl_and_ibat_print(struct seq_file *s, void *p) ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, ibat_raw); - return seq_printf(s, "%d,0x%X\n" "%d,0x%X\n", - bat_ctrl_convert, bat_ctrl_raw, - ibat_convert, ibat_raw); + seq_printf(s, + "%d,0x%X\n" + "%d,0x%X\n", + bat_ctrl_convert, bat_ctrl_raw, + ibat_convert, ibat_raw); + + return 0; } static int ab8540_gpadc_bat_ctrl_and_ibat_open(struct inode *inode, @@ -2269,9 +2292,13 @@ static int ab8540_gpadc_vbat_meas_and_ibat_print(struct seq_file *s, void *p) ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, ibat_raw); - return seq_printf(s, "%d,0x%X\n" "%d,0x%X\n", - vbat_meas_convert, vbat_meas_raw, - ibat_convert, ibat_raw); + seq_printf(s, + "%d,0x%X\n" + "%d,0x%X\n", + vbat_meas_convert, vbat_meas_raw, + ibat_convert, ibat_raw); + + return 0; } static int ab8540_gpadc_vbat_meas_and_ibat_open(struct inode *inode, @@ -2307,9 +2334,13 @@ static int ab8540_gpadc_vbat_true_meas_and_ibat_print(struct seq_file *s, ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, ibat_raw); - return seq_printf(s, "%d,0x%X\n" "%d,0x%X\n", - vbat_true_meas_convert, vbat_true_meas_raw, - ibat_convert, ibat_raw); + seq_printf(s, + "%d,0x%X\n" + "%d,0x%X\n", + vbat_true_meas_convert, vbat_true_meas_raw, + ibat_convert, ibat_raw); + + return 0; } static int ab8540_gpadc_vbat_true_meas_and_ibat_open(struct inode *inode, @@ -2344,9 +2375,13 @@ static int ab8540_gpadc_bat_temp_and_ibat_print(struct seq_file *s, void *p) ibat_convert = ab8500_gpadc_ad_to_voltage(gpadc, IBAT_VIRTUAL_CHANNEL, ibat_raw); - return seq_printf(s, "%d,0x%X\n" "%d,0x%X\n", - bat_temp_convert, bat_temp_raw, - ibat_convert, ibat_raw); + seq_printf(s, + "%d,0x%X\n" + "%d,0x%X\n", + bat_temp_convert, bat_temp_raw, + ibat_convert, ibat_raw); + + return 0; } static int ab8540_gpadc_bat_temp_and_ibat_open(struct inode *inode, @@ -2373,16 +2408,19 @@ static int ab8540_gpadc_otp_cal_print(struct seq_file *s, void *p) gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); ab8540_gpadc_get_otp(gpadc, &vmain_l, &vmain_h, &btemp_l, &btemp_h, &vbat_l, &vbat_h, &ibat_l, &ibat_h); - return seq_printf(s, "VMAIN_L:0x%X\n" - "VMAIN_H:0x%X\n" - "BTEMP_L:0x%X\n" - "BTEMP_H:0x%X\n" - "VBAT_L:0x%X\n" - "VBAT_H:0x%X\n" - "IBAT_L:0x%X\n" - "IBAT_H:0x%X\n", - vmain_l, vmain_h, btemp_l, btemp_h, - vbat_l, vbat_h, ibat_l, ibat_h); + seq_printf(s, + "VMAIN_L:0x%X\n" + "VMAIN_H:0x%X\n" + "BTEMP_L:0x%X\n" + "BTEMP_H:0x%X\n" + "VBAT_L:0x%X\n" + "VBAT_H:0x%X\n" + "IBAT_L:0x%X\n" + "IBAT_H:0x%X\n", + vmain_l, vmain_h, btemp_l, btemp_h, + vbat_l, vbat_h, ibat_l, ibat_h); + + return 0; } static int ab8540_gpadc_otp_cal_open(struct inode *inode, struct file *file) @@ -2400,7 +2438,9 @@ static const struct file_operations ab8540_gpadc_otp_calib_fops = { static int ab8500_gpadc_avg_sample_print(struct seq_file *s, void *p) { - return seq_printf(s, "%d\n", avg_sample); + seq_printf(s, "%d\n", avg_sample); + + return 0; } static int ab8500_gpadc_avg_sample_open(struct inode *inode, struct file *file) @@ -2445,7 +2485,9 @@ static const struct file_operations ab8500_gpadc_avg_sample_fops = { static int ab8500_gpadc_trig_edge_print(struct seq_file *s, void *p) { - return seq_printf(s, "%d\n", trig_edge); + seq_printf(s, "%d\n", trig_edge); + + return 0; } static int ab8500_gpadc_trig_edge_open(struct inode *inode, struct file *file) @@ -2490,7 +2532,9 @@ static const struct file_operations ab8500_gpadc_trig_edge_fops = { static int ab8500_gpadc_trig_timer_print(struct seq_file *s, void *p) { - return seq_printf(s, "%d\n", trig_timer); + seq_printf(s, "%d\n", trig_timer); + + return 0; } static int ab8500_gpadc_trig_timer_open(struct inode *inode, struct file *file) @@ -2533,7 +2577,9 @@ static const struct file_operations ab8500_gpadc_trig_timer_fops = { static int ab8500_gpadc_conv_type_print(struct seq_file *s, void *p) { - return seq_printf(s, "%d\n", conv_type); + seq_printf(s, "%d\n", conv_type); + + return 0; } static int ab8500_gpadc_conv_type_open(struct inode *inode, struct file *file) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 09ba8f186e6ad5..6ca6dfab50ebf3 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -561,12 +561,23 @@ static int arizona_of_get_core_pdata(struct arizona *arizona) count++; } + count = 0; + of_property_for_each_u32(arizona->dev->of_node, "wlf,dmic-ref", prop, + cur, val) { + if (count == ARRAY_SIZE(arizona->pdata.dmic_ref)) + break; + + arizona->pdata.dmic_ref[count] = val; + count++; + } + return 0; } const struct of_device_id arizona_of_match[] = { { .compatible = "wlf,wm5102", .data = (void *)WM5102 }, { .compatible = "wlf,wm5110", .data = (void *)WM5110 }, + { .compatible = "wlf,wm8280", .data = (void *)WM8280 }, { .compatible = "wlf,wm8997", .data = (void *)WM8997 }, {}, }; @@ -671,6 +682,7 @@ int arizona_dev_init(struct arizona *arizona) switch (arizona->type) { case WM5102: case WM5110: + case WM8280: case WM8997: for (i = 0; i < ARRAY_SIZE(wm5102_core_supplies); i++) arizona->core_supplies[i].supply @@ -834,11 +846,19 @@ int arizona_dev_init(struct arizona *arizona) #endif #ifdef CONFIG_MFD_WM5110 case 0x5110: - type_name = "WM5110"; - if (arizona->type != WM5110) { + switch (arizona->type) { + case WM5110: + type_name = "WM5110"; + break; + case WM8280: + type_name = "WM8280"; + break; + default: + type_name = "WM5110"; dev_err(arizona->dev, "WM5110 registered as %d\n", arizona->type); arizona->type = WM5110; + break; } apply_patch = wm5110_patch; break; @@ -1010,6 +1030,7 @@ int arizona_dev_init(struct arizona *arizona) ARRAY_SIZE(wm5102_devs), NULL, 0, NULL); break; case WM5110: + case WM8280: ret = mfd_add_devices(arizona->dev, -1, wm5110_devs, ARRAY_SIZE(wm5110_devs), NULL, 0, NULL); break; diff --git a/drivers/mfd/arizona-i2c.c b/drivers/mfd/arizona-i2c.c index 9d4156fb082ae0..ff782a5de235f8 100644 --- a/drivers/mfd/arizona-i2c.c +++ b/drivers/mfd/arizona-i2c.c @@ -44,6 +44,7 @@ static int arizona_i2c_probe(struct i2c_client *i2c, #endif #ifdef CONFIG_MFD_WM5110 case WM5110: + case WM8280: regmap_config = &wm5110_i2c_regmap; break; #endif @@ -87,6 +88,7 @@ static int arizona_i2c_remove(struct i2c_client *i2c) static const struct i2c_device_id arizona_i2c_id[] = { { "wm5102", WM5102 }, { "wm5110", WM5110 }, + { "wm8280", WM8280 }, { "wm8997", WM8997 }, { } }; diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index 3a3fe7cc6d61e4..d063b94b94b5c9 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -211,6 +211,7 @@ int arizona_irq_init(struct arizona *arizona) #endif #ifdef CONFIG_MFD_WM5110 case WM5110: + case WM8280: aod = &wm5110_aod; switch (arizona->rev) { diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c index 8ef58bcff193e8..1e845f6d407b82 100644 --- a/drivers/mfd/arizona-spi.c +++ b/drivers/mfd/arizona-spi.c @@ -44,6 +44,7 @@ static int arizona_spi_probe(struct spi_device *spi) #endif #ifdef CONFIG_MFD_WM5110 case WM5110: + case WM8280: regmap_config = &wm5110_spi_regmap; break; #endif @@ -84,6 +85,7 @@ static int arizona_spi_remove(struct spi_device *spi) static const struct spi_device_id arizona_spi_ids[] = { { "wm5102", WM5102 }, { "wm5110", WM5110 }, + { "wm8280", WM8280 }, { }, }; MODULE_DEVICE_TABLE(spi, arizona_spi_ids); diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 0acbe52b2411aa..d18029be6a78e3 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -29,7 +29,7 @@ #define AXP20X_OFF 0x80 -static const char const *axp20x_model_names[] = { +static const char * const axp20x_model_names[] = { "AXP202", "AXP209", "AXP288", @@ -290,6 +290,29 @@ static struct resource axp288_adc_resources[] = { }, }; +static struct resource axp288_extcon_resources[] = { + { + .start = AXP288_IRQ_VBUS_FALL, + .end = AXP288_IRQ_VBUS_FALL, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_VBUS_RISE, + .end = AXP288_IRQ_VBUS_RISE, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_MV_CHNG, + .end = AXP288_IRQ_MV_CHNG, + .flags = IORESOURCE_IRQ, + }, + { + .start = AXP288_IRQ_BC_USB_CHNG, + .end = AXP288_IRQ_BC_USB_CHNG, + .flags = IORESOURCE_IRQ, + }, +}; + static struct resource axp288_charger_resources[] = { { .start = AXP288_IRQ_OV, @@ -344,6 +367,11 @@ static struct mfd_cell axp288_cells[] = { .num_resources = ARRAY_SIZE(axp288_adc_resources), .resources = axp288_adc_resources, }, + { + .name = "axp288_extcon", + .num_resources = ARRAY_SIZE(axp288_extcon_resources), + .resources = axp288_extcon_resources, + }, { .name = "axp288_charger", .num_resources = ARRAY_SIZE(axp288_charger_resources), diff --git a/drivers/mfd/da9052-irq.c b/drivers/mfd/da9052-irq.c index 57ae7841f5362a..e65ca194fa9828 100644 --- a/drivers/mfd/da9052-irq.c +++ b/drivers/mfd/da9052-irq.c @@ -262,6 +262,8 @@ int da9052_irq_init(struct da9052 *da9052) goto regmap_err; } + enable_irq_wake(da9052->chip_irq); + ret = da9052_request_irq(da9052, DA9052_IRQ_ADC_EOM, "adc-irq", da9052_auxadc_irq, da9052); diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c index 45ae0b7d13ef19..b5de8a6856c0de 100644 --- a/drivers/mfd/da9052-spi.c +++ b/drivers/mfd/da9052-spi.c @@ -32,7 +32,7 @@ static int da9052_spi_probe(struct spi_device *spi) if (!da9052) return -ENOMEM; - spi->mode = SPI_MODE_0 | SPI_CPOL; + spi->mode = SPI_MODE_0; spi->bits_per_word = 8; spi_setup(spi); @@ -43,6 +43,10 @@ static int da9052_spi_probe(struct spi_device *spi) config = da9052_regmap_config; config.read_flag_mask = 1; + config.reg_bits = 7; + config.pad_bits = 1; + config.val_bits = 8; + config.use_single_rw = 1; da9052->regmap = devm_regmap_init_spi(spi, &config); if (IS_ERR(da9052->regmap)) { diff --git a/drivers/mfd/da9150-core.c b/drivers/mfd/da9150-core.c index 4d757b97ef9a07..5549817df32e79 100644 --- a/drivers/mfd/da9150-core.c +++ b/drivers/mfd/da9150-core.c @@ -95,7 +95,7 @@ static const struct regmap_range_cfg da9150_range_cfg[] = { }, }; -static struct regmap_config da9150_regmap_config = { +static const struct regmap_config da9150_regmap_config = { .reg_bits = 8, .val_bits = 8, .ranges = da9150_range_cfg, diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c index 1be9bd1c046d1e..704e189ca162ab 100644 --- a/drivers/mfd/dln2.c +++ b/drivers/mfd/dln2.c @@ -435,7 +435,7 @@ static int _dln2_transfer(struct dln2_dev *dln2, u16 handle, u16 cmd, struct dln2_response *rsp; struct dln2_rx_context *rxc; struct device *dev = &dln2->interface->dev; - const unsigned long timeout = DLN2_USB_TIMEOUT * HZ / 1000; + const unsigned long timeout = msecs_to_jiffies(DLN2_USB_TIMEOUT); struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[handle]; int size; diff --git a/drivers/mfd/hi6421-pmic-core.c b/drivers/mfd/hi6421-pmic-core.c index 7210ae28bf816c..95b2ff8f223ab3 100644 --- a/drivers/mfd/hi6421-pmic-core.c +++ b/drivers/mfd/hi6421-pmic-core.c @@ -93,7 +93,7 @@ static int hi6421_pmic_remove(struct platform_device *pdev) return 0; } -static struct of_device_id of_hi6421_pmic_match_tbl[] = { +static const struct of_device_id of_hi6421_pmic_match_tbl[] = { { .compatible = "hisilicon,hi6421-pmic", }, { }, }; diff --git a/drivers/mfd/intel_quark_i2c_gpio.c b/drivers/mfd/intel_quark_i2c_gpio.c new file mode 100644 index 00000000000000..1ce16037d043a1 --- /dev/null +++ b/drivers/mfd/intel_quark_i2c_gpio.c @@ -0,0 +1,282 @@ +/* + * Intel Quark MFD PCI driver for I2C & GPIO + * + * Copyright(c) 2014 Intel Corporation. + * + * 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. + * + * Intel Quark PCI device for I2C and GPIO controller sharing the same + * PCI function. This PCI driver will split the 2 devices into their + * respective drivers. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PCI BAR for register base address */ +#define MFD_I2C_BAR 0 +#define MFD_GPIO_BAR 1 + +/* The base GPIO number under GPIOLIB framework */ +#define INTEL_QUARK_MFD_GPIO_BASE 8 + +/* The default number of South-Cluster GPIO on Quark. */ +#define INTEL_QUARK_MFD_NGPIO 8 + +/* The DesignWare GPIO ports on Quark. */ +#define INTEL_QUARK_GPIO_NPORTS 1 + +#define INTEL_QUARK_IORES_MEM 0 +#define INTEL_QUARK_IORES_IRQ 1 + +#define INTEL_QUARK_I2C_CONTROLLER_CLK "i2c_designware.0" + +/* The Quark I2C controller source clock */ +#define INTEL_QUARK_I2C_CLK_HZ 33000000 + +#define INTEL_QUARK_I2C_NCLK 1 + +struct intel_quark_mfd { + struct pci_dev *pdev; + struct clk *i2c_clk; + struct clk_lookup *i2c_clk_lookup; +}; + +struct i2c_mode_info { + const char *name; + unsigned int i2c_scl_freq; +}; + +static const struct i2c_mode_info platform_i2c_mode_info[] = { + { + .name = "Galileo", + .i2c_scl_freq = 100000, + }, + { + .name = "GalileoGen2", + .i2c_scl_freq = 400000, + }, + {} +}; + +static struct resource intel_quark_i2c_res[] = { + [INTEL_QUARK_IORES_MEM] = { + .flags = IORESOURCE_MEM, + }, + [INTEL_QUARK_IORES_IRQ] = { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource intel_quark_gpio_res[] = { + [INTEL_QUARK_IORES_MEM] = { + .flags = IORESOURCE_MEM, + }, +}; + +static struct mfd_cell intel_quark_mfd_cells[] = { + { + .id = MFD_I2C_BAR, + .name = "i2c_designware", + .num_resources = ARRAY_SIZE(intel_quark_i2c_res), + .resources = intel_quark_i2c_res, + .ignore_resource_conflicts = true, + }, + { + .id = MFD_GPIO_BAR, + .name = "gpio-dwapb", + .num_resources = ARRAY_SIZE(intel_quark_gpio_res), + .resources = intel_quark_gpio_res, + .ignore_resource_conflicts = true, + }, +}; + +static const struct pci_device_id intel_quark_mfd_ids[] = { + { PCI_VDEVICE(INTEL, 0x0934), }, + {}, +}; +MODULE_DEVICE_TABLE(pci, intel_quark_mfd_ids); + +static int intel_quark_register_i2c_clk(struct intel_quark_mfd *quark_mfd) +{ + struct pci_dev *pdev = quark_mfd->pdev; + struct clk_lookup *i2c_clk_lookup; + struct clk *i2c_clk; + int ret; + + i2c_clk_lookup = devm_kcalloc(&pdev->dev, INTEL_QUARK_I2C_NCLK, + sizeof(*i2c_clk_lookup), GFP_KERNEL); + if (!i2c_clk_lookup) + return -ENOMEM; + + i2c_clk_lookup[0].dev_id = INTEL_QUARK_I2C_CONTROLLER_CLK; + + i2c_clk = clk_register_fixed_rate(&pdev->dev, + INTEL_QUARK_I2C_CONTROLLER_CLK, NULL, + CLK_IS_ROOT, INTEL_QUARK_I2C_CLK_HZ); + + quark_mfd->i2c_clk_lookup = i2c_clk_lookup; + quark_mfd->i2c_clk = i2c_clk; + + ret = clk_register_clkdevs(i2c_clk, i2c_clk_lookup, + INTEL_QUARK_I2C_NCLK); + if (ret) + dev_err(&pdev->dev, "Fixed clk register failed: %d\n", ret); + + return ret; +} + +static void intel_quark_unregister_i2c_clk(struct pci_dev *pdev) +{ + struct intel_quark_mfd *quark_mfd = dev_get_drvdata(&pdev->dev); + + if (!quark_mfd->i2c_clk || !quark_mfd->i2c_clk_lookup) + return; + + clkdev_drop(quark_mfd->i2c_clk_lookup); + clk_unregister(quark_mfd->i2c_clk); +} + +static int intel_quark_i2c_setup(struct pci_dev *pdev, struct mfd_cell *cell) +{ + const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); + const struct i2c_mode_info *info; + struct dw_i2c_platform_data *pdata; + struct resource *res = (struct resource *)cell->resources; + struct device *dev = &pdev->dev; + + res[INTEL_QUARK_IORES_MEM].start = + pci_resource_start(pdev, MFD_I2C_BAR); + res[INTEL_QUARK_IORES_MEM].end = + pci_resource_end(pdev, MFD_I2C_BAR); + + res[INTEL_QUARK_IORES_IRQ].start = pdev->irq; + res[INTEL_QUARK_IORES_IRQ].end = pdev->irq; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + /* Normal mode by default */ + pdata->i2c_scl_freq = 100000; + + if (board_name) { + for (info = platform_i2c_mode_info; info->name; info++) { + if (!strcmp(board_name, info->name)) { + pdata->i2c_scl_freq = info->i2c_scl_freq; + break; + } + } + } + + cell->platform_data = pdata; + cell->pdata_size = sizeof(*pdata); + + return 0; +} + +static int intel_quark_gpio_setup(struct pci_dev *pdev, struct mfd_cell *cell) +{ + struct dwapb_platform_data *pdata; + struct resource *res = (struct resource *)cell->resources; + struct device *dev = &pdev->dev; + + res[INTEL_QUARK_IORES_MEM].start = + pci_resource_start(pdev, MFD_GPIO_BAR); + res[INTEL_QUARK_IORES_MEM].end = + pci_resource_end(pdev, MFD_GPIO_BAR); + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + /* For intel quark x1000, it has only one port: portA */ + pdata->nports = INTEL_QUARK_GPIO_NPORTS; + pdata->properties = devm_kcalloc(dev, pdata->nports, + sizeof(*pdata->properties), + GFP_KERNEL); + if (!pdata->properties) + return -ENOMEM; + + /* Set the properties for portA */ + pdata->properties->node = NULL; + pdata->properties->name = "intel-quark-x1000-gpio-portA"; + pdata->properties->idx = 0; + pdata->properties->ngpio = INTEL_QUARK_MFD_NGPIO; + pdata->properties->gpio_base = INTEL_QUARK_MFD_GPIO_BASE; + pdata->properties->irq = pdev->irq; + pdata->properties->irq_shared = true; + + cell->platform_data = pdata; + cell->pdata_size = sizeof(*pdata); + + return 0; +} + +static int intel_quark_mfd_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct intel_quark_mfd *quark_mfd; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + quark_mfd = devm_kzalloc(&pdev->dev, sizeof(*quark_mfd), GFP_KERNEL); + if (!quark_mfd) + return -ENOMEM; + quark_mfd->pdev = pdev; + + ret = intel_quark_register_i2c_clk(quark_mfd); + if (ret) + return ret; + + dev_set_drvdata(&pdev->dev, quark_mfd); + + ret = intel_quark_i2c_setup(pdev, &intel_quark_mfd_cells[MFD_I2C_BAR]); + if (ret) + return ret; + + ret = intel_quark_gpio_setup(pdev, + &intel_quark_mfd_cells[MFD_GPIO_BAR]); + if (ret) + return ret; + + return mfd_add_devices(&pdev->dev, 0, intel_quark_mfd_cells, + ARRAY_SIZE(intel_quark_mfd_cells), NULL, 0, + NULL); +} + +static void intel_quark_mfd_remove(struct pci_dev *pdev) +{ + intel_quark_unregister_i2c_clk(pdev); + mfd_remove_devices(&pdev->dev); +} + +static struct pci_driver intel_quark_mfd_driver = { + .name = "intel_quark_mfd_i2c_gpio", + .id_table = intel_quark_mfd_ids, + .probe = intel_quark_mfd_probe, + .remove = intel_quark_mfd_remove, +}; + +module_pci_driver(intel_quark_mfd_driver); + +MODULE_AUTHOR("Raymond Tan "); +MODULE_DESCRIPTION("Intel Quark MFD PCI driver for I2C & GPIO"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/intel_soc_pmic_core.c b/drivers/mfd/intel_soc_pmic_core.c index 80cef048b90402..7b50b6b208a5b0 100644 --- a/drivers/mfd/intel_soc_pmic_core.c +++ b/drivers/mfd/intel_soc_pmic_core.c @@ -26,19 +26,14 @@ #include #include "intel_soc_pmic_core.h" -/* - * On some boards the PMIC interrupt may come from a GPIO line. - * Try to lookup the ACPI table and see if such connection exists. If not, - * return -ENOENT and use the IRQ provided by I2C. - */ static int intel_soc_pmic_find_gpio_irq(struct device *dev) { struct gpio_desc *desc; int irq; - desc = devm_gpiod_get_index(dev, "intel_soc_pmic", 0); + desc = devm_gpiod_get_index(dev, "intel_soc_pmic", 0, GPIOD_IN); if (IS_ERR(desc)) - return -ENOENT; + return PTR_ERR(desc); irq = gpiod_to_irq(desc); if (irq < 0) @@ -71,6 +66,11 @@ static int intel_soc_pmic_i2c_probe(struct i2c_client *i2c, pmic->regmap = devm_regmap_init_i2c(i2c, config->regmap_config); + /* + * On some boards the PMIC interrupt may come from a GPIO line. Try to + * lookup the ACPI table for a such connection and setup a GPIO + * interrupt if it exists. Otherwise use the IRQ provided by I2C + */ irq = intel_soc_pmic_find_gpio_irq(dev); pmic->irq = (irq < 0) ? i2c->irq : irq; diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c index 5615522f8d628b..8057849d51ac5b 100644 --- a/drivers/mfd/kempld-core.c +++ b/drivers/mfd/kempld-core.c @@ -508,8 +508,15 @@ static struct dmi_system_id kempld_dmi_table[] __initdata = { }, .driver_data = (void *)&kempld_platform_data_generic, .callback = kempld_create_platform_device, - }, - { + }, { + .ident = "CBL6", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), + DMI_MATCH(DMI_BOARD_NAME, "COMe-cBL6"), + }, + .driver_data = (void *)&kempld_platform_data_generic, + .callback = kempld_create_platform_device, + }, { .ident = "CCR2", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Kontron"), diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index f35d4280b2f743..12d960a60ec459 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -539,72 +539,7 @@ static struct lpc_ich_info lpc_chipset_info[] = { * functions that probably will be registered by other drivers. */ static const struct pci_device_id lpc_ich_ids[] = { - { PCI_VDEVICE(INTEL, 0x2410), LPC_ICH}, - { PCI_VDEVICE(INTEL, 0x2420), LPC_ICH0}, - { PCI_VDEVICE(INTEL, 0x2440), LPC_ICH2}, - { PCI_VDEVICE(INTEL, 0x244c), LPC_ICH2M}, - { PCI_VDEVICE(INTEL, 0x2480), LPC_ICH3}, - { PCI_VDEVICE(INTEL, 0x248c), LPC_ICH3M}, - { PCI_VDEVICE(INTEL, 0x24c0), LPC_ICH4}, - { PCI_VDEVICE(INTEL, 0x24cc), LPC_ICH4M}, - { PCI_VDEVICE(INTEL, 0x2450), LPC_CICH}, - { PCI_VDEVICE(INTEL, 0x24d0), LPC_ICH5}, - { PCI_VDEVICE(INTEL, 0x25a1), LPC_6300ESB}, - { PCI_VDEVICE(INTEL, 0x2640), LPC_ICH6}, - { PCI_VDEVICE(INTEL, 0x2641), LPC_ICH6M}, - { PCI_VDEVICE(INTEL, 0x2642), LPC_ICH6W}, - { PCI_VDEVICE(INTEL, 0x2670), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2671), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2672), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2673), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2674), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2675), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2676), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2677), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2678), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x2679), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x267a), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x267b), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x267c), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x267d), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x267e), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x267f), LPC_631XESB}, - { PCI_VDEVICE(INTEL, 0x27b8), LPC_ICH7}, - { PCI_VDEVICE(INTEL, 0x27b0), LPC_ICH7DH}, - { PCI_VDEVICE(INTEL, 0x27b9), LPC_ICH7M}, - { PCI_VDEVICE(INTEL, 0x27bd), LPC_ICH7MDH}, - { PCI_VDEVICE(INTEL, 0x27bc), LPC_NM10}, - { PCI_VDEVICE(INTEL, 0x2810), LPC_ICH8}, - { PCI_VDEVICE(INTEL, 0x2812), LPC_ICH8DH}, - { PCI_VDEVICE(INTEL, 0x2814), LPC_ICH8DO}, - { PCI_VDEVICE(INTEL, 0x2815), LPC_ICH8M}, - { PCI_VDEVICE(INTEL, 0x2811), LPC_ICH8ME}, - { PCI_VDEVICE(INTEL, 0x2918), LPC_ICH9}, - { PCI_VDEVICE(INTEL, 0x2916), LPC_ICH9R}, - { PCI_VDEVICE(INTEL, 0x2912), LPC_ICH9DH}, - { PCI_VDEVICE(INTEL, 0x2914), LPC_ICH9DO}, - { PCI_VDEVICE(INTEL, 0x2919), LPC_ICH9M}, - { PCI_VDEVICE(INTEL, 0x2917), LPC_ICH9ME}, - { PCI_VDEVICE(INTEL, 0x3a18), LPC_ICH10}, - { PCI_VDEVICE(INTEL, 0x3a16), LPC_ICH10R}, - { PCI_VDEVICE(INTEL, 0x3a1a), LPC_ICH10D}, - { PCI_VDEVICE(INTEL, 0x3a14), LPC_ICH10DO}, - { PCI_VDEVICE(INTEL, 0x3b00), LPC_PCH}, - { PCI_VDEVICE(INTEL, 0x3b01), LPC_PCHM}, - { PCI_VDEVICE(INTEL, 0x3b02), LPC_P55}, - { PCI_VDEVICE(INTEL, 0x3b03), LPC_PM55}, - { PCI_VDEVICE(INTEL, 0x3b06), LPC_H55}, - { PCI_VDEVICE(INTEL, 0x3b07), LPC_QM57}, - { PCI_VDEVICE(INTEL, 0x3b08), LPC_H57}, - { PCI_VDEVICE(INTEL, 0x3b09), LPC_HM55}, - { PCI_VDEVICE(INTEL, 0x3b0a), LPC_Q57}, - { PCI_VDEVICE(INTEL, 0x3b0b), LPC_HM57}, - { PCI_VDEVICE(INTEL, 0x3b0d), LPC_PCHMSFF}, - { PCI_VDEVICE(INTEL, 0x3b0f), LPC_QS57}, - { PCI_VDEVICE(INTEL, 0x3b12), LPC_3400}, - { PCI_VDEVICE(INTEL, 0x3b14), LPC_3420}, - { PCI_VDEVICE(INTEL, 0x3b16), LPC_3450}, - { PCI_VDEVICE(INTEL, 0x5031), LPC_EP80579}, + { PCI_VDEVICE(INTEL, 0x0f1c), LPC_BAYTRAIL}, { PCI_VDEVICE(INTEL, 0x1c41), LPC_CPT}, { PCI_VDEVICE(INTEL, 0x1c42), LPC_CPTD}, { PCI_VDEVICE(INTEL, 0x1c43), LPC_CPTM}, @@ -638,7 +573,6 @@ static const struct pci_device_id lpc_ich_ids[] = { { PCI_VDEVICE(INTEL, 0x1c5f), LPC_CPT}, { PCI_VDEVICE(INTEL, 0x1d40), LPC_PBG}, { PCI_VDEVICE(INTEL, 0x1d41), LPC_PBG}, - { PCI_VDEVICE(INTEL, 0x2310), LPC_DH89XXCC}, { PCI_VDEVICE(INTEL, 0x1e40), LPC_PPT}, { PCI_VDEVICE(INTEL, 0x1e41), LPC_PPT}, { PCI_VDEVICE(INTEL, 0x1e42), LPC_PPT}, @@ -671,6 +605,79 @@ static const struct pci_device_id lpc_ich_ids[] = { { PCI_VDEVICE(INTEL, 0x1e5d), LPC_PPT}, { PCI_VDEVICE(INTEL, 0x1e5e), LPC_PPT}, { PCI_VDEVICE(INTEL, 0x1e5f), LPC_PPT}, + { PCI_VDEVICE(INTEL, 0x1f38), LPC_AVN}, + { PCI_VDEVICE(INTEL, 0x1f39), LPC_AVN}, + { PCI_VDEVICE(INTEL, 0x1f3a), LPC_AVN}, + { PCI_VDEVICE(INTEL, 0x1f3b), LPC_AVN}, + { PCI_VDEVICE(INTEL, 0x229c), LPC_BRASWELL}, + { PCI_VDEVICE(INTEL, 0x2310), LPC_DH89XXCC}, + { PCI_VDEVICE(INTEL, 0x2390), LPC_COLETO}, + { PCI_VDEVICE(INTEL, 0x2410), LPC_ICH}, + { PCI_VDEVICE(INTEL, 0x2420), LPC_ICH0}, + { PCI_VDEVICE(INTEL, 0x2440), LPC_ICH2}, + { PCI_VDEVICE(INTEL, 0x244c), LPC_ICH2M}, + { PCI_VDEVICE(INTEL, 0x2450), LPC_CICH}, + { PCI_VDEVICE(INTEL, 0x2480), LPC_ICH3}, + { PCI_VDEVICE(INTEL, 0x248c), LPC_ICH3M}, + { PCI_VDEVICE(INTEL, 0x24c0), LPC_ICH4}, + { PCI_VDEVICE(INTEL, 0x24cc), LPC_ICH4M}, + { PCI_VDEVICE(INTEL, 0x24d0), LPC_ICH5}, + { PCI_VDEVICE(INTEL, 0x25a1), LPC_6300ESB}, + { PCI_VDEVICE(INTEL, 0x2640), LPC_ICH6}, + { PCI_VDEVICE(INTEL, 0x2641), LPC_ICH6M}, + { PCI_VDEVICE(INTEL, 0x2642), LPC_ICH6W}, + { PCI_VDEVICE(INTEL, 0x2670), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2671), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2672), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2673), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2674), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2675), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2676), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2677), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2678), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x2679), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267a), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267b), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267c), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267d), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267e), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x267f), LPC_631XESB}, + { PCI_VDEVICE(INTEL, 0x27b0), LPC_ICH7DH}, + { PCI_VDEVICE(INTEL, 0x27b8), LPC_ICH7}, + { PCI_VDEVICE(INTEL, 0x27b9), LPC_ICH7M}, + { PCI_VDEVICE(INTEL, 0x27bc), LPC_NM10}, + { PCI_VDEVICE(INTEL, 0x27bd), LPC_ICH7MDH}, + { PCI_VDEVICE(INTEL, 0x2810), LPC_ICH8}, + { PCI_VDEVICE(INTEL, 0x2811), LPC_ICH8ME}, + { PCI_VDEVICE(INTEL, 0x2812), LPC_ICH8DH}, + { PCI_VDEVICE(INTEL, 0x2814), LPC_ICH8DO}, + { PCI_VDEVICE(INTEL, 0x2815), LPC_ICH8M}, + { PCI_VDEVICE(INTEL, 0x2912), LPC_ICH9DH}, + { PCI_VDEVICE(INTEL, 0x2914), LPC_ICH9DO}, + { PCI_VDEVICE(INTEL, 0x2916), LPC_ICH9R}, + { PCI_VDEVICE(INTEL, 0x2917), LPC_ICH9ME}, + { PCI_VDEVICE(INTEL, 0x2918), LPC_ICH9}, + { PCI_VDEVICE(INTEL, 0x2919), LPC_ICH9M}, + { PCI_VDEVICE(INTEL, 0x3a14), LPC_ICH10DO}, + { PCI_VDEVICE(INTEL, 0x3a16), LPC_ICH10R}, + { PCI_VDEVICE(INTEL, 0x3a18), LPC_ICH10}, + { PCI_VDEVICE(INTEL, 0x3a1a), LPC_ICH10D}, + { PCI_VDEVICE(INTEL, 0x3b00), LPC_PCH}, + { PCI_VDEVICE(INTEL, 0x3b01), LPC_PCHM}, + { PCI_VDEVICE(INTEL, 0x3b02), LPC_P55}, + { PCI_VDEVICE(INTEL, 0x3b03), LPC_PM55}, + { PCI_VDEVICE(INTEL, 0x3b06), LPC_H55}, + { PCI_VDEVICE(INTEL, 0x3b07), LPC_QM57}, + { PCI_VDEVICE(INTEL, 0x3b08), LPC_H57}, + { PCI_VDEVICE(INTEL, 0x3b09), LPC_HM55}, + { PCI_VDEVICE(INTEL, 0x3b0a), LPC_Q57}, + { PCI_VDEVICE(INTEL, 0x3b0b), LPC_HM57}, + { PCI_VDEVICE(INTEL, 0x3b0d), LPC_PCHMSFF}, + { PCI_VDEVICE(INTEL, 0x3b0f), LPC_QS57}, + { PCI_VDEVICE(INTEL, 0x3b12), LPC_3400}, + { PCI_VDEVICE(INTEL, 0x3b14), LPC_3420}, + { PCI_VDEVICE(INTEL, 0x3b16), LPC_3450}, + { PCI_VDEVICE(INTEL, 0x5031), LPC_EP80579}, { PCI_VDEVICE(INTEL, 0x8c40), LPC_LPT}, { PCI_VDEVICE(INTEL, 0x8c41), LPC_LPT}, { PCI_VDEVICE(INTEL, 0x8c42), LPC_LPT}, @@ -703,14 +710,11 @@ static const struct pci_device_id lpc_ich_ids[] = { { PCI_VDEVICE(INTEL, 0x8c5d), LPC_LPT}, { PCI_VDEVICE(INTEL, 0x8c5e), LPC_LPT}, { PCI_VDEVICE(INTEL, 0x8c5f), LPC_LPT}, - { PCI_VDEVICE(INTEL, 0x9c40), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9c41), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9c42), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9c43), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9c44), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9c45), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9c46), LPC_LPT_LP}, - { PCI_VDEVICE(INTEL, 0x9c47), LPC_LPT_LP}, + { PCI_VDEVICE(INTEL, 0x8cc1), LPC_9S}, + { PCI_VDEVICE(INTEL, 0x8cc2), LPC_9S}, + { PCI_VDEVICE(INTEL, 0x8cc3), LPC_9S}, + { PCI_VDEVICE(INTEL, 0x8cc4), LPC_9S}, + { PCI_VDEVICE(INTEL, 0x8cc6), LPC_9S}, { PCI_VDEVICE(INTEL, 0x8d40), LPC_WBG}, { PCI_VDEVICE(INTEL, 0x8d41), LPC_WBG}, { PCI_VDEVICE(INTEL, 0x8d42), LPC_WBG}, @@ -743,12 +747,14 @@ static const struct pci_device_id lpc_ich_ids[] = { { PCI_VDEVICE(INTEL, 0x8d5d), LPC_WBG}, { PCI_VDEVICE(INTEL, 0x8d5e), LPC_WBG}, { PCI_VDEVICE(INTEL, 0x8d5f), LPC_WBG}, - { PCI_VDEVICE(INTEL, 0x1f38), LPC_AVN}, - { PCI_VDEVICE(INTEL, 0x1f39), LPC_AVN}, - { PCI_VDEVICE(INTEL, 0x1f3a), LPC_AVN}, - { PCI_VDEVICE(INTEL, 0x1f3b), LPC_AVN}, - { PCI_VDEVICE(INTEL, 0x0f1c), LPC_BAYTRAIL}, - { PCI_VDEVICE(INTEL, 0x2390), LPC_COLETO}, + { PCI_VDEVICE(INTEL, 0x9c40), LPC_LPT_LP}, + { PCI_VDEVICE(INTEL, 0x9c41), LPC_LPT_LP}, + { PCI_VDEVICE(INTEL, 0x9c42), LPC_LPT_LP}, + { PCI_VDEVICE(INTEL, 0x9c43), LPC_LPT_LP}, + { PCI_VDEVICE(INTEL, 0x9c44), LPC_LPT_LP}, + { PCI_VDEVICE(INTEL, 0x9c45), LPC_LPT_LP}, + { PCI_VDEVICE(INTEL, 0x9c46), LPC_LPT_LP}, + { PCI_VDEVICE(INTEL, 0x9c47), LPC_LPT_LP}, { PCI_VDEVICE(INTEL, 0x9cc1), LPC_WPT_LP}, { PCI_VDEVICE(INTEL, 0x9cc2), LPC_WPT_LP}, { PCI_VDEVICE(INTEL, 0x9cc3), LPC_WPT_LP}, @@ -756,12 +762,6 @@ static const struct pci_device_id lpc_ich_ids[] = { { PCI_VDEVICE(INTEL, 0x9cc6), LPC_WPT_LP}, { PCI_VDEVICE(INTEL, 0x9cc7), LPC_WPT_LP}, { PCI_VDEVICE(INTEL, 0x9cc9), LPC_WPT_LP}, - { PCI_VDEVICE(INTEL, 0x229c), LPC_BRASWELL}, - { PCI_VDEVICE(INTEL, 0x8cc1), LPC_9S}, - { PCI_VDEVICE(INTEL, 0x8cc2), LPC_9S}, - { PCI_VDEVICE(INTEL, 0x8cc3), LPC_9S}, - { PCI_VDEVICE(INTEL, 0x8cc4), LPC_9S}, - { PCI_VDEVICE(INTEL, 0x8cc6), LPC_9S}, { 0, }, /* End of list */ }; MODULE_DEVICE_TABLE(pci, lpc_ich_ids); diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c index a159593e27a0c4..cb14afa97e6fbb 100644 --- a/drivers/mfd/max77693.c +++ b/drivers/mfd/max77693.c @@ -53,8 +53,8 @@ static const struct mfd_cell max77693_devs[] = { .of_compatible = "maxim,max77693-haptic", }, { - .name = "max77693-flash", - .of_compatible = "maxim,max77693-flash", + .name = "max77693-led", + .of_compatible = "maxim,max77693-led", }, }; diff --git a/drivers/mfd/max77843.c b/drivers/mfd/max77843.c new file mode 100644 index 00000000000000..a354ac677ec702 --- /dev/null +++ b/drivers/mfd/max77843.c @@ -0,0 +1,243 @@ +/* + * MFD core driver for the Maxim MAX77843 + * + * Copyright (C) 2015 Samsung Electronics + * Author: Jaewon Kim + * Author: Beomho Seo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct mfd_cell max77843_devs[] = { + { + .name = "max77843-muic", + .of_compatible = "maxim,max77843-muic", + }, { + .name = "max77843-regulator", + .of_compatible = "maxim,max77843-regulator", + }, { + .name = "max77843-charger", + .of_compatible = "maxim,max77843-charger" + }, { + .name = "max77843-fuelgauge", + .of_compatible = "maxim,max77843-fuelgauge", + }, { + .name = "max77843-haptic", + .of_compatible = "maxim,max77843-haptic", + }, +}; + +static const struct regmap_config max77843_charger_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77843_CHG_REG_END, +}; + +static const struct regmap_config max77843_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX77843_SYS_REG_END, +}; + +static const struct regmap_irq max77843_irqs[] = { + /* TOPSYS interrupts */ + { .reg_offset = 0, .mask = MAX77843_SYS_IRQ_SYSUVLO_INT, }, + { .reg_offset = 0, .mask = MAX77843_SYS_IRQ_SYSOVLO_INT, }, + { .reg_offset = 0, .mask = MAX77843_SYS_IRQ_TSHDN_INT, }, + { .reg_offset = 0, .mask = MAX77843_SYS_IRQ_TM_INT, }, +}; + +static const struct regmap_irq_chip max77843_irq_chip = { + .name = "max77843", + .status_base = MAX77843_SYS_REG_SYSINTSRC, + .mask_base = MAX77843_SYS_REG_SYSINTMASK, + .mask_invert = false, + .num_regs = 1, + .irqs = max77843_irqs, + .num_irqs = ARRAY_SIZE(max77843_irqs), +}; + +/* Charger and Charger regulator use same regmap. */ +static int max77843_chg_init(struct max77843 *max77843) +{ + int ret; + + max77843->i2c_chg = i2c_new_dummy(max77843->i2c->adapter, I2C_ADDR_CHG); + if (!max77843->i2c_chg) { + dev_err(&max77843->i2c->dev, + "Cannot allocate I2C device for Charger\n"); + return PTR_ERR(max77843->i2c_chg); + } + i2c_set_clientdata(max77843->i2c_chg, max77843); + + max77843->regmap_chg = devm_regmap_init_i2c(max77843->i2c_chg, + &max77843_charger_regmap_config); + if (IS_ERR(max77843->regmap_chg)) { + ret = PTR_ERR(max77843->regmap_chg); + goto err_chg_i2c; + } + + return 0; + +err_chg_i2c: + i2c_unregister_device(max77843->i2c_chg); + + return ret; +} + +static int max77843_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max77843 *max77843; + unsigned int reg_data; + int ret; + + max77843 = devm_kzalloc(&i2c->dev, sizeof(*max77843), GFP_KERNEL); + if (!max77843) + return -ENOMEM; + + i2c_set_clientdata(i2c, max77843); + max77843->dev = &i2c->dev; + max77843->i2c = i2c; + max77843->irq = i2c->irq; + + max77843->regmap = devm_regmap_init_i2c(i2c, + &max77843_regmap_config); + if (IS_ERR(max77843->regmap)) { + dev_err(&i2c->dev, "Failed to allocate topsys register map\n"); + return PTR_ERR(max77843->regmap); + } + + ret = regmap_add_irq_chip(max77843->regmap, max77843->irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED, + 0, &max77843_irq_chip, &max77843->irq_data); + if (ret) { + dev_err(&i2c->dev, "Failed to add TOPSYS IRQ chip\n"); + return ret; + } + + ret = regmap_read(max77843->regmap, + MAX77843_SYS_REG_PMICID, ®_data); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read PMIC ID\n"); + goto err_pmic_id; + } + dev_info(&i2c->dev, "device ID: 0x%x\n", reg_data); + + ret = max77843_chg_init(max77843); + if (ret) { + dev_err(&i2c->dev, "Failed to init Charger\n"); + goto err_pmic_id; + } + + ret = regmap_update_bits(max77843->regmap, + MAX77843_SYS_REG_INTSRCMASK, + MAX77843_INTSRC_MASK_MASK, + (unsigned int)~MAX77843_INTSRC_MASK_MASK); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to unmask interrupt source\n"); + goto err_pmic_id; + } + + ret = mfd_add_devices(max77843->dev, -1, max77843_devs, + ARRAY_SIZE(max77843_devs), NULL, 0, NULL); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to add mfd device\n"); + goto err_pmic_id; + } + + device_init_wakeup(max77843->dev, true); + + return 0; + +err_pmic_id: + regmap_del_irq_chip(max77843->irq, max77843->irq_data); + + return ret; +} + +static int max77843_remove(struct i2c_client *i2c) +{ + struct max77843 *max77843 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max77843->dev); + + regmap_del_irq_chip(max77843->irq, max77843->irq_data); + + i2c_unregister_device(max77843->i2c_chg); + + return 0; +} + +static const struct of_device_id max77843_dt_match[] = { + { .compatible = "maxim,max77843", }, + { }, +}; + +static const struct i2c_device_id max77843_id[] = { + { "max77843", }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max77843_id); + +static int __maybe_unused max77843_suspend(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max77843 *max77843 = i2c_get_clientdata(i2c); + + disable_irq(max77843->irq); + if (device_may_wakeup(dev)) + enable_irq_wake(max77843->irq); + + return 0; +} + +static int __maybe_unused max77843_resume(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max77843 *max77843 = i2c_get_clientdata(i2c); + + if (device_may_wakeup(dev)) + disable_irq_wake(max77843->irq); + enable_irq(max77843->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(max77843_pm, max77843_suspend, max77843_resume); + +static struct i2c_driver max77843_i2c_driver = { + .driver = { + .name = "max77843", + .pm = &max77843_pm, + .of_match_table = max77843_dt_match, + }, + .probe = max77843_probe, + .remove = max77843_remove, + .id_table = max77843_id, +}; + +static int __init max77843_i2c_init(void) +{ + return i2c_add_driver(&max77843_i2c_driver); +} +subsys_initcall(max77843_i2c_init); + +static void __exit max77843_i2c_exit(void) +{ + i2c_del_driver(&max77843_i2c_driver); +} +module_exit(max77843_i2c_exit); diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 64dde5d24b3206..25fd7116493a38 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -51,19 +51,19 @@ void mc13xxx_lock(struct mc13xxx *mc13xxx) { if (!mutex_trylock(&mc13xxx->lock)) { - dev_dbg(mc13xxx->dev, "wait for %s from %pf\n", + dev_dbg(mc13xxx->dev, "wait for %s from %ps\n", __func__, __builtin_return_address(0)); mutex_lock(&mc13xxx->lock); } - dev_dbg(mc13xxx->dev, "%s from %pf\n", + dev_dbg(mc13xxx->dev, "%s from %ps\n", __func__, __builtin_return_address(0)); } EXPORT_SYMBOL(mc13xxx_lock); void mc13xxx_unlock(struct mc13xxx *mc13xxx) { - dev_dbg(mc13xxx->dev, "%s from %pf\n", + dev_dbg(mc13xxx->dev, "%s from %ps\n", __func__, __builtin_return_address(0)); mutex_unlock(&mc13xxx->lock); } diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c index 9f01aef539ddd7..3ac36f5ccd3e13 100644 --- a/drivers/mfd/menelaus.c +++ b/drivers/mfd/menelaus.c @@ -532,29 +532,6 @@ static const struct menelaus_vtg_value vcore_values[] = { { 1450, 18 }, }; -int menelaus_set_vcore_sw(unsigned int mV) -{ - int val, ret; - struct i2c_client *c = the_menelaus->client; - - val = menelaus_get_vtg_value(mV, vcore_values, - ARRAY_SIZE(vcore_values)); - if (val < 0) - return -EINVAL; - - dev_dbg(&c->dev, "Setting VCORE to %d mV (val 0x%02x)\n", mV, val); - - /* Set SW mode and the voltage in one go. */ - mutex_lock(&the_menelaus->lock); - ret = menelaus_write_reg(MENELAUS_VCORE_CTRL1, val); - if (ret == 0) - the_menelaus->vcore_hw_mode = 0; - mutex_unlock(&the_menelaus->lock); - msleep(1); - - return ret; -} - int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV) { int fval, rval, val, ret; @@ -1239,7 +1216,7 @@ static int menelaus_probe(struct i2c_client *client, err = menelaus_read_reg(MENELAUS_VCORE_CTRL1); if (err < 0) goto fail; - if (err & BIT(7)) + if (err & VCORE_CTRL1_HW_NSW) menelaus->vcore_hw_mode = 1; else menelaus->vcore_hw_mode = 0; @@ -1259,7 +1236,7 @@ static int menelaus_probe(struct i2c_client *client, return err; } -static int __exit menelaus_remove(struct i2c_client *client) +static int menelaus_remove(struct i2c_client *client) { struct menelaus_chip *menelaus = i2c_get_clientdata(client); @@ -1280,7 +1257,7 @@ static struct i2c_driver menelaus_i2c_driver = { .name = DRIVER_NAME, }, .probe = menelaus_probe, - .remove = __exit_p(menelaus_remove), + .remove = menelaus_remove, .id_table = menelaus_id, }; diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 2a87f69be53db0..1aed3b7b8d9be1 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -128,7 +128,7 @@ static int mfd_add_device(struct device *parent, int id, int platform_id; int r; - if (id < 0) + if (id == PLATFORM_DEVID_AUTO) platform_id = id; else platform_id = id + cell->id; diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c new file mode 100644 index 00000000000000..09bc7804952a4a --- /dev/null +++ b/drivers/mfd/mt6397-core.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu, MediaTek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct mfd_cell mt6397_devs[] = { + { + .name = "mt6397-rtc", + .of_compatible = "mediatek,mt6397-rtc", + }, { + .name = "mt6397-regulator", + .of_compatible = "mediatek,mt6397-regulator", + }, { + .name = "mt6397-codec", + .of_compatible = "mediatek,mt6397-codec", + }, { + .name = "mt6397-clk", + .of_compatible = "mediatek,mt6397-clk", + }, +}; + +static void mt6397_irq_lock(struct irq_data *data) +{ + struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq); + + mutex_lock(&mt6397->irqlock); +} + +static void mt6397_irq_sync_unlock(struct irq_data *data) +{ + struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq); + + regmap_write(mt6397->regmap, MT6397_INT_CON0, mt6397->irq_masks_cur[0]); + regmap_write(mt6397->regmap, MT6397_INT_CON1, mt6397->irq_masks_cur[1]); + + mutex_unlock(&mt6397->irqlock); +} + +static void mt6397_irq_disable(struct irq_data *data) +{ + struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq); + int shift = data->hwirq & 0xf; + int reg = data->hwirq >> 4; + + mt6397->irq_masks_cur[reg] &= ~BIT(shift); +} + +static void mt6397_irq_enable(struct irq_data *data) +{ + struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq); + int shift = data->hwirq & 0xf; + int reg = data->hwirq >> 4; + + mt6397->irq_masks_cur[reg] |= BIT(shift); +} + +static struct irq_chip mt6397_irq_chip = { + .name = "mt6397-irq", + .irq_bus_lock = mt6397_irq_lock, + .irq_bus_sync_unlock = mt6397_irq_sync_unlock, + .irq_enable = mt6397_irq_enable, + .irq_disable = mt6397_irq_disable, +}; + +static void mt6397_irq_handle_reg(struct mt6397_chip *mt6397, int reg, + int irqbase) +{ + unsigned int status; + int i, irq, ret; + + ret = regmap_read(mt6397->regmap, reg, &status); + if (ret) { + dev_err(mt6397->dev, "Failed to read irq status: %d\n", ret); + return; + } + + for (i = 0; i < 16; i++) { + if (status & BIT(i)) { + irq = irq_find_mapping(mt6397->irq_domain, irqbase + i); + if (irq) + handle_nested_irq(irq); + } + } + + regmap_write(mt6397->regmap, reg, status); +} + +static irqreturn_t mt6397_irq_thread(int irq, void *data) +{ + struct mt6397_chip *mt6397 = data; + + mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS0, 0); + mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS1, 16); + + return IRQ_HANDLED; +} + +static int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + struct mt6397_chip *mt6397 = d->host_data; + + irq_set_chip_data(irq, mt6397); + irq_set_chip_and_handler(irq, &mt6397_irq_chip, handle_level_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + + return 0; +} + +static struct irq_domain_ops mt6397_irq_domain_ops = { + .map = mt6397_irq_domain_map, +}; + +static int mt6397_irq_init(struct mt6397_chip *mt6397) +{ + int ret; + + mutex_init(&mt6397->irqlock); + + /* Mask all interrupt sources */ + regmap_write(mt6397->regmap, MT6397_INT_CON0, 0x0); + regmap_write(mt6397->regmap, MT6397_INT_CON1, 0x0); + + mt6397->irq_domain = irq_domain_add_linear(mt6397->dev->of_node, + MT6397_IRQ_NR, &mt6397_irq_domain_ops, mt6397); + if (!mt6397->irq_domain) { + dev_err(mt6397->dev, "could not create irq domain\n"); + return -ENOMEM; + } + + ret = devm_request_threaded_irq(mt6397->dev, mt6397->irq, NULL, + mt6397_irq_thread, IRQF_ONESHOT, "mt6397-pmic", mt6397); + if (ret) { + dev_err(mt6397->dev, "failed to register irq=%d; err: %d\n", + mt6397->irq, ret); + return ret; + } + + return 0; +} + +static int mt6397_probe(struct platform_device *pdev) +{ + int ret; + struct mt6397_chip *mt6397; + + mt6397 = devm_kzalloc(&pdev->dev, sizeof(*mt6397), GFP_KERNEL); + if (!mt6397) + return -ENOMEM; + + mt6397->dev = &pdev->dev; + /* + * mt6397 MFD is child device of soc pmic wrapper. + * Regmap is set from its parent. + */ + mt6397->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!mt6397->regmap) + return -ENODEV; + + platform_set_drvdata(pdev, mt6397); + + mt6397->irq = platform_get_irq(pdev, 0); + if (mt6397->irq > 0) { + ret = mt6397_irq_init(mt6397); + if (ret) + return ret; + } + + ret = mfd_add_devices(&pdev->dev, -1, mt6397_devs, + ARRAY_SIZE(mt6397_devs), NULL, 0, NULL); + if (ret) + dev_err(&pdev->dev, "failed to add child devices: %d\n", ret); + + return ret; +} + +static int mt6397_remove(struct platform_device *pdev) +{ + mfd_remove_devices(&pdev->dev); + + return 0; +} + +static const struct of_device_id mt6397_of_match[] = { + { .compatible = "mediatek,mt6397" }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6397_of_match); + +static struct platform_driver mt6397_driver = { + .probe = mt6397_probe, + .remove = mt6397_remove, + .driver = { + .name = "mt6397", + .of_match_table = of_match_ptr(mt6397_of_match), + }, +}; + +module_platform_driver(mt6397_driver); + +MODULE_AUTHOR("Flora Fu, MediaTek"); +MODULE_DESCRIPTION("Driver for MediaTek MT6397 PMIC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mt6397"); diff --git a/drivers/mfd/qcom-spmi-pmic.c b/drivers/mfd/qcom-spmi-pmic.c index 4b8beb2a1579c7..af6ac1c4b45c68 100644 --- a/drivers/mfd/qcom-spmi-pmic.c +++ b/drivers/mfd/qcom-spmi-pmic.c @@ -17,6 +17,100 @@ #include #include +#define PMIC_REV2 0x101 +#define PMIC_REV3 0x102 +#define PMIC_REV4 0x103 +#define PMIC_TYPE 0x104 +#define PMIC_SUBTYPE 0x105 + +#define PMIC_TYPE_VALUE 0x51 + +#define COMMON_SUBTYPE 0x00 +#define PM8941_SUBTYPE 0x01 +#define PM8841_SUBTYPE 0x02 +#define PM8019_SUBTYPE 0x03 +#define PM8226_SUBTYPE 0x04 +#define PM8110_SUBTYPE 0x05 +#define PMA8084_SUBTYPE 0x06 +#define PMI8962_SUBTYPE 0x07 +#define PMD9635_SUBTYPE 0x08 +#define PM8994_SUBTYPE 0x09 +#define PMI8994_SUBTYPE 0x0a +#define PM8916_SUBTYPE 0x0b +#define PM8004_SUBTYPE 0x0c +#define PM8909_SUBTYPE 0x0d + +static const struct of_device_id pmic_spmi_id_table[] = { + { .compatible = "qcom,spmi-pmic", .data = (void *)COMMON_SUBTYPE }, + { .compatible = "qcom,pm8941", .data = (void *)PM8941_SUBTYPE }, + { .compatible = "qcom,pm8841", .data = (void *)PM8841_SUBTYPE }, + { .compatible = "qcom,pm8019", .data = (void *)PM8019_SUBTYPE }, + { .compatible = "qcom,pm8226", .data = (void *)PM8226_SUBTYPE }, + { .compatible = "qcom,pm8110", .data = (void *)PM8110_SUBTYPE }, + { .compatible = "qcom,pma8084", .data = (void *)PMA8084_SUBTYPE }, + { .compatible = "qcom,pmi8962", .data = (void *)PMI8962_SUBTYPE }, + { .compatible = "qcom,pmd9635", .data = (void *)PMD9635_SUBTYPE }, + { .compatible = "qcom,pm8994", .data = (void *)PM8994_SUBTYPE }, + { .compatible = "qcom,pmi8994", .data = (void *)PMI8994_SUBTYPE }, + { .compatible = "qcom,pm8916", .data = (void *)PM8916_SUBTYPE }, + { .compatible = "qcom,pm8004", .data = (void *)PM8004_SUBTYPE }, + { .compatible = "qcom,pm8909", .data = (void *)PM8909_SUBTYPE }, + { } +}; + +static void pmic_spmi_show_revid(struct regmap *map, struct device *dev) +{ + unsigned int rev2, minor, major, type, subtype; + const char *name = "unknown"; + int ret, i; + + ret = regmap_read(map, PMIC_TYPE, &type); + if (ret < 0) + return; + + if (type != PMIC_TYPE_VALUE) + return; + + ret = regmap_read(map, PMIC_SUBTYPE, &subtype); + if (ret < 0) + return; + + for (i = 0; i < ARRAY_SIZE(pmic_spmi_id_table); i++) { + if (subtype == (unsigned long)pmic_spmi_id_table[i].data) + break; + } + + if (i != ARRAY_SIZE(pmic_spmi_id_table)) + name = pmic_spmi_id_table[i].compatible; + + ret = regmap_read(map, PMIC_REV2, &rev2); + if (ret < 0) + return; + + ret = regmap_read(map, PMIC_REV3, &minor); + if (ret < 0) + return; + + ret = regmap_read(map, PMIC_REV4, &major); + if (ret < 0) + return; + + /* + * In early versions of PM8941 and PM8226, the major revision number + * started incrementing from 0 (eg 0 = v1.0, 1 = v2.0). + * Increment the major revision number here if the chip is an early + * version of PM8941 or PM8226. + */ + if ((subtype == PM8941_SUBTYPE || subtype == PM8226_SUBTYPE) && + major < 0x02) + major++; + + if (subtype == PM8110_SUBTYPE) + minor = rev2; + + dev_dbg(dev, "%x: %s v%d.%d\n", subtype, name, major, minor); +} + static const struct regmap_config spmi_regmap_config = { .reg_bits = 16, .val_bits = 8, @@ -33,6 +127,8 @@ static int pmic_spmi_probe(struct spmi_device *sdev) if (IS_ERR(regmap)) return PTR_ERR(regmap); + pmic_spmi_show_revid(regmap, &sdev->dev); + return of_platform_populate(root, NULL, NULL, &sdev->dev); } @@ -41,13 +137,6 @@ static void pmic_spmi_remove(struct spmi_device *sdev) of_platform_depopulate(&sdev->dev); } -static const struct of_device_id pmic_spmi_id_table[] = { - { .compatible = "qcom,spmi-pmic" }, - { .compatible = "qcom,pm8941" }, - { .compatible = "qcom,pm8841" }, - { .compatible = "qcom,pma8084" }, - { } -}; MODULE_DEVICE_TABLE(of, pmic_spmi_id_table); static struct spmi_driver pmic_spmi_driver = { diff --git a/drivers/mfd/qcom_rpm.c b/drivers/mfd/qcom_rpm.c index f696328c2933ff..12e324319573eb 100644 --- a/drivers/mfd/qcom_rpm.c +++ b/drivers/mfd/qcom_rpm.c @@ -323,10 +323,51 @@ static const struct qcom_rpm_data msm8960_template = { .n_resources = ARRAY_SIZE(msm8960_rpm_resource_table), }; +static const struct qcom_rpm_resource ipq806x_rpm_resource_table[] = { + [QCOM_RPM_CXO_CLK] = { 25, 9, 5, 1 }, + [QCOM_RPM_PXO_CLK] = { 26, 10, 6, 1 }, + [QCOM_RPM_APPS_FABRIC_CLK] = { 27, 11, 8, 1 }, + [QCOM_RPM_SYS_FABRIC_CLK] = { 28, 12, 9, 1 }, + [QCOM_RPM_NSS_FABRIC_0_CLK] = { 29, 13, 10, 1 }, + [QCOM_RPM_DAYTONA_FABRIC_CLK] = { 30, 14, 11, 1 }, + [QCOM_RPM_SFPB_CLK] = { 31, 15, 12, 1 }, + [QCOM_RPM_CFPB_CLK] = { 32, 16, 13, 1 }, + [QCOM_RPM_NSS_FABRIC_1_CLK] = { 33, 17, 14, 1 }, + [QCOM_RPM_EBI1_CLK] = { 34, 18, 16, 1 }, + [QCOM_RPM_APPS_FABRIC_HALT] = { 35, 19, 18, 2 }, + [QCOM_RPM_APPS_FABRIC_MODE] = { 37, 20, 19, 3 }, + [QCOM_RPM_APPS_FABRIC_IOCTL] = { 40, 21, 20, 1 }, + [QCOM_RPM_APPS_FABRIC_ARB] = { 41, 22, 21, 12 }, + [QCOM_RPM_SYS_FABRIC_HALT] = { 53, 23, 22, 2 }, + [QCOM_RPM_SYS_FABRIC_MODE] = { 55, 24, 23, 3 }, + [QCOM_RPM_SYS_FABRIC_IOCTL] = { 58, 25, 24, 1 }, + [QCOM_RPM_SYS_FABRIC_ARB] = { 59, 26, 25, 30 }, + [QCOM_RPM_MM_FABRIC_HALT] = { 89, 27, 26, 2 }, + [QCOM_RPM_MM_FABRIC_MODE] = { 91, 28, 27, 3 }, + [QCOM_RPM_MM_FABRIC_IOCTL] = { 94, 29, 28, 1 }, + [QCOM_RPM_MM_FABRIC_ARB] = { 95, 30, 29, 2 }, + [QCOM_RPM_CXO_BUFFERS] = { 209, 33, 31, 1 }, + [QCOM_RPM_USB_OTG_SWITCH] = { 210, 34, 32, 1 }, + [QCOM_RPM_HDMI_SWITCH] = { 211, 35, 33, 1 }, + [QCOM_RPM_DDR_DMM] = { 212, 36, 34, 2 }, + [QCOM_RPM_VDDMIN_GPIO] = { 215, 40, 39, 1 }, + [QCOM_RPM_SMB208_S1a] = { 216, 41, 90, 2 }, + [QCOM_RPM_SMB208_S1b] = { 218, 43, 91, 2 }, + [QCOM_RPM_SMB208_S2a] = { 220, 45, 92, 2 }, + [QCOM_RPM_SMB208_S2b] = { 222, 47, 93, 2 }, +}; + +static const struct qcom_rpm_data ipq806x_template = { + .version = 3, + .resource_table = ipq806x_rpm_resource_table, + .n_resources = ARRAY_SIZE(ipq806x_rpm_resource_table), +}; + static const struct of_device_id qcom_rpm_of_match[] = { { .compatible = "qcom,rpm-apq8064", .data = &apq8064_template }, { .compatible = "qcom,rpm-msm8660", .data = &msm8660_template }, { .compatible = "qcom,rpm-msm8960", .data = &msm8960_template }, + { .compatible = "qcom,rpm-ipq8064", .data = &ipq806x_template }, { } }; MODULE_DEVICE_TABLE(of, qcom_rpm_of_match); diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index bd02150698759d..4b1e4399754bf2 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -89,6 +89,7 @@ static const struct rk808_reg_data pre_init_reg[] = { { RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA }, { RK808_BUCK1_CONFIG_REG, BUCK1_RATE_MASK, BUCK_ILMIN_200MA }, { RK808_BUCK2_CONFIG_REG, BUCK2_RATE_MASK, BUCK_ILMIN_200MA }, + { RK808_DCDC_UV_ACT_REG, BUCK_UV_ACT_MASK, BUCK_UV_ACT_DISABLE}, { RK808_VB_MON_REG, MASK_ALL, VB_LO_ACT | VB_LO_SEL_3500MV }, }; @@ -245,7 +246,7 @@ static int rk808_remove(struct i2c_client *client) return 0; } -static struct of_device_id rk808_of_match[] = { +static const struct of_device_id rk808_of_match[] = { { .compatible = "rockchip,rk808" }, { }, }; diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c index fdd34c883d868b..b3ae6592014a02 100644 --- a/drivers/mfd/rtl8411.c +++ b/drivers/mfd/rtl8411.c @@ -53,7 +53,7 @@ static void rtl8411_fetch_vendor_settings(struct rtsx_pcr *pcr) u8 reg3 = 0; rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®1); - dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg1); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg1); if (!rtsx_vendor_setting_valid(reg1)) return; @@ -65,7 +65,7 @@ static void rtl8411_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg1); rtsx_pci_read_config_byte(pcr, PCR_SETTING_REG3, ®3); - dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG3, reg3); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG3, reg3); pcr->sd30_drive_sel_3v3 = rtl8411_reg_to_sd30_drive_sel_3v3(reg3); } @@ -74,7 +74,7 @@ static void rtl8411b_fetch_vendor_settings(struct rtsx_pcr *pcr) u32 reg = 0; rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); - dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); if (!rtsx_vendor_setting_valid(reg)) return; @@ -260,9 +260,8 @@ static unsigned int rtl8411_cd_deglitch(struct rtsx_pcr *pcr) rtsx_pci_write_register(pcr, CARD_PWR_CTL, BPP_POWER_MASK, BPP_POWER_OFF); - dev_dbg(&(pcr->pci->dev), - "After CD deglitch, card_exist = 0x%x\n", - card_exist); + pcr_dbg(pcr, "After CD deglitch, card_exist = 0x%x\n", + card_exist); } if (card_exist & MS_EXIST) { diff --git a/drivers/mfd/rts5209.c b/drivers/mfd/rts5209.c index cb04174a8924b0..373e253c33df94 100644 --- a/drivers/mfd/rts5209.c +++ b/drivers/mfd/rts5209.c @@ -38,7 +38,7 @@ static void rts5209_fetch_vendor_settings(struct rtsx_pcr *pcr) u32 reg; rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); - dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); if (rts5209_vendor_setting1_valid(reg)) { if (rts5209_reg_check_ms_pmos(reg)) @@ -47,7 +47,7 @@ static void rts5209_fetch_vendor_settings(struct rtsx_pcr *pcr) } rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); - dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); if (rts5209_vendor_setting2_valid(reg)) { pcr->sd30_drive_sel_1v8 = diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c index 32407404d838f2..ce012d78ce2a0f 100644 --- a/drivers/mfd/rts5227.c +++ b/drivers/mfd/rts5227.c @@ -63,7 +63,7 @@ static void rts5227_fetch_vendor_settings(struct rtsx_pcr *pcr) u32 reg; rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); - dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); if (!rtsx_vendor_setting_valid(reg)) return; @@ -74,7 +74,7 @@ static void rts5227_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); - dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg); if (rtsx_reg_check_reverse_socket(reg)) pcr->flags |= PCR_REVERSE_SOCKET; @@ -118,11 +118,9 @@ static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) rts5227_fill_driving(pcr, OUTPUT_3V3); /* Configure force_clock_req */ if (pcr->flags & PCR_REVERSE_SOCKET) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - AUTOLOAD_CFG_BASE + 3, 0xB8, 0xB8); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB8, 0xB8); else - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - AUTOLOAD_CFG_BASE + 3, 0xB8, 0x88); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB8, 0x88); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CTRL3, 0x10, 0x00); return rtsx_pci_send_cmd(pcr, 100); @@ -132,7 +130,7 @@ static int rts5227_optimize_phy(struct rtsx_pcr *pcr) { int err; - err = rtsx_gops_pm_reset(pcr); + err = rtsx_pci_write_register(pcr, PM_CTRL3, D3_DELINK_MODE_EN, 0x00); if (err < 0) return err; diff --git a/drivers/mfd/rts5229.c b/drivers/mfd/rts5229.c index 6353f5df087aa4..ace45384ec8bc2 100644 --- a/drivers/mfd/rts5229.c +++ b/drivers/mfd/rts5229.c @@ -38,7 +38,7 @@ static void rts5229_fetch_vendor_settings(struct rtsx_pcr *pcr) u32 reg; rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); - dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); if (!rtsx_vendor_setting_valid(reg)) return; @@ -50,7 +50,7 @@ static void rts5229_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); - dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); pcr->sd30_drive_sel_3v3 = map_sd_drive(rtsx_reg_to_sd30_drive_sel_3v3(reg)); } diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c index cf425cc959d5df..eb2d5866f71952 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/mfd/rts5249.c @@ -36,16 +36,16 @@ static u8 rts5249_get_ic_version(struct rtsx_pcr *pcr) static void rts5249_fill_driving(struct rtsx_pcr *pcr, u8 voltage) { u8 driving_3v3[4][3] = { - {0x11, 0x11, 0x11}, + {0x11, 0x11, 0x18}, {0x55, 0x55, 0x5C}, - {0x99, 0x99, 0x92}, - {0x99, 0x99, 0x92}, + {0xFF, 0xFF, 0xFF}, + {0x96, 0x96, 0x96}, }; u8 driving_1v8[4][3] = { + {0xC4, 0xC4, 0xC4}, {0x3C, 0x3C, 0x3C}, - {0xB3, 0xB3, 0xB3}, {0xFE, 0xFE, 0xFE}, - {0xC4, 0xC4, 0xC4}, + {0xB3, 0xB3, 0xB3}, }; u8 (*driving)[3], drive_sel; @@ -65,15 +65,17 @@ static void rts5249_fill_driving(struct rtsx_pcr *pcr, u8 voltage) 0xFF, driving[drive_sel][2]); } -static void rts5249_fetch_vendor_settings(struct rtsx_pcr *pcr) +static void rtsx_base_fetch_vendor_settings(struct rtsx_pcr *pcr) { u32 reg; rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); - dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); - if (!rtsx_vendor_setting_valid(reg)) + if (!rtsx_vendor_setting_valid(reg)) { + pcr_dbg(pcr, "skip fetch vendor setting\n"); return; + } pcr->aspm_en = rtsx_reg_to_aspm(reg); pcr->sd30_drive_sel_1v8 = rtsx_reg_to_sd30_drive_sel_1v8(reg); @@ -81,13 +83,13 @@ static void rts5249_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); - dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); + pcr_dbg(pcr, "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg); if (rtsx_reg_check_reverse_socket(reg)) pcr->flags |= PCR_REVERSE_SOCKET; } -static void rts5249_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) +static void rtsx_base_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) { /* Set relink_time to 0 */ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, 0xFF, 0); @@ -95,7 +97,8 @@ static void rts5249_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, 0x01, 0); if (pm_state == HOST_ENTER_S3) - rtsx_pci_write_register(pcr, PM_CTRL3, 0x10, 0x10); + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, + D3_DELINK_MODE_EN, D3_DELINK_MODE_EN); rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03); } @@ -104,6 +107,8 @@ static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) { rtsx_pci_init_cmd(pcr); + /* Rest L1SUB Config */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, L1SUB_CONFIG3, 0xFF, 0x00); /* Configure GPIO as output */ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02); /* Reset ASPM state to default value */ @@ -116,12 +121,9 @@ static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) /* Configure driving */ rts5249_fill_driving(pcr, OUTPUT_3V3); if (pcr->flags & PCR_REVERSE_SOCKET) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - AUTOLOAD_CFG_BASE + 3, 0xB0, 0xB0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB0, 0xB0); else - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - AUTOLOAD_CFG_BASE + 3, 0xB0, 0x80); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CTRL3, 0x10, 0x00); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB0, 0x80); return rtsx_pci_send_cmd(pcr, 100); } @@ -130,15 +132,16 @@ static int rts5249_optimize_phy(struct rtsx_pcr *pcr) { int err; - err = rtsx_gops_pm_reset(pcr); + err = rtsx_pci_write_register(pcr, PM_CTRL3, D3_DELINK_MODE_EN, 0x00); if (err < 0) return err; - err = rtsx_pci_write_phy_register(pcr, PHY_REG_REV, - PHY_REG_REV_RESV | PHY_REG_REV_RXIDLE_LATCHED | - PHY_REG_REV_P1_EN | PHY_REG_REV_RXIDLE_EN | - PHY_REG_REV_RX_PWST | PHY_REG_REV_CLKREQ_DLY_TIMER_1_0 | - PHY_REG_REV_STOP_CLKRD | PHY_REG_REV_STOP_CLKWR); + err = rtsx_pci_write_phy_register(pcr, PHY_REV, + PHY_REV_RESV | PHY_REV_RXIDLE_LATCHED | + PHY_REV_P1_EN | PHY_REV_RXIDLE_EN | + PHY_REV_CLKREQ_TX_EN | PHY_REV_RX_PWST | + PHY_REV_CLKREQ_DT_1_0 | PHY_REV_STOP_CLKRD | + PHY_REV_STOP_CLKWR); if (err < 0) return err; @@ -149,19 +152,21 @@ static int rts5249_optimize_phy(struct rtsx_pcr *pcr) PHY_BPCR_IB_FILTER | PHY_BPCR_CMIRROR_EN); if (err < 0) return err; + err = rtsx_pci_write_phy_register(pcr, PHY_PCR, PHY_PCR_FORCE_CODE | PHY_PCR_OOBS_CALI_50 | PHY_PCR_OOBS_VCM_08 | PHY_PCR_OOBS_SEN_90 | - PHY_PCR_RSSI_EN); + PHY_PCR_RSSI_EN | PHY_PCR_RX10K); if (err < 0) return err; + err = rtsx_pci_write_phy_register(pcr, PHY_RCR2, PHY_RCR2_EMPHASE_EN | PHY_RCR2_NADJR | - PHY_RCR2_CDR_CP_10 | PHY_RCR2_CDR_SR_2 | - PHY_RCR2_FREQSEL_12 | PHY_RCR2_CPADJEN | - PHY_RCR2_CDR_SC_8 | PHY_RCR2_CALIB_LATE); + PHY_RCR2_CDR_SR_2 | PHY_RCR2_FREQSEL_12 | + PHY_RCR2_CDR_SC_12P | PHY_RCR2_CALIB_LATE); if (err < 0) return err; + err = rtsx_pci_write_phy_register(pcr, PHY_FLD4, PHY_FLD4_FLDEN_SEL | PHY_FLD4_REQ_REF | PHY_FLD4_RXAMP_OFF | PHY_FLD4_REQ_ADDA | @@ -169,11 +174,12 @@ static int rts5249_optimize_phy(struct rtsx_pcr *pcr) PHY_FLD4_BER_CHK_EN); if (err < 0) return err; - err = rtsx_pci_write_phy_register(pcr, PHY_RDR, PHY_RDR_RXDSEL_1_9); + err = rtsx_pci_write_phy_register(pcr, PHY_RDR, + PHY_RDR_RXDSEL_1_9 | PHY_SSC_AUTO_PWD); if (err < 0) return err; err = rtsx_pci_write_phy_register(pcr, PHY_RCR1, - PHY_RCR1_ADP_TIME | PHY_RCR1_VCO_COARSE); + PHY_RCR1_ADP_TIME_4 | PHY_RCR1_VCO_COARSE); if (err < 0) return err; err = rtsx_pci_write_phy_register(pcr, PHY_FLD3, @@ -181,33 +187,34 @@ static int rts5249_optimize_phy(struct rtsx_pcr *pcr) PHY_FLD3_RXDELINK); if (err < 0) return err; + return rtsx_pci_write_phy_register(pcr, PHY_TUNE, PHY_TUNE_TUNEREF_1_0 | PHY_TUNE_VBGSEL_1252 | PHY_TUNE_SDBUS_33 | PHY_TUNE_TUNED18 | - PHY_TUNE_TUNED12); + PHY_TUNE_TUNED12 | PHY_TUNE_TUNEA12); } -static int rts5249_turn_on_led(struct rtsx_pcr *pcr) +static int rtsx_base_turn_on_led(struct rtsx_pcr *pcr) { return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02); } -static int rts5249_turn_off_led(struct rtsx_pcr *pcr) +static int rtsx_base_turn_off_led(struct rtsx_pcr *pcr) { return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00); } -static int rts5249_enable_auto_blink(struct rtsx_pcr *pcr) +static int rtsx_base_enable_auto_blink(struct rtsx_pcr *pcr) { return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08); } -static int rts5249_disable_auto_blink(struct rtsx_pcr *pcr) +static int rtsx_base_disable_auto_blink(struct rtsx_pcr *pcr) { return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00); } -static int rts5249_card_power_on(struct rtsx_pcr *pcr, int card) +static int rtsx_base_card_power_on(struct rtsx_pcr *pcr, int card) { int err; @@ -234,7 +241,7 @@ static int rts5249_card_power_on(struct rtsx_pcr *pcr, int card) return 0; } -static int rts5249_card_power_off(struct rtsx_pcr *pcr, int card) +static int rtsx_base_card_power_off(struct rtsx_pcr *pcr, int card) { rtsx_pci_init_cmd(pcr); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, @@ -244,22 +251,35 @@ static int rts5249_card_power_off(struct rtsx_pcr *pcr, int card) return rtsx_pci_send_cmd(pcr, 100); } -static int rts5249_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +static int rtsx_base_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) { int err; + u16 append; - if (voltage == OUTPUT_3V3) { - err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, 0x4FC0 | 0x24); + switch (voltage) { + case OUTPUT_3V3: + err = rtsx_pci_update_phy(pcr, PHY_TUNE, PHY_TUNE_VOLTAGE_MASK, + PHY_TUNE_VOLTAGE_3V3); if (err < 0) return err; - } else if (voltage == OUTPUT_1V8) { - err = rtsx_pci_write_phy_register(pcr, PHY_BACR, 0x3C02); + break; + case OUTPUT_1V8: + append = PHY_TUNE_D18_1V8; + if (CHK_PCI_PID(pcr, 0x5249)) { + err = rtsx_pci_update_phy(pcr, PHY_BACR, + PHY_BACR_BASIC_MASK, 0); + if (err < 0) + return err; + append = PHY_TUNE_D18_1V7; + } + + err = rtsx_pci_update_phy(pcr, PHY_TUNE, PHY_TUNE_VOLTAGE_MASK, + append); if (err < 0) return err; - err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, 0x4C40 | 0x24); - if (err < 0) - return err; - } else { + break; + default: + pcr_dbg(pcr, "unknown output voltage %d\n", voltage); return -EINVAL; } @@ -270,17 +290,17 @@ static int rts5249_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) } static const struct pcr_ops rts5249_pcr_ops = { - .fetch_vendor_settings = rts5249_fetch_vendor_settings, + .fetch_vendor_settings = rtsx_base_fetch_vendor_settings, .extra_init_hw = rts5249_extra_init_hw, .optimize_phy = rts5249_optimize_phy, - .turn_on_led = rts5249_turn_on_led, - .turn_off_led = rts5249_turn_off_led, - .enable_auto_blink = rts5249_enable_auto_blink, - .disable_auto_blink = rts5249_disable_auto_blink, - .card_power_on = rts5249_card_power_on, - .card_power_off = rts5249_card_power_off, - .switch_output_voltage = rts5249_switch_output_voltage, - .force_power_down = rts5249_force_power_down, + .turn_on_led = rtsx_base_turn_on_led, + .turn_off_led = rtsx_base_turn_off_led, + .enable_auto_blink = rtsx_base_enable_auto_blink, + .disable_auto_blink = rtsx_base_disable_auto_blink, + .card_power_on = rtsx_base_card_power_on, + .card_power_off = rtsx_base_card_power_off, + .switch_output_voltage = rtsx_base_switch_output_voltage, + .force_power_down = rtsx_base_force_power_down, }; /* SD Pull Control Enable: @@ -343,7 +363,7 @@ void rts5249_init_params(struct rtsx_pcr *pcr) pcr->flags = 0; pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT; - pcr->sd30_drive_sel_1v8 = CFG_DRIVER_TYPE_C; + pcr->sd30_drive_sel_1v8 = CFG_DRIVER_TYPE_B; pcr->sd30_drive_sel_3v3 = CFG_DRIVER_TYPE_B; pcr->aspm_en = ASPM_L1_EN; pcr->tx_initial_phase = SET_CLOCK_PHASE(1, 29, 16); @@ -354,4 +374,219 @@ void rts5249_init_params(struct rtsx_pcr *pcr) pcr->sd_pull_ctl_disable_tbl = rts5249_sd_pull_ctl_disable_tbl; pcr->ms_pull_ctl_enable_tbl = rts5249_ms_pull_ctl_enable_tbl; pcr->ms_pull_ctl_disable_tbl = rts5249_ms_pull_ctl_disable_tbl; + + pcr->reg_pm_ctrl3 = PM_CTRL3; +} + +static int rts524a_write_phy(struct rtsx_pcr *pcr, u8 addr, u16 val) +{ + addr = addr & 0x80 ? (addr & 0x7F) | 0x40 : addr; + + return __rtsx_pci_write_phy_register(pcr, addr, val); +} + +static int rts524a_read_phy(struct rtsx_pcr *pcr, u8 addr, u16 *val) +{ + addr = addr & 0x80 ? (addr & 0x7F) | 0x40 : addr; + + return __rtsx_pci_read_phy_register(pcr, addr, val); } + +static int rts524a_optimize_phy(struct rtsx_pcr *pcr) +{ + int err; + + err = rtsx_pci_write_register(pcr, RTS524A_PM_CTRL3, + D3_DELINK_MODE_EN, 0x00); + if (err < 0) + return err; + + rtsx_pci_write_phy_register(pcr, PHY_PCR, + PHY_PCR_FORCE_CODE | PHY_PCR_OOBS_CALI_50 | + PHY_PCR_OOBS_VCM_08 | PHY_PCR_OOBS_SEN_90 | PHY_PCR_RSSI_EN); + rtsx_pci_write_phy_register(pcr, PHY_SSCCR3, + PHY_SSCCR3_STEP_IN | PHY_SSCCR3_CHECK_DELAY); + + if (is_version(pcr, 0x524A, IC_VER_A)) { + rtsx_pci_write_phy_register(pcr, PHY_SSCCR3, + PHY_SSCCR3_STEP_IN | PHY_SSCCR3_CHECK_DELAY); + rtsx_pci_write_phy_register(pcr, PHY_SSCCR2, + PHY_SSCCR2_PLL_NCODE | PHY_SSCCR2_TIME0 | + PHY_SSCCR2_TIME2_WIDTH); + rtsx_pci_write_phy_register(pcr, PHY_ANA1A, + PHY_ANA1A_TXR_LOOPBACK | PHY_ANA1A_RXT_BIST | + PHY_ANA1A_TXR_BIST | PHY_ANA1A_REV); + rtsx_pci_write_phy_register(pcr, PHY_ANA1D, + PHY_ANA1D_DEBUG_ADDR); + rtsx_pci_write_phy_register(pcr, PHY_DIG1E, + PHY_DIG1E_REV | PHY_DIG1E_D0_X_D1 | + PHY_DIG1E_RX_ON_HOST | PHY_DIG1E_RCLK_REF_HOST | + PHY_DIG1E_RCLK_TX_EN_KEEP | + PHY_DIG1E_RCLK_TX_TERM_KEEP | + PHY_DIG1E_RCLK_RX_EIDLE_ON | PHY_DIG1E_TX_TERM_KEEP | + PHY_DIG1E_RX_TERM_KEEP | PHY_DIG1E_TX_EN_KEEP | + PHY_DIG1E_RX_EN_KEEP); + } + + rtsx_pci_write_phy_register(pcr, PHY_ANA08, + PHY_ANA08_RX_EQ_DCGAIN | PHY_ANA08_SEL_RX_EN | + PHY_ANA08_RX_EQ_VAL | PHY_ANA08_SCP | PHY_ANA08_SEL_IPI); + + return 0; +} + +static int rts524a_extra_init_hw(struct rtsx_pcr *pcr) +{ + rts5249_extra_init_hw(pcr); + + rtsx_pci_write_register(pcr, FUNC_FORCE_CTL, + FORCE_ASPM_L1_EN, FORCE_ASPM_L1_EN); + rtsx_pci_write_register(pcr, PM_EVENT_DEBUG, PME_DEBUG_0, PME_DEBUG_0); + rtsx_pci_write_register(pcr, LDO_VCC_CFG1, LDO_VCC_LMT_EN, + LDO_VCC_LMT_EN); + rtsx_pci_write_register(pcr, PCLK_CTL, PCLK_MODE_SEL, PCLK_MODE_SEL); + if (is_version(pcr, 0x524A, IC_VER_A)) { + rtsx_pci_write_register(pcr, LDO_DV18_CFG, + LDO_DV18_SR_MASK, LDO_DV18_SR_DF); + rtsx_pci_write_register(pcr, LDO_VCC_CFG1, + LDO_VCC_REF_TUNE_MASK, LDO_VCC_REF_1V2); + rtsx_pci_write_register(pcr, LDO_VIO_CFG, + LDO_VIO_REF_TUNE_MASK, LDO_VIO_REF_1V2); + rtsx_pci_write_register(pcr, LDO_VIO_CFG, + LDO_VIO_SR_MASK, LDO_VIO_SR_DF); + rtsx_pci_write_register(pcr, LDO_DV12S_CFG, + LDO_REF12_TUNE_MASK, LDO_REF12_TUNE_DF); + rtsx_pci_write_register(pcr, SD40_LDO_CTL1, + SD40_VIO_TUNE_MASK, SD40_VIO_TUNE_1V7); + } + + return 0; +} + +static const struct pcr_ops rts524a_pcr_ops = { + .write_phy = rts524a_write_phy, + .read_phy = rts524a_read_phy, + .fetch_vendor_settings = rtsx_base_fetch_vendor_settings, + .extra_init_hw = rts524a_extra_init_hw, + .optimize_phy = rts524a_optimize_phy, + .turn_on_led = rtsx_base_turn_on_led, + .turn_off_led = rtsx_base_turn_off_led, + .enable_auto_blink = rtsx_base_enable_auto_blink, + .disable_auto_blink = rtsx_base_disable_auto_blink, + .card_power_on = rtsx_base_card_power_on, + .card_power_off = rtsx_base_card_power_off, + .switch_output_voltage = rtsx_base_switch_output_voltage, + .force_power_down = rtsx_base_force_power_down, +}; + +void rts524a_init_params(struct rtsx_pcr *pcr) +{ + rts5249_init_params(pcr); + + pcr->reg_pm_ctrl3 = RTS524A_PM_CTRL3; + pcr->ops = &rts524a_pcr_ops; +} + +static int rts525a_card_power_on(struct rtsx_pcr *pcr, int card) +{ + rtsx_pci_write_register(pcr, LDO_VCC_CFG1, + LDO_VCC_TUNE_MASK, LDO_VCC_3V3); + return rtsx_base_card_power_on(pcr, card); +} + +static int rts525a_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ + switch (voltage) { + case OUTPUT_3V3: + rtsx_pci_write_register(pcr, LDO_CONFIG2, + LDO_D3318_MASK, LDO_D3318_33V); + rtsx_pci_write_register(pcr, SD_PAD_CTL, SD_IO_USING_1V8, 0); + break; + case OUTPUT_1V8: + rtsx_pci_write_register(pcr, LDO_CONFIG2, + LDO_D3318_MASK, LDO_D3318_18V); + rtsx_pci_write_register(pcr, SD_PAD_CTL, SD_IO_USING_1V8, + SD_IO_USING_1V8); + break; + default: + return -EINVAL; + } + + rtsx_pci_init_cmd(pcr); + rts5249_fill_driving(pcr, voltage); + return rtsx_pci_send_cmd(pcr, 100); +} + +static int rts525a_optimize_phy(struct rtsx_pcr *pcr) +{ + int err; + + err = rtsx_pci_write_register(pcr, RTS524A_PM_CTRL3, + D3_DELINK_MODE_EN, 0x00); + if (err < 0) + return err; + + rtsx_pci_write_phy_register(pcr, _PHY_FLD0, + _PHY_FLD0_CLK_REQ_20C | _PHY_FLD0_RX_IDLE_EN | + _PHY_FLD0_BIT_ERR_RSTN | _PHY_FLD0_BER_COUNT | + _PHY_FLD0_BER_TIMER | _PHY_FLD0_CHECK_EN); + + rtsx_pci_write_phy_register(pcr, _PHY_ANA03, + _PHY_ANA03_TIMER_MAX | _PHY_ANA03_OOBS_DEB_EN | + _PHY_CMU_DEBUG_EN); + + if (is_version(pcr, 0x525A, IC_VER_A)) + rtsx_pci_write_phy_register(pcr, _PHY_REV0, + _PHY_REV0_FILTER_OUT | _PHY_REV0_CDR_BYPASS_PFD | + _PHY_REV0_CDR_RX_IDLE_BYPASS); + + return 0; +} + +static int rts525a_extra_init_hw(struct rtsx_pcr *pcr) +{ + rts5249_extra_init_hw(pcr); + + rtsx_pci_write_register(pcr, PCLK_CTL, PCLK_MODE_SEL, PCLK_MODE_SEL); + if (is_version(pcr, 0x525A, IC_VER_A)) { + rtsx_pci_write_register(pcr, L1SUB_CONFIG2, + L1SUB_AUTO_CFG, L1SUB_AUTO_CFG); + rtsx_pci_write_register(pcr, RREF_CFG, + RREF_VBGSEL_MASK, RREF_VBGSEL_1V25); + rtsx_pci_write_register(pcr, LDO_VIO_CFG, + LDO_VIO_TUNE_MASK, LDO_VIO_1V7); + rtsx_pci_write_register(pcr, LDO_DV12S_CFG, + LDO_D12_TUNE_MASK, LDO_D12_TUNE_DF); + rtsx_pci_write_register(pcr, LDO_AV12S_CFG, + LDO_AV12S_TUNE_MASK, LDO_AV12S_TUNE_DF); + rtsx_pci_write_register(pcr, LDO_VCC_CFG0, + LDO_VCC_LMTVTH_MASK, LDO_VCC_LMTVTH_2A); + rtsx_pci_write_register(pcr, OOBS_CONFIG, + OOBS_AUTOK_DIS | OOBS_VAL_MASK, 0x89); + } + + return 0; +} + +static const struct pcr_ops rts525a_pcr_ops = { + .fetch_vendor_settings = rtsx_base_fetch_vendor_settings, + .extra_init_hw = rts525a_extra_init_hw, + .optimize_phy = rts525a_optimize_phy, + .turn_on_led = rtsx_base_turn_on_led, + .turn_off_led = rtsx_base_turn_off_led, + .enable_auto_blink = rtsx_base_enable_auto_blink, + .disable_auto_blink = rtsx_base_disable_auto_blink, + .card_power_on = rts525a_card_power_on, + .card_power_off = rtsx_base_card_power_off, + .switch_output_voltage = rts525a_switch_output_voltage, + .force_power_down = rtsx_base_force_power_down, +}; + +void rts525a_init_params(struct rtsx_pcr *pcr) +{ + rts5249_init_params(pcr); + + pcr->reg_pm_ctrl3 = RTS524A_PM_CTRL3; + pcr->ops = &rts525a_pcr_ops; +} + diff --git a/drivers/mfd/rtsx_gops.c b/drivers/mfd/rtsx_gops.c deleted file mode 100644 index b1a98c67859319..00000000000000 --- a/drivers/mfd/rtsx_gops.c +++ /dev/null @@ -1,37 +0,0 @@ -/* Driver for Realtek PCI-Express card reader - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. 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 as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - * - * Author: - * Micky Ching - */ - -#include -#include "rtsx_pcr.h" - -int rtsx_gops_pm_reset(struct rtsx_pcr *pcr) -{ - int err; - - /* init aspm */ - rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, 0xFF, 0x00); - err = rtsx_pci_update_cfg_byte(pcr, LCTLR, ~LCTLR_ASPM_CTL_MASK, 0x00); - if (err < 0) - return err; - - /* reset PM_CTRL3 before send buffer cmd */ - return rtsx_pci_write_register(pcr, PM_CTRL3, D3_DELINK_MODE_EN, 0x00); -} diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 30f7ca89a0e686..a66540a4907995 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -58,11 +58,25 @@ static const struct pci_device_id rtsx_pci_ids[] = { { PCI_DEVICE(0x10EC, 0x5249), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5287), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5286), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x524A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x525A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { 0, } }; MODULE_DEVICE_TABLE(pci, rtsx_pci_ids); +static inline void rtsx_pci_enable_aspm(struct rtsx_pcr *pcr) +{ + rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL, + 0xFC, pcr->aspm_en); +} + +static inline void rtsx_pci_disable_aspm(struct rtsx_pcr *pcr) +{ + rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL, + 0xFC, 0); +} + void rtsx_pci_start_run(struct rtsx_pcr *pcr) { /* If pci device removed, don't queue idle work any more */ @@ -75,7 +89,7 @@ void rtsx_pci_start_run(struct rtsx_pcr *pcr) pcr->ops->enable_auto_blink(pcr); if (pcr->aspm_en) - rtsx_pci_write_config_byte(pcr, LCTLR, 0); + rtsx_pci_disable_aspm(pcr); } mod_delayed_work(system_wq, &pcr->idle_work, msecs_to_jiffies(200)); @@ -130,7 +144,7 @@ int rtsx_pci_read_register(struct rtsx_pcr *pcr, u16 addr, u8 *data) } EXPORT_SYMBOL_GPL(rtsx_pci_read_register); -int rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val) +int __rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val) { int err, i, finished = 0; u8 tmp; @@ -162,9 +176,17 @@ int rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val) return 0; } + +int rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val) +{ + if (pcr->ops->write_phy) + return pcr->ops->write_phy(pcr, addr, val); + + return __rtsx_pci_write_phy_register(pcr, addr, val); +} EXPORT_SYMBOL_GPL(rtsx_pci_write_phy_register); -int rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val) +int __rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val) { int err, i, finished = 0; u16 data; @@ -210,6 +232,14 @@ int rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val) return 0; } + +int rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val) +{ + if (pcr->ops->read_phy) + return pcr->ops->read_phy(pcr, addr, val); + + return __rtsx_pci_read_phy_register(pcr, addr, val); +} EXPORT_SYMBOL_GPL(rtsx_pci_read_phy_register); void rtsx_pci_stop_cmd(struct rtsx_pcr *pcr) @@ -286,8 +316,7 @@ int rtsx_pci_send_cmd(struct rtsx_pcr *pcr, int timeout) timeleft = wait_for_completion_interruptible_timeout( &trans_done, msecs_to_jiffies(timeout)); if (timeleft <= 0) { - dev_dbg(&(pcr->pci->dev), "Timeout (%s %d)\n", - __func__, __LINE__); + pcr_dbg(pcr, "Timeout (%s %d)\n", __func__, __LINE__); err = -ETIMEDOUT; goto finish_send_cmd; } @@ -323,8 +352,7 @@ static void rtsx_pci_add_sg_tbl(struct rtsx_pcr *pcr, u64 val; u8 option = SG_VALID | SG_TRANS_DATA; - dev_dbg(&(pcr->pci->dev), "DMA addr: 0x%x, Len: 0x%x\n", - (unsigned int)addr, len); + pcr_dbg(pcr, "DMA addr: 0x%x, Len: 0x%x\n", (unsigned int)addr, len); if (end) option |= SG_END; @@ -339,11 +367,11 @@ int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist, { int err = 0, count; - dev_dbg(&(pcr->pci->dev), "--> %s: num_sg = %d\n", __func__, num_sg); + pcr_dbg(pcr, "--> %s: num_sg = %d\n", __func__, num_sg); count = rtsx_pci_dma_map_sg(pcr, sglist, num_sg, read); if (count < 1) return -EINVAL; - dev_dbg(&(pcr->pci->dev), "DMA mapping count: %d\n", count); + pcr_dbg(pcr, "DMA mapping count: %d\n", count); err = rtsx_pci_dma_transfer(pcr, sglist, count, read, timeout); @@ -417,8 +445,7 @@ int rtsx_pci_dma_transfer(struct rtsx_pcr *pcr, struct scatterlist *sglist, timeleft = wait_for_completion_interruptible_timeout( &trans_done, msecs_to_jiffies(timeout)); if (timeleft <= 0) { - dev_dbg(&(pcr->pci->dev), "Timeout (%s %d)\n", - __func__, __LINE__); + pcr_dbg(pcr, "Timeout (%s %d)\n", __func__, __LINE__); err = -ETIMEDOUT; goto out; } @@ -592,7 +619,7 @@ static void rtsx_pci_enable_bus_int(struct rtsx_pcr *pcr) /* Enable Bus Interrupt */ rtsx_pci_writel(pcr, RTSX_BIER, pcr->bier); - dev_dbg(&(pcr->pci->dev), "RTSX_BIER: 0x%08x\n", pcr->bier); + pcr_dbg(pcr, "RTSX_BIER: 0x%08x\n", pcr->bier); } static inline u8 double_ssc_depth(u8 depth) @@ -638,14 +665,13 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, return err; card_clock /= 1000000; - dev_dbg(&(pcr->pci->dev), "Switch card clock to %dMHz\n", card_clock); + pcr_dbg(pcr, "Switch card clock to %dMHz\n", card_clock); clk = card_clock; if (!initial_mode && double_clk) clk = card_clock * 2; - dev_dbg(&(pcr->pci->dev), - "Internal SSC clock: %dMHz (cur_clock = %d)\n", - clk, pcr->cur_clock); + pcr_dbg(pcr, "Internal SSC clock: %dMHz (cur_clock = %d)\n", + clk, pcr->cur_clock); if (clk == pcr->cur_clock) return 0; @@ -674,14 +700,14 @@ int rtsx_pci_switch_clock(struct rtsx_pcr *pcr, unsigned int card_clock, } div++; } - dev_dbg(&(pcr->pci->dev), "n = %d, div = %d\n", n, div); + pcr_dbg(pcr, "n = %d, div = %d\n", n, div); ssc_depth = depth[ssc_depth]; if (double_clk) ssc_depth = double_ssc_depth(ssc_depth); ssc_depth = revise_ssc_depth(ssc_depth, div); - dev_dbg(&(pcr->pci->dev), "ssc_depth = %d\n", ssc_depth); + pcr_dbg(pcr, "ssc_depth = %d\n", ssc_depth); rtsx_pci_init_cmd(pcr); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, @@ -803,13 +829,13 @@ static void rtsx_pci_card_detect(struct work_struct *work) dwork = to_delayed_work(work); pcr = container_of(dwork, struct rtsx_pcr, carddet_work); - dev_dbg(&(pcr->pci->dev), "--> %s\n", __func__); + pcr_dbg(pcr, "--> %s\n", __func__); mutex_lock(&pcr->pcr_mutex); spin_lock_irqsave(&pcr->lock, flags); irq_status = rtsx_pci_readl(pcr, RTSX_BIPR); - dev_dbg(&(pcr->pci->dev), "irq_status: 0x%08x\n", irq_status); + pcr_dbg(pcr, "irq_status: 0x%08x\n", irq_status); irq_status &= CARD_EXIST; card_inserted = pcr->card_inserted & irq_status; @@ -820,9 +846,8 @@ static void rtsx_pci_card_detect(struct work_struct *work) spin_unlock_irqrestore(&pcr->lock, flags); if (card_inserted || card_removed) { - dev_dbg(&(pcr->pci->dev), - "card_inserted: 0x%x, card_removed: 0x%x\n", - card_inserted, card_removed); + pcr_dbg(pcr, "card_inserted: 0x%x, card_removed: 0x%x\n", + card_inserted, card_removed); if (pcr->ops->cd_deglitch) card_inserted = pcr->ops->cd_deglitch(pcr); @@ -930,7 +955,7 @@ static void rtsx_pci_idle_work(struct work_struct *work) struct delayed_work *dwork = to_delayed_work(work); struct rtsx_pcr *pcr = container_of(dwork, struct rtsx_pcr, idle_work); - dev_dbg(&(pcr->pci->dev), "--> %s\n", __func__); + pcr_dbg(pcr, "--> %s\n", __func__); mutex_lock(&pcr->pcr_mutex); @@ -942,7 +967,7 @@ static void rtsx_pci_idle_work(struct work_struct *work) pcr->ops->turn_off_led(pcr); if (pcr->aspm_en) - rtsx_pci_write_config_byte(pcr, LCTLR, pcr->aspm_en); + rtsx_pci_enable_aspm(pcr); mutex_unlock(&pcr->pcr_mutex); } @@ -968,6 +993,7 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) { int err; + pcr->pcie_cap = pci_find_capability(pcr->pci, PCI_CAP_ID_EXP); rtsx_pci_writel(pcr, RTSX_HCBAR, pcr->host_cmds_addr); rtsx_pci_enable_bus_int(pcr); @@ -980,6 +1006,7 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) /* Wait SSC power stable */ udelay(200); + rtsx_pci_disable_aspm(pcr); if (pcr->ops->optimize_phy) { err = pcr->ops->optimize_phy(pcr); if (err < 0) @@ -1028,10 +1055,8 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) if (err < 0) return err; - rtsx_pci_write_config_byte(pcr, LCTLR, 0); - /* Enable clk_request_n to enable clock power management */ - rtsx_pci_write_config_byte(pcr, 0x81, 1); + rtsx_pci_write_config_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL + 1, 1); /* Enter L1 when host tx idle */ rtsx_pci_write_config_byte(pcr, 0x70F, 0x5B); @@ -1081,6 +1106,14 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) rts5249_init_params(pcr); break; + case 0x524A: + rts524a_init_params(pcr); + break; + + case 0x525A: + rts525a_init_params(pcr); + break; + case 0x5287: rtl8411b_init_params(pcr); break; @@ -1090,7 +1123,7 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) break; } - dev_dbg(&(pcr->pci->dev), "PID: 0x%04x, IC version: 0x%02x\n", + pcr_dbg(pcr, "PID: 0x%04x, IC version: 0x%02x\n", PCI_PID(pcr), pcr->ic_version); pcr->slots = kcalloc(pcr->num_slots, sizeof(struct rtsx_slot), @@ -1101,14 +1134,14 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) if (pcr->ops->fetch_vendor_settings) pcr->ops->fetch_vendor_settings(pcr); - dev_dbg(&(pcr->pci->dev), "pcr->aspm_en = 0x%x\n", pcr->aspm_en); - dev_dbg(&(pcr->pci->dev), "pcr->sd30_drive_sel_1v8 = 0x%x\n", + pcr_dbg(pcr, "pcr->aspm_en = 0x%x\n", pcr->aspm_en); + pcr_dbg(pcr, "pcr->sd30_drive_sel_1v8 = 0x%x\n", pcr->sd30_drive_sel_1v8); - dev_dbg(&(pcr->pci->dev), "pcr->sd30_drive_sel_3v3 = 0x%x\n", + pcr_dbg(pcr, "pcr->sd30_drive_sel_3v3 = 0x%x\n", pcr->sd30_drive_sel_3v3); - dev_dbg(&(pcr->pci->dev), "pcr->card_drive_sel = 0x%x\n", + pcr_dbg(pcr, "pcr->card_drive_sel = 0x%x\n", pcr->card_drive_sel); - dev_dbg(&(pcr->pci->dev), "pcr->flags = 0x%x\n", pcr->flags); + pcr_dbg(pcr, "pcr->flags = 0x%x\n", pcr->flags); pcr->state = PDEV_STAT_IDLE; err = rtsx_pci_init_hw(pcr); @@ -1126,7 +1159,7 @@ static int rtsx_pci_probe(struct pci_dev *pcidev, struct rtsx_pcr *pcr; struct pcr_handle *handle; u32 base, len; - int ret, i; + int ret, i, bar = 0; dev_dbg(&(pcidev->dev), ": Realtek PCI-E Card Reader found at %s [%04x:%04x] (rev %x)\n", @@ -1171,8 +1204,10 @@ static int rtsx_pci_probe(struct pci_dev *pcidev, pcr->pci = pcidev; dev_set_drvdata(&pcidev->dev, handle); - len = pci_resource_len(pcidev, 0); - base = pci_resource_start(pcidev, 0); + if (CHK_PCI_PID(pcr, 0x525A)) + bar = 1; + len = pci_resource_len(pcidev, bar); + base = pci_resource_start(pcidev, bar); pcr->remap_addr = ioremap_nocache(base, len); if (!pcr->remap_addr) { ret = -ENOMEM; diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index fe2bbb67defcd2..ce48842570d7c9 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -27,12 +27,20 @@ #define MIN_DIV_N_PCR 80 #define MAX_DIV_N_PCR 208 +#define RTS524A_PME_FORCE_CTL 0xFF78 +#define RTS524A_PM_CTRL3 0xFF7E + +int __rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val); +int __rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val); + void rts5209_init_params(struct rtsx_pcr *pcr); void rts5229_init_params(struct rtsx_pcr *pcr); void rtl8411_init_params(struct rtsx_pcr *pcr); void rtl8402_init_params(struct rtsx_pcr *pcr); void rts5227_init_params(struct rtsx_pcr *pcr); void rts5249_init_params(struct rtsx_pcr *pcr); +void rts524a_init_params(struct rtsx_pcr *pcr); +void rts525a_init_params(struct rtsx_pcr *pcr); void rtl8411b_init_params(struct rtsx_pcr *pcr); static inline u8 map_sd_drive(int idx) diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index 0a7bc43db4e47f..4a69afb425ad3e 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -68,6 +68,8 @@ static const struct mfd_cell s5m8767_devs[] = { static const struct mfd_cell s2mps11_devs[] = { { .name = "s2mps11-pmic", + }, { + .name = "s2mps14-rtc", }, { .name = "s2mps11-clk", .of_compatible = "samsung,s2mps11-clk", @@ -267,10 +269,8 @@ static struct sec_platform_data *sec_pmic_i2c_parse_dt_pdata( struct sec_platform_data *pd; pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); - if (!pd) { - dev_err(dev, "could not allocate memory for pdata\n"); + if (!pd) return ERR_PTR(-ENOMEM); - } /* * ToDo: the 'wakeup' member in the platform data is more of a linux @@ -333,7 +333,6 @@ static int sec_pmic_probe(struct i2c_client *i2c, } if (pdata) { sec_pmic->device_type = pdata->device_type; - sec_pmic->ono = pdata->ono; sec_pmic->irq_base = pdata->irq_base; sec_pmic->wakeup = pdata->wakeup; sec_pmic->pdata = pdata; diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c index ba86a918c2dace..806fa8dbb22d81 100644 --- a/drivers/mfd/sec-irq.c +++ b/drivers/mfd/sec-irq.c @@ -61,14 +61,14 @@ static const struct regmap_irq s2mps11_irqs[] = { .reg_offset = 1, .mask = S2MPS11_IRQ_RTC60S_MASK, }, - [S2MPS11_IRQ_RTCA0] = { - .reg_offset = 1, - .mask = S2MPS11_IRQ_RTCA0_MASK, - }, [S2MPS11_IRQ_RTCA1] = { .reg_offset = 1, .mask = S2MPS11_IRQ_RTCA1_MASK, }, + [S2MPS11_IRQ_RTCA0] = { + .reg_offset = 1, + .mask = S2MPS11_IRQ_RTCA0_MASK, + }, [S2MPS11_IRQ_SMPL] = { .reg_offset = 1, .mask = S2MPS11_IRQ_SMPL_MASK, @@ -484,6 +484,12 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic) return ret; } + /* + * The rtc-s5m driver requests S2MPS14_IRQ_RTCA0 also for S2MPS11 + * so the interrupt number must be consistent. + */ + BUILD_BUG_ON(((enum s2mps14_irq)S2MPS11_IRQ_RTCA0) != S2MPS14_IRQ_RTCA0); + return 0; } diff --git a/drivers/mfd/si476x-i2c.c b/drivers/mfd/si476x-i2c.c index 0e4a76daf18789..7f87c62d91b3b6 100644 --- a/drivers/mfd/si476x-i2c.c +++ b/drivers/mfd/si476x-i2c.c @@ -766,7 +766,7 @@ static int si476x_core_probe(struct i2c_client *client, sizeof(struct v4l2_rds_data), GFP_KERNEL); if (rval) { - dev_err(&client->dev, "Could not alloate the FIFO\n"); + dev_err(&client->dev, "Could not allocate the FIFO\n"); goto free_gpio; } mutex_init(&core->rds_drainer_status_lock); diff --git a/drivers/mfd/sky81452.c b/drivers/mfd/sky81452.c new file mode 100644 index 00000000000000..b0c9b041565081 --- /dev/null +++ b/drivers/mfd/sky81452.c @@ -0,0 +1,108 @@ +/* + * sky81452.c SKY81452 MFD driver + * + * Copyright 2014 Skyworks Solutions Inc. + * Author : Gyungoh Yoo + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct regmap_config sky81452_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int sky81452_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + const struct sky81452_platform_data *pdata = dev_get_platdata(dev); + struct mfd_cell cells[2]; + struct regmap *regmap; + int ret; + + if (!pdata) { + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + } + + regmap = devm_regmap_init_i2c(client, &sky81452_config); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to initialize.err=%ld\n", PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + i2c_set_clientdata(client, regmap); + + memset(cells, 0, sizeof(cells)); + cells[0].name = "sky81452-backlight"; + cells[0].of_compatible = "skyworks,sky81452-backlight"; + cells[0].platform_data = pdata->bl_pdata; + cells[0].pdata_size = sizeof(*pdata->bl_pdata); + cells[1].name = "sky81452-regulator"; + cells[1].platform_data = pdata->regulator_init_data; + cells[1].pdata_size = sizeof(*pdata->regulator_init_data); + + ret = mfd_add_devices(dev, -1, cells, ARRAY_SIZE(cells), NULL, 0, NULL); + if (ret) + dev_err(dev, "failed to add child devices. err=%d\n", ret); + + return ret; +} + +static int sky81452_remove(struct i2c_client *client) +{ + mfd_remove_devices(&client->dev); + return 0; +} + +static const struct i2c_device_id sky81452_ids[] = { + { "sky81452" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sky81452_ids); + +#ifdef CONFIG_OF +static const struct of_device_id sky81452_of_match[] = { + { .compatible = "skyworks,sky81452", }, + { } +}; +MODULE_DEVICE_TABLE(of, sky81452_of_match); +#endif + +static struct i2c_driver sky81452_driver = { + .driver = { + .name = "sky81452", + .of_match_table = of_match_ptr(sky81452_of_match), + }, + .probe = sky81452_probe, + .remove = sky81452_remove, + .id_table = sky81452_ids, +}; + +module_i2c_driver(sky81452_driver); + +MODULE_DESCRIPTION("Skyworks SKY81452 MFD driver"); +MODULE_AUTHOR("Gyungoh Yoo "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index aacb3720065c5e..cf356395c9e980 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -318,7 +318,6 @@ static int tc3589x_device_init(struct tc3589x *tc3589x) return ret; } -#ifdef CONFIG_OF static const struct of_device_id tc3589x_match[] = { /* Legacy compatible string */ { .compatible = "tc3589x", .data = (void *) TC3589X_UNKNOWN }, @@ -359,14 +358,6 @@ tc3589x_of_probe(struct device *dev, enum tc3589x_version *version) return pdata; } -#else -static inline struct tc3589x_platform_data * -tc3589x_of_probe(struct device *dev, enum tc3589x_version *version) -{ - dev_err(dev, "no device tree support\n"); - return ERR_PTR(-ENODEV); -} -#endif static int tc3589x_probe(struct i2c_client *i2c, const struct i2c_device_id *id) diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index 467c80e1c4aec1..e4e4b22eebc91c 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -68,12 +68,6 @@ static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc) DEFINE_WAIT(wait); u32 reg; - /* - * disable TSC steps so it does not run while the ADC is using it. If - * write 0 while it is running (it just started or was already running) - * then it completes all steps that were enabled and stops then. - */ - tscadc_writel(tsadc, REG_SE, 0); reg = tscadc_readl(tsadc, REG_ADCFSM); if (reg & SEQ_STATUS) { tsadc->adc_waiting = true; @@ -86,8 +80,12 @@ static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc) spin_lock_irq(&tsadc->reg_lock); finish_wait(&tsadc->reg_se_wait, &wait); + /* + * Sequencer should either be idle or + * busy applying the charge step. + */ reg = tscadc_readl(tsadc, REG_ADCFSM); - WARN_ON(reg & SEQ_STATUS); + WARN_ON((reg & SEQ_STATUS) && !(reg & CHARGE_STEP)); tsadc->adc_waiting = false; } tsadc->adc_in_use = true; @@ -96,7 +94,6 @@ static void am335x_tscadc_need_adc(struct ti_tscadc_dev *tsadc) void am335x_tsc_se_set_once(struct ti_tscadc_dev *tsadc, u32 val) { spin_lock_irq(&tsadc->reg_lock); - tsadc->reg_se_cache |= val; am335x_tscadc_need_adc(tsadc); tscadc_writel(tsadc, REG_SE, val); diff --git a/drivers/mfd/tps65010.c b/drivers/mfd/tps65010.c index 743fb524fc8ae9..448f0a182dc456 100644 --- a/drivers/mfd/tps65010.c +++ b/drivers/mfd/tps65010.c @@ -515,7 +515,7 @@ static int tps65010_gpio_get(struct gpio_chip *chip, unsigned offset) static struct tps65010 *the_tps; -static int __exit tps65010_remove(struct i2c_client *client) +static int tps65010_remove(struct i2c_client *client) { struct tps65010 *tps = i2c_get_clientdata(client); struct tps65010_board *board = dev_get_platdata(&client->dev); @@ -684,7 +684,7 @@ static struct i2c_driver tps65010_driver = { .name = "tps65010", }, .probe = tps65010_probe, - .remove = __exit_p(tps65010_remove), + .remove = tps65010_remove, .id_table = tps65010_id, }; diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c index 3935092460374e..f440aed613056a 100644 --- a/drivers/mfd/twl4030-power.c +++ b/drivers/mfd/twl4030-power.c @@ -829,7 +829,7 @@ static struct twl4030_power_data osc_off_idle = { .board_config = osc_off_rconfig, }; -static struct of_device_id twl4030_power_of_match[] = { +static const struct of_device_id twl4030_power_of_match[] = { { .compatible = "ti,twl4030-power", }, diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index f71ee3dbc2a24a..c5265c1262c50c 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -814,4 +814,3 @@ MODULE_DESCRIPTION("TWL6040 MFD"); MODULE_AUTHOR("Misael Lopez Cruz "); MODULE_AUTHOR("Jorge Eduardo Candelaria "); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:twl6040"); diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index 8f43ab8fd2d6fe..3e628df9280cb5 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -47,71 +47,26 @@ #define SYS_HBI_MASK 0xfff #define SYS_PROCIDx_HBI_SHIFT 0 -#define SYS_MCI_CARDIN (1 << 0) -#define SYS_MCI_WPROT (1 << 1) - #define SYS_MISC_MASTERSITE (1 << 14) - -static void __iomem *__vexpress_sysreg_base; - -static void __iomem *vexpress_sysreg_base(void) +void vexpress_flags_set(u32 data) { - if (!__vexpress_sysreg_base) { + static void __iomem *base; + + if (!base) { struct device_node *node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg"); - __vexpress_sysreg_base = of_iomap(node, 0); + base = of_iomap(node, 0); } - WARN_ON(!__vexpress_sysreg_base); - - return __vexpress_sysreg_base; -} - - -static int vexpress_sysreg_get_master(void) -{ - if (readl(vexpress_sysreg_base() + SYS_MISC) & SYS_MISC_MASTERSITE) - return VEXPRESS_SITE_DB2; - - return VEXPRESS_SITE_DB1; -} - -void vexpress_flags_set(u32 data) -{ - writel(~0, vexpress_sysreg_base() + SYS_FLAGSCLR); - writel(data, vexpress_sysreg_base() + SYS_FLAGSSET); -} - -unsigned int vexpress_get_mci_cardin(struct device *dev) -{ - return readl(vexpress_sysreg_base() + SYS_MCI) & SYS_MCI_CARDIN; -} - -u32 vexpress_get_procid(int site) -{ - if (site == VEXPRESS_SITE_MASTER) - site = vexpress_sysreg_get_master(); + if (WARN_ON(!base)) + return; - return readl(vexpress_sysreg_base() + (site == VEXPRESS_SITE_DB1 ? - SYS_PROCID0 : SYS_PROCID1)); + writel(~0, base + SYS_FLAGSCLR); + writel(data, base + SYS_FLAGSSET); } -void __iomem *vexpress_get_24mhz_clock_base(void) -{ - return vexpress_sysreg_base() + SYS_24MHZ; -} - - -void __init vexpress_sysreg_early_init(void __iomem *base) -{ - __vexpress_sysreg_base = base; - - vexpress_config_set_master(vexpress_sysreg_get_master()); -} - - /* The sysreg block is just a random collection of various functions... */ static struct syscon_platform_data vexpress_sysreg_sys_id_pdata = { @@ -210,6 +165,7 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) struct resource *mem; void __iomem *base; struct bgpio_chip *mmc_gpio_chip; + int master; u32 dt_hbi; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -220,11 +176,14 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) if (!base) return -ENOMEM; - vexpress_config_set_master(vexpress_sysreg_get_master()); + master = readl(base + SYS_MISC) & SYS_MISC_MASTERSITE ? + VEXPRESS_SITE_DB2 : VEXPRESS_SITE_DB1; + vexpress_config_set_master(master); /* Confirm board type against DT property, if available */ if (of_property_read_u32(of_root, "arm,hbi", &dt_hbi) == 0) { - u32 id = vexpress_get_procid(VEXPRESS_SITE_MASTER); + u32 id = readl(base + (master == VEXPRESS_SITE_DB1 ? + SYS_PROCID0 : SYS_PROCID1)); u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK; if (WARN_ON(dt_hbi != hbi)) diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index b326a82017ee7f..aeae6ec123b3ad 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -1172,9 +1172,6 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_DAC_DIGITAL_VOLUME_3L: case ARIZONA_DAC_VOLUME_LIMIT_3L: case ARIZONA_NOISE_GATE_SELECT_3L: - case ARIZONA_OUTPUT_PATH_CONFIG_3R: - case ARIZONA_DAC_DIGITAL_VOLUME_3R: - case ARIZONA_DAC_VOLUME_LIMIT_3R: case ARIZONA_OUTPUT_PATH_CONFIG_4L: case ARIZONA_DAC_DIGITAL_VOLUME_4L: case ARIZONA_OUT_VOLUME_4L: diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index c4cb9a984a5fb3..40ea639fa413a9 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 3c019c0e60eb85..47680c84801c76 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index bd3039ab8f98e6..af44ee26075d8b 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index c84131e2862565..f36c76f8b2321e 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1758,7 +1758,7 @@ static int mmc_runtime_suspend(struct mmc_host *host) err = _mmc_suspend(host, true); if (err) - pr_err("%s: error %d doing aggessive suspend\n", + pr_err("%s: error %d doing aggressive suspend\n", mmc_hostname(host), err); return err; @@ -1776,7 +1776,7 @@ static int mmc_runtime_resume(struct mmc_host *host) err = _mmc_resume(host); if (err) - pr_err("%s: error %d doing aggessive resume\n", + pr_err("%s: error %d doing aggressive resume\n", mmc_hostname(host), err); return 0; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index ad4d43eae99def..31a9ef256d0652 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1157,7 +1157,7 @@ static int mmc_sd_runtime_suspend(struct mmc_host *host) err = _mmc_sd_suspend(host); if (err) - pr_err("%s: error %d doing aggessive suspend\n", + pr_err("%s: error %d doing aggressive suspend\n", mmc_hostname(host), err); return err; @@ -1175,7 +1175,7 @@ static int mmc_sd_runtime_resume(struct mmc_host *host) err = _mmc_sd_resume(host); if (err) - pr_err("%s: error %d doing aggessive resume\n", + pr_err("%s: error %d doing aggressive resume\n", mmc_hostname(host), err); return 0; diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index 27f272ed502ac5..43fa16b5f5107c 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -1105,7 +1105,7 @@ int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip) mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip); reg = readl(r->gpmi_regs + HW_GPMI_STAT); } else - dev_err(this->dev, "unknow arch.\n"); + dev_err(this->dev, "unknown arch.\n"); return reg & mask; } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c index cfe3c7695455e1..4c9678c8e13931 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c @@ -592,7 +592,7 @@ int bnx2x_vf_mcast(struct bnx2x *bp, struct bnx2x_virtf *vf, mc = kzalloc(mc_num * sizeof(struct bnx2x_mcast_list_elem), GFP_KERNEL); if (!mc) { - BNX2X_ERR("Cannot Configure mulicasts due to lack of memory\n"); + BNX2X_ERR("Cannot Configure multicasts due to lack of memory\n"); return -ENOMEM; } } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index 3e0f705a43117d..75ee9e4ced51f5 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -818,7 +818,7 @@ int qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *adapter, if (rv) netdev_err(adapter->netdev, - "Failed to set Rx coalescing parametrs\n"); + "Failed to set Rx coalescing parameters\n"); return rv; } diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index e22e602beef342..4c2b5a80f17ce7 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -614,7 +614,7 @@ static void recalibrate(struct dp83640_clock *clock) trigger = CAL_TRIGGER; cal_gpio = 1 + ptp_find_pin(clock->ptp_clock, PTP_PF_PHYSYNC, 0); if (cal_gpio < 1) { - pr_err("PHY calibration pin not avaible - PHY is not calibrated."); + pr_err("PHY calibration pin not available - PHY is not calibrated."); return; } diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index e71a2ce7a4487a..b97367d5071747 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -3211,7 +3211,7 @@ static void airo_print_status(const char *devname, u16 status) airo_print_dbg(devname, "link lost (TSF sync lost)"); break; default: - airo_print_dbg(devname, "unknow status %x\n", status); + airo_print_dbg(devname, "unknown status %x\n", status); break; } break; @@ -3233,7 +3233,7 @@ static void airo_print_status(const char *devname, u16 status) case STAT_REASSOC: break; default: - airo_print_dbg(devname, "unknow status %x\n", status); + airo_print_dbg(devname, "unknown status %x\n", status); break; } } diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index d6d2f0f00caad1..4ce433f1c1fb5b 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4857,7 +4857,7 @@ static void ath10k_sta_rc_update(struct ieee80211_hw *hw, bw = WMI_PEER_CHWIDTH_80MHZ; break; case IEEE80211_STA_RX_BW_160: - ath10k_warn(ar, "Invalid bandwith %d in rc update for %pM\n", + ath10k_warn(ar, "Invalid bandwidth %d in rc update for %pM\n", sta->bandwidth, sta->addr); bw = WMI_PEER_CHWIDTH_20MHZ; break; diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 69ed39731902c0..dbd894428be60e 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -1701,7 +1701,7 @@ int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn, } else if (packet_type == WCN36XX_HAL_KEEP_ALIVE_UNSOLICIT_ARP_RSP) { /* TODO: it also support ARP response type */ } else { - wcn36xx_warn("unknow keep alive packet type %d\n", packet_type); + wcn36xx_warn("unknown keep alive packet type %d\n", packet_type); ret = -EINVAL; goto out; } diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h new file mode 100644 index 00000000000000..a0da3248b942fd --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2011 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__TRACE_BRCMSMAC_H) || defined(TRACE_HEADER_MULTI_READ) +#define __TRACE_BRCMSMAC_H + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM brcmsmac + +/* + * We define a tracepoint, its arguments, its printk format and its + * 'fast binary record' layout. + */ +TRACE_EVENT(brcms_timer, + /* TPPROTO is the prototype of the function called by this tracepoint */ + TP_PROTO(struct brcms_timer *t), + /* + * TPARGS(firstarg, p) are the parameters names, same as found in the + * prototype. + */ + TP_ARGS(t), + /* + * Fast binary tracing: define the trace record via TP_STRUCT__entry(). + * You can think about it like a regular C structure local variable + * definition. + */ + TP_STRUCT__entry( + __field(uint, ms) + __field(uint, set) + __field(uint, periodic) + ), + TP_fast_assign( + __entry->ms = t->ms; + __entry->set = t->set; + __entry->periodic = t->periodic; + ), + TP_printk( + "ms=%u set=%u periodic=%u", + __entry->ms, __entry->set, __entry->periodic + ) +); + +TRACE_EVENT(brcms_dpc, + TP_PROTO(unsigned long data), + TP_ARGS(data), + TP_STRUCT__entry( + __field(unsigned long, data) + ), + TP_fast_assign( + __entry->data = data; + ), + TP_printk( + "data=%p", + (void *)__entry->data + ) +); + +TRACE_EVENT(brcms_macintstatus, + TP_PROTO(const struct device *dev, int in_isr, u32 macintstatus, + u32 mask), + TP_ARGS(dev, in_isr, macintstatus, mask), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(int, in_isr) + __field(u32, macintstatus) + __field(u32, mask) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)); + __entry->in_isr = in_isr; + __entry->macintstatus = macintstatus; + __entry->mask = mask; + ), + TP_printk("[%s] in_isr=%d macintstatus=%#x mask=%#x", __get_str(dev), + __entry->in_isr, __entry->macintstatus, __entry->mask) +); +#endif /* __TRACE_BRCMSMAC_H */ + +#ifdef CONFIG_BRCM_TRACING + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE brcms_trace_brcmsmac +#include + +#endif /* CONFIG_BRCM_TRACING */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h new file mode 100644 index 00000000000000..0e8a69ab909f2d --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_msg.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2011 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__TRACE_BRCMSMAC_MSG_H) || defined(TRACE_HEADER_MULTI_READ) +#define __TRACE_BRCMSMAC_MSG_H + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM brcmsmac_msg + +#define MAX_MSG_LEN 100 + +DECLARE_EVENT_CLASS(brcms_msg_event, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf), + TP_STRUCT__entry( + __dynamic_array(char, msg, MAX_MSG_LEN) + ), + TP_fast_assign( + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + MAX_MSG_LEN, vaf->fmt, + *vaf->va) >= MAX_MSG_LEN); + ), + TP_printk("%s", __get_str(msg)) +); + +DEFINE_EVENT(brcms_msg_event, brcms_info, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(brcms_msg_event, brcms_warn, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(brcms_msg_event, brcms_err, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(brcms_msg_event, brcms_crit, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +TRACE_EVENT(brcms_dbg, + TP_PROTO(u32 level, const char *func, struct va_format *vaf), + TP_ARGS(level, func, vaf), + TP_STRUCT__entry( + __field(u32, level) + __string(func, func) + __dynamic_array(char, msg, MAX_MSG_LEN) + ), + TP_fast_assign( + __entry->level = level; + __assign_str(func, func); + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + MAX_MSG_LEN, vaf->fmt, + *vaf->va) >= MAX_MSG_LEN); + ), + TP_printk("%s: %s", __get_str(func), __get_str(msg)) +); +#endif /* __TRACE_BRCMSMAC_MSG_H */ + +#ifdef CONFIG_BRCM_TRACING + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE brcms_trace_brcmsmac_msg +#include + +#endif /* CONFIG_BRCM_TRACING */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h new file mode 100644 index 00000000000000..cf2cc070f1e5c7 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_brcmsmac_tx.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2011 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(__TRACE_BRCMSMAC_TX_H) || defined(TRACE_HEADER_MULTI_READ) +#define __TRACE_BRCMSMAC_TX_H + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM brcmsmac_tx + +TRACE_EVENT(brcms_txdesc, + TP_PROTO(const struct device *dev, + void *txh, size_t txh_len), + TP_ARGS(dev, txh, txh_len), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __dynamic_array(u8, txh, txh_len) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)); + memcpy(__get_dynamic_array(txh), txh, txh_len); + ), + TP_printk("[%s] txdesc", __get_str(dev)) +); + +TRACE_EVENT(brcms_txstatus, + TP_PROTO(const struct device *dev, u16 framelen, u16 frameid, + u16 status, u16 lasttxtime, u16 sequence, u16 phyerr, + u16 ackphyrxsh), + TP_ARGS(dev, framelen, frameid, status, lasttxtime, sequence, phyerr, + ackphyrxsh), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(u16, framelen) + __field(u16, frameid) + __field(u16, status) + __field(u16, lasttxtime) + __field(u16, sequence) + __field(u16, phyerr) + __field(u16, ackphyrxsh) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)); + __entry->framelen = framelen; + __entry->frameid = frameid; + __entry->status = status; + __entry->lasttxtime = lasttxtime; + __entry->sequence = sequence; + __entry->phyerr = phyerr; + __entry->ackphyrxsh = ackphyrxsh; + ), + TP_printk("[%s] FrameId %#04x TxStatus %#04x LastTxTime %#04x " + "Seq %#04x PHYTxStatus %#04x RxAck %#04x", + __get_str(dev), __entry->frameid, __entry->status, + __entry->lasttxtime, __entry->sequence, __entry->phyerr, + __entry->ackphyrxsh) +); + +TRACE_EVENT(brcms_ampdu_session, + TP_PROTO(const struct device *dev, unsigned max_ampdu_len, + u16 max_ampdu_frames, u16 ampdu_len, u16 ampdu_frames, + u16 dma_len), + TP_ARGS(dev, max_ampdu_len, max_ampdu_frames, ampdu_len, ampdu_frames, + dma_len), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(unsigned, max_ampdu_len) + __field(u16, max_ampdu_frames) + __field(u16, ampdu_len) + __field(u16, ampdu_frames) + __field(u16, dma_len) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)); + __entry->max_ampdu_len = max_ampdu_len; + __entry->max_ampdu_frames = max_ampdu_frames; + __entry->ampdu_len = ampdu_len; + __entry->ampdu_frames = ampdu_frames; + __entry->dma_len = dma_len; + ), + TP_printk("[%s] ampdu session max_len=%u max_frames=%u len=%u frames=%u dma_len=%u", + __get_str(dev), __entry->max_ampdu_len, + __entry->max_ampdu_frames, __entry->ampdu_len, + __entry->ampdu_frames, __entry->dma_len) +); +#endif /* __TRACE_BRCMSMAC_TX_H */ + +#ifdef CONFIG_BRCM_TRACING + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE brcms_trace_brcmsmac_tx +#include + +#endif /* CONFIG_BRCM_TRACING */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h index 871781e6a7138a..cbf2f06436fcfe 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h @@ -14,9 +14,8 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#if !defined(__TRACE_BRCMSMAC_H) || defined(TRACE_HEADER_MULTI_READ) - -#define __TRACE_BRCMSMAC_H +#ifndef __BRCMS_TRACE_EVENTS_H +#define __BRCMS_TRACE_EVENTS_H #include #include @@ -34,222 +33,8 @@ static inline void trace_ ## name(proto) {} static inline void trace_ ## name(proto) {} #endif -#undef TRACE_SYSTEM -#define TRACE_SYSTEM brcmsmac - -/* - * We define a tracepoint, its arguments, its printk format and its - * 'fast binary record' layout. - */ -TRACE_EVENT(brcms_timer, - /* TPPROTO is the prototype of the function called by this tracepoint */ - TP_PROTO(struct brcms_timer *t), - /* - * TPARGS(firstarg, p) are the parameters names, same as found in the - * prototype. - */ - TP_ARGS(t), - /* - * Fast binary tracing: define the trace record via TP_STRUCT__entry(). - * You can think about it like a regular C structure local variable - * definition. - */ - TP_STRUCT__entry( - __field(uint, ms) - __field(uint, set) - __field(uint, periodic) - ), - TP_fast_assign( - __entry->ms = t->ms; - __entry->set = t->set; - __entry->periodic = t->periodic; - ), - TP_printk( - "ms=%u set=%u periodic=%u", - __entry->ms, __entry->set, __entry->periodic - ) -); - -TRACE_EVENT(brcms_dpc, - TP_PROTO(unsigned long data), - TP_ARGS(data), - TP_STRUCT__entry( - __field(unsigned long, data) - ), - TP_fast_assign( - __entry->data = data; - ), - TP_printk( - "data=%p", - (void *)__entry->data - ) -); - -TRACE_EVENT(brcms_macintstatus, - TP_PROTO(const struct device *dev, int in_isr, u32 macintstatus, - u32 mask), - TP_ARGS(dev, in_isr, macintstatus, mask), - TP_STRUCT__entry( - __string(dev, dev_name(dev)) - __field(int, in_isr) - __field(u32, macintstatus) - __field(u32, mask) - ), - TP_fast_assign( - __assign_str(dev, dev_name(dev)); - __entry->in_isr = in_isr; - __entry->macintstatus = macintstatus; - __entry->mask = mask; - ), - TP_printk("[%s] in_isr=%d macintstatus=%#x mask=%#x", __get_str(dev), - __entry->in_isr, __entry->macintstatus, __entry->mask) -); - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM brcmsmac_tx - -TRACE_EVENT(brcms_txdesc, - TP_PROTO(const struct device *dev, - void *txh, size_t txh_len), - TP_ARGS(dev, txh, txh_len), - TP_STRUCT__entry( - __string(dev, dev_name(dev)) - __dynamic_array(u8, txh, txh_len) - ), - TP_fast_assign( - __assign_str(dev, dev_name(dev)); - memcpy(__get_dynamic_array(txh), txh, txh_len); - ), - TP_printk("[%s] txdesc", __get_str(dev)) -); - -TRACE_EVENT(brcms_txstatus, - TP_PROTO(const struct device *dev, u16 framelen, u16 frameid, - u16 status, u16 lasttxtime, u16 sequence, u16 phyerr, - u16 ackphyrxsh), - TP_ARGS(dev, framelen, frameid, status, lasttxtime, sequence, phyerr, - ackphyrxsh), - TP_STRUCT__entry( - __string(dev, dev_name(dev)) - __field(u16, framelen) - __field(u16, frameid) - __field(u16, status) - __field(u16, lasttxtime) - __field(u16, sequence) - __field(u16, phyerr) - __field(u16, ackphyrxsh) - ), - TP_fast_assign( - __assign_str(dev, dev_name(dev)); - __entry->framelen = framelen; - __entry->frameid = frameid; - __entry->status = status; - __entry->lasttxtime = lasttxtime; - __entry->sequence = sequence; - __entry->phyerr = phyerr; - __entry->ackphyrxsh = ackphyrxsh; - ), - TP_printk("[%s] FrameId %#04x TxStatus %#04x LastTxTime %#04x " - "Seq %#04x PHYTxStatus %#04x RxAck %#04x", - __get_str(dev), __entry->frameid, __entry->status, - __entry->lasttxtime, __entry->sequence, __entry->phyerr, - __entry->ackphyrxsh) -); - -TRACE_EVENT(brcms_ampdu_session, - TP_PROTO(const struct device *dev, unsigned max_ampdu_len, - u16 max_ampdu_frames, u16 ampdu_len, u16 ampdu_frames, - u16 dma_len), - TP_ARGS(dev, max_ampdu_len, max_ampdu_frames, ampdu_len, ampdu_frames, - dma_len), - TP_STRUCT__entry( - __string(dev, dev_name(dev)) - __field(unsigned, max_ampdu_len) - __field(u16, max_ampdu_frames) - __field(u16, ampdu_len) - __field(u16, ampdu_frames) - __field(u16, dma_len) - ), - TP_fast_assign( - __assign_str(dev, dev_name(dev)); - __entry->max_ampdu_len = max_ampdu_len; - __entry->max_ampdu_frames = max_ampdu_frames; - __entry->ampdu_len = ampdu_len; - __entry->ampdu_frames = ampdu_frames; - __entry->dma_len = dma_len; - ), - TP_printk("[%s] ampdu session max_len=%u max_frames=%u len=%u frames=%u dma_len=%u", - __get_str(dev), __entry->max_ampdu_len, - __entry->max_ampdu_frames, __entry->ampdu_len, - __entry->ampdu_frames, __entry->dma_len) -); - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM brcmsmac_msg - -#define MAX_MSG_LEN 100 - -DECLARE_EVENT_CLASS(brcms_msg_event, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf), - TP_STRUCT__entry( - __dynamic_array(char, msg, MAX_MSG_LEN) - ), - TP_fast_assign( - WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), - MAX_MSG_LEN, vaf->fmt, - *vaf->va) >= MAX_MSG_LEN); - ), - TP_printk("%s", __get_str(msg)) -); - -DEFINE_EVENT(brcms_msg_event, brcms_info, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -DEFINE_EVENT(brcms_msg_event, brcms_warn, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -DEFINE_EVENT(brcms_msg_event, brcms_err, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -DEFINE_EVENT(brcms_msg_event, brcms_crit, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -TRACE_EVENT(brcms_dbg, - TP_PROTO(u32 level, const char *func, struct va_format *vaf), - TP_ARGS(level, func, vaf), - TP_STRUCT__entry( - __field(u32, level) - __string(func, func) - __dynamic_array(char, msg, MAX_MSG_LEN) - ), - TP_fast_assign( - __entry->level = level; - __assign_str(func, func); - WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), - MAX_MSG_LEN, vaf->fmt, - *vaf->va) >= MAX_MSG_LEN); - ), - TP_printk("%s: %s", __get_str(func), __get_str(msg)) -); +#include "brcms_trace_brcmsmac.h" +#include "brcms_trace_brcmsmac_tx.h" +#include "brcms_trace_brcmsmac_msg.h" #endif /* __TRACE_BRCMSMAC_H */ - -#ifdef CONFIG_BRCM_TRACING - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_FILE brcms_trace_events - -#include - -#endif /* CONFIG_BRCM_TRACING */ diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h new file mode 100644 index 00000000000000..04e6649340b8c2 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace-data.h @@ -0,0 +1,79 @@ +/****************************************************************************** + * + * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#if !defined(__IWLWIFI_DEVICE_TRACE_DATA) || defined(TRACE_HEADER_MULTI_READ) +#define __IWLWIFI_DEVICE_TRACE_DATA + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi_data + +TRACE_EVENT(iwlwifi_dev_tx_data, + TP_PROTO(const struct device *dev, + struct sk_buff *skb, + void *data, size_t data_len), + TP_ARGS(dev, skb, data, data_len), + TP_STRUCT__entry( + DEV_ENTRY + + __dynamic_array(u8, data, iwl_trace_data(skb) ? data_len : 0) + ), + TP_fast_assign( + DEV_ASSIGN; + if (iwl_trace_data(skb)) + memcpy(__get_dynamic_array(data), data, data_len); + ), + TP_printk("[%s] TX frame data", __get_str(dev)) +); + +TRACE_EVENT(iwlwifi_dev_rx_data, + TP_PROTO(const struct device *dev, + const struct iwl_trans *trans, + void *rxbuf, size_t len), + TP_ARGS(dev, trans, rxbuf, len), + TP_STRUCT__entry( + DEV_ENTRY + + __dynamic_array(u8, data, + len - iwl_rx_trace_len(trans, rxbuf, len)) + ), + TP_fast_assign( + size_t offs = iwl_rx_trace_len(trans, rxbuf, len); + DEV_ASSIGN; + if (offs < len) + memcpy(__get_dynamic_array(data), + ((u8 *)rxbuf) + offs, len - offs); + ), + TP_printk("[%s] RX frame data", __get_str(dev)) +); +#endif /* __IWLWIFI_DEVICE_TRACE_DATA */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE iwl-devtrace-data +#include diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-io.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-io.h new file mode 100644 index 00000000000000..f62c54485852d6 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace-io.h @@ -0,0 +1,155 @@ +/****************************************************************************** + * + * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#if !defined(__IWLWIFI_DEVICE_TRACE_IO) || defined(TRACE_HEADER_MULTI_READ) +#define __IWLWIFI_DEVICE_TRACE_IO + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi_io + +TRACE_EVENT(iwlwifi_dev_ioread32, + TP_PROTO(const struct device *dev, u32 offs, u32 val), + TP_ARGS(dev, offs, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] read io[%#x] = %#x", + __get_str(dev), __entry->offs, __entry->val) +); + +TRACE_EVENT(iwlwifi_dev_iowrite8, + TP_PROTO(const struct device *dev, u32 offs, u8 val), + TP_ARGS(dev, offs, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, offs) + __field(u8, val) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] write io[%#x] = %#x)", + __get_str(dev), __entry->offs, __entry->val) +); + +TRACE_EVENT(iwlwifi_dev_iowrite32, + TP_PROTO(const struct device *dev, u32 offs, u32 val), + TP_ARGS(dev, offs, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] write io[%#x] = %#x)", + __get_str(dev), __entry->offs, __entry->val) +); + +TRACE_EVENT(iwlwifi_dev_iowrite_prph32, + TP_PROTO(const struct device *dev, u32 offs, u32 val), + TP_ARGS(dev, offs, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] write PRPH[%#x] = %#x)", + __get_str(dev), __entry->offs, __entry->val) +); + +TRACE_EVENT(iwlwifi_dev_ioread_prph32, + TP_PROTO(const struct device *dev, u32 offs, u32 val), + TP_ARGS(dev, offs, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%s] read PRPH[%#x] = %#x", + __get_str(dev), __entry->offs, __entry->val) +); + +TRACE_EVENT(iwlwifi_dev_irq, + TP_PROTO(const struct device *dev), + TP_ARGS(dev), + TP_STRUCT__entry( + DEV_ENTRY + ), + TP_fast_assign( + DEV_ASSIGN; + ), + /* TP_printk("") doesn't compile */ + TP_printk("%d", 0) +); + +TRACE_EVENT(iwlwifi_dev_ict_read, + TP_PROTO(const struct device *dev, u32 index, u32 value), + TP_ARGS(dev, index, value), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, index) + __field(u32, value) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->index = index; + __entry->value = value; + ), + TP_printk("[%s] read ict[%d] = %#.8x", + __get_str(dev), __entry->index, __entry->value) +); +#endif /* __IWLWIFI_DEVICE_TRACE_IO */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE iwl-devtrace-io +#include diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h new file mode 100644 index 00000000000000..6cb66a988271ed --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace-iwlwifi.h @@ -0,0 +1,200 @@ +/****************************************************************************** + * + * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#if !defined(__IWLWIFI_DEVICE_TRACE_IWLWIFI) || defined(TRACE_HEADER_MULTI_READ) +#define __IWLWIFI_DEVICE_TRACE_IWLWIFI + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi + +TRACE_EVENT(iwlwifi_dev_hcmd, + TP_PROTO(const struct device *dev, + struct iwl_host_cmd *cmd, u16 total_size, + struct iwl_cmd_header *hdr), + TP_ARGS(dev, cmd, total_size, hdr), + TP_STRUCT__entry( + DEV_ENTRY + __dynamic_array(u8, hcmd, total_size) + __field(u32, flags) + ), + TP_fast_assign( + int i, offset = sizeof(*hdr); + + DEV_ASSIGN; + __entry->flags = cmd->flags; + memcpy(__get_dynamic_array(hcmd), hdr, sizeof(*hdr)); + + for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { + if (!cmd->len[i]) + continue; + memcpy((u8 *)__get_dynamic_array(hcmd) + offset, + cmd->data[i], cmd->len[i]); + offset += cmd->len[i]; + } + ), + TP_printk("[%s] hcmd %#.2x (%ssync)", + __get_str(dev), ((u8 *)__get_dynamic_array(hcmd))[0], + __entry->flags & CMD_ASYNC ? "a" : "") +); + +TRACE_EVENT(iwlwifi_dev_rx, + TP_PROTO(const struct device *dev, const struct iwl_trans *trans, + void *rxbuf, size_t len), + TP_ARGS(dev, trans, rxbuf, len), + TP_STRUCT__entry( + DEV_ENTRY + __dynamic_array(u8, rxbuf, iwl_rx_trace_len(trans, rxbuf, len)) + ), + TP_fast_assign( + DEV_ASSIGN; + memcpy(__get_dynamic_array(rxbuf), rxbuf, + iwl_rx_trace_len(trans, rxbuf, len)); + ), + TP_printk("[%s] RX cmd %#.2x", + __get_str(dev), ((u8 *)__get_dynamic_array(rxbuf))[4]) +); + +TRACE_EVENT(iwlwifi_dev_tx, + TP_PROTO(const struct device *dev, struct sk_buff *skb, + void *tfd, size_t tfdlen, + void *buf0, size_t buf0_len, + void *buf1, size_t buf1_len), + TP_ARGS(dev, skb, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len), + TP_STRUCT__entry( + DEV_ENTRY + + __field(size_t, framelen) + __dynamic_array(u8, tfd, tfdlen) + + /* + * Do not insert between or below these items, + * we want to keep the frame together (except + * for the possible padding). + */ + __dynamic_array(u8, buf0, buf0_len) + __dynamic_array(u8, buf1, iwl_trace_data(skb) ? 0 : buf1_len) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->framelen = buf0_len + buf1_len; + memcpy(__get_dynamic_array(tfd), tfd, tfdlen); + memcpy(__get_dynamic_array(buf0), buf0, buf0_len); + if (!iwl_trace_data(skb)) + memcpy(__get_dynamic_array(buf1), buf1, buf1_len); + ), + TP_printk("[%s] TX %.2x (%zu bytes)", + __get_str(dev), ((u8 *)__get_dynamic_array(buf0))[0], + __entry->framelen) +); + +TRACE_EVENT(iwlwifi_dev_ucode_error, + TP_PROTO(const struct device *dev, u32 desc, u32 tsf_low, + u32 data1, u32 data2, u32 line, u32 blink1, + u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time, + u32 gp1, u32 gp2, u32 gp3, u32 ucode_ver, u32 hw_ver, + u32 brd_ver), + TP_ARGS(dev, desc, tsf_low, data1, data2, line, + blink1, blink2, ilink1, ilink2, bcon_time, gp1, gp2, + gp3, ucode_ver, hw_ver, brd_ver), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, desc) + __field(u32, tsf_low) + __field(u32, data1) + __field(u32, data2) + __field(u32, line) + __field(u32, blink1) + __field(u32, blink2) + __field(u32, ilink1) + __field(u32, ilink2) + __field(u32, bcon_time) + __field(u32, gp1) + __field(u32, gp2) + __field(u32, gp3) + __field(u32, ucode_ver) + __field(u32, hw_ver) + __field(u32, brd_ver) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->desc = desc; + __entry->tsf_low = tsf_low; + __entry->data1 = data1; + __entry->data2 = data2; + __entry->line = line; + __entry->blink1 = blink1; + __entry->blink2 = blink2; + __entry->ilink1 = ilink1; + __entry->ilink2 = ilink2; + __entry->bcon_time = bcon_time; + __entry->gp1 = gp1; + __entry->gp2 = gp2; + __entry->gp3 = gp3; + __entry->ucode_ver = ucode_ver; + __entry->hw_ver = hw_ver; + __entry->brd_ver = brd_ver; + ), + TP_printk("[%s] #%02d %010u data 0x%08X 0x%08X line %u, " + "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X " + "bcon_tm %010u gp 0x%08X 0x%08X 0x%08X uCode 0x%08X " + "hw 0x%08X brd 0x%08X", + __get_str(dev), __entry->desc, __entry->tsf_low, + __entry->data1, + __entry->data2, __entry->line, __entry->blink1, + __entry->blink2, __entry->ilink1, __entry->ilink2, + __entry->bcon_time, __entry->gp1, __entry->gp2, + __entry->gp3, __entry->ucode_ver, __entry->hw_ver, + __entry->brd_ver) +); + +TRACE_EVENT(iwlwifi_dev_ucode_event, + TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev), + TP_ARGS(dev, time, data, ev), + TP_STRUCT__entry( + DEV_ENTRY + + __field(u32, time) + __field(u32, data) + __field(u32, ev) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->time = time; + __entry->data = data; + __entry->ev = ev; + ), + TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u", + __get_str(dev), __entry->time, __entry->data, __entry->ev) +); +#endif /* __IWLWIFI_DEVICE_TRACE_IWLWIFI */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE iwl-devtrace-iwlwifi +#include diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h new file mode 100644 index 00000000000000..a3b3c2465f891a --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace-msg.h @@ -0,0 +1,97 @@ +/****************************************************************************** + * + * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#if !defined(__IWLWIFI_DEVICE_TRACE_MSG) || defined(TRACE_HEADER_MULTI_READ) +#define __IWLWIFI_DEVICE_TRACE_MSG + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi_msg + +#define MAX_MSG_LEN 110 + +DECLARE_EVENT_CLASS(iwlwifi_msg_event, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf), + TP_STRUCT__entry( + __dynamic_array(char, msg, MAX_MSG_LEN) + ), + TP_fast_assign( + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + MAX_MSG_LEN, vaf->fmt, + *vaf->va) >= MAX_MSG_LEN); + ), + TP_printk("%s", __get_str(msg)) +); + +DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_err, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_warn, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_info, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_crit, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +TRACE_EVENT(iwlwifi_dbg, + TP_PROTO(u32 level, bool in_interrupt, const char *function, + struct va_format *vaf), + TP_ARGS(level, in_interrupt, function, vaf), + TP_STRUCT__entry( + __field(u32, level) + __field(u8, in_interrupt) + __string(function, function) + __dynamic_array(char, msg, MAX_MSG_LEN) + ), + TP_fast_assign( + __entry->level = level; + __entry->in_interrupt = in_interrupt; + __assign_str(function, function); + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + MAX_MSG_LEN, vaf->fmt, + *vaf->va) >= MAX_MSG_LEN); + ), + TP_printk("%s", __get_str(msg)) +); +#endif /* __IWLWIFI_DEVICE_TRACE_MSG */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE iwl-devtrace-msg +#include diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h b/drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h new file mode 100644 index 00000000000000..10839fae9cd92d --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace-ucode.h @@ -0,0 +1,81 @@ +/****************************************************************************** + * + * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#if !defined(__IWLWIFI_DEVICE_TRACE_UCODE) || defined(TRACE_HEADER_MULTI_READ) +#define __IWLWIFI_DEVICE_TRACE_UCODE + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi_ucode + +TRACE_EVENT(iwlwifi_dev_ucode_cont_event, + TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev), + TP_ARGS(dev, time, data, ev), + TP_STRUCT__entry( + DEV_ENTRY + + __field(u32, time) + __field(u32, data) + __field(u32, ev) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->time = time; + __entry->data = data; + __entry->ev = ev; + ), + TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u", + __get_str(dev), __entry->time, __entry->data, __entry->ev) +); + +TRACE_EVENT(iwlwifi_dev_ucode_wrap_event, + TP_PROTO(const struct device *dev, u32 wraps, u32 n_entry, u32 p_entry), + TP_ARGS(dev, wraps, n_entry, p_entry), + TP_STRUCT__entry( + DEV_ENTRY + + __field(u32, wraps) + __field(u32, n_entry) + __field(u32, p_entry) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->wraps = wraps; + __entry->n_entry = n_entry; + __entry->p_entry = p_entry; + ), + TP_printk("[%s] wraps=#%02d n=0x%X p=0x%X", + __get_str(dev), __entry->wraps, __entry->n_entry, + __entry->p_entry) +); +#endif /* __IWLWIFI_DEVICE_TRACE_UCODE */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE iwl-devtrace-ucode +#include diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index 78bd41bf34b0f0..b87acd6a229b8b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -24,7 +24,7 @@ * *****************************************************************************/ -#if !defined(__IWLWIFI_DEVICE_TRACE) || defined(TRACE_HEADER_MULTI_READ) +#ifndef __IWLWIFI_DEVICE_TRACE #include #include #include @@ -80,436 +80,10 @@ static inline void trace_ ## name(proto) {} #define DEV_ENTRY __string(dev, dev_name(dev)) #define DEV_ASSIGN __assign_str(dev, dev_name(dev)) -#undef TRACE_SYSTEM -#define TRACE_SYSTEM iwlwifi_io +#include "iwl-devtrace-io.h" +#include "iwl-devtrace-ucode.h" +#include "iwl-devtrace-msg.h" +#include "iwl-devtrace-data.h" +#include "iwl-devtrace-iwlwifi.h" -TRACE_EVENT(iwlwifi_dev_ioread32, - TP_PROTO(const struct device *dev, u32 offs, u32 val), - TP_ARGS(dev, offs, val), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, offs) - __field(u32, val) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->offs = offs; - __entry->val = val; - ), - TP_printk("[%s] read io[%#x] = %#x", - __get_str(dev), __entry->offs, __entry->val) -); - -TRACE_EVENT(iwlwifi_dev_iowrite8, - TP_PROTO(const struct device *dev, u32 offs, u8 val), - TP_ARGS(dev, offs, val), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, offs) - __field(u8, val) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->offs = offs; - __entry->val = val; - ), - TP_printk("[%s] write io[%#x] = %#x)", - __get_str(dev), __entry->offs, __entry->val) -); - -TRACE_EVENT(iwlwifi_dev_iowrite32, - TP_PROTO(const struct device *dev, u32 offs, u32 val), - TP_ARGS(dev, offs, val), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, offs) - __field(u32, val) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->offs = offs; - __entry->val = val; - ), - TP_printk("[%s] write io[%#x] = %#x)", - __get_str(dev), __entry->offs, __entry->val) -); - -TRACE_EVENT(iwlwifi_dev_iowrite_prph32, - TP_PROTO(const struct device *dev, u32 offs, u32 val), - TP_ARGS(dev, offs, val), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, offs) - __field(u32, val) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->offs = offs; - __entry->val = val; - ), - TP_printk("[%s] write PRPH[%#x] = %#x)", - __get_str(dev), __entry->offs, __entry->val) -); - -TRACE_EVENT(iwlwifi_dev_ioread_prph32, - TP_PROTO(const struct device *dev, u32 offs, u32 val), - TP_ARGS(dev, offs, val), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, offs) - __field(u32, val) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->offs = offs; - __entry->val = val; - ), - TP_printk("[%s] read PRPH[%#x] = %#x", - __get_str(dev), __entry->offs, __entry->val) -); - -TRACE_EVENT(iwlwifi_dev_irq, - TP_PROTO(const struct device *dev), - TP_ARGS(dev), - TP_STRUCT__entry( - DEV_ENTRY - ), - TP_fast_assign( - DEV_ASSIGN; - ), - /* TP_printk("") doesn't compile */ - TP_printk("%d", 0) -); - -TRACE_EVENT(iwlwifi_dev_ict_read, - TP_PROTO(const struct device *dev, u32 index, u32 value), - TP_ARGS(dev, index, value), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, index) - __field(u32, value) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->index = index; - __entry->value = value; - ), - TP_printk("[%s] read ict[%d] = %#.8x", - __get_str(dev), __entry->index, __entry->value) -); - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM iwlwifi_ucode - -TRACE_EVENT(iwlwifi_dev_ucode_cont_event, - TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev), - TP_ARGS(dev, time, data, ev), - TP_STRUCT__entry( - DEV_ENTRY - - __field(u32, time) - __field(u32, data) - __field(u32, ev) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->time = time; - __entry->data = data; - __entry->ev = ev; - ), - TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u", - __get_str(dev), __entry->time, __entry->data, __entry->ev) -); - -TRACE_EVENT(iwlwifi_dev_ucode_wrap_event, - TP_PROTO(const struct device *dev, u32 wraps, u32 n_entry, u32 p_entry), - TP_ARGS(dev, wraps, n_entry, p_entry), - TP_STRUCT__entry( - DEV_ENTRY - - __field(u32, wraps) - __field(u32, n_entry) - __field(u32, p_entry) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->wraps = wraps; - __entry->n_entry = n_entry; - __entry->p_entry = p_entry; - ), - TP_printk("[%s] wraps=#%02d n=0x%X p=0x%X", - __get_str(dev), __entry->wraps, __entry->n_entry, - __entry->p_entry) -); - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM iwlwifi_msg - -#define MAX_MSG_LEN 110 - -DECLARE_EVENT_CLASS(iwlwifi_msg_event, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf), - TP_STRUCT__entry( - __dynamic_array(char, msg, MAX_MSG_LEN) - ), - TP_fast_assign( - WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), - MAX_MSG_LEN, vaf->fmt, - *vaf->va) >= MAX_MSG_LEN); - ), - TP_printk("%s", __get_str(msg)) -); - -DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_err, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_warn, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_info, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_crit, - TP_PROTO(struct va_format *vaf), - TP_ARGS(vaf) -); - -TRACE_EVENT(iwlwifi_dbg, - TP_PROTO(u32 level, bool in_interrupt, const char *function, - struct va_format *vaf), - TP_ARGS(level, in_interrupt, function, vaf), - TP_STRUCT__entry( - __field(u32, level) - __field(u8, in_interrupt) - __string(function, function) - __dynamic_array(char, msg, MAX_MSG_LEN) - ), - TP_fast_assign( - __entry->level = level; - __entry->in_interrupt = in_interrupt; - __assign_str(function, function); - WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), - MAX_MSG_LEN, vaf->fmt, - *vaf->va) >= MAX_MSG_LEN); - ), - TP_printk("%s", __get_str(msg)) -); - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM iwlwifi_data - -TRACE_EVENT(iwlwifi_dev_tx_data, - TP_PROTO(const struct device *dev, - struct sk_buff *skb, - void *data, size_t data_len), - TP_ARGS(dev, skb, data, data_len), - TP_STRUCT__entry( - DEV_ENTRY - - __dynamic_array(u8, data, iwl_trace_data(skb) ? data_len : 0) - ), - TP_fast_assign( - DEV_ASSIGN; - if (iwl_trace_data(skb)) - memcpy(__get_dynamic_array(data), data, data_len); - ), - TP_printk("[%s] TX frame data", __get_str(dev)) -); - -TRACE_EVENT(iwlwifi_dev_rx_data, - TP_PROTO(const struct device *dev, - const struct iwl_trans *trans, - void *rxbuf, size_t len), - TP_ARGS(dev, trans, rxbuf, len), - TP_STRUCT__entry( - DEV_ENTRY - - __dynamic_array(u8, data, - len - iwl_rx_trace_len(trans, rxbuf, len)) - ), - TP_fast_assign( - size_t offs = iwl_rx_trace_len(trans, rxbuf, len); - DEV_ASSIGN; - if (offs < len) - memcpy(__get_dynamic_array(data), - ((u8 *)rxbuf) + offs, len - offs); - ), - TP_printk("[%s] RX frame data", __get_str(dev)) -); - -#undef TRACE_SYSTEM -#define TRACE_SYSTEM iwlwifi - -TRACE_EVENT(iwlwifi_dev_hcmd, - TP_PROTO(const struct device *dev, - struct iwl_host_cmd *cmd, u16 total_size, - struct iwl_cmd_header *hdr), - TP_ARGS(dev, cmd, total_size, hdr), - TP_STRUCT__entry( - DEV_ENTRY - __dynamic_array(u8, hcmd, total_size) - __field(u32, flags) - ), - TP_fast_assign( - int i, offset = sizeof(*hdr); - - DEV_ASSIGN; - __entry->flags = cmd->flags; - memcpy(__get_dynamic_array(hcmd), hdr, sizeof(*hdr)); - - for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { - if (!cmd->len[i]) - continue; - memcpy((u8 *)__get_dynamic_array(hcmd) + offset, - cmd->data[i], cmd->len[i]); - offset += cmd->len[i]; - } - ), - TP_printk("[%s] hcmd %#.2x (%ssync)", - __get_str(dev), ((u8 *)__get_dynamic_array(hcmd))[0], - __entry->flags & CMD_ASYNC ? "a" : "") -); - -TRACE_EVENT(iwlwifi_dev_rx, - TP_PROTO(const struct device *dev, const struct iwl_trans *trans, - void *rxbuf, size_t len), - TP_ARGS(dev, trans, rxbuf, len), - TP_STRUCT__entry( - DEV_ENTRY - __dynamic_array(u8, rxbuf, iwl_rx_trace_len(trans, rxbuf, len)) - ), - TP_fast_assign( - DEV_ASSIGN; - memcpy(__get_dynamic_array(rxbuf), rxbuf, - iwl_rx_trace_len(trans, rxbuf, len)); - ), - TP_printk("[%s] RX cmd %#.2x", - __get_str(dev), ((u8 *)__get_dynamic_array(rxbuf))[4]) -); - -TRACE_EVENT(iwlwifi_dev_tx, - TP_PROTO(const struct device *dev, struct sk_buff *skb, - void *tfd, size_t tfdlen, - void *buf0, size_t buf0_len, - void *buf1, size_t buf1_len), - TP_ARGS(dev, skb, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len), - TP_STRUCT__entry( - DEV_ENTRY - - __field(size_t, framelen) - __dynamic_array(u8, tfd, tfdlen) - - /* - * Do not insert between or below these items, - * we want to keep the frame together (except - * for the possible padding). - */ - __dynamic_array(u8, buf0, buf0_len) - __dynamic_array(u8, buf1, iwl_trace_data(skb) ? 0 : buf1_len) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->framelen = buf0_len + buf1_len; - memcpy(__get_dynamic_array(tfd), tfd, tfdlen); - memcpy(__get_dynamic_array(buf0), buf0, buf0_len); - if (!iwl_trace_data(skb)) - memcpy(__get_dynamic_array(buf1), buf1, buf1_len); - ), - TP_printk("[%s] TX %.2x (%zu bytes)", - __get_str(dev), ((u8 *)__get_dynamic_array(buf0))[0], - __entry->framelen) -); - -TRACE_EVENT(iwlwifi_dev_ucode_error, - TP_PROTO(const struct device *dev, u32 desc, u32 tsf_low, - u32 data1, u32 data2, u32 line, u32 blink1, - u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time, - u32 gp1, u32 gp2, u32 gp3, u32 ucode_ver, u32 hw_ver, - u32 brd_ver), - TP_ARGS(dev, desc, tsf_low, data1, data2, line, - blink1, blink2, ilink1, ilink2, bcon_time, gp1, gp2, - gp3, ucode_ver, hw_ver, brd_ver), - TP_STRUCT__entry( - DEV_ENTRY - __field(u32, desc) - __field(u32, tsf_low) - __field(u32, data1) - __field(u32, data2) - __field(u32, line) - __field(u32, blink1) - __field(u32, blink2) - __field(u32, ilink1) - __field(u32, ilink2) - __field(u32, bcon_time) - __field(u32, gp1) - __field(u32, gp2) - __field(u32, gp3) - __field(u32, ucode_ver) - __field(u32, hw_ver) - __field(u32, brd_ver) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->desc = desc; - __entry->tsf_low = tsf_low; - __entry->data1 = data1; - __entry->data2 = data2; - __entry->line = line; - __entry->blink1 = blink1; - __entry->blink2 = blink2; - __entry->ilink1 = ilink1; - __entry->ilink2 = ilink2; - __entry->bcon_time = bcon_time; - __entry->gp1 = gp1; - __entry->gp2 = gp2; - __entry->gp3 = gp3; - __entry->ucode_ver = ucode_ver; - __entry->hw_ver = hw_ver; - __entry->brd_ver = brd_ver; - ), - TP_printk("[%s] #%02d %010u data 0x%08X 0x%08X line %u, " - "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X " - "bcon_tm %010u gp 0x%08X 0x%08X 0x%08X uCode 0x%08X " - "hw 0x%08X brd 0x%08X", - __get_str(dev), __entry->desc, __entry->tsf_low, - __entry->data1, - __entry->data2, __entry->line, __entry->blink1, - __entry->blink2, __entry->ilink1, __entry->ilink2, - __entry->bcon_time, __entry->gp1, __entry->gp2, - __entry->gp3, __entry->ucode_ver, __entry->hw_ver, - __entry->brd_ver) -); - -TRACE_EVENT(iwlwifi_dev_ucode_event, - TP_PROTO(const struct device *dev, u32 time, u32 data, u32 ev), - TP_ARGS(dev, time, data, ev), - TP_STRUCT__entry( - DEV_ENTRY - - __field(u32, time) - __field(u32, data) - __field(u32, ev) - ), - TP_fast_assign( - DEV_ASSIGN; - __entry->time = time; - __entry->data = data; - __entry->ev = ev; - ), - TP_printk("[%s] EVT_LOGT:%010u:0x%08x:%04u", - __get_str(dev), __entry->time, __entry->data, __entry->ev) -); #endif /* __IWLWIFI_DEVICE_TRACE */ - -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#undef TRACE_INCLUDE_FILE -#define TRACE_INCLUDE_FILE iwl-devtrace -#include diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.c b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c index 2367e8f47a5b57..e77c3a46c94a65 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c @@ -309,7 +309,7 @@ static void rtl8723be_dm_find_minimum_rssi(struct ieee80211_hw *hw) rtl_dm_dig->min_undec_pwdb_for_dm = rtlpriv->dm.entry_min_undec_sm_pwdb; RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD, - "AP Ext Port or disconnet PWDB = 0x%x\n", + "AP Ext Port or disconnect PWDB = 0x%x\n", rtl_dm_dig->min_undec_pwdb_for_dm); } RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "MinUndecoratedPWDBForDM =%d\n", diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c index 0b2082dc48f110..342678d2ed427d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c @@ -873,7 +873,7 @@ static void rtl8821ae_dm_dig(struct ieee80211_hw *hw) if (rtlpriv->falsealm_cnt.cnt_all > 10000) { RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, - "Abnornally false alarm case.\n"); + "Abnormally false alarm case.\n"); if (dm_digtable->large_fa_hit != 3) dm_digtable->large_fa_hit++; diff --git a/drivers/of/base.c b/drivers/of/base.c index 8f165b112e03a2..4cc06c702c41a4 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1960,6 +1960,32 @@ int of_alias_get_id(struct device_node *np, const char *stem) } EXPORT_SYMBOL_GPL(of_alias_get_id); +/** + * of_alias_get_highest_id - Get highest alias id for the given stem + * @stem: Alias stem to be examined + * + * The function travels the lookup table to get the highest alias id for the + * given alias stem. It returns the alias id if found. + */ +int of_alias_get_highest_id(const char *stem) +{ + struct alias_prop *app; + int id = -ENODEV; + + mutex_lock(&of_mutex); + list_for_each_entry(app, &aliases_lookup, link) { + if (strcmp(app->stem, stem) != 0) + continue; + + if (app->id > id) + id = app->id; + } + mutex_unlock(&of_mutex); + + return id; +} +EXPORT_SYMBOL_GPL(of_alias_get_highest_id); + const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur, u32 *pu) { diff --git a/drivers/parisc/eisa_enumerator.c b/drivers/parisc/eisa_enumerator.c index caa1531337540d..a656d9e8334302 100644 --- a/drivers/parisc/eisa_enumerator.c +++ b/drivers/parisc/eisa_enumerator.c @@ -357,7 +357,7 @@ static int parse_slot_config(int slot, } if (flags & HPEE_FUNCTION_INFO_CFG_FREE_FORM) { /* I have no idea how to handle this */ - printk("function %d have free-form confgiuration, skipping ", + printk("function %d have free-form configuration, skipping ", num_func); pos = p0 + function_len; continue; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index ee9f44ad7f02f8..c6f299ba25cb6c 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -67,23 +67,20 @@ config PINCTRL_AT91 help Say Y here to enable the at91 pinctrl driver -config PINCTRL_BCM2835 - bool - select PINMUX - select PINCONF - -config PINCTRL_BCM281XX - bool "Broadcom BCM281xx pinctrl driver" - depends on OF && (ARCH_BCM_MOBILE || COMPILE_TEST) - select PINMUX +config PINCTRL_AMD + bool "AMD GPIO pin control" + depends on GPIOLIB + select GPIOLIB_IRQCHIP select PINCONF select GENERIC_PINCONF - select REGMAP_MMIO help - Say Y here to support Broadcom BCM281xx pinctrl driver, which is used - for the BCM281xx SoC family, including BCM11130, BCM11140, BCM11351, - BCM28145, and BCM28155 SoCs. This driver requires the pinctrl - framework. GPIO is provided by a separate GPIO driver. + driver for memory mapped GPIO functionality on AMD platforms + (x86 or arm).Most pins are usually muxed to some other + functionality by firmware,so only a small amount is available + for gpio use. + + Requires ACPI/FDT device enumeration code to set up a platform + device. config PINCTRL_LANTIQ bool @@ -154,6 +151,10 @@ config PINCTRL_TEGRA124 bool select PINCTRL_TEGRA +config PINCTRL_TEGRA210 + bool + select PINCTRL_TEGRA + config PINCTRL_TEGRA_XUSB def_bool y if ARCH_TEGRA select GENERIC_PHY @@ -207,6 +208,7 @@ config PINCTRL_ZYNQ help This selectes the pinctrl driver for Xilinx Zynq. +source "drivers/pinctrl/bcm/Kconfig" source "drivers/pinctrl/berlin/Kconfig" source "drivers/pinctrl/freescale/Kconfig" source "drivers/pinctrl/intel/Kconfig" @@ -218,6 +220,7 @@ source "drivers/pinctrl/sh-pfc/Kconfig" source "drivers/pinctrl/spear/Kconfig" source "drivers/pinctrl/sunxi/Kconfig" source "drivers/pinctrl/vt8500/Kconfig" +source "drivers/pinctrl/mediatek/Kconfig" config PINCTRL_XWAY bool diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 0475206dd60009..6eadf04a33b3e5 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -14,8 +14,7 @@ obj-$(CONFIG_PINCTRL_AS3722) += pinctrl-as3722.o obj-$(CONFIG_PINCTRL_BF54x) += pinctrl-adi2-bf54x.o obj-$(CONFIG_PINCTRL_BF60x) += pinctrl-adi2-bf60x.o obj-$(CONFIG_PINCTRL_AT91) += pinctrl-at91.o -obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o -obj-$(CONFIG_PINCTRL_BCM281XX) += pinctrl-bcm281xx.o +obj-$(CONFIG_PINCTRL_AMD) += pinctrl-amd.o obj-$(CONFIG_PINCTRL_FALCON) += pinctrl-falcon.o obj-$(CONFIG_PINCTRL_MESON) += meson/ obj-$(CONFIG_PINCTRL_PALMAS) += pinctrl-palmas.o @@ -27,6 +26,7 @@ obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o obj-$(CONFIG_PINCTRL_TEGRA124) += pinctrl-tegra124.o +obj-$(CONFIG_PINCTRL_TEGRA210) += pinctrl-tegra210.o obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o obj-$(CONFIG_PINCTRL_TZ1090) += pinctrl-tz1090.o obj-$(CONFIG_PINCTRL_TZ1090_PDC) += pinctrl-tz1090-pdc.o @@ -38,6 +38,7 @@ obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o +obj-$(CONFIG_ARCH_BCM) += bcm/ obj-$(CONFIG_ARCH_BERLIN) += berlin/ obj-y += freescale/ obj-$(CONFIG_X86) += intel/ @@ -49,3 +50,4 @@ obj-$(CONFIG_PINCTRL_SH_PFC) += sh-pfc/ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_VT8500) += vt8500/ +obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ diff --git a/drivers/pinctrl/bcm/Kconfig b/drivers/pinctrl/bcm/Kconfig new file mode 100644 index 00000000000000..cd11d4d9ad58f8 --- /dev/null +++ b/drivers/pinctrl/bcm/Kconfig @@ -0,0 +1,56 @@ +# +# Broadcom pinctrl drivers +# + +config PINCTRL_BCM281XX + bool "Broadcom BCM281xx pinctrl driver" + depends on OF && (ARCH_BCM_MOBILE || COMPILE_TEST) + select PINMUX + select PINCONF + select GENERIC_PINCONF + select REGMAP_MMIO + help + Say Y here to support Broadcom BCM281xx pinctrl driver, which is used + for the BCM281xx SoC family, including BCM11130, BCM11140, BCM11351, + BCM28145, and BCM28155 SoCs. This driver requires the pinctrl + framework. GPIO is provided by a separate GPIO driver. + +config PINCTRL_BCM2835 + bool + select PINMUX + select PINCONF + +config PINCTRL_CYGNUS_GPIO + bool "Broadcom Cygnus GPIO (with PINCONF) driver" + depends on OF_GPIO && ARCH_BCM_CYGNUS + select GPIOLIB_IRQCHIP + select PINCONF + select GENERIC_PINCONF + default ARCH_BCM_CYGNUS + help + Say yes here to enable the Broadcom Cygnus GPIO driver. + + The Broadcom Cygnus SoC has 3 GPIO controllers including the ASIU + GPIO controller (ASIU), the chipCommonG GPIO controller (CCM), and + the always-ON GPIO controller (CRMU/AON). All 3 GPIO controllers are + supported by this driver. + + All 3 Cygnus GPIO controllers support basic PINCONF functions such + as bias pull up, pull down, and drive strength configurations, when + these pins are muxed to GPIO. + + Pins from the ASIU GPIO can be individually muxed to GPIO function, + through interaction with the Cygnus IOMUX controller. + +config PINCTRL_CYGNUS_MUX + bool "Broadcom Cygnus IOMUX driver" + depends on (ARCH_BCM_CYGNUS || COMPILE_TEST) + select PINMUX + select GENERIC_PINCONF + default ARCH_BCM_CYGNUS + help + Say yes here to enable the Broadcom Cygnus IOMUX driver. + + The Broadcom Cygnus IOMUX driver supports group based IOMUX + configuration, with the exception that certain individual pins + can be overrided to GPIO function diff --git a/drivers/pinctrl/bcm/Makefile b/drivers/pinctrl/bcm/Makefile new file mode 100644 index 00000000000000..2b2f70ee804ccd --- /dev/null +++ b/drivers/pinctrl/bcm/Makefile @@ -0,0 +1,6 @@ +# Broadcom pinctrl support + +obj-$(CONFIG_PINCTRL_BCM281XX) += pinctrl-bcm281xx.o +obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o +obj-$(CONFIG_PINCTRL_CYGNUS_GPIO) += pinctrl-cygnus-gpio.o +obj-$(CONFIG_PINCTRL_CYGNUS_MUX) += pinctrl-cygnus-mux.o diff --git a/drivers/pinctrl/pinctrl-bcm281xx.c b/drivers/pinctrl/bcm/pinctrl-bcm281xx.c similarity index 99% rename from drivers/pinctrl/pinctrl-bcm281xx.c rename to drivers/pinctrl/bcm/pinctrl-bcm281xx.c index b88cfe5ed55aed..9641f1c7617eab 100644 --- a/drivers/pinctrl/pinctrl-bcm281xx.c +++ b/drivers/pinctrl/bcm/pinctrl-bcm281xx.c @@ -21,8 +21,8 @@ #include #include #include -#include "core.h" -#include "pinctrl-utils.h" +#include "../core.h" +#include "../pinctrl-utils.h" /* BCM281XX Pin Control Registers Definitions */ diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c similarity index 98% rename from drivers/pinctrl/pinctrl-bcm2835.c rename to drivers/pinctrl/bcm/pinctrl-bcm2835.c index 9aa8a3f10b105a..8d908e3f42c3d1 100644 --- a/drivers/pinctrl/pinctrl-bcm2835.c +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c @@ -403,15 +403,7 @@ static irqreturn_t bcm2835_gpio_irq_handler(int irq, void *dev_id) gpio = (32 * bank) + offset; type = pc->irq_type[gpio]; - /* ack edge triggered IRQs immediately */ - if (!(type & IRQ_TYPE_LEVEL_MASK)) - bcm2835_gpio_set_bit(pc, GPEDS0, gpio); - generic_handle_irq(irq_linear_revmap(pc->irq_domain, gpio)); - - /* ack level triggered IRQ after handling them */ - if (type & IRQ_TYPE_LEVEL_MASK) - bcm2835_gpio_set_bit(pc, GPEDS0, gpio); } return events ? IRQ_HANDLED : IRQ_NONE; } @@ -591,16 +583,32 @@ static int bcm2835_gpio_irq_set_type(struct irq_data *data, unsigned int type) else ret = __bcm2835_gpio_irq_set_type_disabled(pc, gpio, type); + if (type & IRQ_TYPE_EDGE_BOTH) + __irq_set_handler_locked(data->irq, handle_edge_irq); + else + __irq_set_handler_locked(data->irq, handle_level_irq); + spin_unlock_irqrestore(&pc->irq_lock[bank], flags); return ret; } +static void bcm2835_gpio_irq_ack(struct irq_data *data) +{ + struct bcm2835_pinctrl *pc = irq_data_get_irq_chip_data(data); + unsigned gpio = irqd_to_hwirq(data); + + bcm2835_gpio_set_bit(pc, GPEDS0, gpio); +} + static struct irq_chip bcm2835_gpio_irq_chip = { .name = MODULE_NAME, .irq_enable = bcm2835_gpio_irq_enable, .irq_disable = bcm2835_gpio_irq_disable, .irq_set_type = bcm2835_gpio_irq_set_type, + .irq_ack = bcm2835_gpio_irq_ack, + .irq_mask = bcm2835_gpio_irq_disable, + .irq_unmask = bcm2835_gpio_irq_enable, }; static int bcm2835_pctl_get_groups_count(struct pinctrl_dev *pctldev) @@ -977,7 +985,7 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev) int irq = irq_create_mapping(pc->irq_domain, i); irq_set_lockdep_class(irq, &gpio_lock_class); irq_set_chip_and_handler(irq, &bcm2835_gpio_irq_chip, - handle_simple_irq); + handle_level_irq); irq_set_chip_data(irq, pc); set_irq_flags(irq, IRQF_VALID); } @@ -1051,7 +1059,7 @@ static int bcm2835_pinctrl_remove(struct platform_device *pdev) return 0; } -static struct of_device_id bcm2835_pinctrl_match[] = { +static const struct of_device_id bcm2835_pinctrl_match[] = { { .compatible = "brcm,bcm2835-gpio" }, {} }; diff --git a/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c b/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c new file mode 100644 index 00000000000000..4ad5c1a996e3e9 --- /dev/null +++ b/drivers/pinctrl/bcm/pinctrl-cygnus-gpio.c @@ -0,0 +1,907 @@ +/* + * Copyright (C) 2014-2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This file contains the Broadcom Cygnus GPIO driver that supports 3 + * GPIO controllers on Cygnus including the ASIU GPIO controller, the + * chipCommonG GPIO controller, and the always-on GPIO controller. Basic + * PINCONF such as bias pull up/down, and drive strength are also supported + * in this driver. + * + * Pins from the ASIU GPIO can be individually muxed to GPIO function, + * through the interaction with the Cygnus IOMUX controller + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pinctrl-utils.h" + +#define CYGNUS_GPIO_DATA_IN_OFFSET 0x00 +#define CYGNUS_GPIO_DATA_OUT_OFFSET 0x04 +#define CYGNUS_GPIO_OUT_EN_OFFSET 0x08 +#define CYGNUS_GPIO_IN_TYPE_OFFSET 0x0c +#define CYGNUS_GPIO_INT_DE_OFFSET 0x10 +#define CYGNUS_GPIO_INT_EDGE_OFFSET 0x14 +#define CYGNUS_GPIO_INT_MSK_OFFSET 0x18 +#define CYGNUS_GPIO_INT_STAT_OFFSET 0x1c +#define CYGNUS_GPIO_INT_MSTAT_OFFSET 0x20 +#define CYGNUS_GPIO_INT_CLR_OFFSET 0x24 +#define CYGNUS_GPIO_PAD_RES_OFFSET 0x34 +#define CYGNUS_GPIO_RES_EN_OFFSET 0x38 + +/* drive strength control for ASIU GPIO */ +#define CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET 0x58 + +/* drive strength control for CCM/CRMU (AON) GPIO */ +#define CYGNUS_GPIO_DRV0_CTRL_OFFSET 0x00 + +#define GPIO_BANK_SIZE 0x200 +#define NGPIOS_PER_BANK 32 +#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK) + +#define CYGNUS_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg)) +#define CYGNUS_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK) + +#define GPIO_DRV_STRENGTH_BIT_SHIFT 20 +#define GPIO_DRV_STRENGTH_BITS 3 +#define GPIO_DRV_STRENGTH_BIT_MASK ((1 << GPIO_DRV_STRENGTH_BITS) - 1) + +/* + * Cygnus GPIO core + * + * @dev: pointer to device + * @base: I/O register base for Cygnus GPIO controller + * @io_ctrl: I/O register base for certain type of Cygnus GPIO controller that + * has the PINCONF support implemented outside of the GPIO block + * @lock: lock to protect access to I/O registers + * @gc: GPIO chip + * @num_banks: number of GPIO banks, each bank supports up to 32 GPIOs + * @pinmux_is_supported: flag to indicate this GPIO controller contains pins + * that can be individually muxed to GPIO + * @pctl: pointer to pinctrl_dev + * @pctldesc: pinctrl descriptor + */ +struct cygnus_gpio { + struct device *dev; + + void __iomem *base; + void __iomem *io_ctrl; + + spinlock_t lock; + + struct gpio_chip gc; + unsigned num_banks; + + bool pinmux_is_supported; + + struct pinctrl_dev *pctl; + struct pinctrl_desc pctldesc; +}; + +static inline struct cygnus_gpio *to_cygnus_gpio(struct gpio_chip *gc) +{ + return container_of(gc, struct cygnus_gpio, gc); +} + +/* + * Mapping from PINCONF pins to GPIO pins is 1-to-1 + */ +static inline unsigned cygnus_pin_to_gpio(unsigned pin) +{ + return pin; +} + +/** + * cygnus_set_bit - set or clear one bit (corresponding to the GPIO pin) in a + * Cygnus GPIO register + * + * @cygnus_gpio: Cygnus GPIO device + * @reg: register offset + * @gpio: GPIO pin + * @set: set or clear + */ +static inline void cygnus_set_bit(struct cygnus_gpio *chip, unsigned int reg, + unsigned gpio, bool set) +{ + unsigned int offset = CYGNUS_GPIO_REG(gpio, reg); + unsigned int shift = CYGNUS_GPIO_SHIFT(gpio); + u32 val; + + val = readl(chip->base + offset); + if (set) + val |= BIT(shift); + else + val &= ~BIT(shift); + writel(val, chip->base + offset); +} + +static inline bool cygnus_get_bit(struct cygnus_gpio *chip, unsigned int reg, + unsigned gpio) +{ + unsigned int offset = CYGNUS_GPIO_REG(gpio, reg); + unsigned int shift = CYGNUS_GPIO_SHIFT(gpio); + + return !!(readl(chip->base + offset) & BIT(shift)); +} + +static void cygnus_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct cygnus_gpio *chip = to_cygnus_gpio(gc); + struct irq_chip *irq_chip = irq_desc_get_chip(desc); + int i, bit; + + chained_irq_enter(irq_chip, desc); + + /* go through the entire GPIO banks and handle all interrupts */ + for (i = 0; i < chip->num_banks; i++) { + unsigned long val = readl(chip->base + (i * GPIO_BANK_SIZE) + + CYGNUS_GPIO_INT_MSTAT_OFFSET); + + for_each_set_bit(bit, &val, NGPIOS_PER_BANK) { + unsigned pin = NGPIOS_PER_BANK * i + bit; + int child_irq = irq_find_mapping(gc->irqdomain, pin); + + /* + * Clear the interrupt before invoking the + * handler, so we do not leave any window + */ + writel(BIT(bit), chip->base + (i * GPIO_BANK_SIZE) + + CYGNUS_GPIO_INT_CLR_OFFSET); + + generic_handle_irq(child_irq); + } + } + + chained_irq_exit(irq_chip, desc); +} + + +static void cygnus_gpio_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct cygnus_gpio *chip = to_cygnus_gpio(gc); + unsigned gpio = d->hwirq; + unsigned int offset = CYGNUS_GPIO_REG(gpio, + CYGNUS_GPIO_INT_CLR_OFFSET); + unsigned int shift = CYGNUS_GPIO_SHIFT(gpio); + u32 val = BIT(shift); + + writel(val, chip->base + offset); +} + +/** + * cygnus_gpio_irq_set_mask - mask/unmask a GPIO interrupt + * + * @d: IRQ chip data + * @unmask: mask/unmask GPIO interrupt + */ +static void cygnus_gpio_irq_set_mask(struct irq_data *d, bool unmask) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct cygnus_gpio *chip = to_cygnus_gpio(gc); + unsigned gpio = d->hwirq; + + cygnus_set_bit(chip, CYGNUS_GPIO_INT_MSK_OFFSET, gpio, unmask); +} + +static void cygnus_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct cygnus_gpio *chip = to_cygnus_gpio(gc); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + cygnus_gpio_irq_set_mask(d, false); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static void cygnus_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct cygnus_gpio *chip = to_cygnus_gpio(gc); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + cygnus_gpio_irq_set_mask(d, true); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int cygnus_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct cygnus_gpio *chip = to_cygnus_gpio(gc); + unsigned gpio = d->hwirq; + bool level_triggered = false; + bool dual_edge = false; + bool rising_or_high = false; + unsigned long flags; + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + rising_or_high = true; + break; + + case IRQ_TYPE_EDGE_FALLING: + break; + + case IRQ_TYPE_EDGE_BOTH: + dual_edge = true; + break; + + case IRQ_TYPE_LEVEL_HIGH: + level_triggered = true; + rising_or_high = true; + break; + + case IRQ_TYPE_LEVEL_LOW: + level_triggered = true; + break; + + default: + dev_err(chip->dev, "invalid GPIO IRQ type 0x%x\n", + type); + return -EINVAL; + } + + spin_lock_irqsave(&chip->lock, flags); + cygnus_set_bit(chip, CYGNUS_GPIO_IN_TYPE_OFFSET, gpio, + level_triggered); + cygnus_set_bit(chip, CYGNUS_GPIO_INT_DE_OFFSET, gpio, dual_edge); + cygnus_set_bit(chip, CYGNUS_GPIO_INT_EDGE_OFFSET, gpio, + rising_or_high); + spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, + "gpio:%u level_triggered:%d dual_edge:%d rising_or_high:%d\n", + gpio, level_triggered, dual_edge, rising_or_high); + + return 0; +} + +static struct irq_chip cygnus_gpio_irq_chip = { + .name = "bcm-cygnus-gpio", + .irq_ack = cygnus_gpio_irq_ack, + .irq_mask = cygnus_gpio_irq_mask, + .irq_unmask = cygnus_gpio_irq_unmask, + .irq_set_type = cygnus_gpio_irq_set_type, +}; + +/* + * Request the Cygnus IOMUX pinmux controller to mux individual pins to GPIO + */ +static int cygnus_gpio_request(struct gpio_chip *gc, unsigned offset) +{ + struct cygnus_gpio *chip = to_cygnus_gpio(gc); + unsigned gpio = gc->base + offset; + + /* not all Cygnus GPIO pins can be muxed individually */ + if (!chip->pinmux_is_supported) + return 0; + + return pinctrl_request_gpio(gpio); +} + +static void cygnus_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + struct cygnus_gpio *chip = to_cygnus_gpio(gc); + unsigned gpio = gc->base + offset; + + if (!chip->pinmux_is_supported) + return; + + pinctrl_free_gpio(gpio); +} + +static int cygnus_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) +{ + struct cygnus_gpio *chip = to_cygnus_gpio(gc); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, false); + spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set input\n", gpio); + + return 0; +} + +static int cygnus_gpio_direction_output(struct gpio_chip *gc, unsigned gpio, + int val) +{ + struct cygnus_gpio *chip = to_cygnus_gpio(gc); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + cygnus_set_bit(chip, CYGNUS_GPIO_OUT_EN_OFFSET, gpio, true); + cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); + spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set output, value:%d\n", gpio, val); + + return 0; +} + +static void cygnus_gpio_set(struct gpio_chip *gc, unsigned gpio, int val) +{ + struct cygnus_gpio *chip = to_cygnus_gpio(gc); + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + cygnus_set_bit(chip, CYGNUS_GPIO_DATA_OUT_OFFSET, gpio, !!(val)); + spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set, value:%d\n", gpio, val); +} + +static int cygnus_gpio_get(struct gpio_chip *gc, unsigned gpio) +{ + struct cygnus_gpio *chip = to_cygnus_gpio(gc); + unsigned int offset = CYGNUS_GPIO_REG(gpio, + CYGNUS_GPIO_DATA_IN_OFFSET); + unsigned int shift = CYGNUS_GPIO_SHIFT(gpio); + + return !!(readl(chip->base + offset) & BIT(shift)); +} + +static int cygnus_get_groups_count(struct pinctrl_dev *pctldev) +{ + return 1; +} + +/* + * Only one group: "gpio_grp", since this local pinctrl device only performs + * GPIO specific PINCONF configurations + */ +static const char *cygnus_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return "gpio_grp"; +} + +static const struct pinctrl_ops cygnus_pctrl_ops = { + .get_groups_count = cygnus_get_groups_count, + .get_group_name = cygnus_get_group_name, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinctrl_utils_dt_free_map, +}; + +static int cygnus_gpio_set_pull(struct cygnus_gpio *chip, unsigned gpio, + bool disable, bool pull_up) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + + if (disable) { + cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, false); + } else { + cygnus_set_bit(chip, CYGNUS_GPIO_PAD_RES_OFFSET, gpio, + pull_up); + cygnus_set_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio, true); + } + + spin_unlock_irqrestore(&chip->lock, flags); + + dev_dbg(chip->dev, "gpio:%u set pullup:%d\n", gpio, pull_up); + + return 0; +} + +static void cygnus_gpio_get_pull(struct cygnus_gpio *chip, unsigned gpio, + bool *disable, bool *pull_up) +{ + unsigned long flags; + + spin_lock_irqsave(&chip->lock, flags); + *disable = !cygnus_get_bit(chip, CYGNUS_GPIO_RES_EN_OFFSET, gpio); + *pull_up = cygnus_get_bit(chip, CYGNUS_GPIO_PAD_RES_OFFSET, gpio); + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int cygnus_gpio_set_strength(struct cygnus_gpio *chip, unsigned gpio, + unsigned strength) +{ + void __iomem *base; + unsigned int i, offset, shift; + u32 val; + unsigned long flags; + + /* make sure drive strength is supported */ + if (strength < 2 || strength > 16 || (strength % 2)) + return -ENOTSUPP; + + if (chip->io_ctrl) { + base = chip->io_ctrl; + offset = CYGNUS_GPIO_DRV0_CTRL_OFFSET; + } else { + base = chip->base; + offset = CYGNUS_GPIO_REG(gpio, + CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET); + } + + shift = CYGNUS_GPIO_SHIFT(gpio); + + dev_dbg(chip->dev, "gpio:%u set drive strength:%d mA\n", gpio, + strength); + + spin_lock_irqsave(&chip->lock, flags); + strength = (strength / 2) - 1; + for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { + val = readl(base + offset); + val &= ~BIT(shift); + val |= ((strength >> i) & 0x1) << shift; + writel(val, base + offset); + offset += 4; + } + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int cygnus_gpio_get_strength(struct cygnus_gpio *chip, unsigned gpio, + u16 *strength) +{ + void __iomem *base; + unsigned int i, offset, shift; + u32 val; + unsigned long flags; + + if (chip->io_ctrl) { + base = chip->io_ctrl; + offset = CYGNUS_GPIO_DRV0_CTRL_OFFSET; + } else { + base = chip->base; + offset = CYGNUS_GPIO_REG(gpio, + CYGNUS_GPIO_ASIU_DRV0_CTRL_OFFSET); + } + + shift = CYGNUS_GPIO_SHIFT(gpio); + + spin_lock_irqsave(&chip->lock, flags); + *strength = 0; + for (i = 0; i < GPIO_DRV_STRENGTH_BITS; i++) { + val = readl(base + offset) & BIT(shift); + val >>= shift; + *strength += (val << i); + offset += 4; + } + + /* convert to mA */ + *strength = (*strength + 1) * 2; + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int cygnus_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config) +{ + struct cygnus_gpio *chip = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + unsigned gpio = cygnus_pin_to_gpio(pin); + u16 arg; + bool disable, pull_up; + int ret; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up); + if (disable) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_BIAS_PULL_UP: + cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up); + if (!disable && pull_up) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_BIAS_PULL_DOWN: + cygnus_gpio_get_pull(chip, gpio, &disable, &pull_up); + if (!disable && !pull_up) + return 0; + else + return -EINVAL; + + case PIN_CONFIG_DRIVE_STRENGTH: + ret = cygnus_gpio_get_strength(chip, gpio, &arg); + if (ret) + return ret; + else + *config = pinconf_to_config_packed(param, arg); + + return 0; + + default: + return -ENOTSUPP; + } + + return -ENOTSUPP; +} + +static int cygnus_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *configs, unsigned num_configs) +{ + struct cygnus_gpio *chip = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param; + u16 arg; + unsigned i, gpio = cygnus_pin_to_gpio(pin); + int ret = -ENOTSUPP; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + ret = cygnus_gpio_set_pull(chip, gpio, true, false); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_BIAS_PULL_UP: + ret = cygnus_gpio_set_pull(chip, gpio, false, true); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + ret = cygnus_gpio_set_pull(chip, gpio, false, false); + if (ret < 0) + goto out; + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + ret = cygnus_gpio_set_strength(chip, gpio, arg); + if (ret < 0) + goto out; + break; + + default: + dev_err(chip->dev, "invalid configuration\n"); + return -ENOTSUPP; + } + } /* for each config */ + +out: + return ret; +} + +static const struct pinconf_ops cygnus_pconf_ops = { + .is_generic = true, + .pin_config_get = cygnus_pin_config_get, + .pin_config_set = cygnus_pin_config_set, +}; + +/* + * Map a GPIO in the local gpio_chip pin space to a pin in the Cygnus IOMUX + * pinctrl pin space + */ +struct cygnus_gpio_pin_range { + unsigned offset; + unsigned pin_base; + unsigned num_pins; +}; + +#define CYGNUS_PINRANGE(o, p, n) { .offset = o, .pin_base = p, .num_pins = n } + +/* + * Pin mapping table for mapping local GPIO pins to Cygnus IOMUX pinctrl pins + */ +static const struct cygnus_gpio_pin_range cygnus_gpio_pintable[] = { + CYGNUS_PINRANGE(0, 42, 1), + CYGNUS_PINRANGE(1, 44, 3), + CYGNUS_PINRANGE(4, 48, 1), + CYGNUS_PINRANGE(5, 50, 3), + CYGNUS_PINRANGE(8, 126, 1), + CYGNUS_PINRANGE(9, 155, 1), + CYGNUS_PINRANGE(10, 152, 1), + CYGNUS_PINRANGE(11, 154, 1), + CYGNUS_PINRANGE(12, 153, 1), + CYGNUS_PINRANGE(13, 127, 3), + CYGNUS_PINRANGE(16, 140, 1), + CYGNUS_PINRANGE(17, 145, 7), + CYGNUS_PINRANGE(24, 130, 10), + CYGNUS_PINRANGE(34, 141, 4), + CYGNUS_PINRANGE(38, 54, 1), + CYGNUS_PINRANGE(39, 56, 3), + CYGNUS_PINRANGE(42, 60, 3), + CYGNUS_PINRANGE(45, 64, 3), + CYGNUS_PINRANGE(48, 68, 2), + CYGNUS_PINRANGE(50, 84, 6), + CYGNUS_PINRANGE(56, 94, 6), + CYGNUS_PINRANGE(62, 72, 1), + CYGNUS_PINRANGE(63, 70, 1), + CYGNUS_PINRANGE(64, 80, 1), + CYGNUS_PINRANGE(65, 74, 3), + CYGNUS_PINRANGE(68, 78, 1), + CYGNUS_PINRANGE(69, 82, 1), + CYGNUS_PINRANGE(70, 156, 17), + CYGNUS_PINRANGE(87, 104, 12), + CYGNUS_PINRANGE(99, 102, 2), + CYGNUS_PINRANGE(101, 90, 4), + CYGNUS_PINRANGE(105, 116, 10), + CYGNUS_PINRANGE(123, 11, 1), + CYGNUS_PINRANGE(124, 38, 4), + CYGNUS_PINRANGE(128, 43, 1), + CYGNUS_PINRANGE(129, 47, 1), + CYGNUS_PINRANGE(130, 49, 1), + CYGNUS_PINRANGE(131, 53, 1), + CYGNUS_PINRANGE(132, 55, 1), + CYGNUS_PINRANGE(133, 59, 1), + CYGNUS_PINRANGE(134, 63, 1), + CYGNUS_PINRANGE(135, 67, 1), + CYGNUS_PINRANGE(136, 71, 1), + CYGNUS_PINRANGE(137, 73, 1), + CYGNUS_PINRANGE(138, 77, 1), + CYGNUS_PINRANGE(139, 79, 1), + CYGNUS_PINRANGE(140, 81, 1), + CYGNUS_PINRANGE(141, 83, 1), + CYGNUS_PINRANGE(142, 10, 1) +}; + +/* + * The Cygnus IOMUX controller mainly supports group based mux configuration, + * but certain pins can be muxed to GPIO individually. Only the ASIU GPIO + * controller can support this, so it's an optional configuration + * + * Return -ENODEV means no support and that's fine + */ +static int cygnus_gpio_pinmux_add_range(struct cygnus_gpio *chip) +{ + struct device_node *node = chip->dev->of_node; + struct device_node *pinmux_node; + struct platform_device *pinmux_pdev; + struct gpio_chip *gc = &chip->gc; + int i, ret = 0; + + /* parse DT to find the phandle to the pinmux controller */ + pinmux_node = of_parse_phandle(node, "pinmux", 0); + if (!pinmux_node) + return -ENODEV; + + pinmux_pdev = of_find_device_by_node(pinmux_node); + /* no longer need the pinmux node */ + of_node_put(pinmux_node); + if (!pinmux_pdev) { + dev_err(chip->dev, "failed to get pinmux device\n"); + return -EINVAL; + } + + /* now need to create the mapping between local GPIO and PINMUX pins */ + for (i = 0; i < ARRAY_SIZE(cygnus_gpio_pintable); i++) { + ret = gpiochip_add_pin_range(gc, dev_name(&pinmux_pdev->dev), + cygnus_gpio_pintable[i].offset, + cygnus_gpio_pintable[i].pin_base, + cygnus_gpio_pintable[i].num_pins); + if (ret) { + dev_err(chip->dev, "unable to add GPIO pin range\n"); + goto err_put_device; + } + } + + chip->pinmux_is_supported = true; + + /* no need for pinmux_pdev device reference anymore */ + put_device(&pinmux_pdev->dev); + return 0; + +err_put_device: + put_device(&pinmux_pdev->dev); + gpiochip_remove_pin_ranges(gc); + return ret; +} + +/* + * Cygnus GPIO controller supports some PINCONF related configurations such as + * pull up, pull down, and drive strength, when the pin is configured to GPIO + * + * Here a local pinctrl device is created with simple 1-to-1 pin mapping to the + * local GPIO pins + */ +static int cygnus_gpio_register_pinconf(struct cygnus_gpio *chip) +{ + struct pinctrl_desc *pctldesc = &chip->pctldesc; + struct pinctrl_pin_desc *pins; + struct gpio_chip *gc = &chip->gc; + int i; + + pins = devm_kcalloc(chip->dev, gc->ngpio, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + for (i = 0; i < gc->ngpio; i++) { + pins[i].number = i; + pins[i].name = devm_kasprintf(chip->dev, GFP_KERNEL, + "gpio-%d", i); + if (!pins[i].name) + return -ENOMEM; + } + + pctldesc->name = dev_name(chip->dev); + pctldesc->pctlops = &cygnus_pctrl_ops; + pctldesc->pins = pins; + pctldesc->npins = gc->ngpio; + pctldesc->confops = &cygnus_pconf_ops; + + chip->pctl = pinctrl_register(pctldesc, chip->dev, chip); + if (!chip->pctl) { + dev_err(chip->dev, "unable to register pinctrl device\n"); + return -EINVAL; + } + + return 0; +} + +static void cygnus_gpio_unregister_pinconf(struct cygnus_gpio *chip) +{ + if (chip->pctl) + pinctrl_unregister(chip->pctl); +} + +struct cygnus_gpio_data { + unsigned num_gpios; +}; + +static const struct cygnus_gpio_data cygnus_cmm_gpio_data = { + .num_gpios = 24, +}; + +static const struct cygnus_gpio_data cygnus_asiu_gpio_data = { + .num_gpios = 146, +}; + +static const struct cygnus_gpio_data cygnus_crmu_gpio_data = { + .num_gpios = 6, +}; + +static const struct of_device_id cygnus_gpio_of_match[] = { + { + .compatible = "brcm,cygnus-ccm-gpio", + .data = &cygnus_cmm_gpio_data, + }, + { + .compatible = "brcm,cygnus-asiu-gpio", + .data = &cygnus_asiu_gpio_data, + }, + { + .compatible = "brcm,cygnus-crmu-gpio", + .data = &cygnus_crmu_gpio_data, + } +}; + +static int cygnus_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct cygnus_gpio *chip; + struct gpio_chip *gc; + u32 ngpios; + int irq, ret; + const struct of_device_id *match; + const struct cygnus_gpio_data *gpio_data; + + match = of_match_device(cygnus_gpio_of_match, dev); + if (!match) + return -ENODEV; + gpio_data = match->data; + ngpios = gpio_data->num_gpios; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = dev; + platform_set_drvdata(pdev, chip); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->base = devm_ioremap_resource(dev, res); + if (IS_ERR(chip->base)) { + dev_err(dev, "unable to map I/O memory\n"); + return PTR_ERR(chip->base); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + chip->io_ctrl = devm_ioremap_resource(dev, res); + if (IS_ERR(chip->io_ctrl)) { + dev_err(dev, "unable to map I/O memory\n"); + return PTR_ERR(chip->io_ctrl); + } + } + + spin_lock_init(&chip->lock); + + gc = &chip->gc; + gc->base = -1; + gc->ngpio = ngpios; + chip->num_banks = (ngpios + NGPIOS_PER_BANK - 1) / NGPIOS_PER_BANK; + gc->label = dev_name(dev); + gc->dev = dev; + gc->of_node = dev->of_node; + gc->request = cygnus_gpio_request; + gc->free = cygnus_gpio_free; + gc->direction_input = cygnus_gpio_direction_input; + gc->direction_output = cygnus_gpio_direction_output; + gc->set = cygnus_gpio_set; + gc->get = cygnus_gpio_get; + + ret = gpiochip_add(gc); + if (ret < 0) { + dev_err(dev, "unable to add GPIO chip\n"); + return ret; + } + + ret = cygnus_gpio_pinmux_add_range(chip); + if (ret && ret != -ENODEV) { + dev_err(dev, "unable to add GPIO pin range\n"); + goto err_rm_gpiochip; + } + + ret = cygnus_gpio_register_pinconf(chip); + if (ret) { + dev_err(dev, "unable to register pinconf\n"); + goto err_rm_gpiochip; + } + + /* optional GPIO interrupt support */ + irq = platform_get_irq(pdev, 0); + if (irq) { + ret = gpiochip_irqchip_add(gc, &cygnus_gpio_irq_chip, 0, + handle_simple_irq, IRQ_TYPE_NONE); + if (ret) { + dev_err(dev, "no GPIO irqchip\n"); + goto err_unregister_pinconf; + } + + gpiochip_set_chained_irqchip(gc, &cygnus_gpio_irq_chip, irq, + cygnus_gpio_irq_handler); + } + + return 0; + +err_unregister_pinconf: + cygnus_gpio_unregister_pinconf(chip); + +err_rm_gpiochip: + gpiochip_remove(gc); + + return ret; +} + +static struct platform_driver cygnus_gpio_driver = { + .driver = { + .name = "cygnus-gpio", + .of_match_table = cygnus_gpio_of_match, + }, + .probe = cygnus_gpio_probe, +}; + +static int __init cygnus_gpio_init(void) +{ + return platform_driver_probe(&cygnus_gpio_driver, cygnus_gpio_probe); +} +arch_initcall_sync(cygnus_gpio_init); diff --git a/drivers/pinctrl/bcm/pinctrl-cygnus-mux.c b/drivers/pinctrl/bcm/pinctrl-cygnus-mux.c new file mode 100644 index 00000000000000..f9a9283caf8188 --- /dev/null +++ b/drivers/pinctrl/bcm/pinctrl-cygnus-mux.c @@ -0,0 +1,1022 @@ +/* Copyright (C) 2014-2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This file contains the Cygnus IOMUX driver that supports group based PINMUX + * configuration. Although PINMUX configuration is mainly group based, the + * Cygnus IOMUX controller allows certain pins to be individually muxed to GPIO + * function, and therefore be controlled by the Cygnus ASIU GPIO controller + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core.h" +#include "../pinctrl-utils.h" + +#define CYGNUS_NUM_IOMUX_REGS 8 +#define CYGNUS_NUM_MUX_PER_REG 8 +#define CYGNUS_NUM_IOMUX (CYGNUS_NUM_IOMUX_REGS * \ + CYGNUS_NUM_MUX_PER_REG) + +/* + * Cygnus IOMUX register description + * + * @offset: register offset for mux configuration of a group + * @shift: bit shift for mux configuration of a group + * @alt: alternate function to set to + */ +struct cygnus_mux { + unsigned int offset; + unsigned int shift; + unsigned int alt; +}; + +/* + * Keep track of Cygnus IOMUX configuration and prevent double configuration + * + * @cygnus_mux: Cygnus IOMUX register description + * @is_configured: flag to indicate whether a mux setting has already been + * configured + */ +struct cygnus_mux_log { + struct cygnus_mux mux; + bool is_configured; +}; + +/* + * Group based IOMUX configuration + * + * @name: name of the group + * @pins: array of pins used by this group + * @num_pins: total number of pins used by this group + * @mux: Cygnus group based IOMUX configuration + */ +struct cygnus_pin_group { + const char *name; + const unsigned *pins; + unsigned num_pins; + struct cygnus_mux mux; +}; + +/* + * Cygnus mux function and supported pin groups + * + * @name: name of the function + * @groups: array of groups that can be supported by this function + * @num_groups: total number of groups that can be supported by this function + */ +struct cygnus_pin_function { + const char *name; + const char * const *groups; + unsigned num_groups; +}; + +/* + * Cygnus IOMUX pinctrl core + * + * @pctl: pointer to pinctrl_dev + * @dev: pointer to device + * @base0: first I/O register base of the Cygnus IOMUX controller + * @base1: second I/O register base + * @groups: pointer to array of groups + * @num_groups: total number of groups + * @functions: pointer to array of functions + * @num_functions: total number of functions + * @mux_log: pointer to the array of mux logs + * @lock: lock to protect register access + */ +struct cygnus_pinctrl { + struct pinctrl_dev *pctl; + struct device *dev; + void __iomem *base0; + void __iomem *base1; + + const struct cygnus_pin_group *groups; + unsigned num_groups; + + const struct cygnus_pin_function *functions; + unsigned num_functions; + + struct cygnus_mux_log *mux_log; + + spinlock_t lock; +}; + +/* + * Certain pins can be individually muxed to GPIO function + * + * @is_supported: flag to indicate GPIO mux is supported for this pin + * @offset: register offset for GPIO mux override of a pin + * @shift: bit shift for GPIO mux override of a pin + */ +struct cygnus_gpio_mux { + int is_supported; + unsigned int offset; + unsigned int shift; +}; + +/* + * Description of a pin in Cygnus + * + * @pin: pin number + * @name: pin name + * @gpio_mux: GPIO override related information + */ +struct cygnus_pin { + unsigned pin; + char *name; + struct cygnus_gpio_mux gpio_mux; +}; + +#define CYGNUS_PIN_DESC(p, n, i, o, s) \ +{ \ + .pin = p, \ + .name = n, \ + .gpio_mux = { \ + .is_supported = i, \ + .offset = o, \ + .shift = s, \ + }, \ +} + +/* + * List of pins in Cygnus + */ +static struct cygnus_pin cygnus_pins[] = { + CYGNUS_PIN_DESC(0, "ext_device_reset_n", 0, 0, 0), + CYGNUS_PIN_DESC(1, "chip_mode0", 0, 0, 0), + CYGNUS_PIN_DESC(2, "chip_mode1", 0, 0, 0), + CYGNUS_PIN_DESC(3, "chip_mode2", 0, 0, 0), + CYGNUS_PIN_DESC(4, "chip_mode3", 0, 0, 0), + CYGNUS_PIN_DESC(5, "chip_mode4", 0, 0, 0), + CYGNUS_PIN_DESC(6, "bsc0_scl", 0, 0, 0), + CYGNUS_PIN_DESC(7, "bsc0_sda", 0, 0, 0), + CYGNUS_PIN_DESC(8, "bsc1_scl", 0, 0, 0), + CYGNUS_PIN_DESC(9, "bsc1_sda", 0, 0, 0), + CYGNUS_PIN_DESC(10, "d1w_dq", 1, 0x28, 0), + CYGNUS_PIN_DESC(11, "d1wowstz_l", 1, 0x4, 28), + CYGNUS_PIN_DESC(12, "gpio0", 0, 0, 0), + CYGNUS_PIN_DESC(13, "gpio1", 0, 0, 0), + CYGNUS_PIN_DESC(14, "gpio2", 0, 0, 0), + CYGNUS_PIN_DESC(15, "gpio3", 0, 0, 0), + CYGNUS_PIN_DESC(16, "gpio4", 0, 0, 0), + CYGNUS_PIN_DESC(17, "gpio5", 0, 0, 0), + CYGNUS_PIN_DESC(18, "gpio6", 0, 0, 0), + CYGNUS_PIN_DESC(19, "gpio7", 0, 0, 0), + CYGNUS_PIN_DESC(20, "gpio8", 0, 0, 0), + CYGNUS_PIN_DESC(21, "gpio9", 0, 0, 0), + CYGNUS_PIN_DESC(22, "gpio10", 0, 0, 0), + CYGNUS_PIN_DESC(23, "gpio11", 0, 0, 0), + CYGNUS_PIN_DESC(24, "gpio12", 0, 0, 0), + CYGNUS_PIN_DESC(25, "gpio13", 0, 0, 0), + CYGNUS_PIN_DESC(26, "gpio14", 0, 0, 0), + CYGNUS_PIN_DESC(27, "gpio15", 0, 0, 0), + CYGNUS_PIN_DESC(28, "gpio16", 0, 0, 0), + CYGNUS_PIN_DESC(29, "gpio17", 0, 0, 0), + CYGNUS_PIN_DESC(30, "gpio18", 0, 0, 0), + CYGNUS_PIN_DESC(31, "gpio19", 0, 0, 0), + CYGNUS_PIN_DESC(32, "gpio20", 0, 0, 0), + CYGNUS_PIN_DESC(33, "gpio21", 0, 0, 0), + CYGNUS_PIN_DESC(34, "gpio22", 0, 0, 0), + CYGNUS_PIN_DESC(35, "gpio23", 0, 0, 0), + CYGNUS_PIN_DESC(36, "mdc", 0, 0, 0), + CYGNUS_PIN_DESC(37, "mdio", 0, 0, 0), + CYGNUS_PIN_DESC(38, "pwm0", 1, 0x10, 30), + CYGNUS_PIN_DESC(39, "pwm1", 1, 0x10, 28), + CYGNUS_PIN_DESC(40, "pwm2", 1, 0x10, 26), + CYGNUS_PIN_DESC(41, "pwm3", 1, 0x10, 24), + CYGNUS_PIN_DESC(42, "sc0_clk", 1, 0x10, 22), + CYGNUS_PIN_DESC(43, "sc0_cmdvcc_l", 1, 0x10, 20), + CYGNUS_PIN_DESC(44, "sc0_detect", 1, 0x10, 18), + CYGNUS_PIN_DESC(45, "sc0_fcb", 1, 0x10, 16), + CYGNUS_PIN_DESC(46, "sc0_io", 1, 0x10, 14), + CYGNUS_PIN_DESC(47, "sc0_rst_l", 1, 0x10, 12), + CYGNUS_PIN_DESC(48, "sc1_clk", 1, 0x10, 10), + CYGNUS_PIN_DESC(49, "sc1_cmdvcc_l", 1, 0x10, 8), + CYGNUS_PIN_DESC(50, "sc1_detect", 1, 0x10, 6), + CYGNUS_PIN_DESC(51, "sc1_fcb", 1, 0x10, 4), + CYGNUS_PIN_DESC(52, "sc1_io", 1, 0x10, 2), + CYGNUS_PIN_DESC(53, "sc1_rst_l", 1, 0x10, 0), + CYGNUS_PIN_DESC(54, "spi0_clk", 1, 0x18, 10), + CYGNUS_PIN_DESC(55, "spi0_mosi", 1, 0x18, 6), + CYGNUS_PIN_DESC(56, "spi0_miso", 1, 0x18, 8), + CYGNUS_PIN_DESC(57, "spi0_ss", 1, 0x18, 4), + CYGNUS_PIN_DESC(58, "spi1_clk", 1, 0x18, 2), + CYGNUS_PIN_DESC(59, "spi1_mosi", 1, 0x1c, 30), + CYGNUS_PIN_DESC(60, "spi1_miso", 1, 0x18, 0), + CYGNUS_PIN_DESC(61, "spi1_ss", 1, 0x1c, 28), + CYGNUS_PIN_DESC(62, "spi2_clk", 1, 0x1c, 26), + CYGNUS_PIN_DESC(63, "spi2_mosi", 1, 0x1c, 22), + CYGNUS_PIN_DESC(64, "spi2_miso", 1, 0x1c, 24), + CYGNUS_PIN_DESC(65, "spi2_ss", 1, 0x1c, 20), + CYGNUS_PIN_DESC(66, "spi3_clk", 1, 0x1c, 18), + CYGNUS_PIN_DESC(67, "spi3_mosi", 1, 0x1c, 14), + CYGNUS_PIN_DESC(68, "spi3_miso", 1, 0x1c, 16), + CYGNUS_PIN_DESC(69, "spi3_ss", 1, 0x1c, 12), + CYGNUS_PIN_DESC(70, "uart0_cts", 1, 0x1c, 10), + CYGNUS_PIN_DESC(71, "uart0_rts", 1, 0x1c, 8), + CYGNUS_PIN_DESC(72, "uart0_rx", 1, 0x1c, 6), + CYGNUS_PIN_DESC(73, "uart0_tx", 1, 0x1c, 4), + CYGNUS_PIN_DESC(74, "uart1_cts", 1, 0x1c, 2), + CYGNUS_PIN_DESC(75, "uart1_dcd", 1, 0x1c, 0), + CYGNUS_PIN_DESC(76, "uart1_dsr", 1, 0x20, 14), + CYGNUS_PIN_DESC(77, "uart1_dtr", 1, 0x20, 12), + CYGNUS_PIN_DESC(78, "uart1_ri", 1, 0x20, 10), + CYGNUS_PIN_DESC(79, "uart1_rts", 1, 0x20, 8), + CYGNUS_PIN_DESC(80, "uart1_rx", 1, 0x20, 6), + CYGNUS_PIN_DESC(81, "uart1_tx", 1, 0x20, 4), + CYGNUS_PIN_DESC(82, "uart3_rx", 1, 0x20, 2), + CYGNUS_PIN_DESC(83, "uart3_tx", 1, 0x20, 0), + CYGNUS_PIN_DESC(84, "sdio1_clk_sdcard", 1, 0x14, 6), + CYGNUS_PIN_DESC(85, "sdio1_cmd", 1, 0x14, 4), + CYGNUS_PIN_DESC(86, "sdio1_data0", 1, 0x14, 2), + CYGNUS_PIN_DESC(87, "sdio1_data1", 1, 0x14, 0), + CYGNUS_PIN_DESC(88, "sdio1_data2", 1, 0x18, 30), + CYGNUS_PIN_DESC(89, "sdio1_data3", 1, 0x18, 28), + CYGNUS_PIN_DESC(90, "sdio1_wp_n", 1, 0x18, 24), + CYGNUS_PIN_DESC(91, "sdio1_card_rst", 1, 0x14, 10), + CYGNUS_PIN_DESC(92, "sdio1_led_on", 1, 0x18, 26), + CYGNUS_PIN_DESC(93, "sdio1_cd", 1, 0x14, 8), + CYGNUS_PIN_DESC(94, "sdio0_clk_sdcard", 1, 0x14, 26), + CYGNUS_PIN_DESC(95, "sdio0_cmd", 1, 0x14, 24), + CYGNUS_PIN_DESC(96, "sdio0_data0", 1, 0x14, 22), + CYGNUS_PIN_DESC(97, "sdio0_data1", 1, 0x14, 20), + CYGNUS_PIN_DESC(98, "sdio0_data2", 1, 0x14, 18), + CYGNUS_PIN_DESC(99, "sdio0_data3", 1, 0x14, 16), + CYGNUS_PIN_DESC(100, "sdio0_wp_n", 1, 0x14, 12), + CYGNUS_PIN_DESC(101, "sdio0_card_rst", 1, 0x14, 30), + CYGNUS_PIN_DESC(102, "sdio0_led_on", 1, 0x14, 14), + CYGNUS_PIN_DESC(103, "sdio0_cd", 1, 0x14, 28), + CYGNUS_PIN_DESC(104, "sflash_clk", 1, 0x18, 22), + CYGNUS_PIN_DESC(105, "sflash_cs_l", 1, 0x18, 20), + CYGNUS_PIN_DESC(106, "sflash_mosi", 1, 0x18, 14), + CYGNUS_PIN_DESC(107, "sflash_miso", 1, 0x18, 16), + CYGNUS_PIN_DESC(108, "sflash_wp_n", 1, 0x18, 12), + CYGNUS_PIN_DESC(109, "sflash_hold_n", 1, 0x18, 18), + CYGNUS_PIN_DESC(110, "nand_ale", 1, 0xc, 30), + CYGNUS_PIN_DESC(111, "nand_ce0_l", 1, 0xc, 28), + CYGNUS_PIN_DESC(112, "nand_ce1_l", 1, 0xc, 26), + CYGNUS_PIN_DESC(113, "nand_cle", 1, 0xc, 24), + CYGNUS_PIN_DESC(114, "nand_dq0", 1, 0xc, 22), + CYGNUS_PIN_DESC(115, "nand_dq1", 1, 0xc, 20), + CYGNUS_PIN_DESC(116, "nand_dq2", 1, 0xc, 18), + CYGNUS_PIN_DESC(117, "nand_dq3", 1, 0xc, 16), + CYGNUS_PIN_DESC(118, "nand_dq4", 1, 0xc, 14), + CYGNUS_PIN_DESC(119, "nand_dq5", 1, 0xc, 12), + CYGNUS_PIN_DESC(120, "nand_dq6", 1, 0xc, 10), + CYGNUS_PIN_DESC(121, "nand_dq7", 1, 0xc, 8), + CYGNUS_PIN_DESC(122, "nand_rb_l", 1, 0xc, 6), + CYGNUS_PIN_DESC(123, "nand_re_l", 1, 0xc, 4), + CYGNUS_PIN_DESC(124, "nand_we_l", 1, 0xc, 2), + CYGNUS_PIN_DESC(125, "nand_wp_l", 1, 0xc, 0), + CYGNUS_PIN_DESC(126, "lcd_clac", 1, 0x4, 26), + CYGNUS_PIN_DESC(127, "lcd_clcp", 1, 0x4, 24), + CYGNUS_PIN_DESC(128, "lcd_cld0", 1, 0x4, 22), + CYGNUS_PIN_DESC(129, "lcd_cld1", 1, 0x4, 0), + CYGNUS_PIN_DESC(130, "lcd_cld10", 1, 0x4, 20), + CYGNUS_PIN_DESC(131, "lcd_cld11", 1, 0x4, 18), + CYGNUS_PIN_DESC(132, "lcd_cld12", 1, 0x4, 16), + CYGNUS_PIN_DESC(133, "lcd_cld13", 1, 0x4, 14), + CYGNUS_PIN_DESC(134, "lcd_cld14", 1, 0x4, 12), + CYGNUS_PIN_DESC(135, "lcd_cld15", 1, 0x4, 10), + CYGNUS_PIN_DESC(136, "lcd_cld16", 1, 0x4, 8), + CYGNUS_PIN_DESC(137, "lcd_cld17", 1, 0x4, 6), + CYGNUS_PIN_DESC(138, "lcd_cld18", 1, 0x4, 4), + CYGNUS_PIN_DESC(139, "lcd_cld19", 1, 0x4, 2), + CYGNUS_PIN_DESC(140, "lcd_cld2", 1, 0x8, 22), + CYGNUS_PIN_DESC(141, "lcd_cld20", 1, 0x8, 30), + CYGNUS_PIN_DESC(142, "lcd_cld21", 1, 0x8, 28), + CYGNUS_PIN_DESC(143, "lcd_cld22", 1, 0x8, 26), + CYGNUS_PIN_DESC(144, "lcd_cld23", 1, 0x8, 24), + CYGNUS_PIN_DESC(145, "lcd_cld3", 1, 0x8, 20), + CYGNUS_PIN_DESC(146, "lcd_cld4", 1, 0x8, 18), + CYGNUS_PIN_DESC(147, "lcd_cld5", 1, 0x8, 16), + CYGNUS_PIN_DESC(148, "lcd_cld6", 1, 0x8, 14), + CYGNUS_PIN_DESC(149, "lcd_cld7", 1, 0x8, 12), + CYGNUS_PIN_DESC(150, "lcd_cld8", 1, 0x8, 10), + CYGNUS_PIN_DESC(151, "lcd_cld9", 1, 0x8, 8), + CYGNUS_PIN_DESC(152, "lcd_clfp", 1, 0x8, 6), + CYGNUS_PIN_DESC(153, "lcd_clle", 1, 0x8, 4), + CYGNUS_PIN_DESC(154, "lcd_cllp", 1, 0x8, 2), + CYGNUS_PIN_DESC(155, "lcd_clpower", 1, 0x8, 0), + CYGNUS_PIN_DESC(156, "camera_vsync", 1, 0x4, 30), + CYGNUS_PIN_DESC(157, "camera_trigger", 1, 0x0, 0), + CYGNUS_PIN_DESC(158, "camera_strobe", 1, 0x0, 2), + CYGNUS_PIN_DESC(159, "camera_standby", 1, 0x0, 4), + CYGNUS_PIN_DESC(160, "camera_reset_n", 1, 0x0, 6), + CYGNUS_PIN_DESC(161, "camera_pixdata9", 1, 0x0, 8), + CYGNUS_PIN_DESC(162, "camera_pixdata8", 1, 0x0, 10), + CYGNUS_PIN_DESC(163, "camera_pixdata7", 1, 0x0, 12), + CYGNUS_PIN_DESC(164, "camera_pixdata6", 1, 0x0, 14), + CYGNUS_PIN_DESC(165, "camera_pixdata5", 1, 0x0, 16), + CYGNUS_PIN_DESC(166, "camera_pixdata4", 1, 0x0, 18), + CYGNUS_PIN_DESC(167, "camera_pixdata3", 1, 0x0, 20), + CYGNUS_PIN_DESC(168, "camera_pixdata2", 1, 0x0, 22), + CYGNUS_PIN_DESC(169, "camera_pixdata1", 1, 0x0, 24), + CYGNUS_PIN_DESC(170, "camera_pixdata0", 1, 0x0, 26), + CYGNUS_PIN_DESC(171, "camera_pixclk", 1, 0x0, 28), + CYGNUS_PIN_DESC(172, "camera_hsync", 1, 0x0, 30), + CYGNUS_PIN_DESC(173, "camera_pll_ref_clk", 0, 0, 0), + CYGNUS_PIN_DESC(174, "usb_id_indication", 0, 0, 0), + CYGNUS_PIN_DESC(175, "usb_vbus_indication", 0, 0, 0), + CYGNUS_PIN_DESC(176, "gpio0_3p3", 0, 0, 0), + CYGNUS_PIN_DESC(177, "gpio1_3p3", 0, 0, 0), + CYGNUS_PIN_DESC(178, "gpio2_3p3", 0, 0, 0), + CYGNUS_PIN_DESC(179, "gpio3_3p3", 0, 0, 0), +}; + +/* + * List of groups of pins + */ +static const unsigned bsc1_pins[] = { 8, 9 }; +static const unsigned pcie_clkreq_pins[] = { 8, 9 }; + +static const unsigned i2s2_0_pins[] = { 12 }; +static const unsigned i2s2_1_pins[] = { 13 }; +static const unsigned i2s2_2_pins[] = { 14 }; +static const unsigned i2s2_3_pins[] = { 15 }; +static const unsigned i2s2_4_pins[] = { 16 }; + +static const unsigned pwm4_pins[] = { 17 }; +static const unsigned pwm5_pins[] = { 18 }; + +static const unsigned key0_pins[] = { 20 }; +static const unsigned key1_pins[] = { 21 }; +static const unsigned key2_pins[] = { 22 }; +static const unsigned key3_pins[] = { 23 }; +static const unsigned key4_pins[] = { 24 }; +static const unsigned key5_pins[] = { 25 }; + +static const unsigned key6_pins[] = { 26 }; +static const unsigned audio_dte0_pins[] = { 26 }; + +static const unsigned key7_pins[] = { 27 }; +static const unsigned audio_dte1_pins[] = { 27 }; + +static const unsigned key8_pins[] = { 28 }; +static const unsigned key9_pins[] = { 29 }; +static const unsigned key10_pins[] = { 30 }; +static const unsigned key11_pins[] = { 31 }; +static const unsigned key12_pins[] = { 32 }; +static const unsigned key13_pins[] = { 33 }; + +static const unsigned key14_pins[] = { 34 }; +static const unsigned audio_dte2_pins[] = { 34 }; + +static const unsigned key15_pins[] = { 35 }; +static const unsigned audio_dte3_pins[] = { 35 }; + +static const unsigned pwm0_pins[] = { 38 }; +static const unsigned pwm1_pins[] = { 39 }; +static const unsigned pwm2_pins[] = { 40 }; +static const unsigned pwm3_pins[] = { 41 }; + +static const unsigned sdio0_pins[] = { 94, 95, 96, 97, 98, 99 }; + +static const unsigned smart_card0_pins[] = { 42, 43, 44, 46, 47 }; +static const unsigned i2s0_0_pins[] = { 42, 43, 44, 46 }; +static const unsigned spdif_pins[] = { 47 }; + +static const unsigned smart_card1_pins[] = { 48, 49, 50, 52, 53 }; +static const unsigned i2s1_0_pins[] = { 48, 49, 50, 52 }; + +static const unsigned spi0_pins[] = { 54, 55, 56, 57 }; + +static const unsigned spi1_pins[] = { 58, 59, 60, 61 }; + +static const unsigned spi2_pins[] = { 62, 63, 64, 65 }; + +static const unsigned spi3_pins[] = { 66, 67, 68, 69 }; +static const unsigned sw_led0_0_pins[] = { 66, 67, 68, 69 }; + +static const unsigned d1w_pins[] = { 10, 11 }; +static const unsigned uart4_pins[] = { 10, 11 }; +static const unsigned sw_led2_0_pins[] = { 10, 11 }; + +static const unsigned lcd_pins[] = { 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155 }; +static const unsigned sram_0_pins[] = { 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155 }; +static const unsigned spi5_pins[] = { 141, 142, 143, 144 }; + +static const unsigned uart0_pins[] = { 70, 71, 72, 73 }; +static const unsigned sw_led0_1_pins[] = { 70, 71, 72, 73 }; + +static const unsigned uart1_dte_pins[] = { 75, 76, 77, 78 }; +static const unsigned uart2_pins[] = { 75, 76, 77, 78 }; + +static const unsigned uart1_pins[] = { 74, 79, 80, 81 }; + +static const unsigned uart3_pins[] = { 82, 83 }; + +static const unsigned qspi_0_pins[] = { 104, 105, 106, 107 }; + +static const unsigned nand_pins[] = { 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125 }; + +static const unsigned sdio0_cd_pins[] = { 103 }; + +static const unsigned sdio0_mmc_pins[] = { 100, 101, 102 }; + +static const unsigned sdio1_data_0_pins[] = { 86, 87 }; +static const unsigned can0_pins[] = { 86, 87 }; +static const unsigned spi4_0_pins[] = { 86, 87 }; + +static const unsigned sdio1_data_1_pins[] = { 88, 89 }; +static const unsigned can1_pins[] = { 88, 89 }; +static const unsigned spi4_1_pins[] = { 88, 89 }; + +static const unsigned sdio1_cd_pins[] = { 93 }; + +static const unsigned sdio1_led_pins[] = { 84, 85 }; +static const unsigned sw_led2_1_pins[] = { 84, 85 }; + +static const unsigned sdio1_mmc_pins[] = { 90, 91, 92 }; + +static const unsigned cam_led_pins[] = { 156, 157, 158, 159, 160 }; +static const unsigned sw_led1_pins[] = { 156, 157, 158, 159 }; + +static const unsigned cam_0_pins[] = { 169, 170, 171, 169, 170 }; + +static const unsigned cam_1_pins[] = { 161, 162, 163, 164, 165, 166, 167, + 168 }; +static const unsigned sram_1_pins[] = { 161, 162, 163, 164, 165, 166, 167, + 168 }; + +static const unsigned qspi_1_pins[] = { 108, 109 }; + +static const unsigned smart_card0_fcb_pins[] = { 45 }; +static const unsigned i2s0_1_pins[] = { 45 }; + +static const unsigned smart_card1_fcb_pins[] = { 51 }; +static const unsigned i2s1_1_pins[] = { 51 }; + +static const unsigned gpio0_3p3_pins[] = { 176 }; +static const unsigned usb0_oc_pins[] = { 176 }; + +static const unsigned gpio1_3p3_pins[] = { 177 }; +static const unsigned usb1_oc_pins[] = { 177 }; + +static const unsigned gpio2_3p3_pins[] = { 178 }; +static const unsigned usb2_oc_pins[] = { 178 }; + +#define CYGNUS_PIN_GROUP(group_name, off, sh, al) \ +{ \ + .name = __stringify(group_name) "_grp", \ + .pins = group_name ## _pins, \ + .num_pins = ARRAY_SIZE(group_name ## _pins), \ + .mux = { \ + .offset = off, \ + .shift = sh, \ + .alt = al, \ + } \ +} + +/* + * List of Cygnus pin groups + */ +static const struct cygnus_pin_group cygnus_pin_groups[] = { + CYGNUS_PIN_GROUP(i2s2_0, 0x0, 0, 2), + CYGNUS_PIN_GROUP(i2s2_1, 0x0, 4, 2), + CYGNUS_PIN_GROUP(i2s2_2, 0x0, 8, 2), + CYGNUS_PIN_GROUP(i2s2_3, 0x0, 12, 2), + CYGNUS_PIN_GROUP(i2s2_4, 0x0, 16, 2), + CYGNUS_PIN_GROUP(pwm4, 0x0, 20, 0), + CYGNUS_PIN_GROUP(pwm5, 0x0, 24, 2), + CYGNUS_PIN_GROUP(key0, 0x4, 0, 1), + CYGNUS_PIN_GROUP(key1, 0x4, 4, 1), + CYGNUS_PIN_GROUP(key2, 0x4, 8, 1), + CYGNUS_PIN_GROUP(key3, 0x4, 12, 1), + CYGNUS_PIN_GROUP(key4, 0x4, 16, 1), + CYGNUS_PIN_GROUP(key5, 0x4, 20, 1), + CYGNUS_PIN_GROUP(key6, 0x4, 24, 1), + CYGNUS_PIN_GROUP(audio_dte0, 0x4, 24, 2), + CYGNUS_PIN_GROUP(key7, 0x4, 28, 1), + CYGNUS_PIN_GROUP(audio_dte1, 0x4, 28, 2), + CYGNUS_PIN_GROUP(key8, 0x8, 0, 1), + CYGNUS_PIN_GROUP(key9, 0x8, 4, 1), + CYGNUS_PIN_GROUP(key10, 0x8, 8, 1), + CYGNUS_PIN_GROUP(key11, 0x8, 12, 1), + CYGNUS_PIN_GROUP(key12, 0x8, 16, 1), + CYGNUS_PIN_GROUP(key13, 0x8, 20, 1), + CYGNUS_PIN_GROUP(key14, 0x8, 24, 1), + CYGNUS_PIN_GROUP(audio_dte2, 0x8, 24, 2), + CYGNUS_PIN_GROUP(key15, 0x8, 28, 1), + CYGNUS_PIN_GROUP(audio_dte3, 0x8, 28, 2), + CYGNUS_PIN_GROUP(pwm0, 0xc, 0, 0), + CYGNUS_PIN_GROUP(pwm1, 0xc, 4, 0), + CYGNUS_PIN_GROUP(pwm2, 0xc, 8, 0), + CYGNUS_PIN_GROUP(pwm3, 0xc, 12, 0), + CYGNUS_PIN_GROUP(sdio0, 0xc, 16, 0), + CYGNUS_PIN_GROUP(smart_card0, 0xc, 20, 0), + CYGNUS_PIN_GROUP(i2s0_0, 0xc, 20, 1), + CYGNUS_PIN_GROUP(spdif, 0xc, 20, 1), + CYGNUS_PIN_GROUP(smart_card1, 0xc, 24, 0), + CYGNUS_PIN_GROUP(i2s1_0, 0xc, 24, 1), + CYGNUS_PIN_GROUP(spi0, 0x10, 0, 0), + CYGNUS_PIN_GROUP(spi1, 0x10, 4, 0), + CYGNUS_PIN_GROUP(spi2, 0x10, 8, 0), + CYGNUS_PIN_GROUP(spi3, 0x10, 12, 0), + CYGNUS_PIN_GROUP(sw_led0_0, 0x10, 12, 2), + CYGNUS_PIN_GROUP(d1w, 0x10, 16, 0), + CYGNUS_PIN_GROUP(uart4, 0x10, 16, 1), + CYGNUS_PIN_GROUP(sw_led2_0, 0x10, 16, 2), + CYGNUS_PIN_GROUP(lcd, 0x10, 20, 0), + CYGNUS_PIN_GROUP(sram_0, 0x10, 20, 1), + CYGNUS_PIN_GROUP(spi5, 0x10, 20, 2), + CYGNUS_PIN_GROUP(uart0, 0x14, 0, 0), + CYGNUS_PIN_GROUP(sw_led0_1, 0x14, 0, 2), + CYGNUS_PIN_GROUP(uart1_dte, 0x14, 4, 0), + CYGNUS_PIN_GROUP(uart2, 0x14, 4, 1), + CYGNUS_PIN_GROUP(uart1, 0x14, 8, 0), + CYGNUS_PIN_GROUP(uart3, 0x14, 12, 0), + CYGNUS_PIN_GROUP(qspi_0, 0x14, 16, 0), + CYGNUS_PIN_GROUP(nand, 0x14, 20, 0), + CYGNUS_PIN_GROUP(sdio0_cd, 0x18, 0, 0), + CYGNUS_PIN_GROUP(sdio0_mmc, 0x18, 4, 0), + CYGNUS_PIN_GROUP(sdio1_data_0, 0x18, 8, 0), + CYGNUS_PIN_GROUP(can0, 0x18, 8, 1), + CYGNUS_PIN_GROUP(spi4_0, 0x18, 8, 2), + CYGNUS_PIN_GROUP(sdio1_data_1, 0x18, 12, 0), + CYGNUS_PIN_GROUP(can1, 0x18, 12, 1), + CYGNUS_PIN_GROUP(spi4_1, 0x18, 12, 2), + CYGNUS_PIN_GROUP(sdio1_cd, 0x18, 16, 0), + CYGNUS_PIN_GROUP(sdio1_led, 0x18, 20, 0), + CYGNUS_PIN_GROUP(sw_led2_1, 0x18, 20, 2), + CYGNUS_PIN_GROUP(sdio1_mmc, 0x18, 24, 0), + CYGNUS_PIN_GROUP(cam_led, 0x1c, 0, 0), + CYGNUS_PIN_GROUP(sw_led1, 0x1c, 0, 1), + CYGNUS_PIN_GROUP(cam_0, 0x1c, 4, 0), + CYGNUS_PIN_GROUP(cam_1, 0x1c, 8, 0), + CYGNUS_PIN_GROUP(sram_1, 0x1c, 8, 1), + CYGNUS_PIN_GROUP(qspi_1, 0x1c, 12, 0), + CYGNUS_PIN_GROUP(bsc1, 0x1c, 16, 0), + CYGNUS_PIN_GROUP(pcie_clkreq, 0x1c, 16, 1), + CYGNUS_PIN_GROUP(smart_card0_fcb, 0x20, 0, 0), + CYGNUS_PIN_GROUP(i2s0_1, 0x20, 0, 1), + CYGNUS_PIN_GROUP(smart_card1_fcb, 0x20, 4, 0), + CYGNUS_PIN_GROUP(i2s1_1, 0x20, 4, 1), + CYGNUS_PIN_GROUP(gpio0_3p3, 0x28, 0, 0), + CYGNUS_PIN_GROUP(usb0_oc, 0x28, 0, 1), + CYGNUS_PIN_GROUP(gpio1_3p3, 0x28, 4, 0), + CYGNUS_PIN_GROUP(usb1_oc, 0x28, 4, 1), + CYGNUS_PIN_GROUP(gpio2_3p3, 0x28, 8, 0), + CYGNUS_PIN_GROUP(usb2_oc, 0x28, 8, 1), +}; + +/* + * List of groups supported by functions + */ +static const char * const i2s0_grps[] = { "i2s0_0_grp", "i2s0_1_grp" }; +static const char * const i2s1_grps[] = { "i2s1_0_grp", "i2s1_1_grp" }; +static const char * const i2s2_grps[] = { "i2s2_0_grp", "i2s2_1_grp", + "i2s2_2_grp", "i2s2_3_grp", "i2s2_4_grp" }; +static const char * const spdif_grps[] = { "spdif_grp" }; +static const char * const pwm0_grps[] = { "pwm0_grp" }; +static const char * const pwm1_grps[] = { "pwm1_grp" }; +static const char * const pwm2_grps[] = { "pwm2_grp" }; +static const char * const pwm3_grps[] = { "pwm3_grp" }; +static const char * const pwm4_grps[] = { "pwm4_grp" }; +static const char * const pwm5_grps[] = { "pwm5_grp" }; +static const char * const key_grps[] = { "key0_grp", "key1_grp", "key2_grp", + "key3_grp", "key4_grp", "key5_grp", "key6_grp", "key7_grp", "key8_grp", + "key9_grp", "key10_grp", "key11_grp", "key12_grp", "key13_grp", + "key14_grp", "key15_grp" }; +static const char * const audio_dte_grps[] = { "audio_dte0_grp", + "audio_dte1_grp", "audio_dte2_grp", "audio_dte3_grp" }; +static const char * const smart_card0_grps[] = { "smart_card0_grp", + "smart_card0_fcb_grp" }; +static const char * const smart_card1_grps[] = { "smart_card1_grp", + "smart_card1_fcb_grp" }; +static const char * const spi0_grps[] = { "spi0_grp" }; +static const char * const spi1_grps[] = { "spi1_grp" }; +static const char * const spi2_grps[] = { "spi2_grp" }; +static const char * const spi3_grps[] = { "spi3_grp" }; +static const char * const spi4_grps[] = { "spi4_0_grp", "spi4_1_grp" }; +static const char * const spi5_grps[] = { "spi5_grp" }; + +static const char * const sw_led0_grps[] = { "sw_led0_0_grp", + "sw_led0_1_grp" }; +static const char * const sw_led1_grps[] = { "sw_led1_grp" }; +static const char * const sw_led2_grps[] = { "sw_led2_0_grp", + "sw_led2_1_grp" }; +static const char * const d1w_grps[] = { "d1w_grp" }; +static const char * const lcd_grps[] = { "lcd_grp" }; +static const char * const sram_grps[] = { "sram_0_grp", "sram_1_grp" }; + +static const char * const uart0_grps[] = { "uart0_grp" }; +static const char * const uart1_grps[] = { "uart1_grp", "uart1_dte_grp" }; +static const char * const uart2_grps[] = { "uart2_grp" }; +static const char * const uart3_grps[] = { "uart3_grp" }; +static const char * const uart4_grps[] = { "uart4_grp" }; +static const char * const qspi_grps[] = { "qspi_0_grp", "qspi_1_grp" }; +static const char * const nand_grps[] = { "nand_grp" }; +static const char * const sdio0_grps[] = { "sdio0_grp", "sdio0_cd_grp", + "sdio0_mmc_grp" }; +static const char * const sdio1_grps[] = { "sdio1_data_0_grp", + "sdio1_data_1_grp", "sdio1_cd_grp", "sdio1_led_grp", "sdio1_mmc_grp" }; +static const char * const can0_grps[] = { "can0_grp" }; +static const char * const can1_grps[] = { "can1_grp" }; +static const char * const cam_grps[] = { "cam_led_grp", "cam_0_grp", + "cam_1_grp" }; +static const char * const bsc1_grps[] = { "bsc1_grp" }; +static const char * const pcie_clkreq_grps[] = { "pcie_clkreq_grp" }; +static const char * const usb0_oc_grps[] = { "usb0_oc_grp" }; +static const char * const usb1_oc_grps[] = { "usb1_oc_grp" }; +static const char * const usb2_oc_grps[] = { "usb2_oc_grp" }; + +#define CYGNUS_PIN_FUNCTION(func) \ +{ \ + .name = #func, \ + .groups = func ## _grps, \ + .num_groups = ARRAY_SIZE(func ## _grps), \ +} + +/* + * List of supported functions in Cygnus + */ +static const struct cygnus_pin_function cygnus_pin_functions[] = { + CYGNUS_PIN_FUNCTION(i2s0), + CYGNUS_PIN_FUNCTION(i2s1), + CYGNUS_PIN_FUNCTION(i2s2), + CYGNUS_PIN_FUNCTION(spdif), + CYGNUS_PIN_FUNCTION(pwm0), + CYGNUS_PIN_FUNCTION(pwm1), + CYGNUS_PIN_FUNCTION(pwm2), + CYGNUS_PIN_FUNCTION(pwm3), + CYGNUS_PIN_FUNCTION(pwm4), + CYGNUS_PIN_FUNCTION(pwm5), + CYGNUS_PIN_FUNCTION(key), + CYGNUS_PIN_FUNCTION(audio_dte), + CYGNUS_PIN_FUNCTION(smart_card0), + CYGNUS_PIN_FUNCTION(smart_card1), + CYGNUS_PIN_FUNCTION(spi0), + CYGNUS_PIN_FUNCTION(spi1), + CYGNUS_PIN_FUNCTION(spi2), + CYGNUS_PIN_FUNCTION(spi3), + CYGNUS_PIN_FUNCTION(spi4), + CYGNUS_PIN_FUNCTION(spi5), + CYGNUS_PIN_FUNCTION(sw_led0), + CYGNUS_PIN_FUNCTION(sw_led1), + CYGNUS_PIN_FUNCTION(sw_led2), + CYGNUS_PIN_FUNCTION(d1w), + CYGNUS_PIN_FUNCTION(lcd), + CYGNUS_PIN_FUNCTION(sram), + CYGNUS_PIN_FUNCTION(uart0), + CYGNUS_PIN_FUNCTION(uart1), + CYGNUS_PIN_FUNCTION(uart2), + CYGNUS_PIN_FUNCTION(uart3), + CYGNUS_PIN_FUNCTION(uart4), + CYGNUS_PIN_FUNCTION(qspi), + CYGNUS_PIN_FUNCTION(nand), + CYGNUS_PIN_FUNCTION(sdio0), + CYGNUS_PIN_FUNCTION(sdio1), + CYGNUS_PIN_FUNCTION(can0), + CYGNUS_PIN_FUNCTION(can1), + CYGNUS_PIN_FUNCTION(cam), + CYGNUS_PIN_FUNCTION(bsc1), + CYGNUS_PIN_FUNCTION(pcie_clkreq), + CYGNUS_PIN_FUNCTION(usb0_oc), + CYGNUS_PIN_FUNCTION(usb1_oc), + CYGNUS_PIN_FUNCTION(usb2_oc), +}; + +static int cygnus_get_groups_count(struct pinctrl_dev *pctrl_dev) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->num_groups; +} + +static const char *cygnus_get_group_name(struct pinctrl_dev *pctrl_dev, + unsigned selector) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->groups[selector].name; +} + +static int cygnus_get_group_pins(struct pinctrl_dev *pctrl_dev, + unsigned selector, const unsigned **pins, + unsigned *num_pins) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + *pins = pinctrl->groups[selector].pins; + *num_pins = pinctrl->groups[selector].num_pins; + + return 0; +} + +static void cygnus_pin_dbg_show(struct pinctrl_dev *pctrl_dev, + struct seq_file *s, unsigned offset) +{ + seq_printf(s, " %s", dev_name(pctrl_dev->dev)); +} + +static const struct pinctrl_ops cygnus_pinctrl_ops = { + .get_groups_count = cygnus_get_groups_count, + .get_group_name = cygnus_get_group_name, + .get_group_pins = cygnus_get_group_pins, + .pin_dbg_show = cygnus_pin_dbg_show, + .dt_node_to_map = pinconf_generic_dt_node_to_map_group, + .dt_free_map = pinctrl_utils_dt_free_map, +}; + +static int cygnus_get_functions_count(struct pinctrl_dev *pctrl_dev) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->num_functions; +} + +static const char *cygnus_get_function_name(struct pinctrl_dev *pctrl_dev, + unsigned selector) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + return pinctrl->functions[selector].name; +} + +static int cygnus_get_function_groups(struct pinctrl_dev *pctrl_dev, + unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + + *groups = pinctrl->functions[selector].groups; + *num_groups = pinctrl->functions[selector].num_groups; + + return 0; +} + +static int cygnus_pinmux_set(struct cygnus_pinctrl *pinctrl, + const struct cygnus_pin_function *func, + const struct cygnus_pin_group *grp, + struct cygnus_mux_log *mux_log) +{ + const struct cygnus_mux *mux = &grp->mux; + int i; + u32 val, mask = 0x7; + unsigned long flags; + + for (i = 0; i < CYGNUS_NUM_IOMUX; i++) { + if (mux->offset != mux_log[i].mux.offset || + mux->shift != mux_log[i].mux.shift) + continue; + + /* match found if we reach here */ + + /* if this is a new configuration, just do it! */ + if (!mux_log[i].is_configured) + break; + + /* + * IOMUX has been configured previously and one is trying to + * configure it to a different function + */ + if (mux_log[i].mux.alt != mux->alt) { + dev_err(pinctrl->dev, + "double configuration error detected!\n"); + dev_err(pinctrl->dev, "func:%s grp:%s\n", + func->name, grp->name); + return -EINVAL; + } else { + /* + * One tries to configure it to the same function. + * Just quit and don't bother + */ + return 0; + } + } + + mux_log[i].mux.alt = mux->alt; + mux_log[i].is_configured = true; + + spin_lock_irqsave(&pinctrl->lock, flags); + + val = readl(pinctrl->base0 + grp->mux.offset); + val &= ~(mask << grp->mux.shift); + val |= grp->mux.alt << grp->mux.shift; + writel(val, pinctrl->base0 + grp->mux.offset); + + spin_unlock_irqrestore(&pinctrl->lock, flags); + + return 0; +} + +static int cygnus_pinmux_set_mux(struct pinctrl_dev *pctrl_dev, + unsigned func_select, unsigned grp_select) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + const struct cygnus_pin_function *func = + &pinctrl->functions[func_select]; + const struct cygnus_pin_group *grp = &pinctrl->groups[grp_select]; + + dev_dbg(pctrl_dev->dev, "func:%u name:%s grp:%u name:%s\n", + func_select, func->name, grp_select, grp->name); + + dev_dbg(pctrl_dev->dev, "offset:0x%08x shift:%u alt:%u\n", + grp->mux.offset, grp->mux.shift, grp->mux.alt); + + return cygnus_pinmux_set(pinctrl, func, grp, pinctrl->mux_log); +} + +static int cygnus_gpio_request_enable(struct pinctrl_dev *pctrl_dev, + struct pinctrl_gpio_range *range, + unsigned pin) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + const struct cygnus_gpio_mux *mux = pctrl_dev->desc->pins[pin].drv_data; + u32 val; + unsigned long flags; + + /* not all pins support GPIO pinmux override */ + if (!mux->is_supported) + return -ENOTSUPP; + + spin_lock_irqsave(&pinctrl->lock, flags); + + val = readl(pinctrl->base1 + mux->offset); + val |= 0x3 << mux->shift; + writel(val, pinctrl->base1 + mux->offset); + + spin_unlock_irqrestore(&pinctrl->lock, flags); + + dev_dbg(pctrl_dev->dev, + "gpio request enable pin=%u offset=0x%x shift=%u\n", + pin, mux->offset, mux->shift); + + return 0; +} + +static void cygnus_gpio_disable_free(struct pinctrl_dev *pctrl_dev, + struct pinctrl_gpio_range *range, + unsigned pin) +{ + struct cygnus_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctrl_dev); + struct cygnus_gpio_mux *mux = pctrl_dev->desc->pins[pin].drv_data; + u32 val; + unsigned long flags; + + if (!mux->is_supported) + return; + + spin_lock_irqsave(&pinctrl->lock, flags); + + val = readl(pinctrl->base1 + mux->offset); + val &= ~(0x3 << mux->shift); + writel(val, pinctrl->base1 + mux->offset); + + spin_unlock_irqrestore(&pinctrl->lock, flags); + + dev_err(pctrl_dev->dev, + "gpio disable free pin=%u offset=0x%x shift=%u\n", + pin, mux->offset, mux->shift); +} + +static const struct pinmux_ops cygnus_pinmux_ops = { + .get_functions_count = cygnus_get_functions_count, + .get_function_name = cygnus_get_function_name, + .get_function_groups = cygnus_get_function_groups, + .set_mux = cygnus_pinmux_set_mux, + .gpio_request_enable = cygnus_gpio_request_enable, + .gpio_disable_free = cygnus_gpio_disable_free, +}; + +static struct pinctrl_desc cygnus_pinctrl_desc = { + .name = "cygnus-pinmux", + .pctlops = &cygnus_pinctrl_ops, + .pmxops = &cygnus_pinmux_ops, +}; + +static int cygnus_mux_log_init(struct cygnus_pinctrl *pinctrl) +{ + struct cygnus_mux_log *log; + unsigned int i, j; + + pinctrl->mux_log = devm_kcalloc(pinctrl->dev, CYGNUS_NUM_IOMUX, + sizeof(struct cygnus_mux_log), + GFP_KERNEL); + if (!pinctrl->mux_log) + return -ENOMEM; + + log = pinctrl->mux_log; + for (i = 0; i < CYGNUS_NUM_IOMUX_REGS; i++) { + for (j = 0; j < CYGNUS_NUM_MUX_PER_REG; j++) { + log = &pinctrl->mux_log[i * CYGNUS_NUM_MUX_PER_REG + + j]; + log->mux.offset = i * 4; + log->mux.shift = j * 4; + log->mux.alt = 0; + log->is_configured = false; + } + } + + return 0; +} + +static int cygnus_pinmux_probe(struct platform_device *pdev) +{ + struct cygnus_pinctrl *pinctrl; + struct resource *res; + int i, ret; + struct pinctrl_pin_desc *pins; + unsigned num_pins = ARRAY_SIZE(cygnus_pins); + + pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pinctrl), GFP_KERNEL); + if (!pinctrl) + return -ENOMEM; + + pinctrl->dev = &pdev->dev; + platform_set_drvdata(pdev, pinctrl); + spin_lock_init(&pinctrl->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pinctrl->base0 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pinctrl->base0)) { + dev_err(&pdev->dev, "unable to map I/O space\n"); + return PTR_ERR(pinctrl->base0); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + pinctrl->base1 = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pinctrl->base1)) { + dev_err(&pdev->dev, "unable to map I/O space\n"); + return PTR_ERR(pinctrl->base1); + } + + ret = cygnus_mux_log_init(pinctrl); + if (ret) { + dev_err(&pdev->dev, "unable to initialize IOMUX log\n"); + return ret; + } + + pins = devm_kcalloc(&pdev->dev, num_pins, sizeof(*pins), GFP_KERNEL); + if (!pins) + return -ENOMEM; + + for (i = 0; i < num_pins; i++) { + pins[i].number = cygnus_pins[i].pin; + pins[i].name = cygnus_pins[i].name; + pins[i].drv_data = &cygnus_pins[i].gpio_mux; + } + + pinctrl->groups = cygnus_pin_groups; + pinctrl->num_groups = ARRAY_SIZE(cygnus_pin_groups); + pinctrl->functions = cygnus_pin_functions; + pinctrl->num_functions = ARRAY_SIZE(cygnus_pin_functions); + cygnus_pinctrl_desc.pins = pins; + cygnus_pinctrl_desc.npins = num_pins; + + pinctrl->pctl = pinctrl_register(&cygnus_pinctrl_desc, &pdev->dev, + pinctrl); + if (!pinctrl->pctl) { + dev_err(&pdev->dev, "unable to register Cygnus IOMUX pinctrl\n"); + return -EINVAL; + } + + return 0; +} + +static const struct of_device_id cygnus_pinmux_of_match[] = { + { .compatible = "brcm,cygnus-pinmux" }, + { } +}; + +static struct platform_driver cygnus_pinmux_driver = { + .driver = { + .name = "cygnus-pinmux", + .of_match_table = cygnus_pinmux_of_match, + .suppress_bind_attrs = true, + }, + .probe = cygnus_pinmux_probe, +}; + +static int __init cygnus_pinmux_init(void) +{ + return platform_driver_register(&cygnus_pinmux_driver); +} +arch_initcall(cygnus_pinmux_init); + +MODULE_AUTHOR("Ray Jui "); +MODULE_DESCRIPTION("Broadcom Cygnus IOMUX driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c index 448f10986c2849..e261f1cf85c6c4 100644 --- a/drivers/pinctrl/freescale/pinctrl-imx.c +++ b/drivers/pinctrl/freescale/pinctrl-imx.c @@ -542,10 +542,13 @@ static int imx_pinctrl_parse_groups(struct device_node *np, struct imx_pin_reg *pin_reg; struct imx_pin *pin = &grp->pins[i]; - if (info->flags & SHARE_MUX_CONF_REG) + if (info->flags & SHARE_MUX_CONF_REG) { conf_reg = mux_reg; - else + } else { conf_reg = be32_to_cpu(*list++); + if (!conf_reg) + conf_reg = -1; + } pin_id = mux_reg ? mux_reg / 4 : conf_reg / 4; pin_reg = &info->pin_regs[pin_id]; @@ -645,7 +648,7 @@ int imx_pinctrl_probe(struct platform_device *pdev, { struct imx_pinctrl *ipctl; struct resource *res; - int ret; + int ret, i; if (!info || !info->pins || !info->npins) { dev_err(&pdev->dev, "wrong pinctrl info\n"); @@ -662,7 +665,11 @@ int imx_pinctrl_probe(struct platform_device *pdev, info->npins, GFP_KERNEL); if (!info->pin_regs) return -ENOMEM; - memset(info->pin_regs, 0xff, sizeof(*info->pin_regs) * info->npins); + + for (i = 0; i < info->npins; i++) { + info->pin_regs[i].mux_reg = -1; + info->pin_regs[i].conf_reg = -1; + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ipctl->base = devm_ioremap_resource(&pdev->dev, res); diff --git a/drivers/pinctrl/freescale/pinctrl-vf610.c b/drivers/pinctrl/freescale/pinctrl-vf610.c index fc86276892fd3f..37a037543d2991 100644 --- a/drivers/pinctrl/freescale/pinctrl-vf610.c +++ b/drivers/pinctrl/freescale/pinctrl-vf610.c @@ -302,7 +302,7 @@ static struct imx_pinctrl_soc_info vf610_pinctrl_info = { .flags = SHARE_MUX_CONF_REG, }; -static struct of_device_id vf610_pinctrl_of_match[] = { +static const struct of_device_id vf610_pinctrl_of_match[] = { { .compatible = "fsl,vf610-iomuxc", }, { /* sentinel */ } }; diff --git a/drivers/pinctrl/intel/Kconfig b/drivers/pinctrl/intel/Kconfig index b801d869e91c80..fe5e07db0a9582 100644 --- a/drivers/pinctrl/intel/Kconfig +++ b/drivers/pinctrl/intel/Kconfig @@ -25,3 +25,20 @@ config PINCTRL_CHERRYVIEW help Cherryview/Braswell pinctrl driver provides an interface that allows configuring of SoC pins and using them as GPIOs. + +config PINCTRL_INTEL + tristate + select PINMUX + select PINCONF + select GENERIC_PINCONF + select GPIOLIB + select GPIOLIB_IRQCHIP + +config PINCTRL_SUNRISEPOINT + tristate "Intel Sunrisepoint pinctrl and GPIO driver" + depends on ACPI + select PINCTRL_INTEL + help + Sunrisepoint is the PCH of Intel Skylake. This pinctrl driver + provides an interface that allows configuring of PCH pins and + using them as GPIOs. diff --git a/drivers/pinctrl/intel/Makefile b/drivers/pinctrl/intel/Makefile index 4c210e4139e216..fee756e1255bb3 100644 --- a/drivers/pinctrl/intel/Makefile +++ b/drivers/pinctrl/intel/Makefile @@ -2,3 +2,5 @@ obj-$(CONFIG_PINCTRL_BAYTRAIL) += pinctrl-baytrail.o obj-$(CONFIG_PINCTRL_CHERRYVIEW) += pinctrl-cherryview.o +obj-$(CONFIG_PINCTRL_INTEL) += pinctrl-intel.o +obj-$(CONFIG_PINCTRL_SUNRISEPOINT) += pinctrl-sunrisepoint.o diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c new file mode 100644 index 00000000000000..00768e53deec00 --- /dev/null +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -0,0 +1,1149 @@ +/* + * Intel pinctrl/GPIO core driver. + * + * Copyright (C) 2015, Intel Corporation + * Authors: Mathias Nyman + * Mika Westerberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-intel.h" + +/* Maximum number of pads in each group */ +#define NPADS_IN_GPP 24 + +/* Offset from regs */ +#define PADBAR 0x00c +#define GPI_IS 0x100 +#define GPI_GPE_STS 0x140 +#define GPI_GPE_EN 0x160 + +#define PADOWN_BITS 4 +#define PADOWN_SHIFT(p) ((p) % 8 * PADOWN_BITS) +#define PADOWN_MASK(p) (0xf << PADOWN_SHIFT(p)) + +/* Offset from pad_regs */ +#define PADCFG0 0x000 +#define PADCFG0_RXEVCFG_SHIFT 25 +#define PADCFG0_RXEVCFG_MASK (3 << PADCFG0_RXEVCFG_SHIFT) +#define PADCFG0_RXEVCFG_LEVEL 0 +#define PADCFG0_RXEVCFG_EDGE 1 +#define PADCFG0_RXEVCFG_DISABLED 2 +#define PADCFG0_RXEVCFG_EDGE_BOTH 3 +#define PADCFG0_RXINV BIT(23) +#define PADCFG0_GPIROUTIOXAPIC BIT(20) +#define PADCFG0_GPIROUTSCI BIT(19) +#define PADCFG0_GPIROUTSMI BIT(18) +#define PADCFG0_GPIROUTNMI BIT(17) +#define PADCFG0_PMODE_SHIFT 10 +#define PADCFG0_PMODE_MASK (0xf << PADCFG0_PMODE_SHIFT) +#define PADCFG0_GPIORXDIS BIT(9) +#define PADCFG0_GPIOTXDIS BIT(8) +#define PADCFG0_GPIORXSTATE BIT(1) +#define PADCFG0_GPIOTXSTATE BIT(0) + +#define PADCFG1 0x004 +#define PADCFG1_TERM_UP BIT(13) +#define PADCFG1_TERM_SHIFT 10 +#define PADCFG1_TERM_MASK (7 << PADCFG1_TERM_SHIFT) +#define PADCFG1_TERM_20K 4 +#define PADCFG1_TERM_2K 3 +#define PADCFG1_TERM_5K 2 +#define PADCFG1_TERM_1K 1 + +struct intel_pad_context { + u32 padcfg0; + u32 padcfg1; +}; + +struct intel_community_context { + u32 *intmask; +}; + +struct intel_pinctrl_context { + struct intel_pad_context *pads; + struct intel_community_context *communities; +}; + +/** + * struct intel_pinctrl - Intel pinctrl private structure + * @dev: Pointer to the device structure + * @lock: Lock to serialize register access + * @pctldesc: Pin controller description + * @pctldev: Pointer to the pin controller device + * @chip: GPIO chip in this pin controller + * @soc: SoC/PCH specific pin configuration data + * @communities: All communities in this pin controller + * @ncommunities: Number of communities in this pin controller + * @context: Configuration saved over system sleep + */ +struct intel_pinctrl { + struct device *dev; + spinlock_t lock; + struct pinctrl_desc pctldesc; + struct pinctrl_dev *pctldev; + struct gpio_chip chip; + const struct intel_pinctrl_soc_data *soc; + struct intel_community *communities; + size_t ncommunities; + struct intel_pinctrl_context context; +}; + +#define gpiochip_to_pinctrl(c) container_of(c, struct intel_pinctrl, chip) +#define pin_to_padno(c, p) ((p) - (c)->pin_base) + +static struct intel_community *intel_get_community(struct intel_pinctrl *pctrl, + unsigned pin) +{ + struct intel_community *community; + int i; + + for (i = 0; i < pctrl->ncommunities; i++) { + community = &pctrl->communities[i]; + if (pin >= community->pin_base && + pin < community->pin_base + community->npins) + return community; + } + + dev_warn(pctrl->dev, "failed to find community for pin %u\n", pin); + return NULL; +} + +static void __iomem *intel_get_padcfg(struct intel_pinctrl *pctrl, unsigned pin, + unsigned reg) +{ + const struct intel_community *community; + unsigned padno; + + community = intel_get_community(pctrl, pin); + if (!community) + return NULL; + + padno = pin_to_padno(community, pin); + return community->pad_regs + reg + padno * 8; +} + +static bool intel_pad_owned_by_host(struct intel_pinctrl *pctrl, unsigned pin) +{ + const struct intel_community *community; + unsigned padno, gpp, gpp_offset, offset; + void __iomem *padown; + + community = intel_get_community(pctrl, pin); + if (!community) + return false; + if (!community->padown_offset) + return true; + + padno = pin_to_padno(community, pin); + gpp = padno / NPADS_IN_GPP; + gpp_offset = padno % NPADS_IN_GPP; + offset = community->padown_offset + gpp * 16 + (gpp_offset / 8) * 4; + padown = community->regs + offset; + + return !(readl(padown) & PADOWN_MASK(padno)); +} + +static bool intel_pad_reserved_for_acpi(struct intel_pinctrl *pctrl, + unsigned pin) +{ + const struct intel_community *community; + unsigned padno, gpp, offset; + void __iomem *hostown; + + community = intel_get_community(pctrl, pin); + if (!community) + return true; + if (!community->hostown_offset) + return false; + + padno = pin_to_padno(community, pin); + gpp = padno / NPADS_IN_GPP; + offset = community->hostown_offset + gpp * 4; + hostown = community->regs + offset; + + return !(readl(hostown) & BIT(padno % NPADS_IN_GPP)); +} + +static bool intel_pad_locked(struct intel_pinctrl *pctrl, unsigned pin) +{ + struct intel_community *community; + unsigned padno, gpp, offset; + u32 value; + + community = intel_get_community(pctrl, pin); + if (!community) + return true; + if (!community->padcfglock_offset) + return false; + + padno = pin_to_padno(community, pin); + gpp = padno / NPADS_IN_GPP; + + /* + * If PADCFGLOCK and PADCFGLOCKTX bits are both clear for this pad, + * the pad is considered unlocked. Any other case means that it is + * either fully or partially locked and we don't touch it. + */ + offset = community->padcfglock_offset + gpp * 8; + value = readl(community->regs + offset); + if (value & BIT(pin % NPADS_IN_GPP)) + return true; + + offset = community->padcfglock_offset + 4 + gpp * 8; + value = readl(community->regs + offset); + if (value & BIT(pin % NPADS_IN_GPP)) + return true; + + return false; +} + +static bool intel_pad_usable(struct intel_pinctrl *pctrl, unsigned pin) +{ + return intel_pad_owned_by_host(pctrl, pin) && + !intel_pad_reserved_for_acpi(pctrl, pin) && + !intel_pad_locked(pctrl, pin); +} + +static int intel_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->soc->ngroups; +} + +static const char *intel_get_group_name(struct pinctrl_dev *pctldev, + unsigned group) +{ + struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->soc->groups[group].name; +} + +static int intel_get_group_pins(struct pinctrl_dev *pctldev, unsigned group, + const unsigned **pins, unsigned *npins) +{ + struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + *pins = pctrl->soc->groups[group].pins; + *npins = pctrl->soc->groups[group].npins; + return 0; +} + +static void intel_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, + unsigned pin) +{ + struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + u32 cfg0, cfg1, mode; + bool locked, acpi; + + if (!intel_pad_owned_by_host(pctrl, pin)) { + seq_puts(s, "not available"); + return; + } + + cfg0 = readl(intel_get_padcfg(pctrl, pin, PADCFG0)); + cfg1 = readl(intel_get_padcfg(pctrl, pin, PADCFG1)); + + mode = (cfg0 & PADCFG0_PMODE_MASK) >> PADCFG0_PMODE_SHIFT; + if (!mode) + seq_puts(s, "GPIO "); + else + seq_printf(s, "mode %d ", mode); + + seq_printf(s, "0x%08x 0x%08x", cfg0, cfg1); + + locked = intel_pad_locked(pctrl, pin); + acpi = intel_pad_reserved_for_acpi(pctrl, pin); + + if (locked || acpi) { + seq_puts(s, " ["); + if (locked) { + seq_puts(s, "LOCKED"); + if (acpi) + seq_puts(s, ", "); + } + if (acpi) + seq_puts(s, "ACPI"); + seq_puts(s, "]"); + } +} + +static const struct pinctrl_ops intel_pinctrl_ops = { + .get_groups_count = intel_get_groups_count, + .get_group_name = intel_get_group_name, + .get_group_pins = intel_get_group_pins, + .pin_dbg_show = intel_pin_dbg_show, +}; + +static int intel_get_functions_count(struct pinctrl_dev *pctldev) +{ + struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->soc->nfunctions; +} + +static const char *intel_get_function_name(struct pinctrl_dev *pctldev, + unsigned function) +{ + struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + return pctrl->soc->functions[function].name; +} + +static int intel_get_function_groups(struct pinctrl_dev *pctldev, + unsigned function, + const char * const **groups, + unsigned * const ngroups) +{ + struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + + *groups = pctrl->soc->functions[function].groups; + *ngroups = pctrl->soc->functions[function].ngroups; + return 0; +} + +static int intel_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned function, + unsigned group) +{ + struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + const struct intel_pingroup *grp = &pctrl->soc->groups[group]; + unsigned long flags; + int i; + + spin_lock_irqsave(&pctrl->lock, flags); + + /* + * All pins in the groups needs to be accessible and writable + * before we can enable the mux for this group. + */ + for (i = 0; i < grp->npins; i++) { + if (!intel_pad_usable(pctrl, grp->pins[i])) { + spin_unlock_irqrestore(&pctrl->lock, flags); + return -EBUSY; + } + } + + /* Now enable the mux setting for each pin in the group */ + for (i = 0; i < grp->npins; i++) { + void __iomem *padcfg0; + u32 value; + + padcfg0 = intel_get_padcfg(pctrl, grp->pins[i], PADCFG0); + value = readl(padcfg0); + + value &= ~PADCFG0_PMODE_MASK; + value |= grp->mode << PADCFG0_PMODE_SHIFT; + + writel(value, padcfg0); + } + + spin_unlock_irqrestore(&pctrl->lock, flags); + + return 0; +} + +static int intel_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin) +{ + struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + void __iomem *padcfg0; + unsigned long flags; + u32 value; + + spin_lock_irqsave(&pctrl->lock, flags); + + if (!intel_pad_usable(pctrl, pin)) { + spin_unlock_irqrestore(&pctrl->lock, flags); + return -EBUSY; + } + + padcfg0 = intel_get_padcfg(pctrl, pin, PADCFG0); + /* Put the pad into GPIO mode */ + value = readl(padcfg0) & ~PADCFG0_PMODE_MASK; + /* Disable SCI/SMI/NMI generation */ + value &= ~(PADCFG0_GPIROUTIOXAPIC | PADCFG0_GPIROUTSCI); + value &= ~(PADCFG0_GPIROUTSMI | PADCFG0_GPIROUTNMI); + /* Disable TX buffer and enable RX (this will be input) */ + value &= ~PADCFG0_GPIORXDIS; + value |= PADCFG0_GPIOTXDIS; + writel(value, padcfg0); + + spin_unlock_irqrestore(&pctrl->lock, flags); + + return 0; +} + +static int intel_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned pin, bool input) +{ + struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + void __iomem *padcfg0; + unsigned long flags; + u32 value; + + spin_lock_irqsave(&pctrl->lock, flags); + + padcfg0 = intel_get_padcfg(pctrl, pin, PADCFG0); + + value = readl(padcfg0); + if (input) + value |= PADCFG0_GPIOTXDIS; + else + value &= ~PADCFG0_GPIOTXDIS; + writel(value, padcfg0); + + spin_unlock_irqrestore(&pctrl->lock, flags); + + return 0; +} + +static const struct pinmux_ops intel_pinmux_ops = { + .get_functions_count = intel_get_functions_count, + .get_function_name = intel_get_function_name, + .get_function_groups = intel_get_function_groups, + .set_mux = intel_pinmux_set_mux, + .gpio_request_enable = intel_gpio_request_enable, + .gpio_set_direction = intel_gpio_set_direction, +}; + +static int intel_config_get(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *config) +{ + struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + u32 value, term; + u16 arg = 0; + + if (!intel_pad_owned_by_host(pctrl, pin)) + return -ENOTSUPP; + + value = readl(intel_get_padcfg(pctrl, pin, PADCFG1)); + term = (value & PADCFG1_TERM_MASK) >> PADCFG1_TERM_SHIFT; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + if (term) + return -EINVAL; + break; + + case PIN_CONFIG_BIAS_PULL_UP: + if (!term || !(value & PADCFG1_TERM_UP)) + return -EINVAL; + + switch (term) { + case PADCFG1_TERM_1K: + arg = 1000; + break; + case PADCFG1_TERM_2K: + arg = 2000; + break; + case PADCFG1_TERM_5K: + arg = 5000; + break; + case PADCFG1_TERM_20K: + arg = 20000; + break; + } + + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + if (!term || value & PADCFG1_TERM_UP) + return -EINVAL; + + switch (term) { + case PADCFG1_TERM_5K: + arg = 5000; + break; + case PADCFG1_TERM_20K: + arg = 20000; + break; + } + + break; + + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + return 0; +} + +static int intel_config_set_pull(struct intel_pinctrl *pctrl, unsigned pin, + unsigned long config) +{ + unsigned param = pinconf_to_config_param(config); + unsigned arg = pinconf_to_config_argument(config); + void __iomem *padcfg1; + unsigned long flags; + int ret = 0; + u32 value; + + spin_lock_irqsave(&pctrl->lock, flags); + + padcfg1 = intel_get_padcfg(pctrl, pin, PADCFG1); + value = readl(padcfg1); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + value &= ~(PADCFG1_TERM_MASK | PADCFG1_TERM_UP); + break; + + case PIN_CONFIG_BIAS_PULL_UP: + value &= ~PADCFG1_TERM_MASK; + + value |= PADCFG1_TERM_UP; + + switch (arg) { + case 20000: + value |= PADCFG1_TERM_20K << PADCFG1_TERM_SHIFT; + break; + case 5000: + value |= PADCFG1_TERM_5K << PADCFG1_TERM_SHIFT; + break; + case 2000: + value |= PADCFG1_TERM_2K << PADCFG1_TERM_SHIFT; + break; + case 1000: + value |= PADCFG1_TERM_1K << PADCFG1_TERM_SHIFT; + break; + default: + ret = -EINVAL; + } + + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + value &= ~(PADCFG1_TERM_UP | PADCFG1_TERM_MASK); + + switch (arg) { + case 20000: + value |= PADCFG1_TERM_20K << PADCFG1_TERM_SHIFT; + break; + case 5000: + value |= PADCFG1_TERM_5K << PADCFG1_TERM_SHIFT; + break; + default: + ret = -EINVAL; + } + + break; + } + + if (!ret) + writel(value, padcfg1); + + spin_unlock_irqrestore(&pctrl->lock, flags); + + return ret; +} + +static int intel_config_set(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *configs, unsigned nconfigs) +{ + struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); + int i, ret; + + if (!intel_pad_usable(pctrl, pin)) + return -ENOTSUPP; + + for (i = 0; i < nconfigs; i++) { + switch (pinconf_to_config_param(configs[i])) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + ret = intel_config_set_pull(pctrl, pin, configs[i]); + if (ret) + return ret; + break; + + default: + return -ENOTSUPP; + } + } + + return 0; +} + +static const struct pinconf_ops intel_pinconf_ops = { + .is_generic = true, + .pin_config_get = intel_config_get, + .pin_config_set = intel_config_set, +}; + +static const struct pinctrl_desc intel_pinctrl_desc = { + .pctlops = &intel_pinctrl_ops, + .pmxops = &intel_pinmux_ops, + .confops = &intel_pinconf_ops, + .owner = THIS_MODULE, +}; + +static int intel_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_request_gpio(chip->base + offset); +} + +static void intel_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + pinctrl_free_gpio(chip->base + offset); +} + +static int intel_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct intel_pinctrl *pctrl = gpiochip_to_pinctrl(chip); + void __iomem *reg; + + reg = intel_get_padcfg(pctrl, offset, PADCFG0); + if (!reg) + return -EINVAL; + + return !!(readl(reg) & PADCFG0_GPIORXSTATE); +} + +static void intel_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct intel_pinctrl *pctrl = gpiochip_to_pinctrl(chip); + void __iomem *reg; + + reg = intel_get_padcfg(pctrl, offset, PADCFG0); + if (reg) { + unsigned long flags; + u32 padcfg0; + + spin_lock_irqsave(&pctrl->lock, flags); + padcfg0 = readl(reg); + if (value) + padcfg0 |= PADCFG0_GPIOTXSTATE; + else + padcfg0 &= ~PADCFG0_GPIOTXSTATE; + writel(padcfg0, reg); + spin_unlock_irqrestore(&pctrl->lock, flags); + } +} + +static int intel_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_gpio_direction_input(chip->base + offset); +} + +static int intel_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + intel_gpio_set(chip, offset, value); + return pinctrl_gpio_direction_output(chip->base + offset); +} + +static const struct gpio_chip intel_gpio_chip = { + .owner = THIS_MODULE, + .request = intel_gpio_request, + .free = intel_gpio_free, + .direction_input = intel_gpio_direction_input, + .direction_output = intel_gpio_direction_output, + .get = intel_gpio_get, + .set = intel_gpio_set, +}; + +static void intel_gpio_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct intel_pinctrl *pctrl = gpiochip_to_pinctrl(gc); + const struct intel_community *community; + unsigned pin = irqd_to_hwirq(d); + + spin_lock(&pctrl->lock); + + community = intel_get_community(pctrl, pin); + if (community) { + unsigned padno = pin_to_padno(community, pin); + unsigned gpp_offset = padno % NPADS_IN_GPP; + unsigned gpp = padno / NPADS_IN_GPP; + + writel(BIT(gpp_offset), community->regs + GPI_IS + gpp * 4); + } + + spin_unlock(&pctrl->lock); +} + +static void intel_gpio_irq_mask_unmask(struct irq_data *d, bool mask) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct intel_pinctrl *pctrl = gpiochip_to_pinctrl(gc); + const struct intel_community *community; + unsigned pin = irqd_to_hwirq(d); + unsigned long flags; + + spin_lock_irqsave(&pctrl->lock, flags); + + community = intel_get_community(pctrl, pin); + if (community) { + unsigned padno = pin_to_padno(community, pin); + unsigned gpp_offset = padno % NPADS_IN_GPP; + unsigned gpp = padno / NPADS_IN_GPP; + void __iomem *reg; + u32 value; + + reg = community->regs + community->ie_offset + gpp * 4; + value = readl(reg); + if (mask) + value &= ~BIT(gpp_offset); + else + value |= BIT(gpp_offset); + writel(value, reg); + } + + spin_unlock_irqrestore(&pctrl->lock, flags); +} + +static void intel_gpio_irq_mask(struct irq_data *d) +{ + intel_gpio_irq_mask_unmask(d, true); +} + +static void intel_gpio_irq_unmask(struct irq_data *d) +{ + intel_gpio_irq_mask_unmask(d, false); +} + +static int intel_gpio_irq_type(struct irq_data *d, unsigned type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct intel_pinctrl *pctrl = gpiochip_to_pinctrl(gc); + unsigned pin = irqd_to_hwirq(d); + unsigned long flags; + void __iomem *reg; + u32 value; + + reg = intel_get_padcfg(pctrl, pin, PADCFG0); + if (!reg) + return -EINVAL; + + spin_lock_irqsave(&pctrl->lock, flags); + + value = readl(reg); + + value &= ~(PADCFG0_RXEVCFG_MASK | PADCFG0_RXINV); + + if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { + value |= PADCFG0_RXEVCFG_EDGE_BOTH << PADCFG0_RXEVCFG_SHIFT; + } else if (type & IRQ_TYPE_EDGE_FALLING) { + value |= PADCFG0_RXEVCFG_EDGE << PADCFG0_RXEVCFG_SHIFT; + value |= PADCFG0_RXINV; + } else if (type & IRQ_TYPE_EDGE_RISING) { + value |= PADCFG0_RXEVCFG_EDGE << PADCFG0_RXEVCFG_SHIFT; + } else if (type & IRQ_TYPE_LEVEL_LOW) { + value |= PADCFG0_RXINV; + } else { + value |= PADCFG0_RXEVCFG_DISABLED << PADCFG0_RXEVCFG_SHIFT; + } + + writel(value, reg); + + if (type & IRQ_TYPE_EDGE_BOTH) + __irq_set_handler_locked(d->irq, handle_edge_irq); + else if (type & IRQ_TYPE_LEVEL_MASK) + __irq_set_handler_locked(d->irq, handle_level_irq); + + spin_unlock_irqrestore(&pctrl->lock, flags); + + return 0; +} + +static int intel_gpio_irq_wake(struct irq_data *d, unsigned int on) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct intel_pinctrl *pctrl = gpiochip_to_pinctrl(gc); + const struct intel_community *community; + unsigned pin = irqd_to_hwirq(d); + unsigned padno, gpp, gpp_offset; + u32 gpe_en; + + community = intel_get_community(pctrl, pin); + if (!community) + return -EINVAL; + + padno = pin_to_padno(community, pin); + gpp = padno / NPADS_IN_GPP; + gpp_offset = padno % NPADS_IN_GPP; + + /* Clear the existing wake status */ + writel(BIT(gpp_offset), community->regs + GPI_GPE_STS + gpp * 4); + + /* + * The controller will generate wake when GPE of the corresponding + * pad is enabled and it is not routed to SCI (GPIROUTSCI is not + * set). + */ + gpe_en = readl(community->regs + GPI_GPE_EN + gpp * 4); + if (on) + gpe_en |= BIT(gpp_offset); + else + gpe_en &= ~BIT(gpp_offset); + writel(gpe_en, community->regs + GPI_GPE_EN + gpp * 4); + + dev_dbg(pctrl->dev, "%sable wake for pin %u\n", on ? "en" : "dis", pin); + return 0; +} + +static void intel_gpio_community_irq_handler(struct gpio_chip *gc, + const struct intel_community *community) +{ + int gpp; + + for (gpp = 0; gpp < community->ngpps; gpp++) { + unsigned long pending, enabled, gpp_offset; + + pending = readl(community->regs + GPI_IS + gpp * 4); + enabled = readl(community->regs + community->ie_offset + + gpp * 4); + + /* Only interrupts that are enabled */ + pending &= enabled; + + for_each_set_bit(gpp_offset, &pending, NPADS_IN_GPP) { + unsigned padno, irq; + + /* + * The last group in community can have less pins + * than NPADS_IN_GPP. + */ + padno = gpp_offset + gpp * NPADS_IN_GPP; + if (padno >= community->npins) + break; + + irq = irq_find_mapping(gc->irqdomain, + community->pin_base + padno); + generic_handle_irq(irq); + } + } +} + +static void intel_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct intel_pinctrl *pctrl = gpiochip_to_pinctrl(gc); + struct irq_chip *chip = irq_get_chip(irq); + int i; + + chained_irq_enter(chip, desc); + + /* Need to check all communities for pending interrupts */ + for (i = 0; i < pctrl->ncommunities; i++) + intel_gpio_community_irq_handler(gc, &pctrl->communities[i]); + + chained_irq_exit(chip, desc); +} + +static struct irq_chip intel_gpio_irqchip = { + .name = "intel-gpio", + .irq_ack = intel_gpio_irq_ack, + .irq_mask = intel_gpio_irq_mask, + .irq_unmask = intel_gpio_irq_unmask, + .irq_set_type = intel_gpio_irq_type, + .irq_set_wake = intel_gpio_irq_wake, +}; + +static void intel_gpio_irq_init(struct intel_pinctrl *pctrl) +{ + size_t i; + + for (i = 0; i < pctrl->ncommunities; i++) { + const struct intel_community *community; + void __iomem *base; + unsigned gpp; + + community = &pctrl->communities[i]; + base = community->regs; + + for (gpp = 0; gpp < community->ngpps; gpp++) { + /* Mask and clear all interrupts */ + writel(0, base + community->ie_offset + gpp * 4); + writel(0xffff, base + GPI_IS + gpp * 4); + } + } +} + +static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq) +{ + int ret; + + pctrl->chip = intel_gpio_chip; + + pctrl->chip.ngpio = pctrl->soc->npins; + pctrl->chip.label = dev_name(pctrl->dev); + pctrl->chip.dev = pctrl->dev; + pctrl->chip.base = -1; + + ret = gpiochip_add(&pctrl->chip); + if (ret) { + dev_err(pctrl->dev, "failed to register gpiochip\n"); + return ret; + } + + ret = gpiochip_add_pin_range(&pctrl->chip, dev_name(pctrl->dev), + 0, 0, pctrl->soc->npins); + if (ret) { + dev_err(pctrl->dev, "failed to add GPIO pin range\n"); + gpiochip_remove(&pctrl->chip); + return ret; + } + + ret = gpiochip_irqchip_add(&pctrl->chip, &intel_gpio_irqchip, 0, + handle_simple_irq, IRQ_TYPE_NONE); + if (ret) { + dev_err(pctrl->dev, "failed to add irqchip\n"); + gpiochip_remove(&pctrl->chip); + return ret; + } + + gpiochip_set_chained_irqchip(&pctrl->chip, &intel_gpio_irqchip, irq, + intel_gpio_irq_handler); + return 0; +} + +static int intel_pinctrl_pm_init(struct intel_pinctrl *pctrl) +{ +#ifdef CONFIG_PM_SLEEP + const struct intel_pinctrl_soc_data *soc = pctrl->soc; + struct intel_community_context *communities; + struct intel_pad_context *pads; + int i; + + pads = devm_kcalloc(pctrl->dev, soc->npins, sizeof(*pads), GFP_KERNEL); + if (!pads) + return -ENOMEM; + + communities = devm_kcalloc(pctrl->dev, pctrl->ncommunities, + sizeof(*communities), GFP_KERNEL); + if (!communities) + return -ENOMEM; + + + for (i = 0; i < pctrl->ncommunities; i++) { + struct intel_community *community = &pctrl->communities[i]; + u32 *intmask; + + intmask = devm_kcalloc(pctrl->dev, community->ngpps, + sizeof(*intmask), GFP_KERNEL); + if (!intmask) + return -ENOMEM; + + communities[i].intmask = intmask; + } + + pctrl->context.pads = pads; + pctrl->context.communities = communities; +#endif + + return 0; +} + +int intel_pinctrl_probe(struct platform_device *pdev, + const struct intel_pinctrl_soc_data *soc_data) +{ + struct intel_pinctrl *pctrl; + int i, ret, irq; + + if (!soc_data) + return -EINVAL; + + pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); + if (!pctrl) + return -ENOMEM; + + pctrl->dev = &pdev->dev; + pctrl->soc = soc_data; + spin_lock_init(&pctrl->lock); + + /* + * Make a copy of the communities which we can use to hold pointers + * to the registers. + */ + pctrl->ncommunities = pctrl->soc->ncommunities; + pctrl->communities = devm_kcalloc(&pdev->dev, pctrl->ncommunities, + sizeof(*pctrl->communities), GFP_KERNEL); + if (!pctrl->communities) + return -ENOMEM; + + for (i = 0; i < pctrl->ncommunities; i++) { + struct intel_community *community = &pctrl->communities[i]; + struct resource *res; + void __iomem *regs; + u32 padbar; + + *community = pctrl->soc->communities[i]; + + res = platform_get_resource(pdev, IORESOURCE_MEM, + community->barno); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + /* Read offset of the pad configuration registers */ + padbar = readl(regs + PADBAR); + + community->regs = regs; + community->pad_regs = regs + padbar; + community->ngpps = DIV_ROUND_UP(community->npins, NPADS_IN_GPP); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get interrupt number\n"); + return irq; + } + + ret = intel_pinctrl_pm_init(pctrl); + if (ret) + return ret; + + pctrl->pctldesc = intel_pinctrl_desc; + pctrl->pctldesc.name = dev_name(&pdev->dev); + pctrl->pctldesc.pins = pctrl->soc->pins; + pctrl->pctldesc.npins = pctrl->soc->npins; + + pctrl->pctldev = pinctrl_register(&pctrl->pctldesc, &pdev->dev, pctrl); + if (!pctrl->pctldev) { + dev_err(&pdev->dev, "failed to register pinctrl driver\n"); + return -ENODEV; + } + + ret = intel_gpio_probe(pctrl, irq); + if (ret) { + pinctrl_unregister(pctrl->pctldev); + return ret; + } + + platform_set_drvdata(pdev, pctrl); + + return 0; +} +EXPORT_SYMBOL_GPL(intel_pinctrl_probe); + +int intel_pinctrl_remove(struct platform_device *pdev) +{ + struct intel_pinctrl *pctrl = platform_get_drvdata(pdev); + + gpiochip_remove(&pctrl->chip); + pinctrl_unregister(pctrl->pctldev); + + return 0; +} +EXPORT_SYMBOL_GPL(intel_pinctrl_remove); + +#ifdef CONFIG_PM_SLEEP +int intel_pinctrl_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct intel_pinctrl *pctrl = platform_get_drvdata(pdev); + struct intel_community_context *communities; + struct intel_pad_context *pads; + int i; + + pads = pctrl->context.pads; + for (i = 0; i < pctrl->soc->npins; i++) { + const struct pinctrl_pin_desc *desc = &pctrl->soc->pins[i]; + u32 val; + + if (!intel_pad_usable(pctrl, desc->number)) + continue; + + val = readl(intel_get_padcfg(pctrl, desc->number, PADCFG0)); + pads[i].padcfg0 = val & ~PADCFG0_GPIORXSTATE; + val = readl(intel_get_padcfg(pctrl, desc->number, PADCFG1)); + pads[i].padcfg1 = val; + } + + communities = pctrl->context.communities; + for (i = 0; i < pctrl->ncommunities; i++) { + struct intel_community *community = &pctrl->communities[i]; + void __iomem *base; + unsigned gpp; + + base = community->regs + community->ie_offset; + for (gpp = 0; gpp < community->ngpps; gpp++) + communities[i].intmask[gpp] = readl(base + gpp * 4); + } + + return 0; +} +EXPORT_SYMBOL_GPL(intel_pinctrl_suspend); + +int intel_pinctrl_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct intel_pinctrl *pctrl = platform_get_drvdata(pdev); + const struct intel_community_context *communities; + const struct intel_pad_context *pads; + int i; + + /* Mask all interrupts */ + intel_gpio_irq_init(pctrl); + + pads = pctrl->context.pads; + for (i = 0; i < pctrl->soc->npins; i++) { + const struct pinctrl_pin_desc *desc = &pctrl->soc->pins[i]; + void __iomem *padcfg; + u32 val; + + if (!intel_pad_usable(pctrl, desc->number)) + continue; + + padcfg = intel_get_padcfg(pctrl, desc->number, PADCFG0); + val = readl(padcfg) & ~PADCFG0_GPIORXSTATE; + if (val != pads[i].padcfg0) { + writel(pads[i].padcfg0, padcfg); + dev_dbg(dev, "restored pin %u padcfg0 %#08x\n", + desc->number, readl(padcfg)); + } + + padcfg = intel_get_padcfg(pctrl, desc->number, PADCFG1); + val = readl(padcfg); + if (val != pads[i].padcfg1) { + writel(pads[i].padcfg1, padcfg); + dev_dbg(dev, "restored pin %u padcfg1 %#08x\n", + desc->number, readl(padcfg)); + } + } + + communities = pctrl->context.communities; + for (i = 0; i < pctrl->ncommunities; i++) { + struct intel_community *community = &pctrl->communities[i]; + void __iomem *base; + unsigned gpp; + + base = community->regs + community->ie_offset; + for (gpp = 0; gpp < community->ngpps; gpp++) { + writel(communities[i].intmask[gpp], base + gpp * 4); + dev_dbg(dev, "restored mask %d/%u %#08x\n", i, gpp, + readl(base + gpp * 4)); + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(intel_pinctrl_resume); +#endif + +MODULE_AUTHOR("Mathias Nyman "); +MODULE_AUTHOR("Mika Westerberg "); +MODULE_DESCRIPTION("Intel pinctrl/GPIO core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/intel/pinctrl-intel.h b/drivers/pinctrl/intel/pinctrl-intel.h new file mode 100644 index 00000000000000..4ec8b572a288fb --- /dev/null +++ b/drivers/pinctrl/intel/pinctrl-intel.h @@ -0,0 +1,128 @@ +/* + * Core pinctrl/GPIO driver for Intel GPIO controllers + * + * Copyright (C) 2015, Intel Corporation + * Authors: Mathias Nyman + * Mika Westerberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef PINCTRL_INTEL_H +#define PINCTRL_INTEL_H + +struct pinctrl_pin_desc; +struct platform_device; +struct device; + +/** + * struct intel_pingroup - Description about group of pins + * @name: Name of the groups + * @pins: All pins in this group + * @npins: Number of pins in this groups + * @mode: Native mode in which the group is muxed out @pins + */ +struct intel_pingroup { + const char *name; + const unsigned *pins; + size_t npins; + unsigned short mode; +}; + +/** + * struct intel_function - Description about a function + * @name: Name of the function + * @groups: An array of groups for this function + * @ngroups: Number of groups in @groups + */ +struct intel_function { + const char *name; + const char * const *groups; + size_t ngroups; +}; + +/** + * struct intel_community - Intel pin community description + * @barno: MMIO BAR number where registers for this community reside + * @padown_offset: Register offset of PAD_OWN register from @regs. If %0 + * then there is no support for owner. + * @padcfglock_offset: Register offset of PADCFGLOCK from @regs. If %0 then + * locking is not supported. + * @hostown_offset: Register offset of HOSTSW_OWN from @regs. If %0 then it + * is assumed that the host owns the pin (rather than + * ACPI). + * @ie_offset: Register offset of GPI_IE from @regs. + * @pin_base: Starting pin of pins in this community + * @npins: Number of pins in this community + * @regs: Community specific common registers (reserved for core driver) + * @pad_regs: Community specific pad registers (reserved for core driver) + * @ngpps: Number of groups (hw groups) in this community (reserved for + * core driver) + */ +struct intel_community { + unsigned barno; + unsigned padown_offset; + unsigned padcfglock_offset; + unsigned hostown_offset; + unsigned ie_offset; + unsigned pin_base; + size_t npins; + void __iomem *regs; + void __iomem *pad_regs; + size_t ngpps; +}; + +#define PIN_GROUP(n, p, m) \ + { \ + .name = (n), \ + .pins = (p), \ + .npins = ARRAY_SIZE((p)), \ + .mode = (m), \ + } + +#define FUNCTION(n, g) \ + { \ + .name = (n), \ + .groups = (g), \ + .ngroups = ARRAY_SIZE((g)), \ + } + +/** + * struct intel_pinctrl_soc_data - Intel pin controller per-SoC configuration + * @uid: ACPI _UID for the probe driver use if needed + * @pins: Array if pins this pinctrl controls + * @npins: Number of pins in the array + * @groups: Array of pin groups + * @ngroups: Number of groups in the array + * @functions: Array of functions + * @nfunctions: Number of functions in the array + * @communities: Array of communities this pinctrl handles + * @ncommunities: Number of communities in the array + * + * The @communities is used as a template by the core driver. It will make + * copy of all communities and fill in rest of the information. + */ +struct intel_pinctrl_soc_data { + const char *uid; + const struct pinctrl_pin_desc *pins; + size_t npins; + const struct intel_pingroup *groups; + size_t ngroups; + const struct intel_function *functions; + size_t nfunctions; + const struct intel_community *communities; + size_t ncommunities; +}; + +int intel_pinctrl_probe(struct platform_device *pdev, + const struct intel_pinctrl_soc_data *soc_data); +int intel_pinctrl_remove(struct platform_device *pdev); + +#ifdef CONFIG_PM_SLEEP +int intel_pinctrl_suspend(struct device *dev); +int intel_pinctrl_resume(struct device *dev); +#endif + +#endif /* PINCTRL_INTEL_H */ diff --git a/drivers/pinctrl/intel/pinctrl-sunrisepoint.c b/drivers/pinctrl/intel/pinctrl-sunrisepoint.c new file mode 100644 index 00000000000000..55d025dc89e839 --- /dev/null +++ b/drivers/pinctrl/intel/pinctrl-sunrisepoint.c @@ -0,0 +1,336 @@ +/* + * Intel Sunrisepoint PCH pinctrl/GPIO driver + * + * Copyright (C) 2015, Intel Corporation + * Authors: Mathias Nyman + * Mika Westerberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include "pinctrl-intel.h" + +#define SPT_PAD_OWN 0x020 +#define SPT_PADCFGLOCK 0x0a0 +#define SPT_HOSTSW_OWN 0x0d0 +#define SPT_GPI_IE 0x120 + +#define SPT_COMMUNITY(b, s, e) \ + { \ + .barno = (b), \ + .padown_offset = SPT_PAD_OWN, \ + .padcfglock_offset = SPT_PADCFGLOCK, \ + .hostown_offset = SPT_HOSTSW_OWN, \ + .ie_offset = SPT_GPI_IE, \ + .pin_base = (s), \ + .npins = ((e) - (s) + 1), \ + } + +/* Sunrisepoint-LP */ +static const struct pinctrl_pin_desc sptlp_pins[] = { + /* GPP_A */ + PINCTRL_PIN(0, "RCINB"), + PINCTRL_PIN(1, "LAD_0"), + PINCTRL_PIN(2, "LAD_1"), + PINCTRL_PIN(3, "LAD_2"), + PINCTRL_PIN(4, "LAD_3"), + PINCTRL_PIN(5, "LFRAMEB"), + PINCTRL_PIN(6, "SERIQ"), + PINCTRL_PIN(7, "PIRQAB"), + PINCTRL_PIN(8, "CLKRUNB"), + PINCTRL_PIN(9, "CLKOUT_LPC_0"), + PINCTRL_PIN(10, "CLKOUT_LPC_1"), + PINCTRL_PIN(11, "PMEB"), + PINCTRL_PIN(12, "BM_BUSYB"), + PINCTRL_PIN(13, "SUSWARNB_SUS_PWRDNACK"), + PINCTRL_PIN(14, "SUS_STATB"), + PINCTRL_PIN(15, "SUSACKB"), + PINCTRL_PIN(16, "SD_1P8_SEL"), + PINCTRL_PIN(17, "SD_PWR_EN_B"), + PINCTRL_PIN(18, "ISH_GP_0"), + PINCTRL_PIN(19, "ISH_GP_1"), + PINCTRL_PIN(20, "ISH_GP_2"), + PINCTRL_PIN(21, "ISH_GP_3"), + PINCTRL_PIN(22, "ISH_GP_4"), + PINCTRL_PIN(23, "ISH_GP_5"), + /* GPP_B */ + PINCTRL_PIN(24, "CORE_VID_0"), + PINCTRL_PIN(25, "CORE_VID_1"), + PINCTRL_PIN(26, "VRALERTB"), + PINCTRL_PIN(27, "CPU_GP_2"), + PINCTRL_PIN(28, "CPU_GP_3"), + PINCTRL_PIN(29, "SRCCLKREQB_0"), + PINCTRL_PIN(30, "SRCCLKREQB_1"), + PINCTRL_PIN(31, "SRCCLKREQB_2"), + PINCTRL_PIN(32, "SRCCLKREQB_3"), + PINCTRL_PIN(33, "SRCCLKREQB_4"), + PINCTRL_PIN(34, "SRCCLKREQB_5"), + PINCTRL_PIN(35, "EXT_PWR_GATEB"), + PINCTRL_PIN(36, "SLP_S0B"), + PINCTRL_PIN(37, "PLTRSTB"), + PINCTRL_PIN(38, "SPKR"), + PINCTRL_PIN(39, "GSPI0_CSB"), + PINCTRL_PIN(40, "GSPI0_CLK"), + PINCTRL_PIN(41, "GSPI0_MISO"), + PINCTRL_PIN(42, "GSPI0_MOSI"), + PINCTRL_PIN(43, "GSPI1_CSB"), + PINCTRL_PIN(44, "GSPI1_CLK"), + PINCTRL_PIN(45, "GSPI1_MISO"), + PINCTRL_PIN(46, "GSPI1_MOSI"), + PINCTRL_PIN(47, "SML1ALERTB"), + /* GPP_C */ + PINCTRL_PIN(48, "SMBCLK"), + PINCTRL_PIN(49, "SMBDATA"), + PINCTRL_PIN(50, "SMBALERTB"), + PINCTRL_PIN(51, "SML0CLK"), + PINCTRL_PIN(52, "SML0DATA"), + PINCTRL_PIN(53, "SML0ALERTB"), + PINCTRL_PIN(54, "SML1CLK"), + PINCTRL_PIN(55, "SML1DATA"), + PINCTRL_PIN(56, "UART0_RXD"), + PINCTRL_PIN(57, "UART0_TXD"), + PINCTRL_PIN(58, "UART0_RTSB"), + PINCTRL_PIN(59, "UART0_CTSB"), + PINCTRL_PIN(60, "UART1_RXD"), + PINCTRL_PIN(61, "UART1_TXD"), + PINCTRL_PIN(62, "UART1_RTSB"), + PINCTRL_PIN(63, "UART1_CTSB"), + PINCTRL_PIN(64, "I2C0_SDA"), + PINCTRL_PIN(65, "I2C0_SCL"), + PINCTRL_PIN(66, "I2C1_SDA"), + PINCTRL_PIN(67, "I2C1_SCL"), + PINCTRL_PIN(68, "UART2_RXD"), + PINCTRL_PIN(69, "UART2_TXD"), + PINCTRL_PIN(70, "UART2_RTSB"), + PINCTRL_PIN(71, "UART2_CTSB"), + /* GPP_D */ + PINCTRL_PIN(72, "SPI1_CSB"), + PINCTRL_PIN(73, "SPI1_CLK"), + PINCTRL_PIN(74, "SPI1_MISO_IO_1"), + PINCTRL_PIN(75, "SPI1_MOSI_IO_0"), + PINCTRL_PIN(76, "FLASHTRIG"), + PINCTRL_PIN(77, "ISH_I2C0_SDA"), + PINCTRL_PIN(78, "ISH_I2C0_SCL"), + PINCTRL_PIN(79, "ISH_I2C1_SDA"), + PINCTRL_PIN(80, "ISH_I2C1_SCL"), + PINCTRL_PIN(81, "ISH_SPI_CSB"), + PINCTRL_PIN(82, "ISH_SPI_CLK"), + PINCTRL_PIN(83, "ISH_SPI_MISO"), + PINCTRL_PIN(84, "ISH_SPI_MOSI"), + PINCTRL_PIN(85, "ISH_UART0_RXD"), + PINCTRL_PIN(86, "ISH_UART0_TXD"), + PINCTRL_PIN(87, "ISH_UART0_RTSB"), + PINCTRL_PIN(88, "ISH_UART0_CTSB"), + PINCTRL_PIN(89, "DMIC_CLK_1"), + PINCTRL_PIN(90, "DMIC_DATA_1"), + PINCTRL_PIN(91, "DMIC_CLK_0"), + PINCTRL_PIN(92, "DMIC_DATA_0"), + PINCTRL_PIN(93, "SPI1_IO_2"), + PINCTRL_PIN(94, "SPI1_IO_3"), + PINCTRL_PIN(95, "SSP_MCLK"), + /* GPP_E */ + PINCTRL_PIN(96, "SATAXPCIE_0"), + PINCTRL_PIN(97, "SATAXPCIE_1"), + PINCTRL_PIN(98, "SATAXPCIE_2"), + PINCTRL_PIN(99, "CPU_GP_0"), + PINCTRL_PIN(100, "SATA_DEVSLP_0"), + PINCTRL_PIN(101, "SATA_DEVSLP_1"), + PINCTRL_PIN(102, "SATA_DEVSLP_2"), + PINCTRL_PIN(103, "CPU_GP_1"), + PINCTRL_PIN(104, "SATA_LEDB"), + PINCTRL_PIN(105, "USB2_OCB_0"), + PINCTRL_PIN(106, "USB2_OCB_1"), + PINCTRL_PIN(107, "USB2_OCB_2"), + PINCTRL_PIN(108, "USB2_OCB_3"), + PINCTRL_PIN(109, "DDSP_HPD_0"), + PINCTRL_PIN(110, "DDSP_HPD_1"), + PINCTRL_PIN(111, "DDSP_HPD_2"), + PINCTRL_PIN(112, "DDSP_HPD_3"), + PINCTRL_PIN(113, "EDP_HPD"), + PINCTRL_PIN(114, "DDPB_CTRLCLK"), + PINCTRL_PIN(115, "DDPB_CTRLDATA"), + PINCTRL_PIN(116, "DDPC_CTRLCLK"), + PINCTRL_PIN(117, "DDPC_CTRLDATA"), + PINCTRL_PIN(118, "DDPD_CTRLCLK"), + PINCTRL_PIN(119, "DDPD_CTRLDATA"), + /* GPP_F */ + PINCTRL_PIN(120, "SSP2_SCLK"), + PINCTRL_PIN(121, "SSP2_SFRM"), + PINCTRL_PIN(122, "SSP2_TXD"), + PINCTRL_PIN(123, "SSP2_RXD"), + PINCTRL_PIN(124, "I2C2_SDA"), + PINCTRL_PIN(125, "I2C2_SCL"), + PINCTRL_PIN(126, "I2C3_SDA"), + PINCTRL_PIN(127, "I2C3_SCL"), + PINCTRL_PIN(128, "I2C4_SDA"), + PINCTRL_PIN(129, "I2C4_SCL"), + PINCTRL_PIN(130, "I2C5_SDA"), + PINCTRL_PIN(131, "I2C5_SCL"), + PINCTRL_PIN(132, "EMMC_CMD"), + PINCTRL_PIN(133, "EMMC_DATA_0"), + PINCTRL_PIN(134, "EMMC_DATA_1"), + PINCTRL_PIN(135, "EMMC_DATA_2"), + PINCTRL_PIN(136, "EMMC_DATA_3"), + PINCTRL_PIN(137, "EMMC_DATA_4"), + PINCTRL_PIN(138, "EMMC_DATA_5"), + PINCTRL_PIN(139, "EMMC_DATA_6"), + PINCTRL_PIN(140, "EMMC_DATA_7"), + PINCTRL_PIN(141, "EMMC_RCLK"), + PINCTRL_PIN(142, "EMMC_CLK"), + PINCTRL_PIN(143, "GPP_F_23"), + /* GPP_G */ + PINCTRL_PIN(144, "SD_CMD"), + PINCTRL_PIN(145, "SD_DATA_0"), + PINCTRL_PIN(146, "SD_DATA_1"), + PINCTRL_PIN(147, "SD_DATA_2"), + PINCTRL_PIN(148, "SD_DATA_3"), + PINCTRL_PIN(149, "SD_CDB"), + PINCTRL_PIN(150, "SD_CLK"), + PINCTRL_PIN(151, "SD_WP"), +}; + +static const unsigned sptlp_spi0_pins[] = { 39, 40, 41, 42 }; +static const unsigned sptlp_spi1_pins[] = { 43, 44, 45, 46 }; +static const unsigned sptlp_uart0_pins[] = { 56, 57, 58, 59 }; +static const unsigned sptlp_uart1_pins[] = { 60, 61, 62, 63 }; +static const unsigned sptlp_uart2_pins[] = { 68, 69, 71, 71 }; +static const unsigned sptlp_i2c0_pins[] = { 64, 65 }; +static const unsigned sptlp_i2c1_pins[] = { 66, 67 }; +static const unsigned sptlp_i2c2_pins[] = { 124, 125 }; +static const unsigned sptlp_i2c3_pins[] = { 126, 127 }; +static const unsigned sptlp_i2c4_pins[] = { 128, 129 }; +static const unsigned sptlp_i2c4b_pins[] = { 85, 86 }; +static const unsigned sptlp_i2c5_pins[] = { 130, 131 }; +static const unsigned sptlp_ssp2_pins[] = { 120, 121, 122, 123 }; +static const unsigned sptlp_emmc_pins[] = { + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, +}; +static const unsigned sptlp_sd_pins[] = { + 144, 145, 146, 147, 148, 149, 150, 151, +}; + +static const struct intel_pingroup sptlp_groups[] = { + PIN_GROUP("spi0_grp", sptlp_spi0_pins, 1), + PIN_GROUP("spi1_grp", sptlp_spi1_pins, 1), + PIN_GROUP("uart0_grp", sptlp_uart0_pins, 1), + PIN_GROUP("uart1_grp", sptlp_uart1_pins, 1), + PIN_GROUP("uart2_grp", sptlp_uart2_pins, 1), + PIN_GROUP("i2c0_grp", sptlp_i2c0_pins, 1), + PIN_GROUP("i2c1_grp", sptlp_i2c1_pins, 1), + PIN_GROUP("i2c2_grp", sptlp_i2c2_pins, 1), + PIN_GROUP("i2c3_grp", sptlp_i2c3_pins, 1), + PIN_GROUP("i2c4_grp", sptlp_i2c4_pins, 1), + PIN_GROUP("i2c4b_grp", sptlp_i2c4b_pins, 3), + PIN_GROUP("i2c5_grp", sptlp_i2c5_pins, 1), + PIN_GROUP("ssp2_grp", sptlp_ssp2_pins, 1), + PIN_GROUP("emmc_grp", sptlp_emmc_pins, 1), + PIN_GROUP("sd_grp", sptlp_sd_pins, 1), +}; + +static const char * const sptlp_spi0_groups[] = { "spi0_grp" }; +static const char * const sptlp_spi1_groups[] = { "spi0_grp" }; +static const char * const sptlp_uart0_groups[] = { "uart0_grp" }; +static const char * const sptlp_uart1_groups[] = { "uart1_grp" }; +static const char * const sptlp_uart2_groups[] = { "uart2_grp" }; +static const char * const sptlp_i2c0_groups[] = { "i2c0_grp" }; +static const char * const sptlp_i2c1_groups[] = { "i2c1_grp" }; +static const char * const sptlp_i2c2_groups[] = { "i2c2_grp" }; +static const char * const sptlp_i2c3_groups[] = { "i2c3_grp" }; +static const char * const sptlp_i2c4_groups[] = { "i2c4_grp", "i2c4b_grp" }; +static const char * const sptlp_i2c5_groups[] = { "i2c5_grp" }; +static const char * const sptlp_ssp2_groups[] = { "ssp2_grp" }; +static const char * const sptlp_emmc_groups[] = { "emmc_grp" }; +static const char * const sptlp_sd_groups[] = { "sd_grp" }; + +static const struct intel_function sptlp_functions[] = { + FUNCTION("spi0", sptlp_spi0_groups), + FUNCTION("spi1", sptlp_spi1_groups), + FUNCTION("uart0", sptlp_uart0_groups), + FUNCTION("uart1", sptlp_uart1_groups), + FUNCTION("uart2", sptlp_uart2_groups), + FUNCTION("i2c0", sptlp_i2c0_groups), + FUNCTION("i2c1", sptlp_i2c1_groups), + FUNCTION("i2c2", sptlp_i2c2_groups), + FUNCTION("i2c3", sptlp_i2c3_groups), + FUNCTION("i2c4", sptlp_i2c4_groups), + FUNCTION("i2c5", sptlp_i2c5_groups), + FUNCTION("ssp2", sptlp_ssp2_groups), + FUNCTION("emmc", sptlp_emmc_groups), + FUNCTION("sd", sptlp_sd_groups), +}; + +static const struct intel_community sptlp_communities[] = { + SPT_COMMUNITY(0, 0, 47), + SPT_COMMUNITY(1, 48, 119), + SPT_COMMUNITY(2, 120, 151), +}; + +static const struct intel_pinctrl_soc_data sptlp_soc_data = { + .pins = sptlp_pins, + .npins = ARRAY_SIZE(sptlp_pins), + .groups = sptlp_groups, + .ngroups = ARRAY_SIZE(sptlp_groups), + .functions = sptlp_functions, + .nfunctions = ARRAY_SIZE(sptlp_functions), + .communities = sptlp_communities, + .ncommunities = ARRAY_SIZE(sptlp_communities), +}; + +static const struct acpi_device_id spt_pinctrl_acpi_match[] = { + { "INT344B", (kernel_ulong_t)&sptlp_soc_data }, + { } +}; +MODULE_DEVICE_TABLE(acpi, spt_pinctrl_acpi_match); + +static int spt_pinctrl_probe(struct platform_device *pdev) +{ + const struct intel_pinctrl_soc_data *soc_data; + const struct acpi_device_id *id; + + id = acpi_match_device(spt_pinctrl_acpi_match, &pdev->dev); + if (!id || !id->driver_data) + return -ENODEV; + + soc_data = (const struct intel_pinctrl_soc_data *)id->driver_data; + return intel_pinctrl_probe(pdev, soc_data); +} + +static const struct dev_pm_ops spt_pinctrl_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(intel_pinctrl_suspend, + intel_pinctrl_resume) +}; + +static struct platform_driver spt_pinctrl_driver = { + .probe = spt_pinctrl_probe, + .remove = intel_pinctrl_remove, + .driver = { + .name = "sunrisepoint-pinctrl", + .acpi_match_table = spt_pinctrl_acpi_match, + .pm = &spt_pinctrl_pm_ops, + }, +}; + +static int __init spt_pinctrl_init(void) +{ + return platform_driver_register(&spt_pinctrl_driver); +} +subsys_initcall(spt_pinctrl_init); + +static void __exit spt_pinctrl_exit(void) +{ + platform_driver_unregister(&spt_pinctrl_driver); +} +module_exit(spt_pinctrl_exit); + +MODULE_AUTHOR("Mathias Nyman "); +MODULE_AUTHOR("Mika Westerberg "); +MODULE_DESCRIPTION("Intel Sunrisepoint PCH pinctrl/GPIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig new file mode 100644 index 00000000000000..5983cf5b2c463f --- /dev/null +++ b/drivers/pinctrl/mediatek/Kconfig @@ -0,0 +1,23 @@ +if ARCH_MEDIATEK || COMPILE_TEST + +config PINCTRL_MTK_COMMON + bool + select PINMUX + select GENERIC_PINCONF + select GPIOLIB + select OF_GPIO + +# For ARMv7 SoCs +config PINCTRL_MT8135 + bool "Mediatek MT8135 pin control" if COMPILE_TEST && !MACH_MT8135 + default MACH_MT8135 + select PINCTRL_MTK_COMMON + +# For ARMv8 SoCs +config PINCTRL_MT8173 + bool "Mediatek MT8173 pin control" + depends on ARM64 || COMPILE_TEST + default ARM64 && ARCH_MEDIATEK + select PINCTRL_MTK_COMMON + +endif diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile new file mode 100644 index 00000000000000..d8606a2179cf61 --- /dev/null +++ b/drivers/pinctrl/mediatek/Makefile @@ -0,0 +1,6 @@ +# Core +obj-$(CONFIG_PINCTRL_MTK_COMMON) += pinctrl-mtk-common.o + +# SoC Drivers +obj-$(CONFIG_PINCTRL_MT8135) += pinctrl-mt8135.o +obj-$(CONFIG_PINCTRL_MT8173) += pinctrl-mt8173.o diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8135.c b/drivers/pinctrl/mediatek/pinctrl-mt8135.c new file mode 100644 index 00000000000000..f1e1e187ce96c0 --- /dev/null +++ b/drivers/pinctrl/mediatek/pinctrl-mt8135.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Hongzhou.Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-mtk-common.h" +#include "pinctrl-mtk-mt8135.h" + +#define DRV_BASE1 0x500 +#define DRV_BASE2 0x510 +#define PUPD_BASE1 0x400 +#define PUPD_BASE2 0x450 +#define R0_BASE1 0x4d0 +#define R1_BASE1 0x200 +#define R1_BASE2 0x250 + +struct mtk_spec_pull_set { + unsigned int pin; + unsigned int pupd_offset; + unsigned char pupd_bit; + unsigned int r0_offset; + unsigned char r0_bit; + unsigned int r1_offset; + unsigned char r1_bit; +}; + +#define SPEC_PULL(_pin, _pupd_offset, _pupd_bit, _r0_offset, \ + _r0_bit, _r1_offset, _r1_bit) \ + { \ + .pin = _pin, \ + .pupd_offset = _pupd_offset, \ + .pupd_bit = _pupd_bit, \ + .r0_offset = _r0_offset, \ + .r0_bit = _r0_bit, \ + .r1_offset = _r1_offset, \ + .r1_bit = _r1_bit, \ + } + +static const struct mtk_drv_group_desc mt8135_drv_grp[] = { + /* E8E4E2 2/4/6/8/10/12/14/16 */ + MTK_DRV_GRP(2, 16, 0, 2, 2), + /* E8E4 4/8/12/16 */ + MTK_DRV_GRP(4, 16, 1, 2, 4), + /* E4E2 2/4/6/8 */ + MTK_DRV_GRP(2, 8, 0, 1, 2), + /* E16E8E4 4/8/12/16/20/24/28/32 */ + MTK_DRV_GRP(4, 32, 0, 2, 4) +}; + +static const struct mtk_pin_drv_grp mt8135_pin_drv[] = { + MTK_PIN_DRV_GRP(0, DRV_BASE1, 0, 0), + MTK_PIN_DRV_GRP(1, DRV_BASE1, 0, 0), + MTK_PIN_DRV_GRP(2, DRV_BASE1, 0, 0), + MTK_PIN_DRV_GRP(3, DRV_BASE1, 0, 0), + MTK_PIN_DRV_GRP(4, DRV_BASE1, 4, 0), + MTK_PIN_DRV_GRP(5, DRV_BASE1, 8, 0), + MTK_PIN_DRV_GRP(6, DRV_BASE1, 0, 0), + MTK_PIN_DRV_GRP(7, DRV_BASE1, 0, 0), + MTK_PIN_DRV_GRP(8, DRV_BASE1, 0, 0), + MTK_PIN_DRV_GRP(9, DRV_BASE1, 0, 0), + + MTK_PIN_DRV_GRP(10, DRV_BASE1, 12, 1), + MTK_PIN_DRV_GRP(11, DRV_BASE1, 12, 1), + MTK_PIN_DRV_GRP(12, DRV_BASE1, 12, 1), + MTK_PIN_DRV_GRP(13, DRV_BASE1, 12, 1), + MTK_PIN_DRV_GRP(14, DRV_BASE1, 12, 1), + MTK_PIN_DRV_GRP(15, DRV_BASE1, 12, 1), + MTK_PIN_DRV_GRP(16, DRV_BASE1, 12, 1), + MTK_PIN_DRV_GRP(17, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(18, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(19, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(20, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(21, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(22, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(23, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(24, DRV_BASE1, 16, 1), + MTK_PIN_DRV_GRP(33, DRV_BASE1, 24, 1), + MTK_PIN_DRV_GRP(34, DRV_BASE2, 12, 2), + MTK_PIN_DRV_GRP(37, DRV_BASE2, 20, 1), + MTK_PIN_DRV_GRP(38, DRV_BASE2, 20, 1), + MTK_PIN_DRV_GRP(39, DRV_BASE2, 20, 1), + MTK_PIN_DRV_GRP(40, DRV_BASE2, 24, 1), + MTK_PIN_DRV_GRP(41, DRV_BASE2, 24, 1), + MTK_PIN_DRV_GRP(42, DRV_BASE2, 24, 1), + MTK_PIN_DRV_GRP(43, DRV_BASE2, 28, 1), + MTK_PIN_DRV_GRP(44, DRV_BASE2, 28, 1), + MTK_PIN_DRV_GRP(45, DRV_BASE2, 28, 1), + MTK_PIN_DRV_GRP(46, DRV_BASE2, 28, 1), + MTK_PIN_DRV_GRP(47, DRV_BASE2, 28, 1), + + MTK_PIN_DRV_GRP(49, DRV_BASE2+0x10, 0, 1), + MTK_PIN_DRV_GRP(50, DRV_BASE2+0x10, 4, 1), + MTK_PIN_DRV_GRP(51, DRV_BASE2+0x10, 8, 1), + MTK_PIN_DRV_GRP(52, DRV_BASE2+0x10, 12, 2), + MTK_PIN_DRV_GRP(53, DRV_BASE2+0x10, 16, 1), + MTK_PIN_DRV_GRP(54, DRV_BASE2+0x10, 20, 1), + MTK_PIN_DRV_GRP(55, DRV_BASE2+0x10, 24, 1), + MTK_PIN_DRV_GRP(56, DRV_BASE2+0x10, 28, 1), + + MTK_PIN_DRV_GRP(57, DRV_BASE2+0x20, 0, 1), + MTK_PIN_DRV_GRP(58, DRV_BASE2+0x20, 0, 1), + MTK_PIN_DRV_GRP(59, DRV_BASE2+0x20, 0, 1), + MTK_PIN_DRV_GRP(60, DRV_BASE2+0x20, 0, 1), + MTK_PIN_DRV_GRP(61, DRV_BASE2+0x20, 0, 1), + MTK_PIN_DRV_GRP(62, DRV_BASE2+0x20, 0, 1), + MTK_PIN_DRV_GRP(63, DRV_BASE2+0x20, 4, 1), + MTK_PIN_DRV_GRP(64, DRV_BASE2+0x20, 8, 1), + MTK_PIN_DRV_GRP(65, DRV_BASE2+0x20, 12, 1), + MTK_PIN_DRV_GRP(66, DRV_BASE2+0x20, 16, 1), + MTK_PIN_DRV_GRP(67, DRV_BASE2+0x20, 20, 1), + MTK_PIN_DRV_GRP(68, DRV_BASE2+0x20, 24, 1), + MTK_PIN_DRV_GRP(69, DRV_BASE2+0x20, 28, 1), + + MTK_PIN_DRV_GRP(70, DRV_BASE2+0x30, 0, 1), + MTK_PIN_DRV_GRP(71, DRV_BASE2+0x30, 4, 1), + MTK_PIN_DRV_GRP(72, DRV_BASE2+0x30, 8, 1), + MTK_PIN_DRV_GRP(73, DRV_BASE2+0x30, 12, 1), + MTK_PIN_DRV_GRP(74, DRV_BASE2+0x30, 16, 1), + MTK_PIN_DRV_GRP(75, DRV_BASE2+0x30, 20, 1), + MTK_PIN_DRV_GRP(76, DRV_BASE2+0x30, 24, 1), + MTK_PIN_DRV_GRP(77, DRV_BASE2+0x30, 28, 3), + MTK_PIN_DRV_GRP(78, DRV_BASE2+0x30, 28, 3), + + MTK_PIN_DRV_GRP(79, DRV_BASE2+0x40, 0, 3), + MTK_PIN_DRV_GRP(80, DRV_BASE2+0x40, 4, 3), + + MTK_PIN_DRV_GRP(81, DRV_BASE2+0x30, 28, 3), + MTK_PIN_DRV_GRP(82, DRV_BASE2+0x30, 28, 3), + + MTK_PIN_DRV_GRP(83, DRV_BASE2+0x40, 8, 3), + MTK_PIN_DRV_GRP(84, DRV_BASE2+0x40, 8, 3), + MTK_PIN_DRV_GRP(85, DRV_BASE2+0x40, 12, 3), + MTK_PIN_DRV_GRP(86, DRV_BASE2+0x40, 16, 3), + MTK_PIN_DRV_GRP(87, DRV_BASE2+0x40, 8, 3), + MTK_PIN_DRV_GRP(88, DRV_BASE2+0x40, 8, 3), + + MTK_PIN_DRV_GRP(89, DRV_BASE2+0x50, 12, 0), + MTK_PIN_DRV_GRP(90, DRV_BASE2+0x50, 12, 0), + MTK_PIN_DRV_GRP(91, DRV_BASE2+0x50, 12, 0), + MTK_PIN_DRV_GRP(92, DRV_BASE2+0x50, 12, 0), + MTK_PIN_DRV_GRP(93, DRV_BASE2+0x50, 12, 0), + MTK_PIN_DRV_GRP(94, DRV_BASE2+0x50, 12, 0), + MTK_PIN_DRV_GRP(95, DRV_BASE2+0x50, 12, 0), + + MTK_PIN_DRV_GRP(96, DRV_BASE1+0xb0, 28, 0), + + MTK_PIN_DRV_GRP(97, DRV_BASE2+0x50, 12, 0), + MTK_PIN_DRV_GRP(98, DRV_BASE2+0x50, 16, 0), + MTK_PIN_DRV_GRP(99, DRV_BASE2+0x50, 20, 1), + MTK_PIN_DRV_GRP(102, DRV_BASE2+0x50, 24, 1), + MTK_PIN_DRV_GRP(103, DRV_BASE2+0x50, 28, 1), + + + MTK_PIN_DRV_GRP(104, DRV_BASE2+0x60, 0, 1), + MTK_PIN_DRV_GRP(105, DRV_BASE2+0x60, 4, 1), + MTK_PIN_DRV_GRP(106, DRV_BASE2+0x60, 4, 1), + MTK_PIN_DRV_GRP(107, DRV_BASE2+0x60, 4, 1), + MTK_PIN_DRV_GRP(108, DRV_BASE2+0x60, 4, 1), + MTK_PIN_DRV_GRP(109, DRV_BASE2+0x60, 8, 2), + MTK_PIN_DRV_GRP(110, DRV_BASE2+0x60, 12, 2), + MTK_PIN_DRV_GRP(111, DRV_BASE2+0x60, 16, 2), + MTK_PIN_DRV_GRP(112, DRV_BASE2+0x60, 20, 2), + MTK_PIN_DRV_GRP(113, DRV_BASE2+0x60, 24, 2), + MTK_PIN_DRV_GRP(114, DRV_BASE2+0x60, 28, 2), + + MTK_PIN_DRV_GRP(115, DRV_BASE2+0x70, 0, 2), + MTK_PIN_DRV_GRP(116, DRV_BASE2+0x70, 4, 2), + MTK_PIN_DRV_GRP(117, DRV_BASE2+0x70, 8, 2), + MTK_PIN_DRV_GRP(118, DRV_BASE2+0x70, 12, 2), + MTK_PIN_DRV_GRP(119, DRV_BASE2+0x70, 16, 2), + MTK_PIN_DRV_GRP(120, DRV_BASE2+0x70, 20, 2), + + MTK_PIN_DRV_GRP(181, DRV_BASE1+0xa0, 12, 1), + MTK_PIN_DRV_GRP(182, DRV_BASE1+0xa0, 16, 1), + MTK_PIN_DRV_GRP(183, DRV_BASE1+0xa0, 20, 1), + MTK_PIN_DRV_GRP(184, DRV_BASE1+0xa0, 24, 1), + MTK_PIN_DRV_GRP(185, DRV_BASE1+0xa0, 28, 1), + + MTK_PIN_DRV_GRP(186, DRV_BASE1+0xb0, 0, 2), + MTK_PIN_DRV_GRP(187, DRV_BASE1+0xb0, 0, 2), + MTK_PIN_DRV_GRP(188, DRV_BASE1+0xb0, 0, 2), + MTK_PIN_DRV_GRP(189, DRV_BASE1+0xb0, 0, 2), + MTK_PIN_DRV_GRP(190, DRV_BASE1+0xb0, 4, 1), + MTK_PIN_DRV_GRP(191, DRV_BASE1+0xb0, 8, 1), + MTK_PIN_DRV_GRP(192, DRV_BASE1+0xb0, 12, 1), + + MTK_PIN_DRV_GRP(197, DRV_BASE1+0xb0, 16, 0), + MTK_PIN_DRV_GRP(198, DRV_BASE1+0xb0, 16, 0), + MTK_PIN_DRV_GRP(199, DRV_BASE1+0xb0, 20, 0), + MTK_PIN_DRV_GRP(200, DRV_BASE1+0xb0, 24, 0), + MTK_PIN_DRV_GRP(201, DRV_BASE1+0xb0, 16, 0), + MTK_PIN_DRV_GRP(202, DRV_BASE1+0xb0, 16, 0) +}; + +static const struct mtk_spec_pull_set spec_pupd[] = { + SPEC_PULL(0, PUPD_BASE1, 0, R0_BASE1, 9, R1_BASE1, 0), + SPEC_PULL(1, PUPD_BASE1, 1, R0_BASE1, 8, R1_BASE1, 1), + SPEC_PULL(2, PUPD_BASE1, 2, R0_BASE1, 7, R1_BASE1, 2), + SPEC_PULL(3, PUPD_BASE1, 3, R0_BASE1, 6, R1_BASE1, 3), + SPEC_PULL(4, PUPD_BASE1, 4, R0_BASE1, 1, R1_BASE1, 4), + SPEC_PULL(5, PUPD_BASE1, 5, R0_BASE1, 0, R1_BASE1, 5), + SPEC_PULL(6, PUPD_BASE1, 6, R0_BASE1, 5, R1_BASE1, 6), + SPEC_PULL(7, PUPD_BASE1, 7, R0_BASE1, 4, R1_BASE1, 7), + SPEC_PULL(8, PUPD_BASE1, 8, R0_BASE1, 3, R1_BASE1, 8), + SPEC_PULL(9, PUPD_BASE1, 9, R0_BASE1, 2, R1_BASE1, 9), + SPEC_PULL(89, PUPD_BASE2, 9, R0_BASE1, 18, R1_BASE2, 9), + SPEC_PULL(90, PUPD_BASE2, 10, R0_BASE1, 19, R1_BASE2, 10), + SPEC_PULL(91, PUPD_BASE2, 11, R0_BASE1, 23, R1_BASE2, 11), + SPEC_PULL(92, PUPD_BASE2, 12, R0_BASE1, 24, R1_BASE2, 12), + SPEC_PULL(93, PUPD_BASE2, 13, R0_BASE1, 25, R1_BASE2, 13), + SPEC_PULL(94, PUPD_BASE2, 14, R0_BASE1, 22, R1_BASE2, 14), + SPEC_PULL(95, PUPD_BASE2, 15, R0_BASE1, 20, R1_BASE2, 15), + SPEC_PULL(96, PUPD_BASE2+0x10, 0, R0_BASE1, 16, R1_BASE2+0x10, 0), + SPEC_PULL(97, PUPD_BASE2+0x10, 1, R0_BASE1, 21, R1_BASE2+0x10, 1), + SPEC_PULL(98, PUPD_BASE2+0x10, 2, R0_BASE1, 17, R1_BASE2+0x10, 2), + SPEC_PULL(197, PUPD_BASE1+0xc0, 5, R0_BASE1, 13, R1_BASE2+0xc0, 5), + SPEC_PULL(198, PUPD_BASE2+0xc0, 6, R0_BASE1, 14, R1_BASE2+0xc0, 6), + SPEC_PULL(199, PUPD_BASE2+0xc0, 7, R0_BASE1, 11, R1_BASE2+0xc0, 7), + SPEC_PULL(200, PUPD_BASE2+0xc0, 8, R0_BASE1, 10, R1_BASE2+0xc0, 8), + SPEC_PULL(201, PUPD_BASE2+0xc0, 9, R0_BASE1, 13, R1_BASE2+0xc0, 9), + SPEC_PULL(202, PUPD_BASE2+0xc0, 10, R0_BASE1, 12, R1_BASE2+0xc0, 10) +}; + +static int spec_pull_set(struct regmap *regmap, unsigned int pin, + unsigned char align, bool isup, unsigned int r1r0) +{ + unsigned int i; + unsigned int reg_pupd, reg_set_r0, reg_set_r1; + unsigned int reg_rst_r0, reg_rst_r1; + bool find = false; + + for (i = 0; i < ARRAY_SIZE(spec_pupd); i++) { + if (pin == spec_pupd[i].pin) { + find = true; + break; + } + } + + if (!find) + return -EINVAL; + + if (isup) + reg_pupd = spec_pupd[i].pupd_offset + align; + else + reg_pupd = spec_pupd[i].pupd_offset + (align << 1); + + regmap_write(regmap, reg_pupd, spec_pupd[i].pupd_bit); + + reg_set_r0 = spec_pupd[i].r0_offset + align; + reg_rst_r0 = spec_pupd[i].r0_offset + (align << 1); + reg_set_r1 = spec_pupd[i].r1_offset + align; + reg_rst_r1 = spec_pupd[i].r1_offset + (align << 1); + + switch (r1r0) { + case MTK_PUPD_SET_R1R0_00: + regmap_write(regmap, reg_rst_r0, spec_pupd[i].r0_bit); + regmap_write(regmap, reg_rst_r1, spec_pupd[i].r1_bit); + break; + case MTK_PUPD_SET_R1R0_01: + regmap_write(regmap, reg_set_r0, spec_pupd[i].r0_bit); + regmap_write(regmap, reg_rst_r1, spec_pupd[i].r1_bit); + break; + case MTK_PUPD_SET_R1R0_10: + regmap_write(regmap, reg_rst_r0, spec_pupd[i].r0_bit); + regmap_write(regmap, reg_set_r1, spec_pupd[i].r1_bit); + break; + case MTK_PUPD_SET_R1R0_11: + regmap_write(regmap, reg_set_r0, spec_pupd[i].r0_bit); + regmap_write(regmap, reg_set_r1, spec_pupd[i].r1_bit); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct mtk_pinctrl_devdata mt8135_pinctrl_data = { + .pins = mtk_pins_mt8135, + .npins = ARRAY_SIZE(mtk_pins_mt8135), + .grp_desc = mt8135_drv_grp, + .n_grp_cls = ARRAY_SIZE(mt8135_drv_grp), + .pin_drv_grp = mt8135_pin_drv, + .n_pin_drv_grps = ARRAY_SIZE(mt8135_pin_drv), + .spec_pull_set = spec_pull_set, + .dir_offset = 0x0000, + .ies_offset = 0x0100, + .pullen_offset = 0x0200, + .smt_offset = 0x0300, + .pullsel_offset = 0x0400, + .invser_offset = 0x0600, + .dout_offset = 0x0800, + .din_offset = 0x0A00, + .pinmux_offset = 0x0C00, + .type1_start = 34, + .type1_end = 149, + .port_shf = 4, + .port_mask = 0xf, + .port_align = 4, + .chip_type = MTK_CHIP_TYPE_BASE, + .eint_offsets = { + .name = "mt8135_eint", + .stat = 0x000, + .ack = 0x040, + .mask = 0x080, + .mask_set = 0x0c0, + .mask_clr = 0x100, + .sens = 0x140, + .sens_set = 0x180, + .sens_clr = 0x1c0, + .soft = 0x200, + .soft_set = 0x240, + .soft_clr = 0x280, + .pol = 0x300, + .pol_set = 0x340, + .pol_clr = 0x380, + .dom_en = 0x400, + .dbnc_ctrl = 0x500, + .dbnc_set = 0x600, + .dbnc_clr = 0x700, + .port_mask = 7, + .ports = 6, + }, + .ap_num = 192, + .db_cnt = 16, +}; + +static int mt8135_pinctrl_probe(struct platform_device *pdev) +{ + return mtk_pctrl_init(pdev, &mt8135_pinctrl_data); +} + +static const struct of_device_id mt8135_pctrl_match[] = { + { + .compatible = "mediatek,mt8135-pinctrl", + }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8135_pctrl_match); + +static struct platform_driver mtk_pinctrl_driver = { + .probe = mt8135_pinctrl_probe, + .driver = { + .name = "mediatek-mt8135-pinctrl", + .owner = THIS_MODULE, + .of_match_table = mt8135_pctrl_match, + }, +}; + +static int __init mtk_pinctrl_init(void) +{ + return platform_driver_register(&mtk_pinctrl_driver); +} + +module_init(mtk_pinctrl_init); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MediaTek Pinctrl Driver"); +MODULE_AUTHOR("Hongzhou Yang "); diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8173.c b/drivers/pinctrl/mediatek/pinctrl-mt8173.c new file mode 100644 index 00000000000000..412ea84836a106 --- /dev/null +++ b/drivers/pinctrl/mediatek/pinctrl-mt8173.c @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2014-2015 MediaTek Inc. + * Author: Hongzhou.Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-mtk-common.h" +#include "pinctrl-mtk-mt8173.h" + +#define DRV_BASE 0xb00 + +/** + * struct mtk_pin_ies_smt_set - For special pins' ies and smt setting. + * @start: The start pin number of those special pins. + * @end: The end pin number of those special pins. + * @offset: The offset of special setting register. + * @bit: The bit of special setting register. + */ +struct mtk_pin_ies_smt_set { + unsigned int start; + unsigned int end; + unsigned int offset; + unsigned char bit; +}; + +#define MTK_PIN_IES_SMT_SET(_start, _end, _offset, _bit) \ + { \ + .start = _start, \ + .end = _end, \ + .bit = _bit, \ + .offset = _offset, \ + } + +/** + * struct mtk_pin_spec_pupd_set - For special pins' pull up/down setting. + * @pin: The pin number. + * @offset: The offset of special pull up/down setting register. + * @pupd_bit: The pull up/down bit in this register. + * @r0_bit: The r0 bit of pull resistor. + * @r1_bit: The r1 bit of pull resistor. + */ +struct mtk_pin_spec_pupd_set { + unsigned int pin; + unsigned int offset; + unsigned char pupd_bit; + unsigned char r1_bit; + unsigned char r0_bit; +}; + +#define MTK_PIN_PUPD_SPEC(_pin, _offset, _pupd, _r1, _r0) \ + { \ + .pin = _pin, \ + .offset = _offset, \ + .pupd_bit = _pupd, \ + .r1_bit = _r1, \ + .r0_bit = _r0, \ + } + +static const struct mtk_pin_spec_pupd_set mt8173_spec_pupd[] = { + MTK_PIN_PUPD_SPEC(119, 0xe00, 2, 1, 0), /* KROW0 */ + MTK_PIN_PUPD_SPEC(120, 0xe00, 6, 5, 4), /* KROW1 */ + MTK_PIN_PUPD_SPEC(121, 0xe00, 10, 9, 8), /* KROW2 */ + MTK_PIN_PUPD_SPEC(122, 0xe10, 2, 1, 0), /* KCOL0 */ + MTK_PIN_PUPD_SPEC(123, 0xe10, 6, 5, 4), /* KCOL1 */ + MTK_PIN_PUPD_SPEC(124, 0xe10, 10, 9, 8), /* KCOL2 */ + + MTK_PIN_PUPD_SPEC(67, 0xd10, 2, 1, 0), /* ms0 DS */ + MTK_PIN_PUPD_SPEC(68, 0xd00, 2, 1, 0), /* ms0 RST */ + MTK_PIN_PUPD_SPEC(66, 0xc10, 2, 1, 0), /* ms0 cmd */ + MTK_PIN_PUPD_SPEC(65, 0xc00, 2, 1, 0), /* ms0 clk */ + MTK_PIN_PUPD_SPEC(57, 0xc20, 2, 1, 0), /* ms0 data0 */ + MTK_PIN_PUPD_SPEC(58, 0xc20, 2, 1, 0), /* ms0 data1 */ + MTK_PIN_PUPD_SPEC(59, 0xc20, 2, 1, 0), /* ms0 data2 */ + MTK_PIN_PUPD_SPEC(60, 0xc20, 2, 1, 0), /* ms0 data3 */ + MTK_PIN_PUPD_SPEC(61, 0xc20, 2, 1, 0), /* ms0 data4 */ + MTK_PIN_PUPD_SPEC(62, 0xc20, 2, 1, 0), /* ms0 data5 */ + MTK_PIN_PUPD_SPEC(63, 0xc20, 2, 1, 0), /* ms0 data6 */ + MTK_PIN_PUPD_SPEC(64, 0xc20, 2, 1, 0), /* ms0 data7 */ + + MTK_PIN_PUPD_SPEC(78, 0xc50, 2, 1, 0), /* ms1 cmd */ + MTK_PIN_PUPD_SPEC(73, 0xd20, 2, 1, 0), /* ms1 dat0 */ + MTK_PIN_PUPD_SPEC(74, 0xd20, 6, 5, 4), /* ms1 dat1 */ + MTK_PIN_PUPD_SPEC(75, 0xd20, 10, 9, 8), /* ms1 dat2 */ + MTK_PIN_PUPD_SPEC(76, 0xd20, 14, 13, 12), /* ms1 dat3 */ + MTK_PIN_PUPD_SPEC(77, 0xc40, 2, 1, 0), /* ms1 clk */ + + MTK_PIN_PUPD_SPEC(100, 0xd40, 2, 1, 0), /* ms2 dat0 */ + MTK_PIN_PUPD_SPEC(101, 0xd40, 6, 5, 4), /* ms2 dat1 */ + MTK_PIN_PUPD_SPEC(102, 0xd40, 10, 9, 8), /* ms2 dat2 */ + MTK_PIN_PUPD_SPEC(103, 0xd40, 14, 13, 12), /* ms2 dat3 */ + MTK_PIN_PUPD_SPEC(104, 0xc80, 2, 1, 0), /* ms2 clk */ + MTK_PIN_PUPD_SPEC(105, 0xc90, 2, 1, 0), /* ms2 cmd */ + + MTK_PIN_PUPD_SPEC(22, 0xd60, 2, 1, 0), /* ms3 dat0 */ + MTK_PIN_PUPD_SPEC(23, 0xd60, 6, 5, 4), /* ms3 dat1 */ + MTK_PIN_PUPD_SPEC(24, 0xd60, 10, 9, 8), /* ms3 dat2 */ + MTK_PIN_PUPD_SPEC(25, 0xd60, 14, 13, 12), /* ms3 dat3 */ + MTK_PIN_PUPD_SPEC(26, 0xcc0, 2, 1, 0), /* ms3 clk */ + MTK_PIN_PUPD_SPEC(27, 0xcd0, 2, 1, 0) /* ms3 cmd */ +}; + +static int spec_pull_set(struct regmap *regmap, unsigned int pin, + unsigned char align, bool isup, unsigned int r1r0) +{ + unsigned int i; + unsigned int reg_pupd, reg_set, reg_rst; + unsigned int bit_pupd, bit_r0, bit_r1; + const struct mtk_pin_spec_pupd_set *spec_pupd_pin; + bool find = false; + + for (i = 0; i < ARRAY_SIZE(mt8173_spec_pupd); i++) { + if (pin == mt8173_spec_pupd[i].pin) { + find = true; + break; + } + } + + if (!find) + return -EINVAL; + + spec_pupd_pin = mt8173_spec_pupd + i; + reg_set = spec_pupd_pin->offset + align; + reg_rst = spec_pupd_pin->offset + (align << 1); + + if (isup) + reg_pupd = reg_rst; + else + reg_pupd = reg_set; + + bit_pupd = BIT(spec_pupd_pin->pupd_bit); + regmap_write(regmap, reg_pupd, bit_pupd); + + bit_r0 = BIT(spec_pupd_pin->r0_bit); + bit_r1 = BIT(spec_pupd_pin->r1_bit); + + switch (r1r0) { + case MTK_PUPD_SET_R1R0_00: + regmap_write(regmap, reg_rst, bit_r0); + regmap_write(regmap, reg_rst, bit_r1); + break; + case MTK_PUPD_SET_R1R0_01: + regmap_write(regmap, reg_set, bit_r0); + regmap_write(regmap, reg_rst, bit_r1); + break; + case MTK_PUPD_SET_R1R0_10: + regmap_write(regmap, reg_rst, bit_r0); + regmap_write(regmap, reg_set, bit_r1); + break; + case MTK_PUPD_SET_R1R0_11: + regmap_write(regmap, reg_set, bit_r0); + regmap_write(regmap, reg_set, bit_r1); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct mtk_pin_ies_smt_set mt8173_ies_smt_set[] = { + MTK_PIN_IES_SMT_SET(0, 4, 0x930, 1), + MTK_PIN_IES_SMT_SET(5, 9, 0x930, 2), + MTK_PIN_IES_SMT_SET(10, 13, 0x930, 10), + MTK_PIN_IES_SMT_SET(14, 15, 0x940, 10), + MTK_PIN_IES_SMT_SET(16, 16, 0x930, 0), + MTK_PIN_IES_SMT_SET(17, 17, 0x950, 2), + MTK_PIN_IES_SMT_SET(18, 21, 0x940, 3), + MTK_PIN_IES_SMT_SET(29, 32, 0x930, 3), + MTK_PIN_IES_SMT_SET(33, 33, 0x930, 4), + MTK_PIN_IES_SMT_SET(34, 36, 0x930, 5), + MTK_PIN_IES_SMT_SET(37, 38, 0x930, 6), + MTK_PIN_IES_SMT_SET(39, 39, 0x930, 7), + MTK_PIN_IES_SMT_SET(40, 41, 0x930, 9), + MTK_PIN_IES_SMT_SET(42, 42, 0x940, 0), + MTK_PIN_IES_SMT_SET(43, 44, 0x930, 11), + MTK_PIN_IES_SMT_SET(45, 46, 0x930, 12), + MTK_PIN_IES_SMT_SET(57, 64, 0xc20, 13), + MTK_PIN_IES_SMT_SET(65, 65, 0xc10, 13), + MTK_PIN_IES_SMT_SET(66, 66, 0xc00, 13), + MTK_PIN_IES_SMT_SET(67, 67, 0xd10, 13), + MTK_PIN_IES_SMT_SET(68, 68, 0xd00, 13), + MTK_PIN_IES_SMT_SET(69, 72, 0x940, 14), + MTK_PIN_IES_SMT_SET(73, 76, 0xc60, 13), + MTK_PIN_IES_SMT_SET(77, 77, 0xc40, 13), + MTK_PIN_IES_SMT_SET(78, 78, 0xc50, 13), + MTK_PIN_IES_SMT_SET(79, 82, 0x940, 15), + MTK_PIN_IES_SMT_SET(83, 83, 0x950, 0), + MTK_PIN_IES_SMT_SET(84, 85, 0x950, 1), + MTK_PIN_IES_SMT_SET(86, 91, 0x950, 2), + MTK_PIN_IES_SMT_SET(92, 92, 0x930, 13), + MTK_PIN_IES_SMT_SET(93, 95, 0x930, 14), + MTK_PIN_IES_SMT_SET(96, 99, 0x930, 15), + MTK_PIN_IES_SMT_SET(100, 103, 0xca0, 13), + MTK_PIN_IES_SMT_SET(104, 104, 0xc80, 13), + MTK_PIN_IES_SMT_SET(105, 105, 0xc90, 13), + MTK_PIN_IES_SMT_SET(106, 107, 0x940, 4), + MTK_PIN_IES_SMT_SET(108, 112, 0x940, 1), + MTK_PIN_IES_SMT_SET(113, 116, 0x940, 2), + MTK_PIN_IES_SMT_SET(117, 118, 0x940, 5), + MTK_PIN_IES_SMT_SET(119, 124, 0x940, 6), + MTK_PIN_IES_SMT_SET(125, 126, 0x940, 7), + MTK_PIN_IES_SMT_SET(127, 127, 0x940, 0), + MTK_PIN_IES_SMT_SET(128, 128, 0x950, 8), + MTK_PIN_IES_SMT_SET(129, 130, 0x950, 9), + MTK_PIN_IES_SMT_SET(131, 132, 0x950, 8), + MTK_PIN_IES_SMT_SET(133, 134, 0x910, 8) +}; + +static int spec_ies_smt_set(struct regmap *regmap, unsigned int pin, + unsigned char align, int value) +{ + unsigned int i, reg_addr, bit; + bool find = false; + + for (i = 0; i < ARRAY_SIZE(mt8173_ies_smt_set); i++) { + if (pin >= mt8173_ies_smt_set[i].start && + pin <= mt8173_ies_smt_set[i].end) { + find = true; + break; + } + } + + if (!find) + return -EINVAL; + + if (value) + reg_addr = mt8173_ies_smt_set[i].offset + align; + else + reg_addr = mt8173_ies_smt_set[i].offset + (align << 1); + + bit = BIT(mt8173_ies_smt_set[i].bit); + regmap_write(regmap, reg_addr, bit); + return 0; +} + +static const struct mtk_drv_group_desc mt8173_drv_grp[] = { + /* 0E4E8SR 4/8/12/16 */ + MTK_DRV_GRP(4, 16, 1, 2, 4), + /* 0E2E4SR 2/4/6/8 */ + MTK_DRV_GRP(2, 8, 1, 2, 2), + /* E8E4E2 2/4/6/8/10/12/14/16 */ + MTK_DRV_GRP(2, 16, 0, 2, 2) +}; + +static const struct mtk_pin_drv_grp mt8173_pin_drv[] = { + MTK_PIN_DRV_GRP(0, DRV_BASE+0x20, 12, 0), + MTK_PIN_DRV_GRP(1, DRV_BASE+0x20, 12, 0), + MTK_PIN_DRV_GRP(2, DRV_BASE+0x20, 12, 0), + MTK_PIN_DRV_GRP(3, DRV_BASE+0x20, 12, 0), + MTK_PIN_DRV_GRP(4, DRV_BASE+0x20, 12, 0), + MTK_PIN_DRV_GRP(5, DRV_BASE+0x30, 0, 0), + MTK_PIN_DRV_GRP(6, DRV_BASE+0x30, 0, 0), + MTK_PIN_DRV_GRP(7, DRV_BASE+0x30, 0, 0), + MTK_PIN_DRV_GRP(8, DRV_BASE+0x30, 0, 0), + MTK_PIN_DRV_GRP(9, DRV_BASE+0x30, 0, 0), + MTK_PIN_DRV_GRP(10, DRV_BASE+0x30, 4, 1), + MTK_PIN_DRV_GRP(11, DRV_BASE+0x30, 4, 1), + MTK_PIN_DRV_GRP(12, DRV_BASE+0x30, 4, 1), + MTK_PIN_DRV_GRP(13, DRV_BASE+0x30, 4, 1), + MTK_PIN_DRV_GRP(14, DRV_BASE+0x40, 8, 1), + MTK_PIN_DRV_GRP(15, DRV_BASE+0x40, 8, 1), + MTK_PIN_DRV_GRP(16, DRV_BASE, 8, 1), + MTK_PIN_DRV_GRP(17, 0xce0, 8, 2), + MTK_PIN_DRV_GRP(22, 0xce0, 8, 2), + MTK_PIN_DRV_GRP(23, 0xce0, 8, 2), + MTK_PIN_DRV_GRP(24, 0xce0, 8, 2), + MTK_PIN_DRV_GRP(25, 0xce0, 8, 2), + MTK_PIN_DRV_GRP(26, 0xcc0, 8, 2), + MTK_PIN_DRV_GRP(27, 0xcd0, 8, 2), + MTK_PIN_DRV_GRP(28, 0xd70, 8, 2), + MTK_PIN_DRV_GRP(29, DRV_BASE+0x80, 12, 1), + MTK_PIN_DRV_GRP(30, DRV_BASE+0x80, 12, 1), + MTK_PIN_DRV_GRP(31, DRV_BASE+0x80, 12, 1), + MTK_PIN_DRV_GRP(32, DRV_BASE+0x80, 12, 1), + MTK_PIN_DRV_GRP(33, DRV_BASE+0x10, 12, 1), + MTK_PIN_DRV_GRP(34, DRV_BASE+0x10, 8, 1), + MTK_PIN_DRV_GRP(35, DRV_BASE+0x10, 8, 1), + MTK_PIN_DRV_GRP(36, DRV_BASE+0x10, 8, 1), + MTK_PIN_DRV_GRP(37, DRV_BASE+0x10, 4, 1), + MTK_PIN_DRV_GRP(38, DRV_BASE+0x10, 4, 1), + MTK_PIN_DRV_GRP(39, DRV_BASE+0x20, 0, 0), + MTK_PIN_DRV_GRP(40, DRV_BASE+0x20, 8, 0), + MTK_PIN_DRV_GRP(41, DRV_BASE+0x20, 8, 0), + MTK_PIN_DRV_GRP(42, DRV_BASE+0x50, 8, 1), + MTK_PIN_DRV_GRP(57, 0xc20, 8, 2), + MTK_PIN_DRV_GRP(58, 0xc20, 8, 2), + MTK_PIN_DRV_GRP(59, 0xc20, 8, 2), + MTK_PIN_DRV_GRP(60, 0xc20, 8, 2), + MTK_PIN_DRV_GRP(61, 0xc20, 8, 2), + MTK_PIN_DRV_GRP(62, 0xc20, 8, 2), + MTK_PIN_DRV_GRP(63, 0xc20, 8, 2), + MTK_PIN_DRV_GRP(64, 0xc20, 8, 2), + MTK_PIN_DRV_GRP(65, 0xc00, 8, 2), + MTK_PIN_DRV_GRP(66, 0xc10, 8, 2), + MTK_PIN_DRV_GRP(67, 0xd10, 8, 2), + MTK_PIN_DRV_GRP(68, 0xd00, 8, 2), + MTK_PIN_DRV_GRP(69, DRV_BASE+0x80, 0, 1), + MTK_PIN_DRV_GRP(70, DRV_BASE+0x80, 0, 1), + MTK_PIN_DRV_GRP(71, DRV_BASE+0x80, 0, 1), + MTK_PIN_DRV_GRP(72, DRV_BASE+0x80, 0, 1), + MTK_PIN_DRV_GRP(73, 0xc60, 8, 2), + MTK_PIN_DRV_GRP(74, 0xc60, 8, 2), + MTK_PIN_DRV_GRP(75, 0xc60, 8, 2), + MTK_PIN_DRV_GRP(76, 0xc60, 8, 2), + MTK_PIN_DRV_GRP(77, 0xc40, 8, 2), + MTK_PIN_DRV_GRP(78, 0xc50, 8, 2), + MTK_PIN_DRV_GRP(79, DRV_BASE+0x70, 12, 1), + MTK_PIN_DRV_GRP(80, DRV_BASE+0x70, 12, 1), + MTK_PIN_DRV_GRP(81, DRV_BASE+0x70, 12, 1), + MTK_PIN_DRV_GRP(82, DRV_BASE+0x70, 12, 1), + MTK_PIN_DRV_GRP(83, DRV_BASE, 4, 1), + MTK_PIN_DRV_GRP(84, DRV_BASE, 0, 1), + MTK_PIN_DRV_GRP(85, DRV_BASE, 0, 1), + MTK_PIN_DRV_GRP(85, DRV_BASE+0x60, 8, 1), + MTK_PIN_DRV_GRP(86, DRV_BASE+0x60, 8, 1), + MTK_PIN_DRV_GRP(87, DRV_BASE+0x60, 8, 1), + MTK_PIN_DRV_GRP(88, DRV_BASE+0x60, 8, 1), + MTK_PIN_DRV_GRP(89, DRV_BASE+0x60, 8, 1), + MTK_PIN_DRV_GRP(90, DRV_BASE+0x60, 8, 1), + MTK_PIN_DRV_GRP(91, DRV_BASE+0x60, 8, 1), + MTK_PIN_DRV_GRP(92, DRV_BASE+0x60, 4, 0), + MTK_PIN_DRV_GRP(93, DRV_BASE+0x60, 0, 0), + MTK_PIN_DRV_GRP(94, DRV_BASE+0x60, 0, 0), + MTK_PIN_DRV_GRP(95, DRV_BASE+0x60, 0, 0), + MTK_PIN_DRV_GRP(96, DRV_BASE+0x80, 8, 1), + MTK_PIN_DRV_GRP(97, DRV_BASE+0x80, 8, 1), + MTK_PIN_DRV_GRP(98, DRV_BASE+0x80, 8, 1), + MTK_PIN_DRV_GRP(99, DRV_BASE+0x80, 8, 1), + MTK_PIN_DRV_GRP(100, 0xca0, 8, 2), + MTK_PIN_DRV_GRP(101, 0xca0, 8, 2), + MTK_PIN_DRV_GRP(102, 0xca0, 8, 2), + MTK_PIN_DRV_GRP(103, 0xca0, 8, 2), + MTK_PIN_DRV_GRP(104, 0xc80, 8, 2), + MTK_PIN_DRV_GRP(105, 0xc90, 8, 2), + MTK_PIN_DRV_GRP(108, DRV_BASE+0x50, 0, 1), + MTK_PIN_DRV_GRP(109, DRV_BASE+0x50, 0, 1), + MTK_PIN_DRV_GRP(110, DRV_BASE+0x50, 0, 1), + MTK_PIN_DRV_GRP(111, DRV_BASE+0x50, 0, 1), + MTK_PIN_DRV_GRP(112, DRV_BASE+0x50, 0, 1), + MTK_PIN_DRV_GRP(113, DRV_BASE+0x80, 4, 1), + MTK_PIN_DRV_GRP(114, DRV_BASE+0x80, 4, 1), + MTK_PIN_DRV_GRP(115, DRV_BASE+0x80, 4, 1), + MTK_PIN_DRV_GRP(116, DRV_BASE+0x80, 4, 1), + MTK_PIN_DRV_GRP(117, DRV_BASE+0x90, 0, 1), + MTK_PIN_DRV_GRP(118, DRV_BASE+0x90, 0, 1), + MTK_PIN_DRV_GRP(119, DRV_BASE+0x50, 4, 1), + MTK_PIN_DRV_GRP(120, DRV_BASE+0x50, 4, 1), + MTK_PIN_DRV_GRP(121, DRV_BASE+0x50, 4, 1), + MTK_PIN_DRV_GRP(122, DRV_BASE+0x50, 4, 1), + MTK_PIN_DRV_GRP(123, DRV_BASE+0x50, 4, 1), + MTK_PIN_DRV_GRP(124, DRV_BASE+0x50, 4, 1), + MTK_PIN_DRV_GRP(125, DRV_BASE+0x30, 12, 1), + MTK_PIN_DRV_GRP(126, DRV_BASE+0x30, 12, 1), + MTK_PIN_DRV_GRP(127, DRV_BASE+0x50, 8, 1), + MTK_PIN_DRV_GRP(128, DRV_BASE+0x40, 0, 1), + MTK_PIN_DRV_GRP(129, DRV_BASE+0x40, 0, 1), + MTK_PIN_DRV_GRP(130, DRV_BASE+0x40, 0, 1), + MTK_PIN_DRV_GRP(131, DRV_BASE+0x40, 0, 1), + MTK_PIN_DRV_GRP(132, DRV_BASE+0x40, 0, 1) +}; + +static const struct mtk_pinctrl_devdata mt8173_pinctrl_data = { + .pins = mtk_pins_mt8173, + .npins = ARRAY_SIZE(mtk_pins_mt8173), + .grp_desc = mt8173_drv_grp, + .n_grp_cls = ARRAY_SIZE(mt8173_drv_grp), + .pin_drv_grp = mt8173_pin_drv, + .n_pin_drv_grps = ARRAY_SIZE(mt8173_pin_drv), + .spec_pull_set = spec_pull_set, + .spec_ies_smt_set = spec_ies_smt_set, + .dir_offset = 0x0000, + .pullen_offset = 0x0100, + .pullsel_offset = 0x0200, + .dout_offset = 0x0400, + .din_offset = 0x0500, + .pinmux_offset = 0x0600, + .type1_start = 135, + .type1_end = 135, + .port_shf = 4, + .port_mask = 0xf, + .port_align = 4, + .eint_offsets = { + .name = "mt8173_eint", + .stat = 0x000, + .ack = 0x040, + .mask = 0x080, + .mask_set = 0x0c0, + .mask_clr = 0x100, + .sens = 0x140, + .sens_set = 0x180, + .sens_clr = 0x1c0, + .soft = 0x200, + .soft_set = 0x240, + .soft_clr = 0x280, + .pol = 0x300, + .pol_set = 0x340, + .pol_clr = 0x380, + .dom_en = 0x400, + .dbnc_ctrl = 0x500, + .dbnc_set = 0x600, + .dbnc_clr = 0x700, + .port_mask = 7, + .ports = 6, + }, + .ap_num = 224, + .db_cnt = 16, +}; + +static int mt8173_pinctrl_probe(struct platform_device *pdev) +{ + return mtk_pctrl_init(pdev, &mt8173_pinctrl_data); +} + +static const struct of_device_id mt8173_pctrl_match[] = { + { + .compatible = "mediatek,mt8173-pinctrl", + }, + { } +}; +MODULE_DEVICE_TABLE(of, mt8173_pctrl_match); + +static struct platform_driver mtk_pinctrl_driver = { + .probe = mt8173_pinctrl_probe, + .driver = { + .name = "mediatek-mt8173-pinctrl", + .of_match_table = mt8173_pctrl_match, + }, +}; + +static int __init mtk_pinctrl_init(void) +{ + return platform_driver_register(&mtk_pinctrl_driver); +} + +module_init(mtk_pinctrl_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MediaTek Pinctrl Driver"); +MODULE_AUTHOR("Hongzhou Yang "); diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c new file mode 100644 index 00000000000000..493294c0ebe6fa --- /dev/null +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c @@ -0,0 +1,1257 @@ +/* + * mt65xx pinctrl driver based on Allwinner A1X pinctrl driver. + * Copyright (c) 2014 MediaTek Inc. + * Author: Hongzhou.Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../core.h" +#include "../pinconf.h" +#include "../pinctrl-utils.h" +#include "pinctrl-mtk-common.h" + +#define MAX_GPIO_MODE_PER_REG 5 +#define GPIO_MODE_BITS 3 + +static const char * const mtk_gpio_functions[] = { + "func0", "func1", "func2", "func3", + "func4", "func5", "func6", "func7", +}; + +/* + * There are two base address for pull related configuration + * in mt8135, and different GPIO pins use different base address. + * When pin number greater than type1_start and less than type1_end, + * should use the second base address. + */ +static struct regmap *mtk_get_regmap(struct mtk_pinctrl *pctl, + unsigned long pin) +{ + if (pin >= pctl->devdata->type1_start && pin < pctl->devdata->type1_end) + return pctl->regmap2; + return pctl->regmap1; +} + +static unsigned int mtk_get_port(struct mtk_pinctrl *pctl, unsigned long pin) +{ + /* Different SoC has different mask and port shift. */ + return ((pin >> 4) & pctl->devdata->port_mask) + << pctl->devdata->port_shf; +} + +static int mtk_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned offset, + bool input) +{ + unsigned int reg_addr; + unsigned int bit; + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dir_offset; + bit = BIT(offset & 0xf); + + if (input) + /* Different SoC has different alignment offset. */ + reg_addr = CLR_ADDR(reg_addr, pctl); + else + reg_addr = SET_ADDR(reg_addr, pctl); + + regmap_write(mtk_get_regmap(pctl, offset), reg_addr, bit); + return 0; +} + +static void mtk_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + unsigned int reg_addr; + unsigned int bit; + struct mtk_pinctrl *pctl = dev_get_drvdata(chip->dev); + + reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dout_offset; + bit = BIT(offset & 0xf); + + if (value) + reg_addr = SET_ADDR(reg_addr, pctl); + else + reg_addr = CLR_ADDR(reg_addr, pctl); + + regmap_write(mtk_get_regmap(pctl, offset), reg_addr, bit); +} + +static void mtk_pconf_set_ies_smt(struct mtk_pinctrl *pctl, unsigned pin, + int value, enum pin_config_param param) +{ + unsigned int reg_addr, offset; + unsigned int bit; + int ret; + + /* + * Due to some pins are irregular, their input enable and smt + * control register are discontinuous, but they are mapping together. + * So we need this special handle. + */ + if (pctl->devdata->spec_ies_smt_set) { + ret = pctl->devdata->spec_ies_smt_set(mtk_get_regmap(pctl, pin), + pin, pctl->devdata->port_align, value); + if (!ret) + return; + } + + bit = BIT(pin & 0xf); + + if (param == PIN_CONFIG_INPUT_ENABLE) + offset = pctl->devdata->ies_offset; + else + offset = pctl->devdata->smt_offset; + + if (value) + reg_addr = SET_ADDR(mtk_get_port(pctl, pin) + offset, pctl); + else + reg_addr = CLR_ADDR(mtk_get_port(pctl, pin) + offset, pctl); + + regmap_write(mtk_get_regmap(pctl, pin), reg_addr, bit); +} + +static const struct mtk_pin_drv_grp *mtk_find_pin_drv_grp_by_pin( + struct mtk_pinctrl *pctl, unsigned long pin) { + int i; + + for (i = 0; i < pctl->devdata->n_pin_drv_grps; i++) { + const struct mtk_pin_drv_grp *pin_drv = + pctl->devdata->pin_drv_grp + i; + if (pin == pin_drv->pin) + return pin_drv; + } + + return NULL; +} + +static int mtk_pconf_set_driving(struct mtk_pinctrl *pctl, + unsigned int pin, unsigned char driving) +{ + const struct mtk_pin_drv_grp *pin_drv; + unsigned int val; + unsigned int bits, mask, shift; + const struct mtk_drv_group_desc *drv_grp; + + if (pin >= pctl->devdata->npins) + return -EINVAL; + + pin_drv = mtk_find_pin_drv_grp_by_pin(pctl, pin); + if (!pin_drv || pin_drv->grp > pctl->devdata->n_grp_cls) + return -EINVAL; + + drv_grp = pctl->devdata->grp_desc + pin_drv->grp; + if (driving >= drv_grp->min_drv && driving <= drv_grp->max_drv + && !(driving % drv_grp->step)) { + val = driving / drv_grp->step - 1; + bits = drv_grp->high_bit - drv_grp->low_bit + 1; + mask = BIT(bits) - 1; + shift = pin_drv->bit + drv_grp->low_bit; + mask <<= shift; + val <<= shift; + return regmap_update_bits(mtk_get_regmap(pctl, pin), + pin_drv->offset, mask, val); + } + + return -EINVAL; +} + +static int mtk_pconf_set_pull_select(struct mtk_pinctrl *pctl, + unsigned int pin, bool enable, bool isup, unsigned int arg) +{ + unsigned int bit; + unsigned int reg_pullen, reg_pullsel; + int ret; + + /* Some pins' pull setting are very different, + * they have separate pull up/down bit, R0 and R1 + * resistor bit, so we need this special handle. + */ + if (pctl->devdata->spec_pull_set) { + ret = pctl->devdata->spec_pull_set(mtk_get_regmap(pctl, pin), + pin, pctl->devdata->port_align, isup, arg); + if (!ret) + return 0; + } + + /* For generic pull config, default arg value should be 0 or 1. */ + if (arg != 0 && arg != 1) { + dev_err(pctl->dev, "invalid pull-up argument %d on pin %d .\n", + arg, pin); + return -EINVAL; + } + + bit = BIT(pin & 0xf); + if (enable) + reg_pullen = SET_ADDR(mtk_get_port(pctl, pin) + + pctl->devdata->pullen_offset, pctl); + else + reg_pullen = CLR_ADDR(mtk_get_port(pctl, pin) + + pctl->devdata->pullen_offset, pctl); + + if (isup) + reg_pullsel = SET_ADDR(mtk_get_port(pctl, pin) + + pctl->devdata->pullsel_offset, pctl); + else + reg_pullsel = CLR_ADDR(mtk_get_port(pctl, pin) + + pctl->devdata->pullsel_offset, pctl); + + regmap_write(mtk_get_regmap(pctl, pin), reg_pullen, bit); + regmap_write(mtk_get_regmap(pctl, pin), reg_pullsel, bit); + return 0; +} + +static int mtk_pconf_parse_conf(struct pinctrl_dev *pctldev, + unsigned int pin, enum pin_config_param param, + enum pin_config_param arg) +{ + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + mtk_pconf_set_pull_select(pctl, pin, false, false, arg); + break; + case PIN_CONFIG_BIAS_PULL_UP: + mtk_pconf_set_pull_select(pctl, pin, true, true, arg); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + mtk_pconf_set_pull_select(pctl, pin, true, false, arg); + break; + case PIN_CONFIG_INPUT_ENABLE: + mtk_pconf_set_ies_smt(pctl, pin, arg, param); + break; + case PIN_CONFIG_OUTPUT: + mtk_gpio_set(pctl->chip, pin, arg); + mtk_pmx_gpio_set_direction(pctldev, NULL, pin, false); + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + mtk_pconf_set_ies_smt(pctl, pin, arg, param); + break; + case PIN_CONFIG_DRIVE_STRENGTH: + mtk_pconf_set_driving(pctl, pin, arg); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mtk_pconf_group_get(struct pinctrl_dev *pctldev, + unsigned group, + unsigned long *config) +{ + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + *config = pctl->groups[group].config; + + return 0; +} + +static int mtk_pconf_group_set(struct pinctrl_dev *pctldev, unsigned group, + unsigned long *configs, unsigned num_configs) +{ + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct mtk_pinctrl_group *g = &pctl->groups[group]; + int i; + + for (i = 0; i < num_configs; i++) { + mtk_pconf_parse_conf(pctldev, g->pin, + pinconf_to_config_param(configs[i]), + pinconf_to_config_argument(configs[i])); + + g->config = configs[i]; + } + + return 0; +} + +static const struct pinconf_ops mtk_pconf_ops = { + .pin_config_group_get = mtk_pconf_group_get, + .pin_config_group_set = mtk_pconf_group_set, +}; + +static struct mtk_pinctrl_group * +mtk_pctrl_find_group_by_pin(struct mtk_pinctrl *pctl, u32 pin) +{ + int i; + + for (i = 0; i < pctl->ngroups; i++) { + struct mtk_pinctrl_group *grp = pctl->groups + i; + + if (grp->pin == pin) + return grp; + } + + return NULL; +} + +static const struct mtk_desc_function *mtk_pctrl_find_function_by_pin( + struct mtk_pinctrl *pctl, u32 pin_num, u32 fnum) +{ + const struct mtk_desc_pin *pin = pctl->devdata->pins + pin_num; + const struct mtk_desc_function *func = pin->functions; + + while (func && func->name) { + if (func->muxval == fnum) + return func; + func++; + } + + return NULL; +} + +static bool mtk_pctrl_is_function_valid(struct mtk_pinctrl *pctl, + u32 pin_num, u32 fnum) +{ + int i; + + for (i = 0; i < pctl->devdata->npins; i++) { + const struct mtk_desc_pin *pin = pctl->devdata->pins + i; + + if (pin->pin.number == pin_num) { + const struct mtk_desc_function *func = + pin->functions; + + while (func && func->name) { + if (func->muxval == fnum) + return true; + func++; + } + + break; + } + } + + return false; +} + +static int mtk_pctrl_dt_node_to_map_func(struct mtk_pinctrl *pctl, + u32 pin, u32 fnum, struct mtk_pinctrl_group *grp, + struct pinctrl_map **map, unsigned *reserved_maps, + unsigned *num_maps) +{ + bool ret; + + if (*num_maps == *reserved_maps) + return -ENOSPC; + + (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; + (*map)[*num_maps].data.mux.group = grp->name; + + ret = mtk_pctrl_is_function_valid(pctl, pin, fnum); + if (!ret) { + dev_err(pctl->dev, "invalid function %d on pin %d .\n", + fnum, pin); + return -EINVAL; + } + + (*map)[*num_maps].data.mux.function = mtk_gpio_functions[fnum]; + (*num_maps)++; + + return 0; +} + +static int mtk_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct device_node *node, + struct pinctrl_map **map, + unsigned *reserved_maps, + unsigned *num_maps) +{ + struct property *pins; + u32 pinfunc, pin, func; + int num_pins, num_funcs, maps_per_pin; + unsigned long *configs; + unsigned int num_configs; + bool has_config = 0; + int i, err; + unsigned reserve = 0; + struct mtk_pinctrl_group *grp; + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + pins = of_find_property(node, "pinmux", NULL); + if (!pins) { + dev_err(pctl->dev, "missing pins property in node %s .\n", + node->name); + return -EINVAL; + } + + err = pinconf_generic_parse_dt_config(node, pctldev, &configs, + &num_configs); + if (num_configs) + has_config = 1; + + num_pins = pins->length / sizeof(u32); + num_funcs = num_pins; + maps_per_pin = 0; + if (num_funcs) + maps_per_pin++; + if (has_config && num_pins >= 1) + maps_per_pin++; + + if (!num_pins || !maps_per_pin) + return -EINVAL; + + reserve = num_pins * maps_per_pin; + + err = pinctrl_utils_reserve_map(pctldev, map, + reserved_maps, num_maps, reserve); + if (err < 0) + goto fail; + + for (i = 0; i < num_pins; i++) { + err = of_property_read_u32_index(node, "pinmux", + i, &pinfunc); + if (err) + goto fail; + + pin = MTK_GET_PIN_NO(pinfunc); + func = MTK_GET_PIN_FUNC(pinfunc); + + if (pin >= pctl->devdata->npins || + func >= ARRAY_SIZE(mtk_gpio_functions)) { + dev_err(pctl->dev, "invalid pins value.\n"); + err = -EINVAL; + goto fail; + } + + grp = mtk_pctrl_find_group_by_pin(pctl, pin); + if (!grp) { + dev_err(pctl->dev, "unable to match pin %d to group\n", + pin); + return -EINVAL; + } + + err = mtk_pctrl_dt_node_to_map_func(pctl, pin, func, grp, map, + reserved_maps, num_maps); + if (err < 0) + goto fail; + + if (has_config) { + err = pinctrl_utils_add_map_configs(pctldev, map, + reserved_maps, num_maps, grp->name, + configs, num_configs, + PIN_MAP_TYPE_CONFIGS_GROUP); + if (err < 0) + goto fail; + } + } + + return 0; + +fail: + return err; +} + +static int mtk_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, + struct pinctrl_map **map, unsigned *num_maps) +{ + struct device_node *np; + unsigned reserved_maps; + int ret; + + *map = NULL; + *num_maps = 0; + reserved_maps = 0; + + for_each_child_of_node(np_config, np) { + ret = mtk_pctrl_dt_subnode_to_map(pctldev, np, map, + &reserved_maps, num_maps); + if (ret < 0) { + pinctrl_utils_dt_free_map(pctldev, *map, *num_maps); + return ret; + } + } + + return 0; +} + +static int mtk_pctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + return pctl->ngroups; +} + +static const char *mtk_pctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned group) +{ + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + return pctl->groups[group].name; +} + +static int mtk_pctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned group, + const unsigned **pins, + unsigned *num_pins) +{ + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + *pins = (unsigned *)&pctl->groups[group].pin; + *num_pins = 1; + + return 0; +} + +static const struct pinctrl_ops mtk_pctrl_ops = { + .dt_node_to_map = mtk_pctrl_dt_node_to_map, + .dt_free_map = pinctrl_utils_dt_free_map, + .get_groups_count = mtk_pctrl_get_groups_count, + .get_group_name = mtk_pctrl_get_group_name, + .get_group_pins = mtk_pctrl_get_group_pins, +}; + +static int mtk_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(mtk_gpio_functions); +} + +static const char *mtk_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return mtk_gpio_functions[selector]; +} + +static int mtk_pmx_get_func_groups(struct pinctrl_dev *pctldev, + unsigned function, + const char * const **groups, + unsigned * const num_groups) +{ + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + *groups = pctl->grp_names; + *num_groups = pctl->ngroups; + + return 0; +} + +static int mtk_pmx_set_mode(struct pinctrl_dev *pctldev, + unsigned long pin, unsigned long mode) +{ + unsigned int reg_addr; + unsigned char bit; + unsigned int val; + unsigned int mask = (1L << GPIO_MODE_BITS) - 1; + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + + reg_addr = ((pin / MAX_GPIO_MODE_PER_REG) << pctl->devdata->port_shf) + + pctl->devdata->pinmux_offset; + + bit = pin % MAX_GPIO_MODE_PER_REG; + mask <<= (GPIO_MODE_BITS * bit); + val = (mode << (GPIO_MODE_BITS * bit)); + return regmap_update_bits(mtk_get_regmap(pctl, pin), + reg_addr, mask, val); +} + +static const struct mtk_desc_pin * +mtk_find_pin_by_eint_num(struct mtk_pinctrl *pctl, unsigned int eint_num) +{ + int i; + const struct mtk_desc_pin *pin; + + for (i = 0; i < pctl->devdata->npins; i++) { + pin = pctl->devdata->pins + i; + if (pin->eint.eintnum == eint_num) + return pin; + } + + return NULL; +} + +static int mtk_pmx_set_mux(struct pinctrl_dev *pctldev, + unsigned function, + unsigned group) +{ + bool ret; + const struct mtk_desc_function *desc; + struct mtk_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct mtk_pinctrl_group *g = pctl->groups + group; + + ret = mtk_pctrl_is_function_valid(pctl, g->pin, function); + if (!ret) { + dev_err(pctl->dev, "invaild function %d on group %d .\n", + function, group); + return -EINVAL; + } + + desc = mtk_pctrl_find_function_by_pin(pctl, g->pin, function); + if (!desc) + return -EINVAL; + mtk_pmx_set_mode(pctldev, g->pin, desc->muxval); + return 0; +} + +static const struct pinmux_ops mtk_pmx_ops = { + .get_functions_count = mtk_pmx_get_funcs_cnt, + .get_function_name = mtk_pmx_get_func_name, + .get_function_groups = mtk_pmx_get_func_groups, + .set_mux = mtk_pmx_set_mux, + .gpio_set_direction = mtk_pmx_gpio_set_direction, +}; + +static int mtk_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_request_gpio(chip->base + offset); +} + +static void mtk_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + pinctrl_free_gpio(chip->base + offset); +} + +static int mtk_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + return pinctrl_gpio_direction_input(chip->base + offset); +} + +static int mtk_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + mtk_gpio_set(chip, offset, value); + return pinctrl_gpio_direction_output(chip->base + offset); +} + +static int mtk_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + unsigned int reg_addr; + unsigned int bit; + unsigned int read_val = 0; + + struct mtk_pinctrl *pctl = dev_get_drvdata(chip->dev); + + reg_addr = mtk_get_port(pctl, offset) + pctl->devdata->dir_offset; + bit = BIT(offset & 0xf); + regmap_read(pctl->regmap1, reg_addr, &read_val); + return !!(read_val & bit); +} + +static int mtk_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + unsigned int reg_addr; + unsigned int bit; + unsigned int read_val = 0; + struct mtk_pinctrl *pctl = dev_get_drvdata(chip->dev); + + if (mtk_gpio_get_direction(chip, offset)) + reg_addr = mtk_get_port(pctl, offset) + + pctl->devdata->dout_offset; + else + reg_addr = mtk_get_port(pctl, offset) + + pctl->devdata->din_offset; + + bit = BIT(offset & 0xf); + regmap_read(pctl->regmap1, reg_addr, &read_val); + return !!(read_val & bit); +} + +static int mtk_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + const struct mtk_desc_pin *pin; + struct mtk_pinctrl *pctl = dev_get_drvdata(chip->dev); + int irq; + + pin = pctl->devdata->pins + offset; + if (pin->eint.eintnum == NO_EINT_SUPPORT) + return -EINVAL; + + irq = irq_find_mapping(pctl->domain, pin->eint.eintnum); + if (!irq) + return -EINVAL; + + return irq; +} + +static int mtk_pinctrl_irq_request_resources(struct irq_data *d) +{ + struct mtk_pinctrl *pctl = irq_data_get_irq_chip_data(d); + const struct mtk_desc_pin *pin; + int ret; + + pin = mtk_find_pin_by_eint_num(pctl, d->hwirq); + + if (!pin) { + dev_err(pctl->dev, "Can not find pin\n"); + return -EINVAL; + } + + ret = gpiochip_lock_as_irq(pctl->chip, pin->pin.number); + if (ret) { + dev_err(pctl->dev, "unable to lock HW IRQ %lu for IRQ\n", + irqd_to_hwirq(d)); + return ret; + } + + /* set mux to INT mode */ + mtk_pmx_set_mode(pctl->pctl_dev, pin->pin.number, pin->eint.eintmux); + + return 0; +} + +static void mtk_pinctrl_irq_release_resources(struct irq_data *d) +{ + struct mtk_pinctrl *pctl = irq_data_get_irq_chip_data(d); + const struct mtk_desc_pin *pin; + + pin = mtk_find_pin_by_eint_num(pctl, d->hwirq); + + if (!pin) { + dev_err(pctl->dev, "Can not find pin\n"); + return; + } + + gpiochip_unlock_as_irq(pctl->chip, pin->pin.number); +} + +static void __iomem *mtk_eint_get_offset(struct mtk_pinctrl *pctl, + unsigned int eint_num, unsigned int offset) +{ + unsigned int eint_base = 0; + void __iomem *reg; + + if (eint_num >= pctl->devdata->ap_num) + eint_base = pctl->devdata->ap_num; + + reg = pctl->eint_reg_base + offset + ((eint_num - eint_base) / 32) * 4; + + return reg; +} + +/* + * mtk_can_en_debounce: Check the EINT number is able to enable debounce or not + * @eint_num: the EINT number to setmtk_pinctrl + */ +static unsigned int mtk_eint_can_en_debounce(struct mtk_pinctrl *pctl, + unsigned int eint_num) +{ + unsigned int sens; + unsigned int bit = BIT(eint_num % 32); + const struct mtk_eint_offsets *eint_offsets = + &pctl->devdata->eint_offsets; + + void __iomem *reg = mtk_eint_get_offset(pctl, eint_num, + eint_offsets->sens); + + if (readl(reg) & bit) + sens = MT_LEVEL_SENSITIVE; + else + sens = MT_EDGE_SENSITIVE; + + if ((eint_num < pctl->devdata->db_cnt) && (sens != MT_EDGE_SENSITIVE)) + return 1; + else + return 0; +} + +/* + * mtk_eint_get_mask: To get the eint mask + * @eint_num: the EINT number to get + */ +static unsigned int mtk_eint_get_mask(struct mtk_pinctrl *pctl, + unsigned int eint_num) +{ + unsigned int bit = BIT(eint_num % 32); + const struct mtk_eint_offsets *eint_offsets = + &pctl->devdata->eint_offsets; + + void __iomem *reg = mtk_eint_get_offset(pctl, eint_num, + eint_offsets->mask); + + return !!(readl(reg) & bit); +} + +static int mtk_eint_flip_edge(struct mtk_pinctrl *pctl, int hwirq) +{ + int start_level, curr_level; + unsigned int reg_offset; + const struct mtk_eint_offsets *eint_offsets = &(pctl->devdata->eint_offsets); + u32 mask = 1 << (hwirq & 0x1f); + u32 port = (hwirq >> 5) & eint_offsets->port_mask; + void __iomem *reg = pctl->eint_reg_base + (port << 2); + const struct mtk_desc_pin *pin; + + pin = mtk_find_pin_by_eint_num(pctl, hwirq); + curr_level = mtk_gpio_get(pctl->chip, pin->pin.number); + do { + start_level = curr_level; + if (start_level) + reg_offset = eint_offsets->pol_clr; + else + reg_offset = eint_offsets->pol_set; + writel(mask, reg + reg_offset); + + curr_level = mtk_gpio_get(pctl->chip, pin->pin.number); + } while (start_level != curr_level); + + return start_level; +} + +static void mtk_eint_mask(struct irq_data *d) +{ + struct mtk_pinctrl *pctl = irq_data_get_irq_chip_data(d); + const struct mtk_eint_offsets *eint_offsets = + &pctl->devdata->eint_offsets; + u32 mask = BIT(d->hwirq & 0x1f); + void __iomem *reg = mtk_eint_get_offset(pctl, d->hwirq, + eint_offsets->mask_set); + + writel(mask, reg); +} + +static void mtk_eint_unmask(struct irq_data *d) +{ + struct mtk_pinctrl *pctl = irq_data_get_irq_chip_data(d); + const struct mtk_eint_offsets *eint_offsets = + &pctl->devdata->eint_offsets; + u32 mask = BIT(d->hwirq & 0x1f); + void __iomem *reg = mtk_eint_get_offset(pctl, d->hwirq, + eint_offsets->mask_clr); + + writel(mask, reg); + + if (pctl->eint_dual_edges[d->hwirq]) + mtk_eint_flip_edge(pctl, d->hwirq); +} + +static int mtk_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, + unsigned debounce) +{ + struct mtk_pinctrl *pctl = dev_get_drvdata(chip->dev); + int eint_num, virq, eint_offset; + unsigned int set_offset, bit, clr_bit, clr_offset, rst, i, unmask, dbnc; + static const unsigned int dbnc_arr[] = {0 , 1, 16, 32, 64, 128, 256}; + const struct mtk_desc_pin *pin; + struct irq_data *d; + + pin = pctl->devdata->pins + offset; + if (pin->eint.eintnum == NO_EINT_SUPPORT) + return -EINVAL; + + eint_num = pin->eint.eintnum; + virq = irq_find_mapping(pctl->domain, eint_num); + eint_offset = (eint_num % 4) * 8; + d = irq_get_irq_data(virq); + + set_offset = (eint_num / 4) * 4 + pctl->devdata->eint_offsets.dbnc_set; + clr_offset = (eint_num / 4) * 4 + pctl->devdata->eint_offsets.dbnc_clr; + if (!mtk_eint_can_en_debounce(pctl, eint_num)) + return -ENOSYS; + + dbnc = ARRAY_SIZE(dbnc_arr); + for (i = 0; i < ARRAY_SIZE(dbnc_arr); i++) { + if (debounce <= dbnc_arr[i]) { + dbnc = i; + break; + } + } + + if (!mtk_eint_get_mask(pctl, eint_num)) { + mtk_eint_mask(d); + unmask = 1; + } + + clr_bit = 0xff << eint_offset; + writel(clr_bit, pctl->eint_reg_base + clr_offset); + + bit = ((dbnc << EINT_DBNC_SET_DBNC_BITS) | EINT_DBNC_SET_EN) << + eint_offset; + rst = EINT_DBNC_RST_BIT << eint_offset; + writel(rst | bit, pctl->eint_reg_base + set_offset); + + /* Delay a while (more than 2T) to wait for hw debounce counter reset + work correctly */ + udelay(1); + if (unmask == 1) + mtk_eint_unmask(d); + + return 0; +} + +static struct gpio_chip mtk_gpio_chip = { + .owner = THIS_MODULE, + .request = mtk_gpio_request, + .free = mtk_gpio_free, + .direction_input = mtk_gpio_direction_input, + .direction_output = mtk_gpio_direction_output, + .get = mtk_gpio_get, + .set = mtk_gpio_set, + .to_irq = mtk_gpio_to_irq, + .set_debounce = mtk_gpio_set_debounce, + .of_gpio_n_cells = 2, +}; + +static int mtk_eint_set_type(struct irq_data *d, + unsigned int type) +{ + struct mtk_pinctrl *pctl = irq_data_get_irq_chip_data(d); + const struct mtk_eint_offsets *eint_offsets = + &pctl->devdata->eint_offsets; + u32 mask = BIT(d->hwirq & 0x1f); + void __iomem *reg; + + if (((type & IRQ_TYPE_EDGE_BOTH) && (type & IRQ_TYPE_LEVEL_MASK)) || + ((type & IRQ_TYPE_LEVEL_MASK) == IRQ_TYPE_LEVEL_MASK)) { + dev_err(pctl->dev, "Can't configure IRQ%d (EINT%lu) for type 0x%X\n", + d->irq, d->hwirq, type); + return -EINVAL; + } + + if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) + pctl->eint_dual_edges[d->hwirq] = 1; + else + pctl->eint_dual_edges[d->hwirq] = 0; + + if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) { + reg = mtk_eint_get_offset(pctl, d->hwirq, + eint_offsets->pol_clr); + writel(mask, reg); + } else { + reg = mtk_eint_get_offset(pctl, d->hwirq, + eint_offsets->pol_set); + writel(mask, reg); + } + + if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { + reg = mtk_eint_get_offset(pctl, d->hwirq, + eint_offsets->sens_clr); + writel(mask, reg); + } else { + reg = mtk_eint_get_offset(pctl, d->hwirq, + eint_offsets->sens_set); + writel(mask, reg); + } + + if (pctl->eint_dual_edges[d->hwirq]) + mtk_eint_flip_edge(pctl, d->hwirq); + + return 0; +} + +static void mtk_eint_ack(struct irq_data *d) +{ + struct mtk_pinctrl *pctl = irq_data_get_irq_chip_data(d); + const struct mtk_eint_offsets *eint_offsets = + &pctl->devdata->eint_offsets; + u32 mask = BIT(d->hwirq & 0x1f); + void __iomem *reg = mtk_eint_get_offset(pctl, d->hwirq, + eint_offsets->ack); + + writel(mask, reg); +} + +static struct irq_chip mtk_pinctrl_irq_chip = { + .name = "mt-eint", + .irq_mask = mtk_eint_mask, + .irq_unmask = mtk_eint_unmask, + .irq_ack = mtk_eint_ack, + .irq_set_type = mtk_eint_set_type, + .irq_request_resources = mtk_pinctrl_irq_request_resources, + .irq_release_resources = mtk_pinctrl_irq_release_resources, +}; + +static unsigned int mtk_eint_init(struct mtk_pinctrl *pctl) +{ + const struct mtk_eint_offsets *eint_offsets = + &pctl->devdata->eint_offsets; + void __iomem *reg = pctl->eint_reg_base + eint_offsets->dom_en; + unsigned int i; + + for (i = 0; i < pctl->devdata->ap_num; i += 32) { + writel(0xffffffff, reg); + reg += 4; + } + return 0; +} + +static inline void +mtk_eint_debounce_process(struct mtk_pinctrl *pctl, int index) +{ + unsigned int rst, ctrl_offset; + unsigned int bit, dbnc; + const struct mtk_eint_offsets *eint_offsets = + &pctl->devdata->eint_offsets; + + ctrl_offset = (index / 4) * 4 + eint_offsets->dbnc_ctrl; + dbnc = readl(pctl->eint_reg_base + ctrl_offset); + bit = EINT_DBNC_SET_EN << ((index % 4) * 8); + if ((bit & dbnc) > 0) { + ctrl_offset = (index / 4) * 4 + eint_offsets->dbnc_set; + rst = EINT_DBNC_RST_BIT << ((index % 4) * 8); + writel(rst, pctl->eint_reg_base + ctrl_offset); + } +} + +static void mtk_eint_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_get_chip(irq); + struct mtk_pinctrl *pctl = irq_get_handler_data(irq); + unsigned int status, eint_num; + int offset, index, virq; + const struct mtk_eint_offsets *eint_offsets = + &pctl->devdata->eint_offsets; + void __iomem *reg = mtk_eint_get_offset(pctl, 0, eint_offsets->stat); + int dual_edges, start_level, curr_level; + const struct mtk_desc_pin *pin; + + chained_irq_enter(chip, desc); + for (eint_num = 0; eint_num < pctl->devdata->ap_num; eint_num += 32) { + status = readl(reg); + reg += 4; + while (status) { + offset = __ffs(status); + index = eint_num + offset; + virq = irq_find_mapping(pctl->domain, index); + status &= ~BIT(offset); + + dual_edges = pctl->eint_dual_edges[index]; + if (dual_edges) { + /* Clear soft-irq in case we raised it + last time */ + writel(BIT(offset), reg - eint_offsets->stat + + eint_offsets->soft_clr); + + pin = mtk_find_pin_by_eint_num(pctl, index); + start_level = mtk_gpio_get(pctl->chip, + pin->pin.number); + } + + generic_handle_irq(virq); + + if (dual_edges) { + curr_level = mtk_eint_flip_edge(pctl, index); + + /* If level changed, we might lost one edge + interrupt, raised it through soft-irq */ + if (start_level != curr_level) + writel(BIT(offset), reg - + eint_offsets->stat + + eint_offsets->soft_set); + } + + if (index < pctl->devdata->db_cnt) + mtk_eint_debounce_process(pctl , index); + } + } + chained_irq_exit(chip, desc); +} + +static int mtk_pctrl_build_state(struct platform_device *pdev) +{ + struct mtk_pinctrl *pctl = platform_get_drvdata(pdev); + int i; + + pctl->ngroups = pctl->devdata->npins; + + /* Allocate groups */ + pctl->groups = devm_kcalloc(&pdev->dev, pctl->ngroups, + sizeof(*pctl->groups), GFP_KERNEL); + if (!pctl->groups) + return -ENOMEM; + + /* We assume that one pin is one group, use pin name as group name. */ + pctl->grp_names = devm_kcalloc(&pdev->dev, pctl->ngroups, + sizeof(*pctl->grp_names), GFP_KERNEL); + if (!pctl->grp_names) + return -ENOMEM; + + for (i = 0; i < pctl->devdata->npins; i++) { + const struct mtk_desc_pin *pin = pctl->devdata->pins + i; + struct mtk_pinctrl_group *group = pctl->groups + i; + + group->name = pin->pin.name; + group->pin = pin->pin.number; + + pctl->grp_names[i] = pin->pin.name; + } + + return 0; +} + +static struct pinctrl_desc mtk_pctrl_desc = { + .confops = &mtk_pconf_ops, + .pctlops = &mtk_pctrl_ops, + .pmxops = &mtk_pmx_ops, +}; + +int mtk_pctrl_init(struct platform_device *pdev, + const struct mtk_pinctrl_devdata *data) +{ + struct pinctrl_pin_desc *pins; + struct mtk_pinctrl *pctl; + struct device_node *np = pdev->dev.of_node, *node; + struct property *prop; + struct resource *res; + int i, ret, irq; + + pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL); + if (!pctl) + return -ENOMEM; + + platform_set_drvdata(pdev, pctl); + + prop = of_find_property(np, "pins-are-numbered", NULL); + if (!prop) { + dev_err(&pdev->dev, "only support pins-are-numbered format\n"); + return -EINVAL; + } + + node = of_parse_phandle(np, "mediatek,pctl-regmap", 0); + if (node) { + pctl->regmap1 = syscon_node_to_regmap(node); + if (IS_ERR(pctl->regmap1)) + return PTR_ERR(pctl->regmap1); + } + + /* Only 8135 has two base addr, other SoCs have only one. */ + node = of_parse_phandle(np, "mediatek,pctl-regmap", 1); + if (node) { + pctl->regmap2 = syscon_node_to_regmap(node); + if (IS_ERR(pctl->regmap2)) + return PTR_ERR(pctl->regmap2); + } + + pctl->devdata = data; + ret = mtk_pctrl_build_state(pdev); + if (ret) { + dev_err(&pdev->dev, "build state failed: %d\n", ret); + return -EINVAL; + } + + pins = devm_kcalloc(&pdev->dev, pctl->devdata->npins, sizeof(*pins), + GFP_KERNEL); + if (!pins) + return -ENOMEM; + + for (i = 0; i < pctl->devdata->npins; i++) + pins[i] = pctl->devdata->pins[i].pin; + mtk_pctrl_desc.name = dev_name(&pdev->dev); + mtk_pctrl_desc.owner = THIS_MODULE; + mtk_pctrl_desc.pins = pins; + mtk_pctrl_desc.npins = pctl->devdata->npins; + pctl->dev = &pdev->dev; + pctl->pctl_dev = pinctrl_register(&mtk_pctrl_desc, &pdev->dev, pctl); + if (!pctl->pctl_dev) { + dev_err(&pdev->dev, "couldn't register pinctrl driver\n"); + return -EINVAL; + } + + pctl->chip = devm_kzalloc(&pdev->dev, sizeof(*pctl->chip), GFP_KERNEL); + if (!pctl->chip) { + ret = -ENOMEM; + goto pctrl_error; + } + + pctl->chip = &mtk_gpio_chip; + pctl->chip->ngpio = pctl->devdata->npins; + pctl->chip->label = dev_name(&pdev->dev); + pctl->chip->dev = &pdev->dev; + pctl->chip->base = 0; + + ret = gpiochip_add(pctl->chip); + if (ret) { + ret = -EINVAL; + goto pctrl_error; + } + + /* Register the GPIO to pin mappings. */ + ret = gpiochip_add_pin_range(pctl->chip, dev_name(&pdev->dev), + 0, 0, pctl->devdata->npins); + if (ret) { + ret = -EINVAL; + goto chip_error; + } + + /* Get EINT register base from dts. */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Unable to get Pinctrl resource\n"); + ret = -EINVAL; + goto chip_error; + } + + pctl->eint_reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pctl->eint_reg_base)) { + ret = -EINVAL; + goto chip_error; + } + + pctl->eint_dual_edges = devm_kcalloc(&pdev->dev, pctl->devdata->ap_num, + sizeof(int), GFP_KERNEL); + if (!pctl->eint_dual_edges) { + ret = -ENOMEM; + goto chip_error; + } + + irq = irq_of_parse_and_map(np, 0); + if (!irq) { + dev_err(&pdev->dev, "couldn't parse and map irq\n"); + ret = -EINVAL; + goto chip_error; + } + + pctl->domain = irq_domain_add_linear(np, + pctl->devdata->ap_num, &irq_domain_simple_ops, NULL); + if (!pctl->domain) { + dev_err(&pdev->dev, "Couldn't register IRQ domain\n"); + ret = -ENOMEM; + goto chip_error; + } + + mtk_eint_init(pctl); + for (i = 0; i < pctl->devdata->ap_num; i++) { + int virq = irq_create_mapping(pctl->domain, i); + + irq_set_chip_and_handler(virq, &mtk_pinctrl_irq_chip, + handle_level_irq); + irq_set_chip_data(virq, pctl); + set_irq_flags(virq, IRQF_VALID); + }; + + irq_set_chained_handler(irq, mtk_eint_irq_handler); + irq_set_handler_data(irq, pctl); + set_irq_flags(irq, IRQF_VALID); + return 0; + +chip_error: + gpiochip_remove(pctl->chip); +pctrl_error: + pinctrl_unregister(pctl->pctl_dev); + return ret; +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MediaTek Pinctrl Driver"); +MODULE_AUTHOR("Hongzhou Yang "); diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.h b/drivers/pinctrl/mediatek/pinctrl-mtk-common.h new file mode 100644 index 00000000000000..375771db9bd09c --- /dev/null +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.h @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Hongzhou.Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __PINCTRL_MTK_COMMON_H +#define __PINCTRL_MTK_COMMON_H + +#include +#include + +#define NO_EINT_SUPPORT 255 +#define MTK_CHIP_TYPE_BASE 0 +#define MTK_CHIP_TYPE_PMIC 1 +#define MT_EDGE_SENSITIVE 0 +#define MT_LEVEL_SENSITIVE 1 +#define EINT_DBNC_SET_DBNC_BITS 4 +#define EINT_DBNC_RST_BIT (0x1 << 1) +#define EINT_DBNC_SET_EN (0x1 << 0) + +struct mtk_desc_function { + const char *name; + unsigned char muxval; +}; + +struct mtk_desc_eint { + unsigned char eintmux; + unsigned char eintnum; +}; + +struct mtk_desc_pin { + struct pinctrl_pin_desc pin; + const char *chip; + const struct mtk_desc_eint eint; + const struct mtk_desc_function *functions; +}; + +#define MTK_PIN(_pin, _pad, _chip, _eint, ...) \ + { \ + .pin = _pin, \ + .chip = _chip, \ + .eint = _eint, \ + .functions = (struct mtk_desc_function[]){ \ + __VA_ARGS__, { } }, \ + } + +#define MTK_EINT_FUNCTION(_eintmux, _eintnum) \ + { \ + .eintmux = _eintmux, \ + .eintnum = _eintnum, \ + } + +#define MTK_FUNCTION(_val, _name) \ + { \ + .muxval = _val, \ + .name = _name, \ + } + +#define SET_ADDR(x, y) (x + (y->devdata->port_align)) +#define CLR_ADDR(x, y) (x + (y->devdata->port_align << 1)) + +struct mtk_pinctrl_group { + const char *name; + unsigned long config; + unsigned pin; +}; + +/** + * struct mtk_drv_group_desc - Provide driving group data. + * @max_drv: The maximum current of this group. + * @min_drv: The minimum current of this group. + * @low_bit: The lowest bit of this group. + * @high_bit: The highest bit of this group. + * @step: The step current of this group. + */ +struct mtk_drv_group_desc { + unsigned char min_drv; + unsigned char max_drv; + unsigned char low_bit; + unsigned char high_bit; + unsigned char step; +}; + +#define MTK_DRV_GRP(_min, _max, _low, _high, _step) \ + { \ + .min_drv = _min, \ + .max_drv = _max, \ + .low_bit = _low, \ + .high_bit = _high, \ + .step = _step, \ + } + +/** + * struct mtk_pin_drv_grp - Provide each pin driving info. + * @pin: The pin number. + * @offset: The offset of driving register for this pin. + * @bit: The bit of driving register for this pin. + * @grp: The group for this pin belongs to. + */ +struct mtk_pin_drv_grp { + unsigned int pin; + unsigned int offset; + unsigned char bit; + unsigned char grp; +}; + +#define MTK_PIN_DRV_GRP(_pin, _offset, _bit, _grp) \ + { \ + .pin = _pin, \ + .offset = _offset, \ + .bit = _bit, \ + .grp = _grp, \ + } + +struct mtk_eint_offsets { + const char *name; + unsigned int stat; + unsigned int ack; + unsigned int mask; + unsigned int mask_set; + unsigned int mask_clr; + unsigned int sens; + unsigned int sens_set; + unsigned int sens_clr; + unsigned int soft; + unsigned int soft_set; + unsigned int soft_clr; + unsigned int pol; + unsigned int pol_set; + unsigned int pol_clr; + unsigned int dom_en; + unsigned int dbnc_ctrl; + unsigned int dbnc_set; + unsigned int dbnc_clr; + u8 port_mask; + u8 ports; +}; + +/** + * struct mtk_pinctrl_devdata - Provide HW GPIO related data. + * @pins: An array describing all pins the pin controller affects. + * @npins: The number of entries in @pins. + * + * @grp_desc: The driving group info. + * @pin_drv_grp: The driving group for all pins. + * @spec_pull_set: Each SoC may have special pins for pull up/down setting, + * these pins' pull setting are very different, they have separate pull + * up/down bit, R0 and R1 resistor bit, so they need special pull setting. + * If special setting is success, this should return 0, otherwise it should + * return non-zero value. + * @spec_ies_smt_set: Some pins are irregular, their input enable and smt + * control register are discontinuous, but they are mapping together. That + * means when user set smt, input enable is set at the same time. So they + * also need special control. If special control is success, this should + * return 0, otherwise return non-zero value. + * + * @dir_offset: The direction register offset. + * @pullen_offset: The pull-up/pull-down enable register offset. + * @pinmux_offset: The pinmux register offset. + * + * @type1_start: Some chips have two base addresses for pull select register, + * that means some pins use the first address and others use the second. This + * member record the start of pin number to use the second address. + * @type1_end: The end of pin number to use the second address. + * + * @port_shf: The shift between two registers. + * @port_mask: The mask of register. + * @port_align: Provide clear register and set register step. + */ +struct mtk_pinctrl_devdata { + const struct mtk_desc_pin *pins; + unsigned int npins; + const struct mtk_drv_group_desc *grp_desc; + unsigned int n_grp_cls; + const struct mtk_pin_drv_grp *pin_drv_grp; + unsigned int n_pin_drv_grps; + int (*spec_pull_set)(struct regmap *reg, unsigned int pin, + unsigned char align, bool isup, unsigned int arg); + int (*spec_ies_smt_set)(struct regmap *reg, unsigned int pin, + unsigned char align, int value); + unsigned int dir_offset; + unsigned int ies_offset; + unsigned int smt_offset; + unsigned int pullen_offset; + unsigned int pullsel_offset; + unsigned int drv_offset; + unsigned int invser_offset; + unsigned int dout_offset; + unsigned int din_offset; + unsigned int pinmux_offset; + unsigned short type1_start; + unsigned short type1_end; + unsigned char port_shf; + unsigned char port_mask; + unsigned char port_align; + unsigned char chip_type; + struct mtk_eint_offsets eint_offsets; + unsigned int ap_num; + unsigned int db_cnt; +}; + +struct mtk_pinctrl { + struct regmap *regmap1; + struct regmap *regmap2; + struct device *dev; + struct gpio_chip *chip; + struct mtk_pinctrl_group *groups; + unsigned ngroups; + const char **grp_names; + struct pinctrl_dev *pctl_dev; + const struct mtk_pinctrl_devdata *devdata; + void __iomem *eint_reg_base; + struct irq_domain *domain; + int *eint_dual_edges; +}; + +int mtk_pctrl_init(struct platform_device *pdev, + const struct mtk_pinctrl_devdata *data); + +#endif /* __PINCTRL_MTK_COMMON_H */ diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h new file mode 100644 index 00000000000000..e17aedb73c8dcd --- /dev/null +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8135.h @@ -0,0 +1,2114 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Hongzhou.Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __PINCTRL_MTK_MT8135_H +#define __PINCTRL_MTK_MT8135_H + +#include +#include "pinctrl-mtk-common.h" + +static const struct mtk_desc_pin mtk_pins_mt8135[] = { + MTK_PIN( + PINCTRL_PIN(0, "MSDC0_DAT7"), + "D21", "mt8135", + MTK_EINT_FUNCTION(2, 49), + MTK_FUNCTION(0, "GPIO0"), + MTK_FUNCTION(1, "MSDC0_DAT7"), + MTK_FUNCTION(2, "EINT49"), + MTK_FUNCTION(3, "I2SOUT_DAT"), + MTK_FUNCTION(4, "DAC_DAT_OUT"), + MTK_FUNCTION(5, "PCM1_DO"), + MTK_FUNCTION(6, "SPI1_MO"), + MTK_FUNCTION(7, "NALE") + ), + MTK_PIN( + PINCTRL_PIN(1, "MSDC0_DAT6"), + "D22", "mt8135", + MTK_EINT_FUNCTION(2, 48), + MTK_FUNCTION(0, "GPIO1"), + MTK_FUNCTION(1, "MSDC0_DAT6"), + MTK_FUNCTION(2, "EINT48"), + MTK_FUNCTION(3, "I2SIN_WS"), + MTK_FUNCTION(4, "DAC_WS"), + MTK_FUNCTION(5, "PCM1_WS"), + MTK_FUNCTION(6, "SPI1_CSN"), + MTK_FUNCTION(7, "NCLE") + ), + MTK_PIN( + PINCTRL_PIN(2, "MSDC0_DAT5"), + "E22", "mt8135", + MTK_EINT_FUNCTION(2, 47), + MTK_FUNCTION(0, "GPIO2"), + MTK_FUNCTION(1, "MSDC0_DAT5"), + MTK_FUNCTION(2, "EINT47"), + MTK_FUNCTION(3, "I2SIN_CK"), + MTK_FUNCTION(4, "DAC_CK"), + MTK_FUNCTION(5, "PCM1_CK"), + MTK_FUNCTION(6, "SPI1_CLK"), + MTK_FUNCTION(7, "NLD4") + ), + MTK_PIN( + PINCTRL_PIN(3, "MSDC0_DAT4"), + "F21", "mt8135", + MTK_EINT_FUNCTION(2, 46), + MTK_FUNCTION(0, "GPIO3"), + MTK_FUNCTION(1, "MSDC0_DAT4"), + MTK_FUNCTION(2, "EINT46"), + MTK_FUNCTION(3, "A_FUNC_CK"), + MTK_FUNCTION(6, "LSCE1B_2X"), + MTK_FUNCTION(7, "NLD5") + ), + MTK_PIN( + PINCTRL_PIN(4, "MSDC0_CMD"), + "F20", "mt8135", + MTK_EINT_FUNCTION(2, 41), + MTK_FUNCTION(0, "GPIO4"), + MTK_FUNCTION(1, "MSDC0_CMD"), + MTK_FUNCTION(2, "EINT41"), + MTK_FUNCTION(3, "A_FUNC_DOUT[0]"), + MTK_FUNCTION(5, "USB_TEST_IO[0]"), + MTK_FUNCTION(6, "LRSTB_2X"), + MTK_FUNCTION(7, "NRNB") + ), + MTK_PIN( + PINCTRL_PIN(5, "MSDC0_CLK"), + "G18", "mt8135", + MTK_EINT_FUNCTION(2, 40), + MTK_FUNCTION(0, "GPIO5"), + MTK_FUNCTION(1, "MSDC0_CLK"), + MTK_FUNCTION(2, "EINT40"), + MTK_FUNCTION(3, "A_FUNC_DOUT[1]"), + MTK_FUNCTION(5, "USB_TEST_IO[1]"), + MTK_FUNCTION(6, "LPTE"), + MTK_FUNCTION(7, "NREB") + ), + MTK_PIN( + PINCTRL_PIN(6, "MSDC0_DAT3"), + "G21", "mt8135", + MTK_EINT_FUNCTION(2, 45), + MTK_FUNCTION(0, "GPIO6"), + MTK_FUNCTION(1, "MSDC0_DAT3"), + MTK_FUNCTION(2, "EINT45"), + MTK_FUNCTION(3, "A_FUNC_DOUT[2]"), + MTK_FUNCTION(5, "USB_TEST_IO[2]"), + MTK_FUNCTION(6, "LSCE0B_2X"), + MTK_FUNCTION(7, "NLD7") + ), + MTK_PIN( + PINCTRL_PIN(7, "MSDC0_DAT2"), + "E21", "mt8135", + MTK_EINT_FUNCTION(2, 44), + MTK_FUNCTION(0, "GPIO7"), + MTK_FUNCTION(1, "MSDC0_DAT2"), + MTK_FUNCTION(2, "EINT44"), + MTK_FUNCTION(3, "A_FUNC_DOUT[3]"), + MTK_FUNCTION(5, "USB_TEST_IO[3]"), + MTK_FUNCTION(6, "LSA0_2X"), + MTK_FUNCTION(7, "NLD14") + ), + MTK_PIN( + PINCTRL_PIN(8, "MSDC0_DAT1"), + "E23", "mt8135", + MTK_EINT_FUNCTION(2, 43), + MTK_FUNCTION(0, "GPIO8"), + MTK_FUNCTION(1, "MSDC0_DAT1"), + MTK_FUNCTION(2, "EINT43"), + MTK_FUNCTION(5, "USB_TEST_IO[4]"), + MTK_FUNCTION(6, "LSCK_2X"), + MTK_FUNCTION(7, "NLD11") + ), + MTK_PIN( + PINCTRL_PIN(9, "MSDC0_DAT0"), + "F22", "mt8135", + MTK_EINT_FUNCTION(2, 42), + MTK_FUNCTION(0, "GPIO9"), + MTK_FUNCTION(1, "MSDC0_DAT0"), + MTK_FUNCTION(2, "EINT42"), + MTK_FUNCTION(5, "USB_TEST_IO[5]"), + MTK_FUNCTION(6, "LSDA_2X") + ), + MTK_PIN( + PINCTRL_PIN(10, "NCEB0"), + "G20", "mt8135", + MTK_EINT_FUNCTION(2, 139), + MTK_FUNCTION(0, "GPIO10"), + MTK_FUNCTION(1, "NCEB0"), + MTK_FUNCTION(2, "EINT139"), + MTK_FUNCTION(7, "TESTA_OUT4") + ), + MTK_PIN( + PINCTRL_PIN(11, "NCEB1"), + "L17", "mt8135", + MTK_EINT_FUNCTION(2, 140), + MTK_FUNCTION(0, "GPIO11"), + MTK_FUNCTION(1, "NCEB1"), + MTK_FUNCTION(2, "EINT140"), + MTK_FUNCTION(6, "USB_DRVVBUS"), + MTK_FUNCTION(7, "TESTA_OUT5") + ), + MTK_PIN( + PINCTRL_PIN(12, "NRNB"), + "G19", "mt8135", + MTK_EINT_FUNCTION(2, 141), + MTK_FUNCTION(0, "GPIO12"), + MTK_FUNCTION(1, "NRNB"), + MTK_FUNCTION(2, "EINT141"), + MTK_FUNCTION(3, "A_FUNC_DOUT[4]"), + MTK_FUNCTION(7, "TESTA_OUT6") + ), + MTK_PIN( + PINCTRL_PIN(13, "NCLE"), + "J18", "mt8135", + MTK_EINT_FUNCTION(2, 142), + MTK_FUNCTION(0, "GPIO13"), + MTK_FUNCTION(1, "NCLE"), + MTK_FUNCTION(2, "EINT142"), + MTK_FUNCTION(3, "A_FUNC_DOUT[5]"), + MTK_FUNCTION(4, "CM2PDN_1X"), + MTK_FUNCTION(6, "NALE"), + MTK_FUNCTION(7, "TESTA_OUT7") + ), + MTK_PIN( + PINCTRL_PIN(14, "NALE"), + "J19", "mt8135", + MTK_EINT_FUNCTION(2, 143), + MTK_FUNCTION(0, "GPIO14"), + MTK_FUNCTION(1, "NALE"), + MTK_FUNCTION(2, "EINT143"), + MTK_FUNCTION(3, "A_FUNC_DOUT[6]"), + MTK_FUNCTION(4, "CM2MCLK_1X"), + MTK_FUNCTION(5, "IRDA_RXD"), + MTK_FUNCTION(6, "NCLE"), + MTK_FUNCTION(7, "TESTA_OUT8") + ), + MTK_PIN( + PINCTRL_PIN(15, "NREB"), + "L18", "mt8135", + MTK_EINT_FUNCTION(2, 144), + MTK_FUNCTION(0, "GPIO15"), + MTK_FUNCTION(1, "NREB"), + MTK_FUNCTION(2, "EINT144"), + MTK_FUNCTION(3, "A_FUNC_DOUT[7]"), + MTK_FUNCTION(4, "CM2RST_1X"), + MTK_FUNCTION(5, "IRDA_TXD"), + MTK_FUNCTION(7, "TESTA_OUT9") + ), + MTK_PIN( + PINCTRL_PIN(16, "NWEB"), + "J20", "mt8135", + MTK_EINT_FUNCTION(2, 145), + MTK_FUNCTION(0, "GPIO16"), + MTK_FUNCTION(1, "NWEB"), + MTK_FUNCTION(2, "EINT145"), + MTK_FUNCTION(3, "A_FUNC_DIN[0]"), + MTK_FUNCTION(4, "CM2PCLK_1X"), + MTK_FUNCTION(5, "IRDA_PDN"), + MTK_FUNCTION(7, "TESTA_OUT10") + ), + MTK_PIN( + PINCTRL_PIN(17, "NLD0"), + "K21", "mt8135", + MTK_EINT_FUNCTION(2, 146), + MTK_FUNCTION(0, "GPIO17"), + MTK_FUNCTION(1, "NLD0"), + MTK_FUNCTION(2, "EINT146"), + MTK_FUNCTION(3, "A_FUNC_DIN[1]"), + MTK_FUNCTION(4, "CM2DAT_1X[0]"), + MTK_FUNCTION(5, "I2SIN_CK"), + MTK_FUNCTION(6, "DAC_CK"), + MTK_FUNCTION(7, "TESTA_OUT11") + ), + MTK_PIN( + PINCTRL_PIN(18, "NLD1"), + "K22", "mt8135", + MTK_EINT_FUNCTION(2, 147), + MTK_FUNCTION(0, "GPIO18"), + MTK_FUNCTION(1, "NLD1"), + MTK_FUNCTION(2, "EINT147"), + MTK_FUNCTION(3, "A_FUNC_DIN[2]"), + MTK_FUNCTION(4, "CM2DAT_1X[1]"), + MTK_FUNCTION(5, "I2SIN_WS"), + MTK_FUNCTION(6, "DAC_WS"), + MTK_FUNCTION(7, "TESTA_OUT12") + ), + MTK_PIN( + PINCTRL_PIN(19, "NLD2"), + "J21", "mt8135", + MTK_EINT_FUNCTION(2, 148), + MTK_FUNCTION(0, "GPIO19"), + MTK_FUNCTION(1, "NLD2"), + MTK_FUNCTION(2, "EINT148"), + MTK_FUNCTION(3, "A_FUNC_DIN[3]"), + MTK_FUNCTION(4, "CM2DAT_1X[2]"), + MTK_FUNCTION(5, "I2SOUT_DAT"), + MTK_FUNCTION(6, "DAC_DAT_OUT"), + MTK_FUNCTION(7, "TESTA_OUT13") + ), + MTK_PIN( + PINCTRL_PIN(20, "NLD3"), + "J23", "mt8135", + MTK_EINT_FUNCTION(2, 149), + MTK_FUNCTION(0, "GPIO20"), + MTK_FUNCTION(1, "NLD3"), + MTK_FUNCTION(2, "EINT149"), + MTK_FUNCTION(3, "A_FUNC_DIN[4]"), + MTK_FUNCTION(4, "CM2DAT_1X[3]"), + MTK_FUNCTION(7, "TESTA_OUT14") + ), + MTK_PIN( + PINCTRL_PIN(21, "NLD4"), + "J22", "mt8135", + MTK_EINT_FUNCTION(2, 150), + MTK_FUNCTION(0, "GPIO21"), + MTK_FUNCTION(1, "NLD4"), + MTK_FUNCTION(2, "EINT150"), + MTK_FUNCTION(3, "A_FUNC_DIN[5]"), + MTK_FUNCTION(4, "CM2DAT_1X[4]"), + MTK_FUNCTION(7, "TESTA_OUT15") + ), + MTK_PIN( + PINCTRL_PIN(22, "NLD5"), + "H21", "mt8135", + MTK_EINT_FUNCTION(2, 151), + MTK_FUNCTION(0, "GPIO22"), + MTK_FUNCTION(1, "NLD5"), + MTK_FUNCTION(2, "EINT151"), + MTK_FUNCTION(3, "A_FUNC_DIN[6]"), + MTK_FUNCTION(4, "CM2DAT_1X[5]"), + MTK_FUNCTION(7, "TESTA_OUT16") + ), + MTK_PIN( + PINCTRL_PIN(23, "NLD6"), + "H22", "mt8135", + MTK_EINT_FUNCTION(2, 152), + MTK_FUNCTION(0, "GPIO23"), + MTK_FUNCTION(1, "NLD6"), + MTK_FUNCTION(2, "EINT152"), + MTK_FUNCTION(3, "A_FUNC_DIN[7]"), + MTK_FUNCTION(4, "CM2DAT_1X[6]"), + MTK_FUNCTION(7, "TESTA_OUT17") + ), + MTK_PIN( + PINCTRL_PIN(24, "NLD7"), + "H20", "mt8135", + MTK_EINT_FUNCTION(2, 153), + MTK_FUNCTION(0, "GPIO24"), + MTK_FUNCTION(1, "NLD7"), + MTK_FUNCTION(2, "EINT153"), + MTK_FUNCTION(3, "A_FUNC_DIN[8]"), + MTK_FUNCTION(4, "CM2DAT_1X[7]"), + MTK_FUNCTION(7, "TESTA_OUT18") + ), + MTK_PIN( + PINCTRL_PIN(25, "NLD8"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 154), + MTK_FUNCTION(0, "GPIO25"), + MTK_FUNCTION(1, "NLD8"), + MTK_FUNCTION(2, "EINT154"), + MTK_FUNCTION(4, "CM2DAT_1X[8]") + ), + MTK_PIN( + PINCTRL_PIN(26, "NLD9"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 155), + MTK_FUNCTION(0, "GPIO26"), + MTK_FUNCTION(1, "NLD9"), + MTK_FUNCTION(2, "EINT155"), + MTK_FUNCTION(4, "CM2DAT_1X[9]"), + MTK_FUNCTION(5, "PWM1") + ), + MTK_PIN( + PINCTRL_PIN(27, "NLD10"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 156), + MTK_FUNCTION(0, "GPIO27"), + MTK_FUNCTION(1, "NLD10"), + MTK_FUNCTION(2, "EINT156"), + MTK_FUNCTION(4, "CM2VSYNC_1X"), + MTK_FUNCTION(5, "PWM2") + ), + MTK_PIN( + PINCTRL_PIN(28, "NLD11"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 157), + MTK_FUNCTION(0, "GPIO28"), + MTK_FUNCTION(1, "NLD11"), + MTK_FUNCTION(2, "EINT157"), + MTK_FUNCTION(4, "CM2HSYNC_1X"), + MTK_FUNCTION(5, "PWM3") + ), + MTK_PIN( + PINCTRL_PIN(29, "NLD12"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 158), + MTK_FUNCTION(0, "GPIO29"), + MTK_FUNCTION(1, "NLD12"), + MTK_FUNCTION(2, "EINT158"), + MTK_FUNCTION(3, "I2SIN_CK"), + MTK_FUNCTION(4, "DAC_CK"), + MTK_FUNCTION(5, "PCM1_CK") + ), + MTK_PIN( + PINCTRL_PIN(30, "NLD13"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 159), + MTK_FUNCTION(0, "GPIO30"), + MTK_FUNCTION(1, "NLD13"), + MTK_FUNCTION(2, "EINT159"), + MTK_FUNCTION(3, "I2SIN_WS"), + MTK_FUNCTION(4, "DAC_WS"), + MTK_FUNCTION(5, "PCM1_WS") + ), + MTK_PIN( + PINCTRL_PIN(31, "NLD14"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 160), + MTK_FUNCTION(0, "GPIO31"), + MTK_FUNCTION(1, "NLD14"), + MTK_FUNCTION(2, "EINT160"), + MTK_FUNCTION(3, "I2SOUT_DAT"), + MTK_FUNCTION(4, "DAC_DAT_OUT"), + MTK_FUNCTION(5, "PCM1_DO") + ), + MTK_PIN( + PINCTRL_PIN(32, "NLD15"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 161), + MTK_FUNCTION(0, "GPIO32"), + MTK_FUNCTION(1, "NLD15"), + MTK_FUNCTION(2, "EINT161"), + MTK_FUNCTION(3, "DISP_PWM"), + MTK_FUNCTION(4, "PWM4"), + MTK_FUNCTION(5, "PCM1_DI") + ), + MTK_PIN( + PINCTRL_PIN(33, "MSDC0_RSTB"), + "G22", "mt8135", + MTK_EINT_FUNCTION(2, 50), + MTK_FUNCTION(0, "GPIO33"), + MTK_FUNCTION(1, "MSDC0_RSTB"), + MTK_FUNCTION(2, "EINT50"), + MTK_FUNCTION(3, "I2SIN_DAT"), + MTK_FUNCTION(5, "PCM1_DI"), + MTK_FUNCTION(6, "SPI1_MI"), + MTK_FUNCTION(7, "NLD10") + ), + MTK_PIN( + PINCTRL_PIN(34, "IDDIG"), + "N17", "mt8135", + MTK_EINT_FUNCTION(2, 34), + MTK_FUNCTION(0, "GPIO34"), + MTK_FUNCTION(1, "IDDIG"), + MTK_FUNCTION(2, "EINT34") + ), + MTK_PIN( + PINCTRL_PIN(35, "SCL3"), + "L19", "mt8135", + MTK_EINT_FUNCTION(2, 96), + MTK_FUNCTION(0, "GPIO35"), + MTK_FUNCTION(1, "SCL3"), + MTK_FUNCTION(2, "EINT96"), + MTK_FUNCTION(3, "CLKM6"), + MTK_FUNCTION(4, "PWM6") + ), + MTK_PIN( + PINCTRL_PIN(36, "SDA3"), + "L20", "mt8135", + MTK_EINT_FUNCTION(2, 97), + MTK_FUNCTION(0, "GPIO36"), + MTK_FUNCTION(1, "SDA3"), + MTK_FUNCTION(2, "EINT97") + ), + MTK_PIN( + PINCTRL_PIN(37, "AUD_CLK_MOSI"), + "L21", "mt8135", + MTK_EINT_FUNCTION(4, 19), + MTK_FUNCTION(0, "GPIO37"), + MTK_FUNCTION(1, "AUD_CLK"), + MTK_FUNCTION(2, "ADC_CK"), + MTK_FUNCTION(3, " HDMI_SDATA0"), + MTK_FUNCTION(4, "EINT19"), + MTK_FUNCTION(5, "USB_TEST_IO[6]"), + MTK_FUNCTION(7, "TESTA_OUT19") + ), + MTK_PIN( + PINCTRL_PIN(38, "AUD_DAT_MOSI"), + "L23", "mt8135", + MTK_EINT_FUNCTION(4, 21), + MTK_FUNCTION(0, "GPIO38"), + MTK_FUNCTION(1, "AUD_DAT_MOSI"), + MTK_FUNCTION(2, "ADC_WS"), + MTK_FUNCTION(3, "AUD_DAT_MISO"), + MTK_FUNCTION(4, "EINT21"), + MTK_FUNCTION(5, "USB_TEST_IO[7]"), + MTK_FUNCTION(7, "TESTA_OUT20") + ), + MTK_PIN( + PINCTRL_PIN(39, "AUD_DAT_MISO"), + "L22", "mt8135", + MTK_EINT_FUNCTION(4, 20), + MTK_FUNCTION(0, "GPIO39"), + MTK_FUNCTION(1, "AUD_DAT_MISO"), + MTK_FUNCTION(2, "ADC_DAT_IN"), + MTK_FUNCTION(3, "AUD_DAT_MOSI"), + MTK_FUNCTION(4, "EINT20"), + MTK_FUNCTION(5, "USB_TEST_IO[8]"), + MTK_FUNCTION(7, "TESTA_OUT21") + ), + MTK_PIN( + PINCTRL_PIN(40, "DAC_CLK"), + "P21", "mt8135", + MTK_EINT_FUNCTION(2, 22), + MTK_FUNCTION(0, "GPIO40"), + MTK_FUNCTION(1, "DAC_CK"), + MTK_FUNCTION(2, "EINT22"), + MTK_FUNCTION(3, " HDMI_SDATA1"), + MTK_FUNCTION(5, "USB_TEST_IO[9]"), + MTK_FUNCTION(7, "TESTA_OUT22") + ), + MTK_PIN( + PINCTRL_PIN(41, "DAC_WS"), + "N18", "mt8135", + MTK_EINT_FUNCTION(2, 24), + MTK_FUNCTION(0, "GPIO41"), + MTK_FUNCTION(1, "DAC_WS"), + MTK_FUNCTION(2, "EINT24"), + MTK_FUNCTION(3, " HDMI_SDATA2"), + MTK_FUNCTION(5, "USB_TEST_IO[10]"), + MTK_FUNCTION(7, "TESTA_OUT23") + ), + MTK_PIN( + PINCTRL_PIN(42, "DAC_DAT_OUT"), + "N22", "mt8135", + MTK_EINT_FUNCTION(2, 23), + MTK_FUNCTION(0, "GPIO42"), + MTK_FUNCTION(1, "DAC_DAT_OUT"), + MTK_FUNCTION(2, "EINT23"), + MTK_FUNCTION(3, " HDMI_SDATA3"), + MTK_FUNCTION(5, "USB_TEST_IO[11]"), + MTK_FUNCTION(7, "TESTA_OUT24") + ), + MTK_PIN( + PINCTRL_PIN(43, "PWRAP_SPI0_MO"), + "M22", "mt8135", + MTK_EINT_FUNCTION(2, 29), + MTK_FUNCTION(0, "GPIO43"), + MTK_FUNCTION(1, "PWRAP_SPIDI"), + MTK_FUNCTION(2, "EINT29") + ), + MTK_PIN( + PINCTRL_PIN(44, "PWRAP_SPI0_MI"), + "P23", "mt8135", + MTK_EINT_FUNCTION(2, 28), + MTK_FUNCTION(0, "GPIO44"), + MTK_FUNCTION(1, "PWRAP_SPIDO"), + MTK_FUNCTION(2, "EINT28") + ), + MTK_PIN( + PINCTRL_PIN(45, "PWRAP_SPI0_CSN"), + "M21", "mt8135", + MTK_EINT_FUNCTION(2, 27), + MTK_FUNCTION(0, "GPIO45"), + MTK_FUNCTION(1, "PWRAP_SPICS_B_I"), + MTK_FUNCTION(2, "EINT27") + ), + MTK_PIN( + PINCTRL_PIN(46, "PWRAP_SPI0_CLK"), + "P22", "mt8135", + MTK_EINT_FUNCTION(2, 26), + MTK_FUNCTION(0, "GPIO46"), + MTK_FUNCTION(1, "PWRAP_SPICK_I"), + MTK_FUNCTION(2, "EINT26") + ), + MTK_PIN( + PINCTRL_PIN(47, "PWRAP_EVENT"), + "M23", "mt8135", + MTK_EINT_FUNCTION(2, 25), + MTK_FUNCTION(0, "GPIO47"), + MTK_FUNCTION(1, "PWRAP_EVENT_IN"), + MTK_FUNCTION(2, "EINT25"), + MTK_FUNCTION(7, "TESTA_OUT2") + ), + MTK_PIN( + PINCTRL_PIN(48, "RTC32K_CK"), + "N20", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO48"), + MTK_FUNCTION(1, "RTC32K_CK") + ), + MTK_PIN( + PINCTRL_PIN(49, "WATCHDOG"), + "R22", "mt8135", + MTK_EINT_FUNCTION(2, 36), + MTK_FUNCTION(0, "GPIO49"), + MTK_FUNCTION(1, "WATCHDOG"), + MTK_FUNCTION(2, "EINT36") + ), + MTK_PIN( + PINCTRL_PIN(50, "SRCLKENA"), + "T22", "mt8135", + MTK_EINT_FUNCTION(2, 38), + MTK_FUNCTION(0, "GPIO50"), + MTK_FUNCTION(1, "SRCLKENA"), + MTK_FUNCTION(2, "EINT38") + ), + MTK_PIN( + PINCTRL_PIN(51, "SRCVOLTEN"), + "T23", "mt8135", + MTK_EINT_FUNCTION(2, 37), + MTK_FUNCTION(0, "GPIO51"), + MTK_FUNCTION(1, "SRCVOLTEN"), + MTK_FUNCTION(2, "EINT37") + ), + MTK_PIN( + PINCTRL_PIN(52, "EINT0"), + "T21", "mt8135", + MTK_EINT_FUNCTION(1, 0), + MTK_FUNCTION(0, "GPIO52"), + MTK_FUNCTION(1, "EINT0"), + MTK_FUNCTION(2, "PWM1"), + MTK_FUNCTION(3, "CLKM0"), + MTK_FUNCTION(4, " SPDIF_OUT"), + MTK_FUNCTION(5, "USB_TEST_IO[12]"), + MTK_FUNCTION(7, "USB_SCL") + ), + MTK_PIN( + PINCTRL_PIN(53, "URXD2"), + "R18", "mt8135", + MTK_EINT_FUNCTION(2, 83), + MTK_FUNCTION(0, "GPIO53"), + MTK_FUNCTION(1, "URXD2"), + MTK_FUNCTION(2, "EINT83"), + MTK_FUNCTION(4, " HDMI_LRCK"), + MTK_FUNCTION(5, "CLKM3"), + MTK_FUNCTION(7, "UTXD2") + ), + MTK_PIN( + PINCTRL_PIN(54, "UTXD2"), + "R17", "mt8135", + MTK_EINT_FUNCTION(2, 82), + MTK_FUNCTION(0, "GPIO54"), + MTK_FUNCTION(1, "UTXD2"), + MTK_FUNCTION(2, "EINT82"), + MTK_FUNCTION(4, " HDMI_BCK_OUT"), + MTK_FUNCTION(5, "CLKM2"), + MTK_FUNCTION(7, "URXD2") + ), + MTK_PIN( + PINCTRL_PIN(55, "UCTS2"), + "R20", "mt8135", + MTK_EINT_FUNCTION(2, 84), + MTK_FUNCTION(0, "GPIO55"), + MTK_FUNCTION(1, "UCTS2"), + MTK_FUNCTION(2, "EINT84"), + MTK_FUNCTION(5, "PWM1"), + MTK_FUNCTION(7, "URTS2") + ), + MTK_PIN( + PINCTRL_PIN(56, "URTS2"), + "R19", "mt8135", + MTK_EINT_FUNCTION(2, 85), + MTK_FUNCTION(0, "GPIO56"), + MTK_FUNCTION(1, "URTS2"), + MTK_FUNCTION(2, "EINT85"), + MTK_FUNCTION(5, "PWM2"), + MTK_FUNCTION(7, "UCTS2") + ), + MTK_PIN( + PINCTRL_PIN(57, "JTCK"), + "V17", "mt8135", + MTK_EINT_FUNCTION(2, 188), + MTK_FUNCTION(0, "GPIO57"), + MTK_FUNCTION(1, "JTCK"), + MTK_FUNCTION(2, "EINT188"), + MTK_FUNCTION(3, "DSP1_ICK") + ), + MTK_PIN( + PINCTRL_PIN(58, "JTDO"), + "T16", "mt8135", + MTK_EINT_FUNCTION(2, 190), + MTK_FUNCTION(0, "GPIO58"), + MTK_FUNCTION(1, "JTDO"), + MTK_FUNCTION(2, "EINT190"), + MTK_FUNCTION(3, "DSP2_IMS") + ), + MTK_PIN( + PINCTRL_PIN(59, "JTRST_B"), + "T19", "mt8135", + MTK_EINT_FUNCTION(2, 0), + MTK_FUNCTION(0, "GPIO59"), + MTK_FUNCTION(1, "JTRST_B"), + MTK_FUNCTION(2, "EINT0"), + MTK_FUNCTION(3, "DSP2_ICK") + ), + MTK_PIN( + PINCTRL_PIN(60, "JTDI"), + "T18", "mt8135", + MTK_EINT_FUNCTION(2, 189), + MTK_FUNCTION(0, "GPIO60"), + MTK_FUNCTION(1, "JTDI"), + MTK_FUNCTION(2, "EINT189"), + MTK_FUNCTION(3, "DSP1_IMS") + ), + MTK_PIN( + PINCTRL_PIN(61, "JRTCK"), + "T20", "mt8135", + MTK_EINT_FUNCTION(2, 187), + MTK_FUNCTION(0, "GPIO61"), + MTK_FUNCTION(1, "JRTCK"), + MTK_FUNCTION(2, "EINT187"), + MTK_FUNCTION(3, "DSP1_ID") + ), + MTK_PIN( + PINCTRL_PIN(62, "JTMS"), + "T17", "mt8135", + MTK_EINT_FUNCTION(2, 191), + MTK_FUNCTION(0, "GPIO62"), + MTK_FUNCTION(1, "JTMS"), + MTK_FUNCTION(2, "EINT191"), + MTK_FUNCTION(3, "DSP2_ID") + ), + MTK_PIN( + PINCTRL_PIN(63, "MSDC1_INSI"), + "V18", "mt8135", + MTK_EINT_FUNCTION(1, 15), + MTK_FUNCTION(0, "GPIO63"), + MTK_FUNCTION(1, "MSDC1_INSI"), + MTK_FUNCTION(3, "SCL5"), + MTK_FUNCTION(4, "PWM6"), + MTK_FUNCTION(5, "CLKM5"), + MTK_FUNCTION(7, "TESTB_OUT6") + ), + MTK_PIN( + PINCTRL_PIN(64, "MSDC1_SDWPI"), + "W18", "mt8135", + MTK_EINT_FUNCTION(2, 58), + MTK_FUNCTION(0, "GPIO64"), + MTK_FUNCTION(1, "MSDC1_SDWPI"), + MTK_FUNCTION(2, "EINT58"), + MTK_FUNCTION(3, "SDA5"), + MTK_FUNCTION(4, "PWM7"), + MTK_FUNCTION(5, "CLKM6"), + MTK_FUNCTION(7, "TESTB_OUT7") + ), + MTK_PIN( + PINCTRL_PIN(65, "MSDC2_INSI"), + "U22", "mt8135", + MTK_EINT_FUNCTION(1, 14), + MTK_FUNCTION(0, "GPIO65"), + MTK_FUNCTION(1, "MSDC2_INSI"), + MTK_FUNCTION(5, "USB_TEST_IO[27]"), + MTK_FUNCTION(7, "TESTA_OUT3") + ), + MTK_PIN( + PINCTRL_PIN(66, "MSDC2_SDWPI"), + "U21", "mt8135", + MTK_EINT_FUNCTION(2, 66), + MTK_FUNCTION(0, "GPIO66"), + MTK_FUNCTION(1, "MSDC2_SDWPI"), + MTK_FUNCTION(2, "EINT66"), + MTK_FUNCTION(5, "USB_TEST_IO[28]") + ), + MTK_PIN( + PINCTRL_PIN(67, "URXD4"), + "V23", "mt8135", + MTK_EINT_FUNCTION(2, 89), + MTK_FUNCTION(0, "GPIO67"), + MTK_FUNCTION(1, "URXD4"), + MTK_FUNCTION(2, "EINT89"), + MTK_FUNCTION(3, "URXD1"), + MTK_FUNCTION(6, "UTXD4"), + MTK_FUNCTION(7, "TESTB_OUT10") + ), + MTK_PIN( + PINCTRL_PIN(68, "UTXD4"), + "V22", "mt8135", + MTK_EINT_FUNCTION(2, 88), + MTK_FUNCTION(0, "GPIO68"), + MTK_FUNCTION(1, "UTXD4"), + MTK_FUNCTION(2, "EINT88"), + MTK_FUNCTION(3, "UTXD1"), + MTK_FUNCTION(6, "URXD4"), + MTK_FUNCTION(7, "TESTB_OUT11") + ), + MTK_PIN( + PINCTRL_PIN(69, "URXD1"), + "W22", "mt8135", + MTK_EINT_FUNCTION(2, 79), + MTK_FUNCTION(0, "GPIO69"), + MTK_FUNCTION(1, "URXD1"), + MTK_FUNCTION(2, "EINT79"), + MTK_FUNCTION(3, "URXD4"), + MTK_FUNCTION(6, "UTXD1"), + MTK_FUNCTION(7, "TESTB_OUT24") + ), + MTK_PIN( + PINCTRL_PIN(70, "UTXD1"), + "V21", "mt8135", + MTK_EINT_FUNCTION(2, 78), + MTK_FUNCTION(0, "GPIO70"), + MTK_FUNCTION(1, "UTXD1"), + MTK_FUNCTION(2, "EINT78"), + MTK_FUNCTION(3, "UTXD4"), + MTK_FUNCTION(6, "URXD1"), + MTK_FUNCTION(7, "TESTB_OUT25") + ), + MTK_PIN( + PINCTRL_PIN(71, "UCTS1"), + "V19", "mt8135", + MTK_EINT_FUNCTION(2, 80), + MTK_FUNCTION(0, "GPIO71"), + MTK_FUNCTION(1, "UCTS1"), + MTK_FUNCTION(2, "EINT80"), + MTK_FUNCTION(5, "CLKM0"), + MTK_FUNCTION(6, "URTS1"), + MTK_FUNCTION(7, "TESTB_OUT31") + ), + MTK_PIN( + PINCTRL_PIN(72, "URTS1"), + "V20", "mt8135", + MTK_EINT_FUNCTION(2, 81), + MTK_FUNCTION(0, "GPIO72"), + MTK_FUNCTION(1, "URTS1"), + MTK_FUNCTION(2, "EINT81"), + MTK_FUNCTION(5, "CLKM1"), + MTK_FUNCTION(6, "UCTS1"), + MTK_FUNCTION(7, "TESTB_OUT21") + ), + MTK_PIN( + PINCTRL_PIN(73, "PWM1"), + "W17", "mt8135", + MTK_EINT_FUNCTION(2, 73), + MTK_FUNCTION(0, "GPIO73"), + MTK_FUNCTION(1, "PWM1"), + MTK_FUNCTION(2, "EINT73"), + MTK_FUNCTION(5, "USB_DRVVBUS"), + MTK_FUNCTION(6, "DISP_PWM"), + MTK_FUNCTION(7, "TESTB_OUT8") + ), + MTK_PIN( + PINCTRL_PIN(74, "PWM2"), + "Y17", "mt8135", + MTK_EINT_FUNCTION(2, 74), + MTK_FUNCTION(0, "GPIO74"), + MTK_FUNCTION(1, "PWM2"), + MTK_FUNCTION(2, "EINT74"), + MTK_FUNCTION(3, "DPI33_CK"), + MTK_FUNCTION(4, "PWM5"), + MTK_FUNCTION(5, "URXD2"), + MTK_FUNCTION(6, "DISP_PWM"), + MTK_FUNCTION(7, "TESTB_OUT9") + ), + MTK_PIN( + PINCTRL_PIN(75, "PWM3"), + "Y19", "mt8135", + MTK_EINT_FUNCTION(2, 75), + MTK_FUNCTION(0, "GPIO75"), + MTK_FUNCTION(1, "PWM3"), + MTK_FUNCTION(2, "EINT75"), + MTK_FUNCTION(3, "DPI33_D0"), + MTK_FUNCTION(4, "PWM6"), + MTK_FUNCTION(5, "UTXD2"), + MTK_FUNCTION(6, "DISP_PWM"), + MTK_FUNCTION(7, "TESTB_OUT12") + ), + MTK_PIN( + PINCTRL_PIN(76, "PWM4"), + "W19", "mt8135", + MTK_EINT_FUNCTION(2, 76), + MTK_FUNCTION(0, "GPIO76"), + MTK_FUNCTION(1, "PWM4"), + MTK_FUNCTION(2, "EINT76"), + MTK_FUNCTION(3, "DPI33_D1"), + MTK_FUNCTION(4, "PWM7"), + MTK_FUNCTION(6, "DISP_PWM"), + MTK_FUNCTION(7, "TESTB_OUT13") + ), + MTK_PIN( + PINCTRL_PIN(77, "MSDC2_DAT2"), + "W21", "mt8135", + MTK_EINT_FUNCTION(2, 63), + MTK_FUNCTION(0, "GPIO77"), + MTK_FUNCTION(1, "MSDC2_DAT2"), + MTK_FUNCTION(2, "EINT63"), + MTK_FUNCTION(4, "DSP2_IMS"), + MTK_FUNCTION(6, "DPI33_D6"), + MTK_FUNCTION(7, "TESTA_OUT25") + ), + MTK_PIN( + PINCTRL_PIN(78, "MSDC2_DAT3"), + "AA23", "mt8135", + MTK_EINT_FUNCTION(2, 64), + MTK_FUNCTION(0, "GPIO78"), + MTK_FUNCTION(1, "MSDC2_DAT3"), + MTK_FUNCTION(2, "EINT64"), + MTK_FUNCTION(4, "DSP2_ID"), + MTK_FUNCTION(6, "DPI33_D7"), + MTK_FUNCTION(7, "TESTA_OUT26") + ), + MTK_PIN( + PINCTRL_PIN(79, "MSDC2_CMD"), + "Y22", "mt8135", + MTK_EINT_FUNCTION(2, 60), + MTK_FUNCTION(0, "GPIO79"), + MTK_FUNCTION(1, "MSDC2_CMD"), + MTK_FUNCTION(2, "EINT60"), + MTK_FUNCTION(4, "DSP1_IMS"), + MTK_FUNCTION(5, "PCM1_WS"), + MTK_FUNCTION(6, "DPI33_D3"), + MTK_FUNCTION(7, "TESTA_OUT0") + ), + MTK_PIN( + PINCTRL_PIN(80, "MSDC2_CLK"), + "AA22", "mt8135", + MTK_EINT_FUNCTION(2, 59), + MTK_FUNCTION(0, "GPIO80"), + MTK_FUNCTION(1, "MSDC2_CLK"), + MTK_FUNCTION(2, "EINT59"), + MTK_FUNCTION(4, "DSP1_ICK"), + MTK_FUNCTION(5, "PCM1_CK"), + MTK_FUNCTION(6, "DPI33_D2"), + MTK_FUNCTION(7, "TESTA_OUT1") + ), + MTK_PIN( + PINCTRL_PIN(81, "MSDC2_DAT1"), + "Y21", "mt8135", + MTK_EINT_FUNCTION(2, 62), + MTK_FUNCTION(0, "GPIO81"), + MTK_FUNCTION(1, "MSDC2_DAT1"), + MTK_FUNCTION(2, "EINT62"), + MTK_FUNCTION(4, "DSP2_ICK"), + MTK_FUNCTION(5, "PCM1_DO"), + MTK_FUNCTION(6, "DPI33_D5") + ), + MTK_PIN( + PINCTRL_PIN(82, "MSDC2_DAT0"), + "AB22", "mt8135", + MTK_EINT_FUNCTION(2, 61), + MTK_FUNCTION(0, "GPIO82"), + MTK_FUNCTION(1, "MSDC2_DAT0"), + MTK_FUNCTION(2, "EINT61"), + MTK_FUNCTION(4, "DSP1_ID"), + MTK_FUNCTION(5, "PCM1_DI"), + MTK_FUNCTION(6, "DPI33_D4") + ), + MTK_PIN( + PINCTRL_PIN(83, "MSDC1_DAT0"), + "AC19", "mt8135", + MTK_EINT_FUNCTION(2, 53), + MTK_FUNCTION(0, "GPIO83"), + MTK_FUNCTION(1, "MSDC1_DAT0"), + MTK_FUNCTION(2, "EINT53"), + MTK_FUNCTION(3, "SCL1"), + MTK_FUNCTION(4, "PWM2"), + MTK_FUNCTION(5, "CLKM1"), + MTK_FUNCTION(7, "TESTB_OUT2") + ), + MTK_PIN( + PINCTRL_PIN(84, "MSDC1_DAT1"), + "AA19", "mt8135", + MTK_EINT_FUNCTION(2, 54), + MTK_FUNCTION(0, "GPIO84"), + MTK_FUNCTION(1, "MSDC1_DAT1"), + MTK_FUNCTION(2, "EINT54"), + MTK_FUNCTION(3, "SDA1"), + MTK_FUNCTION(4, "PWM3"), + MTK_FUNCTION(5, "CLKM2"), + MTK_FUNCTION(7, "TESTB_OUT3") + ), + MTK_PIN( + PINCTRL_PIN(85, "MSDC1_CMD"), + "AA20", "mt8135", + MTK_EINT_FUNCTION(2, 52), + MTK_FUNCTION(0, "GPIO85"), + MTK_FUNCTION(1, "MSDC1_CMD"), + MTK_FUNCTION(2, "EINT52"), + MTK_FUNCTION(3, "SDA0"), + MTK_FUNCTION(4, "PWM1"), + MTK_FUNCTION(5, "CLKM0"), + MTK_FUNCTION(7, "TESTB_OUT1") + ), + MTK_PIN( + PINCTRL_PIN(86, "MSDC1_CLK"), + "AB19", "mt8135", + MTK_EINT_FUNCTION(2, 51), + MTK_FUNCTION(0, "GPIO86"), + MTK_FUNCTION(1, "MSDC1_CLK"), + MTK_FUNCTION(2, "EINT51"), + MTK_FUNCTION(3, "SCL0"), + MTK_FUNCTION(4, "DISP_PWM"), + MTK_FUNCTION(7, "TESTB_OUT0") + ), + MTK_PIN( + PINCTRL_PIN(87, "MSDC1_DAT2"), + "AA21", "mt8135", + MTK_EINT_FUNCTION(2, 55), + MTK_FUNCTION(0, "GPIO87"), + MTK_FUNCTION(1, "MSDC1_DAT2"), + MTK_FUNCTION(2, "EINT55"), + MTK_FUNCTION(3, "SCL4"), + MTK_FUNCTION(4, "PWM4"), + MTK_FUNCTION(5, "CLKM3"), + MTK_FUNCTION(7, "TESTB_OUT4") + ), + MTK_PIN( + PINCTRL_PIN(88, "MSDC1_DAT3"), + "AB20", "mt8135", + MTK_EINT_FUNCTION(2, 56), + MTK_FUNCTION(0, "GPIO88"), + MTK_FUNCTION(1, "MSDC1_DAT3"), + MTK_FUNCTION(2, "EINT56"), + MTK_FUNCTION(3, "SDA4"), + MTK_FUNCTION(4, "PWM5"), + MTK_FUNCTION(5, "CLKM4"), + MTK_FUNCTION(7, "TESTB_OUT5") + ), + MTK_PIN( + PINCTRL_PIN(89, "MSDC4_DAT0"), + "AB8", "mt8135", + MTK_EINT_FUNCTION(2, 133), + MTK_FUNCTION(0, "GPIO89"), + MTK_FUNCTION(1, "MSDC4_DAT0"), + MTK_FUNCTION(2, "EINT133"), + MTK_FUNCTION(4, "EXT_FRAME_SYNC"), + MTK_FUNCTION(5, "USB_DRVVBUS"), + MTK_FUNCTION(6, "A_FUNC_DIN[9]"), + MTK_FUNCTION(7, "LPTE") + ), + MTK_PIN( + PINCTRL_PIN(90, "MSDC4_DAT1"), + "AB7", "mt8135", + MTK_EINT_FUNCTION(2, 134), + MTK_FUNCTION(0, "GPIO90"), + MTK_FUNCTION(1, "MSDC4_DAT1"), + MTK_FUNCTION(2, "EINT134"), + MTK_FUNCTION(6, "A_FUNC_DIN[10]"), + MTK_FUNCTION(7, "LRSTB_1X") + ), + MTK_PIN( + PINCTRL_PIN(91, "MSDC4_DAT5"), + "AA8", "mt8135", + MTK_EINT_FUNCTION(2, 136), + MTK_FUNCTION(0, "GPIO91"), + MTK_FUNCTION(1, "MSDC4_DAT5"), + MTK_FUNCTION(2, "EINT136"), + MTK_FUNCTION(3, "I2SIN_WS"), + MTK_FUNCTION(4, "DAC_WS"), + MTK_FUNCTION(5, "PCM1_WS"), + MTK_FUNCTION(6, "A_FUNC_DIN[11]"), + MTK_FUNCTION(7, "SPI1_CSN") + ), + MTK_PIN( + PINCTRL_PIN(92, "MSDC4_DAT6"), + "AC4", "mt8135", + MTK_EINT_FUNCTION(2, 137), + MTK_FUNCTION(0, "GPIO92"), + MTK_FUNCTION(1, "MSDC4_DAT6"), + MTK_FUNCTION(2, "EINT137"), + MTK_FUNCTION(3, "I2SOUT_DAT"), + MTK_FUNCTION(4, "DAC_DAT_OUT"), + MTK_FUNCTION(5, "PCM1_DO"), + MTK_FUNCTION(6, "A_FUNC_DIN[12]"), + MTK_FUNCTION(7, "SPI1_MO") + ), + MTK_PIN( + PINCTRL_PIN(93, "MSDC4_DAT7"), + "AC6", "mt8135", + MTK_EINT_FUNCTION(2, 138), + MTK_FUNCTION(0, "GPIO93"), + MTK_FUNCTION(1, "MSDC4_DAT7"), + MTK_FUNCTION(2, "EINT138"), + MTK_FUNCTION(3, "I2SIN_DAT"), + MTK_FUNCTION(5, "PCM1_DI"), + MTK_FUNCTION(6, "A_FUNC_DIN[13]"), + MTK_FUNCTION(7, "SPI1_MI") + ), + MTK_PIN( + PINCTRL_PIN(94, "MSDC4_DAT4"), + "AA7", "mt8135", + MTK_EINT_FUNCTION(2, 135), + MTK_FUNCTION(0, "GPIO94"), + MTK_FUNCTION(1, "MSDC4_DAT4"), + MTK_FUNCTION(2, "EINT135"), + MTK_FUNCTION(3, "I2SIN_CK"), + MTK_FUNCTION(4, "DAC_CK"), + MTK_FUNCTION(5, "PCM1_CK"), + MTK_FUNCTION(6, "A_FUNC_DIN[14]"), + MTK_FUNCTION(7, "SPI1_CLK") + ), + MTK_PIN( + PINCTRL_PIN(95, "MSDC4_DAT2"), + "AB6", "mt8135", + MTK_EINT_FUNCTION(2, 131), + MTK_FUNCTION(0, "GPIO95"), + MTK_FUNCTION(1, "MSDC4_DAT2"), + MTK_FUNCTION(2, "EINT131"), + MTK_FUNCTION(3, "I2SIN_WS"), + MTK_FUNCTION(4, "CM2PDN_2X"), + MTK_FUNCTION(5, "DAC_WS"), + MTK_FUNCTION(6, "PCM1_WS"), + MTK_FUNCTION(7, "LSCE0B_1X") + ), + MTK_PIN( + PINCTRL_PIN(96, "MSDC4_CLK"), + "AB5", "mt8135", + MTK_EINT_FUNCTION(2, 129), + MTK_FUNCTION(0, "GPIO96"), + MTK_FUNCTION(1, "MSDC4_CLK"), + MTK_FUNCTION(2, "EINT129"), + MTK_FUNCTION(3, "DPI1_CK_2X"), + MTK_FUNCTION(4, "CM2PCLK_2X"), + MTK_FUNCTION(5, "PWM4"), + MTK_FUNCTION(6, "PCM1_DI"), + MTK_FUNCTION(7, "LSCK_1X") + ), + MTK_PIN( + PINCTRL_PIN(97, "MSDC4_DAT3"), + "Y8", "mt8135", + MTK_EINT_FUNCTION(2, 132), + MTK_FUNCTION(0, "GPIO97"), + MTK_FUNCTION(1, "MSDC4_DAT3"), + MTK_FUNCTION(2, "EINT132"), + MTK_FUNCTION(3, "I2SOUT_DAT"), + MTK_FUNCTION(4, "CM2RST_2X"), + MTK_FUNCTION(5, "DAC_DAT_OUT"), + MTK_FUNCTION(6, "PCM1_DO"), + MTK_FUNCTION(7, "LSCE1B_1X") + ), + MTK_PIN( + PINCTRL_PIN(98, "MSDC4_CMD"), + "AC3", "mt8135", + MTK_EINT_FUNCTION(2, 128), + MTK_FUNCTION(0, "GPIO98"), + MTK_FUNCTION(1, "MSDC4_CMD"), + MTK_FUNCTION(2, "EINT128"), + MTK_FUNCTION(3, "DPI1_DE_2X"), + MTK_FUNCTION(5, "PWM3"), + MTK_FUNCTION(7, "LSDA_1X") + ), + MTK_PIN( + PINCTRL_PIN(99, "MSDC4_RSTB"), + "AB4", "mt8135", + MTK_EINT_FUNCTION(2, 130), + MTK_FUNCTION(0, "GPIO99"), + MTK_FUNCTION(1, "MSDC4_RSTB"), + MTK_FUNCTION(2, "EINT130"), + MTK_FUNCTION(3, "I2SIN_CK"), + MTK_FUNCTION(4, "CM2MCLK_2X"), + MTK_FUNCTION(5, "DAC_CK"), + MTK_FUNCTION(6, "PCM1_CK"), + MTK_FUNCTION(7, "LSA0_1X") + ), + MTK_PIN( + PINCTRL_PIN(100, "SDA0"), + "W9", "mt8135", + MTK_EINT_FUNCTION(2, 91), + MTK_FUNCTION(0, "GPIO100"), + MTK_FUNCTION(1, "SDA0"), + MTK_FUNCTION(2, "EINT91"), + MTK_FUNCTION(3, "CLKM1"), + MTK_FUNCTION(4, "PWM1"), + MTK_FUNCTION(7, "A_FUNC_DIN[15]") + ), + MTK_PIN( + PINCTRL_PIN(101, "SCL0"), + "W11", "mt8135", + MTK_EINT_FUNCTION(2, 90), + MTK_FUNCTION(0, "GPIO101"), + MTK_FUNCTION(1, "SCL0"), + MTK_FUNCTION(2, "EINT90"), + MTK_FUNCTION(3, "CLKM0"), + MTK_FUNCTION(4, "DISP_PWM"), + MTK_FUNCTION(7, "A_FUNC_DIN[16]") + ), + MTK_PIN( + PINCTRL_PIN(102, "EINT10_AUXIN2"), + "AA3", "mt8135", + MTK_EINT_FUNCTION(1, 10), + MTK_FUNCTION(0, "GPIO102"), + MTK_FUNCTION(1, "EINT10"), + MTK_FUNCTION(5, "USB_TEST_IO[16]"), + MTK_FUNCTION(6, "TESTB_OUT16"), + MTK_FUNCTION(7, "A_FUNC_DIN[17]") + ), + MTK_PIN( + PINCTRL_PIN(103, "EINT11_AUXIN3"), + "AB2", "mt8135", + MTK_EINT_FUNCTION(1, 11), + MTK_FUNCTION(0, "GPIO103"), + MTK_FUNCTION(1, "EINT11"), + MTK_FUNCTION(5, "USB_TEST_IO[17]"), + MTK_FUNCTION(6, "TESTB_OUT17"), + MTK_FUNCTION(7, "A_FUNC_DIN[18]") + ), + MTK_PIN( + PINCTRL_PIN(104, "EINT16_AUXIN4"), + "AB3", "mt8135", + MTK_EINT_FUNCTION(1, 16), + MTK_FUNCTION(0, "GPIO104"), + MTK_FUNCTION(1, "EINT16"), + MTK_FUNCTION(5, "USB_TEST_IO[18]"), + MTK_FUNCTION(6, "TESTB_OUT18"), + MTK_FUNCTION(7, "A_FUNC_DIN[19]") + ), + MTK_PIN( + PINCTRL_PIN(105, "I2S_CLK"), + "W6", "mt8135", + MTK_EINT_FUNCTION(2, 10), + MTK_FUNCTION(0, "GPIO105"), + MTK_FUNCTION(1, "I2SIN_CK"), + MTK_FUNCTION(2, "EINT10"), + MTK_FUNCTION(3, "DAC_CK"), + MTK_FUNCTION(4, "PCM1_CK"), + MTK_FUNCTION(5, "USB_TEST_IO[19]"), + MTK_FUNCTION(6, "TESTB_OUT19"), + MTK_FUNCTION(7, "A_FUNC_DIN[20]") + ), + MTK_PIN( + PINCTRL_PIN(106, "I2S_WS"), + "AA6", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO106"), + MTK_FUNCTION(1, "I2SIN_WS"), + MTK_FUNCTION(3, "DAC_WS"), + MTK_FUNCTION(4, "PCM1_WS"), + MTK_FUNCTION(5, "USB_TEST_IO[20]"), + MTK_FUNCTION(6, "TESTB_OUT20"), + MTK_FUNCTION(7, "A_FUNC_DIN[21]") + ), + MTK_PIN( + PINCTRL_PIN(107, "I2S_DATA_IN"), + "AA5", "mt8135", + MTK_EINT_FUNCTION(2, 11), + MTK_FUNCTION(0, "GPIO107"), + MTK_FUNCTION(1, "I2SIN_DAT"), + MTK_FUNCTION(2, "EINT11"), + MTK_FUNCTION(4, "PCM1_DI"), + MTK_FUNCTION(5, "USB_TEST_IO[21]"), + MTK_FUNCTION(6, "TESTB_OUT22"), + MTK_FUNCTION(7, "A_FUNC_DIN[22]") + ), + MTK_PIN( + PINCTRL_PIN(108, "I2S_DATA_OUT"), + "AA4", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO108"), + MTK_FUNCTION(1, "I2SOUT_DAT"), + MTK_FUNCTION(3, "DAC_DAT_OUT"), + MTK_FUNCTION(4, "PCM1_DO"), + MTK_FUNCTION(5, "USB_TEST_IO[22]"), + MTK_FUNCTION(6, "TESTB_OUT23"), + MTK_FUNCTION(7, "A_FUNC_DIN[23]") + ), + MTK_PIN( + PINCTRL_PIN(109, "EINT5"), + "W5", "mt8135", + MTK_EINT_FUNCTION(1, 5), + MTK_FUNCTION(0, "GPIO109"), + MTK_FUNCTION(1, "EINT5"), + MTK_FUNCTION(2, "PWM5"), + MTK_FUNCTION(3, "CLKM3"), + MTK_FUNCTION(4, "GPU_JTRSTB"), + MTK_FUNCTION(5, "USB_TEST_IO[23]"), + MTK_FUNCTION(6, "TESTB_OUT26"), + MTK_FUNCTION(7, "A_FUNC_DIN[24]") + ), + MTK_PIN( + PINCTRL_PIN(110, "EINT6"), + "V5", "mt8135", + MTK_EINT_FUNCTION(1, 6), + MTK_FUNCTION(0, "GPIO110"), + MTK_FUNCTION(1, "EINT6"), + MTK_FUNCTION(2, "PWM6"), + MTK_FUNCTION(3, "CLKM4"), + MTK_FUNCTION(4, "GPU_JTMS"), + MTK_FUNCTION(5, "USB_TEST_IO[24]"), + MTK_FUNCTION(6, "TESTB_OUT27"), + MTK_FUNCTION(7, "A_FUNC_DIN[25]") + ), + MTK_PIN( + PINCTRL_PIN(111, "EINT7"), + "W3", "mt8135", + MTK_EINT_FUNCTION(1, 7), + MTK_FUNCTION(0, "GPIO111"), + MTK_FUNCTION(1, "EINT7"), + MTK_FUNCTION(2, "PWM7"), + MTK_FUNCTION(3, "CLKM5"), + MTK_FUNCTION(4, "GPU_JTDO"), + MTK_FUNCTION(5, "USB_TEST_IO[25]"), + MTK_FUNCTION(6, "TESTB_OUT28"), + MTK_FUNCTION(7, "A_FUNC_DIN[26]") + ), + MTK_PIN( + PINCTRL_PIN(112, "EINT8"), + "V6", "mt8135", + MTK_EINT_FUNCTION(1, 8), + MTK_FUNCTION(0, "GPIO112"), + MTK_FUNCTION(1, "EINT8"), + MTK_FUNCTION(2, "DISP_PWM"), + MTK_FUNCTION(3, "CLKM6"), + MTK_FUNCTION(4, "GPU_JTDI"), + MTK_FUNCTION(5, "USB_TEST_IO[26]"), + MTK_FUNCTION(6, "TESTB_OUT29"), + MTK_FUNCTION(7, "EXT_FRAME_SYNC") + ), + MTK_PIN( + PINCTRL_PIN(113, "EINT9"), + "W8", "mt8135", + MTK_EINT_FUNCTION(1, 9), + MTK_FUNCTION(0, "GPIO113"), + MTK_FUNCTION(1, "EINT9"), + MTK_FUNCTION(4, "GPU_JTCK"), + MTK_FUNCTION(5, "USB_DRVVBUS"), + MTK_FUNCTION(6, "TESTB_OUT30"), + MTK_FUNCTION(7, "A_FUNC_DIN[27]") + ), + MTK_PIN( + PINCTRL_PIN(114, "LPCE1B"), + "W4", "mt8135", + MTK_EINT_FUNCTION(2, 127), + MTK_FUNCTION(0, "GPIO114"), + MTK_FUNCTION(1, "LPCE1B"), + MTK_FUNCTION(2, "EINT127"), + MTK_FUNCTION(5, "PWM2"), + MTK_FUNCTION(6, "TESTB_OUT14"), + MTK_FUNCTION(7, "A_FUNC_DIN[28]") + ), + MTK_PIN( + PINCTRL_PIN(115, "LPCE0B"), + "T5", "mt8135", + MTK_EINT_FUNCTION(2, 126), + MTK_FUNCTION(0, "GPIO115"), + MTK_FUNCTION(1, "LPCE0B"), + MTK_FUNCTION(2, "EINT126"), + MTK_FUNCTION(5, "PWM1"), + MTK_FUNCTION(6, "TESTB_OUT15"), + MTK_FUNCTION(7, "A_FUNC_DIN[29]") + ), + MTK_PIN( + PINCTRL_PIN(116, "DISP_PWM"), + "V4", "mt8135", + MTK_EINT_FUNCTION(2, 77), + MTK_FUNCTION(0, "GPIO116"), + MTK_FUNCTION(1, "DISP_PWM"), + MTK_FUNCTION(2, "EINT77"), + MTK_FUNCTION(3, "LSDI"), + MTK_FUNCTION(4, "PWM1"), + MTK_FUNCTION(5, "PWM2"), + MTK_FUNCTION(7, "PWM3") + ), + MTK_PIN( + PINCTRL_PIN(117, "EINT1"), + "T6", "mt8135", + MTK_EINT_FUNCTION(1, 1), + MTK_FUNCTION(0, "GPIO117"), + MTK_FUNCTION(1, "EINT1"), + MTK_FUNCTION(2, "PWM2"), + MTK_FUNCTION(3, "CLKM1"), + MTK_FUNCTION(5, "USB_TEST_IO[13]"), + MTK_FUNCTION(7, "USB_SDA") + ), + MTK_PIN( + PINCTRL_PIN(118, "EINT2"), + "T4", "mt8135", + MTK_EINT_FUNCTION(1, 2), + MTK_FUNCTION(0, "GPIO118"), + MTK_FUNCTION(1, "EINT2"), + MTK_FUNCTION(2, "PWM3"), + MTK_FUNCTION(3, "CLKM2"), + MTK_FUNCTION(5, "USB_TEST_IO[14]"), + MTK_FUNCTION(6, "SRCLKENAI2"), + MTK_FUNCTION(7, "A_FUNC_DIN[30]") + ), + MTK_PIN( + PINCTRL_PIN(119, "EINT3"), + "R4", "mt8135", + MTK_EINT_FUNCTION(1, 3), + MTK_FUNCTION(0, "GPIO119"), + MTK_FUNCTION(1, "EINT3"), + MTK_FUNCTION(5, "USB_TEST_IO[15]"), + MTK_FUNCTION(6, "SRCLKENAI1"), + MTK_FUNCTION(7, "EXT_26M_CK") + ), + MTK_PIN( + PINCTRL_PIN(120, "EINT4"), + "R5", "mt8135", + MTK_EINT_FUNCTION(1, 4), + MTK_FUNCTION(0, "GPIO120"), + MTK_FUNCTION(1, "EINT4"), + MTK_FUNCTION(2, "PWM4"), + MTK_FUNCTION(5, "USB_DRVVBUS"), + MTK_FUNCTION(7, "A_FUNC_DIN[31]") + ), + MTK_PIN( + PINCTRL_PIN(121, "DPIDE"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 100), + MTK_FUNCTION(0, "GPIO121"), + MTK_FUNCTION(1, "DPI0_DE"), + MTK_FUNCTION(2, "EINT100"), + MTK_FUNCTION(3, "I2SOUT_DAT"), + MTK_FUNCTION(4, "DAC_DAT_OUT"), + MTK_FUNCTION(5, "PCM1_DO"), + MTK_FUNCTION(6, "IRDA_TXD") + ), + MTK_PIN( + PINCTRL_PIN(122, "DPICK"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 101), + MTK_FUNCTION(0, "GPIO122"), + MTK_FUNCTION(1, "DPI0_CK"), + MTK_FUNCTION(2, "EINT101"), + MTK_FUNCTION(3, "I2SIN_DAT"), + MTK_FUNCTION(5, "PCM1_DI"), + MTK_FUNCTION(6, "IRDA_PDN") + ), + MTK_PIN( + PINCTRL_PIN(123, "DPIG4"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 114), + MTK_FUNCTION(0, "GPIO123"), + MTK_FUNCTION(1, "DPI0_G4"), + MTK_FUNCTION(2, "EINT114"), + MTK_FUNCTION(4, "CM2DAT_2X[0]"), + MTK_FUNCTION(5, "DSP2_ID") + ), + MTK_PIN( + PINCTRL_PIN(124, "DPIG5"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 115), + MTK_FUNCTION(0, "GPIO124"), + MTK_FUNCTION(1, "DPI0_G5"), + MTK_FUNCTION(2, "EINT115"), + MTK_FUNCTION(4, "CM2DAT_2X[1]"), + MTK_FUNCTION(5, "DSP2_ICK") + ), + MTK_PIN( + PINCTRL_PIN(125, "DPIR3"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 121), + MTK_FUNCTION(0, "GPIO125"), + MTK_FUNCTION(1, "DPI0_R3"), + MTK_FUNCTION(2, "EINT121"), + MTK_FUNCTION(4, "CM2DAT_2X[7]") + ), + MTK_PIN( + PINCTRL_PIN(126, "DPIG1"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 111), + MTK_FUNCTION(0, "GPIO126"), + MTK_FUNCTION(1, "DPI0_G1"), + MTK_FUNCTION(2, "EINT111"), + MTK_FUNCTION(5, "DSP1_ICK") + ), + MTK_PIN( + PINCTRL_PIN(127, "DPIVSYNC"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 98), + MTK_FUNCTION(0, "GPIO127"), + MTK_FUNCTION(1, "DPI0_VSYNC"), + MTK_FUNCTION(2, "EINT98"), + MTK_FUNCTION(3, "I2SIN_CK"), + MTK_FUNCTION(4, "DAC_CK"), + MTK_FUNCTION(5, "PCM1_CK") + ), + MTK_PIN( + PINCTRL_PIN(128, "DPIHSYNC"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 99), + MTK_FUNCTION(0, "GPIO128"), + MTK_FUNCTION(1, "DPI0_HSYNC"), + MTK_FUNCTION(2, "EINT99"), + MTK_FUNCTION(3, "I2SIN_WS"), + MTK_FUNCTION(4, "DAC_WS"), + MTK_FUNCTION(5, "PCM1_WS"), + MTK_FUNCTION(6, "IRDA_RXD") + ), + MTK_PIN( + PINCTRL_PIN(129, "DPIB0"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 102), + MTK_FUNCTION(0, "GPIO129"), + MTK_FUNCTION(1, "DPI0_B0"), + MTK_FUNCTION(2, "EINT102"), + MTK_FUNCTION(4, "SCL0"), + MTK_FUNCTION(5, "DISP_PWM") + ), + MTK_PIN( + PINCTRL_PIN(130, "DPIB1"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 103), + MTK_FUNCTION(0, "GPIO130"), + MTK_FUNCTION(1, "DPI0_B1"), + MTK_FUNCTION(2, "EINT103"), + MTK_FUNCTION(3, "CLKM0"), + MTK_FUNCTION(4, "SDA0"), + MTK_FUNCTION(5, "PWM1") + ), + MTK_PIN( + PINCTRL_PIN(131, "DPIB2"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 104), + MTK_FUNCTION(0, "GPIO131"), + MTK_FUNCTION(1, "DPI0_B2"), + MTK_FUNCTION(2, "EINT104"), + MTK_FUNCTION(3, "CLKM1"), + MTK_FUNCTION(4, "SCL1"), + MTK_FUNCTION(5, "PWM2") + ), + MTK_PIN( + PINCTRL_PIN(132, "DPIB3"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 105), + MTK_FUNCTION(0, "GPIO132"), + MTK_FUNCTION(1, "DPI0_B3"), + MTK_FUNCTION(2, "EINT105"), + MTK_FUNCTION(3, "CLKM2"), + MTK_FUNCTION(4, "SDA1"), + MTK_FUNCTION(5, "PWM3") + ), + MTK_PIN( + PINCTRL_PIN(133, "DPIB4"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 106), + MTK_FUNCTION(0, "GPIO133"), + MTK_FUNCTION(1, "DPI0_B4"), + MTK_FUNCTION(2, "EINT106"), + MTK_FUNCTION(3, "CLKM3"), + MTK_FUNCTION(4, "SCL2"), + MTK_FUNCTION(5, "PWM4") + ), + MTK_PIN( + PINCTRL_PIN(134, "DPIB5"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 107), + MTK_FUNCTION(0, "GPIO134"), + MTK_FUNCTION(1, "DPI0_B5"), + MTK_FUNCTION(2, "EINT107"), + MTK_FUNCTION(3, "CLKM4"), + MTK_FUNCTION(4, "SDA2"), + MTK_FUNCTION(5, "PWM5") + ), + MTK_PIN( + PINCTRL_PIN(135, "DPIB6"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 108), + MTK_FUNCTION(0, "GPIO135"), + MTK_FUNCTION(1, "DPI0_B6"), + MTK_FUNCTION(2, "EINT108"), + MTK_FUNCTION(3, "CLKM5"), + MTK_FUNCTION(4, "SCL3"), + MTK_FUNCTION(5, "PWM6") + ), + MTK_PIN( + PINCTRL_PIN(136, "DPIB7"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 109), + MTK_FUNCTION(0, "GPIO136"), + MTK_FUNCTION(1, "DPI0_B7"), + MTK_FUNCTION(2, "EINT109"), + MTK_FUNCTION(3, "CLKM6"), + MTK_FUNCTION(4, "SDA3"), + MTK_FUNCTION(5, "PWM7") + ), + MTK_PIN( + PINCTRL_PIN(137, "DPIG0"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 110), + MTK_FUNCTION(0, "GPIO137"), + MTK_FUNCTION(1, "DPI0_G0"), + MTK_FUNCTION(2, "EINT110"), + MTK_FUNCTION(5, "DSP1_ID") + ), + MTK_PIN( + PINCTRL_PIN(138, "DPIG2"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 112), + MTK_FUNCTION(0, "GPIO138"), + MTK_FUNCTION(1, "DPI0_G2"), + MTK_FUNCTION(2, "EINT112"), + MTK_FUNCTION(5, "DSP1_IMS") + ), + MTK_PIN( + PINCTRL_PIN(139, "DPIG3"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 113), + MTK_FUNCTION(0, "GPIO139"), + MTK_FUNCTION(1, "DPI0_G3"), + MTK_FUNCTION(2, "EINT113"), + MTK_FUNCTION(5, "DSP2_IMS") + ), + MTK_PIN( + PINCTRL_PIN(140, "DPIG6"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 116), + MTK_FUNCTION(0, "GPIO140"), + MTK_FUNCTION(1, "DPI0_G6"), + MTK_FUNCTION(2, "EINT116"), + MTK_FUNCTION(4, "CM2DAT_2X[2]") + ), + MTK_PIN( + PINCTRL_PIN(141, "DPIG7"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 117), + MTK_FUNCTION(0, "GPIO141"), + MTK_FUNCTION(1, "DPI0_G7"), + MTK_FUNCTION(2, "EINT117"), + MTK_FUNCTION(4, "CM2DAT_2X[3]") + ), + MTK_PIN( + PINCTRL_PIN(142, "DPIR0"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 118), + MTK_FUNCTION(0, "GPIO142"), + MTK_FUNCTION(1, "DPI0_R0"), + MTK_FUNCTION(2, "EINT118"), + MTK_FUNCTION(4, "CM2DAT_2X[4]") + ), + MTK_PIN( + PINCTRL_PIN(143, "DPIR1"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 119), + MTK_FUNCTION(0, "GPIO143"), + MTK_FUNCTION(1, "DPI0_R1"), + MTK_FUNCTION(2, "EINT119"), + MTK_FUNCTION(4, "CM2DAT_2X[5]") + ), + MTK_PIN( + PINCTRL_PIN(144, "DPIR2"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 120), + MTK_FUNCTION(0, "GPIO144"), + MTK_FUNCTION(1, "DPI0_R2"), + MTK_FUNCTION(2, "EINT120"), + MTK_FUNCTION(4, "CM2DAT_2X[6]") + ), + MTK_PIN( + PINCTRL_PIN(145, "DPIR4"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 122), + MTK_FUNCTION(0, "GPIO145"), + MTK_FUNCTION(1, "DPI0_R4"), + MTK_FUNCTION(2, "EINT122"), + MTK_FUNCTION(4, "CM2DAT_2X[8]") + ), + MTK_PIN( + PINCTRL_PIN(146, "DPIR5"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 123), + MTK_FUNCTION(0, "GPIO146"), + MTK_FUNCTION(1, "DPI0_R5"), + MTK_FUNCTION(2, "EINT123"), + MTK_FUNCTION(4, "CM2DAT_2X[9]") + ), + MTK_PIN( + PINCTRL_PIN(147, "DPIR6"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 124), + MTK_FUNCTION(0, "GPIO147"), + MTK_FUNCTION(1, "DPI0_R6"), + MTK_FUNCTION(2, "EINT124"), + MTK_FUNCTION(4, "CM2VSYNC_2X") + ), + MTK_PIN( + PINCTRL_PIN(148, "DPIR7"), + NULL, "mt8135", + MTK_EINT_FUNCTION(2, 125), + MTK_FUNCTION(0, "GPIO148"), + MTK_FUNCTION(1, "DPI0_R7"), + MTK_FUNCTION(2, "EINT125"), + MTK_FUNCTION(4, "CM2HSYNC_2X") + ), + MTK_PIN( + PINCTRL_PIN(149, "TDN3/LVDS(TDN3)"), + "AA2", "mt8135", + MTK_EINT_FUNCTION(2, 36), + MTK_FUNCTION(0, "GPIO149"), + MTK_FUNCTION(2, "EINT36") + ), + MTK_PIN( + PINCTRL_PIN(150, "TDP3/LVDS(TDP3)"), + "AA1", "mt8135", + MTK_EINT_FUNCTION(2, 35), + MTK_FUNCTION(0, "GPIO150"), + MTK_FUNCTION(2, "EINT35") + ), + MTK_PIN( + PINCTRL_PIN(151, "TDN2/LVDS(TCN)"), + "Y2", "mt8135", + MTK_EINT_FUNCTION(2, 169), + MTK_FUNCTION(0, "GPIO151"), + MTK_FUNCTION(2, "EINT169") + ), + MTK_PIN( + PINCTRL_PIN(152, "TDP2/LVDS(TCP)"), + "Y1", "mt8135", + MTK_EINT_FUNCTION(2, 168), + MTK_FUNCTION(0, "GPIO152"), + MTK_FUNCTION(2, "EINT168") + ), + MTK_PIN( + PINCTRL_PIN(153, "TCN/LVDS(TDN2)"), + "W2", "mt8135", + MTK_EINT_FUNCTION(2, 163), + MTK_FUNCTION(0, "GPIO153"), + MTK_FUNCTION(2, "EINT163") + ), + MTK_PIN( + PINCTRL_PIN(154, "TCP/LVDS(TDP2)"), + "W1", "mt8135", + MTK_EINT_FUNCTION(2, 162), + MTK_FUNCTION(0, "GPIO154"), + MTK_FUNCTION(2, "EINT162") + ), + MTK_PIN( + PINCTRL_PIN(155, "TDN1/LVDS(TDN1)"), + "V3", "mt8135", + MTK_EINT_FUNCTION(2, 167), + MTK_FUNCTION(0, "GPIO155"), + MTK_FUNCTION(2, "EINT167") + ), + MTK_PIN( + PINCTRL_PIN(156, "TDP1/LVDS(TDP1)"), + "V2", "mt8135", + MTK_EINT_FUNCTION(2, 166), + MTK_FUNCTION(0, "GPIO156"), + MTK_FUNCTION(2, "EINT166") + ), + MTK_PIN( + PINCTRL_PIN(157, "TDN0/LVDS(TDN0)"), + "U3", "mt8135", + MTK_EINT_FUNCTION(2, 165), + MTK_FUNCTION(0, "GPIO157"), + MTK_FUNCTION(2, "EINT165") + ), + MTK_PIN( + PINCTRL_PIN(158, "TDP0/LVDS(TDP0)"), + "U2", "mt8135", + MTK_EINT_FUNCTION(2, 164), + MTK_FUNCTION(0, "GPIO158"), + MTK_FUNCTION(2, "EINT164") + ), + MTK_PIN( + PINCTRL_PIN(159, "RDN3"), + "N5", "mt8135", + MTK_EINT_FUNCTION(2, 18), + MTK_FUNCTION(0, "GPIO159"), + MTK_FUNCTION(2, "EINT18") + ), + MTK_PIN( + PINCTRL_PIN(160, "RDP3"), + "N4", "mt8135", + MTK_EINT_FUNCTION(2, 30), + MTK_FUNCTION(0, "GPIO160"), + MTK_FUNCTION(2, "EINT30") + ), + MTK_PIN( + PINCTRL_PIN(161, "RDN2"), + "T2", "mt8135", + MTK_EINT_FUNCTION(2, 31), + MTK_FUNCTION(0, "GPIO161"), + MTK_FUNCTION(2, "EINT31") + ), + MTK_PIN( + PINCTRL_PIN(162, "RDP2"), + "T3", "mt8135", + MTK_EINT_FUNCTION(2, 32), + MTK_FUNCTION(0, "GPIO162"), + MTK_FUNCTION(2, "EINT32") + ), + MTK_PIN( + PINCTRL_PIN(163, "RCN"), + "P2", "mt8135", + MTK_EINT_FUNCTION(2, 33), + MTK_FUNCTION(0, "GPIO163"), + MTK_FUNCTION(2, "EINT33") + ), + MTK_PIN( + PINCTRL_PIN(164, "RCP"), + "P3", "mt8135", + MTK_EINT_FUNCTION(2, 39), + MTK_FUNCTION(0, "GPIO164"), + MTK_FUNCTION(2, "EINT39") + ), + MTK_PIN( + PINCTRL_PIN(165, "RDN1"), + "R3", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO165") + ), + MTK_PIN( + PINCTRL_PIN(166, "RDP1"), + "R2", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO166") + ), + MTK_PIN( + PINCTRL_PIN(167, "RDN0"), + "N3", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO167") + ), + MTK_PIN( + PINCTRL_PIN(168, "RDP0"), + "N2", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO168") + ), + MTK_PIN( + PINCTRL_PIN(169, "RDN1_A"), + "M4", "mt8135", + MTK_EINT_FUNCTION(2, 175), + MTK_FUNCTION(0, "GPIO169"), + MTK_FUNCTION(1, "CMDAT6"), + MTK_FUNCTION(2, "EINT175") + ), + MTK_PIN( + PINCTRL_PIN(170, "RDP1_A"), + "M3", "mt8135", + MTK_EINT_FUNCTION(2, 174), + MTK_FUNCTION(0, "GPIO170"), + MTK_FUNCTION(1, "CMDAT7"), + MTK_FUNCTION(2, "EINT174") + ), + MTK_PIN( + PINCTRL_PIN(171, "RCN_A"), + "L3", "mt8135", + MTK_EINT_FUNCTION(2, 171), + MTK_FUNCTION(0, "GPIO171"), + MTK_FUNCTION(1, "CMDAT8"), + MTK_FUNCTION(2, "EINT171") + ), + MTK_PIN( + PINCTRL_PIN(172, "RCP_A"), + "L2", "mt8135", + MTK_EINT_FUNCTION(2, 170), + MTK_FUNCTION(0, "GPIO172"), + MTK_FUNCTION(1, "CMDAT9"), + MTK_FUNCTION(2, "EINT170") + ), + MTK_PIN( + PINCTRL_PIN(173, "RDN0_A"), + "M2", "mt8135", + MTK_EINT_FUNCTION(2, 173), + MTK_FUNCTION(0, "GPIO173"), + MTK_FUNCTION(1, "CMHSYNC"), + MTK_FUNCTION(2, "EINT173") + ), + MTK_PIN( + PINCTRL_PIN(174, "RDP0_A"), + "M1", "mt8135", + MTK_EINT_FUNCTION(2, 172), + MTK_FUNCTION(0, "GPIO174"), + MTK_FUNCTION(1, "CMVSYNC"), + MTK_FUNCTION(2, "EINT172") + ), + MTK_PIN( + PINCTRL_PIN(175, "RDN1_B"), + "H2", "mt8135", + MTK_EINT_FUNCTION(2, 181), + MTK_FUNCTION(0, "GPIO175"), + MTK_FUNCTION(1, "CMDAT2"), + MTK_FUNCTION(2, "EINT181"), + MTK_FUNCTION(3, "CMCSD2") + ), + MTK_PIN( + PINCTRL_PIN(176, "RDP1_B"), + "H1", "mt8135", + MTK_EINT_FUNCTION(2, 180), + MTK_FUNCTION(0, "GPIO176"), + MTK_FUNCTION(1, "CMDAT3"), + MTK_FUNCTION(2, "EINT180"), + MTK_FUNCTION(3, "CMCSD3") + ), + MTK_PIN( + PINCTRL_PIN(177, "RCN_B"), + "K3", "mt8135", + MTK_EINT_FUNCTION(2, 177), + MTK_FUNCTION(0, "GPIO177"), + MTK_FUNCTION(1, "CMDAT4"), + MTK_FUNCTION(2, "EINT177") + ), + MTK_PIN( + PINCTRL_PIN(178, "RCP_B"), + "K2", "mt8135", + MTK_EINT_FUNCTION(2, 176), + MTK_FUNCTION(0, "GPIO178"), + MTK_FUNCTION(1, "CMDAT5"), + MTK_FUNCTION(2, "EINT176") + ), + MTK_PIN( + PINCTRL_PIN(179, "RDN0_B"), + "J3", "mt8135", + MTK_EINT_FUNCTION(2, 179), + MTK_FUNCTION(0, "GPIO179"), + MTK_FUNCTION(1, "CMDAT0"), + MTK_FUNCTION(2, "EINT179"), + MTK_FUNCTION(3, "CMCSD0") + ), + MTK_PIN( + PINCTRL_PIN(180, "RDP0_B"), + "J2", "mt8135", + MTK_EINT_FUNCTION(2, 178), + MTK_FUNCTION(0, "GPIO180"), + MTK_FUNCTION(1, "CMDAT1"), + MTK_FUNCTION(2, "EINT178"), + MTK_FUNCTION(3, "CMCSD1") + ), + MTK_PIN( + PINCTRL_PIN(181, "CMPCLK"), + "K4", "mt8135", + MTK_EINT_FUNCTION(2, 182), + MTK_FUNCTION(0, "GPIO181"), + MTK_FUNCTION(1, "CMPCLK"), + MTK_FUNCTION(2, "EINT182"), + MTK_FUNCTION(3, "CMCSK"), + MTK_FUNCTION(4, "CM2MCLK_4X"), + MTK_FUNCTION(5, "TS_AUXADC_SEL[3]"), + MTK_FUNCTION(6, "VENC_TEST_CK"), + MTK_FUNCTION(7, "TESTA_OUT27") + ), + MTK_PIN( + PINCTRL_PIN(182, "CMMCLK"), + "J5", "mt8135", + MTK_EINT_FUNCTION(2, 183), + MTK_FUNCTION(0, "GPIO182"), + MTK_FUNCTION(1, "CMMCLK"), + MTK_FUNCTION(2, "EINT183"), + MTK_FUNCTION(5, "TS_AUXADC_SEL[2]"), + MTK_FUNCTION(7, "TESTA_OUT28") + ), + MTK_PIN( + PINCTRL_PIN(183, "CMRST"), + "J6", "mt8135", + MTK_EINT_FUNCTION(2, 185), + MTK_FUNCTION(0, "GPIO183"), + MTK_FUNCTION(1, "CMRST"), + MTK_FUNCTION(2, "EINT185"), + MTK_FUNCTION(5, "TS_AUXADC_SEL[1]"), + MTK_FUNCTION(7, "TESTA_OUT30") + ), + MTK_PIN( + PINCTRL_PIN(184, "CMPDN"), + "J4", "mt8135", + MTK_EINT_FUNCTION(2, 184), + MTK_FUNCTION(0, "GPIO184"), + MTK_FUNCTION(1, "CMPDN"), + MTK_FUNCTION(2, "EINT184"), + MTK_FUNCTION(5, "TS_AUXADC_SEL[0]"), + MTK_FUNCTION(7, "TESTA_OUT29") + ), + MTK_PIN( + PINCTRL_PIN(185, "CMFLASH"), + "G4", "mt8135", + MTK_EINT_FUNCTION(2, 186), + MTK_FUNCTION(0, "GPIO185"), + MTK_FUNCTION(1, "CMFLASH"), + MTK_FUNCTION(2, "EINT186"), + MTK_FUNCTION(3, "CM2MCLK_3X"), + MTK_FUNCTION(6, "MFG_TEST_CK_1"), + MTK_FUNCTION(7, "TESTA_OUT31") + ), + MTK_PIN( + PINCTRL_PIN(186, "MRG_I2S_PCM_CLK"), + "F5", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO186"), + MTK_FUNCTION(1, "MRG_I2S_PCM_CLK"), + MTK_FUNCTION(3, "I2SIN_CK"), + MTK_FUNCTION(4, "PCM0_CK"), + MTK_FUNCTION(5, "DSP2_ICK"), + MTK_FUNCTION(6, "IMG_TEST_CK"), + MTK_FUNCTION(7, "USB_SCL") + ), + MTK_PIN( + PINCTRL_PIN(187, "MRG_I2S_PCM_SYNC"), + "G6", "mt8135", + MTK_EINT_FUNCTION(2, 16), + MTK_FUNCTION(0, "GPIO187"), + MTK_FUNCTION(1, "MRG_I2S_PCM_SYNC"), + MTK_FUNCTION(2, "EINT16"), + MTK_FUNCTION(3, "I2SIN_WS"), + MTK_FUNCTION(4, "PCM0_WS"), + MTK_FUNCTION(6, "DISP_TEST_CK") + ), + MTK_PIN( + PINCTRL_PIN(188, "MRG_I2S_PCM_RX"), + "G3", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO188"), + MTK_FUNCTION(1, "MRG_I2S_PCM_RX"), + MTK_FUNCTION(3, "I2SIN_DAT"), + MTK_FUNCTION(4, "PCM0_DI"), + MTK_FUNCTION(5, "DSP2_ID"), + MTK_FUNCTION(6, "MFG_TEST_CK"), + MTK_FUNCTION(7, "USB_SDA") + ), + MTK_PIN( + PINCTRL_PIN(189, "MRG_I2S_PCM_TX"), + "G5", "mt8135", + MTK_EINT_FUNCTION(2, 17), + MTK_FUNCTION(0, "GPIO189"), + MTK_FUNCTION(1, "MRG_I2S_PCM_TX"), + MTK_FUNCTION(2, "EINT17"), + MTK_FUNCTION(3, "I2SOUT_DAT"), + MTK_FUNCTION(4, "PCM0_DO"), + MTK_FUNCTION(6, "VDEC_TEST_CK") + ), + MTK_PIN( + PINCTRL_PIN(190, "SRCLKENAI"), + "K5", "mt8135", + MTK_EINT_FUNCTION(NO_EINT_SUPPORT, NO_EINT_SUPPORT), + MTK_FUNCTION(0, "GPIO190"), + MTK_FUNCTION(1, "SRCLKENAI") + ), + MTK_PIN( + PINCTRL_PIN(191, "URXD3"), + "C3", "mt8135", + MTK_EINT_FUNCTION(2, 87), + MTK_FUNCTION(0, "GPIO191"), + MTK_FUNCTION(1, "URXD3"), + MTK_FUNCTION(2, "EINT87"), + MTK_FUNCTION(3, "UTXD3"), + MTK_FUNCTION(5, "TS_AUX_ST"), + MTK_FUNCTION(6, "PWM4") + ), + MTK_PIN( + PINCTRL_PIN(192, "UTXD3"), + "B2", "mt8135", + MTK_EINT_FUNCTION(2, 86), + MTK_FUNCTION(0, "GPIO192"), + MTK_FUNCTION(1, "UTXD3"), + MTK_FUNCTION(2, "EINT86"), + MTK_FUNCTION(3, "URXD3"), + MTK_FUNCTION(5, "TS_AUX_CS_B"), + MTK_FUNCTION(6, "PWM3") + ), + MTK_PIN( + PINCTRL_PIN(193, "SDA2"), + "G2", "mt8135", + MTK_EINT_FUNCTION(2, 95), + MTK_FUNCTION(0, "GPIO193"), + MTK_FUNCTION(1, "SDA2"), + MTK_FUNCTION(2, "EINT95"), + MTK_FUNCTION(3, "CLKM5"), + MTK_FUNCTION(4, "PWM5"), + MTK_FUNCTION(5, "TS_AUX_PWDB") + ), + MTK_PIN( + PINCTRL_PIN(194, "SCL2"), + "F4", "mt8135", + MTK_EINT_FUNCTION(2, 94), + MTK_FUNCTION(0, "GPIO194"), + MTK_FUNCTION(1, "SCL2"), + MTK_FUNCTION(2, "EINT94"), + MTK_FUNCTION(3, "CLKM4"), + MTK_FUNCTION(4, "PWM4"), + MTK_FUNCTION(5, "TS_AUXADC_TEST_CK") + ), + MTK_PIN( + PINCTRL_PIN(195, "SDA1"), + "F2", "mt8135", + MTK_EINT_FUNCTION(2, 93), + MTK_FUNCTION(0, "GPIO195"), + MTK_FUNCTION(1, "SDA1"), + MTK_FUNCTION(2, "EINT93"), + MTK_FUNCTION(3, "CLKM3"), + MTK_FUNCTION(4, "PWM3"), + MTK_FUNCTION(5, "TS_AUX_SCLK_PWDB") + ), + MTK_PIN( + PINCTRL_PIN(196, "SCL1"), + "F3", "mt8135", + MTK_EINT_FUNCTION(2, 92), + MTK_FUNCTION(0, "GPIO196"), + MTK_FUNCTION(1, "SCL1"), + MTK_FUNCTION(2, "EINT92"), + MTK_FUNCTION(3, "CLKM2"), + MTK_FUNCTION(4, "PWM2"), + MTK_FUNCTION(5, "TS_AUX_DIN") + ), + MTK_PIN( + PINCTRL_PIN(197, "MSDC3_DAT2"), + "E1", "mt8135", + MTK_EINT_FUNCTION(2, 71), + MTK_FUNCTION(0, "GPIO197"), + MTK_FUNCTION(1, "MSDC3_DAT2"), + MTK_FUNCTION(2, "EINT71"), + MTK_FUNCTION(3, "SCL6"), + MTK_FUNCTION(4, "PWM5"), + MTK_FUNCTION(5, "CLKM4"), + MTK_FUNCTION(6, "MFG_TEST_CK_2") + ), + MTK_PIN( + PINCTRL_PIN(198, "MSDC3_DAT3"), + "C2", "mt8135", + MTK_EINT_FUNCTION(2, 72), + MTK_FUNCTION(0, "GPIO198"), + MTK_FUNCTION(1, "MSDC3_DAT3"), + MTK_FUNCTION(2, "EINT72"), + MTK_FUNCTION(3, "SDA6"), + MTK_FUNCTION(4, "PWM6"), + MTK_FUNCTION(5, "CLKM5"), + MTK_FUNCTION(6, "MFG_TEST_CK_3") + ), + MTK_PIN( + PINCTRL_PIN(199, "MSDC3_CMD"), + "D2", "mt8135", + MTK_EINT_FUNCTION(2, 68), + MTK_FUNCTION(0, "GPIO199"), + MTK_FUNCTION(1, "MSDC3_CMD"), + MTK_FUNCTION(2, "EINT68"), + MTK_FUNCTION(3, "SDA2"), + MTK_FUNCTION(4, "PWM2"), + MTK_FUNCTION(5, "CLKM1"), + MTK_FUNCTION(6, "MFG_TEST_CK_4") + ), + MTK_PIN( + PINCTRL_PIN(200, "MSDC3_CLK"), + "E2", "mt8135", + MTK_EINT_FUNCTION(2, 67), + MTK_FUNCTION(0, "GPIO200"), + MTK_FUNCTION(1, "MSDC3_CLK"), + MTK_FUNCTION(2, "EINT67"), + MTK_FUNCTION(3, "SCL2"), + MTK_FUNCTION(4, "PWM1"), + MTK_FUNCTION(5, "CLKM0") + ), + MTK_PIN( + PINCTRL_PIN(201, "MSDC3_DAT1"), + "D3", "mt8135", + MTK_EINT_FUNCTION(2, 70), + MTK_FUNCTION(0, "GPIO201"), + MTK_FUNCTION(1, "MSDC3_DAT1"), + MTK_FUNCTION(2, "EINT70"), + MTK_FUNCTION(3, "SDA3"), + MTK_FUNCTION(4, "PWM4"), + MTK_FUNCTION(5, "CLKM3") + ), + MTK_PIN( + PINCTRL_PIN(202, "MSDC3_DAT0"), + "E3", "mt8135", + MTK_EINT_FUNCTION(2, 69), + MTK_FUNCTION(0, "GPIO202"), + MTK_FUNCTION(1, "MSDC3_DAT0"), + MTK_FUNCTION(2, "EINT69"), + MTK_FUNCTION(3, "SCL3"), + MTK_FUNCTION(4, "PWM3"), + MTK_FUNCTION(5, "CLKM2") + ), +}; + +#endif /* __PINCTRL_MTK_MT8135_H */ diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-mt8173.h b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8173.h new file mode 100644 index 00000000000000..13e5b68bfe1b49 --- /dev/null +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-mt8173.h @@ -0,0 +1,1226 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Hongzhou.Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __PINCTRL_MTK_MT8173_H +#define __PINCTRL_MTK_MT8173_H + +#include +#include "pinctrl-mtk-common.h" + +static const struct mtk_desc_pin mtk_pins_mt8173[] = { + MTK_PIN( + PINCTRL_PIN(0, "EINT0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 0), + MTK_FUNCTION(0, "GPIO0"), + MTK_FUNCTION(1, "IRDA_PDN"), + MTK_FUNCTION(2, "I2S1_WS"), + MTK_FUNCTION(3, "AUD_SPDIF"), + MTK_FUNCTION(4, "UTXD0"), + MTK_FUNCTION(7, "DBG_MON_A_20_") + ), + MTK_PIN( + PINCTRL_PIN(1, "EINT1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 1), + MTK_FUNCTION(0, "GPIO1"), + MTK_FUNCTION(1, "IRDA_RXD"), + MTK_FUNCTION(2, "I2S1_BCK"), + MTK_FUNCTION(3, "SDA5"), + MTK_FUNCTION(4, "URXD0"), + MTK_FUNCTION(7, "DBG_MON_A_21_") + ), + MTK_PIN( + PINCTRL_PIN(2, "EINT2"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 2), + MTK_FUNCTION(0, "GPIO2"), + MTK_FUNCTION(1, "IRDA_TXD"), + MTK_FUNCTION(2, "I2S1_MCK"), + MTK_FUNCTION(3, "SCL5"), + MTK_FUNCTION(4, "UTXD3"), + MTK_FUNCTION(7, "DBG_MON_A_22_") + ), + MTK_PIN( + PINCTRL_PIN(3, "EINT3"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 3), + MTK_FUNCTION(0, "GPIO3"), + MTK_FUNCTION(1, "DSI1_TE"), + MTK_FUNCTION(2, "I2S1_DO_1"), + MTK_FUNCTION(3, "SDA3"), + MTK_FUNCTION(4, "URXD3"), + MTK_FUNCTION(7, "DBG_MON_A_23_") + ), + MTK_PIN( + PINCTRL_PIN(4, "EINT4"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 4), + MTK_FUNCTION(0, "GPIO4"), + MTK_FUNCTION(1, "DISP_PWM1"), + MTK_FUNCTION(2, "I2S1_DO_2"), + MTK_FUNCTION(3, "SCL3"), + MTK_FUNCTION(4, "UCTS3"), + MTK_FUNCTION(6, "SFWP_B") + ), + MTK_PIN( + PINCTRL_PIN(5, "EINT5"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 5), + MTK_FUNCTION(0, "GPIO5"), + MTK_FUNCTION(1, "PCM1_CLK"), + MTK_FUNCTION(2, "I2S2_WS"), + MTK_FUNCTION(3, "SPI_CK_3_"), + MTK_FUNCTION(4, "URTS3"), + MTK_FUNCTION(5, "AP_MD32_JTAG_TMS"), + MTK_FUNCTION(6, "SFOUT") + ), + MTK_PIN( + PINCTRL_PIN(6, "EINT6"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 6), + MTK_FUNCTION(0, "GPIO6"), + MTK_FUNCTION(1, "PCM1_SYNC"), + MTK_FUNCTION(2, "I2S2_BCK"), + MTK_FUNCTION(3, "SPI_MI_3_"), + MTK_FUNCTION(5, "AP_MD32_JTAG_TCK"), + MTK_FUNCTION(6, "SFCS0") + ), + MTK_PIN( + PINCTRL_PIN(7, "EINT7"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 7), + MTK_FUNCTION(0, "GPIO7"), + MTK_FUNCTION(1, "PCM1_DI"), + MTK_FUNCTION(2, "I2S2_DI_1"), + MTK_FUNCTION(3, "SPI_MO_3_"), + MTK_FUNCTION(5, "AP_MD32_JTAG_TDI"), + MTK_FUNCTION(6, "SFHOLD") + ), + MTK_PIN( + PINCTRL_PIN(8, "EINT8"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 8), + MTK_FUNCTION(0, "GPIO8"), + MTK_FUNCTION(1, "PCM1_DO"), + MTK_FUNCTION(2, "I2S2_DI_2"), + MTK_FUNCTION(3, "SPI_CS_3_"), + MTK_FUNCTION(4, "AUD_SPDIF"), + MTK_FUNCTION(5, "AP_MD32_JTAG_TDO"), + MTK_FUNCTION(6, "SFIN") + ), + MTK_PIN( + PINCTRL_PIN(9, "EINT9"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 9), + MTK_FUNCTION(0, "GPIO9"), + MTK_FUNCTION(1, "USB_DRVVBUS_P0"), + MTK_FUNCTION(2, "I2S2_MCK"), + MTK_FUNCTION(4, "USB_DRVVBUS_P1"), + MTK_FUNCTION(5, "AP_MD32_JTAG_TRST"), + MTK_FUNCTION(6, "SFCK") + ), + MTK_PIN( + PINCTRL_PIN(10, "EINT10"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 10), + MTK_FUNCTION(0, "GPIO10"), + MTK_FUNCTION(1, "CLKM0"), + MTK_FUNCTION(2, "DSI1_TE"), + MTK_FUNCTION(3, "DISP_PWM1"), + MTK_FUNCTION(4, "PWM4"), + MTK_FUNCTION(5, "IRDA_RXD") + ), + MTK_PIN( + PINCTRL_PIN(11, "EINT11"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 11), + MTK_FUNCTION(0, "GPIO11"), + MTK_FUNCTION(1, "CLKM1"), + MTK_FUNCTION(2, "I2S3_WS"), + MTK_FUNCTION(3, "USB_DRVVBUS_P0"), + MTK_FUNCTION(4, "PWM5"), + MTK_FUNCTION(5, "IRDA_TXD"), + MTK_FUNCTION(6, "USB_DRVVBUS_P1"), + MTK_FUNCTION(7, "DBG_MON_B_30_") + ), + MTK_PIN( + PINCTRL_PIN(12, "EINT12"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 12), + MTK_FUNCTION(0, "GPIO12"), + MTK_FUNCTION(1, "CLKM2"), + MTK_FUNCTION(2, "I2S3_BCK"), + MTK_FUNCTION(3, "SRCLKENA0"), + MTK_FUNCTION(5, "I2S2_WS"), + MTK_FUNCTION(7, "DBG_MON_B_32_") + ), + MTK_PIN( + PINCTRL_PIN(13, "EINT13"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 13), + MTK_FUNCTION(0, "GPIO13"), + MTK_FUNCTION(1, "CLKM3"), + MTK_FUNCTION(2, "I2S3_MCK"), + MTK_FUNCTION(3, "SRCLKENA0"), + MTK_FUNCTION(5, "I2S2_BCK"), + MTK_FUNCTION(7, "DBG_MON_A_32_") + ), + MTK_PIN( + PINCTRL_PIN(14, "EINT14"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 14), + MTK_FUNCTION(0, "GPIO14"), + MTK_FUNCTION(1, "CMDAT0"), + MTK_FUNCTION(2, "CMCSD0"), + MTK_FUNCTION(4, "CLKM2"), + MTK_FUNCTION(7, "DBG_MON_B_6_") + ), + MTK_PIN( + PINCTRL_PIN(15, "EINT15"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 15), + MTK_FUNCTION(0, "GPIO15"), + MTK_FUNCTION(1, "CMDAT1"), + MTK_FUNCTION(2, "CMCSD1"), + MTK_FUNCTION(3, "CMFLASH"), + MTK_FUNCTION(4, "CLKM3"), + MTK_FUNCTION(7, "DBG_MON_B_29_") + ), + MTK_PIN( + PINCTRL_PIN(16, "IDDIG"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 16), + MTK_FUNCTION(0, "GPIO16"), + MTK_FUNCTION(1, "IDDIG"), + MTK_FUNCTION(2, "CMFLASH"), + MTK_FUNCTION(4, "PWM5") + ), + MTK_PIN( + PINCTRL_PIN(17, "WATCHDOG"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 17), + MTK_FUNCTION(0, "GPIO17"), + MTK_FUNCTION(1, "WATCHDOG_AO") + ), + MTK_PIN( + PINCTRL_PIN(18, "CEC"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 18), + MTK_FUNCTION(0, "GPIO18"), + MTK_FUNCTION(1, "CEC") + ), + MTK_PIN( + PINCTRL_PIN(19, "HDMISCK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 19), + MTK_FUNCTION(0, "GPIO19"), + MTK_FUNCTION(1, "HDMISCK"), + MTK_FUNCTION(2, "HDCP_SCL") + ), + MTK_PIN( + PINCTRL_PIN(20, "HDMISD"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 20), + MTK_FUNCTION(0, "GPIO20"), + MTK_FUNCTION(1, "HDMISD"), + MTK_FUNCTION(2, "HDCP_SDA") + ), + MTK_PIN( + PINCTRL_PIN(21, "HTPLG"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 21), + MTK_FUNCTION(0, "GPIO21"), + MTK_FUNCTION(1, "HTPLG") + ), + MTK_PIN( + PINCTRL_PIN(22, "MSDC3_DAT0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 22), + MTK_FUNCTION(0, "GPIO22"), + MTK_FUNCTION(1, "MSDC3_DAT0") + ), + MTK_PIN( + PINCTRL_PIN(23, "MSDC3_DAT1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 23), + MTK_FUNCTION(0, "GPIO23"), + MTK_FUNCTION(1, "MSDC3_DAT1") + ), + MTK_PIN( + PINCTRL_PIN(24, "MSDC3_DAT2"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 24), + MTK_FUNCTION(0, "GPIO24"), + MTK_FUNCTION(1, "MSDC3_DAT2") + ), + MTK_PIN( + PINCTRL_PIN(25, "MSDC3_DAT3"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 25), + MTK_FUNCTION(0, "GPIO25"), + MTK_FUNCTION(1, "MSDC3_DAT3") + ), + MTK_PIN( + PINCTRL_PIN(26, "MSDC3_CLK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 26), + MTK_FUNCTION(0, "GPIO26"), + MTK_FUNCTION(1, "MSDC3_CLK") + ), + MTK_PIN( + PINCTRL_PIN(27, "MSDC3_CMD"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 27), + MTK_FUNCTION(0, "GPIO27"), + MTK_FUNCTION(1, "MSDC3_CMD") + ), + MTK_PIN( + PINCTRL_PIN(28, "MSDC3_DSL"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 28), + MTK_FUNCTION(0, "GPIO28"), + MTK_FUNCTION(1, "MSDC3_DSL") + ), + MTK_PIN( + PINCTRL_PIN(29, "UCTS2"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 29), + MTK_FUNCTION(0, "GPIO29"), + MTK_FUNCTION(1, "UCTS2") + ), + MTK_PIN( + PINCTRL_PIN(30, "URTS2"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 30), + MTK_FUNCTION(0, "GPIO30"), + MTK_FUNCTION(1, "URTS2") + ), + MTK_PIN( + PINCTRL_PIN(31, "URXD2"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 31), + MTK_FUNCTION(0, "GPIO31"), + MTK_FUNCTION(1, "URXD2"), + MTK_FUNCTION(2, "UTXD2") + ), + MTK_PIN( + PINCTRL_PIN(32, "UTXD2"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 32), + MTK_FUNCTION(0, "GPIO32"), + MTK_FUNCTION(1, "UTXD2"), + MTK_FUNCTION(2, "URXD2") + ), + MTK_PIN( + PINCTRL_PIN(33, "DAICLK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 33), + MTK_FUNCTION(0, "GPIO33"), + MTK_FUNCTION(1, " MRG_CLK"), + MTK_FUNCTION(2, "PCM0_CLK") + ), + MTK_PIN( + PINCTRL_PIN(34, "DAIPCMIN"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 34), + MTK_FUNCTION(0, "GPIO34"), + MTK_FUNCTION(1, " MRG_DI"), + MTK_FUNCTION(2, "PCM0_DI") + ), + MTK_PIN( + PINCTRL_PIN(35, "DAIPCMOUT"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 35), + MTK_FUNCTION(0, "GPIO35"), + MTK_FUNCTION(1, " MRG_DO"), + MTK_FUNCTION(2, "PCM0_DO") + ), + MTK_PIN( + PINCTRL_PIN(36, "DAISYNC"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 36), + MTK_FUNCTION(0, "GPIO36"), + MTK_FUNCTION(1, " MRG_SYNC"), + MTK_FUNCTION(2, "PCM0_SYNC") + ), + MTK_PIN( + PINCTRL_PIN(37, "EINT16"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 37), + MTK_FUNCTION(0, "GPIO37"), + MTK_FUNCTION(1, "USB_DRVVBUS_P0"), + MTK_FUNCTION(2, "USB_DRVVBUS_P1"), + MTK_FUNCTION(3, "PWM0"), + MTK_FUNCTION(4, "PWM1"), + MTK_FUNCTION(5, "PWM2"), + MTK_FUNCTION(6, "CLKM0") + ), + MTK_PIN( + PINCTRL_PIN(38, "CONN_RST"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 38), + MTK_FUNCTION(0, "GPIO38"), + MTK_FUNCTION(1, "USB_DRVVBUS_P0"), + MTK_FUNCTION(2, "USB_DRVVBUS_P1"), + MTK_FUNCTION(6, "CLKM1") + ), + MTK_PIN( + PINCTRL_PIN(39, "CM2MCLK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 39), + MTK_FUNCTION(0, "GPIO39"), + MTK_FUNCTION(1, "CM2MCLK"), + MTK_FUNCTION(2, "CMCSD0"), + MTK_FUNCTION(7, "DBG_MON_A_17_") + ), + MTK_PIN( + PINCTRL_PIN(40, "CMPCLK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 40), + MTK_FUNCTION(0, "GPIO40"), + MTK_FUNCTION(1, "CMPCLK"), + MTK_FUNCTION(2, "CMCSK"), + MTK_FUNCTION(3, "CMCSD2"), + MTK_FUNCTION(7, "DBG_MON_A_18_") + ), + MTK_PIN( + PINCTRL_PIN(41, "CMMCLK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 41), + MTK_FUNCTION(0, "GPIO41"), + MTK_FUNCTION(1, "CMMCLK"), + MTK_FUNCTION(7, "DBG_MON_A_19_") + ), + MTK_PIN( + PINCTRL_PIN(42, "DSI_TE"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 42), + MTK_FUNCTION(0, "GPIO42"), + MTK_FUNCTION(1, "DSI_TE") + ), + MTK_PIN( + PINCTRL_PIN(43, "SDA2"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 43), + MTK_FUNCTION(0, "GPIO43"), + MTK_FUNCTION(1, "SDA2") + ), + MTK_PIN( + PINCTRL_PIN(44, "SCL2"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 44), + MTK_FUNCTION(0, "GPIO44"), + MTK_FUNCTION(1, "SCL2") + ), + MTK_PIN( + PINCTRL_PIN(45, "SDA0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 45), + MTK_FUNCTION(0, "GPIO45"), + MTK_FUNCTION(1, "SDA0") + ), + MTK_PIN( + PINCTRL_PIN(46, "SCL0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 46), + MTK_FUNCTION(0, "GPIO46"), + MTK_FUNCTION(1, "SCL0") + ), + MTK_PIN( + PINCTRL_PIN(47, "RDN0_A"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 47), + MTK_FUNCTION(0, "GPIO47"), + MTK_FUNCTION(1, "CMDAT2") + ), + MTK_PIN( + PINCTRL_PIN(48, "RDP0_A"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 48), + MTK_FUNCTION(0, "GPIO48"), + MTK_FUNCTION(1, "CMDAT3") + ), + MTK_PIN( + PINCTRL_PIN(49, "RDN1_A"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 49), + MTK_FUNCTION(0, "GPIO49"), + MTK_FUNCTION(1, "CMDAT4") + ), + MTK_PIN( + PINCTRL_PIN(50, "RDP1_A"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 50), + MTK_FUNCTION(0, "GPIO50"), + MTK_FUNCTION(1, "CMDAT5") + ), + MTK_PIN( + PINCTRL_PIN(51, "RCN_A"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 51), + MTK_FUNCTION(0, "GPIO51"), + MTK_FUNCTION(1, "CMDAT6") + ), + MTK_PIN( + PINCTRL_PIN(52, "RCP_A"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 52), + MTK_FUNCTION(0, "GPIO52"), + MTK_FUNCTION(1, "CMDAT7") + ), + MTK_PIN( + PINCTRL_PIN(53, "RDN2_A"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 53), + MTK_FUNCTION(0, "GPIO53"), + MTK_FUNCTION(1, "CMDAT8"), + MTK_FUNCTION(2, "CMCSD3") + ), + MTK_PIN( + PINCTRL_PIN(54, "RDP2_A"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 54), + MTK_FUNCTION(0, "GPIO54"), + MTK_FUNCTION(1, "CMDAT9"), + MTK_FUNCTION(2, "CMCSD2") + ), + MTK_PIN( + PINCTRL_PIN(55, "RDN3_A"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 55), + MTK_FUNCTION(0, "GPIO55"), + MTK_FUNCTION(1, "CMHSYNC"), + MTK_FUNCTION(2, "CMCSD1") + ), + MTK_PIN( + PINCTRL_PIN(56, "RDP3_A"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 56), + MTK_FUNCTION(0, "GPIO56"), + MTK_FUNCTION(1, "CMVSYNC"), + MTK_FUNCTION(2, "CMCSD0") + ), + MTK_PIN( + PINCTRL_PIN(57, "MSDC0_DAT0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 57), + MTK_FUNCTION(0, "GPIO57"), + MTK_FUNCTION(1, "MSDC0_DAT0"), + MTK_FUNCTION(2, "I2S1_WS"), + MTK_FUNCTION(7, "DBG_MON_B_7_") + ), + MTK_PIN( + PINCTRL_PIN(58, "MSDC0_DAT1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 58), + MTK_FUNCTION(0, "GPIO58"), + MTK_FUNCTION(1, "MSDC0_DAT1"), + MTK_FUNCTION(2, "I2S1_BCK"), + MTK_FUNCTION(7, "DBG_MON_B_8_") + ), + MTK_PIN( + PINCTRL_PIN(59, "MSDC0_DAT2"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 59), + MTK_FUNCTION(0, "GPIO59"), + MTK_FUNCTION(1, "MSDC0_DAT2"), + MTK_FUNCTION(2, "I2S1_MCK"), + MTK_FUNCTION(7, "DBG_MON_B_9_") + ), + MTK_PIN( + PINCTRL_PIN(60, "MSDC0_DAT3"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 60), + MTK_FUNCTION(0, "GPIO60"), + MTK_FUNCTION(1, "MSDC0_DAT3"), + MTK_FUNCTION(2, "I2S1_DO_1"), + MTK_FUNCTION(7, "DBG_MON_B_10_") + ), + MTK_PIN( + PINCTRL_PIN(61, "MSDC0_DAT4"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 61), + MTK_FUNCTION(0, "GPIO61"), + MTK_FUNCTION(1, "MSDC0_DAT4"), + MTK_FUNCTION(2, "I2S1_DO_2"), + MTK_FUNCTION(7, "DBG_MON_B_11_") + ), + MTK_PIN( + PINCTRL_PIN(62, "MSDC0_DAT5"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 62), + MTK_FUNCTION(0, "GPIO62"), + MTK_FUNCTION(1, "MSDC0_DAT5"), + MTK_FUNCTION(2, "I2S2_WS"), + MTK_FUNCTION(7, "DBG_MON_B_12_") + ), + MTK_PIN( + PINCTRL_PIN(63, "MSDC0_DAT6"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 63), + MTK_FUNCTION(0, "GPIO63"), + MTK_FUNCTION(1, "MSDC0_DAT6"), + MTK_FUNCTION(2, "I2S2_BCK"), + MTK_FUNCTION(7, "DBG_MON_B_13_") + ), + MTK_PIN( + PINCTRL_PIN(64, "MSDC0_DAT7"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 64), + MTK_FUNCTION(0, "GPIO64"), + MTK_FUNCTION(1, "MSDC0_DAT7"), + MTK_FUNCTION(2, "I2S2_DI_1"), + MTK_FUNCTION(7, "DBG_MON_B_14_") + ), + MTK_PIN( + PINCTRL_PIN(65, "MSDC0_CLK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 65), + MTK_FUNCTION(0, "GPIO65"), + MTK_FUNCTION(1, "MSDC0_CLK"), + MTK_FUNCTION(7, "DBG_MON_B_16_") + ), + MTK_PIN( + PINCTRL_PIN(66, "MSDC0_CMD"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 66), + MTK_FUNCTION(0, "GPIO66"), + MTK_FUNCTION(1, "MSDC0_CMD"), + MTK_FUNCTION(2, "I2S2_DI_2"), + MTK_FUNCTION(7, "DBG_MON_B_15_") + ), + MTK_PIN( + PINCTRL_PIN(67, "MSDC0_DSL"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 67), + MTK_FUNCTION(0, "GPIO67"), + MTK_FUNCTION(1, "MSDC0_DSL"), + MTK_FUNCTION(7, "DBG_MON_B_17_") + ), + MTK_PIN( + PINCTRL_PIN(68, "MSDC0_RST_"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 68), + MTK_FUNCTION(0, "GPIO68"), + MTK_FUNCTION(1, "MSDC0_RSTB"), + MTK_FUNCTION(2, "I2S2_MCK"), + MTK_FUNCTION(7, "DBG_MON_B_18_") + ), + MTK_PIN( + PINCTRL_PIN(69, "SPI_CK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 69), + MTK_FUNCTION(0, "GPIO69"), + MTK_FUNCTION(1, "SPI_CK_0_"), + MTK_FUNCTION(2, "I2S3_DO_1"), + MTK_FUNCTION(3, "PWM0"), + MTK_FUNCTION(4, "PWM5"), + MTK_FUNCTION(5, "I2S2_MCK"), + MTK_FUNCTION(7, "DBG_MON_B_19_") + ), + MTK_PIN( + PINCTRL_PIN(70, "SPI_MI"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 70), + MTK_FUNCTION(0, "GPIO70"), + MTK_FUNCTION(1, "SPI_MI_0_"), + MTK_FUNCTION(2, "I2S3_DO_2"), + MTK_FUNCTION(3, "PWM1"), + MTK_FUNCTION(4, "SPI_MO_0_"), + MTK_FUNCTION(5, "I2S2_DI_1"), + MTK_FUNCTION(6, "DSI1_TE"), + MTK_FUNCTION(7, "DBG_MON_B_20_") + ), + MTK_PIN( + PINCTRL_PIN(71, "SPI_MO"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 71), + MTK_FUNCTION(0, "GPIO71"), + MTK_FUNCTION(1, "SPI_MO_0_"), + MTK_FUNCTION(2, "I2S3_DO_3"), + MTK_FUNCTION(3, "PWM2"), + MTK_FUNCTION(4, "SPI_MI_0_"), + MTK_FUNCTION(5, "I2S2_DI_2"), + MTK_FUNCTION(7, "DBG_MON_B_21_") + ), + MTK_PIN( + PINCTRL_PIN(72, "SPI_CS"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 72), + MTK_FUNCTION(0, "GPIO72"), + MTK_FUNCTION(1, "SPI_CS_0_"), + MTK_FUNCTION(2, "I2S3_DO_4"), + MTK_FUNCTION(3, "PWM3"), + MTK_FUNCTION(4, "PWM6"), + MTK_FUNCTION(5, "DISP_PWM1"), + MTK_FUNCTION(7, "DBG_MON_B_22_") + ), + MTK_PIN( + PINCTRL_PIN(73, "MSDC1_DAT0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 73), + MTK_FUNCTION(0, "GPIO73"), + MTK_FUNCTION(1, "MSDC1_DAT0"), + MTK_FUNCTION(7, "DBG_MON_B_24_") + ), + MTK_PIN( + PINCTRL_PIN(74, "MSDC1_DAT1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 74), + MTK_FUNCTION(0, "GPIO74"), + MTK_FUNCTION(1, "MSDC1_DAT1"), + MTK_FUNCTION(7, "DBG_MON_B_25_") + ), + MTK_PIN( + PINCTRL_PIN(75, "MSDC1_DAT2"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 75), + MTK_FUNCTION(0, "GPIO75"), + MTK_FUNCTION(1, "MSDC1_DAT2"), + MTK_FUNCTION(7, "DBG_MON_B_26_") + ), + MTK_PIN( + PINCTRL_PIN(76, "MSDC1_DAT3"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 76), + MTK_FUNCTION(0, "GPIO76"), + MTK_FUNCTION(1, "MSDC1_DAT3"), + MTK_FUNCTION(7, "DBG_MON_B_27_") + ), + MTK_PIN( + PINCTRL_PIN(77, "MSDC1_CLK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 77), + MTK_FUNCTION(0, "GPIO77"), + MTK_FUNCTION(1, "MSDC1_CLK"), + MTK_FUNCTION(7, "DBG_MON_B_28_") + ), + MTK_PIN( + PINCTRL_PIN(78, "MSDC1_CMD"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 78), + MTK_FUNCTION(0, "GPIO78"), + MTK_FUNCTION(1, "MSDC1_CMD"), + MTK_FUNCTION(7, "DBG_MON_B_23_") + ), + MTK_PIN( + PINCTRL_PIN(79, "PWRAP_SPI0_MI"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 79), + MTK_FUNCTION(0, "GPIO79"), + MTK_FUNCTION(1, "PWRAP_SPIMI"), + MTK_FUNCTION(2, "PWRAP_SPIMO") + ), + MTK_PIN( + PINCTRL_PIN(80, "PWRAP_SPI0_MO"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 80), + MTK_FUNCTION(0, "GPIO80"), + MTK_FUNCTION(1, "PWRAP_SPIMO"), + MTK_FUNCTION(2, "PWRAP_SPIMI") + ), + MTK_PIN( + PINCTRL_PIN(81, "PWRAP_SPI0_CK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 81), + MTK_FUNCTION(0, "GPIO81"), + MTK_FUNCTION(1, "PWRAP_SPICK") + ), + MTK_PIN( + PINCTRL_PIN(82, "PWRAP_SPI0_CSN"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 82), + MTK_FUNCTION(0, "GPIO82"), + MTK_FUNCTION(1, "PWRAP_SPICS") + ), + MTK_PIN( + PINCTRL_PIN(83, "AUD_CLK_MOSI"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 83), + MTK_FUNCTION(0, "GPIO83"), + MTK_FUNCTION(1, "AUD_CLK_MOSI") + ), + MTK_PIN( + PINCTRL_PIN(84, "AUD_DAT_MISO"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 84), + MTK_FUNCTION(0, "GPIO84"), + MTK_FUNCTION(1, "AUD_DAT_MISO"), + MTK_FUNCTION(2, "AUD_DAT_MOSI") + ), + MTK_PIN( + PINCTRL_PIN(85, "AUD_DAT_MOSI"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 85), + MTK_FUNCTION(0, "GPIO85"), + MTK_FUNCTION(1, "AUD_DAT_MOSI"), + MTK_FUNCTION(2, "AUD_DAT_MISO") + ), + MTK_PIN( + PINCTRL_PIN(86, "RTC32K_CK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 86), + MTK_FUNCTION(0, "GPIO86"), + MTK_FUNCTION(1, "RTC32K_CK") + ), + MTK_PIN( + PINCTRL_PIN(87, "DISP_PWM0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 87), + MTK_FUNCTION(0, "GPIO87"), + MTK_FUNCTION(1, "DISP_PWM0"), + MTK_FUNCTION(2, "DISP_PWM1"), + MTK_FUNCTION(7, "DBG_MON_B_31_") + ), + MTK_PIN( + PINCTRL_PIN(88, "SRCLKENAI"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 88), + MTK_FUNCTION(0, "GPIO88"), + MTK_FUNCTION(1, "SRCLKENAI") + ), + MTK_PIN( + PINCTRL_PIN(89, "SRCLKENAI2"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 89), + MTK_FUNCTION(0, "GPIO89"), + MTK_FUNCTION(1, "SRCLKENAI2") + ), + MTK_PIN( + PINCTRL_PIN(90, "SRCLKENA0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 90), + MTK_FUNCTION(0, "GPIO90"), + MTK_FUNCTION(1, "SRCLKENA0") + ), + MTK_PIN( + PINCTRL_PIN(91, "SRCLKENA1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 91), + MTK_FUNCTION(0, "GPIO91"), + MTK_FUNCTION(1, "SRCLKENA1") + ), + MTK_PIN( + PINCTRL_PIN(92, "PCM_CLK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 92), + MTK_FUNCTION(0, "GPIO92"), + MTK_FUNCTION(1, "PCM1_CLK"), + MTK_FUNCTION(2, "I2S0_BCK"), + MTK_FUNCTION(7, "DBG_MON_A_24_") + ), + MTK_PIN( + PINCTRL_PIN(93, "PCM_SYNC"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 93), + MTK_FUNCTION(0, "GPIO93"), + MTK_FUNCTION(1, "PCM1_SYNC"), + MTK_FUNCTION(2, "I2S0_WS"), + MTK_FUNCTION(7, "DBG_MON_A_25_") + ), + MTK_PIN( + PINCTRL_PIN(94, "PCM_RX"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 94), + MTK_FUNCTION(0, "GPIO94"), + MTK_FUNCTION(1, "PCM1_DI"), + MTK_FUNCTION(2, "I2S0_DI"), + MTK_FUNCTION(7, "DBG_MON_A_26_") + ), + MTK_PIN( + PINCTRL_PIN(95, "PCM_TX"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 95), + MTK_FUNCTION(0, "GPIO95"), + MTK_FUNCTION(1, "PCM1_DO"), + MTK_FUNCTION(2, "I2S0_DO"), + MTK_FUNCTION(7, "DBG_MON_A_27_") + ), + MTK_PIN( + PINCTRL_PIN(96, "URXD1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 96), + MTK_FUNCTION(0, "GPIO96"), + MTK_FUNCTION(1, "URXD1"), + MTK_FUNCTION(2, "UTXD1"), + MTK_FUNCTION(7, "DBG_MON_A_28_") + ), + MTK_PIN( + PINCTRL_PIN(97, "UTXD1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 97), + MTK_FUNCTION(0, "GPIO97"), + MTK_FUNCTION(1, "UTXD1"), + MTK_FUNCTION(2, "URXD1"), + MTK_FUNCTION(7, "DBG_MON_A_29_") + ), + MTK_PIN( + PINCTRL_PIN(98, "URTS1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 98), + MTK_FUNCTION(0, "GPIO98"), + MTK_FUNCTION(1, "URTS1"), + MTK_FUNCTION(2, "UCTS1"), + MTK_FUNCTION(7, "DBG_MON_A_30_") + ), + MTK_PIN( + PINCTRL_PIN(99, "UCTS1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 99), + MTK_FUNCTION(0, "GPIO99"), + MTK_FUNCTION(1, "UCTS1"), + MTK_FUNCTION(2, "URTS1"), + MTK_FUNCTION(7, "DBG_MON_A_31_") + ), + MTK_PIN( + PINCTRL_PIN(100, "MSDC2_DAT0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 100), + MTK_FUNCTION(0, "GPIO100"), + MTK_FUNCTION(1, "MSDC2_DAT0"), + MTK_FUNCTION(3, "USB_DRVVBUS_P0"), + MTK_FUNCTION(4, "SDA5"), + MTK_FUNCTION(5, "USB_DRVVBUS_P1"), + MTK_FUNCTION(7, "DBG_MON_B_0_") + ), + MTK_PIN( + PINCTRL_PIN(101, "MSDC2_DAT1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 101), + MTK_FUNCTION(0, "GPIO101"), + MTK_FUNCTION(1, "MSDC2_DAT1"), + MTK_FUNCTION(3, "AUD_SPDIF"), + MTK_FUNCTION(4, "SCL5"), + MTK_FUNCTION(7, "DBG_MON_B_1_") + ), + MTK_PIN( + PINCTRL_PIN(102, "MSDC2_DAT2"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 102), + MTK_FUNCTION(0, "GPIO102"), + MTK_FUNCTION(1, "MSDC2_DAT2"), + MTK_FUNCTION(3, "UTXD0"), + MTK_FUNCTION(5, "PWM0"), + MTK_FUNCTION(6, "SPI_CK_1_"), + MTK_FUNCTION(7, "DBG_MON_B_2_") + ), + MTK_PIN( + PINCTRL_PIN(103, "MSDC2_DAT3"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 103), + MTK_FUNCTION(0, "GPIO103"), + MTK_FUNCTION(1, "MSDC2_DAT3"), + MTK_FUNCTION(3, "URXD0"), + MTK_FUNCTION(5, "PWM1"), + MTK_FUNCTION(6, "SPI_MI_1_"), + MTK_FUNCTION(7, "DBG_MON_B_3_") + ), + MTK_PIN( + PINCTRL_PIN(104, "MSDC2_CLK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 104), + MTK_FUNCTION(0, "GPIO104"), + MTK_FUNCTION(1, "MSDC2_CLK"), + MTK_FUNCTION(3, "UTXD3"), + MTK_FUNCTION(4, "SDA3"), + MTK_FUNCTION(5, "PWM2"), + MTK_FUNCTION(6, "SPI_MO_1_"), + MTK_FUNCTION(7, "DBG_MON_B_4_") + ), + MTK_PIN( + PINCTRL_PIN(105, "MSDC2_CMD"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 105), + MTK_FUNCTION(0, "GPIO105"), + MTK_FUNCTION(1, "MSDC2_CMD"), + MTK_FUNCTION(3, "URXD3"), + MTK_FUNCTION(4, "SCL3"), + MTK_FUNCTION(5, "PWM3"), + MTK_FUNCTION(6, "SPI_CS_1_"), + MTK_FUNCTION(7, "DBG_MON_B_5_") + ), + MTK_PIN( + PINCTRL_PIN(106, "SDA3"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 106), + MTK_FUNCTION(0, "GPIO106"), + MTK_FUNCTION(1, "SDA3") + ), + MTK_PIN( + PINCTRL_PIN(107, "SCL3"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 107), + MTK_FUNCTION(0, "GPIO107"), + MTK_FUNCTION(1, "SCL3") + ), + MTK_PIN( + PINCTRL_PIN(108, "JTMS"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 108), + MTK_FUNCTION(0, "GPIO108"), + MTK_FUNCTION(1, "JTMS"), + MTK_FUNCTION(2, " MFG_JTAG_TMS"), + MTK_FUNCTION(5, "AP_MD32_JTAG_TMS"), + MTK_FUNCTION(6, "DFD_TMS") + ), + MTK_PIN( + PINCTRL_PIN(109, "JTCK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 109), + MTK_FUNCTION(0, "GPIO109"), + MTK_FUNCTION(1, "JTCK"), + MTK_FUNCTION(2, " MFG_JTAG_TCK"), + MTK_FUNCTION(5, "AP_MD32_JTAG_TCK"), + MTK_FUNCTION(6, "DFD_TCK") + ), + MTK_PIN( + PINCTRL_PIN(110, "JTDI"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 110), + MTK_FUNCTION(0, "GPIO110"), + MTK_FUNCTION(1, "JTDI"), + MTK_FUNCTION(2, " MFG_JTAG_TDI"), + MTK_FUNCTION(5, "AP_MD32_JTAG_TDI"), + MTK_FUNCTION(6, "DFD_TDI") + ), + MTK_PIN( + PINCTRL_PIN(111, "JTDO"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 111), + MTK_FUNCTION(0, "GPIO111"), + MTK_FUNCTION(1, "JTDO"), + MTK_FUNCTION(2, "MFG_JTAG_TDO"), + MTK_FUNCTION(5, "AP_MD32_JTAG_TDO"), + MTK_FUNCTION(6, "DFD_TDO") + ), + MTK_PIN( + PINCTRL_PIN(112, "JTRST_B"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 112), + MTK_FUNCTION(0, "GPIO112"), + MTK_FUNCTION(1, "JTRST_B"), + MTK_FUNCTION(2, " MFG_JTAG_TRSTN"), + MTK_FUNCTION(5, "AP_MD32_JTAG_TRST"), + MTK_FUNCTION(6, "DFD_NTRST") + ), + MTK_PIN( + PINCTRL_PIN(113, "URXD0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 113), + MTK_FUNCTION(0, "GPIO113"), + MTK_FUNCTION(1, "URXD0"), + MTK_FUNCTION(2, "UTXD0"), + MTK_FUNCTION(6, "I2S2_WS"), + MTK_FUNCTION(7, "DBG_MON_A_0_") + ), + MTK_PIN( + PINCTRL_PIN(114, "UTXD0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 114), + MTK_FUNCTION(0, "GPIO114"), + MTK_FUNCTION(1, "UTXD0"), + MTK_FUNCTION(2, "URXD0"), + MTK_FUNCTION(6, "I2S2_BCK"), + MTK_FUNCTION(7, "DBG_MON_A_1_") + ), + MTK_PIN( + PINCTRL_PIN(115, "URTS0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 115), + MTK_FUNCTION(0, "GPIO115"), + MTK_FUNCTION(1, "URTS0"), + MTK_FUNCTION(2, "UCTS0"), + MTK_FUNCTION(6, "I2S2_MCK"), + MTK_FUNCTION(7, "DBG_MON_A_2_") + ), + MTK_PIN( + PINCTRL_PIN(116, "UCTS0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 116), + MTK_FUNCTION(0, "GPIO116"), + MTK_FUNCTION(1, "UCTS0"), + MTK_FUNCTION(2, "URTS0"), + MTK_FUNCTION(6, "I2S2_DI_1"), + MTK_FUNCTION(7, "DBG_MON_A_3_") + ), + MTK_PIN( + PINCTRL_PIN(117, "URXD3"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 117), + MTK_FUNCTION(0, "GPIO117"), + MTK_FUNCTION(1, "URXD3"), + MTK_FUNCTION(2, "UTXD3"), + MTK_FUNCTION(7, "DBG_MON_A_9_") + ), + MTK_PIN( + PINCTRL_PIN(118, "UTXD3"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 118), + MTK_FUNCTION(0, "GPIO118"), + MTK_FUNCTION(1, "UTXD3"), + MTK_FUNCTION(2, "URXD3"), + MTK_FUNCTION(7, "DBG_MON_A_10_") + ), + MTK_PIN( + PINCTRL_PIN(119, "KPROW0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 119), + MTK_FUNCTION(0, "GPIO119"), + MTK_FUNCTION(1, "KROW0"), + MTK_FUNCTION(7, "DBG_MON_A_11_") + ), + MTK_PIN( + PINCTRL_PIN(120, "KPROW1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 120), + MTK_FUNCTION(0, "GPIO120"), + MTK_FUNCTION(1, "KROW1"), + MTK_FUNCTION(3, "PWM6"), + MTK_FUNCTION(7, "DBG_MON_A_12_") + ), + MTK_PIN( + PINCTRL_PIN(121, "KPROW2"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 121), + MTK_FUNCTION(0, "GPIO121"), + MTK_FUNCTION(1, "KROW2"), + MTK_FUNCTION(2, "IRDA_PDN"), + MTK_FUNCTION(3, "USB_DRVVBUS_P0"), + MTK_FUNCTION(4, "PWM4"), + MTK_FUNCTION(5, "USB_DRVVBUS_P1"), + MTK_FUNCTION(7, "DBG_MON_A_13_") + ), + MTK_PIN( + PINCTRL_PIN(122, "KPCOL0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 122), + MTK_FUNCTION(0, "GPIO122"), + MTK_FUNCTION(1, "KCOL0"), + MTK_FUNCTION(7, "DBG_MON_A_14_") + ), + MTK_PIN( + PINCTRL_PIN(123, "KPCOL1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 123), + MTK_FUNCTION(0, "GPIO123"), + MTK_FUNCTION(1, "KCOL1"), + MTK_FUNCTION(2, "IRDA_RXD"), + MTK_FUNCTION(3, "PWM5"), + MTK_FUNCTION(7, "DBG_MON_A_15_") + ), + MTK_PIN( + PINCTRL_PIN(124, "KPCOL2"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 124), + MTK_FUNCTION(0, "GPIO124"), + MTK_FUNCTION(1, "KCOL2"), + MTK_FUNCTION(2, "IRDA_TXD"), + MTK_FUNCTION(3, "USB_DRVVBUS_P0"), + MTK_FUNCTION(4, "PWM3"), + MTK_FUNCTION(5, "USB_DRVVBUS_P1"), + MTK_FUNCTION(7, "DBG_MON_A_16_") + ), + MTK_PIN( + PINCTRL_PIN(125, "SDA1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 125), + MTK_FUNCTION(0, "GPIO125"), + MTK_FUNCTION(1, "SDA1") + ), + MTK_PIN( + PINCTRL_PIN(126, "SCL1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 126), + MTK_FUNCTION(0, "GPIO126"), + MTK_FUNCTION(1, "SCL1") + ), + MTK_PIN( + PINCTRL_PIN(127, "LCM_RST"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 127), + MTK_FUNCTION(0, "GPIO127"), + MTK_FUNCTION(1, "LCM_RST") + ), + MTK_PIN( + PINCTRL_PIN(128, "I2S0_LRCK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 128), + MTK_FUNCTION(0, "GPIO128"), + MTK_FUNCTION(1, "I2S0_WS"), + MTK_FUNCTION(2, "I2S1_WS"), + MTK_FUNCTION(3, "I2S2_WS"), + MTK_FUNCTION(5, "SPI_CK_2_"), + MTK_FUNCTION(7, "DBG_MON_A_4_") + ), + MTK_PIN( + PINCTRL_PIN(129, "I2S0_BCK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 129), + MTK_FUNCTION(0, "GPIO129"), + MTK_FUNCTION(1, "I2S0_BCK"), + MTK_FUNCTION(2, "I2S1_BCK"), + MTK_FUNCTION(3, "I2S2_BCK"), + MTK_FUNCTION(5, "SPI_MI_2_"), + MTK_FUNCTION(7, "DBG_MON_A_5_") + ), + MTK_PIN( + PINCTRL_PIN(130, "I2S0_MCK"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 130), + MTK_FUNCTION(0, "GPIO130"), + MTK_FUNCTION(1, "I2S0_MCK"), + MTK_FUNCTION(2, "I2S1_MCK"), + MTK_FUNCTION(3, "I2S2_MCK"), + MTK_FUNCTION(5, "SPI_MO_2_"), + MTK_FUNCTION(7, "DBG_MON_A_6_") + ), + MTK_PIN( + PINCTRL_PIN(131, "I2S0_DATA0"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 131), + MTK_FUNCTION(0, "GPIO131"), + MTK_FUNCTION(1, "I2S0_DO"), + MTK_FUNCTION(2, "I2S1_DO_1"), + MTK_FUNCTION(3, "I2S2_DI_1"), + MTK_FUNCTION(5, "SPI_CS_2_"), + MTK_FUNCTION(7, "DBG_MON_A_7_") + ), + MTK_PIN( + PINCTRL_PIN(132, "I2S0_DATA1"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 132), + MTK_FUNCTION(0, "GPIO132"), + MTK_FUNCTION(1, "I2S0_DI"), + MTK_FUNCTION(2, "I2S1_DO_2"), + MTK_FUNCTION(3, "I2S2_DI_2"), + MTK_FUNCTION(7, "DBG_MON_A_8_") + ), + MTK_PIN( + PINCTRL_PIN(133, "SDA4"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 133), + MTK_FUNCTION(0, "GPIO133"), + MTK_FUNCTION(1, "SDA4") + ), + MTK_PIN( + PINCTRL_PIN(134, "SCL4"), + NULL, "mt8173", + MTK_EINT_FUNCTION(0, 134), + MTK_FUNCTION(0, "GPIO134"), + MTK_FUNCTION(1, "SCL4") + ), +}; + +#endif /* __PINCTRL_MTK_MT8173_H */ diff --git a/drivers/pinctrl/meson/Makefile b/drivers/pinctrl/meson/Makefile index eafc216067a4e8..c751d22fdf29f6 100644 --- a/drivers/pinctrl/meson/Makefile +++ b/drivers/pinctrl/meson/Makefile @@ -1,2 +1,2 @@ -obj-y += pinctrl-meson8.o +obj-y += pinctrl-meson8.o pinctrl-meson8b.o obj-y += pinctrl-meson.o diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index a2bf49ce16e702..edcd140e089968 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -13,8 +13,9 @@ /* * The available pins are organized in banks (A,B,C,D,E,X,Y,Z,AO, - * BOOT,CARD for meson6 and X,Y,DV,H,Z,AO,BOOT,CARD for meson8) and - * each bank has a variable number of pins. + * BOOT,CARD for meson6, X,Y,DV,H,Z,AO,BOOT,CARD for meson8 and + * X,Y,DV,H,AO,BOOT,CARD,DIF for meson8b) and each bank has a + * variable number of pins. * * The AO bank is special because it belongs to the Always-On power * domain which can't be powered off; the bank also uses a set of @@ -544,6 +545,10 @@ static const struct of_device_id meson_pinctrl_dt_match[] = { .compatible = "amlogic,meson8-pinctrl", .data = &meson8_pinctrl_data, }, + { + .compatible = "amlogic,meson8b-pinctrl", + .data = &meson8b_pinctrl_data, + }, { }, }; MODULE_DEVICE_TABLE(of, meson_pinctrl_dt_match); diff --git a/drivers/pinctrl/meson/pinctrl-meson.h b/drivers/pinctrl/meson/pinctrl-meson.h index bfea8adc79534b..0fe7d53849ce09 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.h +++ b/drivers/pinctrl/meson/pinctrl-meson.h @@ -155,6 +155,8 @@ struct meson_pinctrl { struct meson_domain *domains; }; +#define PIN(x, b) (b + x) + #define GROUP(grp, r, b) \ { \ .name = #grp, \ @@ -165,10 +167,10 @@ struct meson_pinctrl { .domain = 0, \ } -#define GPIO_GROUP(gpio) \ +#define GPIO_GROUP(gpio, b) \ { \ .name = #gpio, \ - .pins = (const unsigned int[]){ PIN_ ## gpio}, \ + .pins = (const unsigned int[]){ PIN(gpio, b) }, \ .num_pins = 1, \ .is_gpio = true, \ } @@ -204,6 +206,7 @@ struct meson_pinctrl { }, \ } -#define MESON_PIN(x) PINCTRL_PIN(PIN_ ## x, #x) +#define MESON_PIN(x, b) PINCTRL_PIN(PIN(x, b), #x) extern struct meson_pinctrl_data meson8_pinctrl_data; +extern struct meson_pinctrl_data meson8b_pinctrl_data; diff --git a/drivers/pinctrl/meson/pinctrl-meson8.c b/drivers/pinctrl/meson/pinctrl-meson8.c index f8aa3a28176730..7b1cc91733efaf 100644 --- a/drivers/pinctrl/meson/pinctrl-meson8.c +++ b/drivers/pinctrl/meson/pinctrl-meson8.c @@ -14,620 +14,482 @@ #include #include "pinctrl-meson.h" -#define AO_OFFSET 120 - -#define PIN_GPIOX_0 GPIOX_0 -#define PIN_GPIOX_1 GPIOX_1 -#define PIN_GPIOX_2 GPIOX_2 -#define PIN_GPIOX_3 GPIOX_3 -#define PIN_GPIOX_4 GPIOX_4 -#define PIN_GPIOX_5 GPIOX_5 -#define PIN_GPIOX_6 GPIOX_6 -#define PIN_GPIOX_7 GPIOX_7 -#define PIN_GPIOX_8 GPIOX_8 -#define PIN_GPIOX_9 GPIOX_9 -#define PIN_GPIOX_10 GPIOX_10 -#define PIN_GPIOX_11 GPIOX_11 -#define PIN_GPIOX_12 GPIOX_12 -#define PIN_GPIOX_13 GPIOX_13 -#define PIN_GPIOX_14 GPIOX_14 -#define PIN_GPIOX_15 GPIOX_15 -#define PIN_GPIOX_16 GPIOX_16 -#define PIN_GPIOX_17 GPIOX_17 -#define PIN_GPIOX_18 GPIOX_18 -#define PIN_GPIOX_19 GPIOX_19 -#define PIN_GPIOX_20 GPIOX_20 -#define PIN_GPIOX_21 GPIOX_21 -#define PIN_GPIOY_0 GPIOY_0 -#define PIN_GPIOY_1 GPIOY_1 -#define PIN_GPIOY_2 GPIOY_2 -#define PIN_GPIOY_3 GPIOY_3 -#define PIN_GPIOY_4 GPIOY_4 -#define PIN_GPIOY_5 GPIOY_5 -#define PIN_GPIOY_6 GPIOY_6 -#define PIN_GPIOY_7 GPIOY_7 -#define PIN_GPIOY_8 GPIOY_8 -#define PIN_GPIOY_9 GPIOY_9 -#define PIN_GPIOY_10 GPIOY_10 -#define PIN_GPIOY_11 GPIOY_11 -#define PIN_GPIOY_12 GPIOY_12 -#define PIN_GPIOY_13 GPIOY_13 -#define PIN_GPIOY_14 GPIOY_14 -#define PIN_GPIOY_15 GPIOY_15 -#define PIN_GPIOY_16 GPIOY_16 -#define PIN_GPIODV_0 GPIODV_0 -#define PIN_GPIODV_1 GPIODV_1 -#define PIN_GPIODV_2 GPIODV_2 -#define PIN_GPIODV_3 GPIODV_3 -#define PIN_GPIODV_4 GPIODV_4 -#define PIN_GPIODV_5 GPIODV_5 -#define PIN_GPIODV_6 GPIODV_6 -#define PIN_GPIODV_7 GPIODV_7 -#define PIN_GPIODV_8 GPIODV_8 -#define PIN_GPIODV_9 GPIODV_9 -#define PIN_GPIODV_10 GPIODV_10 -#define PIN_GPIODV_11 GPIODV_11 -#define PIN_GPIODV_12 GPIODV_12 -#define PIN_GPIODV_13 GPIODV_13 -#define PIN_GPIODV_14 GPIODV_14 -#define PIN_GPIODV_15 GPIODV_15 -#define PIN_GPIODV_16 GPIODV_16 -#define PIN_GPIODV_17 GPIODV_17 -#define PIN_GPIODV_18 GPIODV_18 -#define PIN_GPIODV_19 GPIODV_19 -#define PIN_GPIODV_20 GPIODV_20 -#define PIN_GPIODV_21 GPIODV_21 -#define PIN_GPIODV_22 GPIODV_22 -#define PIN_GPIODV_23 GPIODV_23 -#define PIN_GPIODV_24 GPIODV_24 -#define PIN_GPIODV_25 GPIODV_25 -#define PIN_GPIODV_26 GPIODV_26 -#define PIN_GPIODV_27 GPIODV_27 -#define PIN_GPIODV_28 GPIODV_28 -#define PIN_GPIODV_29 GPIODV_29 -#define PIN_GPIOH_0 GPIOH_0 -#define PIN_GPIOH_1 GPIOH_1 -#define PIN_GPIOH_2 GPIOH_2 -#define PIN_GPIOH_3 GPIOH_3 -#define PIN_GPIOH_4 GPIOH_4 -#define PIN_GPIOH_5 GPIOH_5 -#define PIN_GPIOH_6 GPIOH_6 -#define PIN_GPIOH_7 GPIOH_7 -#define PIN_GPIOH_8 GPIOH_8 -#define PIN_GPIOH_9 GPIOH_9 -#define PIN_GPIOZ_0 GPIOZ_0 -#define PIN_GPIOZ_1 GPIOZ_1 -#define PIN_GPIOZ_2 GPIOZ_2 -#define PIN_GPIOZ_3 GPIOZ_3 -#define PIN_GPIOZ_4 GPIOZ_4 -#define PIN_GPIOZ_5 GPIOZ_5 -#define PIN_GPIOZ_6 GPIOZ_6 -#define PIN_GPIOZ_7 GPIOZ_7 -#define PIN_GPIOZ_8 GPIOZ_8 -#define PIN_GPIOZ_9 GPIOZ_9 -#define PIN_GPIOZ_10 GPIOZ_10 -#define PIN_GPIOZ_11 GPIOZ_11 -#define PIN_GPIOZ_12 GPIOZ_12 -#define PIN_GPIOZ_13 GPIOZ_13 -#define PIN_GPIOZ_14 GPIOZ_14 -#define PIN_CARD_0 CARD_0 -#define PIN_CARD_1 CARD_1 -#define PIN_CARD_2 CARD_2 -#define PIN_CARD_3 CARD_3 -#define PIN_CARD_4 CARD_4 -#define PIN_CARD_5 CARD_5 -#define PIN_CARD_6 CARD_6 -#define PIN_BOOT_0 BOOT_0 -#define PIN_BOOT_1 BOOT_1 -#define PIN_BOOT_2 BOOT_2 -#define PIN_BOOT_3 BOOT_3 -#define PIN_BOOT_4 BOOT_4 -#define PIN_BOOT_5 BOOT_5 -#define PIN_BOOT_6 BOOT_6 -#define PIN_BOOT_7 BOOT_7 -#define PIN_BOOT_8 BOOT_8 -#define PIN_BOOT_9 BOOT_9 -#define PIN_BOOT_10 BOOT_10 -#define PIN_BOOT_11 BOOT_11 -#define PIN_BOOT_12 BOOT_12 -#define PIN_BOOT_13 BOOT_13 -#define PIN_BOOT_14 BOOT_14 -#define PIN_BOOT_15 BOOT_15 -#define PIN_BOOT_16 BOOT_16 -#define PIN_BOOT_17 BOOT_17 -#define PIN_BOOT_18 BOOT_18 - -#define PIN_GPIOAO_0 (AO_OFFSET + GPIOAO_0) -#define PIN_GPIOAO_1 (AO_OFFSET + GPIOAO_1) -#define PIN_GPIOAO_2 (AO_OFFSET + GPIOAO_2) -#define PIN_GPIOAO_3 (AO_OFFSET + GPIOAO_3) -#define PIN_GPIOAO_4 (AO_OFFSET + GPIOAO_4) -#define PIN_GPIOAO_5 (AO_OFFSET + GPIOAO_5) -#define PIN_GPIOAO_6 (AO_OFFSET + GPIOAO_6) -#define PIN_GPIOAO_7 (AO_OFFSET + GPIOAO_7) -#define PIN_GPIOAO_8 (AO_OFFSET + GPIOAO_8) -#define PIN_GPIOAO_9 (AO_OFFSET + GPIOAO_9) -#define PIN_GPIOAO_10 (AO_OFFSET + GPIOAO_10) -#define PIN_GPIOAO_11 (AO_OFFSET + GPIOAO_11) -#define PIN_GPIOAO_12 (AO_OFFSET + GPIOAO_12) -#define PIN_GPIOAO_13 (AO_OFFSET + GPIOAO_13) -#define PIN_GPIO_BSD_EN (AO_OFFSET + GPIO_BSD_EN) -#define PIN_GPIO_TEST_N (AO_OFFSET + GPIO_TEST_N) +#define AO_OFF 120 static const struct pinctrl_pin_desc meson8_pins[] = { - MESON_PIN(GPIOX_0), - MESON_PIN(GPIOX_1), - MESON_PIN(GPIOX_2), - MESON_PIN(GPIOX_3), - MESON_PIN(GPIOX_4), - MESON_PIN(GPIOX_5), - MESON_PIN(GPIOX_6), - MESON_PIN(GPIOX_7), - MESON_PIN(GPIOX_8), - MESON_PIN(GPIOX_9), - MESON_PIN(GPIOX_10), - MESON_PIN(GPIOX_11), - MESON_PIN(GPIOX_12), - MESON_PIN(GPIOX_13), - MESON_PIN(GPIOX_14), - MESON_PIN(GPIOX_15), - MESON_PIN(GPIOX_16), - MESON_PIN(GPIOX_17), - MESON_PIN(GPIOX_18), - MESON_PIN(GPIOX_19), - MESON_PIN(GPIOX_20), - MESON_PIN(GPIOX_21), - MESON_PIN(GPIOY_0), - MESON_PIN(GPIOY_1), - MESON_PIN(GPIOY_2), - MESON_PIN(GPIOY_3), - MESON_PIN(GPIOY_4), - MESON_PIN(GPIOY_5), - MESON_PIN(GPIOY_6), - MESON_PIN(GPIOY_7), - MESON_PIN(GPIOY_8), - MESON_PIN(GPIOY_9), - MESON_PIN(GPIOY_10), - MESON_PIN(GPIOY_11), - MESON_PIN(GPIOY_12), - MESON_PIN(GPIOY_13), - MESON_PIN(GPIOY_14), - MESON_PIN(GPIOY_15), - MESON_PIN(GPIOY_16), - MESON_PIN(GPIODV_0), - MESON_PIN(GPIODV_1), - MESON_PIN(GPIODV_2), - MESON_PIN(GPIODV_3), - MESON_PIN(GPIODV_4), - MESON_PIN(GPIODV_5), - MESON_PIN(GPIODV_6), - MESON_PIN(GPIODV_7), - MESON_PIN(GPIODV_8), - MESON_PIN(GPIODV_9), - MESON_PIN(GPIODV_10), - MESON_PIN(GPIODV_11), - MESON_PIN(GPIODV_12), - MESON_PIN(GPIODV_13), - MESON_PIN(GPIODV_14), - MESON_PIN(GPIODV_15), - MESON_PIN(GPIODV_16), - MESON_PIN(GPIODV_17), - MESON_PIN(GPIODV_18), - MESON_PIN(GPIODV_19), - MESON_PIN(GPIODV_20), - MESON_PIN(GPIODV_21), - MESON_PIN(GPIODV_22), - MESON_PIN(GPIODV_23), - MESON_PIN(GPIODV_24), - MESON_PIN(GPIODV_25), - MESON_PIN(GPIODV_26), - MESON_PIN(GPIODV_27), - MESON_PIN(GPIODV_28), - MESON_PIN(GPIODV_29), - MESON_PIN(GPIOH_0), - MESON_PIN(GPIOH_1), - MESON_PIN(GPIOH_2), - MESON_PIN(GPIOH_3), - MESON_PIN(GPIOH_4), - MESON_PIN(GPIOH_5), - MESON_PIN(GPIOH_6), - MESON_PIN(GPIOH_7), - MESON_PIN(GPIOH_8), - MESON_PIN(GPIOH_9), - MESON_PIN(GPIOZ_0), - MESON_PIN(GPIOZ_1), - MESON_PIN(GPIOZ_2), - MESON_PIN(GPIOZ_3), - MESON_PIN(GPIOZ_4), - MESON_PIN(GPIOZ_5), - MESON_PIN(GPIOZ_6), - MESON_PIN(GPIOZ_7), - MESON_PIN(GPIOZ_8), - MESON_PIN(GPIOZ_9), - MESON_PIN(GPIOZ_10), - MESON_PIN(GPIOZ_11), - MESON_PIN(GPIOZ_12), - MESON_PIN(GPIOZ_13), - MESON_PIN(GPIOZ_14), - MESON_PIN(CARD_0), - MESON_PIN(CARD_1), - MESON_PIN(CARD_2), - MESON_PIN(CARD_3), - MESON_PIN(CARD_4), - MESON_PIN(CARD_5), - MESON_PIN(CARD_6), - MESON_PIN(BOOT_0), - MESON_PIN(BOOT_1), - MESON_PIN(BOOT_2), - MESON_PIN(BOOT_3), - MESON_PIN(BOOT_4), - MESON_PIN(BOOT_5), - MESON_PIN(BOOT_6), - MESON_PIN(BOOT_7), - MESON_PIN(BOOT_8), - MESON_PIN(BOOT_9), - MESON_PIN(BOOT_10), - MESON_PIN(BOOT_11), - MESON_PIN(BOOT_12), - MESON_PIN(BOOT_13), - MESON_PIN(BOOT_14), - MESON_PIN(BOOT_15), - MESON_PIN(BOOT_16), - MESON_PIN(BOOT_17), - MESON_PIN(BOOT_18), - MESON_PIN(GPIOAO_0), - MESON_PIN(GPIOAO_1), - MESON_PIN(GPIOAO_2), - MESON_PIN(GPIOAO_3), - MESON_PIN(GPIOAO_4), - MESON_PIN(GPIOAO_5), - MESON_PIN(GPIOAO_6), - MESON_PIN(GPIOAO_7), - MESON_PIN(GPIOAO_8), - MESON_PIN(GPIOAO_9), - MESON_PIN(GPIOAO_10), - MESON_PIN(GPIOAO_11), - MESON_PIN(GPIOAO_12), - MESON_PIN(GPIOAO_13), - MESON_PIN(GPIO_BSD_EN), - MESON_PIN(GPIO_TEST_N), + MESON_PIN(GPIOX_0, 0), + MESON_PIN(GPIOX_1, 0), + MESON_PIN(GPIOX_2, 0), + MESON_PIN(GPIOX_3, 0), + MESON_PIN(GPIOX_4, 0), + MESON_PIN(GPIOX_5, 0), + MESON_PIN(GPIOX_6, 0), + MESON_PIN(GPIOX_7, 0), + MESON_PIN(GPIOX_8, 0), + MESON_PIN(GPIOX_9, 0), + MESON_PIN(GPIOX_10, 0), + MESON_PIN(GPIOX_11, 0), + MESON_PIN(GPIOX_12, 0), + MESON_PIN(GPIOX_13, 0), + MESON_PIN(GPIOX_14, 0), + MESON_PIN(GPIOX_15, 0), + MESON_PIN(GPIOX_16, 0), + MESON_PIN(GPIOX_17, 0), + MESON_PIN(GPIOX_18, 0), + MESON_PIN(GPIOX_19, 0), + MESON_PIN(GPIOX_20, 0), + MESON_PIN(GPIOX_21, 0), + MESON_PIN(GPIOY_0, 0), + MESON_PIN(GPIOY_1, 0), + MESON_PIN(GPIOY_2, 0), + MESON_PIN(GPIOY_3, 0), + MESON_PIN(GPIOY_4, 0), + MESON_PIN(GPIOY_5, 0), + MESON_PIN(GPIOY_6, 0), + MESON_PIN(GPIOY_7, 0), + MESON_PIN(GPIOY_8, 0), + MESON_PIN(GPIOY_9, 0), + MESON_PIN(GPIOY_10, 0), + MESON_PIN(GPIOY_11, 0), + MESON_PIN(GPIOY_12, 0), + MESON_PIN(GPIOY_13, 0), + MESON_PIN(GPIOY_14, 0), + MESON_PIN(GPIOY_15, 0), + MESON_PIN(GPIOY_16, 0), + MESON_PIN(GPIODV_0, 0), + MESON_PIN(GPIODV_1, 0), + MESON_PIN(GPIODV_2, 0), + MESON_PIN(GPIODV_3, 0), + MESON_PIN(GPIODV_4, 0), + MESON_PIN(GPIODV_5, 0), + MESON_PIN(GPIODV_6, 0), + MESON_PIN(GPIODV_7, 0), + MESON_PIN(GPIODV_8, 0), + MESON_PIN(GPIODV_9, 0), + MESON_PIN(GPIODV_10, 0), + MESON_PIN(GPIODV_11, 0), + MESON_PIN(GPIODV_12, 0), + MESON_PIN(GPIODV_13, 0), + MESON_PIN(GPIODV_14, 0), + MESON_PIN(GPIODV_15, 0), + MESON_PIN(GPIODV_16, 0), + MESON_PIN(GPIODV_17, 0), + MESON_PIN(GPIODV_18, 0), + MESON_PIN(GPIODV_19, 0), + MESON_PIN(GPIODV_20, 0), + MESON_PIN(GPIODV_21, 0), + MESON_PIN(GPIODV_22, 0), + MESON_PIN(GPIODV_23, 0), + MESON_PIN(GPIODV_24, 0), + MESON_PIN(GPIODV_25, 0), + MESON_PIN(GPIODV_26, 0), + MESON_PIN(GPIODV_27, 0), + MESON_PIN(GPIODV_28, 0), + MESON_PIN(GPIODV_29, 0), + MESON_PIN(GPIOH_0, 0), + MESON_PIN(GPIOH_1, 0), + MESON_PIN(GPIOH_2, 0), + MESON_PIN(GPIOH_3, 0), + MESON_PIN(GPIOH_4, 0), + MESON_PIN(GPIOH_5, 0), + MESON_PIN(GPIOH_6, 0), + MESON_PIN(GPIOH_7, 0), + MESON_PIN(GPIOH_8, 0), + MESON_PIN(GPIOH_9, 0), + MESON_PIN(GPIOZ_0, 0), + MESON_PIN(GPIOZ_1, 0), + MESON_PIN(GPIOZ_2, 0), + MESON_PIN(GPIOZ_3, 0), + MESON_PIN(GPIOZ_4, 0), + MESON_PIN(GPIOZ_5, 0), + MESON_PIN(GPIOZ_6, 0), + MESON_PIN(GPIOZ_7, 0), + MESON_PIN(GPIOZ_8, 0), + MESON_PIN(GPIOZ_9, 0), + MESON_PIN(GPIOZ_10, 0), + MESON_PIN(GPIOZ_11, 0), + MESON_PIN(GPIOZ_12, 0), + MESON_PIN(GPIOZ_13, 0), + MESON_PIN(GPIOZ_14, 0), + MESON_PIN(CARD_0, 0), + MESON_PIN(CARD_1, 0), + MESON_PIN(CARD_2, 0), + MESON_PIN(CARD_3, 0), + MESON_PIN(CARD_4, 0), + MESON_PIN(CARD_5, 0), + MESON_PIN(CARD_6, 0), + MESON_PIN(BOOT_0, 0), + MESON_PIN(BOOT_1, 0), + MESON_PIN(BOOT_2, 0), + MESON_PIN(BOOT_3, 0), + MESON_PIN(BOOT_4, 0), + MESON_PIN(BOOT_5, 0), + MESON_PIN(BOOT_6, 0), + MESON_PIN(BOOT_7, 0), + MESON_PIN(BOOT_8, 0), + MESON_PIN(BOOT_9, 0), + MESON_PIN(BOOT_10, 0), + MESON_PIN(BOOT_11, 0), + MESON_PIN(BOOT_12, 0), + MESON_PIN(BOOT_13, 0), + MESON_PIN(BOOT_14, 0), + MESON_PIN(BOOT_15, 0), + MESON_PIN(BOOT_16, 0), + MESON_PIN(BOOT_17, 0), + MESON_PIN(BOOT_18, 0), + MESON_PIN(GPIOAO_0, AO_OFF), + MESON_PIN(GPIOAO_1, AO_OFF), + MESON_PIN(GPIOAO_2, AO_OFF), + MESON_PIN(GPIOAO_3, AO_OFF), + MESON_PIN(GPIOAO_4, AO_OFF), + MESON_PIN(GPIOAO_5, AO_OFF), + MESON_PIN(GPIOAO_6, AO_OFF), + MESON_PIN(GPIOAO_7, AO_OFF), + MESON_PIN(GPIOAO_8, AO_OFF), + MESON_PIN(GPIOAO_9, AO_OFF), + MESON_PIN(GPIOAO_10, AO_OFF), + MESON_PIN(GPIOAO_11, AO_OFF), + MESON_PIN(GPIOAO_12, AO_OFF), + MESON_PIN(GPIOAO_13, AO_OFF), + MESON_PIN(GPIO_BSD_EN, AO_OFF), + MESON_PIN(GPIO_TEST_N, AO_OFF), }; /* bank X */ -static const unsigned int sd_d0_a_pins[] = { PIN_GPIOX_0 }; -static const unsigned int sd_d1_a_pins[] = { PIN_GPIOX_1 }; -static const unsigned int sd_d2_a_pins[] = { PIN_GPIOX_2 }; -static const unsigned int sd_d3_a_pins[] = { PIN_GPIOX_3 }; -static const unsigned int sd_clk_a_pins[] = { PIN_GPIOX_8 }; -static const unsigned int sd_cmd_a_pins[] = { PIN_GPIOX_9 }; - -static const unsigned int sdxc_d0_a_pins[] = { PIN_GPIOX_0 }; -static const unsigned int sdxc_d13_a_pins[] = { PIN_GPIOX_1, PIN_GPIOX_2, - PIN_GPIOX_3 }; -static const unsigned int sdxc_d47_a_pins[] = { PIN_GPIOX_4, PIN_GPIOX_5, - PIN_GPIOX_6, PIN_GPIOX_7 }; -static const unsigned int sdxc_clk_a_pins[] = { PIN_GPIOX_8 }; -static const unsigned int sdxc_cmd_a_pins[] = { PIN_GPIOX_9 }; - -static const unsigned int pcm_out_a_pins[] = { PIN_GPIOX_4 }; -static const unsigned int pcm_in_a_pins[] = { PIN_GPIOX_5 }; -static const unsigned int pcm_fs_a_pins[] = { PIN_GPIOX_6 }; -static const unsigned int pcm_clk_a_pins[] = { PIN_GPIOX_7 }; - -static const unsigned int uart_tx_a0_pins[] = { PIN_GPIOX_4 }; -static const unsigned int uart_rx_a0_pins[] = { PIN_GPIOX_5 }; -static const unsigned int uart_cts_a0_pins[] = { PIN_GPIOX_6 }; -static const unsigned int uart_rts_a0_pins[] = { PIN_GPIOX_7 }; - -static const unsigned int uart_tx_a1_pins[] = { PIN_GPIOX_12 }; -static const unsigned int uart_rx_a1_pins[] = { PIN_GPIOX_13 }; -static const unsigned int uart_cts_a1_pins[] = { PIN_GPIOX_14 }; -static const unsigned int uart_rts_a1_pins[] = { PIN_GPIOX_15 }; - -static const unsigned int uart_tx_b0_pins[] = { PIN_GPIOX_16 }; -static const unsigned int uart_rx_b0_pins[] = { PIN_GPIOX_17 }; -static const unsigned int uart_cts_b0_pins[] = { PIN_GPIOX_18 }; -static const unsigned int uart_rts_b0_pins[] = { PIN_GPIOX_19 }; - -static const unsigned int iso7816_det_pins[] = { PIN_GPIOX_16 }; -static const unsigned int iso7816_reset_pins[] = { PIN_GPIOX_17 }; -static const unsigned int iso7816_clk_pins[] = { PIN_GPIOX_18 }; -static const unsigned int iso7816_data_pins[] = { PIN_GPIOX_19 }; - -static const unsigned int i2c_sda_d0_pins[] = { PIN_GPIOX_16 }; -static const unsigned int i2c_sck_d0_pins[] = { PIN_GPIOX_17 }; - -static const unsigned int xtal_32k_out_pins[] = { PIN_GPIOX_10 }; -static const unsigned int xtal_24m_out_pins[] = { PIN_GPIOX_11 }; +static const unsigned int sd_d0_a_pins[] = { PIN(GPIOX_0, 0) }; +static const unsigned int sd_d1_a_pins[] = { PIN(GPIOX_1, 0) }; +static const unsigned int sd_d2_a_pins[] = { PIN(GPIOX_2, 0) }; +static const unsigned int sd_d3_a_pins[] = { PIN(GPIOX_3, 0) }; +static const unsigned int sd_clk_a_pins[] = { PIN(GPIOX_8, 0) }; +static const unsigned int sd_cmd_a_pins[] = { PIN(GPIOX_9, 0) }; + +static const unsigned int sdxc_d0_a_pins[] = { PIN(GPIOX_0, 0) }; +static const unsigned int sdxc_d13_a_pins[] = { PIN(GPIOX_1, 0), PIN(GPIOX_2, 0), + PIN(GPIOX_3, 0) }; +static const unsigned int sdxc_d47_a_pins[] = { PIN(GPIOX_4, 0), PIN(GPIOX_5, 0), + PIN(GPIOX_6, 0), PIN(GPIOX_7, 0) }; +static const unsigned int sdxc_clk_a_pins[] = { PIN(GPIOX_8, 0) }; +static const unsigned int sdxc_cmd_a_pins[] = { PIN(GPIOX_9, 0) }; + +static const unsigned int pcm_out_a_pins[] = { PIN(GPIOX_4, 0) }; +static const unsigned int pcm_in_a_pins[] = { PIN(GPIOX_5, 0) }; +static const unsigned int pcm_fs_a_pins[] = { PIN(GPIOX_6, 0) }; +static const unsigned int pcm_clk_a_pins[] = { PIN(GPIOX_7, 0) }; + +static const unsigned int uart_tx_a0_pins[] = { PIN(GPIOX_4, 0) }; +static const unsigned int uart_rx_a0_pins[] = { PIN(GPIOX_5, 0) }; +static const unsigned int uart_cts_a0_pins[] = { PIN(GPIOX_6, 0) }; +static const unsigned int uart_rts_a0_pins[] = { PIN(GPIOX_7, 0) }; + +static const unsigned int uart_tx_a1_pins[] = { PIN(GPIOX_12, 0) }; +static const unsigned int uart_rx_a1_pins[] = { PIN(GPIOX_13, 0) }; +static const unsigned int uart_cts_a1_pins[] = { PIN(GPIOX_14, 0) }; +static const unsigned int uart_rts_a1_pins[] = { PIN(GPIOX_15, 0) }; + +static const unsigned int uart_tx_b0_pins[] = { PIN(GPIOX_16, 0) }; +static const unsigned int uart_rx_b0_pins[] = { PIN(GPIOX_17, 0) }; +static const unsigned int uart_cts_b0_pins[] = { PIN(GPIOX_18, 0) }; +static const unsigned int uart_rts_b0_pins[] = { PIN(GPIOX_19, 0) }; + +static const unsigned int iso7816_det_pins[] = { PIN(GPIOX_16, 0) }; +static const unsigned int iso7816_reset_pins[] = { PIN(GPIOX_17, 0) }; +static const unsigned int iso7816_clk_pins[] = { PIN(GPIOX_18, 0) }; +static const unsigned int iso7816_data_pins[] = { PIN(GPIOX_19, 0) }; + +static const unsigned int i2c_sda_d0_pins[] = { PIN(GPIOX_16, 0) }; +static const unsigned int i2c_sck_d0_pins[] = { PIN(GPIOX_17, 0) }; + +static const unsigned int xtal_32k_out_pins[] = { PIN(GPIOX_10, 0) }; +static const unsigned int xtal_24m_out_pins[] = { PIN(GPIOX_11, 0) }; /* bank Y */ -static const unsigned int uart_tx_c_pins[] = { PIN_GPIOY_0 }; -static const unsigned int uart_rx_c_pins[] = { PIN_GPIOY_1 }; -static const unsigned int uart_cts_c_pins[] = { PIN_GPIOY_2 }; -static const unsigned int uart_rts_c_pins[] = { PIN_GPIOY_3 }; +static const unsigned int uart_tx_c_pins[] = { PIN(GPIOY_0, 0) }; +static const unsigned int uart_rx_c_pins[] = { PIN(GPIOY_1, 0) }; +static const unsigned int uart_cts_c_pins[] = { PIN(GPIOY_2, 0) }; +static const unsigned int uart_rts_c_pins[] = { PIN(GPIOY_3, 0) }; -static const unsigned int pcm_out_b_pins[] = { PIN_GPIOY_4 }; -static const unsigned int pcm_in_b_pins[] = { PIN_GPIOY_5 }; -static const unsigned int pcm_fs_b_pins[] = { PIN_GPIOY_6 }; -static const unsigned int pcm_clk_b_pins[] = { PIN_GPIOY_7 }; +static const unsigned int pcm_out_b_pins[] = { PIN(GPIOY_4, 0) }; +static const unsigned int pcm_in_b_pins[] = { PIN(GPIOY_5, 0) }; +static const unsigned int pcm_fs_b_pins[] = { PIN(GPIOY_6, 0) }; +static const unsigned int pcm_clk_b_pins[] = { PIN(GPIOY_7, 0) }; -static const unsigned int i2c_sda_c0_pins[] = { PIN_GPIOY_0 }; -static const unsigned int i2c_sck_c0_pins[] = { PIN_GPIOY_1 }; +static const unsigned int i2c_sda_c0_pins[] = { PIN(GPIOY_0, 0) }; +static const unsigned int i2c_sck_c0_pins[] = { PIN(GPIOY_1, 0) }; /* bank DV */ -static const unsigned int dvin_rgb_pins[] = { PIN_GPIODV_0, PIN_GPIODV_1, - PIN_GPIODV_2, PIN_GPIODV_3, - PIN_GPIODV_4, PIN_GPIODV_5, - PIN_GPIODV_6, PIN_GPIODV_7, - PIN_GPIODV_8, PIN_GPIODV_9, - PIN_GPIODV_10, PIN_GPIODV_11, - PIN_GPIODV_12, PIN_GPIODV_13, - PIN_GPIODV_14, PIN_GPIODV_15, - PIN_GPIODV_16, PIN_GPIODV_17, - PIN_GPIODV_18, PIN_GPIODV_19, - PIN_GPIODV_20, PIN_GPIODV_21, - PIN_GPIODV_22, PIN_GPIODV_23 }; -static const unsigned int dvin_vs_pins[] = { PIN_GPIODV_24 }; -static const unsigned int dvin_hs_pins[] = { PIN_GPIODV_25 }; -static const unsigned int dvin_clk_pins[] = { PIN_GPIODV_26 }; -static const unsigned int dvin_de_pins[] = { PIN_GPIODV_27 }; - -static const unsigned int enc_0_pins[] = { PIN_GPIODV_0 }; -static const unsigned int enc_1_pins[] = { PIN_GPIODV_1 }; -static const unsigned int enc_2_pins[] = { PIN_GPIODV_2 }; -static const unsigned int enc_3_pins[] = { PIN_GPIODV_3 }; -static const unsigned int enc_4_pins[] = { PIN_GPIODV_4 }; -static const unsigned int enc_5_pins[] = { PIN_GPIODV_5 }; -static const unsigned int enc_6_pins[] = { PIN_GPIODV_6 }; -static const unsigned int enc_7_pins[] = { PIN_GPIODV_7 }; -static const unsigned int enc_8_pins[] = { PIN_GPIODV_8 }; -static const unsigned int enc_9_pins[] = { PIN_GPIODV_9 }; -static const unsigned int enc_10_pins[] = { PIN_GPIODV_10 }; -static const unsigned int enc_11_pins[] = { PIN_GPIODV_11 }; -static const unsigned int enc_12_pins[] = { PIN_GPIODV_12 }; -static const unsigned int enc_13_pins[] = { PIN_GPIODV_13 }; -static const unsigned int enc_14_pins[] = { PIN_GPIODV_14 }; -static const unsigned int enc_15_pins[] = { PIN_GPIODV_15 }; -static const unsigned int enc_16_pins[] = { PIN_GPIODV_16 }; -static const unsigned int enc_17_pins[] = { PIN_GPIODV_17 }; - -static const unsigned int uart_tx_b1_pins[] = { PIN_GPIODV_24 }; -static const unsigned int uart_rx_b1_pins[] = { PIN_GPIODV_25 }; -static const unsigned int uart_cts_b1_pins[] = { PIN_GPIODV_26 }; -static const unsigned int uart_rts_b1_pins[] = { PIN_GPIODV_27 }; - -static const unsigned int vga_vs_pins[] = { PIN_GPIODV_24 }; -static const unsigned int vga_hs_pins[] = { PIN_GPIODV_25 }; +static const unsigned int dvin_rgb_pins[] = { PIN(GPIODV_0, 0), PIN(GPIODV_1, 0), + PIN(GPIODV_2, 0), PIN(GPIODV_3, 0), + PIN(GPIODV_4, 0), PIN(GPIODV_5, 0), + PIN(GPIODV_6, 0), PIN(GPIODV_7, 0), + PIN(GPIODV_8, 0), PIN(GPIODV_9, 0), + PIN(GPIODV_10, 0), PIN(GPIODV_11, 0), + PIN(GPIODV_12, 0), PIN(GPIODV_13, 0), + PIN(GPIODV_14, 0), PIN(GPIODV_15, 0), + PIN(GPIODV_16, 0), PIN(GPIODV_17, 0), + PIN(GPIODV_18, 0), PIN(GPIODV_19, 0), + PIN(GPIODV_20, 0), PIN(GPIODV_21, 0), + PIN(GPIODV_22, 0), PIN(GPIODV_23, 0) }; +static const unsigned int dvin_vs_pins[] = { PIN(GPIODV_24, 0) }; +static const unsigned int dvin_hs_pins[] = { PIN(GPIODV_25, 0) }; +static const unsigned int dvin_clk_pins[] = { PIN(GPIODV_26, 0) }; +static const unsigned int dvin_de_pins[] = { PIN(GPIODV_27, 0) }; + +static const unsigned int enc_0_pins[] = { PIN(GPIODV_0, 0) }; +static const unsigned int enc_1_pins[] = { PIN(GPIODV_1, 0) }; +static const unsigned int enc_2_pins[] = { PIN(GPIODV_2, 0) }; +static const unsigned int enc_3_pins[] = { PIN(GPIODV_3, 0) }; +static const unsigned int enc_4_pins[] = { PIN(GPIODV_4, 0) }; +static const unsigned int enc_5_pins[] = { PIN(GPIODV_5, 0) }; +static const unsigned int enc_6_pins[] = { PIN(GPIODV_6, 0) }; +static const unsigned int enc_7_pins[] = { PIN(GPIODV_7, 0) }; +static const unsigned int enc_8_pins[] = { PIN(GPIODV_8, 0) }; +static const unsigned int enc_9_pins[] = { PIN(GPIODV_9, 0) }; +static const unsigned int enc_10_pins[] = { PIN(GPIODV_10, 0) }; +static const unsigned int enc_11_pins[] = { PIN(GPIODV_11, 0) }; +static const unsigned int enc_12_pins[] = { PIN(GPIODV_12, 0) }; +static const unsigned int enc_13_pins[] = { PIN(GPIODV_13, 0) }; +static const unsigned int enc_14_pins[] = { PIN(GPIODV_14, 0) }; +static const unsigned int enc_15_pins[] = { PIN(GPIODV_15, 0) }; +static const unsigned int enc_16_pins[] = { PIN(GPIODV_16, 0) }; +static const unsigned int enc_17_pins[] = { PIN(GPIODV_17, 0) }; + +static const unsigned int uart_tx_b1_pins[] = { PIN(GPIODV_24, 0) }; +static const unsigned int uart_rx_b1_pins[] = { PIN(GPIODV_25, 0) }; +static const unsigned int uart_cts_b1_pins[] = { PIN(GPIODV_26, 0) }; +static const unsigned int uart_rts_b1_pins[] = { PIN(GPIODV_27, 0) }; + +static const unsigned int vga_vs_pins[] = { PIN(GPIODV_24, 0) }; +static const unsigned int vga_hs_pins[] = { PIN(GPIODV_25, 0) }; /* bank H */ -static const unsigned int hdmi_hpd_pins[] = { PIN_GPIOH_0 }; -static const unsigned int hdmi_sda_pins[] = { PIN_GPIOH_1 }; -static const unsigned int hdmi_scl_pins[] = { PIN_GPIOH_2 }; -static const unsigned int hdmi_cec_pins[] = { PIN_GPIOH_3 }; +static const unsigned int hdmi_hpd_pins[] = { PIN(GPIOH_0, 0) }; +static const unsigned int hdmi_sda_pins[] = { PIN(GPIOH_1, 0) }; +static const unsigned int hdmi_scl_pins[] = { PIN(GPIOH_2, 0) }; +static const unsigned int hdmi_cec_pins[] = { PIN(GPIOH_3, 0) }; -static const unsigned int spi_ss0_0_pins[] = { PIN_GPIOH_3 }; -static const unsigned int spi_miso_0_pins[] = { PIN_GPIOH_4 }; -static const unsigned int spi_mosi_0_pins[] = { PIN_GPIOH_5 }; -static const unsigned int spi_sclk_0_pins[] = { PIN_GPIOH_6 }; +static const unsigned int spi_ss0_0_pins[] = { PIN(GPIOH_3, 0) }; +static const unsigned int spi_miso_0_pins[] = { PIN(GPIOH_4, 0) }; +static const unsigned int spi_mosi_0_pins[] = { PIN(GPIOH_5, 0) }; +static const unsigned int spi_sclk_0_pins[] = { PIN(GPIOH_6, 0) }; -static const unsigned int i2c_sda_d1_pins[] = { PIN_GPIOH_7 }; -static const unsigned int i2c_sck_d1_pins[] = { PIN_GPIOH_8 }; +static const unsigned int i2c_sda_d1_pins[] = { PIN(GPIOH_7, 0) }; +static const unsigned int i2c_sck_d1_pins[] = { PIN(GPIOH_8, 0) }; /* bank Z */ -static const unsigned int spi_ss0_1_pins[] = { PIN_GPIOZ_9 }; -static const unsigned int spi_ss1_1_pins[] = { PIN_GPIOZ_10 }; -static const unsigned int spi_sclk_1_pins[] = { PIN_GPIOZ_11 }; -static const unsigned int spi_mosi_1_pins[] = { PIN_GPIOZ_12 }; -static const unsigned int spi_miso_1_pins[] = { PIN_GPIOZ_13 }; -static const unsigned int spi_ss2_1_pins[] = { PIN_GPIOZ_14 }; - -static const unsigned int eth_tx_clk_50m_pins[] = { PIN_GPIOZ_4 }; -static const unsigned int eth_tx_en_pins[] = { PIN_GPIOZ_5 }; -static const unsigned int eth_txd1_pins[] = { PIN_GPIOZ_6 }; -static const unsigned int eth_txd0_pins[] = { PIN_GPIOZ_7 }; -static const unsigned int eth_rx_clk_in_pins[] = { PIN_GPIOZ_8 }; -static const unsigned int eth_rx_dv_pins[] = { PIN_GPIOZ_9 }; -static const unsigned int eth_rxd1_pins[] = { PIN_GPIOZ_10 }; -static const unsigned int eth_rxd0_pins[] = { PIN_GPIOZ_11 }; -static const unsigned int eth_mdio_pins[] = { PIN_GPIOZ_12 }; -static const unsigned int eth_mdc_pins[] = { PIN_GPIOZ_13 }; - -static const unsigned int i2c_sda_a0_pins[] = { PIN_GPIOZ_0 }; -static const unsigned int i2c_sck_a0_pins[] = { PIN_GPIOZ_1 }; - -static const unsigned int i2c_sda_b_pins[] = { PIN_GPIOZ_2 }; -static const unsigned int i2c_sck_b_pins[] = { PIN_GPIOZ_3 }; - -static const unsigned int i2c_sda_c1_pins[] = { PIN_GPIOZ_4 }; -static const unsigned int i2c_sck_c1_pins[] = { PIN_GPIOZ_5 }; - -static const unsigned int i2c_sda_a1_pins[] = { PIN_GPIOZ_0 }; -static const unsigned int i2c_sck_a1_pins[] = { PIN_GPIOZ_1 }; - -static const unsigned int i2c_sda_a2_pins[] = { PIN_GPIOZ_0 }; -static const unsigned int i2c_sck_a2_pins[] = { PIN_GPIOZ_1 }; +static const unsigned int spi_ss0_1_pins[] = { PIN(GPIOZ_9, 0) }; +static const unsigned int spi_ss1_1_pins[] = { PIN(GPIOZ_10, 0) }; +static const unsigned int spi_sclk_1_pins[] = { PIN(GPIOZ_11, 0) }; +static const unsigned int spi_mosi_1_pins[] = { PIN(GPIOZ_12, 0) }; +static const unsigned int spi_miso_1_pins[] = { PIN(GPIOZ_13, 0) }; +static const unsigned int spi_ss2_1_pins[] = { PIN(GPIOZ_14, 0) }; + +static const unsigned int eth_tx_clk_50m_pins[] = { PIN(GPIOZ_4, 0) }; +static const unsigned int eth_tx_en_pins[] = { PIN(GPIOZ_5, 0) }; +static const unsigned int eth_txd1_pins[] = { PIN(GPIOZ_6, 0) }; +static const unsigned int eth_txd0_pins[] = { PIN(GPIOZ_7, 0) }; +static const unsigned int eth_rx_clk_in_pins[] = { PIN(GPIOZ_8, 0) }; +static const unsigned int eth_rx_dv_pins[] = { PIN(GPIOZ_9, 0) }; +static const unsigned int eth_rxd1_pins[] = { PIN(GPIOZ_10, 0) }; +static const unsigned int eth_rxd0_pins[] = { PIN(GPIOZ_11, 0) }; +static const unsigned int eth_mdio_pins[] = { PIN(GPIOZ_12, 0) }; +static const unsigned int eth_mdc_pins[] = { PIN(GPIOZ_13, 0) }; + +static const unsigned int i2c_sda_a0_pins[] = { PIN(GPIOZ_0, 0) }; +static const unsigned int i2c_sck_a0_pins[] = { PIN(GPIOZ_1, 0) }; + +static const unsigned int i2c_sda_b_pins[] = { PIN(GPIOZ_2, 0) }; +static const unsigned int i2c_sck_b_pins[] = { PIN(GPIOZ_3, 0) }; + +static const unsigned int i2c_sda_c1_pins[] = { PIN(GPIOZ_4, 0) }; +static const unsigned int i2c_sck_c1_pins[] = { PIN(GPIOZ_5, 0) }; + +static const unsigned int i2c_sda_a1_pins[] = { PIN(GPIOZ_0, 0) }; +static const unsigned int i2c_sck_a1_pins[] = { PIN(GPIOZ_1, 0) }; + +static const unsigned int i2c_sda_a2_pins[] = { PIN(GPIOZ_0, 0) }; +static const unsigned int i2c_sck_a2_pins[] = { PIN(GPIOZ_1, 0) }; /* bank BOOT */ -static const unsigned int sd_d0_c_pins[] = { PIN_BOOT_0 }; -static const unsigned int sd_d1_c_pins[] = { PIN_BOOT_1 }; -static const unsigned int sd_d2_c_pins[] = { PIN_BOOT_2 }; -static const unsigned int sd_d3_c_pins[] = { PIN_BOOT_3 }; -static const unsigned int sd_cmd_c_pins[] = { PIN_BOOT_16 }; -static const unsigned int sd_clk_c_pins[] = { PIN_BOOT_17 }; - -static const unsigned int sdxc_d0_c_pins[] = { PIN_BOOT_0}; -static const unsigned int sdxc_d13_c_pins[] = { PIN_BOOT_1, PIN_BOOT_2, - PIN_BOOT_3 }; -static const unsigned int sdxc_d47_c_pins[] = { PIN_BOOT_4, PIN_BOOT_5, - PIN_BOOT_6, PIN_BOOT_7 }; -static const unsigned int sdxc_cmd_c_pins[] = { PIN_BOOT_16 }; -static const unsigned int sdxc_clk_c_pins[] = { PIN_BOOT_17 }; - -static const unsigned int nand_io_pins[] = { PIN_BOOT_0, PIN_BOOT_1, - PIN_BOOT_2, PIN_BOOT_3, - PIN_BOOT_4, PIN_BOOT_5, - PIN_BOOT_6, PIN_BOOT_7 }; -static const unsigned int nand_io_ce0_pins[] = { PIN_BOOT_8 }; -static const unsigned int nand_io_ce1_pins[] = { PIN_BOOT_9 }; -static const unsigned int nand_io_rb0_pins[] = { PIN_BOOT_10 }; -static const unsigned int nand_ale_pins[] = { PIN_BOOT_11 }; -static const unsigned int nand_cle_pins[] = { PIN_BOOT_12 }; -static const unsigned int nand_wen_clk_pins[] = { PIN_BOOT_13 }; -static const unsigned int nand_ren_clk_pins[] = { PIN_BOOT_14 }; -static const unsigned int nand_dqs_pins[] = { PIN_BOOT_15 }; -static const unsigned int nand_ce2_pins[] = { PIN_BOOT_16 }; -static const unsigned int nand_ce3_pins[] = { PIN_BOOT_17 }; - -static const unsigned int nor_d_pins[] = { PIN_BOOT_11 }; -static const unsigned int nor_q_pins[] = { PIN_BOOT_12 }; -static const unsigned int nor_c_pins[] = { PIN_BOOT_13 }; -static const unsigned int nor_cs_pins[] = { PIN_BOOT_18 }; +static const unsigned int sd_d0_c_pins[] = { PIN(BOOT_0, 0) }; +static const unsigned int sd_d1_c_pins[] = { PIN(BOOT_1, 0) }; +static const unsigned int sd_d2_c_pins[] = { PIN(BOOT_2, 0) }; +static const unsigned int sd_d3_c_pins[] = { PIN(BOOT_3, 0) }; +static const unsigned int sd_cmd_c_pins[] = { PIN(BOOT_16, 0) }; +static const unsigned int sd_clk_c_pins[] = { PIN(BOOT_17, 0) }; + +static const unsigned int sdxc_d0_c_pins[] = { PIN(BOOT_0, 0)}; +static const unsigned int sdxc_d13_c_pins[] = { PIN(BOOT_1, 0), PIN(BOOT_2, 0), + PIN(BOOT_3, 0) }; +static const unsigned int sdxc_d47_c_pins[] = { PIN(BOOT_4, 0), PIN(BOOT_5, 0), + PIN(BOOT_6, 0), PIN(BOOT_7, 0) }; +static const unsigned int sdxc_cmd_c_pins[] = { PIN(BOOT_16, 0) }; +static const unsigned int sdxc_clk_c_pins[] = { PIN(BOOT_17, 0) }; + +static const unsigned int nand_io_pins[] = { PIN(BOOT_0, 0), PIN(BOOT_1, 0), + PIN(BOOT_2, 0), PIN(BOOT_3, 0), + PIN(BOOT_4, 0), PIN(BOOT_5, 0), + PIN(BOOT_6, 0), PIN(BOOT_7, 0) }; +static const unsigned int nand_io_ce0_pins[] = { PIN(BOOT_8, 0) }; +static const unsigned int nand_io_ce1_pins[] = { PIN(BOOT_9, 0) }; +static const unsigned int nand_io_rb0_pins[] = { PIN(BOOT_10, 0) }; +static const unsigned int nand_ale_pins[] = { PIN(BOOT_11, 0) }; +static const unsigned int nand_cle_pins[] = { PIN(BOOT_12, 0) }; +static const unsigned int nand_wen_clk_pins[] = { PIN(BOOT_13, 0) }; +static const unsigned int nand_ren_clk_pins[] = { PIN(BOOT_14, 0) }; +static const unsigned int nand_dqs_pins[] = { PIN(BOOT_15, 0) }; +static const unsigned int nand_ce2_pins[] = { PIN(BOOT_16, 0) }; +static const unsigned int nand_ce3_pins[] = { PIN(BOOT_17, 0) }; + +static const unsigned int nor_d_pins[] = { PIN(BOOT_11, 0) }; +static const unsigned int nor_q_pins[] = { PIN(BOOT_12, 0) }; +static const unsigned int nor_c_pins[] = { PIN(BOOT_13, 0) }; +static const unsigned int nor_cs_pins[] = { PIN(BOOT_18, 0) }; /* bank CARD */ -static const unsigned int sd_d1_b_pins[] = { PIN_CARD_0 }; -static const unsigned int sd_d0_b_pins[] = { PIN_CARD_1 }; -static const unsigned int sd_clk_b_pins[] = { PIN_CARD_2 }; -static const unsigned int sd_cmd_b_pins[] = { PIN_CARD_3 }; -static const unsigned int sd_d3_b_pins[] = { PIN_CARD_4 }; -static const unsigned int sd_d2_b_pins[] = { PIN_CARD_5 }; - -static const unsigned int sdxc_d13_b_pins[] = { PIN_CARD_0, PIN_CARD_4, - PIN_CARD_5 }; -static const unsigned int sdxc_d0_b_pins[] = { PIN_CARD_1 }; -static const unsigned int sdxc_clk_b_pins[] = { PIN_CARD_2 }; -static const unsigned int sdxc_cmd_b_pins[] = { PIN_CARD_3 }; +static const unsigned int sd_d1_b_pins[] = { PIN(CARD_0, 0) }; +static const unsigned int sd_d0_b_pins[] = { PIN(CARD_1, 0) }; +static const unsigned int sd_clk_b_pins[] = { PIN(CARD_2, 0) }; +static const unsigned int sd_cmd_b_pins[] = { PIN(CARD_3, 0) }; +static const unsigned int sd_d3_b_pins[] = { PIN(CARD_4, 0) }; +static const unsigned int sd_d2_b_pins[] = { PIN(CARD_5, 0) }; + +static const unsigned int sdxc_d13_b_pins[] = { PIN(CARD_0, 0), PIN(CARD_4, 0), + PIN(CARD_5, 0) }; +static const unsigned int sdxc_d0_b_pins[] = { PIN(CARD_1, 0) }; +static const unsigned int sdxc_clk_b_pins[] = { PIN(CARD_2, 0) }; +static const unsigned int sdxc_cmd_b_pins[] = { PIN(CARD_3, 0) }; /* bank AO */ -static const unsigned int uart_tx_ao_a_pins[] = { PIN_GPIOAO_0 }; -static const unsigned int uart_rx_ao_a_pins[] = { PIN_GPIOAO_1 }; -static const unsigned int uart_cts_ao_a_pins[] = { PIN_GPIOAO_2 }; -static const unsigned int uart_rts_ao_a_pins[] = { PIN_GPIOAO_3 }; +static const unsigned int uart_tx_ao_a_pins[] = { PIN(GPIOAO_0, AO_OFF) }; +static const unsigned int uart_rx_ao_a_pins[] = { PIN(GPIOAO_1, AO_OFF) }; +static const unsigned int uart_cts_ao_a_pins[] = { PIN(GPIOAO_2, AO_OFF) }; +static const unsigned int uart_rts_ao_a_pins[] = { PIN(GPIOAO_3, AO_OFF) }; -static const unsigned int remote_input_pins[] = { PIN_GPIOAO_7 }; +static const unsigned int remote_input_pins[] = { PIN(GPIOAO_7, AO_OFF) }; -static const unsigned int i2c_slave_sck_ao_pins[] = { PIN_GPIOAO_4 }; -static const unsigned int i2c_slave_sda_ao_pins[] = { PIN_GPIOAO_5 }; +static const unsigned int i2c_slave_sck_ao_pins[] = { PIN(GPIOAO_4, AO_OFF) }; +static const unsigned int i2c_slave_sda_ao_pins[] = { PIN(GPIOAO_5, AO_OFF) }; -static const unsigned int uart_tx_ao_b0_pins[] = { PIN_GPIOAO_0 }; -static const unsigned int uart_rx_ao_b0_pins[] = { PIN_GPIOAO_1 }; +static const unsigned int uart_tx_ao_b0_pins[] = { PIN(GPIOAO_0, AO_OFF) }; +static const unsigned int uart_rx_ao_b0_pins[] = { PIN(GPIOAO_1, AO_OFF) }; -static const unsigned int uart_tx_ao_b1_pins[] = { PIN_GPIOAO_4 }; -static const unsigned int uart_rx_ao_b1_pins[] = { PIN_GPIOAO_5 }; +static const unsigned int uart_tx_ao_b1_pins[] = { PIN(GPIOAO_4, AO_OFF) }; +static const unsigned int uart_rx_ao_b1_pins[] = { PIN(GPIOAO_5, AO_OFF) }; -static const unsigned int i2c_mst_sck_ao_pins[] = { PIN_GPIOAO_4 }; -static const unsigned int i2c_mst_sda_ao_pins[] = { PIN_GPIOAO_5 }; +static const unsigned int i2c_mst_sck_ao_pins[] = { PIN(GPIOAO_4, AO_OFF) }; +static const unsigned int i2c_mst_sda_ao_pins[] = { PIN(GPIOAO_5, AO_OFF) }; static struct meson_pmx_group meson8_groups[] = { - GPIO_GROUP(GPIOX_0), - GPIO_GROUP(GPIOX_1), - GPIO_GROUP(GPIOX_2), - GPIO_GROUP(GPIOX_3), - GPIO_GROUP(GPIOX_4), - GPIO_GROUP(GPIOX_5), - GPIO_GROUP(GPIOX_6), - GPIO_GROUP(GPIOX_7), - GPIO_GROUP(GPIOX_8), - GPIO_GROUP(GPIOX_9), - GPIO_GROUP(GPIOX_10), - GPIO_GROUP(GPIOX_11), - GPIO_GROUP(GPIOX_12), - GPIO_GROUP(GPIOX_13), - GPIO_GROUP(GPIOX_14), - GPIO_GROUP(GPIOX_15), - GPIO_GROUP(GPIOX_16), - GPIO_GROUP(GPIOX_17), - GPIO_GROUP(GPIOX_18), - GPIO_GROUP(GPIOX_19), - GPIO_GROUP(GPIOX_20), - GPIO_GROUP(GPIOX_21), - GPIO_GROUP(GPIOY_0), - GPIO_GROUP(GPIOY_1), - GPIO_GROUP(GPIOY_2), - GPIO_GROUP(GPIOY_3), - GPIO_GROUP(GPIOY_4), - GPIO_GROUP(GPIOY_5), - GPIO_GROUP(GPIOY_6), - GPIO_GROUP(GPIOY_7), - GPIO_GROUP(GPIOY_8), - GPIO_GROUP(GPIOY_9), - GPIO_GROUP(GPIOY_10), - GPIO_GROUP(GPIOY_11), - GPIO_GROUP(GPIOY_12), - GPIO_GROUP(GPIOY_13), - GPIO_GROUP(GPIOY_14), - GPIO_GROUP(GPIOY_15), - GPIO_GROUP(GPIOY_16), - GPIO_GROUP(GPIODV_0), - GPIO_GROUP(GPIODV_1), - GPIO_GROUP(GPIODV_2), - GPIO_GROUP(GPIODV_3), - GPIO_GROUP(GPIODV_4), - GPIO_GROUP(GPIODV_5), - GPIO_GROUP(GPIODV_6), - GPIO_GROUP(GPIODV_7), - GPIO_GROUP(GPIODV_8), - GPIO_GROUP(GPIODV_9), - GPIO_GROUP(GPIODV_10), - GPIO_GROUP(GPIODV_11), - GPIO_GROUP(GPIODV_12), - GPIO_GROUP(GPIODV_13), - GPIO_GROUP(GPIODV_14), - GPIO_GROUP(GPIODV_15), - GPIO_GROUP(GPIODV_16), - GPIO_GROUP(GPIODV_17), - GPIO_GROUP(GPIODV_18), - GPIO_GROUP(GPIODV_19), - GPIO_GROUP(GPIODV_20), - GPIO_GROUP(GPIODV_21), - GPIO_GROUP(GPIODV_22), - GPIO_GROUP(GPIODV_23), - GPIO_GROUP(GPIODV_24), - GPIO_GROUP(GPIODV_25), - GPIO_GROUP(GPIODV_26), - GPIO_GROUP(GPIODV_27), - GPIO_GROUP(GPIODV_28), - GPIO_GROUP(GPIODV_29), - GPIO_GROUP(GPIOH_0), - GPIO_GROUP(GPIOH_1), - GPIO_GROUP(GPIOH_2), - GPIO_GROUP(GPIOH_3), - GPIO_GROUP(GPIOH_4), - GPIO_GROUP(GPIOH_5), - GPIO_GROUP(GPIOH_6), - GPIO_GROUP(GPIOH_7), - GPIO_GROUP(GPIOH_8), - GPIO_GROUP(GPIOH_9), - GPIO_GROUP(GPIOZ_0), - GPIO_GROUP(GPIOZ_1), - GPIO_GROUP(GPIOZ_2), - GPIO_GROUP(GPIOZ_3), - GPIO_GROUP(GPIOZ_4), - GPIO_GROUP(GPIOZ_5), - GPIO_GROUP(GPIOZ_6), - GPIO_GROUP(GPIOZ_7), - GPIO_GROUP(GPIOZ_8), - GPIO_GROUP(GPIOZ_9), - GPIO_GROUP(GPIOZ_10), - GPIO_GROUP(GPIOZ_11), - GPIO_GROUP(GPIOZ_12), - GPIO_GROUP(GPIOZ_13), - GPIO_GROUP(GPIOZ_14), - GPIO_GROUP(GPIOAO_0), - GPIO_GROUP(GPIOAO_1), - GPIO_GROUP(GPIOAO_2), - GPIO_GROUP(GPIOAO_3), - GPIO_GROUP(GPIOAO_4), - GPIO_GROUP(GPIOAO_5), - GPIO_GROUP(GPIOAO_6), - GPIO_GROUP(GPIOAO_7), - GPIO_GROUP(GPIOAO_8), - GPIO_GROUP(GPIOAO_9), - GPIO_GROUP(GPIOAO_10), - GPIO_GROUP(GPIOAO_11), - GPIO_GROUP(GPIOAO_12), - GPIO_GROUP(GPIOAO_13), - GPIO_GROUP(GPIO_BSD_EN), - GPIO_GROUP(GPIO_TEST_N), + GPIO_GROUP(GPIOX_0, 0), + GPIO_GROUP(GPIOX_1, 0), + GPIO_GROUP(GPIOX_2, 0), + GPIO_GROUP(GPIOX_3, 0), + GPIO_GROUP(GPIOX_4, 0), + GPIO_GROUP(GPIOX_5, 0), + GPIO_GROUP(GPIOX_6, 0), + GPIO_GROUP(GPIOX_7, 0), + GPIO_GROUP(GPIOX_8, 0), + GPIO_GROUP(GPIOX_9, 0), + GPIO_GROUP(GPIOX_10, 0), + GPIO_GROUP(GPIOX_11, 0), + GPIO_GROUP(GPIOX_12, 0), + GPIO_GROUP(GPIOX_13, 0), + GPIO_GROUP(GPIOX_14, 0), + GPIO_GROUP(GPIOX_15, 0), + GPIO_GROUP(GPIOX_16, 0), + GPIO_GROUP(GPIOX_17, 0), + GPIO_GROUP(GPIOX_18, 0), + GPIO_GROUP(GPIOX_19, 0), + GPIO_GROUP(GPIOX_20, 0), + GPIO_GROUP(GPIOX_21, 0), + GPIO_GROUP(GPIOY_0, 0), + GPIO_GROUP(GPIOY_1, 0), + GPIO_GROUP(GPIOY_2, 0), + GPIO_GROUP(GPIOY_3, 0), + GPIO_GROUP(GPIOY_4, 0), + GPIO_GROUP(GPIOY_5, 0), + GPIO_GROUP(GPIOY_6, 0), + GPIO_GROUP(GPIOY_7, 0), + GPIO_GROUP(GPIOY_8, 0), + GPIO_GROUP(GPIOY_9, 0), + GPIO_GROUP(GPIOY_10, 0), + GPIO_GROUP(GPIOY_11, 0), + GPIO_GROUP(GPIOY_12, 0), + GPIO_GROUP(GPIOY_13, 0), + GPIO_GROUP(GPIOY_14, 0), + GPIO_GROUP(GPIOY_15, 0), + GPIO_GROUP(GPIOY_16, 0), + GPIO_GROUP(GPIODV_0, 0), + GPIO_GROUP(GPIODV_1, 0), + GPIO_GROUP(GPIODV_2, 0), + GPIO_GROUP(GPIODV_3, 0), + GPIO_GROUP(GPIODV_4, 0), + GPIO_GROUP(GPIODV_5, 0), + GPIO_GROUP(GPIODV_6, 0), + GPIO_GROUP(GPIODV_7, 0), + GPIO_GROUP(GPIODV_8, 0), + GPIO_GROUP(GPIODV_9, 0), + GPIO_GROUP(GPIODV_10, 0), + GPIO_GROUP(GPIODV_11, 0), + GPIO_GROUP(GPIODV_12, 0), + GPIO_GROUP(GPIODV_13, 0), + GPIO_GROUP(GPIODV_14, 0), + GPIO_GROUP(GPIODV_15, 0), + GPIO_GROUP(GPIODV_16, 0), + GPIO_GROUP(GPIODV_17, 0), + GPIO_GROUP(GPIODV_18, 0), + GPIO_GROUP(GPIODV_19, 0), + GPIO_GROUP(GPIODV_20, 0), + GPIO_GROUP(GPIODV_21, 0), + GPIO_GROUP(GPIODV_22, 0), + GPIO_GROUP(GPIODV_23, 0), + GPIO_GROUP(GPIODV_24, 0), + GPIO_GROUP(GPIODV_25, 0), + GPIO_GROUP(GPIODV_26, 0), + GPIO_GROUP(GPIODV_27, 0), + GPIO_GROUP(GPIODV_28, 0), + GPIO_GROUP(GPIODV_29, 0), + GPIO_GROUP(GPIOH_0, 0), + GPIO_GROUP(GPIOH_1, 0), + GPIO_GROUP(GPIOH_2, 0), + GPIO_GROUP(GPIOH_3, 0), + GPIO_GROUP(GPIOH_4, 0), + GPIO_GROUP(GPIOH_5, 0), + GPIO_GROUP(GPIOH_6, 0), + GPIO_GROUP(GPIOH_7, 0), + GPIO_GROUP(GPIOH_8, 0), + GPIO_GROUP(GPIOH_9, 0), + GPIO_GROUP(GPIOZ_0, 0), + GPIO_GROUP(GPIOZ_1, 0), + GPIO_GROUP(GPIOZ_2, 0), + GPIO_GROUP(GPIOZ_3, 0), + GPIO_GROUP(GPIOZ_4, 0), + GPIO_GROUP(GPIOZ_5, 0), + GPIO_GROUP(GPIOZ_6, 0), + GPIO_GROUP(GPIOZ_7, 0), + GPIO_GROUP(GPIOZ_8, 0), + GPIO_GROUP(GPIOZ_9, 0), + GPIO_GROUP(GPIOZ_10, 0), + GPIO_GROUP(GPIOZ_11, 0), + GPIO_GROUP(GPIOZ_12, 0), + GPIO_GROUP(GPIOZ_13, 0), + GPIO_GROUP(GPIOZ_14, 0), + GPIO_GROUP(GPIOAO_0, AO_OFF), + GPIO_GROUP(GPIOAO_1, AO_OFF), + GPIO_GROUP(GPIOAO_2, AO_OFF), + GPIO_GROUP(GPIOAO_3, AO_OFF), + GPIO_GROUP(GPIOAO_4, AO_OFF), + GPIO_GROUP(GPIOAO_5, AO_OFF), + GPIO_GROUP(GPIOAO_6, AO_OFF), + GPIO_GROUP(GPIOAO_7, AO_OFF), + GPIO_GROUP(GPIOAO_8, AO_OFF), + GPIO_GROUP(GPIOAO_9, AO_OFF), + GPIO_GROUP(GPIOAO_10, AO_OFF), + GPIO_GROUP(GPIOAO_11, AO_OFF), + GPIO_GROUP(GPIOAO_12, AO_OFF), + GPIO_GROUP(GPIOAO_13, AO_OFF), + GPIO_GROUP(GPIO_BSD_EN, AO_OFF), + GPIO_GROUP(GPIO_TEST_N, AO_OFF), /* bank X */ GROUP(sd_d0_a, 8, 5), @@ -1045,19 +907,19 @@ static struct meson_pmx_func meson8_functions[] = { }; static struct meson_bank meson8_banks[] = { - /* name first last pullen pull dir out in */ - BANK("X", PIN_GPIOX_0, PIN_GPIOX_21, 4, 0, 4, 0, 0, 0, 1, 0, 2, 0), - BANK("Y", PIN_GPIOY_0, PIN_GPIOY_16, 3, 0, 3, 0, 3, 0, 4, 0, 5, 0), - BANK("DV", PIN_GPIODV_0, PIN_GPIODV_29, 0, 0, 0, 0, 7, 0, 8, 0, 9, 0), - BANK("H", PIN_GPIOH_0, PIN_GPIOH_9, 1, 16, 1, 16, 9, 19, 10, 19, 11, 19), - BANK("Z", PIN_GPIOZ_0, PIN_GPIOZ_14, 1, 0, 1, 0, 3, 17, 4, 17, 5, 17), - BANK("CARD", PIN_CARD_0, PIN_CARD_6, 2, 20, 2, 20, 0, 22, 1, 22, 2, 22), - BANK("BOOT", PIN_BOOT_0, PIN_BOOT_18, 2, 0, 2, 0, 9, 0, 10, 0, 11, 0), + /* name first last pullen pull dir out in */ + BANK("X", PIN(GPIOX_0, 0), PIN(GPIOX_21, 0), 4, 0, 4, 0, 0, 0, 1, 0, 2, 0), + BANK("Y", PIN(GPIOY_0, 0), PIN(GPIOY_16, 0), 3, 0, 3, 0, 3, 0, 4, 0, 5, 0), + BANK("DV", PIN(GPIODV_0, 0), PIN(GPIODV_29, 0), 0, 0, 0, 0, 7, 0, 8, 0, 9, 0), + BANK("H", PIN(GPIOH_0, 0), PIN(GPIOH_9, 0), 1, 16, 1, 16, 9, 19, 10, 19, 11, 19), + BANK("Z", PIN(GPIOZ_0, 0), PIN(GPIOZ_14, 0), 1, 0, 1, 0, 3, 17, 4, 17, 5, 17), + BANK("CARD", PIN(CARD_0, 0), PIN(CARD_6, 0), 2, 20, 2, 20, 0, 22, 1, 22, 2, 22), + BANK("BOOT", PIN(BOOT_0, 0), PIN(BOOT_18, 0), 2, 0, 2, 0, 9, 0, 10, 0, 11, 0), }; static struct meson_bank meson8_ao_banks[] = { - /* name first last pullen pull dir out in */ - BANK("AO", PIN_GPIOAO_0, PIN_GPIO_TEST_N, 0, 0, 0, 16, 0, 0, 0, 16, 1, 0), + /* name first last pullen pull dir out in */ + BANK("AO", PIN(GPIOAO_0, AO_OFF), PIN(GPIO_TEST_N, AO_OFF), 0, 0, 0, 16, 0, 0, 0, 16, 1, 0), }; static struct meson_domain_data meson8_domain_data[] = { diff --git a/drivers/pinctrl/meson/pinctrl-meson8b.c b/drivers/pinctrl/meson/pinctrl-meson8b.c new file mode 100644 index 00000000000000..2f7ea62298801c --- /dev/null +++ b/drivers/pinctrl/meson/pinctrl-meson8b.c @@ -0,0 +1,899 @@ +/* + * Pin controller and GPIO driver for Amlogic Meson8b. + * + * Copyright (C) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "pinctrl-meson.h" + +#define AO_OFF 130 + +static const struct pinctrl_pin_desc meson8b_pins[] = { + MESON_PIN(GPIOX_0, 0), + MESON_PIN(GPIOX_1, 0), + MESON_PIN(GPIOX_2, 0), + MESON_PIN(GPIOX_3, 0), + MESON_PIN(GPIOX_4, 0), + MESON_PIN(GPIOX_5, 0), + MESON_PIN(GPIOX_6, 0), + MESON_PIN(GPIOX_7, 0), + MESON_PIN(GPIOX_8, 0), + MESON_PIN(GPIOX_9, 0), + MESON_PIN(GPIOX_10, 0), + MESON_PIN(GPIOX_11, 0), + MESON_PIN(GPIOX_16, 0), + MESON_PIN(GPIOX_17, 0), + MESON_PIN(GPIOX_18, 0), + MESON_PIN(GPIOX_19, 0), + MESON_PIN(GPIOX_20, 0), + MESON_PIN(GPIOX_21, 0), + + MESON_PIN(GPIOY_0, 0), + MESON_PIN(GPIOY_1, 0), + MESON_PIN(GPIOY_3, 0), + MESON_PIN(GPIOY_6, 0), + MESON_PIN(GPIOY_7, 0), + MESON_PIN(GPIOY_8, 0), + MESON_PIN(GPIOY_9, 0), + MESON_PIN(GPIOY_10, 0), + MESON_PIN(GPIOY_11, 0), + MESON_PIN(GPIOY_12, 0), + MESON_PIN(GPIOY_13, 0), + MESON_PIN(GPIOY_14, 0), + + MESON_PIN(GPIODV_9, 0), + MESON_PIN(GPIODV_24, 0), + MESON_PIN(GPIODV_25, 0), + MESON_PIN(GPIODV_26, 0), + MESON_PIN(GPIODV_27, 0), + MESON_PIN(GPIODV_28, 0), + MESON_PIN(GPIODV_29, 0), + + MESON_PIN(GPIOH_0, 0), + MESON_PIN(GPIOH_1, 0), + MESON_PIN(GPIOH_2, 0), + MESON_PIN(GPIOH_3, 0), + MESON_PIN(GPIOH_4, 0), + MESON_PIN(GPIOH_5, 0), + MESON_PIN(GPIOH_6, 0), + MESON_PIN(GPIOH_7, 0), + MESON_PIN(GPIOH_8, 0), + MESON_PIN(GPIOH_9, 0), + + MESON_PIN(CARD_0, 0), + MESON_PIN(CARD_1, 0), + MESON_PIN(CARD_2, 0), + MESON_PIN(CARD_3, 0), + MESON_PIN(CARD_4, 0), + MESON_PIN(CARD_5, 0), + MESON_PIN(CARD_6, 0), + + MESON_PIN(BOOT_0, 0), + MESON_PIN(BOOT_1, 0), + MESON_PIN(BOOT_2, 0), + MESON_PIN(BOOT_3, 0), + MESON_PIN(BOOT_4, 0), + MESON_PIN(BOOT_5, 0), + MESON_PIN(BOOT_6, 0), + MESON_PIN(BOOT_7, 0), + MESON_PIN(BOOT_8, 0), + MESON_PIN(BOOT_9, 0), + MESON_PIN(BOOT_10, 0), + MESON_PIN(BOOT_11, 0), + MESON_PIN(BOOT_12, 0), + MESON_PIN(BOOT_13, 0), + MESON_PIN(BOOT_14, 0), + MESON_PIN(BOOT_15, 0), + MESON_PIN(BOOT_16, 0), + MESON_PIN(BOOT_17, 0), + MESON_PIN(BOOT_18, 0), + + MESON_PIN(DIF_0_P, 0), + MESON_PIN(DIF_0_N, 0), + MESON_PIN(DIF_1_P, 0), + MESON_PIN(DIF_1_N, 0), + MESON_PIN(DIF_2_P, 0), + MESON_PIN(DIF_2_N, 0), + MESON_PIN(DIF_3_P, 0), + MESON_PIN(DIF_3_N, 0), + MESON_PIN(DIF_4_P, 0), + MESON_PIN(DIF_4_N, 0), + + MESON_PIN(GPIOAO_0, AO_OFF), + MESON_PIN(GPIOAO_1, AO_OFF), + MESON_PIN(GPIOAO_2, AO_OFF), + MESON_PIN(GPIOAO_3, AO_OFF), + MESON_PIN(GPIOAO_4, AO_OFF), + MESON_PIN(GPIOAO_5, AO_OFF), + MESON_PIN(GPIOAO_6, AO_OFF), + MESON_PIN(GPIOAO_7, AO_OFF), + MESON_PIN(GPIOAO_8, AO_OFF), + MESON_PIN(GPIOAO_9, AO_OFF), + MESON_PIN(GPIOAO_10, AO_OFF), + MESON_PIN(GPIOAO_11, AO_OFF), + MESON_PIN(GPIOAO_12, AO_OFF), + MESON_PIN(GPIOAO_13, AO_OFF), + MESON_PIN(GPIO_BSD_EN, AO_OFF), + MESON_PIN(GPIO_TEST_N, AO_OFF), +}; + +/* bank X */ +static const unsigned int sd_d0_a_pins[] = { PIN(GPIOX_0, 0) }; +static const unsigned int sd_d1_a_pins[] = { PIN(GPIOX_1, 0) }; +static const unsigned int sd_d2_a_pins[] = { PIN(GPIOX_2, 0) }; +static const unsigned int sd_d3_a_pins[] = { PIN(GPIOX_3, 0) }; +static const unsigned int sdxc_d0_0_a_pins[] = { PIN(GPIOX_4, 0) }; +static const unsigned int sdxc_d47_a_pins[] = { PIN(GPIOX_4, 0), PIN(GPIOX_5, 0), + PIN(GPIOX_6, 0), PIN(GPIOX_7, 0) }; +static const unsigned int sdxc_d13_0_a_pins[] = { PIN(GPIOX_5, 0), PIN(GPIOX_6, 0), + PIN(GPIOX_7, 0) }; +static const unsigned int sd_clk_a_pins[] = { PIN(GPIOX_8, 0) }; +static const unsigned int sd_cmd_a_pins[] = { PIN(GPIOX_9, 0) }; +static const unsigned int xtal_32k_out_pins[] = { PIN(GPIOX_10, 0) }; +static const unsigned int xtal_24m_out_pins[] = { PIN(GPIOX_11, 0) }; +static const unsigned int uart_tx_b0_pins[] = { PIN(GPIOX_16, 0) }; +static const unsigned int uart_rx_b0_pins[] = { PIN(GPIOX_17, 0) }; +static const unsigned int uart_cts_b0_pins[] = { PIN(GPIOX_18, 0) }; +static const unsigned int uart_rts_b0_pins[] = { PIN(GPIOX_19, 0) }; + +static const unsigned int sdxc_d0_1_a_pins[] = { PIN(GPIOX_0, 0) }; +static const unsigned int sdxc_d13_1_a_pins[] = { PIN(GPIOX_1, 0), PIN(GPIOX_2, 0), + PIN(GPIOX_3, 0) }; +static const unsigned int pcm_out_a_pins[] = { PIN(GPIOX_4, 0) }; +static const unsigned int pcm_in_a_pins[] = { PIN(GPIOX_5, 0) }; +static const unsigned int pcm_fs_a_pins[] = { PIN(GPIOX_6, 0) }; +static const unsigned int pcm_clk_a_pins[] = { PIN(GPIOX_7, 0) }; +static const unsigned int sdxc_clk_a_pins[] = { PIN(GPIOX_8, 0) }; +static const unsigned int sdxc_cmd_a_pins[] = { PIN(GPIOX_9, 0) }; +static const unsigned int pwm_vs_0_pins[] = { PIN(GPIOX_10, 0) }; +static const unsigned int pwm_e_pins[] = { PIN(GPIOX_10, 0) }; +static const unsigned int pwm_vs_1_pins[] = { PIN(GPIOX_11, 0) }; + +static const unsigned int uart_tx_a_pins[] = { PIN(GPIOX_4, 0) }; +static const unsigned int uart_rx_a_pins[] = { PIN(GPIOX_5, 0) }; +static const unsigned int uart_cts_a_pins[] = { PIN(GPIOX_6, 0) }; +static const unsigned int uart_rts_a_pins[] = { PIN(GPIOX_7, 0) }; +static const unsigned int uart_tx_b1_pins[] = { PIN(GPIOX_8, 0) }; +static const unsigned int uart_rx_b1_pins[] = { PIN(GPIOX_9, 0) }; +static const unsigned int uart_cts_b1_pins[] = { PIN(GPIOX_10, 0) }; +static const unsigned int uart_rts_b1_pins[] = { PIN(GPIOX_20, 0) }; + +static const unsigned int iso7816_0_clk_pins[] = { PIN(GPIOX_6, 0) }; +static const unsigned int iso7816_0_data_pins[] = { PIN(GPIOX_7, 0) }; +static const unsigned int spi_sclk_0_pins[] = { PIN(GPIOX_8, 0) }; +static const unsigned int spi_miso_0_pins[] = { PIN(GPIOX_9, 0) }; +static const unsigned int spi_mosi_0_pins[] = { PIN(GPIOX_10, 0) }; +static const unsigned int iso7816_det_pins[] = { PIN(GPIOX_16, 0) }; +static const unsigned int iso7816_reset_pins[] = { PIN(GPIOX_17, 0) }; +static const unsigned int iso7816_1_clk_pins[] = { PIN(GPIOX_18, 0) }; +static const unsigned int iso7816_1_data_pins[] = { PIN(GPIOX_19, 0) }; +static const unsigned int spi_ss0_0_pins[] = { PIN(GPIOX_20, 0) }; + +static const unsigned int tsin_clk_b_pins[] = { PIN(GPIOX_8, 0) }; +static const unsigned int tsin_sop_b_pins[] = { PIN(GPIOX_9, 0) }; +static const unsigned int tsin_d0_b_pins[] = { PIN(GPIOX_10, 0) }; +static const unsigned int pwm_b_pins[] = { PIN(GPIOX_11, 0) }; +static const unsigned int i2c_sda_d0_pins[] = { PIN(GPIOX_16, 0) }; +static const unsigned int i2c_sck_d0_pins[] = { PIN(GPIOX_17, 0) }; +static const unsigned int tsin_d_valid_b_pins[] = { PIN(GPIOX_20, 0) }; + +/* bank Y */ +static const unsigned int tsin_d_valid_a_pins[] = { PIN(GPIOY_0, 0) }; +static const unsigned int tsin_sop_a_pins[] = { PIN(GPIOY_1, 0) }; +static const unsigned int tsin_d17_a_pins[] = { PIN(GPIOY_6, 0), PIN(GPIOY_7, 0), + PIN(GPIOY_10, 0), PIN(GPIOY_11, 0), + PIN(GPIOY_12, 0), PIN(GPIOY_13, 0), + PIN(GPIOY_14, 0) }; +static const unsigned int tsin_clk_a_pins[] = { PIN(GPIOY_8, 0) }; +static const unsigned int tsin_d0_a_pins[] = { PIN(GPIOY_9, 0) }; + +static const unsigned int spdif_out_0_pins[] = { PIN(GPIOY_3, 0) }; + +static const unsigned int xtal_24m_pins[] = { PIN(GPIOY_3, 0) }; +static const unsigned int iso7816_2_clk_pins[] = { PIN(GPIOY_13, 0) }; +static const unsigned int iso7816_2_data_pins[] = { PIN(GPIOY_14, 0) }; + +/* bank DV */ +static const unsigned int pwm_d_pins[] = { PIN(GPIODV_28, 0) }; +static const unsigned int pwm_c0_pins[] = { PIN(GPIODV_29, 0) }; + +static const unsigned int pwm_vs_2_pins[] = { PIN(GPIODV_9, 0) }; +static const unsigned int pwm_vs_3_pins[] = { PIN(GPIODV_28, 0) }; +static const unsigned int pwm_vs_4_pins[] = { PIN(GPIODV_29, 0) }; + +static const unsigned int xtal24_out_pins[] = { PIN(GPIODV_29, 0) }; + +static const unsigned int uart_tx_c_pins[] = { PIN(GPIODV_24, 0) }; +static const unsigned int uart_rx_c_pins[] = { PIN(GPIODV_25, 0) }; +static const unsigned int uart_cts_c_pins[] = { PIN(GPIODV_26, 0) }; +static const unsigned int uart_rts_c_pins[] = { PIN(GPIODV_27, 0) }; + +static const unsigned int pwm_c1_pins[] = { PIN(GPIODV_9, 0) }; + +static const unsigned int i2c_sda_a_pins[] = { PIN(GPIODV_24, 0) }; +static const unsigned int i2c_sck_a_pins[] = { PIN(GPIODV_25, 0) }; +static const unsigned int i2c_sda_b0_pins[] = { PIN(GPIODV_26, 0) }; +static const unsigned int i2c_sck_b0_pins[] = { PIN(GPIODV_27, 0) }; +static const unsigned int i2c_sda_c0_pins[] = { PIN(GPIODV_28, 0) }; +static const unsigned int i2c_sck_c0_pins[] = { PIN(GPIODV_29, 0) }; + +/* bank H */ +static const unsigned int hdmi_hpd_pins[] = { PIN(GPIOH_0, 0) }; +static const unsigned int hdmi_sda_pins[] = { PIN(GPIOH_1, 0) }; +static const unsigned int hdmi_scl_pins[] = { PIN(GPIOH_2, 0) }; +static const unsigned int hdmi_cec_0_pins[] = { PIN(GPIOH_3, 0) }; +static const unsigned int eth_txd1_0_pins[] = { PIN(GPIOH_5, 0) }; +static const unsigned int eth_txd0_0_pins[] = { PIN(GPIOH_6, 0) }; +static const unsigned int clk_24m_out_pins[] = { PIN(GPIOH_9, 0) }; + +static const unsigned int spi_ss1_pins[] = { PIN(GPIOH_0, 0) }; +static const unsigned int spi_ss2_pins[] = { PIN(GPIOH_1, 0) }; +static const unsigned int spi_ss0_1_pins[] = { PIN(GPIOH_3, 0) }; +static const unsigned int spi_miso_1_pins[] = { PIN(GPIOH_4, 0) }; +static const unsigned int spi_mosi_1_pins[] = { PIN(GPIOH_5, 0) }; +static const unsigned int spi_sclk_1_pins[] = { PIN(GPIOH_6, 0) }; + +static const unsigned int eth_txd3_pins[] = { PIN(GPIOH_7, 0) }; +static const unsigned int eth_txd2_pins[] = { PIN(GPIOH_8, 0) }; +static const unsigned int eth_tx_clk_pins[] = { PIN(GPIOH_9, 0) }; + +static const unsigned int i2c_sda_b1_pins[] = { PIN(GPIOH_3, 0) }; +static const unsigned int i2c_sck_b1_pins[] = { PIN(GPIOH_4, 0) }; +static const unsigned int i2c_sda_c1_pins[] = { PIN(GPIOH_5, 0) }; +static const unsigned int i2c_sck_c1_pins[] = { PIN(GPIOH_6, 0) }; +static const unsigned int i2c_sda_d1_pins[] = { PIN(GPIOH_7, 0) }; +static const unsigned int i2c_sck_d1_pins[] = { PIN(GPIOH_8, 0) }; + +/* bank BOOT */ +static const unsigned int nand_io_pins[] = { PIN(BOOT_0, 0), PIN(BOOT_1, 0), + PIN(BOOT_2, 0), PIN(BOOT_3, 0), + PIN(BOOT_4, 0), PIN(BOOT_5, 0), + PIN(BOOT_6, 0), PIN(BOOT_7, 0) }; +static const unsigned int nand_io_ce0_pins[] = { PIN(BOOT_8, 0) }; +static const unsigned int nand_io_ce1_pins[] = { PIN(BOOT_9, 0) }; +static const unsigned int nand_io_rb0_pins[] = { PIN(BOOT_10, 0) }; +static const unsigned int nand_ale_pins[] = { PIN(BOOT_11, 0) }; +static const unsigned int nand_cle_pins[] = { PIN(BOOT_12, 0) }; +static const unsigned int nand_wen_clk_pins[] = { PIN(BOOT_13, 0) }; +static const unsigned int nand_ren_clk_pins[] = { PIN(BOOT_14, 0) }; +static const unsigned int nand_dqs_0_pins[] = { PIN(BOOT_15, 0) }; +static const unsigned int nand_dqs_1_pins[] = { PIN(BOOT_18, 0) }; + +static const unsigned int sdxc_d0_c_pins[] = { PIN(BOOT_0, 0)}; +static const unsigned int sdxc_d13_c_pins[] = { PIN(BOOT_1, 0), PIN(BOOT_2, 0), + PIN(BOOT_3, 0) }; +static const unsigned int sdxc_d47_c_pins[] = { PIN(BOOT_4, 0), PIN(BOOT_5, 0), + PIN(BOOT_6, 0), PIN(BOOT_7, 0) }; +static const unsigned int sdxc_clk_c_pins[] = { PIN(BOOT_8, 0) }; +static const unsigned int sdxc_cmd_c_pins[] = { PIN(BOOT_10, 0) }; +static const unsigned int nor_d_pins[] = { PIN(BOOT_11, 0) }; +static const unsigned int nor_q_pins[] = { PIN(BOOT_12, 0) }; +static const unsigned int nor_c_pins[] = { PIN(BOOT_13, 0) }; +static const unsigned int nor_cs_pins[] = { PIN(BOOT_18, 0) }; + +static const unsigned int sd_d0_c_pins[] = { PIN(BOOT_0, 0) }; +static const unsigned int sd_d1_c_pins[] = { PIN(BOOT_1, 0) }; +static const unsigned int sd_d2_c_pins[] = { PIN(BOOT_2, 0) }; +static const unsigned int sd_d3_c_pins[] = { PIN(BOOT_3, 0) }; +static const unsigned int sd_cmd_c_pins[] = { PIN(BOOT_8, 0) }; +static const unsigned int sd_clk_c_pins[] = { PIN(BOOT_10, 0) }; + +/* bank CARD */ +static const unsigned int sd_d1_b_pins[] = { PIN(CARD_0, 0) }; +static const unsigned int sd_d0_b_pins[] = { PIN(CARD_1, 0) }; +static const unsigned int sd_clk_b_pins[] = { PIN(CARD_2, 0) }; +static const unsigned int sd_cmd_b_pins[] = { PIN(CARD_3, 0) }; +static const unsigned int sd_d3_b_pins[] = { PIN(CARD_4, 0) }; +static const unsigned int sd_d2_b_pins[] = { PIN(CARD_5, 0) }; + +static const unsigned int sdxc_d13_b_pins[] = { PIN(CARD_0, 0), PIN(CARD_4, 0), + PIN(CARD_5, 0) }; +static const unsigned int sdxc_d0_b_pins[] = { PIN(CARD_1, 0) }; +static const unsigned int sdxc_clk_b_pins[] = { PIN(CARD_2, 0) }; +static const unsigned int sdxc_cmd_b_pins[] = { PIN(CARD_3, 0) }; + +/* bank AO */ +static const unsigned int uart_tx_ao_a_pins[] = { PIN(GPIOAO_0, AO_OFF) }; +static const unsigned int uart_rx_ao_a_pins[] = { PIN(GPIOAO_1, AO_OFF) }; +static const unsigned int uart_cts_ao_a_pins[] = { PIN(GPIOAO_2, AO_OFF) }; +static const unsigned int uart_rts_ao_a_pins[] = { PIN(GPIOAO_3, AO_OFF) }; +static const unsigned int i2c_mst_sck_ao_pins[] = { PIN(GPIOAO_4, AO_OFF) }; +static const unsigned int i2c_mst_sda_ao_pins[] = { PIN(GPIOAO_5, AO_OFF) }; +static const unsigned int clk_32k_in_out_pins[] = { PIN(GPIOAO_6, AO_OFF) }; +static const unsigned int remote_input_pins[] = { PIN(GPIOAO_7, AO_OFF) }; +static const unsigned int hdmi_cec_1_pins[] = { PIN(GPIOAO_12, AO_OFF) }; +static const unsigned int ir_blaster_pins[] = { PIN(GPIOAO_13, AO_OFF) }; + +static const unsigned int pwm_c2_pins[] = { PIN(GPIOAO_3, AO_OFF) }; +static const unsigned int i2c_sck_ao_pins[] = { PIN(GPIOAO_4, AO_OFF) }; +static const unsigned int i2c_sda_ao_pins[] = { PIN(GPIOAO_5, AO_OFF) }; +static const unsigned int ir_remote_out_pins[] = { PIN(GPIOAO_7, AO_OFF) }; +static const unsigned int i2s_am_clk_out_pins[] = { PIN(GPIOAO_8, AO_OFF) }; +static const unsigned int i2s_ao_clk_out_pins[] = { PIN(GPIOAO_9, AO_OFF) }; +static const unsigned int i2s_lr_clk_out_pins[] = { PIN(GPIOAO_10, AO_OFF) }; +static const unsigned int i2s_out_01_pins[] = { PIN(GPIOAO_11, AO_OFF) }; + +static const unsigned int uart_tx_ao_b0_pins[] = { PIN(GPIOAO_0, AO_OFF) }; +static const unsigned int uart_rx_ao_b0_pins[] = { PIN(GPIOAO_1, AO_OFF) }; +static const unsigned int uart_cts_ao_b_pins[] = { PIN(GPIOAO_2, AO_OFF) }; +static const unsigned int uart_rts_ao_b_pins[] = { PIN(GPIOAO_3, AO_OFF) }; +static const unsigned int uart_tx_ao_b1_pins[] = { PIN(GPIOAO_4, AO_OFF) }; +static const unsigned int uart_rx_ao_b1_pins[] = { PIN(GPIOAO_5, AO_OFF) }; +static const unsigned int spdif_out_1_pins[] = { PIN(GPIOAO_6, AO_OFF) }; + +static const unsigned int i2s_in_ch01_pins[] = { PIN(GPIOAO_6, AO_OFF) }; +static const unsigned int i2s_ao_clk_in_pins[] = { PIN(GPIOAO_9, AO_OFF) }; +static const unsigned int i2s_lr_clk_in_pins[] = { PIN(GPIOAO_10, AO_OFF) }; + +/* bank DIF */ +static const unsigned int eth_rxd1_pins[] = { PIN(DIF_0_P, 0) }; +static const unsigned int eth_rxd0_pins[] = { PIN(DIF_0_N, 0) }; +static const unsigned int eth_rx_dv_pins[] = { PIN(DIF_1_P, 0) }; +static const unsigned int eth_rx_clk_pins[] = { PIN(DIF_1_N, 0) }; +static const unsigned int eth_txd0_1_pins[] = { PIN(DIF_2_P, 0) }; +static const unsigned int eth_txd1_1_pins[] = { PIN(DIF_2_N, 0) }; +static const unsigned int eth_tx_en_pins[] = { PIN(DIF_3_P, 0) }; +static const unsigned int eth_ref_clk_pins[] = { PIN(DIF_3_N, 0) }; +static const unsigned int eth_mdc_pins[] = { PIN(DIF_4_P, 0) }; +static const unsigned int eth_mdio_en_pins[] = { PIN(DIF_4_N, 0) }; + +static struct meson_pmx_group meson8b_groups[] = { + GPIO_GROUP(GPIOX_0, 0), + GPIO_GROUP(GPIOX_1, 0), + GPIO_GROUP(GPIOX_2, 0), + GPIO_GROUP(GPIOX_3, 0), + GPIO_GROUP(GPIOX_4, 0), + GPIO_GROUP(GPIOX_5, 0), + GPIO_GROUP(GPIOX_6, 0), + GPIO_GROUP(GPIOX_7, 0), + GPIO_GROUP(GPIOX_8, 0), + GPIO_GROUP(GPIOX_9, 0), + GPIO_GROUP(GPIOX_10, 0), + GPIO_GROUP(GPIOX_11, 0), + GPIO_GROUP(GPIOX_16, 0), + GPIO_GROUP(GPIOX_17, 0), + GPIO_GROUP(GPIOX_18, 0), + GPIO_GROUP(GPIOX_19, 0), + GPIO_GROUP(GPIOX_20, 0), + GPIO_GROUP(GPIOX_21, 0), + + GPIO_GROUP(GPIOY_0, 0), + GPIO_GROUP(GPIOY_1, 0), + GPIO_GROUP(GPIOY_3, 0), + GPIO_GROUP(GPIOY_6, 0), + GPIO_GROUP(GPIOY_7, 0), + GPIO_GROUP(GPIOY_8, 0), + GPIO_GROUP(GPIOY_9, 0), + GPIO_GROUP(GPIOY_10, 0), + GPIO_GROUP(GPIOY_11, 0), + GPIO_GROUP(GPIOY_12, 0), + GPIO_GROUP(GPIOY_13, 0), + GPIO_GROUP(GPIOY_14, 0), + + GPIO_GROUP(GPIODV_9, 0), + GPIO_GROUP(GPIODV_24, 0), + GPIO_GROUP(GPIODV_25, 0), + GPIO_GROUP(GPIODV_26, 0), + GPIO_GROUP(GPIODV_27, 0), + GPIO_GROUP(GPIODV_28, 0), + GPIO_GROUP(GPIODV_29, 0), + + GPIO_GROUP(GPIOH_0, 0), + GPIO_GROUP(GPIOH_1, 0), + GPIO_GROUP(GPIOH_2, 0), + GPIO_GROUP(GPIOH_3, 0), + GPIO_GROUP(GPIOH_4, 0), + GPIO_GROUP(GPIOH_5, 0), + GPIO_GROUP(GPIOH_6, 0), + GPIO_GROUP(GPIOH_7, 0), + GPIO_GROUP(GPIOH_8, 0), + GPIO_GROUP(GPIOH_9, 0), + + GPIO_GROUP(DIF_0_P, 0), + GPIO_GROUP(DIF_0_N, 0), + GPIO_GROUP(DIF_1_P, 0), + GPIO_GROUP(DIF_1_N, 0), + GPIO_GROUP(DIF_2_P, 0), + GPIO_GROUP(DIF_2_N, 0), + GPIO_GROUP(DIF_3_P, 0), + GPIO_GROUP(DIF_3_N, 0), + GPIO_GROUP(DIF_4_P, 0), + GPIO_GROUP(DIF_4_N, 0), + + GPIO_GROUP(GPIOAO_0, AO_OFF), + GPIO_GROUP(GPIOAO_1, AO_OFF), + GPIO_GROUP(GPIOAO_2, AO_OFF), + GPIO_GROUP(GPIOAO_3, AO_OFF), + GPIO_GROUP(GPIOAO_4, AO_OFF), + GPIO_GROUP(GPIOAO_5, AO_OFF), + GPIO_GROUP(GPIOAO_6, AO_OFF), + GPIO_GROUP(GPIOAO_7, AO_OFF), + GPIO_GROUP(GPIOAO_8, AO_OFF), + GPIO_GROUP(GPIOAO_9, AO_OFF), + GPIO_GROUP(GPIOAO_10, AO_OFF), + GPIO_GROUP(GPIOAO_11, AO_OFF), + GPIO_GROUP(GPIOAO_12, AO_OFF), + GPIO_GROUP(GPIOAO_13, AO_OFF), + GPIO_GROUP(GPIO_BSD_EN, AO_OFF), + GPIO_GROUP(GPIO_TEST_N, AO_OFF), + + /* bank X */ + GROUP(sd_d0_a, 8, 5), + GROUP(sd_d1_a, 8, 4), + GROUP(sd_d2_a, 8, 3), + GROUP(sd_d3_a, 8, 2), + GROUP(sdxc_d0_0_a, 5, 29), + GROUP(sdxc_d47_a, 5, 12), + GROUP(sdxc_d13_0_a, 5, 28), + GROUP(sd_clk_a, 8, 1), + GROUP(sd_cmd_a, 8, 0), + GROUP(xtal_32k_out, 3, 22), + GROUP(xtal_24m_out, 3, 20), + GROUP(uart_tx_b0, 4, 9), + GROUP(uart_rx_b0, 4, 8), + GROUP(uart_cts_b0, 4, 7), + GROUP(uart_rts_b0, 4, 6), + GROUP(sdxc_d0_1_a, 5, 14), + GROUP(sdxc_d13_1_a, 5, 13), + GROUP(pcm_out_a, 3, 30), + GROUP(pcm_in_a, 3, 29), + GROUP(pcm_fs_a, 3, 28), + GROUP(pcm_clk_a, 3, 27), + GROUP(sdxc_clk_a, 5, 11), + GROUP(sdxc_cmd_a, 5, 10), + GROUP(pwm_vs_0, 7, 31), + GROUP(pwm_e, 9, 19), + GROUP(pwm_vs_1, 7, 30), + GROUP(uart_tx_a, 4, 17), + GROUP(uart_rx_a, 4, 16), + GROUP(uart_cts_a, 4, 15), + GROUP(uart_rts_a, 4, 14), + GROUP(uart_tx_b1, 6, 19), + GROUP(uart_rx_b1, 6, 18), + GROUP(uart_cts_b1, 6, 17), + GROUP(uart_rts_b1, 6, 16), + GROUP(iso7816_0_clk, 5, 9), + GROUP(iso7816_0_data, 5, 8), + GROUP(spi_sclk_0, 4, 22), + GROUP(spi_miso_0, 4, 24), + GROUP(spi_mosi_0, 4, 23), + GROUP(iso7816_det, 4, 21), + GROUP(iso7816_reset, 4, 20), + GROUP(iso7816_1_clk, 4, 19), + GROUP(iso7816_1_data, 4, 18), + GROUP(spi_ss0_0, 4, 25), + GROUP(tsin_clk_b, 3, 6), + GROUP(tsin_sop_b, 3, 7), + GROUP(tsin_d0_b, 3, 8), + GROUP(pwm_b, 2, 3), + GROUP(i2c_sda_d0, 4, 5), + GROUP(i2c_sck_d0, 4, 4), + GROUP(tsin_d_valid_b, 3, 9), + + /* bank Y */ + GROUP(tsin_d_valid_a, 3, 2), + GROUP(tsin_sop_a, 3, 1), + GROUP(tsin_d17_a, 3, 5), + GROUP(tsin_clk_a, 3, 0), + GROUP(tsin_d0_a, 3, 4), + GROUP(spdif_out_0, 1, 7), + GROUP(xtal_24m, 3, 18), + GROUP(iso7816_2_clk, 5, 7), + GROUP(iso7816_2_data, 5, 6), + + /* bank DV */ + GROUP(pwm_d, 3, 26), + GROUP(pwm_c0, 3, 25), + GROUP(pwm_vs_2, 7, 28), + GROUP(pwm_vs_3, 7, 27), + GROUP(pwm_vs_4, 7, 26), + GROUP(xtal24_out, 7, 25), + GROUP(uart_tx_c, 6, 23), + GROUP(uart_rx_c, 6, 22), + GROUP(uart_cts_c, 6, 21), + GROUP(uart_rts_c, 6, 20), + GROUP(pwm_c1, 3, 24), + GROUP(i2c_sda_a, 9, 31), + GROUP(i2c_sck_a, 9, 30), + GROUP(i2c_sda_b0, 9, 29), + GROUP(i2c_sck_b0, 9, 28), + GROUP(i2c_sda_c0, 9, 27), + GROUP(i2c_sck_c0, 9, 26), + + /* bank H */ + GROUP(hdmi_hpd, 1, 26), + GROUP(hdmi_sda, 1, 25), + GROUP(hdmi_scl, 1, 24), + GROUP(hdmi_cec_0, 1, 23), + GROUP(eth_txd1_0, 7, 21), + GROUP(eth_txd0_0, 7, 20), + GROUP(clk_24m_out, 4, 1), + GROUP(spi_ss1, 8, 11), + GROUP(spi_ss2, 8, 12), + GROUP(spi_ss0_1, 9, 13), + GROUP(spi_miso_1, 9, 12), + GROUP(spi_mosi_1, 9, 11), + GROUP(spi_sclk_1, 9, 10), + GROUP(eth_txd3, 6, 13), + GROUP(eth_txd2, 6, 12), + GROUP(eth_tx_clk, 6, 11), + GROUP(i2c_sda_b1, 5, 27), + GROUP(i2c_sck_b1, 5, 26), + GROUP(i2c_sda_c1, 5, 25), + GROUP(i2c_sck_c1, 5, 24), + GROUP(i2c_sda_d1, 4, 3), + GROUP(i2c_sck_d1, 4, 2), + + /* bank BOOT */ + GROUP(nand_io, 2, 26), + GROUP(nand_io_ce0, 2, 25), + GROUP(nand_io_ce1, 2, 24), + GROUP(nand_io_rb0, 2, 17), + GROUP(nand_ale, 2, 21), + GROUP(nand_cle, 2, 20), + GROUP(nand_wen_clk, 2, 19), + GROUP(nand_ren_clk, 2, 18), + GROUP(nand_dqs_0, 2, 27), + GROUP(nand_dqs_1, 2, 28), + GROUP(sdxc_d0_c, 4, 30), + GROUP(sdxc_d13_c, 4, 29), + GROUP(sdxc_d47_c, 4, 28), + GROUP(sdxc_clk_c, 7, 19), + GROUP(sdxc_cmd_c, 7, 18), + GROUP(nor_d, 5, 1), + GROUP(nor_q, 5, 3), + GROUP(nor_c, 5, 2), + GROUP(nor_cs, 5, 0), + GROUP(sd_d0_c, 6, 29), + GROUP(sd_d1_c, 6, 28), + GROUP(sd_d2_c, 6, 27), + GROUP(sd_d3_c, 6, 26), + GROUP(sd_cmd_c, 6, 30), + GROUP(sd_clk_c, 6, 31), + + /* bank CARD */ + GROUP(sd_d1_b, 2, 14), + GROUP(sd_d0_b, 2, 15), + GROUP(sd_clk_b, 2, 11), + GROUP(sd_cmd_b, 2, 10), + GROUP(sd_d3_b, 2, 12), + GROUP(sd_d2_b, 2, 13), + GROUP(sdxc_d13_b, 2, 6), + GROUP(sdxc_d0_b, 2, 7), + GROUP(sdxc_clk_b, 2, 5), + GROUP(sdxc_cmd_b, 2, 4), + + /* bank AO */ + GROUP(uart_tx_ao_a, 0, 12), + GROUP(uart_rx_ao_a, 0, 11), + GROUP(uart_cts_ao_a, 0, 10), + GROUP(uart_rts_ao_a, 0, 9), + GROUP(i2c_mst_sck_ao, 0, 6), + GROUP(i2c_mst_sda_ao, 0, 5), + GROUP(clk_32k_in_out, 0, 18), + GROUP(remote_input, 0, 0), + GROUP(hdmi_cec_1, 0, 17), + GROUP(ir_blaster, 0, 31), + GROUP(pwm_c2, 0, 22), + GROUP(i2c_sck_ao, 0, 2), + GROUP(i2c_sda_ao, 0, 1), + GROUP(ir_remote_out, 0, 21), + GROUP(i2s_am_clk_out, 0, 30), + GROUP(i2s_ao_clk_out, 0, 29), + GROUP(i2s_lr_clk_out, 0, 28), + GROUP(i2s_out_01, 0, 27), + GROUP(uart_tx_ao_b0, 0, 26), + GROUP(uart_rx_ao_b0, 0, 25), + GROUP(uart_cts_ao_b, 0, 8), + GROUP(uart_rts_ao_b, 0, 7), + GROUP(uart_tx_ao_b1, 0, 24), + GROUP(uart_rx_ao_b1, 0, 23), + GROUP(spdif_out_1, 0, 16), + GROUP(i2s_in_ch01, 0, 13), + GROUP(i2s_ao_clk_in, 0, 15), + GROUP(i2s_lr_clk_in, 0, 14), + + /* bank DIF */ + GROUP(eth_rxd1, 6, 0), + GROUP(eth_rxd0, 6, 1), + GROUP(eth_rx_dv, 6, 2), + GROUP(eth_rx_clk, 6, 3), + GROUP(eth_txd0_1, 6, 4), + GROUP(eth_txd1_1, 6, 5), + GROUP(eth_tx_en, 6, 0), + GROUP(eth_ref_clk, 6, 8), + GROUP(eth_mdc, 6, 9), + GROUP(eth_mdio_en, 6, 10), +}; + +static const char * const gpio_groups[] = { + "GPIOX_0", "GPIOX_1", "GPIOX_2", "GPIOX_3", "GPIOX_4", + "GPIOX_5", "GPIOX_6", "GPIOX_7", "GPIOX_8", "GPIOX_9", + "GPIOX_10", "GPIOX_11", "GPIOX_16", "GPIOX_17", "GPIOX_18", + "GPIOX_19", "GPIOX_20", "GPIOX_21", + + "GPIOY_0", "GPIOY_1", "GPIOY_3", "GPIOY_6", "GPIOY_7", + "GPIOY_8", "GPIOY_9", "GPIOY_10", "GPIOY_11", "GPIOY_12", + "GPIOY_13", "GPIOY_14", + + "GPIODV_9", "GPIODV_24", "GPIODV_25", "GPIODV_26", + "GPIODV_27", "GPIODV_28", "GPIODV_29", + + "GPIOH_0", "GPIOH_1", "GPIOH_2", "GPIOH_3", "GPIOH_4", + "GPIOH_5", "GPIOH_6", "GPIOH_7", "GPIOH_8", "GPIOH_9", + + "CARD_0", "CARD_1", "CARD_2", "CARD_3", "CARD_4", + "CARD_5", "CARD_6", + + "BOOT_0", "BOOT_1", "BOOT_2", "BOOT_3", "BOOT_4", + "BOOT_5", "BOOT_6", "BOOT_7", "BOOT_8", "BOOT_9", + "BOOT_10", "BOOT_11", "BOOT_12", "BOOT_13", "BOOT_14", + "BOOT_15", "BOOT_16", "BOOT_17", "BOOT_18", + + "GPIOAO_0", "GPIOAO_1", "GPIOAO_2", "GPIOAO_3", + "GPIOAO_4", "GPIOAO_5", "GPIOAO_6", "GPIOAO_7", + "GPIOAO_8", "GPIOAO_9", "GPIOAO_10", "GPIOAO_11", + "GPIOAO_12", "GPIOAO_13", "GPIO_BSD_EN", "GPIO_TEST_N", + + "DIF_0_P", "DIF_0_N", "DIF_1_P", "DIF_1_N", + "DIF_2_P", "DIF_2_N", "DIF_3_P", "DIF_3_N", + "DIF_4_P", "DIF_4_N" +}; + +static const char * const sd_a_groups[] = { + "sd_d0_a", "sd_d1_a", "sd_d2_a", "sd_d3_a", "sd_clk_a", + "sd_cmd_a" +}; + +static const char * const sdxc_a_groups[] = { + "sdxc_d0_0_a", "sdxc_d13_0_a", "sdxc_d47_a", "sdxc_clk_a", + "sdxc_cmd_a", "sdxc_d0_1_a", "sdxc_d0_13_1_a" +}; + +static const char * const pcm_a_groups[] = { + "pcm_out_a", "pcm_in_a", "pcm_fs_a", "pcm_clk_a" +}; + +static const char * const uart_a_groups[] = { + "uart_tx_a", "uart_rx_a", "uart_cts_a", "uart_rts_a" +}; + +static const char * const uart_b_groups[] = { + "uart_tx_b0", "uart_rx_b0", "uart_cts_b0", "uart_rts_b0", + "uart_tx_b1", "uart_rx_b1", "uart_cts_b1", "uart_rts_b1" +}; + +static const char * const iso7816_groups[] = { + "iso7816_det", "iso7816_reset", "iso7816_0_clk", "iso7816_0_data", + "iso7816_1_clk", "iso7816_1_data", "iso7816_2_clk", "iso7816_2_data" +}; + +static const char * const i2c_d_groups[] = { + "i2c_sda_d0", "i2c_sck_d0", "i2c_sda_d1", "i2c_sck_d1" +}; + +static const char * const xtal_groups[] = { + "xtal_32k_out", "xtal_24m_out", "xtal_24m", "xtal24_out" +}; + +static const char * const uart_c_groups[] = { + "uart_tx_c", "uart_rx_c", "uart_cts_c", "uart_rts_c" +}; + +static const char * const i2c_c_groups[] = { + "i2c_sda_c0", "i2c_sck_c0", "i2c_sda_c1", "i2c_sck_c1" +}; + +static const char * const hdmi_groups[] = { + "hdmi_hpd", "hdmi_sda", "hdmi_scl", "hdmi_cec_0", + "hdmi_cec_1" +}; + +static const char * const spi_groups[] = { + "spi_ss0_0", "spi_miso_0", "spi_mosi_0", "spi_sclk_0", + "spi_ss0_1", "spi_ss1", "spi_sclk_1", "spi_mosi_1", + "spi_miso_1", "spi_ss2" +}; + +static const char * const ethernet_groups[] = { + "eth_tx_clk", "eth_tx_en", "eth_txd1_0", "eth_txd1_1", + "eth_txd0_0", "eth_txd0_1", "eth_rx_clk", "eth_rx_dv", + "eth_rxd1", "eth_rxd0", "eth_mdio_en", "eth_mdc", "eth_ref_clk", + "eth_txd2", "eth_txd3" +}; + +static const char * const i2c_a_groups[] = { + "i2c_sda_a", "i2c_sck_a", +}; + +static const char * const i2c_b_groups[] = { + "i2c_sda_b0", "i2c_sck_b0", "i2c_sda_b1", "i2c_sck_b1" +}; + +static const char * const sd_c_groups[] = { + "sd_d0_c", "sd_d1_c", "sd_d2_c", "sd_d3_c", + "sd_cmd_c", "sd_clk_c" +}; + +static const char * const sdxc_c_groups[] = { + "sdxc_d0_c", "sdxc_d13_c", "sdxc_d47_c", "sdxc_cmd_c", + "sdxc_clk_c" +}; + +static const char * const nand_groups[] = { + "nand_io", "nand_io_ce0", "nand_io_ce1", + "nand_io_rb0", "nand_ale", "nand_cle", + "nand_wen_clk", "nand_ren_clk", "nand_dqs0", + "nand_dqs1" +}; + +static const char * const nor_groups[] = { + "nor_d", "nor_q", "nor_c", "nor_cs" +}; + +static const char * const sd_b_groups[] = { + "sd_d1_b", "sd_d0_b", "sd_clk_b", "sd_cmd_b", + "sd_d3_b", "sd_d2_b" +}; + +static const char * const sdxc_b_groups[] = { + "sdxc_d13_b", "sdxc_d0_b", "sdxc_clk_b", "sdxc_cmd_b" +}; + +static const char * const uart_ao_groups[] = { + "uart_tx_ao_a", "uart_rx_ao_a", "uart_cts_ao_a", "uart_rts_ao_a" +}; + +static const char * const remote_groups[] = { + "remote_input", "ir_blaster", "ir_remote_out" +}; + +static const char * const i2c_slave_ao_groups[] = { + "i2c_sck_ao", "i2c_sda_ao" +}; + +static const char * const uart_ao_b_groups[] = { + "uart_tx_ao_b0", "uart_rx_ao_b0", "uart_tx_ao_b1", "uart_rx_ao_b1", + "uart_cts_ao_b", "uart_rts_ao_b" +}; + +static const char * const i2c_mst_ao_groups[] = { + "i2c_mst_sck_ao", "i2c_mst_sda_ao" +}; + +static const char * const clk_groups[] = { + "clk_24m_out", "clk_32k_in_out" +}; + +static const char * const spdif_groups[] = { + "spdif_out_1", "spdif_out_0" +}; + +static const char * const i2s_groups[] = { + "i2s_am_clk_out", "i2s_ao_clk_out", "i2s_lr_clk_out", + "i2s_out_01", "i2s_in_ch01", "i2s_ao_clk_in", + "i2s_lr_clk_in" +}; + +static const char * const pwm_b_groups[] = { + "pwm_b" +}; + +static const char * const pwm_c_groups[] = { + "pwm_c0", "pwm_c1", "pwm_c2" +}; + +static const char * const pwm_d_groups[] = { + "pwm_d" +}; + +static const char * const pwm_e_groups[] = { + "pwm_e" +}; + +static const char * const pwm_vs_groups[] = { + "pwm_vs_0", "pwm_vs_1", "pwm_vs_2", + "pwm_vs_3", "pwm_vs_4" +}; + +static const char * const tsin_a_groups[] = { + "tsin_d0_a", "tsin_d17_a", "tsin_clk_a", "tsin_sop_a", + "tsin_d_valid_a" +}; + +static const char * const tsin_b_groups[] = { + "tsin_d0_b", "tsin_clk_b", "tsin_sop_b", "tsin_d_valid_b" +}; + +static struct meson_pmx_func meson8b_functions[] = { + FUNCTION(gpio), + FUNCTION(sd_a), + FUNCTION(sdxc_a), + FUNCTION(pcm_a), + FUNCTION(uart_a), + FUNCTION(uart_b), + FUNCTION(iso7816), + FUNCTION(i2c_d), + FUNCTION(xtal), + FUNCTION(uart_c), + FUNCTION(i2c_c), + FUNCTION(hdmi), + FUNCTION(spi), + FUNCTION(ethernet), + FUNCTION(i2c_a), + FUNCTION(i2c_b), + FUNCTION(sd_c), + FUNCTION(sdxc_c), + FUNCTION(nand), + FUNCTION(nor), + FUNCTION(sd_b), + FUNCTION(sdxc_b), + FUNCTION(uart_ao), + FUNCTION(remote), + FUNCTION(i2c_slave_ao), + FUNCTION(uart_ao_b), + FUNCTION(i2c_mst_ao), + FUNCTION(clk), + FUNCTION(spdif), + FUNCTION(i2s), + FUNCTION(pwm_b), + FUNCTION(pwm_c), + FUNCTION(pwm_d), + FUNCTION(pwm_e), + FUNCTION(pwm_vs), + FUNCTION(tsin_a), + FUNCTION(tsin_b), +}; + +static struct meson_bank meson8b_banks[] = { + /* name first last pullen pull dir out in */ + BANK("X", PIN(GPIOX_0, 0), PIN(GPIOX_21, 0), 4, 0, 4, 0, 0, 0, 1, 0, 2, 0), + BANK("Y", PIN(GPIOY_0, 0), PIN(GPIOY_14, 0), 3, 0, 3, 0, 3, 0, 4, 0, 5, 0), + BANK("DV", PIN(GPIODV_9, 0), PIN(GPIODV_29, 0), 0, 0, 0, 0, 7, 0, 8, 0, 9, 0), + BANK("H", PIN(GPIOH_0, 0), PIN(GPIOH_9, 0), 1, 16, 1, 16, 9, 19, 10, 19, 11, 19), + BANK("CARD", PIN(CARD_0, 0), PIN(CARD_6, 0), 2, 20, 2, 20, 0, 22, 1, 22, 2, 22), + BANK("BOOT", PIN(BOOT_0, 0), PIN(BOOT_18, 0), 2, 0, 2, 0, 9, 0, 10, 0, 11, 0), + BANK("DIF", PIN(DIF_0_P, 0), PIN(DIF_4_N, 0), 5, 8, 5, 8, 12, 12, 13, 12, 14, 12), +}; + +static struct meson_bank meson8b_ao_banks[] = { + /* name first last pullen pull dir out in */ + BANK("AO", PIN(GPIOAO_0, AO_OFF), PIN(GPIO_TEST_N, AO_OFF), 0, 0, 0, 16, 0, 0, 0, 16, 1, 0), +}; + +static struct meson_domain_data meson8b_domain_data[] = { + { + .name = "banks", + .banks = meson8b_banks, + .num_banks = ARRAY_SIZE(meson8b_banks), + .pin_base = 0, + .num_pins = 83, + }, + { + .name = "ao-bank", + .banks = meson8b_ao_banks, + .num_banks = ARRAY_SIZE(meson8b_ao_banks), + .pin_base = 83, + .num_pins = 16, + }, +}; + +struct meson_pinctrl_data meson8b_pinctrl_data = { + .pins = meson8b_pins, + .groups = meson8b_groups, + .funcs = meson8b_functions, + .domain_data = meson8b_domain_data, + .num_pins = ARRAY_SIZE(meson8b_pins), + .num_groups = ARRAY_SIZE(meson8b_groups), + .num_funcs = ARRAY_SIZE(meson8b_functions), + .num_domains = ARRAY_SIZE(meson8b_domain_data), +}; diff --git a/drivers/pinctrl/mvebu/Kconfig b/drivers/pinctrl/mvebu/Kconfig index d6dd8358a6f60e..170602407c0d75 100644 --- a/drivers/pinctrl/mvebu/Kconfig +++ b/drivers/pinctrl/mvebu/Kconfig @@ -26,6 +26,10 @@ config PINCTRL_ARMADA_38X bool select PINCTRL_MVEBU +config PINCTRL_ARMADA_39X + bool + select PINCTRL_MVEBU + config PINCTRL_ARMADA_XP bool select PINCTRL_MVEBU diff --git a/drivers/pinctrl/mvebu/Makefile b/drivers/pinctrl/mvebu/Makefile index a0818e96374b7f..554d8af14eebbb 100644 --- a/drivers/pinctrl/mvebu/Makefile +++ b/drivers/pinctrl/mvebu/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_PINCTRL_KIRKWOOD) += pinctrl-kirkwood.o obj-$(CONFIG_PINCTRL_ARMADA_370) += pinctrl-armada-370.o obj-$(CONFIG_PINCTRL_ARMADA_375) += pinctrl-armada-375.o obj-$(CONFIG_PINCTRL_ARMADA_38X) += pinctrl-armada-38x.o +obj-$(CONFIG_PINCTRL_ARMADA_39X) += pinctrl-armada-39x.o obj-$(CONFIG_PINCTRL_ARMADA_XP) += pinctrl-armada-xp.o obj-$(CONFIG_PINCTRL_ORION) += pinctrl-orion.o diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-370.c b/drivers/pinctrl/mvebu/pinctrl-armada-370.c index c4f51d0cd2ccec..42f930f70de31e 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-370.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-370.c @@ -379,7 +379,7 @@ static struct mvebu_mpp_mode mv88f6710_mpp_modes[] = { static struct mvebu_pinctrl_soc_info armada_370_pinctrl_info; -static struct of_device_id armada_370_pinctrl_of_match[] = { +static const struct of_device_id armada_370_pinctrl_of_match[] = { { .compatible = "marvell,mv88f6710-pinctrl" }, { }, }; diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-375.c b/drivers/pinctrl/mvebu/pinctrl-armada-375.c index cd7c8f51f7d900..ca1e7571fedb51 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-375.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-375.c @@ -399,7 +399,7 @@ static struct mvebu_mpp_mode mv88f6720_mpp_modes[] = { static struct mvebu_pinctrl_soc_info armada_375_pinctrl_info; -static struct of_device_id armada_375_pinctrl_of_match[] = { +static const struct of_device_id armada_375_pinctrl_of_match[] = { { .compatible = "marvell,mv88f6720-pinctrl" }, { }, }; diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-38x.c b/drivers/pinctrl/mvebu/pinctrl-armada-38x.c index 7302f66f4f1990..83bbcc72be1f8a 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-38x.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-38x.c @@ -389,7 +389,7 @@ static struct mvebu_mpp_mode armada_38x_mpp_modes[] = { static struct mvebu_pinctrl_soc_info armada_38x_pinctrl_info; -static struct of_device_id armada_38x_pinctrl_of_match[] = { +static const struct of_device_id armada_38x_pinctrl_of_match[] = { { .compatible = "marvell,mv88f6810-pinctrl", .data = (void *) V_88F6810, diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-39x.c b/drivers/pinctrl/mvebu/pinctrl-armada-39x.c new file mode 100644 index 00000000000000..42491624d660d6 --- /dev/null +++ b/drivers/pinctrl/mvebu/pinctrl-armada-39x.c @@ -0,0 +1,432 @@ +/* + * Marvell Armada 39x pinctrl driver based on mvebu pinctrl core + * + * Copyright (C) 2015 Marvell + * + * Thomas Petazzoni + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-mvebu.h" + +static void __iomem *mpp_base; + +static int armada_39x_mpp_ctrl_get(unsigned pid, unsigned long *config) +{ + return default_mpp_ctrl_get(mpp_base, pid, config); +} + +static int armada_39x_mpp_ctrl_set(unsigned pid, unsigned long config) +{ + return default_mpp_ctrl_set(mpp_base, pid, config); +} + +enum { + V_88F6920 = BIT(0), + V_88F6928 = BIT(1), + V_88F6920_PLUS = (V_88F6920 | V_88F6928), +}; + +static struct mvebu_mpp_mode armada_39x_mpp_modes[] = { + MPP_MODE(0, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "ua0", "rxd", V_88F6920_PLUS)), + MPP_MODE(1, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "ua0", "txd", V_88F6920_PLUS)), + MPP_MODE(2, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "i2c0", "sck", V_88F6920_PLUS)), + MPP_MODE(3, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "i2c0", "sda", V_88F6920_PLUS)), + MPP_MODE(4, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(2, "ua1", "txd", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "rts", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "smi", "mdc", V_88F6920_PLUS)), + MPP_MODE(5, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(2, "ua1", "rxd", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "cts", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "smi", "mdio", V_88F6920_PLUS)), + MPP_MODE(6, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "cs3", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "xsmi", "mdio", V_88F6920_PLUS)), + MPP_MODE(7, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad9", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "xsmi", "mdc", V_88F6920_PLUS)), + MPP_MODE(8, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad10", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "ptp", "trig", V_88F6920_PLUS)), + MPP_MODE(9, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad11", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "ptp", "clk", V_88F6920_PLUS)), + MPP_MODE(10, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad12", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "ptp", "event", V_88F6920_PLUS)), + MPP_MODE(11, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad13", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "led", "clk", V_88F6920_PLUS)), + MPP_MODE(12, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(2, "pcie0", "rstout", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad14", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "led", "stb", V_88F6920_PLUS)), + MPP_MODE(13, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad15", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "led", "data", V_88F6920_PLUS)), + MPP_MODE(14, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "m", "vtt", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "wen1", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "ua1", "txd", V_88F6920_PLUS)), + MPP_MODE(15, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "pcie0", "rstout", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "mosi", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "i2c1", "sck", V_88F6920_PLUS)), + MPP_MODE(16, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "m", "decc", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "miso", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "i2c1", "sda", V_88F6920_PLUS)), + MPP_MODE(17, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "ua1", "rxd", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "sck", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "smi", "mdio", V_88F6920_PLUS)), + MPP_MODE(18, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "ua1", "txd", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "spi0", "cs0", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "i2c2", "sck", V_88F6920_PLUS)), + MPP_MODE(19, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "sata1", "present", V_88F6928), + MPP_VAR_FUNCTION(5, "ua0", "cts", V_88F6920_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "rxd", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "i2c2", "sda", V_88F6920_PLUS)), + MPP_MODE(20, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "sata0", "present", V_88F6928), + MPP_VAR_FUNCTION(5, "ua0", "rts", V_88F6920_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "txd", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "smi", "mdc", V_88F6920_PLUS)), + MPP_MODE(21, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "cs1", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "sata0", "present", V_88F6928), + MPP_VAR_FUNCTION(4, "sd", "cmd", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "bootcs", V_88F6920_PLUS), + MPP_VAR_FUNCTION(8, "ge", "rxd0", V_88F6920_PLUS)), + MPP_MODE(22, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "mosi", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad0", V_88F6920_PLUS)), + MPP_MODE(23, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "sck", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad2", V_88F6920_PLUS)), + MPP_MODE(24, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "miso", V_88F6920_PLUS), + MPP_VAR_FUNCTION(2, "ua0", "cts", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "ua1", "rxd", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "sd", "d4", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "readyn", V_88F6920_PLUS)), + MPP_MODE(25, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "cs0", V_88F6920_PLUS), + MPP_VAR_FUNCTION(2, "ua0", "rts", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "ua1", "txd", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "sd", "d5", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "cs0", V_88F6920_PLUS)), + MPP_MODE(26, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "cs2", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "i2c1", "sck", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "sd", "d6", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "cs1", V_88F6920_PLUS)), + MPP_MODE(27, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "spi0", "cs3", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "i2c1", "sda", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "sd", "d7", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "cs2", V_88F6920_PLUS), + MPP_VAR_FUNCTION(8, "ge", "txclkout", V_88F6920_PLUS)), + MPP_MODE(28, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "sd", "clk", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad5", V_88F6920_PLUS), + MPP_VAR_FUNCTION(8, "ge", "txd0", V_88F6920_PLUS)), + MPP_MODE(29, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ale0", V_88F6920_PLUS), + MPP_VAR_FUNCTION(8, "ge", "txd1", V_88F6920_PLUS)), + MPP_MODE(30, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "oen", V_88F6920_PLUS), + MPP_VAR_FUNCTION(8, "ge", "txd2", V_88F6920_PLUS)), + MPP_MODE(31, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ale1", V_88F6920_PLUS), + MPP_VAR_FUNCTION(8, "ge", "txd3", V_88F6920_PLUS)), + MPP_MODE(32, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "wen0", V_88F6920_PLUS), + MPP_VAR_FUNCTION(8, "ge", "txctl", V_88F6920_PLUS)), + MPP_MODE(33, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "m", "decc", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad3", V_88F6920_PLUS)), + MPP_MODE(34, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad1", V_88F6920_PLUS)), + MPP_MODE(35, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "ref", "clk", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "a1", V_88F6920_PLUS)), + MPP_MODE(36, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "a0", V_88F6920_PLUS)), + MPP_MODE(37, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "sd", "d3", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad8", V_88F6920_PLUS), + MPP_VAR_FUNCTION(8, "ge", "rxclk", V_88F6920_PLUS)), + MPP_MODE(38, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "ref", "clk", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "sd", "d0", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad4", V_88F6920_PLUS), + MPP_VAR_FUNCTION(8, "ge", "rxd1", V_88F6920_PLUS)), + MPP_MODE(39, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "i2c1", "sck", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "cts", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "sd", "d1", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "a2", V_88F6920_PLUS), + MPP_VAR_FUNCTION(8, "ge", "rxd2", V_88F6920_PLUS)), + MPP_MODE(40, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "i2c1", "sda", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "rts", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "sd", "d2", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad6", V_88F6920_PLUS), + MPP_VAR_FUNCTION(8, "ge", "rxd3", V_88F6920_PLUS)), + MPP_MODE(41, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "ua1", "rxd", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "cts", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "cs3", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "burstn", V_88F6920_PLUS), + MPP_VAR_FUNCTION(6, "nd", "rbn0", V_88F6920_PLUS), + MPP_VAR_FUNCTION(8, "ge", "rxctl", V_88F6920_PLUS)), + MPP_MODE(42, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "ua1", "txd", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "ua0", "rts", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "ad7", V_88F6920_PLUS)), + MPP_MODE(43, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "pcie0", "clkreq", V_88F6920_PLUS), + MPP_VAR_FUNCTION(2, "m", "vtt", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "m", "decc", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "cs2", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "dev", "clkout", V_88F6920_PLUS), + MPP_VAR_FUNCTION(6, "nd", "rbn1", V_88F6920_PLUS)), + MPP_MODE(44, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "sata0", "present", V_88F6928), + MPP_VAR_FUNCTION(2, "sata1", "present", V_88F6928), + MPP_VAR_FUNCTION(7, "led", "clk", V_88F6920_PLUS)), + MPP_MODE(45, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "ref", "clk", V_88F6920_PLUS), + MPP_VAR_FUNCTION(2, "pcie0", "rstout", V_88F6920_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "rxd", V_88F6920_PLUS)), + MPP_MODE(46, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "ref", "clk", V_88F6920_PLUS), + MPP_VAR_FUNCTION(2, "pcie0", "rstout", V_88F6920_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "txd", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "led", "stb", V_88F6920_PLUS)), + MPP_MODE(47, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "sata0", "present", V_88F6928), + MPP_VAR_FUNCTION(2, "sata1", "present", V_88F6928), + MPP_VAR_FUNCTION(7, "led", "data", V_88F6920_PLUS)), + MPP_MODE(48, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "sata0", "present", V_88F6928), + MPP_VAR_FUNCTION(2, "m", "vtt", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "tdm", "pclk", V_88F6928), + MPP_VAR_FUNCTION(4, "audio", "mclk", V_88F6928), + MPP_VAR_FUNCTION(5, "sd", "d4", V_88F6920_PLUS), + MPP_VAR_FUNCTION(6, "pcie0", "clkreq", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "ua1", "txd", V_88F6920_PLUS)), + MPP_MODE(49, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "tdm", "fsync", V_88F6928), + MPP_VAR_FUNCTION(4, "audio", "lrclk", V_88F6928), + MPP_VAR_FUNCTION(5, "sd", "d5", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "ua2", "rxd", V_88F6920_PLUS)), + MPP_MODE(50, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "pcie0", "rstout", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "tdm", "drx", V_88F6928), + MPP_VAR_FUNCTION(4, "audio", "extclk", V_88F6928), + MPP_VAR_FUNCTION(5, "sd", "cmd", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "ua2", "rxd", V_88F6920_PLUS)), + MPP_MODE(51, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "tdm", "dtx", V_88F6928), + MPP_VAR_FUNCTION(4, "audio", "sdo", V_88F6928), + MPP_VAR_FUNCTION(5, "m", "decc", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "ua2", "txd", V_88F6920_PLUS)), + MPP_MODE(52, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "pcie0", "rstout", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "tdm", "intn", V_88F6928), + MPP_VAR_FUNCTION(4, "audio", "sdi", V_88F6928), + MPP_VAR_FUNCTION(5, "sd", "d6", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "i2c3", "sck", V_88F6920_PLUS)), + MPP_MODE(53, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "sata1", "present", V_88F6928), + MPP_VAR_FUNCTION(2, "sata0", "present", V_88F6928), + MPP_VAR_FUNCTION(3, "tdm", "rstn", V_88F6928), + MPP_VAR_FUNCTION(4, "audio", "bclk", V_88F6928), + MPP_VAR_FUNCTION(5, "sd", "d7", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "i2c3", "sda", V_88F6920_PLUS)), + MPP_MODE(54, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "sata0", "present", V_88F6928), + MPP_VAR_FUNCTION(2, "sata1", "present", V_88F6928), + MPP_VAR_FUNCTION(3, "pcie0", "rstout", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "sd", "d3", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "ua3", "txd", V_88F6920_PLUS)), + MPP_MODE(55, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "ua1", "cts", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "cs1", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "sd", "d0", V_88F6920_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "rxd", V_88F6920_PLUS), + MPP_VAR_FUNCTION(7, "ua3", "rxd", V_88F6920_PLUS)), + MPP_MODE(56, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "ua1", "rts", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "m", "decc", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "mosi", V_88F6920_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "txd", V_88F6920_PLUS)), + MPP_MODE(57, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "sck", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "sd", "clk", V_88F6920_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "txd", V_88F6920_PLUS)), + MPP_MODE(58, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(2, "i2c1", "sck", V_88F6920_PLUS), + MPP_VAR_FUNCTION(3, "pcie2", "clkreq", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "miso", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "sd", "d1", V_88F6920_PLUS), + MPP_VAR_FUNCTION(6, "ua1", "rxd", V_88F6920_PLUS)), + MPP_MODE(59, + MPP_VAR_FUNCTION(0, "gpio", NULL, V_88F6920_PLUS), + MPP_VAR_FUNCTION(1, "pcie0", "rstout", V_88F6920_PLUS), + MPP_VAR_FUNCTION(2, "i2c1", "sda", V_88F6920_PLUS), + MPP_VAR_FUNCTION(4, "spi1", "cs0", V_88F6920_PLUS), + MPP_VAR_FUNCTION(5, "sd", "d2", V_88F6920_PLUS)), +}; + +static struct mvebu_pinctrl_soc_info armada_39x_pinctrl_info; + +static const struct of_device_id armada_39x_pinctrl_of_match[] = { + { + .compatible = "marvell,mv88f6920-pinctrl", + .data = (void *) V_88F6920, + }, + { + .compatible = "marvell,mv88f6928-pinctrl", + .data = (void *) V_88F6928, + }, + { }, +}; + +static struct mvebu_mpp_ctrl armada_39x_mpp_controls[] = { + MPP_FUNC_CTRL(0, 59, NULL, armada_39x_mpp_ctrl), +}; + +static struct pinctrl_gpio_range armada_39x_mpp_gpio_ranges[] = { + MPP_GPIO_RANGE(0, 0, 0, 32), + MPP_GPIO_RANGE(1, 32, 32, 27), +}; + +static int armada_39x_pinctrl_probe(struct platform_device *pdev) +{ + struct mvebu_pinctrl_soc_info *soc = &armada_39x_pinctrl_info; + const struct of_device_id *match = + of_match_device(armada_39x_pinctrl_of_match, &pdev->dev); + struct resource *res; + + if (!match) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mpp_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mpp_base)) + return PTR_ERR(mpp_base); + + soc->variant = (unsigned) match->data & 0xff; + soc->controls = armada_39x_mpp_controls; + soc->ncontrols = ARRAY_SIZE(armada_39x_mpp_controls); + soc->gpioranges = armada_39x_mpp_gpio_ranges; + soc->ngpioranges = ARRAY_SIZE(armada_39x_mpp_gpio_ranges); + soc->modes = armada_39x_mpp_modes; + soc->nmodes = armada_39x_mpp_controls[0].npins; + + pdev->dev.platform_data = soc; + + return mvebu_pinctrl_probe(pdev); +} + +static int armada_39x_pinctrl_remove(struct platform_device *pdev) +{ + return mvebu_pinctrl_remove(pdev); +} + +static struct platform_driver armada_39x_pinctrl_driver = { + .driver = { + .name = "armada-39x-pinctrl", + .of_match_table = of_match_ptr(armada_39x_pinctrl_of_match), + }, + .probe = armada_39x_pinctrl_probe, + .remove = armada_39x_pinctrl_remove, +}; + +module_platform_driver(armada_39x_pinctrl_driver); + +MODULE_AUTHOR("Thomas Petazzoni "); +MODULE_DESCRIPTION("Marvell Armada 39x pinctrl driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-xp.c b/drivers/pinctrl/mvebu/pinctrl-armada-xp.c index fc3376147c1899..578db9f033b23c 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-xp.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-xp.c @@ -34,6 +34,7 @@ #include "pinctrl-mvebu.h" static void __iomem *mpp_base; +static u32 *mpp_saved_regs; static int armada_xp_mpp_ctrl_get(unsigned pid, unsigned long *config) { @@ -361,7 +362,7 @@ static struct mvebu_mpp_mode armada_xp_mpp_modes[] = { static struct mvebu_pinctrl_soc_info armada_xp_pinctrl_info; -static struct of_device_id armada_xp_pinctrl_of_match[] = { +static const struct of_device_id armada_xp_pinctrl_of_match[] = { { .compatible = "marvell,mv78230-pinctrl", .data = (void *) V_MV78230, @@ -406,12 +407,42 @@ static struct pinctrl_gpio_range mv78460_mpp_gpio_ranges[] = { MPP_GPIO_RANGE(2, 64, 64, 3), }; +static int armada_xp_pinctrl_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct mvebu_pinctrl_soc_info *soc = + platform_get_drvdata(pdev); + int i, nregs; + + nregs = DIV_ROUND_UP(soc->nmodes, MVEBU_MPPS_PER_REG); + + for (i = 0; i < nregs; i++) + mpp_saved_regs[i] = readl(mpp_base + i * 4); + + return 0; +} + +static int armada_xp_pinctrl_resume(struct platform_device *pdev) +{ + struct mvebu_pinctrl_soc_info *soc = + platform_get_drvdata(pdev); + int i, nregs; + + nregs = DIV_ROUND_UP(soc->nmodes, MVEBU_MPPS_PER_REG); + + for (i = 0; i < nregs; i++) + writel(mpp_saved_regs[i], mpp_base + i * 4); + + return 0; +} + static int armada_xp_pinctrl_probe(struct platform_device *pdev) { struct mvebu_pinctrl_soc_info *soc = &armada_xp_pinctrl_info; const struct of_device_id *match = of_match_device(armada_xp_pinctrl_of_match, &pdev->dev); struct resource *res; + int nregs; if (!match) return -ENODEV; @@ -459,6 +490,13 @@ static int armada_xp_pinctrl_probe(struct platform_device *pdev) break; } + nregs = DIV_ROUND_UP(soc->nmodes, MVEBU_MPPS_PER_REG); + + mpp_saved_regs = devm_kmalloc(&pdev->dev, nregs * sizeof(u32), + GFP_KERNEL); + if (!mpp_saved_regs) + return -ENOMEM; + pdev->dev.platform_data = soc; return mvebu_pinctrl_probe(pdev); @@ -476,6 +514,8 @@ static struct platform_driver armada_xp_pinctrl_driver = { }, .probe = armada_xp_pinctrl_probe, .remove = armada_xp_pinctrl_remove, + .suspend = armada_xp_pinctrl_suspend, + .resume = armada_xp_pinctrl_resume, }; module_platform_driver(armada_xp_pinctrl_driver); diff --git a/drivers/pinctrl/mvebu/pinctrl-kirkwood.c b/drivers/pinctrl/mvebu/pinctrl-kirkwood.c index dbc673cf713135..0f07dc554a1dd8 100644 --- a/drivers/pinctrl/mvebu/pinctrl-kirkwood.c +++ b/drivers/pinctrl/mvebu/pinctrl-kirkwood.c @@ -456,7 +456,7 @@ static struct mvebu_pinctrl_soc_info mv98dx4122_info = { .ngpioranges = ARRAY_SIZE(mv88f628x_gpio_ranges), }; -static struct of_device_id kirkwood_pinctrl_of_match[] = { +static const struct of_device_id kirkwood_pinctrl_of_match[] = { { .compatible = "marvell,88f6180-pinctrl", .data = &mv88f6180_info }, { .compatible = "marvell,88f6190-pinctrl", .data = &mv88f6190_info }, { .compatible = "marvell,88f6192-pinctrl", .data = &mv88f6192_info }, diff --git a/drivers/pinctrl/mvebu/pinctrl-orion.c b/drivers/pinctrl/mvebu/pinctrl-orion.c index 3a632efb56bb7a..3b7122d826e415 100644 --- a/drivers/pinctrl/mvebu/pinctrl-orion.c +++ b/drivers/pinctrl/mvebu/pinctrl-orion.c @@ -211,7 +211,7 @@ static struct mvebu_pinctrl_soc_info mv88f5281_info = { * There are multiple variants of the Orion SoCs, but in terms of pin * muxing, they are identical. */ -static struct of_device_id orion_pinctrl_of_match[] = { +static const struct of_device_id orion_pinctrl_of_match[] = { { .compatible = "marvell,88f5181l-pinctrl", .data = &mv88f5181l_info }, { .compatible = "marvell,88f5182-pinctrl", .data = &mv88f5182_info }, { .compatible = "marvell,88f5281-pinctrl", .data = &mv88f5281_info }, diff --git a/drivers/pinctrl/nomadik/pinctrl-abx500.c b/drivers/pinctrl/nomadik/pinctrl-abx500.c index 1806b24faa14e6..23db4c9ac76cc2 100644 --- a/drivers/pinctrl/nomadik/pinctrl-abx500.c +++ b/drivers/pinctrl/nomadik/pinctrl-abx500.c @@ -466,7 +466,7 @@ static int abx500_set_mode(struct pinctrl_dev *pctldev, struct gpio_chip *chip, break; default: - dev_dbg(pct->dev, "unknow alt_setting %d\n", alt_setting); + dev_dbg(pct->dev, "unknown alt_setting %d\n", alt_setting); return -EINVAL; } diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index 4db92f64b4de77..e63ad9fbd388a7 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -283,23 +283,40 @@ int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, struct device *dev = pctldev->dev; unsigned long *configs = NULL; unsigned num_configs = 0; - unsigned reserve; + unsigned reserve, strings_count; struct property *prop; const char *group; const char *subnode_target_type = "pins"; + ret = of_property_count_strings(np, "pins"); + if (ret < 0) { + ret = of_property_count_strings(np, "groups"); + if (ret < 0) + /* skip this node; may contain config child nodes */ + return 0; + if (type == PIN_MAP_TYPE_INVALID) + type = PIN_MAP_TYPE_CONFIGS_GROUP; + subnode_target_type = "groups"; + } else { + if (type == PIN_MAP_TYPE_INVALID) + type = PIN_MAP_TYPE_CONFIGS_PIN; + } + strings_count = ret; + ret = of_property_read_string(np, "function", &function); if (ret < 0) { /* EINVAL=missing, which is fine since it's optional */ if (ret != -EINVAL) - dev_err(dev, "could not parse property function\n"); + dev_err(dev, "%s: could not parse property function\n", + of_node_full_name(np)); function = NULL; } ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, &num_configs); if (ret < 0) { - dev_err(dev, "could not parse node property\n"); + dev_err(dev, "%s: could not parse node property\n", + of_node_full_name(np)); return ret; } @@ -309,21 +326,7 @@ int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, if (num_configs) reserve++; - ret = of_property_count_strings(np, "pins"); - if (ret < 0) { - ret = of_property_count_strings(np, "groups"); - if (ret < 0) { - dev_err(dev, "could not parse property pins/groups\n"); - goto exit; - } - if (type == PIN_MAP_TYPE_INVALID) - type = PIN_MAP_TYPE_CONFIGS_GROUP; - subnode_target_type = "groups"; - } else { - if (type == PIN_MAP_TYPE_INVALID) - type = PIN_MAP_TYPE_CONFIGS_PIN; - } - reserve *= ret; + reserve *= strings_count; ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, num_maps, reserve); @@ -367,15 +370,22 @@ int pinconf_generic_dt_node_to_map(struct pinctrl_dev *pctldev, *map = NULL; *num_maps = 0; + ret = pinconf_generic_dt_subnode_to_map(pctldev, np_config, map, + &reserved_maps, num_maps, type); + if (ret < 0) + goto exit; + for_each_child_of_node(np_config, np) { ret = pinconf_generic_dt_subnode_to_map(pctldev, np, map, &reserved_maps, num_maps, type); - if (ret < 0) { - pinctrl_utils_dt_free_map(pctldev, *map, *num_maps); - return ret; - } + if (ret < 0) + goto exit; } return 0; + +exit: + pinctrl_utils_dt_free_map(pctldev, *map, *num_maps); + return ret; } EXPORT_SYMBOL_GPL(pinconf_generic_dt_node_to_map); diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c new file mode 100644 index 00000000000000..7de3b64bf14289 --- /dev/null +++ b/drivers/pinctrl/pinctrl-amd.c @@ -0,0 +1,869 @@ +/* + * GPIO driver for AMD + * + * Copyright (c) 2014,2015 AMD Corporation. + * Authors: Ken Xue + * Wu, Jeff + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pinctrl-utils.h" +#include "pinctrl-amd.h" + +static inline struct amd_gpio *to_amd_gpio(struct gpio_chip *gc) +{ + return container_of(gc, struct amd_gpio, gc); +} + +static int amd_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + unsigned long flags; + u32 pin_reg; + struct amd_gpio *gpio_dev = to_amd_gpio(gc); + + spin_lock_irqsave(&gpio_dev->lock, flags); + pin_reg = readl(gpio_dev->base + offset * 4); + /* + * Suppose BIOS or Bootloader sets specific debounce for the + * GPIO. if not, set debounce to be 2.75ms and remove glitch. + */ + if ((pin_reg & DB_TMR_OUT_MASK) == 0) { + pin_reg |= 0xf; + pin_reg |= BIT(DB_TMR_OUT_UNIT_OFF); + pin_reg |= DB_TYPE_REMOVE_GLITCH << DB_CNTRL_OFF; + pin_reg &= ~BIT(DB_TMR_LARGE_OFF); + } + + pin_reg &= ~BIT(OUTPUT_ENABLE_OFF); + writel(pin_reg, gpio_dev->base + offset * 4); + spin_unlock_irqrestore(&gpio_dev->lock, flags); + + return 0; +} + +static int amd_gpio_direction_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + u32 pin_reg; + unsigned long flags; + struct amd_gpio *gpio_dev = to_amd_gpio(gc); + + spin_lock_irqsave(&gpio_dev->lock, flags); + pin_reg = readl(gpio_dev->base + offset * 4); + pin_reg |= BIT(OUTPUT_ENABLE_OFF); + if (value) + pin_reg |= BIT(OUTPUT_VALUE_OFF); + else + pin_reg &= ~BIT(OUTPUT_VALUE_OFF); + writel(pin_reg, gpio_dev->base + offset * 4); + spin_unlock_irqrestore(&gpio_dev->lock, flags); + + return 0; +} + +static int amd_gpio_get_value(struct gpio_chip *gc, unsigned offset) +{ + u32 pin_reg; + unsigned long flags; + struct amd_gpio *gpio_dev = to_amd_gpio(gc); + + spin_lock_irqsave(&gpio_dev->lock, flags); + pin_reg = readl(gpio_dev->base + offset * 4); + spin_unlock_irqrestore(&gpio_dev->lock, flags); + + return !!(pin_reg & BIT(PIN_STS_OFF)); +} + +static void amd_gpio_set_value(struct gpio_chip *gc, unsigned offset, int value) +{ + u32 pin_reg; + unsigned long flags; + struct amd_gpio *gpio_dev = to_amd_gpio(gc); + + spin_lock_irqsave(&gpio_dev->lock, flags); + pin_reg = readl(gpio_dev->base + offset * 4); + if (value) + pin_reg |= BIT(OUTPUT_VALUE_OFF); + else + pin_reg &= ~BIT(OUTPUT_VALUE_OFF); + writel(pin_reg, gpio_dev->base + offset * 4); + spin_unlock_irqrestore(&gpio_dev->lock, flags); +} + +static int amd_gpio_set_debounce(struct gpio_chip *gc, unsigned offset, + unsigned debounce) +{ + u32 time; + u32 pin_reg; + int ret = 0; + unsigned long flags; + struct amd_gpio *gpio_dev = to_amd_gpio(gc); + + spin_lock_irqsave(&gpio_dev->lock, flags); + pin_reg = readl(gpio_dev->base + offset * 4); + + if (debounce) { + pin_reg |= DB_TYPE_REMOVE_GLITCH << DB_CNTRL_OFF; + pin_reg &= ~DB_TMR_OUT_MASK; + /* + Debounce Debounce Timer Max + TmrLarge TmrOutUnit Unit Debounce + Time + 0 0 61 usec (2 RtcClk) 976 usec + 0 1 244 usec (8 RtcClk) 3.9 msec + 1 0 15.6 msec (512 RtcClk) 250 msec + 1 1 62.5 msec (2048 RtcClk) 1 sec + */ + + if (debounce < 61) { + pin_reg |= 1; + pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF); + pin_reg &= ~BIT(DB_TMR_LARGE_OFF); + } else if (debounce < 976) { + time = debounce / 61; + pin_reg |= time & DB_TMR_OUT_MASK; + pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF); + pin_reg &= ~BIT(DB_TMR_LARGE_OFF); + } else if (debounce < 3900) { + time = debounce / 244; + pin_reg |= time & DB_TMR_OUT_MASK; + pin_reg |= BIT(DB_TMR_OUT_UNIT_OFF); + pin_reg &= ~BIT(DB_TMR_LARGE_OFF); + } else if (debounce < 250000) { + time = debounce / 15600; + pin_reg |= time & DB_TMR_OUT_MASK; + pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF); + pin_reg |= BIT(DB_TMR_LARGE_OFF); + } else if (debounce < 1000000) { + time = debounce / 62500; + pin_reg |= time & DB_TMR_OUT_MASK; + pin_reg |= BIT(DB_TMR_OUT_UNIT_OFF); + pin_reg |= BIT(DB_TMR_LARGE_OFF); + } else { + pin_reg &= ~DB_CNTRl_MASK; + ret = -EINVAL; + } + } else { + pin_reg &= ~BIT(DB_TMR_OUT_UNIT_OFF); + pin_reg &= ~BIT(DB_TMR_LARGE_OFF); + pin_reg &= ~DB_TMR_OUT_MASK; + pin_reg &= ~DB_CNTRl_MASK; + } + writel(pin_reg, gpio_dev->base + offset * 4); + spin_unlock_irqrestore(&gpio_dev->lock, flags); + + return ret; +} + +#ifdef CONFIG_DEBUG_FS +static void amd_gpio_dbg_show(struct seq_file *s, struct gpio_chip *gc) +{ + u32 pin_reg; + unsigned long flags; + unsigned int bank, i, pin_num; + struct amd_gpio *gpio_dev = to_amd_gpio(gc); + + char *level_trig; + char *active_level; + char *interrupt_enable; + char *interrupt_mask; + char *wake_cntrl0; + char *wake_cntrl1; + char *wake_cntrl2; + char *pin_sts; + char *pull_up_sel; + char *pull_up_enable; + char *pull_down_enable; + char *output_value; + char *output_enable; + + for (bank = 0; bank < AMD_GPIO_TOTAL_BANKS; bank++) { + seq_printf(s, "GPIO bank%d\t", bank); + + switch (bank) { + case 0: + i = 0; + pin_num = AMD_GPIO_PINS_BANK0; + break; + case 1: + i = 64; + pin_num = AMD_GPIO_PINS_BANK1 + i; + break; + case 2: + i = 128; + pin_num = AMD_GPIO_PINS_BANK2 + i; + break; + } + + for (; i < pin_num; i++) { + seq_printf(s, "pin%d\t", i); + spin_lock_irqsave(&gpio_dev->lock, flags); + pin_reg = readl(gpio_dev->base + i * 4); + spin_unlock_irqrestore(&gpio_dev->lock, flags); + + if (pin_reg & BIT(INTERRUPT_ENABLE_OFF)) { + interrupt_enable = "interrupt is enabled|"; + + if (!(pin_reg & BIT(ACTIVE_LEVEL_OFF)) + && !(pin_reg & BIT(ACTIVE_LEVEL_OFF+1))) + active_level = "Active low|"; + else if (pin_reg & BIT(ACTIVE_LEVEL_OFF) + && !(pin_reg & BIT(ACTIVE_LEVEL_OFF+1))) + active_level = "Active high|"; + else if (!(pin_reg & BIT(ACTIVE_LEVEL_OFF)) + && pin_reg & BIT(ACTIVE_LEVEL_OFF+1)) + active_level = "Active on both|"; + else + active_level = "Unknow Active level|"; + + if (pin_reg & BIT(LEVEL_TRIG_OFF)) + level_trig = "Level trigger|"; + else + level_trig = "Edge trigger|"; + + } else { + interrupt_enable = + "interrupt is disabled|"; + active_level = " "; + level_trig = " "; + } + + if (pin_reg & BIT(INTERRUPT_MASK_OFF)) + interrupt_mask = + "interrupt is unmasked|"; + else + interrupt_mask = + "interrupt is masked|"; + + if (pin_reg & BIT(WAKE_CNTRL_OFF)) + wake_cntrl0 = "enable wakeup in S0i3 state|"; + else + wake_cntrl0 = "disable wakeup in S0i3 state|"; + + if (pin_reg & BIT(WAKE_CNTRL_OFF)) + wake_cntrl1 = "enable wakeup in S3 state|"; + else + wake_cntrl1 = "disable wakeup in S3 state|"; + + if (pin_reg & BIT(WAKE_CNTRL_OFF)) + wake_cntrl2 = "enable wakeup in S4/S5 state|"; + else + wake_cntrl2 = "disable wakeup in S4/S5 state|"; + + if (pin_reg & BIT(PULL_UP_ENABLE_OFF)) { + pull_up_enable = "pull-up is enabled|"; + if (pin_reg & BIT(PULL_UP_SEL_OFF)) + pull_up_sel = "8k pull-up|"; + else + pull_up_sel = "4k pull-up|"; + } else { + pull_up_enable = "pull-up is disabled|"; + pull_up_sel = " "; + } + + if (pin_reg & BIT(PULL_DOWN_ENABLE_OFF)) + pull_down_enable = "pull-down is enabled|"; + else + pull_down_enable = "Pull-down is disabled|"; + + if (pin_reg & BIT(OUTPUT_ENABLE_OFF)) { + pin_sts = " "; + output_enable = "output is enabled|"; + if (pin_reg & BIT(OUTPUT_VALUE_OFF)) + output_value = "output is high|"; + else + output_value = "output is low|"; + } else { + output_enable = "output is disabled|"; + output_value = " "; + + if (pin_reg & BIT(PIN_STS_OFF)) + pin_sts = "input is high|"; + else + pin_sts = "input is low|"; + } + + seq_printf(s, "%s %s %s %s %s %s\n" + " %s %s %s %s %s %s %s 0x%x\n", + level_trig, active_level, interrupt_enable, + interrupt_mask, wake_cntrl0, wake_cntrl1, + wake_cntrl2, pin_sts, pull_up_sel, + pull_up_enable, pull_down_enable, + output_value, output_enable, pin_reg); + } + } +} +#else +#define amd_gpio_dbg_show NULL +#endif + +static void amd_gpio_irq_enable(struct irq_data *d) +{ + u32 pin_reg; + unsigned long flags; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct amd_gpio *gpio_dev = to_amd_gpio(gc); + + spin_lock_irqsave(&gpio_dev->lock, flags); + pin_reg = readl(gpio_dev->base + (d->hwirq)*4); + /* + Suppose BIOS or Bootloader sets specific debounce for the + GPIO. if not, set debounce to be 2.75ms. + */ + if ((pin_reg & DB_TMR_OUT_MASK) == 0) { + pin_reg |= 0xf; + pin_reg |= BIT(DB_TMR_OUT_UNIT_OFF); + pin_reg &= ~BIT(DB_TMR_LARGE_OFF); + } + pin_reg |= BIT(INTERRUPT_ENABLE_OFF); + pin_reg |= BIT(INTERRUPT_MASK_OFF); + writel(pin_reg, gpio_dev->base + (d->hwirq)*4); + spin_unlock_irqrestore(&gpio_dev->lock, flags); +} + +static void amd_gpio_irq_disable(struct irq_data *d) +{ + u32 pin_reg; + unsigned long flags; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct amd_gpio *gpio_dev = to_amd_gpio(gc); + + spin_lock_irqsave(&gpio_dev->lock, flags); + pin_reg = readl(gpio_dev->base + (d->hwirq)*4); + pin_reg &= ~BIT(INTERRUPT_ENABLE_OFF); + pin_reg &= ~BIT(INTERRUPT_MASK_OFF); + writel(pin_reg, gpio_dev->base + (d->hwirq)*4); + spin_unlock_irqrestore(&gpio_dev->lock, flags); +} + +static void amd_gpio_irq_mask(struct irq_data *d) +{ + u32 pin_reg; + unsigned long flags; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct amd_gpio *gpio_dev = to_amd_gpio(gc); + + spin_lock_irqsave(&gpio_dev->lock, flags); + pin_reg = readl(gpio_dev->base + (d->hwirq)*4); + pin_reg &= ~BIT(INTERRUPT_MASK_OFF); + writel(pin_reg, gpio_dev->base + (d->hwirq)*4); + spin_unlock_irqrestore(&gpio_dev->lock, flags); +} + +static void amd_gpio_irq_unmask(struct irq_data *d) +{ + u32 pin_reg; + unsigned long flags; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct amd_gpio *gpio_dev = to_amd_gpio(gc); + + spin_lock_irqsave(&gpio_dev->lock, flags); + pin_reg = readl(gpio_dev->base + (d->hwirq)*4); + pin_reg |= BIT(INTERRUPT_MASK_OFF); + writel(pin_reg, gpio_dev->base + (d->hwirq)*4); + spin_unlock_irqrestore(&gpio_dev->lock, flags); +} + +static void amd_gpio_irq_eoi(struct irq_data *d) +{ + u32 reg; + unsigned long flags; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct amd_gpio *gpio_dev = to_amd_gpio(gc); + + spin_lock_irqsave(&gpio_dev->lock, flags); + reg = readl(gpio_dev->base + WAKE_INT_MASTER_REG); + reg |= EOI_MASK; + writel(reg, gpio_dev->base + WAKE_INT_MASTER_REG); + spin_unlock_irqrestore(&gpio_dev->lock, flags); +} + +static int amd_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + int ret = 0; + u32 pin_reg; + unsigned long flags; + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct amd_gpio *gpio_dev = to_amd_gpio(gc); + + spin_lock_irqsave(&gpio_dev->lock, flags); + pin_reg = readl(gpio_dev->base + (d->hwirq)*4); + + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + pin_reg &= ~BIT(LEVEL_TRIG_OFF); + pin_reg &= ~(ACTIVE_LEVEL_MASK << ACTIVE_LEVEL_OFF); + pin_reg |= ACTIVE_HIGH << ACTIVE_LEVEL_OFF; + pin_reg |= DB_TYPE_REMOVE_GLITCH << DB_CNTRL_OFF; + __irq_set_handler_locked(d->irq, handle_edge_irq); + break; + + case IRQ_TYPE_EDGE_FALLING: + pin_reg &= ~BIT(LEVEL_TRIG_OFF); + pin_reg &= ~(ACTIVE_LEVEL_MASK << ACTIVE_LEVEL_OFF); + pin_reg |= ACTIVE_LOW << ACTIVE_LEVEL_OFF; + pin_reg |= DB_TYPE_REMOVE_GLITCH << DB_CNTRL_OFF; + __irq_set_handler_locked(d->irq, handle_edge_irq); + break; + + case IRQ_TYPE_EDGE_BOTH: + pin_reg &= ~BIT(LEVEL_TRIG_OFF); + pin_reg &= ~(ACTIVE_LEVEL_MASK << ACTIVE_LEVEL_OFF); + pin_reg |= BOTH_EADGE << ACTIVE_LEVEL_OFF; + pin_reg |= DB_TYPE_REMOVE_GLITCH << DB_CNTRL_OFF; + __irq_set_handler_locked(d->irq, handle_edge_irq); + break; + + case IRQ_TYPE_LEVEL_HIGH: + pin_reg |= LEVEL_TRIGGER << LEVEL_TRIG_OFF; + pin_reg &= ~(ACTIVE_LEVEL_MASK << ACTIVE_LEVEL_OFF); + pin_reg |= ACTIVE_HIGH << ACTIVE_LEVEL_OFF; + pin_reg &= ~(DB_CNTRl_MASK << DB_CNTRL_OFF); + pin_reg |= DB_TYPE_PRESERVE_LOW_GLITCH << DB_CNTRL_OFF; + __irq_set_handler_locked(d->irq, handle_level_irq); + break; + + case IRQ_TYPE_LEVEL_LOW: + pin_reg |= LEVEL_TRIGGER << LEVEL_TRIG_OFF; + pin_reg &= ~(ACTIVE_LEVEL_MASK << ACTIVE_LEVEL_OFF); + pin_reg |= ACTIVE_LOW << ACTIVE_LEVEL_OFF; + pin_reg &= ~(DB_CNTRl_MASK << DB_CNTRL_OFF); + pin_reg |= DB_TYPE_PRESERVE_HIGH_GLITCH << DB_CNTRL_OFF; + __irq_set_handler_locked(d->irq, handle_level_irq); + break; + + case IRQ_TYPE_NONE: + break; + + default: + dev_err(&gpio_dev->pdev->dev, "Invalid type value\n"); + ret = -EINVAL; + } + + pin_reg |= CLR_INTR_STAT << INTERRUPT_STS_OFF; + writel(pin_reg, gpio_dev->base + (d->hwirq)*4); + spin_unlock_irqrestore(&gpio_dev->lock, flags); + + return ret; +} + +static void amd_irq_ack(struct irq_data *d) +{ + /* + * based on HW design,there is no need to ack HW + * before handle current irq. But this routine is + * necessary for handle_edge_irq + */ +} + +static struct irq_chip amd_gpio_irqchip = { + .name = "amd_gpio", + .irq_ack = amd_irq_ack, + .irq_enable = amd_gpio_irq_enable, + .irq_disable = amd_gpio_irq_disable, + .irq_mask = amd_gpio_irq_mask, + .irq_unmask = amd_gpio_irq_unmask, + .irq_eoi = amd_gpio_irq_eoi, + .irq_set_type = amd_gpio_irq_set_type, +}; + +static void amd_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + u32 i; + u32 off; + u32 reg; + u32 pin_reg; + u64 reg64; + int handled = 0; + unsigned long flags; + struct irq_chip *chip = irq_get_chip(irq); + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct amd_gpio *gpio_dev = to_amd_gpio(gc); + + chained_irq_enter(chip, desc); + /*enable GPIO interrupt again*/ + spin_lock_irqsave(&gpio_dev->lock, flags); + reg = readl(gpio_dev->base + WAKE_INT_STATUS_REG1); + reg64 = reg; + reg64 = reg64 << 32; + + reg = readl(gpio_dev->base + WAKE_INT_STATUS_REG0); + reg64 |= reg; + spin_unlock_irqrestore(&gpio_dev->lock, flags); + + /* + * first 46 bits indicates interrupt status. + * one bit represents four interrupt sources. + */ + for (off = 0; off < 46 ; off++) { + if (reg64 & BIT(off)) { + for (i = 0; i < 4; i++) { + pin_reg = readl(gpio_dev->base + + (off * 4 + i) * 4); + if ((pin_reg & BIT(INTERRUPT_STS_OFF)) || + (pin_reg & BIT(WAKE_STS_OFF))) { + irq = irq_find_mapping(gc->irqdomain, + off * 4 + i); + generic_handle_irq(irq); + writel(pin_reg, + gpio_dev->base + + (off * 4 + i) * 4); + handled++; + } + } + } + } + + if (handled == 0) + handle_bad_irq(irq, desc); + + spin_lock_irqsave(&gpio_dev->lock, flags); + reg = readl(gpio_dev->base + WAKE_INT_MASTER_REG); + reg |= EOI_MASK; + writel(reg, gpio_dev->base + WAKE_INT_MASTER_REG); + spin_unlock_irqrestore(&gpio_dev->lock, flags); + + chained_irq_exit(chip, desc); +} + +static int amd_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct amd_gpio *gpio_dev = pinctrl_dev_get_drvdata(pctldev); + + return gpio_dev->ngroups; +} + +static const char *amd_get_group_name(struct pinctrl_dev *pctldev, + unsigned group) +{ + struct amd_gpio *gpio_dev = pinctrl_dev_get_drvdata(pctldev); + + return gpio_dev->groups[group].name; +} + +static int amd_get_group_pins(struct pinctrl_dev *pctldev, + unsigned group, + const unsigned **pins, + unsigned *num_pins) +{ + struct amd_gpio *gpio_dev = pinctrl_dev_get_drvdata(pctldev); + + *pins = gpio_dev->groups[group].pins; + *num_pins = gpio_dev->groups[group].npins; + return 0; +} + +static const struct pinctrl_ops amd_pinctrl_ops = { + .get_groups_count = amd_get_groups_count, + .get_group_name = amd_get_group_name, + .get_group_pins = amd_get_group_pins, +#ifdef CONFIG_OF + .dt_node_to_map = pinconf_generic_dt_node_to_map_group, + .dt_free_map = pinctrl_utils_dt_free_map, +#endif +}; + +static int amd_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int pin, + unsigned long *config) +{ + u32 pin_reg; + unsigned arg; + unsigned long flags; + struct amd_gpio *gpio_dev = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + + spin_lock_irqsave(&gpio_dev->lock, flags); + pin_reg = readl(gpio_dev->base + pin*4); + spin_unlock_irqrestore(&gpio_dev->lock, flags); + switch (param) { + case PIN_CONFIG_INPUT_DEBOUNCE: + arg = pin_reg & DB_TMR_OUT_MASK; + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + arg = (pin_reg >> PULL_DOWN_ENABLE_OFF) & BIT(0); + break; + + case PIN_CONFIG_BIAS_PULL_UP: + arg = (pin_reg >> PULL_UP_SEL_OFF) & (BIT(0) | BIT(1)); + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + arg = (pin_reg >> DRV_STRENGTH_SEL_OFF) & DRV_STRENGTH_SEL_MASK; + break; + + default: + dev_err(&gpio_dev->pdev->dev, "Invalid config param %04x\n", + param); + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int amd_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned num_configs) +{ + int i; + u32 arg; + int ret = 0; + u32 pin_reg; + unsigned long flags; + enum pin_config_param param; + struct amd_gpio *gpio_dev = pinctrl_dev_get_drvdata(pctldev); + + spin_lock_irqsave(&gpio_dev->lock, flags); + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + pin_reg = readl(gpio_dev->base + pin*4); + + switch (param) { + case PIN_CONFIG_INPUT_DEBOUNCE: + pin_reg &= ~DB_TMR_OUT_MASK; + pin_reg |= arg & DB_TMR_OUT_MASK; + break; + + case PIN_CONFIG_BIAS_PULL_DOWN: + pin_reg &= ~BIT(PULL_DOWN_ENABLE_OFF); + pin_reg |= (arg & BIT(0)) << PULL_DOWN_ENABLE_OFF; + break; + + case PIN_CONFIG_BIAS_PULL_UP: + pin_reg &= ~BIT(PULL_UP_SEL_OFF); + pin_reg |= (arg & BIT(0)) << PULL_UP_SEL_OFF; + pin_reg &= ~BIT(PULL_UP_ENABLE_OFF); + pin_reg |= ((arg>>1) & BIT(0)) << PULL_UP_ENABLE_OFF; + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + pin_reg &= ~(DRV_STRENGTH_SEL_MASK + << DRV_STRENGTH_SEL_OFF); + pin_reg |= (arg & DRV_STRENGTH_SEL_MASK) + << DRV_STRENGTH_SEL_OFF; + break; + + default: + dev_err(&gpio_dev->pdev->dev, + "Invalid config param %04x\n", param); + ret = -ENOTSUPP; + } + + writel(pin_reg, gpio_dev->base + pin*4); + } + spin_unlock_irqrestore(&gpio_dev->lock, flags); + + return ret; +} + +static int amd_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned int group, + unsigned long *config) +{ + const unsigned *pins; + unsigned npins; + int ret; + + ret = amd_get_group_pins(pctldev, group, &pins, &npins); + if (ret) + return ret; + + if (amd_pinconf_get(pctldev, pins[0], config)) + return -ENOTSUPP; + + return 0; +} + +static int amd_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned group, unsigned long *configs, + unsigned num_configs) +{ + const unsigned *pins; + unsigned npins; + int i, ret; + + ret = amd_get_group_pins(pctldev, group, &pins, &npins); + if (ret) + return ret; + for (i = 0; i < npins; i++) { + if (amd_pinconf_set(pctldev, pins[i], configs, num_configs)) + return -ENOTSUPP; + } + return 0; +} + +static const struct pinconf_ops amd_pinconf_ops = { + .pin_config_get = amd_pinconf_get, + .pin_config_set = amd_pinconf_set, + .pin_config_group_get = amd_pinconf_group_get, + .pin_config_group_set = amd_pinconf_group_set, +}; + +static struct pinctrl_desc amd_pinctrl_desc = { + .pins = kerncz_pins, + .npins = ARRAY_SIZE(kerncz_pins), + .pctlops = &amd_pinctrl_ops, + .confops = &amd_pinconf_ops, + .owner = THIS_MODULE, +}; + +static int amd_gpio_probe(struct platform_device *pdev) +{ + int ret = 0; + int irq_base; + struct resource *res; + struct amd_gpio *gpio_dev; + + gpio_dev = devm_kzalloc(&pdev->dev, + sizeof(struct amd_gpio), GFP_KERNEL); + if (!gpio_dev) + return -ENOMEM; + + spin_lock_init(&gpio_dev->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get gpio io resource.\n"); + return -EINVAL; + } + + gpio_dev->base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (IS_ERR(gpio_dev->base)) + return PTR_ERR(gpio_dev->base); + + irq_base = platform_get_irq(pdev, 0); + if (irq_base < 0) { + dev_err(&pdev->dev, "Failed to get gpio IRQ.\n"); + return -EINVAL; + } + + gpio_dev->pdev = pdev; + gpio_dev->gc.direction_input = amd_gpio_direction_input; + gpio_dev->gc.direction_output = amd_gpio_direction_output; + gpio_dev->gc.get = amd_gpio_get_value; + gpio_dev->gc.set = amd_gpio_set_value; + gpio_dev->gc.set_debounce = amd_gpio_set_debounce; + gpio_dev->gc.dbg_show = amd_gpio_dbg_show; + + gpio_dev->gc.base = 0; + gpio_dev->gc.label = pdev->name; + gpio_dev->gc.owner = THIS_MODULE; + gpio_dev->gc.dev = &pdev->dev; + gpio_dev->gc.ngpio = TOTAL_NUMBER_OF_PINS; +#if defined(CONFIG_OF_GPIO) + gpio_dev->gc.of_node = pdev->dev.of_node; +#endif + + gpio_dev->groups = kerncz_groups; + gpio_dev->ngroups = ARRAY_SIZE(kerncz_groups); + + amd_pinctrl_desc.name = dev_name(&pdev->dev); + gpio_dev->pctrl = pinctrl_register(&amd_pinctrl_desc, + &pdev->dev, gpio_dev); + if (!gpio_dev->pctrl) { + dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); + return -ENODEV; + } + + ret = gpiochip_add(&gpio_dev->gc); + if (ret) + goto out1; + + ret = gpiochip_add_pin_range(&gpio_dev->gc, dev_name(&pdev->dev), + 0, 0, TOTAL_NUMBER_OF_PINS); + if (ret) { + dev_err(&pdev->dev, "Failed to add pin range\n"); + goto out2; + } + + ret = gpiochip_irqchip_add(&gpio_dev->gc, + &amd_gpio_irqchip, + 0, + handle_simple_irq, + IRQ_TYPE_NONE); + if (ret) { + dev_err(&pdev->dev, "could not add irqchip\n"); + ret = -ENODEV; + goto out2; + } + + gpiochip_set_chained_irqchip(&gpio_dev->gc, + &amd_gpio_irqchip, + irq_base, + amd_gpio_irq_handler); + + platform_set_drvdata(pdev, gpio_dev); + + dev_dbg(&pdev->dev, "amd gpio driver loaded\n"); + return ret; + +out2: + gpiochip_remove(&gpio_dev->gc); + +out1: + pinctrl_unregister(gpio_dev->pctrl); + return ret; +} + +static int amd_gpio_remove(struct platform_device *pdev) +{ + struct amd_gpio *gpio_dev; + + gpio_dev = platform_get_drvdata(pdev); + + gpiochip_remove(&gpio_dev->gc); + pinctrl_unregister(gpio_dev->pctrl); + + return 0; +} + +static const struct acpi_device_id amd_gpio_acpi_match[] = { + { "AMD0030", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, amd_gpio_acpi_match); + +static struct platform_driver amd_gpio_driver = { + .driver = { + .name = "amd_gpio", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(amd_gpio_acpi_match), + }, + .probe = amd_gpio_probe, + .remove = amd_gpio_remove, +}; + +module_platform_driver(amd_gpio_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Ken Xue , Jeff Wu "); +MODULE_DESCRIPTION("AMD GPIO pinctrl driver"); diff --git a/drivers/pinctrl/pinctrl-amd.h b/drivers/pinctrl/pinctrl-amd.h new file mode 100644 index 00000000000000..7bfea47dbb472f --- /dev/null +++ b/drivers/pinctrl/pinctrl-amd.h @@ -0,0 +1,261 @@ +/* + * GPIO driver for AMD + * + * Copyright (c) 2014,2015 Ken Xue + * Jeff Wu + * + * 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. + * + */ + +#ifndef _PINCTRL_AMD_H +#define _PINCTRL_AMD_H + +#define TOTAL_NUMBER_OF_PINS 192 +#define AMD_GPIO_PINS_PER_BANK 64 +#define AMD_GPIO_TOTAL_BANKS 3 + +#define AMD_GPIO_PINS_BANK0 63 +#define AMD_GPIO_PINS_BANK1 64 +#define AMD_GPIO_PINS_BANK2 56 + +#define WAKE_INT_MASTER_REG 0xfc +#define EOI_MASK (1 << 29) + +#define WAKE_INT_STATUS_REG0 0x2f8 +#define WAKE_INT_STATUS_REG1 0x2fc + +#define DB_TMR_OUT_OFF 0 +#define DB_TMR_OUT_UNIT_OFF 4 +#define DB_CNTRL_OFF 5 +#define DB_TMR_LARGE_OFF 7 +#define LEVEL_TRIG_OFF 8 +#define ACTIVE_LEVEL_OFF 9 +#define INTERRUPT_ENABLE_OFF 11 +#define INTERRUPT_MASK_OFF 12 +#define WAKE_CNTRL_OFF 13 +#define PIN_STS_OFF 16 +#define DRV_STRENGTH_SEL_OFF 17 +#define PULL_UP_SEL_OFF 19 +#define PULL_UP_ENABLE_OFF 20 +#define PULL_DOWN_ENABLE_OFF 21 +#define OUTPUT_VALUE_OFF 22 +#define OUTPUT_ENABLE_OFF 23 +#define SW_CNTRL_IN_OFF 24 +#define SW_CNTRL_EN_OFF 25 +#define INTERRUPT_STS_OFF 28 +#define WAKE_STS_OFF 29 + +#define DB_TMR_OUT_MASK 0xFUL +#define DB_CNTRl_MASK 0x3UL +#define ACTIVE_LEVEL_MASK 0x3UL +#define DRV_STRENGTH_SEL_MASK 0x3UL + +#define DB_TYPE_NO_DEBOUNCE 0x0UL +#define DB_TYPE_PRESERVE_LOW_GLITCH 0x1UL +#define DB_TYPE_PRESERVE_HIGH_GLITCH 0x2UL +#define DB_TYPE_REMOVE_GLITCH 0x3UL + +#define EDGE_TRAGGER 0x0UL +#define LEVEL_TRIGGER 0x1UL + +#define ACTIVE_HIGH 0x0UL +#define ACTIVE_LOW 0x1UL +#define BOTH_EADGE 0x2UL + +#define ENABLE_INTERRUPT 0x1UL +#define DISABLE_INTERRUPT 0x0UL + +#define ENABLE_INTERRUPT_MASK 0x0UL +#define DISABLE_INTERRUPT_MASK 0x1UL + +#define CLR_INTR_STAT 0x1UL + +struct amd_pingroup { + const char *name; + const unsigned *pins; + unsigned npins; +}; + +struct amd_function { + const char *name; + const char * const *groups; + unsigned ngroups; +}; + +struct amd_gpio { + spinlock_t lock; + void __iomem *base; + + const struct amd_pingroup *groups; + u32 ngroups; + struct pinctrl_dev *pctrl; + struct gpio_chip gc; + struct resource *res; + struct platform_device *pdev; +}; + +/* KERNCZ configuration*/ +static const struct pinctrl_pin_desc kerncz_pins[] = { + PINCTRL_PIN(0, "GPIO_0"), + PINCTRL_PIN(1, "GPIO_1"), + PINCTRL_PIN(2, "GPIO_2"), + PINCTRL_PIN(3, "GPIO_3"), + PINCTRL_PIN(4, "GPIO_4"), + PINCTRL_PIN(5, "GPIO_5"), + PINCTRL_PIN(6, "GPIO_6"), + PINCTRL_PIN(7, "GPIO_7"), + PINCTRL_PIN(8, "GPIO_8"), + PINCTRL_PIN(9, "GPIO_9"), + PINCTRL_PIN(10, "GPIO_10"), + PINCTRL_PIN(11, "GPIO_11"), + PINCTRL_PIN(12, "GPIO_12"), + PINCTRL_PIN(13, "GPIO_13"), + PINCTRL_PIN(14, "GPIO_14"), + PINCTRL_PIN(15, "GPIO_15"), + PINCTRL_PIN(16, "GPIO_16"), + PINCTRL_PIN(17, "GPIO_17"), + PINCTRL_PIN(18, "GPIO_18"), + PINCTRL_PIN(19, "GPIO_19"), + PINCTRL_PIN(20, "GPIO_20"), + PINCTRL_PIN(23, "GPIO_23"), + PINCTRL_PIN(24, "GPIO_24"), + PINCTRL_PIN(25, "GPIO_25"), + PINCTRL_PIN(26, "GPIO_26"), + PINCTRL_PIN(39, "GPIO_39"), + PINCTRL_PIN(40, "GPIO_40"), + PINCTRL_PIN(43, "GPIO_42"), + PINCTRL_PIN(46, "GPIO_46"), + PINCTRL_PIN(47, "GPIO_47"), + PINCTRL_PIN(48, "GPIO_48"), + PINCTRL_PIN(49, "GPIO_49"), + PINCTRL_PIN(50, "GPIO_50"), + PINCTRL_PIN(51, "GPIO_51"), + PINCTRL_PIN(52, "GPIO_52"), + PINCTRL_PIN(53, "GPIO_53"), + PINCTRL_PIN(54, "GPIO_54"), + PINCTRL_PIN(55, "GPIO_55"), + PINCTRL_PIN(56, "GPIO_56"), + PINCTRL_PIN(57, "GPIO_57"), + PINCTRL_PIN(58, "GPIO_58"), + PINCTRL_PIN(59, "GPIO_59"), + PINCTRL_PIN(60, "GPIO_60"), + PINCTRL_PIN(61, "GPIO_61"), + PINCTRL_PIN(62, "GPIO_62"), + PINCTRL_PIN(64, "GPIO_64"), + PINCTRL_PIN(65, "GPIO_65"), + PINCTRL_PIN(66, "GPIO_66"), + PINCTRL_PIN(68, "GPIO_68"), + PINCTRL_PIN(69, "GPIO_69"), + PINCTRL_PIN(70, "GPIO_70"), + PINCTRL_PIN(71, "GPIO_71"), + PINCTRL_PIN(72, "GPIO_72"), + PINCTRL_PIN(74, "GPIO_74"), + PINCTRL_PIN(75, "GPIO_75"), + PINCTRL_PIN(76, "GPIO_76"), + PINCTRL_PIN(84, "GPIO_84"), + PINCTRL_PIN(85, "GPIO_85"), + PINCTRL_PIN(86, "GPIO_86"), + PINCTRL_PIN(87, "GPIO_87"), + PINCTRL_PIN(88, "GPIO_88"), + PINCTRL_PIN(89, "GPIO_89"), + PINCTRL_PIN(90, "GPIO_90"), + PINCTRL_PIN(91, "GPIO_91"), + PINCTRL_PIN(92, "GPIO_92"), + PINCTRL_PIN(93, "GPIO_93"), + PINCTRL_PIN(95, "GPIO_95"), + PINCTRL_PIN(96, "GPIO_96"), + PINCTRL_PIN(97, "GPIO_97"), + PINCTRL_PIN(98, "GPIO_98"), + PINCTRL_PIN(99, "GPIO_99"), + PINCTRL_PIN(100, "GPIO_100"), + PINCTRL_PIN(101, "GPIO_101"), + PINCTRL_PIN(102, "GPIO_102"), + PINCTRL_PIN(113, "GPIO_113"), + PINCTRL_PIN(114, "GPIO_114"), + PINCTRL_PIN(115, "GPIO_115"), + PINCTRL_PIN(116, "GPIO_116"), + PINCTRL_PIN(117, "GPIO_117"), + PINCTRL_PIN(118, "GPIO_118"), + PINCTRL_PIN(119, "GPIO_119"), + PINCTRL_PIN(120, "GPIO_120"), + PINCTRL_PIN(121, "GPIO_121"), + PINCTRL_PIN(122, "GPIO_122"), + PINCTRL_PIN(126, "GPIO_126"), + PINCTRL_PIN(129, "GPIO_129"), + PINCTRL_PIN(130, "GPIO_130"), + PINCTRL_PIN(131, "GPIO_131"), + PINCTRL_PIN(132, "GPIO_132"), + PINCTRL_PIN(133, "GPIO_133"), + PINCTRL_PIN(135, "GPIO_135"), + PINCTRL_PIN(136, "GPIO_136"), + PINCTRL_PIN(137, "GPIO_137"), + PINCTRL_PIN(138, "GPIO_138"), + PINCTRL_PIN(139, "GPIO_139"), + PINCTRL_PIN(140, "GPIO_140"), + PINCTRL_PIN(141, "GPIO_141"), + PINCTRL_PIN(142, "GPIO_142"), + PINCTRL_PIN(143, "GPIO_143"), + PINCTRL_PIN(144, "GPIO_144"), + PINCTRL_PIN(145, "GPIO_145"), + PINCTRL_PIN(146, "GPIO_146"), + PINCTRL_PIN(147, "GPIO_147"), + PINCTRL_PIN(148, "GPIO_148"), + PINCTRL_PIN(166, "GPIO_166"), + PINCTRL_PIN(167, "GPIO_167"), + PINCTRL_PIN(168, "GPIO_168"), + PINCTRL_PIN(169, "GPIO_169"), + PINCTRL_PIN(170, "GPIO_170"), + PINCTRL_PIN(171, "GPIO_171"), + PINCTRL_PIN(172, "GPIO_172"), + PINCTRL_PIN(173, "GPIO_173"), + PINCTRL_PIN(174, "GPIO_174"), + PINCTRL_PIN(175, "GPIO_175"), + PINCTRL_PIN(176, "GPIO_176"), + PINCTRL_PIN(177, "GPIO_177"), +}; + +static const unsigned i2c0_pins[] = {145, 146}; +static const unsigned i2c1_pins[] = {147, 148}; +static const unsigned i2c2_pins[] = {113, 114}; +static const unsigned i2c3_pins[] = {19, 20}; + +static const unsigned uart0_pins[] = {135, 136, 137, 138, 139}; +static const unsigned uart1_pins[] = {140, 141, 142, 143, 144}; + +static const struct amd_pingroup kerncz_groups[] = { + { + .name = "i2c0", + .pins = i2c0_pins, + .npins = 2, + }, + { + .name = "i2c1", + .pins = i2c1_pins, + .npins = 2, + }, + { + .name = "i2c2", + .pins = i2c2_pins, + .npins = 2, + }, + { + .name = "i2c3", + .pins = i2c3_pins, + .npins = 2, + }, + { + .name = "uart0", + .pins = uart0_pins, + .npins = 9, + }, + { + .name = "uart1", + .pins = uart1_pins, + .npins = 5, + }, +}; + +#endif diff --git a/drivers/pinctrl/pinctrl-as3722.c b/drivers/pinctrl/pinctrl-as3722.c index 169b1bfa00c819..db0571ffbe99e2 100644 --- a/drivers/pinctrl/pinctrl-as3722.c +++ b/drivers/pinctrl/pinctrl-as3722.c @@ -625,7 +625,7 @@ static int as3722_pinctrl_remove(struct platform_device *pdev) return 0; } -static struct of_device_id as3722_pinctrl_of_match[] = { +static const struct of_device_id as3722_pinctrl_of_match[] = { { .compatible = "ams,as3722-pinctrl", }, { }, }; diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index a4814066ea0876..2f797cb7e20501 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -451,18 +451,18 @@ static enum at91_mux at91_mux_get_periph(void __iomem *pio, unsigned mask) static bool at91_mux_get_deglitch(void __iomem *pio, unsigned pin) { - return (__raw_readl(pio + PIO_IFSR) >> pin) & 0x1; + return (readl_relaxed(pio + PIO_IFSR) >> pin) & 0x1; } static void at91_mux_set_deglitch(void __iomem *pio, unsigned mask, bool is_on) { - __raw_writel(mask, pio + (is_on ? PIO_IFER : PIO_IFDR)); + writel_relaxed(mask, pio + (is_on ? PIO_IFER : PIO_IFDR)); } static bool at91_mux_pio3_get_deglitch(void __iomem *pio, unsigned pin) { - if ((__raw_readl(pio + PIO_IFSR) >> pin) & 0x1) - return !((__raw_readl(pio + PIO_IFSCSR) >> pin) & 0x1); + if ((readl_relaxed(pio + PIO_IFSR) >> pin) & 0x1) + return !((readl_relaxed(pio + PIO_IFSCSR) >> pin) & 0x1); return false; } @@ -470,55 +470,55 @@ static bool at91_mux_pio3_get_deglitch(void __iomem *pio, unsigned pin) static void at91_mux_pio3_set_deglitch(void __iomem *pio, unsigned mask, bool is_on) { if (is_on) - __raw_writel(mask, pio + PIO_IFSCDR); + writel_relaxed(mask, pio + PIO_IFSCDR); at91_mux_set_deglitch(pio, mask, is_on); } static bool at91_mux_pio3_get_debounce(void __iomem *pio, unsigned pin, u32 *div) { - *div = __raw_readl(pio + PIO_SCDR); + *div = readl_relaxed(pio + PIO_SCDR); - return ((__raw_readl(pio + PIO_IFSR) >> pin) & 0x1) && - ((__raw_readl(pio + PIO_IFSCSR) >> pin) & 0x1); + return ((readl_relaxed(pio + PIO_IFSR) >> pin) & 0x1) && + ((readl_relaxed(pio + PIO_IFSCSR) >> pin) & 0x1); } static void at91_mux_pio3_set_debounce(void __iomem *pio, unsigned mask, bool is_on, u32 div) { if (is_on) { - __raw_writel(mask, pio + PIO_IFSCER); - __raw_writel(div & PIO_SCDR_DIV, pio + PIO_SCDR); - __raw_writel(mask, pio + PIO_IFER); + writel_relaxed(mask, pio + PIO_IFSCER); + writel_relaxed(div & PIO_SCDR_DIV, pio + PIO_SCDR); + writel_relaxed(mask, pio + PIO_IFER); } else - __raw_writel(mask, pio + PIO_IFSCDR); + writel_relaxed(mask, pio + PIO_IFSCDR); } static bool at91_mux_pio3_get_pulldown(void __iomem *pio, unsigned pin) { - return !((__raw_readl(pio + PIO_PPDSR) >> pin) & 0x1); + return !((readl_relaxed(pio + PIO_PPDSR) >> pin) & 0x1); } static void at91_mux_pio3_set_pulldown(void __iomem *pio, unsigned mask, bool is_on) { if (is_on) - __raw_writel(mask, pio + PIO_PUDR); + writel_relaxed(mask, pio + PIO_PUDR); - __raw_writel(mask, pio + (is_on ? PIO_PPDER : PIO_PPDDR)); + writel_relaxed(mask, pio + (is_on ? PIO_PPDER : PIO_PPDDR)); } static void at91_mux_pio3_disable_schmitt_trig(void __iomem *pio, unsigned mask) { - __raw_writel(__raw_readl(pio + PIO_SCHMITT) | mask, pio + PIO_SCHMITT); + writel_relaxed(readl_relaxed(pio + PIO_SCHMITT) | mask, pio + PIO_SCHMITT); } static bool at91_mux_pio3_get_schmitt_trig(void __iomem *pio, unsigned pin) { - return (__raw_readl(pio + PIO_SCHMITT) >> pin) & 0x1; + return (readl_relaxed(pio + PIO_SCHMITT) >> pin) & 0x1; } static inline u32 read_drive_strength(void __iomem *reg, unsigned pin) { - unsigned tmp = __raw_readl(reg); + unsigned tmp = readl_relaxed(reg); tmp = tmp >> two_bit_pin_value_shift_amount(pin); @@ -554,13 +554,13 @@ static unsigned at91_mux_sam9x5_get_drivestrength(void __iomem *pio, static void set_drive_strength(void __iomem *reg, unsigned pin, u32 strength) { - unsigned tmp = __raw_readl(reg); + unsigned tmp = readl_relaxed(reg); unsigned shift = two_bit_pin_value_shift_amount(pin); tmp &= ~(DRIVE_STRENGTH_MASK << shift); tmp |= strength << shift; - __raw_writel(tmp, reg); + writel_relaxed(tmp, reg); } static void at91_mux_sama5d3_set_drivestrength(void __iomem *pio, unsigned pin, @@ -1114,7 +1114,7 @@ static int at91_pinctrl_parse_functions(struct device_node *np, return 0; } -static struct of_device_id at91_pinctrl_of_match[] = { +static const struct of_device_id at91_pinctrl_of_match[] = { { .compatible = "atmel,sama5d3-pinctrl", .data = &sama5d3_ops }, { .compatible = "atmel,at91sam9x5-pinctrl", .data = &at91sam9x5_ops }, { .compatible = "atmel,at91rm9200-pinctrl", .data = &at91rm9200_ops }, @@ -1240,8 +1240,7 @@ static int at91_pinctrl_probe(struct platform_device *pdev) if (!info->pctl) { dev_err(&pdev->dev, "could not register AT91 pinctrl driver\n"); - ret = -EINVAL; - goto err; + return -EINVAL; } /* We will handle a range of GPIO pins */ @@ -1252,9 +1251,6 @@ static int at91_pinctrl_probe(struct platform_device *pdev) dev_info(&pdev->dev, "initialized AT91 pinctrl driver\n"); return 0; - -err: - return ret; } static int at91_pinctrl_remove(struct platform_device *pdev) @@ -1535,9 +1531,9 @@ void at91_pinctrl_gpio_suspend(void) pio = gpio_chips[i]->regbase; - backups[i] = __raw_readl(pio + PIO_IMR); - __raw_writel(backups[i], pio + PIO_IDR); - __raw_writel(wakeups[i], pio + PIO_IER); + backups[i] = readl_relaxed(pio + PIO_IMR); + writel_relaxed(backups[i], pio + PIO_IDR); + writel_relaxed(wakeups[i], pio + PIO_IER); if (!wakeups[i]) clk_disable_unprepare(gpio_chips[i]->clock); @@ -1562,8 +1558,8 @@ void at91_pinctrl_gpio_resume(void) if (!wakeups[i]) clk_prepare_enable(gpio_chips[i]->clock); - __raw_writel(wakeups[i], pio + PIO_IDR); - __raw_writel(backups[i], pio + PIO_IER); + writel_relaxed(wakeups[i], pio + PIO_IDR); + writel_relaxed(backups[i], pio + PIO_IER); } } @@ -1694,7 +1690,7 @@ static struct gpio_chip at91_gpio_template = { .ngpio = MAX_NB_GPIO_PER_BANK, }; -static struct of_device_id at91_gpio_of_match[] = { +static const struct of_device_id at91_gpio_of_match[] = { { .compatible = "atmel,at91sam9x5-gpio", .data = &at91sam9x5_ops, }, { .compatible = "atmel,at91rm9200-gpio", .data = &at91rm9200_ops }, { /* sentinel */ } diff --git a/drivers/pinctrl/pinctrl-lantiq.h b/drivers/pinctrl/pinctrl-lantiq.h index c7cfad5527d70a..eb89ba04522825 100644 --- a/drivers/pinctrl/pinctrl-lantiq.h +++ b/drivers/pinctrl/pinctrl-lantiq.h @@ -193,4 +193,4 @@ enum ltq_pin { extern int ltq_pinctrl_register(struct platform_device *pdev, struct ltq_pinmux_info *info); extern int ltq_pinctrl_unregister(struct platform_device *pdev); -#endif /* __PINCTRL_PXA3XX_H */ +#endif /* __PINCTRL_LANTIQ_H */ diff --git a/drivers/pinctrl/pinctrl-palmas.c b/drivers/pinctrl/pinctrl-palmas.c index 26461e30f0aebf..2631df0504bd62 100644 --- a/drivers/pinctrl/pinctrl-palmas.c +++ b/drivers/pinctrl/pinctrl-palmas.c @@ -987,7 +987,7 @@ static struct palmas_pinctrl_data tps80036_pinctrl_data = { .num_pin_groups = ARRAY_SIZE(tps80036_pingroups), }; -static struct of_device_id palmas_pinctrl_of_match[] = { +static const struct of_device_id palmas_pinctrl_of_match[] = { { .compatible = "ti,palmas-pinctrl", .data = &tps65913_pinctrl_data}, { .compatible = "ti,tps65913-pinctrl", .data = &tps65913_pinctrl_data}, { .compatible = "ti,tps80036-pinctrl", .data = &tps80036_pinctrl_data}, diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 69e84427f9134a..13b45f29772788 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -1501,7 +1501,7 @@ static void pcs_free_resources(struct pcs_device *pcs) } \ } while (0); -static struct of_device_id pcs_of_match[]; +static const struct of_device_id pcs_of_match[]; static int pcs_add_gpio_func(struct device_node *node, struct pcs_device *pcs) { @@ -2000,7 +2000,7 @@ static const struct pcs_soc_data pinconf_single = { .flags = PCS_FEAT_PINCONF, }; -static struct of_device_id pcs_of_match[] = { +static const struct of_device_id pcs_of_match[] = { { .compatible = "ti,omap3-padconf", .data = &pinctrl_single_omap_wkup }, { .compatible = "ti,omap4-padconf", .data = &pinctrl_single_omap_wkup }, { .compatible = "ti,omap5-padconf", .data = &pinctrl_single_omap_wkup }, diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c index 9e5ec00084bb1d..65bf73b70e3406 100644 --- a/drivers/pinctrl/pinctrl-st.c +++ b/drivers/pinctrl/pinctrl-st.c @@ -206,6 +206,8 @@ #define gpio_chip_to_bank(chip) \ container_of(chip, struct st_gpio_bank, gpio_chip) +#define pc_to_bank(pc) \ + container_of(pc, struct st_gpio_bank, pc) enum st_retime_style { st_retime_style_none, @@ -398,6 +400,16 @@ static const struct st_pctl_data stih407_flashdata = { .rt = 100, }; +static struct st_pio_control *st_get_pio_control( + struct pinctrl_dev *pctldev, int pin) +{ + struct pinctrl_gpio_range *range = + pinctrl_find_gpio_range_from_pin(pctldev, pin); + struct st_gpio_bank *bank = gpio_range_to_bank(range); + + return &bank->pc; +} + /* Low level functions.. */ static inline int st_gpio_bank(int gpio) { @@ -460,6 +472,20 @@ static void st_pctl_set_function(struct st_pio_control *pc, regmap_field_write(alt, val); } +static unsigned int st_pctl_get_pin_function(struct st_pio_control *pc, int pin) +{ + struct regmap_field *alt = pc->alt; + unsigned int val; + int offset = pin * 4; + + if (!alt) + return 0; + + regmap_field_read(alt, &val); + + return (val >> offset) & 0xf; +} + static unsigned long st_pinconf_delay_to_bit(unsigned int delay, const struct st_pctl_data *data, unsigned long config) { @@ -757,6 +783,35 @@ static int st_gpio_direction_output(struct gpio_chip *chip, return 0; } +static int st_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct st_gpio_bank *bank = gpio_chip_to_bank(chip); + struct st_pio_control pc = bank->pc; + unsigned long config; + unsigned int direction = 0; + unsigned int function; + unsigned int value; + int i = 0; + + /* Alternate function direction is handled by Pinctrl */ + function = st_pctl_get_pin_function(&pc, offset); + if (function) { + st_pinconf_get_direction(&pc, offset, &config); + return !ST_PINCONF_UNPACK_OE(config); + } + + /* + * GPIO direction is handled differently + * - See st_gpio_direction() above for an explanation + */ + for (i = 0; i <= 2; i++) { + value = readl(bank->base + REG_PIO_PC(i)); + direction |= ((value >> offset) & 0x1) << i; + } + + return (direction == ST_GPIO_DIRECTION_IN); +} + static int st_gpio_xlate(struct gpio_chip *gc, const struct of_phandle_args *gpiospec, u32 *flags) { @@ -904,16 +959,6 @@ static int st_pmx_get_groups(struct pinctrl_dev *pctldev, return 0; } -static struct st_pio_control *st_get_pio_control( - struct pinctrl_dev *pctldev, int pin) -{ - struct pinctrl_gpio_range *range = - pinctrl_find_gpio_range_from_pin(pctldev, pin); - struct st_gpio_bank *bank = gpio_range_to_bank(range); - - return &bank->pc; -} - static int st_pmx_set_mux(struct pinctrl_dev *pctldev, unsigned fselector, unsigned group) { @@ -1011,17 +1056,30 @@ static int st_pinconf_get(struct pinctrl_dev *pctldev, static void st_pinconf_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned pin_id) { + struct st_pio_control *pc; unsigned long config; + unsigned int function; + int offset = st_gpio_pin(pin_id); + char f[16]; mutex_unlock(&pctldev->mutex); + pc = st_get_pio_control(pctldev, pin_id); st_pinconf_get(pctldev, pin_id, &config); mutex_lock(&pctldev->mutex); - seq_printf(s, "[OE:%ld,PU:%ld,OD:%ld]\n" + + function = st_pctl_get_pin_function(pc, offset); + if (function) + snprintf(f, 10, "Alt Fn %d", function); + else + snprintf(f, 5, "GPIO"); + + seq_printf(s, "[OE:%d,PU:%ld,OD:%ld]\t%s\n" "\t\t[retime:%ld,invclk:%ld,clknotdat:%ld," "de:%ld,rt-clk:%ld,rt-delay:%ld]", - ST_PINCONF_UNPACK_OE(config), + !st_gpio_get_direction(&pc_to_bank(pc)->gpio_chip, offset), ST_PINCONF_UNPACK_PU(config), ST_PINCONF_UNPACK_OD(config), + f, ST_PINCONF_UNPACK_RT(config), ST_PINCONF_UNPACK_RT_INVERTCLK(config), ST_PINCONF_UNPACK_RT_CLKNOTDATA(config), @@ -1438,6 +1496,7 @@ static struct gpio_chip st_gpio_template = { .set = st_gpio_set, .direction_input = st_gpio_direction_input, .direction_output = st_gpio_direction_output, + .get_direction = st_gpio_get_direction, .ngpio = ST_GPIO_PINS_PER_BANK, .of_gpio_n_cells = 1, .of_xlate = st_gpio_xlate, @@ -1531,7 +1590,7 @@ static int st_gpiolib_register_bank(struct st_pinctrl *info, return 0; } -static struct of_device_id st_pctl_of_match[] = { +static const struct of_device_id st_pctl_of_match[] = { { .compatible = "st,stih415-sbc-pinctrl", .data = &stih415_sbc_data }, { .compatible = "st,stih415-rear-pinctrl", .data = &stih415_rear_data }, { .compatible = "st,stih415-left-pinctrl", .data = &stih415_left_data }, diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c index e5949d51bc52c8..4c95c2024a1c24 100644 --- a/drivers/pinctrl/pinctrl-tegra.c +++ b/drivers/pinctrl/pinctrl-tegra.c @@ -103,6 +103,7 @@ static const struct cfg_param { {"nvidia,lock", TEGRA_PINCONF_PARAM_LOCK}, {"nvidia,io-reset", TEGRA_PINCONF_PARAM_IORESET}, {"nvidia,rcv-sel", TEGRA_PINCONF_PARAM_RCV_SEL}, + {"nvidia,io-hv", TEGRA_PINCONF_PARAM_RCV_SEL}, {"nvidia,high-speed-mode", TEGRA_PINCONF_PARAM_HIGH_SPEED_MODE}, {"nvidia,schmitt", TEGRA_PINCONF_PARAM_SCHMITT}, {"nvidia,low-power-mode", TEGRA_PINCONF_PARAM_LOW_POWER_MODE}, @@ -348,14 +349,24 @@ static int tegra_pinconf_reg(struct tegra_pmx *pmx, *width = 1; break; case TEGRA_PINCONF_PARAM_HIGH_SPEED_MODE: - *bank = g->drv_bank; - *reg = g->drv_reg; + if (pmx->soc->hsm_in_mux) { + *bank = g->mux_bank; + *reg = g->mux_reg; + } else { + *bank = g->drv_bank; + *reg = g->drv_reg; + } *bit = g->hsm_bit; *width = 1; break; case TEGRA_PINCONF_PARAM_SCHMITT: - *bank = g->drv_bank; - *reg = g->drv_reg; + if (pmx->soc->schmitt_in_mux) { + *bank = g->mux_bank; + *reg = g->mux_reg; + } else { + *bank = g->drv_bank; + *reg = g->drv_reg; + } *bit = g->schmitt_bit; *width = 1; break; @@ -390,8 +401,13 @@ static int tegra_pinconf_reg(struct tegra_pmx *pmx, *width = g->slwr_width; break; case TEGRA_PINCONF_PARAM_DRIVE_TYPE: - *bank = g->drv_bank; - *reg = g->drv_reg; + if (pmx->soc->drvtype_in_mux) { + *bank = g->mux_bank; + *reg = g->mux_reg; + } else { + *bank = g->drv_bank; + *reg = g->drv_reg; + } *bit = g->drvtype_bit; *width = 2; break; diff --git a/drivers/pinctrl/pinctrl-tegra.h b/drivers/pinctrl/pinctrl-tegra.h index 8d94d1332e7b48..1615db7e3a4bd0 100644 --- a/drivers/pinctrl/pinctrl-tegra.h +++ b/drivers/pinctrl/pinctrl-tegra.h @@ -139,26 +139,26 @@ struct tegra_pingroup { u32 pupd_bank:2; u32 tri_bank:2; u32 drv_bank:2; - u32 mux_bit:6; - u32 pupd_bit:6; - u32 tri_bit:6; - u32 einput_bit:6; - u32 odrain_bit:6; - u32 lock_bit:6; - u32 ioreset_bit:6; - u32 rcv_sel_bit:6; - u32 hsm_bit:6; - u32 schmitt_bit:6; - u32 lpmd_bit:6; - u32 drvdn_bit:6; - u32 drvup_bit:6; - u32 slwr_bit:6; - u32 slwf_bit:6; - u32 drvtype_bit:6; - u32 drvdn_width:6; - u32 drvup_width:6; - u32 slwr_width:6; - u32 slwf_width:6; + s32 mux_bit:6; + s32 pupd_bit:6; + s32 tri_bit:6; + s32 einput_bit:6; + s32 odrain_bit:6; + s32 lock_bit:6; + s32 ioreset_bit:6; + s32 rcv_sel_bit:6; + s32 hsm_bit:6; + s32 schmitt_bit:6; + s32 lpmd_bit:6; + s32 drvdn_bit:6; + s32 drvup_bit:6; + s32 slwr_bit:6; + s32 slwf_bit:6; + s32 drvtype_bit:6; + s32 drvdn_width:6; + s32 drvup_width:6; + s32 slwr_width:6; + s32 slwf_width:6; }; /** @@ -182,6 +182,9 @@ struct tegra_pinctrl_soc_data { unsigned nfunctions; const struct tegra_pingroup *groups; unsigned ngroups; + bool hsm_in_mux; + bool schmitt_in_mux; + bool drvtype_in_mux; }; int tegra_pinctrl_probe(struct platform_device *pdev, diff --git a/drivers/pinctrl/pinctrl-tegra114.c b/drivers/pinctrl/pinctrl-tegra114.c index 52e4ec6386b418..05e49d5137ab35 100644 --- a/drivers/pinctrl/pinctrl-tegra114.c +++ b/drivers/pinctrl/pinctrl-tegra114.c @@ -1547,6 +1547,7 @@ static struct tegra_function tegra114_functions[] = { #define DRV_PINGROUP_REG_A 0x868 /* bank 0 */ #define PINGROUP_REG_A 0x3000 /* bank 1 */ +#define DRV_PINGROUP_REG(r) ((r) - DRV_PINGROUP_REG_A) #define PINGROUP_REG(r) ((r) - PINGROUP_REG_A) #define PINGROUP_BIT_Y(b) (b) @@ -1572,20 +1573,17 @@ static struct tegra_function tegra114_functions[] = { .tri_reg = PINGROUP_REG(r), \ .tri_bank = 1, \ .tri_bit = 4, \ - .einput_bit = PINGROUP_BIT_Y(5), \ + .einput_bit = 5, \ .odrain_bit = PINGROUP_BIT_##od(6), \ - .lock_bit = PINGROUP_BIT_Y(7), \ + .lock_bit = 7, \ .ioreset_bit = PINGROUP_BIT_##ior(8), \ .rcv_sel_bit = PINGROUP_BIT_##rcv_sel(9), \ .drv_reg = -1, \ } -#define DRV_PINGROUP_REG(r) ((r) - DRV_PINGROUP_REG_A) - -#define DRV_PINGROUP(pg_name, r, hsm_b, schmitt_b, lpmd_b, \ - drvdn_b, drvdn_w, drvup_b, drvup_w, \ - slwr_b, slwr_w, slwf_b, slwf_w, \ - drvtype) \ +#define DRV_PINGROUP(pg_name, r, hsm_b, schmitt_b, lpmd_b, drvdn_b, \ + drvdn_w, drvup_b, drvup_w, slwr_b, slwr_w, \ + slwf_b, slwf_w, drvtype) \ { \ .name = "drive_" #pg_name, \ .pins = drive_##pg_name##_pins, \ @@ -1843,6 +1841,9 @@ static const struct tegra_pinctrl_soc_data tegra114_pinctrl = { .nfunctions = ARRAY_SIZE(tegra114_functions), .groups = tegra114_groups, .ngroups = ARRAY_SIZE(tegra114_groups), + .hsm_in_mux = false, + .schmitt_in_mux = false, + .drvtype_in_mux = false, }; static int tegra114_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/pinctrl-tegra124.c b/drivers/pinctrl/pinctrl-tegra124.c index 2b20906c53561a..7cd44c7c296daa 100644 --- a/drivers/pinctrl/pinctrl-tegra124.c +++ b/drivers/pinctrl/pinctrl-tegra124.c @@ -1536,6 +1536,7 @@ enum tegra_mux { TEGRA_MUX_CLK, TEGRA_MUX_CLK12, TEGRA_MUX_CPU, + TEGRA_MUX_CSI, TEGRA_MUX_DAP, TEGRA_MUX_DAP1, TEGRA_MUX_DAP2, @@ -1544,6 +1545,7 @@ enum tegra_mux { TEGRA_MUX_DISPLAYA_ALT, TEGRA_MUX_DISPLAYB, TEGRA_MUX_DP, + TEGRA_MUX_DSI_B, TEGRA_MUX_DTV, TEGRA_MUX_EXTPERIPH1, TEGRA_MUX_EXTPERIPH2, @@ -1613,8 +1615,6 @@ enum tegra_mux { TEGRA_MUX_VI_ALT3, TEGRA_MUX_VIMCLK2, TEGRA_MUX_VIMCLK2_ALT, - TEGRA_MUX_CSI, - TEGRA_MUX_DSI_B, }; #define FUNCTION(fname) \ @@ -1630,6 +1630,7 @@ static struct tegra_function tegra124_functions[] = { FUNCTION(clk), FUNCTION(clk12), FUNCTION(cpu), + FUNCTION(csi), FUNCTION(dap), FUNCTION(dap1), FUNCTION(dap2), @@ -1638,6 +1639,7 @@ static struct tegra_function tegra124_functions[] = { FUNCTION(displaya_alt), FUNCTION(displayb), FUNCTION(dp), + FUNCTION(dsi_b), FUNCTION(dtv), FUNCTION(extperiph1), FUNCTION(extperiph2), @@ -1707,15 +1709,15 @@ static struct tegra_function tegra124_functions[] = { FUNCTION(vi_alt3), FUNCTION(vimclk2), FUNCTION(vimclk2_alt), - FUNCTION(csi), - FUNCTION(dsi_b), }; #define DRV_PINGROUP_REG_A 0x868 /* bank 0 */ #define PINGROUP_REG_A 0x3000 /* bank 1 */ #define MIPI_PAD_CTRL_PINGROUP_REG_A 0x820 /* bank 2 */ +#define DRV_PINGROUP_REG(r) ((r) - DRV_PINGROUP_REG_A) #define PINGROUP_REG(r) ((r) - PINGROUP_REG_A) +#define MIPI_PAD_CTRL_PINGROUP_REG_Y(r) ((r) - MIPI_PAD_CTRL_PINGROUP_REG_A) #define PINGROUP_BIT_Y(b) (b) #define PINGROUP_BIT_N(b) (-1) @@ -1740,20 +1742,17 @@ static struct tegra_function tegra124_functions[] = { .tri_reg = PINGROUP_REG(r), \ .tri_bank = 1, \ .tri_bit = 4, \ - .einput_bit = PINGROUP_BIT_Y(5), \ + .einput_bit = 5, \ .odrain_bit = PINGROUP_BIT_##od(6), \ - .lock_bit = PINGROUP_BIT_Y(7), \ + .lock_bit = 7, \ .ioreset_bit = PINGROUP_BIT_##ior(8), \ .rcv_sel_bit = PINGROUP_BIT_##rcv_sel(9), \ .drv_reg = -1, \ } -#define DRV_PINGROUP_REG(r) ((r) - DRV_PINGROUP_REG_A) - -#define DRV_PINGROUP(pg_name, r, hsm_b, schmitt_b, lpmd_b, \ - drvdn_b, drvdn_w, drvup_b, drvup_w, \ - slwr_b, slwr_w, slwf_b, slwf_w, \ - drvtype) \ +#define DRV_PINGROUP(pg_name, r, hsm_b, schmitt_b, lpmd_b, drvdn_b, \ + drvdn_w, drvup_b, drvup_w, slwr_b, slwr_w, \ + slwf_b, slwf_w, drvtype) \ { \ .name = "drive_" #pg_name, \ .pins = drive_##pg_name##_pins, \ @@ -1782,8 +1781,6 @@ static struct tegra_function tegra124_functions[] = { .drvtype_bit = PINGROUP_BIT_##drvtype(6), \ } -#define MIPI_PAD_CTRL_PINGROUP_REG_Y(r) ((r) - MIPI_PAD_CTRL_PINGROUP_REG_A) - #define MIPI_PAD_CTRL_PINGROUP(pg_name, r, b, f0, f1) \ { \ .name = "mipi_pad_ctrl_" #pg_name, \ @@ -2044,8 +2041,8 @@ static const struct tegra_pingroup tegra124_groups[] = { DRV_PINGROUP(sdio4, 0x9c4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2, N), DRV_PINGROUP(ao4, 0x9c8, 2, 3, 4, 12, 7, 20, 7, 28, 2, 30, 2, Y), - /* pg_name, r b f0, f1 */ - MIPI_PAD_CTRL_PINGROUP(dsi_b, 0x820, 1, CSI, DSI_B) + /* pg_name, r, b, f0, f1 */ + MIPI_PAD_CTRL_PINGROUP(dsi_b, 0x820, 1, CSI, DSI_B), }; static const struct tegra_pinctrl_soc_data tegra124_pinctrl = { @@ -2056,6 +2053,9 @@ static const struct tegra_pinctrl_soc_data tegra124_pinctrl = { .nfunctions = ARRAY_SIZE(tegra124_functions), .groups = tegra124_groups, .ngroups = ARRAY_SIZE(tegra124_groups), + .hsm_in_mux = false, + .schmitt_in_mux = false, + .drvtype_in_mux = false, }; static int tegra124_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/pinctrl-tegra20.c b/drivers/pinctrl/pinctrl-tegra20.c index d3a5722e4acbde..4833db4433d948 100644 --- a/drivers/pinctrl/pinctrl-tegra20.c +++ b/drivers/pinctrl/pinctrl-tegra20.c @@ -2221,6 +2221,9 @@ static const struct tegra_pinctrl_soc_data tegra20_pinctrl = { .nfunctions = ARRAY_SIZE(tegra20_functions), .groups = tegra20_groups, .ngroups = ARRAY_SIZE(tegra20_groups), + .hsm_in_mux = false, + .schmitt_in_mux = false, + .drvtype_in_mux = false, }; static int tegra20_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/pinctrl-tegra210.c b/drivers/pinctrl/pinctrl-tegra210.c new file mode 100644 index 00000000000000..252b464901c03a --- /dev/null +++ b/drivers/pinctrl/pinctrl-tegra210.c @@ -0,0 +1,1588 @@ +/* + * Pinctrl data for the NVIDIA Tegra210 pinmux + * + * Copyright (c) 2015, 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. + */ + +#include +#include +#include +#include +#include + +#include "pinctrl-tegra.h" + +/* + * Most pins affected by the pinmux can also be GPIOs. Define these first. + * These must match how the GPIO driver names/numbers its pins. + */ +#define _GPIO(offset) (offset) + +#define TEGRA_PIN_PEX_L0_RST_N_PA0 _GPIO(0) +#define TEGRA_PIN_PEX_L0_CLKREQ_N_PA1 _GPIO(1) +#define TEGRA_PIN_PEX_WAKE_N_PA2 _GPIO(2) +#define TEGRA_PIN_PEX_L1_RST_N_PA3 _GPIO(3) +#define TEGRA_PIN_PEX_L1_CLKREQ_N_PA4 _GPIO(4) +#define TEGRA_PIN_SATA_LED_ACTIVE_PA5 _GPIO(5) +#define TEGRA_PIN_PA6 _GPIO(6) +#define TEGRA_PIN_DAP1_FS_PB0 _GPIO(8) +#define TEGRA_PIN_DAP1_DIN_PB1 _GPIO(9) +#define TEGRA_PIN_DAP1_DOUT_PB2 _GPIO(10) +#define TEGRA_PIN_DAP1_SCLK_PB3 _GPIO(11) +#define TEGRA_PIN_SPI2_MOSI_PB4 _GPIO(12) +#define TEGRA_PIN_SPI2_MISO_PB5 _GPIO(13) +#define TEGRA_PIN_SPI2_SCK_PB6 _GPIO(14) +#define TEGRA_PIN_SPI2_CS0_PB7 _GPIO(15) +#define TEGRA_PIN_SPI1_MOSI_PC0 _GPIO(16) +#define TEGRA_PIN_SPI1_MISO_PC1 _GPIO(17) +#define TEGRA_PIN_SPI1_SCK_PC2 _GPIO(18) +#define TEGRA_PIN_SPI1_CS0_PC3 _GPIO(19) +#define TEGRA_PIN_SPI1_CS1_PC4 _GPIO(20) +#define TEGRA_PIN_SPI4_SCK_PC5 _GPIO(21) +#define TEGRA_PIN_SPI4_CS0_PC6 _GPIO(22) +#define TEGRA_PIN_SPI4_MOSI_PC7 _GPIO(23) +#define TEGRA_PIN_SPI4_MISO_PD0 _GPIO(24) +#define TEGRA_PIN_UART3_TX_PD1 _GPIO(25) +#define TEGRA_PIN_UART3_RX_PD2 _GPIO(26) +#define TEGRA_PIN_UART3_RTS_PD3 _GPIO(27) +#define TEGRA_PIN_UART3_CTS_PD4 _GPIO(28) +#define TEGRA_PIN_DMIC1_CLK_PE0 _GPIO(32) +#define TEGRA_PIN_DMIC1_DAT_PE1 _GPIO(33) +#define TEGRA_PIN_DMIC2_CLK_PE2 _GPIO(34) +#define TEGRA_PIN_DMIC2_DAT_PE3 _GPIO(35) +#define TEGRA_PIN_DMIC3_CLK_PE4 _GPIO(36) +#define TEGRA_PIN_DMIC3_DAT_PE5 _GPIO(37) +#define TEGRA_PIN_PE6 _GPIO(38) +#define TEGRA_PIN_PE7 _GPIO(39) +#define TEGRA_PIN_GEN3_I2C_SCL_PF0 _GPIO(40) +#define TEGRA_PIN_GEN3_I2C_SDA_PF1 _GPIO(41) +#define TEGRA_PIN_UART2_TX_PG0 _GPIO(48) +#define TEGRA_PIN_UART2_RX_PG1 _GPIO(49) +#define TEGRA_PIN_UART2_RTS_PG2 _GPIO(50) +#define TEGRA_PIN_UART2_CTS_PG3 _GPIO(51) +#define TEGRA_PIN_WIFI_EN_PH0 _GPIO(56) +#define TEGRA_PIN_WIFI_RST_PH1 _GPIO(57) +#define TEGRA_PIN_WIFI_WAKE_AP_PH2 _GPIO(58) +#define TEGRA_PIN_AP_WAKE_BT_PH3 _GPIO(59) +#define TEGRA_PIN_BT_RST_PH4 _GPIO(60) +#define TEGRA_PIN_BT_WAKE_AP_PH5 _GPIO(61) +#define TEGRA_PIN_PH6 _GPIO(62) +#define TEGRA_PIN_AP_WAKE_NFC_PH7 _GPIO(63) +#define TEGRA_PIN_NFC_EN_PI0 _GPIO(64) +#define TEGRA_PIN_NFC_INT_PI1 _GPIO(65) +#define TEGRA_PIN_GPS_EN_PI2 _GPIO(66) +#define TEGRA_PIN_GPS_RST_PI3 _GPIO(67) +#define TEGRA_PIN_UART4_TX_PI4 _GPIO(68) +#define TEGRA_PIN_UART4_RX_PI5 _GPIO(69) +#define TEGRA_PIN_UART4_RTS_PI6 _GPIO(70) +#define TEGRA_PIN_UART4_CTS_PI7 _GPIO(71) +#define TEGRA_PIN_GEN1_I2C_SDA_PJ0 _GPIO(72) +#define TEGRA_PIN_GEN1_I2C_SCL_PJ1 _GPIO(73) +#define TEGRA_PIN_GEN2_I2C_SCL_PJ2 _GPIO(74) +#define TEGRA_PIN_GEN2_I2C_SDA_PJ3 _GPIO(75) +#define TEGRA_PIN_DAP4_FS_PJ4 _GPIO(76) +#define TEGRA_PIN_DAP4_DIN_PJ5 _GPIO(77) +#define TEGRA_PIN_DAP4_DOUT_PJ6 _GPIO(78) +#define TEGRA_PIN_DAP4_SCLK_PJ7 _GPIO(79) +#define TEGRA_PIN_PK0 _GPIO(80) +#define TEGRA_PIN_PK1 _GPIO(81) +#define TEGRA_PIN_PK2 _GPIO(82) +#define TEGRA_PIN_PK3 _GPIO(83) +#define TEGRA_PIN_PK4 _GPIO(84) +#define TEGRA_PIN_PK5 _GPIO(85) +#define TEGRA_PIN_PK6 _GPIO(86) +#define TEGRA_PIN_PK7 _GPIO(87) +#define TEGRA_PIN_PL0 _GPIO(88) +#define TEGRA_PIN_PL1 _GPIO(89) +#define TEGRA_PIN_SDMMC1_CLK_PM0 _GPIO(96) +#define TEGRA_PIN_SDMMC1_CMD_PM1 _GPIO(97) +#define TEGRA_PIN_SDMMC1_DAT3_PM2 _GPIO(98) +#define TEGRA_PIN_SDMMC1_DAT2_PM3 _GPIO(99) +#define TEGRA_PIN_SDMMC1_DAT1_PM4 _GPIO(100) +#define TEGRA_PIN_SDMMC1_DAT0_PM5 _GPIO(101) +#define TEGRA_PIN_SDMMC3_CLK_PP0 _GPIO(120) +#define TEGRA_PIN_SDMMC3_CMD_PP1 _GPIO(121) +#define TEGRA_PIN_SDMMC3_DAT3_PP2 _GPIO(122) +#define TEGRA_PIN_SDMMC3_DAT2_PP3 _GPIO(123) +#define TEGRA_PIN_SDMMC3_DAT1_PP4 _GPIO(124) +#define TEGRA_PIN_SDMMC3_DAT0_PP5 _GPIO(125) +#define TEGRA_PIN_CAM1_MCLK_PS0 _GPIO(144) +#define TEGRA_PIN_CAM2_MCLK_PS1 _GPIO(145) +#define TEGRA_PIN_CAM_I2C_SCL_PS2 _GPIO(146) +#define TEGRA_PIN_CAM_I2C_SDA_PS3 _GPIO(147) +#define TEGRA_PIN_CAM_RST_PS4 _GPIO(148) +#define TEGRA_PIN_CAM_AF_EN_PS5 _GPIO(149) +#define TEGRA_PIN_CAM_FLASH_EN_PS6 _GPIO(150) +#define TEGRA_PIN_CAM1_PWDN_PS7 _GPIO(151) +#define TEGRA_PIN_CAM2_PWDN_PT0 _GPIO(152) +#define TEGRA_PIN_CAM1_STROBE_PT1 _GPIO(153) +#define TEGRA_PIN_UART1_TX_PU0 _GPIO(160) +#define TEGRA_PIN_UART1_RX_PU1 _GPIO(161) +#define TEGRA_PIN_UART1_RTS_PU2 _GPIO(162) +#define TEGRA_PIN_UART1_CTS_PU3 _GPIO(163) +#define TEGRA_PIN_LCD_BL_PWM_PV0 _GPIO(168) +#define TEGRA_PIN_LCD_BL_EN_PV1 _GPIO(169) +#define TEGRA_PIN_LCD_RST_PV2 _GPIO(170) +#define TEGRA_PIN_LCD_GPIO1_PV3 _GPIO(171) +#define TEGRA_PIN_LCD_GPIO2_PV4 _GPIO(172) +#define TEGRA_PIN_AP_READY_PV5 _GPIO(173) +#define TEGRA_PIN_TOUCH_RST_PV6 _GPIO(174) +#define TEGRA_PIN_TOUCH_CLK_PV7 _GPIO(175) +#define TEGRA_PIN_MODEM_WAKE_AP_PX0 _GPIO(184) +#define TEGRA_PIN_TOUCH_INT_PX1 _GPIO(185) +#define TEGRA_PIN_MOTION_INT_PX2 _GPIO(186) +#define TEGRA_PIN_ALS_PROX_INT_PX3 _GPIO(187) +#define TEGRA_PIN_TEMP_ALERT_PX4 _GPIO(188) +#define TEGRA_PIN_BUTTON_POWER_ON_PX5 _GPIO(189) +#define TEGRA_PIN_BUTTON_VOL_UP_PX6 _GPIO(190) +#define TEGRA_PIN_BUTTON_VOL_DOWN_PX7 _GPIO(191) +#define TEGRA_PIN_BUTTON_SLIDE_SW_PY0 _GPIO(192) +#define TEGRA_PIN_BUTTON_HOME_PY1 _GPIO(193) +#define TEGRA_PIN_LCD_TE_PY2 _GPIO(194) +#define TEGRA_PIN_PWR_I2C_SCL_PY3 _GPIO(195) +#define TEGRA_PIN_PWR_I2C_SDA_PY4 _GPIO(196) +#define TEGRA_PIN_CLK_32K_OUT_PY5 _GPIO(197) +#define TEGRA_PIN_PZ0 _GPIO(200) +#define TEGRA_PIN_PZ1 _GPIO(201) +#define TEGRA_PIN_PZ2 _GPIO(202) +#define TEGRA_PIN_PZ3 _GPIO(203) +#define TEGRA_PIN_PZ4 _GPIO(204) +#define TEGRA_PIN_PZ5 _GPIO(205) +#define TEGRA_PIN_DAP2_FS_PAA0 _GPIO(208) +#define TEGRA_PIN_DAP2_SCLK_PAA1 _GPIO(209) +#define TEGRA_PIN_DAP2_DIN_PAA2 _GPIO(210) +#define TEGRA_PIN_DAP2_DOUT_PAA3 _GPIO(211) +#define TEGRA_PIN_AUD_MCLK_PBB0 _GPIO(216) +#define TEGRA_PIN_DVFS_PWM_PBB1 _GPIO(217) +#define TEGRA_PIN_DVFS_CLK_PBB2 _GPIO(218) +#define TEGRA_PIN_GPIO_X1_AUD_PBB3 _GPIO(219) +#define TEGRA_PIN_GPIO_X3_AUD_PBB4 _GPIO(220) +#define TEGRA_PIN_HDMI_CEC_PCC0 _GPIO(224) +#define TEGRA_PIN_HDMI_INT_DP_HPD_PCC1 _GPIO(225) +#define TEGRA_PIN_SPDIF_OUT_PCC2 _GPIO(226) +#define TEGRA_PIN_SPDIF_IN_PCC3 _GPIO(227) +#define TEGRA_PIN_USB_VBUS_EN0_PCC4 _GPIO(228) +#define TEGRA_PIN_USB_VBUS_EN1_PCC5 _GPIO(229) +#define TEGRA_PIN_DP_HPD0_PCC6 _GPIO(230) +#define TEGRA_PIN_PCC7 _GPIO(231) +#define TEGRA_PIN_SPI2_CS1_PDD0 _GPIO(232) +#define TEGRA_PIN_QSPI_SCK_PEE0 _GPIO(240) +#define TEGRA_PIN_QSPI_CS_N_PEE1 _GPIO(241) +#define TEGRA_PIN_QSPI_IO0_PEE2 _GPIO(242) +#define TEGRA_PIN_QSPI_IO1_PEE3 _GPIO(243) +#define TEGRA_PIN_QSPI_IO2_PEE4 _GPIO(244) +#define TEGRA_PIN_QSPI_IO3_PEE5 _GPIO(245) + +/* All non-GPIO pins follow */ +#define NUM_GPIOS (TEGRA_PIN_QSPI_IO3_PEE5 + 1) +#define _PIN(offset) (NUM_GPIOS + (offset)) + +/* Non-GPIO pins */ +#define TEGRA_PIN_CORE_PWR_REQ _PIN(0) +#define TEGRA_PIN_CPU_PWR_REQ _PIN(1) +#define TEGRA_PIN_PWR_INT_N _PIN(2) +#define TEGRA_PIN_CLK_32K_IN _PIN(3) +#define TEGRA_PIN_JTAG_RTCK _PIN(4) +#define TEGRA_PIN_BATT_BCL _PIN(5) +#define TEGRA_PIN_CLK_REQ _PIN(6) +#define TEGRA_PIN_SHUTDOWN _PIN(7) + +static const struct pinctrl_pin_desc tegra210_pins[] = { + PINCTRL_PIN(TEGRA_PIN_PEX_L0_RST_N_PA0, "PEX_L0_RST_N PA0"), + PINCTRL_PIN(TEGRA_PIN_PEX_L0_CLKREQ_N_PA1, "PEX_L0_CLKREQ_N PA1"), + PINCTRL_PIN(TEGRA_PIN_PEX_WAKE_N_PA2, "PEX_WAKE_N PA2"), + PINCTRL_PIN(TEGRA_PIN_PEX_L1_RST_N_PA3, "PEX_L1_RST_N PA3"), + PINCTRL_PIN(TEGRA_PIN_PEX_L1_CLKREQ_N_PA4, "PEX_L1_CLKREQ_N PA4"), + PINCTRL_PIN(TEGRA_PIN_SATA_LED_ACTIVE_PA5, "SATA_LED_ACTIVE PA5"), + PINCTRL_PIN(TEGRA_PIN_PA6, "PA6"), + PINCTRL_PIN(TEGRA_PIN_DAP1_FS_PB0, "DAP1_FS PB0"), + PINCTRL_PIN(TEGRA_PIN_DAP1_DIN_PB1, "DAP1_DIN PB1"), + PINCTRL_PIN(TEGRA_PIN_DAP1_DOUT_PB2, "DAP1_DOUT PB2"), + PINCTRL_PIN(TEGRA_PIN_DAP1_SCLK_PB3, "DAP1_SCLK PB3"), + PINCTRL_PIN(TEGRA_PIN_SPI2_MOSI_PB4, "SPI2_MOSI PB4"), + PINCTRL_PIN(TEGRA_PIN_SPI2_MISO_PB5, "SPI2_MISO PB5"), + PINCTRL_PIN(TEGRA_PIN_SPI2_SCK_PB6, "SPI2_SCK PB6"), + PINCTRL_PIN(TEGRA_PIN_SPI2_CS0_PB7, "SPI2_CS0 PB7"), + PINCTRL_PIN(TEGRA_PIN_SPI1_MOSI_PC0, "SPI1_MOSI PC0"), + PINCTRL_PIN(TEGRA_PIN_SPI1_MISO_PC1, "SPI1_MISO PC1"), + PINCTRL_PIN(TEGRA_PIN_SPI1_SCK_PC2, "SPI1_SCK PC2"), + PINCTRL_PIN(TEGRA_PIN_SPI1_CS0_PC3, "SPI1_CS0 PC3"), + PINCTRL_PIN(TEGRA_PIN_SPI1_CS1_PC4, "SPI1_CS1 PC4"), + PINCTRL_PIN(TEGRA_PIN_SPI4_SCK_PC5, "SPI4_SCK PC5"), + PINCTRL_PIN(TEGRA_PIN_SPI4_CS0_PC6, "SPI4_CS0 PC6"), + PINCTRL_PIN(TEGRA_PIN_SPI4_MOSI_PC7, "SPI4_MOSI PC7"), + PINCTRL_PIN(TEGRA_PIN_SPI4_MISO_PD0, "SPI4_MISO PD0"), + PINCTRL_PIN(TEGRA_PIN_UART3_TX_PD1, "UART3_TX PD1"), + PINCTRL_PIN(TEGRA_PIN_UART3_RX_PD2, "UART3_RX PD2"), + PINCTRL_PIN(TEGRA_PIN_UART3_RTS_PD3, "UART3_RTS PD3"), + PINCTRL_PIN(TEGRA_PIN_UART3_CTS_PD4, "UART3_CTS PD4"), + PINCTRL_PIN(TEGRA_PIN_DMIC1_CLK_PE0, "DMIC1_CLK PE0"), + PINCTRL_PIN(TEGRA_PIN_DMIC1_DAT_PE1, "DMIC1_DAT PE1"), + PINCTRL_PIN(TEGRA_PIN_DMIC2_CLK_PE2, "DMIC2_CLK PE2"), + PINCTRL_PIN(TEGRA_PIN_DMIC2_DAT_PE3, "DMIC2_DAT PE3"), + PINCTRL_PIN(TEGRA_PIN_DMIC3_CLK_PE4, "DMIC3_CLK PE4"), + PINCTRL_PIN(TEGRA_PIN_DMIC3_DAT_PE5, "DMIC3_DAT PE5"), + PINCTRL_PIN(TEGRA_PIN_PE6, "PE6"), + PINCTRL_PIN(TEGRA_PIN_PE7, "PE7"), + PINCTRL_PIN(TEGRA_PIN_GEN3_I2C_SCL_PF0, "GEN3_I2C_SCL PF0"), + PINCTRL_PIN(TEGRA_PIN_GEN3_I2C_SDA_PF1, "GEN3_I2C_SDA PF1"), + PINCTRL_PIN(TEGRA_PIN_UART2_TX_PG0, "UART2_TX PG0"), + PINCTRL_PIN(TEGRA_PIN_UART2_RX_PG1, "UART2_RX PG1"), + PINCTRL_PIN(TEGRA_PIN_UART2_RTS_PG2, "UART2_RTS PG2"), + PINCTRL_PIN(TEGRA_PIN_UART2_CTS_PG3, "UART2_CTS PG3"), + PINCTRL_PIN(TEGRA_PIN_WIFI_EN_PH0, "WIFI_EN PH0"), + PINCTRL_PIN(TEGRA_PIN_WIFI_RST_PH1, "WIFI_RST PH1"), + PINCTRL_PIN(TEGRA_PIN_WIFI_WAKE_AP_PH2, "WIFI_WAKE_AP PH2"), + PINCTRL_PIN(TEGRA_PIN_AP_WAKE_BT_PH3, "AP_WAKE_BT PH3"), + PINCTRL_PIN(TEGRA_PIN_BT_RST_PH4, "BT_RST PH4"), + PINCTRL_PIN(TEGRA_PIN_BT_WAKE_AP_PH5, "BT_WAKE_AP PH5"), + PINCTRL_PIN(TEGRA_PIN_PH6, "PH6"), + PINCTRL_PIN(TEGRA_PIN_AP_WAKE_NFC_PH7, "AP_WAKE_NFC PH7"), + PINCTRL_PIN(TEGRA_PIN_NFC_EN_PI0, "NFC_EN PI0"), + PINCTRL_PIN(TEGRA_PIN_NFC_INT_PI1, "NFC_INT PI1"), + PINCTRL_PIN(TEGRA_PIN_GPS_EN_PI2, "GPS_EN PI2"), + PINCTRL_PIN(TEGRA_PIN_GPS_RST_PI3, "GPS_RST PI3"), + PINCTRL_PIN(TEGRA_PIN_UART4_TX_PI4, "UART4_TX PI4"), + PINCTRL_PIN(TEGRA_PIN_UART4_RX_PI5, "UART4_RX PI5"), + PINCTRL_PIN(TEGRA_PIN_UART4_RTS_PI6, "UART4_RTS PI6"), + PINCTRL_PIN(TEGRA_PIN_UART4_CTS_PI7, "UART4_CTS PI7"), + PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SDA_PJ0, "GEN1_I2C_SDA PJ0"), + PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SCL_PJ1, "GEN1_I2C_SCL PJ1"), + PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SCL_PJ2, "GEN2_I2C_SCL PJ2"), + PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SDA_PJ3, "GEN2_I2C_SDA PJ3"), + PINCTRL_PIN(TEGRA_PIN_DAP4_FS_PJ4, "DAP4_FS PJ4"), + PINCTRL_PIN(TEGRA_PIN_DAP4_DIN_PJ5, "DAP4_DIN PJ5"), + PINCTRL_PIN(TEGRA_PIN_DAP4_DOUT_PJ6, "DAP4_DOUT PJ6"), + PINCTRL_PIN(TEGRA_PIN_DAP4_SCLK_PJ7, "DAP4_SCLK PJ7"), + PINCTRL_PIN(TEGRA_PIN_PK0, "PK0"), + PINCTRL_PIN(TEGRA_PIN_PK1, "PK1"), + PINCTRL_PIN(TEGRA_PIN_PK2, "PK2"), + PINCTRL_PIN(TEGRA_PIN_PK3, "PK3"), + PINCTRL_PIN(TEGRA_PIN_PK4, "PK4"), + PINCTRL_PIN(TEGRA_PIN_PK5, "PK5"), + PINCTRL_PIN(TEGRA_PIN_PK6, "PK6"), + PINCTRL_PIN(TEGRA_PIN_PK7, "PK7"), + PINCTRL_PIN(TEGRA_PIN_PL0, "PL0"), + PINCTRL_PIN(TEGRA_PIN_PL1, "PL1"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_CLK_PM0, "SDMMC1_CLK PM0"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_CMD_PM1, "SDMMC1_CMD PM1"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT3_PM2, "SDMMC1_DAT3 PM2"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT2_PM3, "SDMMC1_DAT2 PM3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT1_PM4, "SDMMC1_DAT1 PM4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT0_PM5, "SDMMC1_DAT0 PM5"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_CLK_PP0, "SDMMC3_CLK PP0"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_CMD_PP1, "SDMMC3_CMD PP1"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT3_PP2, "SDMMC3_DAT3 PP2"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT2_PP3, "SDMMC3_DAT2 PP3"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT1_PP4, "SDMMC3_DAT1 PP4"), + PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT0_PP5, "SDMMC3_DAT0 PP5"), + PINCTRL_PIN(TEGRA_PIN_CAM1_MCLK_PS0, "CAM1_MCLK PS0"), + PINCTRL_PIN(TEGRA_PIN_CAM2_MCLK_PS1, "CAM2_MCLK PS1"), + PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SCL_PS2, "CAM_I2C_SCL PS2"), + PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SDA_PS3, "CAM_I2C_SDA PS3"), + PINCTRL_PIN(TEGRA_PIN_CAM_RST_PS4, "CAM_RST PS4"), + PINCTRL_PIN(TEGRA_PIN_CAM_AF_EN_PS5, "CAM_AF_EN PS5"), + PINCTRL_PIN(TEGRA_PIN_CAM_FLASH_EN_PS6, "CAM_FLASH_EN PS6"), + PINCTRL_PIN(TEGRA_PIN_CAM1_PWDN_PS7, "CAM1_PWDN PS7"), + PINCTRL_PIN(TEGRA_PIN_CAM2_PWDN_PT0, "CAM2_PWDN PT0"), + PINCTRL_PIN(TEGRA_PIN_CAM1_STROBE_PT1, "CAM1_STROBE PT1"), + PINCTRL_PIN(TEGRA_PIN_UART1_TX_PU0, "UART1_TX PU0"), + PINCTRL_PIN(TEGRA_PIN_UART1_RX_PU1, "UART1_RX PU1"), + PINCTRL_PIN(TEGRA_PIN_UART1_RTS_PU2, "UART1_RTS PU2"), + PINCTRL_PIN(TEGRA_PIN_UART1_CTS_PU3, "UART1_CTS PU3"), + PINCTRL_PIN(TEGRA_PIN_LCD_BL_PWM_PV0, "LCD_BL_PWM PV0"), + PINCTRL_PIN(TEGRA_PIN_LCD_BL_EN_PV1, "LCD_BL_EN PV1"), + PINCTRL_PIN(TEGRA_PIN_LCD_RST_PV2, "LCD_RST PV2"), + PINCTRL_PIN(TEGRA_PIN_LCD_GPIO1_PV3, "LCD_GPIO1 PV3"), + PINCTRL_PIN(TEGRA_PIN_LCD_GPIO2_PV4, "LCD_GPIO2 PV4"), + PINCTRL_PIN(TEGRA_PIN_AP_READY_PV5, "AP_READY PV5"), + PINCTRL_PIN(TEGRA_PIN_TOUCH_RST_PV6, "TOUCH_RST PV6"), + PINCTRL_PIN(TEGRA_PIN_TOUCH_CLK_PV7, "TOUCH_CLK PV7"), + PINCTRL_PIN(TEGRA_PIN_MODEM_WAKE_AP_PX0, "MODEM_WAKE_AP PX0"), + PINCTRL_PIN(TEGRA_PIN_TOUCH_INT_PX1, "TOUCH_INT PX1"), + PINCTRL_PIN(TEGRA_PIN_MOTION_INT_PX2, "MOTION_INT PX2"), + PINCTRL_PIN(TEGRA_PIN_ALS_PROX_INT_PX3, "ALS_PROX_INT PX3"), + PINCTRL_PIN(TEGRA_PIN_TEMP_ALERT_PX4, "TEMP_ALERT PX4"), + PINCTRL_PIN(TEGRA_PIN_BUTTON_POWER_ON_PX5, "BUTTON_POWER_ON PX5"), + PINCTRL_PIN(TEGRA_PIN_BUTTON_VOL_UP_PX6, "BUTTON_VOL_UP PX6"), + PINCTRL_PIN(TEGRA_PIN_BUTTON_VOL_DOWN_PX7, "BUTTON_VOL_DOWN PX7"), + PINCTRL_PIN(TEGRA_PIN_BUTTON_SLIDE_SW_PY0, "BUTTON_SLIDE_SW PY0"), + PINCTRL_PIN(TEGRA_PIN_BUTTON_HOME_PY1, "BUTTON_HOME PY1"), + PINCTRL_PIN(TEGRA_PIN_LCD_TE_PY2, "LCD_TE PY2"), + PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SCL_PY3, "PWR_I2C_SCL PY3"), + PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SDA_PY4, "PWR_I2C_SDA PY4"), + PINCTRL_PIN(TEGRA_PIN_CLK_32K_OUT_PY5, "CLK_32K_OUT PY5"), + PINCTRL_PIN(TEGRA_PIN_PZ0, "PZ0"), + PINCTRL_PIN(TEGRA_PIN_PZ1, "PZ1"), + PINCTRL_PIN(TEGRA_PIN_PZ2, "PZ2"), + PINCTRL_PIN(TEGRA_PIN_PZ3, "PZ3"), + PINCTRL_PIN(TEGRA_PIN_PZ4, "PZ4"), + PINCTRL_PIN(TEGRA_PIN_PZ5, "PZ5"), + PINCTRL_PIN(TEGRA_PIN_DAP2_FS_PAA0, "DAP2_FS PAA0"), + PINCTRL_PIN(TEGRA_PIN_DAP2_SCLK_PAA1, "DAP2_SCLK PAA1"), + PINCTRL_PIN(TEGRA_PIN_DAP2_DIN_PAA2, "DAP2_DIN PAA2"), + PINCTRL_PIN(TEGRA_PIN_DAP2_DOUT_PAA3, "DAP2_DOUT PAA3"), + PINCTRL_PIN(TEGRA_PIN_AUD_MCLK_PBB0, "AUD_MCLK PBB0"), + PINCTRL_PIN(TEGRA_PIN_DVFS_PWM_PBB1, "DVFS_PWM PBB1"), + PINCTRL_PIN(TEGRA_PIN_DVFS_CLK_PBB2, "DVFS_CLK PBB2"), + PINCTRL_PIN(TEGRA_PIN_GPIO_X1_AUD_PBB3, "GPIO_X1_AUD PBB3"), + PINCTRL_PIN(TEGRA_PIN_GPIO_X3_AUD_PBB4, "GPIO_X3_AUD PBB4"), + PINCTRL_PIN(TEGRA_PIN_HDMI_CEC_PCC0, "HDMI_CEC PCC0"), + PINCTRL_PIN(TEGRA_PIN_HDMI_INT_DP_HPD_PCC1, "HDMI_INT_DP_HPD PCC1"), + PINCTRL_PIN(TEGRA_PIN_SPDIF_OUT_PCC2, "SPDIF_OUT PCC2"), + PINCTRL_PIN(TEGRA_PIN_SPDIF_IN_PCC3, "SPDIF_IN PCC3"), + PINCTRL_PIN(TEGRA_PIN_USB_VBUS_EN0_PCC4, "USB_VBUS_EN0 PCC4"), + PINCTRL_PIN(TEGRA_PIN_USB_VBUS_EN1_PCC5, "USB_VBUS_EN1 PCC5"), + PINCTRL_PIN(TEGRA_PIN_DP_HPD0_PCC6, "DP_HPD0 PCC6"), + PINCTRL_PIN(TEGRA_PIN_PCC7, "PCC7"), + PINCTRL_PIN(TEGRA_PIN_SPI2_CS1_PDD0, "SPI2_CS1 PDD0"), + PINCTRL_PIN(TEGRA_PIN_QSPI_SCK_PEE0, "QSPI_SCK PEE0"), + PINCTRL_PIN(TEGRA_PIN_QSPI_CS_N_PEE1, "QSPI_CS_N PEE1"), + PINCTRL_PIN(TEGRA_PIN_QSPI_IO0_PEE2, "QSPI_IO0 PEE2"), + PINCTRL_PIN(TEGRA_PIN_QSPI_IO1_PEE3, "QSPI_IO1 PEE3"), + PINCTRL_PIN(TEGRA_PIN_QSPI_IO2_PEE4, "QSPI_IO2 PEE4"), + PINCTRL_PIN(TEGRA_PIN_QSPI_IO3_PEE5, "QSPI_IO3 PEE5"), + PINCTRL_PIN(TEGRA_PIN_CORE_PWR_REQ, "CORE_PWR_REQ"), + PINCTRL_PIN(TEGRA_PIN_CPU_PWR_REQ, "CPU_PWR_REQ"), + PINCTRL_PIN(TEGRA_PIN_PWR_INT_N, "PWR_INT_N"), + PINCTRL_PIN(TEGRA_PIN_CLK_32K_IN, "CLK_32K_IN"), + PINCTRL_PIN(TEGRA_PIN_JTAG_RTCK, "JTAG_RTCK"), + PINCTRL_PIN(TEGRA_PIN_BATT_BCL, "BATT_BCL"), + PINCTRL_PIN(TEGRA_PIN_CLK_REQ, "CLK_REQ"), + PINCTRL_PIN(TEGRA_PIN_SHUTDOWN, "SHUTDOWN"), +}; + +static const unsigned pex_l0_rst_n_pa0_pins[] = { + TEGRA_PIN_PEX_L0_RST_N_PA0, +}; + +static const unsigned pex_l0_clkreq_n_pa1_pins[] = { + TEGRA_PIN_PEX_L0_CLKREQ_N_PA1, +}; + +static const unsigned pex_wake_n_pa2_pins[] = { + TEGRA_PIN_PEX_WAKE_N_PA2, +}; + +static const unsigned pex_l1_rst_n_pa3_pins[] = { + TEGRA_PIN_PEX_L1_RST_N_PA3, +}; + +static const unsigned pex_l1_clkreq_n_pa4_pins[] = { + TEGRA_PIN_PEX_L1_CLKREQ_N_PA4, +}; + +static const unsigned sata_led_active_pa5_pins[] = { + TEGRA_PIN_SATA_LED_ACTIVE_PA5, +}; + +static const unsigned pa6_pins[] = { + TEGRA_PIN_PA6, +}; + +static const unsigned dap1_fs_pb0_pins[] = { + TEGRA_PIN_DAP1_FS_PB0, +}; + +static const unsigned dap1_din_pb1_pins[] = { + TEGRA_PIN_DAP1_DIN_PB1, +}; + +static const unsigned dap1_dout_pb2_pins[] = { + TEGRA_PIN_DAP1_DOUT_PB2, +}; + +static const unsigned dap1_sclk_pb3_pins[] = { + TEGRA_PIN_DAP1_SCLK_PB3, +}; + +static const unsigned spi2_mosi_pb4_pins[] = { + TEGRA_PIN_SPI2_MOSI_PB4, +}; + +static const unsigned spi2_miso_pb5_pins[] = { + TEGRA_PIN_SPI2_MISO_PB5, +}; + +static const unsigned spi2_sck_pb6_pins[] = { + TEGRA_PIN_SPI2_SCK_PB6, +}; + +static const unsigned spi2_cs0_pb7_pins[] = { + TEGRA_PIN_SPI2_CS0_PB7, +}; + +static const unsigned spi1_mosi_pc0_pins[] = { + TEGRA_PIN_SPI1_MOSI_PC0, +}; + +static const unsigned spi1_miso_pc1_pins[] = { + TEGRA_PIN_SPI1_MISO_PC1, +}; + +static const unsigned spi1_sck_pc2_pins[] = { + TEGRA_PIN_SPI1_SCK_PC2, +}; + +static const unsigned spi1_cs0_pc3_pins[] = { + TEGRA_PIN_SPI1_CS0_PC3, +}; + +static const unsigned spi1_cs1_pc4_pins[] = { + TEGRA_PIN_SPI1_CS1_PC4, +}; + +static const unsigned spi4_sck_pc5_pins[] = { + TEGRA_PIN_SPI4_SCK_PC5, +}; + +static const unsigned spi4_cs0_pc6_pins[] = { + TEGRA_PIN_SPI4_CS0_PC6, +}; + +static const unsigned spi4_mosi_pc7_pins[] = { + TEGRA_PIN_SPI4_MOSI_PC7, +}; + +static const unsigned spi4_miso_pd0_pins[] = { + TEGRA_PIN_SPI4_MISO_PD0, +}; + +static const unsigned uart3_tx_pd1_pins[] = { + TEGRA_PIN_UART3_TX_PD1, +}; + +static const unsigned uart3_rx_pd2_pins[] = { + TEGRA_PIN_UART3_RX_PD2, +}; + +static const unsigned uart3_rts_pd3_pins[] = { + TEGRA_PIN_UART3_RTS_PD3, +}; + +static const unsigned uart3_cts_pd4_pins[] = { + TEGRA_PIN_UART3_CTS_PD4, +}; + +static const unsigned dmic1_clk_pe0_pins[] = { + TEGRA_PIN_DMIC1_CLK_PE0, +}; + +static const unsigned dmic1_dat_pe1_pins[] = { + TEGRA_PIN_DMIC1_DAT_PE1, +}; + +static const unsigned dmic2_clk_pe2_pins[] = { + TEGRA_PIN_DMIC2_CLK_PE2, +}; + +static const unsigned dmic2_dat_pe3_pins[] = { + TEGRA_PIN_DMIC2_DAT_PE3, +}; + +static const unsigned dmic3_clk_pe4_pins[] = { + TEGRA_PIN_DMIC3_CLK_PE4, +}; + +static const unsigned dmic3_dat_pe5_pins[] = { + TEGRA_PIN_DMIC3_DAT_PE5, +}; + +static const unsigned pe6_pins[] = { + TEGRA_PIN_PE6, +}; + +static const unsigned pe7_pins[] = { + TEGRA_PIN_PE7, +}; + +static const unsigned gen3_i2c_scl_pf0_pins[] = { + TEGRA_PIN_GEN3_I2C_SCL_PF0, +}; + +static const unsigned gen3_i2c_sda_pf1_pins[] = { + TEGRA_PIN_GEN3_I2C_SDA_PF1, +}; + +static const unsigned uart2_tx_pg0_pins[] = { + TEGRA_PIN_UART2_TX_PG0, +}; + +static const unsigned uart2_rx_pg1_pins[] = { + TEGRA_PIN_UART2_RX_PG1, +}; + +static const unsigned uart2_rts_pg2_pins[] = { + TEGRA_PIN_UART2_RTS_PG2, +}; + +static const unsigned uart2_cts_pg3_pins[] = { + TEGRA_PIN_UART2_CTS_PG3, +}; + +static const unsigned wifi_en_ph0_pins[] = { + TEGRA_PIN_WIFI_EN_PH0, +}; + +static const unsigned wifi_rst_ph1_pins[] = { + TEGRA_PIN_WIFI_RST_PH1, +}; + +static const unsigned wifi_wake_ap_ph2_pins[] = { + TEGRA_PIN_WIFI_WAKE_AP_PH2, +}; + +static const unsigned ap_wake_bt_ph3_pins[] = { + TEGRA_PIN_AP_WAKE_BT_PH3, +}; + +static const unsigned bt_rst_ph4_pins[] = { + TEGRA_PIN_BT_RST_PH4, +}; + +static const unsigned bt_wake_ap_ph5_pins[] = { + TEGRA_PIN_BT_WAKE_AP_PH5, +}; + +static const unsigned ph6_pins[] = { + TEGRA_PIN_PH6, +}; + +static const unsigned ap_wake_nfc_ph7_pins[] = { + TEGRA_PIN_AP_WAKE_NFC_PH7, +}; + +static const unsigned nfc_en_pi0_pins[] = { + TEGRA_PIN_NFC_EN_PI0, +}; + +static const unsigned nfc_int_pi1_pins[] = { + TEGRA_PIN_NFC_INT_PI1, +}; + +static const unsigned gps_en_pi2_pins[] = { + TEGRA_PIN_GPS_EN_PI2, +}; + +static const unsigned gps_rst_pi3_pins[] = { + TEGRA_PIN_GPS_RST_PI3, +}; + +static const unsigned uart4_tx_pi4_pins[] = { + TEGRA_PIN_UART4_TX_PI4, +}; + +static const unsigned uart4_rx_pi5_pins[] = { + TEGRA_PIN_UART4_RX_PI5, +}; + +static const unsigned uart4_rts_pi6_pins[] = { + TEGRA_PIN_UART4_RTS_PI6, +}; + +static const unsigned uart4_cts_pi7_pins[] = { + TEGRA_PIN_UART4_CTS_PI7, +}; + +static const unsigned gen1_i2c_sda_pj0_pins[] = { + TEGRA_PIN_GEN1_I2C_SDA_PJ0, +}; + +static const unsigned gen1_i2c_scl_pj1_pins[] = { + TEGRA_PIN_GEN1_I2C_SCL_PJ1, +}; + +static const unsigned gen2_i2c_scl_pj2_pins[] = { + TEGRA_PIN_GEN2_I2C_SCL_PJ2, +}; + +static const unsigned gen2_i2c_sda_pj3_pins[] = { + TEGRA_PIN_GEN2_I2C_SDA_PJ3, +}; + +static const unsigned dap4_fs_pj4_pins[] = { + TEGRA_PIN_DAP4_FS_PJ4, +}; + +static const unsigned dap4_din_pj5_pins[] = { + TEGRA_PIN_DAP4_DIN_PJ5, +}; + +static const unsigned dap4_dout_pj6_pins[] = { + TEGRA_PIN_DAP4_DOUT_PJ6, +}; + +static const unsigned dap4_sclk_pj7_pins[] = { + TEGRA_PIN_DAP4_SCLK_PJ7, +}; + +static const unsigned pk0_pins[] = { + TEGRA_PIN_PK0, +}; + +static const unsigned pk1_pins[] = { + TEGRA_PIN_PK1, +}; + +static const unsigned pk2_pins[] = { + TEGRA_PIN_PK2, +}; + +static const unsigned pk3_pins[] = { + TEGRA_PIN_PK3, +}; + +static const unsigned pk4_pins[] = { + TEGRA_PIN_PK4, +}; + +static const unsigned pk5_pins[] = { + TEGRA_PIN_PK5, +}; + +static const unsigned pk6_pins[] = { + TEGRA_PIN_PK6, +}; + +static const unsigned pk7_pins[] = { + TEGRA_PIN_PK7, +}; + +static const unsigned pl0_pins[] = { + TEGRA_PIN_PL0, +}; + +static const unsigned pl1_pins[] = { + TEGRA_PIN_PL1, +}; + +static const unsigned sdmmc1_clk_pm0_pins[] = { + TEGRA_PIN_SDMMC1_CLK_PM0, +}; + +static const unsigned sdmmc1_cmd_pm1_pins[] = { + TEGRA_PIN_SDMMC1_CMD_PM1, +}; + +static const unsigned sdmmc1_dat3_pm2_pins[] = { + TEGRA_PIN_SDMMC1_DAT3_PM2, +}; + +static const unsigned sdmmc1_dat2_pm3_pins[] = { + TEGRA_PIN_SDMMC1_DAT2_PM3, +}; + +static const unsigned sdmmc1_dat1_pm4_pins[] = { + TEGRA_PIN_SDMMC1_DAT1_PM4, +}; + +static const unsigned sdmmc1_dat0_pm5_pins[] = { + TEGRA_PIN_SDMMC1_DAT0_PM5, +}; + +static const unsigned sdmmc3_clk_pp0_pins[] = { + TEGRA_PIN_SDMMC3_CLK_PP0, +}; + +static const unsigned sdmmc3_cmd_pp1_pins[] = { + TEGRA_PIN_SDMMC3_CMD_PP1, +}; + +static const unsigned sdmmc3_dat3_pp2_pins[] = { + TEGRA_PIN_SDMMC3_DAT3_PP2, +}; + +static const unsigned sdmmc3_dat2_pp3_pins[] = { + TEGRA_PIN_SDMMC3_DAT2_PP3, +}; + +static const unsigned sdmmc3_dat1_pp4_pins[] = { + TEGRA_PIN_SDMMC3_DAT1_PP4, +}; + +static const unsigned sdmmc3_dat0_pp5_pins[] = { + TEGRA_PIN_SDMMC3_DAT0_PP5, +}; + +static const unsigned cam1_mclk_ps0_pins[] = { + TEGRA_PIN_CAM1_MCLK_PS0, +}; + +static const unsigned cam2_mclk_ps1_pins[] = { + TEGRA_PIN_CAM2_MCLK_PS1, +}; + +static const unsigned cam_i2c_scl_ps2_pins[] = { + TEGRA_PIN_CAM_I2C_SCL_PS2, +}; + +static const unsigned cam_i2c_sda_ps3_pins[] = { + TEGRA_PIN_CAM_I2C_SDA_PS3, +}; + +static const unsigned cam_rst_ps4_pins[] = { + TEGRA_PIN_CAM_RST_PS4, +}; + +static const unsigned cam_af_en_ps5_pins[] = { + TEGRA_PIN_CAM_AF_EN_PS5, +}; + +static const unsigned cam_flash_en_ps6_pins[] = { + TEGRA_PIN_CAM_FLASH_EN_PS6, +}; + +static const unsigned cam1_pwdn_ps7_pins[] = { + TEGRA_PIN_CAM1_PWDN_PS7, +}; + +static const unsigned cam2_pwdn_pt0_pins[] = { + TEGRA_PIN_CAM2_PWDN_PT0, +}; + +static const unsigned cam1_strobe_pt1_pins[] = { + TEGRA_PIN_CAM1_STROBE_PT1, +}; + +static const unsigned uart1_tx_pu0_pins[] = { + TEGRA_PIN_UART1_TX_PU0, +}; + +static const unsigned uart1_rx_pu1_pins[] = { + TEGRA_PIN_UART1_RX_PU1, +}; + +static const unsigned uart1_rts_pu2_pins[] = { + TEGRA_PIN_UART1_RTS_PU2, +}; + +static const unsigned uart1_cts_pu3_pins[] = { + TEGRA_PIN_UART1_CTS_PU3, +}; + +static const unsigned lcd_bl_pwm_pv0_pins[] = { + TEGRA_PIN_LCD_BL_PWM_PV0, +}; + +static const unsigned lcd_bl_en_pv1_pins[] = { + TEGRA_PIN_LCD_BL_EN_PV1, +}; + +static const unsigned lcd_rst_pv2_pins[] = { + TEGRA_PIN_LCD_RST_PV2, +}; + +static const unsigned lcd_gpio1_pv3_pins[] = { + TEGRA_PIN_LCD_GPIO1_PV3, +}; + +static const unsigned lcd_gpio2_pv4_pins[] = { + TEGRA_PIN_LCD_GPIO2_PV4, +}; + +static const unsigned ap_ready_pv5_pins[] = { + TEGRA_PIN_AP_READY_PV5, +}; + +static const unsigned touch_rst_pv6_pins[] = { + TEGRA_PIN_TOUCH_RST_PV6, +}; + +static const unsigned touch_clk_pv7_pins[] = { + TEGRA_PIN_TOUCH_CLK_PV7, +}; + +static const unsigned modem_wake_ap_px0_pins[] = { + TEGRA_PIN_MODEM_WAKE_AP_PX0, +}; + +static const unsigned touch_int_px1_pins[] = { + TEGRA_PIN_TOUCH_INT_PX1, +}; + +static const unsigned motion_int_px2_pins[] = { + TEGRA_PIN_MOTION_INT_PX2, +}; + +static const unsigned als_prox_int_px3_pins[] = { + TEGRA_PIN_ALS_PROX_INT_PX3, +}; + +static const unsigned temp_alert_px4_pins[] = { + TEGRA_PIN_TEMP_ALERT_PX4, +}; + +static const unsigned button_power_on_px5_pins[] = { + TEGRA_PIN_BUTTON_POWER_ON_PX5, +}; + +static const unsigned button_vol_up_px6_pins[] = { + TEGRA_PIN_BUTTON_VOL_UP_PX6, +}; + +static const unsigned button_vol_down_px7_pins[] = { + TEGRA_PIN_BUTTON_VOL_DOWN_PX7, +}; + +static const unsigned button_slide_sw_py0_pins[] = { + TEGRA_PIN_BUTTON_SLIDE_SW_PY0, +}; + +static const unsigned button_home_py1_pins[] = { + TEGRA_PIN_BUTTON_HOME_PY1, +}; + +static const unsigned lcd_te_py2_pins[] = { + TEGRA_PIN_LCD_TE_PY2, +}; + +static const unsigned pwr_i2c_scl_py3_pins[] = { + TEGRA_PIN_PWR_I2C_SCL_PY3, +}; + +static const unsigned pwr_i2c_sda_py4_pins[] = { + TEGRA_PIN_PWR_I2C_SDA_PY4, +}; + +static const unsigned clk_32k_out_py5_pins[] = { + TEGRA_PIN_CLK_32K_OUT_PY5, +}; + +static const unsigned pz0_pins[] = { + TEGRA_PIN_PZ0, +}; + +static const unsigned pz1_pins[] = { + TEGRA_PIN_PZ1, +}; + +static const unsigned pz2_pins[] = { + TEGRA_PIN_PZ2, +}; + +static const unsigned pz3_pins[] = { + TEGRA_PIN_PZ3, +}; + +static const unsigned pz4_pins[] = { + TEGRA_PIN_PZ4, +}; + +static const unsigned pz5_pins[] = { + TEGRA_PIN_PZ5, +}; + +static const unsigned dap2_fs_paa0_pins[] = { + TEGRA_PIN_DAP2_FS_PAA0, +}; + +static const unsigned dap2_sclk_paa1_pins[] = { + TEGRA_PIN_DAP2_SCLK_PAA1, +}; + +static const unsigned dap2_din_paa2_pins[] = { + TEGRA_PIN_DAP2_DIN_PAA2, +}; + +static const unsigned dap2_dout_paa3_pins[] = { + TEGRA_PIN_DAP2_DOUT_PAA3, +}; + +static const unsigned aud_mclk_pbb0_pins[] = { + TEGRA_PIN_AUD_MCLK_PBB0, +}; + +static const unsigned dvfs_pwm_pbb1_pins[] = { + TEGRA_PIN_DVFS_PWM_PBB1, +}; + +static const unsigned dvfs_clk_pbb2_pins[] = { + TEGRA_PIN_DVFS_CLK_PBB2, +}; + +static const unsigned gpio_x1_aud_pbb3_pins[] = { + TEGRA_PIN_GPIO_X1_AUD_PBB3, +}; + +static const unsigned gpio_x3_aud_pbb4_pins[] = { + TEGRA_PIN_GPIO_X3_AUD_PBB4, +}; + +static const unsigned hdmi_cec_pcc0_pins[] = { + TEGRA_PIN_HDMI_CEC_PCC0, +}; + +static const unsigned hdmi_int_dp_hpd_pcc1_pins[] = { + TEGRA_PIN_HDMI_INT_DP_HPD_PCC1, +}; + +static const unsigned spdif_out_pcc2_pins[] = { + TEGRA_PIN_SPDIF_OUT_PCC2, +}; + +static const unsigned spdif_in_pcc3_pins[] = { + TEGRA_PIN_SPDIF_IN_PCC3, +}; + +static const unsigned usb_vbus_en0_pcc4_pins[] = { + TEGRA_PIN_USB_VBUS_EN0_PCC4, +}; + +static const unsigned usb_vbus_en1_pcc5_pins[] = { + TEGRA_PIN_USB_VBUS_EN1_PCC5, +}; + +static const unsigned dp_hpd0_pcc6_pins[] = { + TEGRA_PIN_DP_HPD0_PCC6, +}; + +static const unsigned pcc7_pins[] = { + TEGRA_PIN_PCC7, +}; + +static const unsigned spi2_cs1_pdd0_pins[] = { + TEGRA_PIN_SPI2_CS1_PDD0, +}; + +static const unsigned qspi_sck_pee0_pins[] = { + TEGRA_PIN_QSPI_SCK_PEE0, +}; + +static const unsigned qspi_cs_n_pee1_pins[] = { + TEGRA_PIN_QSPI_CS_N_PEE1, +}; + +static const unsigned qspi_io0_pee2_pins[] = { + TEGRA_PIN_QSPI_IO0_PEE2, +}; + +static const unsigned qspi_io1_pee3_pins[] = { + TEGRA_PIN_QSPI_IO1_PEE3, +}; + +static const unsigned qspi_io2_pee4_pins[] = { + TEGRA_PIN_QSPI_IO2_PEE4, +}; + +static const unsigned qspi_io3_pee5_pins[] = { + TEGRA_PIN_QSPI_IO3_PEE5, +}; + +static const unsigned core_pwr_req_pins[] = { + TEGRA_PIN_CORE_PWR_REQ, +}; + +static const unsigned cpu_pwr_req_pins[] = { + TEGRA_PIN_CPU_PWR_REQ, +}; + +static const unsigned pwr_int_n_pins[] = { + TEGRA_PIN_PWR_INT_N, +}; + +static const unsigned clk_32k_in_pins[] = { + TEGRA_PIN_CLK_32K_IN, +}; + +static const unsigned jtag_rtck_pins[] = { + TEGRA_PIN_JTAG_RTCK, +}; + +static const unsigned batt_bcl_pins[] = { + TEGRA_PIN_BATT_BCL, +}; + +static const unsigned clk_req_pins[] = { + TEGRA_PIN_CLK_REQ, +}; + +static const unsigned shutdown_pins[] = { + TEGRA_PIN_SHUTDOWN, +}; + +static const unsigned drive_pa6_pins[] = { + TEGRA_PIN_PA6, +}; + +static const unsigned drive_pcc7_pins[] = { + TEGRA_PIN_PCC7, +}; + +static const unsigned drive_pe6_pins[] = { + TEGRA_PIN_PE6, +}; + +static const unsigned drive_pe7_pins[] = { + TEGRA_PIN_PE7, +}; + +static const unsigned drive_ph6_pins[] = { + TEGRA_PIN_PH6, +}; + +static const unsigned drive_pk0_pins[] = { + TEGRA_PIN_PK0, +}; + +static const unsigned drive_pk1_pins[] = { + TEGRA_PIN_PK1, +}; + +static const unsigned drive_pk2_pins[] = { + TEGRA_PIN_PK2, +}; + +static const unsigned drive_pk3_pins[] = { + TEGRA_PIN_PK3, +}; + +static const unsigned drive_pk4_pins[] = { + TEGRA_PIN_PK4, +}; + +static const unsigned drive_pk5_pins[] = { + TEGRA_PIN_PK5, +}; + +static const unsigned drive_pk6_pins[] = { + TEGRA_PIN_PK6, +}; + +static const unsigned drive_pk7_pins[] = { + TEGRA_PIN_PK7, +}; + +static const unsigned drive_pl0_pins[] = { + TEGRA_PIN_PL0, +}; + +static const unsigned drive_pl1_pins[] = { + TEGRA_PIN_PL1, +}; + +static const unsigned drive_pz0_pins[] = { + TEGRA_PIN_PZ0, +}; + +static const unsigned drive_pz1_pins[] = { + TEGRA_PIN_PZ1, +}; + +static const unsigned drive_pz2_pins[] = { + TEGRA_PIN_PZ2, +}; + +static const unsigned drive_pz3_pins[] = { + TEGRA_PIN_PZ3, +}; + +static const unsigned drive_pz4_pins[] = { + TEGRA_PIN_PZ4, +}; + +static const unsigned drive_pz5_pins[] = { + TEGRA_PIN_PZ5, +}; + +static const unsigned drive_sdmmc1_pins[] = { + TEGRA_PIN_SDMMC1_CLK_PM0, + TEGRA_PIN_SDMMC1_CMD_PM1, + TEGRA_PIN_SDMMC1_DAT3_PM2, + TEGRA_PIN_SDMMC1_DAT2_PM3, + TEGRA_PIN_SDMMC1_DAT1_PM4, + TEGRA_PIN_SDMMC1_DAT0_PM5, +}; + +static const unsigned drive_sdmmc2_pins[] = { +}; + +static const unsigned drive_sdmmc3_pins[] = { + TEGRA_PIN_SDMMC3_CLK_PP0, + TEGRA_PIN_SDMMC3_CMD_PP1, + TEGRA_PIN_SDMMC3_DAT3_PP2, + TEGRA_PIN_SDMMC3_DAT2_PP3, + TEGRA_PIN_SDMMC3_DAT1_PP4, + TEGRA_PIN_SDMMC3_DAT0_PP5, +}; + +static const unsigned drive_sdmmc4_pins[] = { +}; + +enum tegra_mux { + TEGRA_MUX_AUD, + TEGRA_MUX_BCL, + TEGRA_MUX_BLINK, + TEGRA_MUX_CCLA, + TEGRA_MUX_CEC, + TEGRA_MUX_CLDVFS, + TEGRA_MUX_CLK, + TEGRA_MUX_CORE, + TEGRA_MUX_CPU, + TEGRA_MUX_DISPLAYA, + TEGRA_MUX_DISPLAYB, + TEGRA_MUX_DMIC1, + TEGRA_MUX_DMIC2, + TEGRA_MUX_DMIC3, + TEGRA_MUX_DP, + TEGRA_MUX_DTV, + TEGRA_MUX_EXTPERIPH3, + TEGRA_MUX_I2C1, + TEGRA_MUX_I2C2, + TEGRA_MUX_I2C3, + TEGRA_MUX_I2CPMU, + TEGRA_MUX_I2CVI, + TEGRA_MUX_I2S1, + TEGRA_MUX_I2S2, + TEGRA_MUX_I2S3, + TEGRA_MUX_I2S4A, + TEGRA_MUX_I2S4B, + TEGRA_MUX_I2S5A, + TEGRA_MUX_I2S5B, + TEGRA_MUX_IQC0, + TEGRA_MUX_IQC1, + TEGRA_MUX_JTAG, + TEGRA_MUX_PE, + TEGRA_MUX_PE0, + TEGRA_MUX_PE1, + TEGRA_MUX_PMI, + TEGRA_MUX_PWM0, + TEGRA_MUX_PWM1, + TEGRA_MUX_PWM2, + TEGRA_MUX_PWM3, + TEGRA_MUX_QSPI, + TEGRA_MUX_RSVD0, + TEGRA_MUX_RSVD1, + TEGRA_MUX_RSVD2, + TEGRA_MUX_RSVD3, + TEGRA_MUX_SATA, + TEGRA_MUX_SDMMC1, + TEGRA_MUX_SDMMC3, + TEGRA_MUX_SHUTDOWN, + TEGRA_MUX_SOC, + TEGRA_MUX_SOR0, + TEGRA_MUX_SOR1, + TEGRA_MUX_SPDIF, + TEGRA_MUX_SPI1, + TEGRA_MUX_SPI2, + TEGRA_MUX_SPI3, + TEGRA_MUX_SPI4, + TEGRA_MUX_SYS, + TEGRA_MUX_TOUCH, + TEGRA_MUX_UART, + TEGRA_MUX_UARTA, + TEGRA_MUX_UARTB, + TEGRA_MUX_UARTC, + TEGRA_MUX_UARTD, + TEGRA_MUX_USB, + TEGRA_MUX_VGP1, + TEGRA_MUX_VGP2, + TEGRA_MUX_VGP3, + TEGRA_MUX_VGP4, + TEGRA_MUX_VGP5, + TEGRA_MUX_VGP6, + TEGRA_MUX_VIMCLK, + TEGRA_MUX_VIMCLK2, +}; + +#define FUNCTION(fname) \ + { \ + .name = #fname, \ + } + +static struct tegra_function tegra210_functions[] = { + FUNCTION(aud), + FUNCTION(bcl), + FUNCTION(blink), + FUNCTION(ccla), + FUNCTION(cec), + FUNCTION(cldvfs), + FUNCTION(clk), + FUNCTION(core), + FUNCTION(cpu), + FUNCTION(displaya), + FUNCTION(displayb), + FUNCTION(dmic1), + FUNCTION(dmic2), + FUNCTION(dmic3), + FUNCTION(dp), + FUNCTION(dtv), + FUNCTION(extperiph3), + FUNCTION(i2c1), + FUNCTION(i2c2), + FUNCTION(i2c3), + FUNCTION(i2cpmu), + FUNCTION(i2cvi), + FUNCTION(i2s1), + FUNCTION(i2s2), + FUNCTION(i2s3), + FUNCTION(i2s4a), + FUNCTION(i2s4b), + FUNCTION(i2s5a), + FUNCTION(i2s5b), + FUNCTION(iqc0), + FUNCTION(iqc1), + FUNCTION(jtag), + FUNCTION(pe), + FUNCTION(pe0), + FUNCTION(pe1), + FUNCTION(pmi), + FUNCTION(pwm0), + FUNCTION(pwm1), + FUNCTION(pwm2), + FUNCTION(pwm3), + FUNCTION(qspi), + FUNCTION(rsvd0), + FUNCTION(rsvd1), + FUNCTION(rsvd2), + FUNCTION(rsvd3), + FUNCTION(sata), + FUNCTION(sdmmc1), + FUNCTION(sdmmc3), + FUNCTION(shutdown), + FUNCTION(soc), + FUNCTION(sor0), + FUNCTION(sor1), + FUNCTION(spdif), + FUNCTION(spi1), + FUNCTION(spi2), + FUNCTION(spi3), + FUNCTION(spi4), + FUNCTION(sys), + FUNCTION(touch), + FUNCTION(uart), + FUNCTION(uarta), + FUNCTION(uartb), + FUNCTION(uartc), + FUNCTION(uartd), + FUNCTION(usb), + FUNCTION(vgp1), + FUNCTION(vgp2), + FUNCTION(vgp3), + FUNCTION(vgp4), + FUNCTION(vgp5), + FUNCTION(vgp6), + FUNCTION(vimclk), + FUNCTION(vimclk2), +}; + +#define DRV_PINGROUP_REG_A 0x8d4 /* bank 0 */ +#define PINGROUP_REG_A 0x3000 /* bank 1 */ + +#define DRV_PINGROUP_REG(r) ((r) - DRV_PINGROUP_REG_A) +#define PINGROUP_REG(r) ((r) - PINGROUP_REG_A) + +#define PINGROUP_BIT_Y(b) (b) +#define PINGROUP_BIT_N(b) (-1) + +#define PINGROUP(pg_name, f0, f1, f2, f3, r, hsm, drvtype, e_io_hv, \ + rdrv, drvdn_b, drvdn_w, drvup_b, drvup_w, slwr_b, \ + slwr_w, slwf_b, slwf_w) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .funcs = { \ + TEGRA_MUX_##f0, \ + TEGRA_MUX_##f1, \ + TEGRA_MUX_##f2, \ + TEGRA_MUX_##f3, \ + }, \ + .mux_reg = PINGROUP_REG(r), \ + .mux_bank = 1, \ + .mux_bit = 0, \ + .pupd_reg = PINGROUP_REG(r), \ + .pupd_bank = 1, \ + .pupd_bit = 2, \ + .tri_reg = PINGROUP_REG(r), \ + .tri_bank = 1, \ + .tri_bit = 4, \ + .einput_bit = 6, \ + .odrain_bit = 11, \ + .lock_bit = 7, \ + .ioreset_bit = -1, \ + .rcv_sel_bit = PINGROUP_BIT_##e_io_hv(10), \ + .hsm_bit = PINGROUP_BIT_##hsm(9), \ + .schmitt_bit = 12, \ + .drvtype_bit = PINGROUP_BIT_##drvtype(13), \ + .drv_reg = DRV_PINGROUP_REG(rdrv), \ + .drv_bank = 0, \ + .lpmd_bit = -1, \ + .drvdn_bit = drvdn_b, \ + .drvdn_width = drvdn_w, \ + .drvup_bit = drvup_b, \ + .drvup_width = drvup_w, \ + .slwr_bit = slwr_b, \ + .slwr_width = slwr_w, \ + .slwf_bit = slwf_b, \ + .slwf_width = slwf_w, \ + } + +#define DRV_PINGROUP(pg_name, r, drvdn_b, drvdn_w, drvup_b, drvup_w, \ + slwr_b, slwr_w, slwf_b, slwf_w) \ + { \ + .name = "drive_" #pg_name, \ + .pins = drive_##pg_name##_pins, \ + .npins = ARRAY_SIZE(drive_##pg_name##_pins), \ + .mux_reg = -1, \ + .pupd_reg = -1, \ + .tri_reg = -1, \ + .einput_bit = -1, \ + .odrain_bit = -1, \ + .lock_bit = -1, \ + .ioreset_bit = -1, \ + .rcv_sel_bit = -1, \ + .drv_reg = DRV_PINGROUP_REG(r), \ + .drv_bank = 0, \ + .hsm_bit = -1, \ + .schmitt_bit = -1, \ + .lpmd_bit = -1, \ + .drvdn_bit = drvdn_b, \ + .drvdn_width = drvdn_w, \ + .drvup_bit = drvup_b, \ + .drvup_width = drvup_w, \ + .slwr_bit = slwr_b, \ + .slwr_width = slwr_w, \ + .slwf_bit = slwf_b, \ + .slwf_width = slwf_w, \ + .drvtype_bit = -1, \ + } + +static const struct tegra_pingroup tegra210_groups[] = { + /* pg_name, f0, f1, f2, f3, r, hsm, drvtype, e_io_hv, rdrv, drvdn_b, drvdn_w, drvup_b, drvup_w, slwr_b, slwr_w, slwf_b, slwf_w */ + PINGROUP(sdmmc1_clk_pm0, SDMMC1, RSVD1, RSVD2, RSVD3, 0x3000, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(sdmmc1_cmd_pm1, SDMMC1, SPI3, RSVD2, RSVD3, 0x3004, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(sdmmc1_dat3_pm2, SDMMC1, SPI3, RSVD2, RSVD3, 0x3008, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(sdmmc1_dat2_pm3, SDMMC1, SPI3, RSVD2, RSVD3, 0x300c, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(sdmmc1_dat1_pm4, SDMMC1, SPI3, RSVD2, RSVD3, 0x3010, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(sdmmc1_dat0_pm5, SDMMC1, RSVD1, RSVD2, RSVD3, 0x3014, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(sdmmc3_clk_pp0, SDMMC3, RSVD1, RSVD2, RSVD3, 0x301c, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(sdmmc3_cmd_pp1, SDMMC3, RSVD1, RSVD2, RSVD3, 0x3020, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(sdmmc3_dat0_pp5, SDMMC3, RSVD1, RSVD2, RSVD3, 0x3024, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(sdmmc3_dat1_pp4, SDMMC3, RSVD1, RSVD2, RSVD3, 0x3028, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(sdmmc3_dat2_pp3, SDMMC3, RSVD1, RSVD2, RSVD3, 0x302c, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(sdmmc3_dat3_pp2, SDMMC3, RSVD1, RSVD2, RSVD3, 0x3030, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pex_l0_rst_n_pa0, PE0, RSVD1, RSVD2, RSVD3, 0x3038, N, N, Y, 0xa5c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(pex_l0_clkreq_n_pa1, PE0, RSVD1, RSVD2, RSVD3, 0x303c, N, N, Y, 0xa58, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(pex_wake_n_pa2, PE, RSVD1, RSVD2, RSVD3, 0x3040, N, N, Y, 0xa68, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(pex_l1_rst_n_pa3, PE1, RSVD1, RSVD2, RSVD3, 0x3044, N, N, Y, 0xa64, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(pex_l1_clkreq_n_pa4, PE1, RSVD1, RSVD2, RSVD3, 0x3048, N, N, Y, 0xa60, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(sata_led_active_pa5, SATA, RSVD1, RSVD2, RSVD3, 0x304c, N, N, N, 0xa94, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(spi1_mosi_pc0, SPI1, RSVD1, RSVD2, RSVD3, 0x3050, Y, Y, N, 0xae0, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(spi1_miso_pc1, SPI1, RSVD1, RSVD2, RSVD3, 0x3054, Y, Y, N, 0xadc, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(spi1_sck_pc2, SPI1, RSVD1, RSVD2, RSVD3, 0x3058, Y, Y, N, 0xae4, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(spi1_cs0_pc3, SPI1, RSVD1, RSVD2, RSVD3, 0x305c, Y, Y, N, 0xad4, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(spi1_cs1_pc4, SPI1, RSVD1, RSVD2, RSVD3, 0x3060, Y, Y, N, 0xad8, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(spi2_mosi_pb4, SPI2, DTV, RSVD2, RSVD3, 0x3064, Y, Y, N, 0xaf4, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(spi2_miso_pb5, SPI2, DTV, RSVD2, RSVD3, 0x3068, Y, Y, N, 0xaf0, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(spi2_sck_pb6, SPI2, DTV, RSVD2, RSVD3, 0x306c, Y, Y, N, 0xaf8, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(spi2_cs0_pb7, SPI2, DTV, RSVD2, RSVD3, 0x3070, Y, Y, N, 0xae8, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(spi2_cs1_pdd0, SPI2, RSVD1, RSVD2, RSVD3, 0x3074, Y, Y, N, 0xaec, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(spi4_mosi_pc7, SPI4, RSVD1, RSVD2, RSVD3, 0x3078, Y, Y, N, 0xb04, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(spi4_miso_pd0, SPI4, RSVD1, RSVD2, RSVD3, 0x307c, Y, Y, N, 0xb00, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(spi4_sck_pc5, SPI4, RSVD1, RSVD2, RSVD3, 0x3080, Y, Y, N, 0xb08, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(spi4_cs0_pc6, SPI4, RSVD1, RSVD2, RSVD3, 0x3084, Y, Y, N, 0xafc, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(qspi_sck_pee0, QSPI, RSVD1, RSVD2, RSVD3, 0x3088, Y, Y, N, 0xa90, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(qspi_cs_n_pee1, QSPI, RSVD1, RSVD2, RSVD3, 0x308c, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(qspi_io0_pee2, QSPI, RSVD1, RSVD2, RSVD3, 0x3090, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(qspi_io1_pee3, QSPI, RSVD1, RSVD2, RSVD3, 0x3094, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(qspi_io2_pee4, QSPI, RSVD1, RSVD2, RSVD3, 0x3098, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(qspi_io3_pee5, QSPI, RSVD1, RSVD2, RSVD3, 0x309c, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(dmic1_clk_pe0, DMIC1, I2S3, RSVD2, RSVD3, 0x30a4, N, N, N, 0x984, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(dmic1_dat_pe1, DMIC1, I2S3, RSVD2, RSVD3, 0x30a8, N, N, N, 0x988, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(dmic2_clk_pe2, DMIC2, I2S3, RSVD2, RSVD3, 0x30ac, N, N, N, 0x98c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(dmic2_dat_pe3, DMIC2, I2S3, RSVD2, RSVD3, 0x30b0, N, N, N, 0x990, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(dmic3_clk_pe4, DMIC3, I2S5A, RSVD2, RSVD3, 0x30b4, N, N, N, 0x994, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(dmic3_dat_pe5, DMIC3, I2S5A, RSVD2, RSVD3, 0x30b8, N, N, N, 0x998, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(gen1_i2c_scl_pj1, I2C1, RSVD1, RSVD2, RSVD3, 0x30bc, N, N, Y, 0x9a8, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(gen1_i2c_sda_pj0, I2C1, RSVD1, RSVD2, RSVD3, 0x30c0, N, N, Y, 0x9ac, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(gen2_i2c_scl_pj2, I2C2, RSVD1, RSVD2, RSVD3, 0x30c4, N, N, Y, 0x9b0, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(gen2_i2c_sda_pj3, I2C2, RSVD1, RSVD2, RSVD3, 0x30c8, N, N, Y, 0x9b4, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(gen3_i2c_scl_pf0, I2C3, RSVD1, RSVD2, RSVD3, 0x30cc, N, N, Y, 0x9b8, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(gen3_i2c_sda_pf1, I2C3, RSVD1, RSVD2, RSVD3, 0x30d0, N, N, Y, 0x9bc, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(cam_i2c_scl_ps2, I2C3, I2CVI, RSVD2, RSVD3, 0x30d4, N, N, Y, 0x934, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(cam_i2c_sda_ps3, I2C3, I2CVI, RSVD2, RSVD3, 0x30d8, N, N, Y, 0x938, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(pwr_i2c_scl_py3, I2CPMU, RSVD1, RSVD2, RSVD3, 0x30dc, N, N, Y, 0xa6c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(pwr_i2c_sda_py4, I2CPMU, RSVD1, RSVD2, RSVD3, 0x30e0, N, N, Y, 0xa70, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart1_tx_pu0, UARTA, RSVD1, RSVD2, RSVD3, 0x30e4, N, N, N, 0xb28, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart1_rx_pu1, UARTA, RSVD1, RSVD2, RSVD3, 0x30e8, N, N, N, 0xb24, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart1_rts_pu2, UARTA, RSVD1, RSVD2, RSVD3, 0x30ec, N, N, N, 0xb20, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart1_cts_pu3, UARTA, RSVD1, RSVD2, RSVD3, 0x30f0, N, N, N, 0xb1c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart2_tx_pg0, UARTB, I2S4A, SPDIF, UART, 0x30f4, N, N, N, 0xb38, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart2_rx_pg1, UARTB, I2S4A, SPDIF, UART, 0x30f8, N, N, N, 0xb34, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart2_rts_pg2, UARTB, I2S4A, RSVD2, UART, 0x30fc, N, N, N, 0xb30, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart2_cts_pg3, UARTB, I2S4A, RSVD2, UART, 0x3100, N, N, N, 0xb2c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart3_tx_pd1, UARTC, SPI4, RSVD2, RSVD3, 0x3104, N, N, N, 0xb48, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart3_rx_pd2, UARTC, SPI4, RSVD2, RSVD3, 0x3108, N, N, N, 0xb44, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart3_rts_pd3, UARTC, SPI4, RSVD2, RSVD3, 0x310c, N, N, N, 0xb40, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart3_cts_pd4, UARTC, SPI4, RSVD2, RSVD3, 0x3110, N, N, N, 0xb3c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart4_tx_pi4, UARTD, UART, RSVD2, RSVD3, 0x3114, N, N, N, 0xb58, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart4_rx_pi5, UARTD, UART, RSVD2, RSVD3, 0x3118, N, N, N, 0xb54, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart4_rts_pi6, UARTD, UART, RSVD2, RSVD3, 0x311c, N, N, N, 0xb50, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(uart4_cts_pi7, UARTD, UART, RSVD2, RSVD3, 0x3120, N, N, N, 0xb4c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(dap1_fs_pb0, I2S1, RSVD1, RSVD2, RSVD3, 0x3124, Y, Y, N, 0x95c, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(dap1_din_pb1, I2S1, RSVD1, RSVD2, RSVD3, 0x3128, Y, Y, N, 0x954, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(dap1_dout_pb2, I2S1, RSVD1, RSVD2, RSVD3, 0x312c, Y, Y, N, 0x958, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(dap1_sclk_pb3, I2S1, RSVD1, RSVD2, RSVD3, 0x3130, Y, Y, N, 0x960, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(dap2_fs_paa0, I2S2, RSVD1, RSVD2, RSVD3, 0x3134, Y, Y, N, 0x96c, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(dap2_din_paa2, I2S2, RSVD1, RSVD2, RSVD3, 0x3138, Y, Y, N, 0x964, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(dap2_dout_paa3, I2S2, RSVD1, RSVD2, RSVD3, 0x313c, Y, Y, N, 0x968, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(dap2_sclk_paa1, I2S2, RSVD1, RSVD2, RSVD3, 0x3140, Y, Y, N, 0x970, -1, -1, -1, -1, 28, 2, 30, 2), + PINGROUP(dap4_fs_pj4, I2S4B, RSVD1, RSVD2, RSVD3, 0x3144, N, N, N, 0x97c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(dap4_din_pj5, I2S4B, RSVD1, RSVD2, RSVD3, 0x3148, N, N, N, 0x974, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(dap4_dout_pj6, I2S4B, RSVD1, RSVD2, RSVD3, 0x314c, N, N, N, 0x978, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(dap4_sclk_pj7, I2S4B, RSVD1, RSVD2, RSVD3, 0x3150, N, N, N, 0x980, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(cam1_mclk_ps0, EXTPERIPH3, RSVD1, RSVD2, RSVD3, 0x3154, N, N, N, 0x918, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(cam2_mclk_ps1, EXTPERIPH3, RSVD1, RSVD2, RSVD3, 0x3158, N, N, N, 0x924, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(jtag_rtck, JTAG, RSVD1, RSVD2, RSVD3, 0x315c, N, N, N, 0xa2c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(clk_32k_in, CLK, RSVD1, RSVD2, RSVD3, 0x3160, N, N, N, 0x940, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(clk_32k_out_py5, SOC, BLINK, RSVD2, RSVD3, 0x3164, N, N, N, 0x944, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(batt_bcl, BCL, RSVD1, RSVD2, RSVD3, 0x3168, N, N, Y, 0x8f8, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(clk_req, SYS, RSVD1, RSVD2, RSVD3, 0x316c, N, N, N, 0x948, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(cpu_pwr_req, CPU, RSVD1, RSVD2, RSVD3, 0x3170, N, N, N, 0x950, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(pwr_int_n, PMI, RSVD1, RSVD2, RSVD3, 0x3174, N, N, N, 0xa74, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(shutdown, SHUTDOWN, RSVD1, RSVD2, RSVD3, 0x3178, N, N, N, 0xac8, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(core_pwr_req, CORE, RSVD1, RSVD2, RSVD3, 0x317c, N, N, N, 0x94c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(aud_mclk_pbb0, AUD, RSVD1, RSVD2, RSVD3, 0x3180, N, N, N, 0x8f4, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(dvfs_pwm_pbb1, RSVD0, CLDVFS, SPI3, RSVD3, 0x3184, N, N, N, 0x9a4, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(dvfs_clk_pbb2, RSVD0, CLDVFS, SPI3, RSVD3, 0x3188, N, N, N, 0x9a0, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(gpio_x1_aud_pbb3, RSVD0, RSVD1, SPI3, RSVD3, 0x318c, N, N, N, 0xa14, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(gpio_x3_aud_pbb4, RSVD0, RSVD1, SPI3, RSVD3, 0x3190, N, N, N, 0xa18, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(pcc7, RSVD0, RSVD1, RSVD2, RSVD3, 0x3194, N, N, Y, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(hdmi_cec_pcc0, CEC, RSVD1, RSVD2, RSVD3, 0x3198, N, N, Y, 0xa24, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(hdmi_int_dp_hpd_pcc1, DP, RSVD1, RSVD2, RSVD3, 0x319c, N, N, Y, 0xa28, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(spdif_out_pcc2, SPDIF, RSVD1, RSVD2, RSVD3, 0x31a0, N, N, N, 0xad0, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(spdif_in_pcc3, SPDIF, RSVD1, RSVD2, RSVD3, 0x31a4, N, N, N, 0xacc, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(usb_vbus_en0_pcc4, USB, RSVD1, RSVD2, RSVD3, 0x31a8, N, N, Y, 0xb5c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(usb_vbus_en1_pcc5, USB, RSVD1, RSVD2, RSVD3, 0x31ac, N, N, Y, 0xb60, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(dp_hpd0_pcc6, DP, RSVD1, RSVD2, RSVD3, 0x31b0, N, N, N, 0x99c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(wifi_en_ph0, RSVD0, RSVD1, RSVD2, RSVD3, 0x31b4, N, N, N, 0xb64, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(wifi_rst_ph1, RSVD0, RSVD1, RSVD2, RSVD3, 0x31b8, N, N, N, 0xb68, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(wifi_wake_ap_ph2, RSVD0, RSVD1, RSVD2, RSVD3, 0x31bc, N, N, N, 0xb6c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(ap_wake_bt_ph3, RSVD0, UARTB, SPDIF, RSVD3, 0x31c0, N, N, N, 0x8ec, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(bt_rst_ph4, RSVD0, UARTB, SPDIF, RSVD3, 0x31c4, N, N, N, 0x8fc, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(bt_wake_ap_ph5, RSVD0, RSVD1, RSVD2, RSVD3, 0x31c8, N, N, N, 0x900, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(ap_wake_nfc_ph7, RSVD0, RSVD1, RSVD2, RSVD3, 0x31cc, N, N, N, 0x8f0, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(nfc_en_pi0, RSVD0, RSVD1, RSVD2, RSVD3, 0x31d0, N, N, N, 0xa50, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(nfc_int_pi1, RSVD0, RSVD1, RSVD2, RSVD3, 0x31d4, N, N, N, 0xa54, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(gps_en_pi2, RSVD0, RSVD1, RSVD2, RSVD3, 0x31d8, N, N, N, 0xa1c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(gps_rst_pi3, RSVD0, RSVD1, RSVD2, RSVD3, 0x31dc, N, N, N, 0xa20, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(cam_rst_ps4, VGP1, RSVD1, RSVD2, RSVD3, 0x31e0, N, N, N, 0x93c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(cam_af_en_ps5, VIMCLK, VGP2, RSVD2, RSVD3, 0x31e4, N, N, N, 0x92c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(cam_flash_en_ps6, VIMCLK, VGP3, RSVD2, RSVD3, 0x31e8, N, N, N, 0x930, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(cam1_pwdn_ps7, VGP4, RSVD1, RSVD2, RSVD3, 0x31ec, N, N, N, 0x91c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(cam2_pwdn_pt0, VGP5, RSVD1, RSVD2, RSVD3, 0x31f0, N, N, N, 0x928, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(cam1_strobe_pt1, VGP6, RSVD1, RSVD2, RSVD3, 0x31f4, N, N, N, 0x920, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(lcd_te_py2, DISPLAYA, RSVD1, RSVD2, RSVD3, 0x31f8, N, N, N, 0xa44, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(lcd_bl_pwm_pv0, DISPLAYA, PWM0, SOR0, RSVD3, 0x31fc, N, N, N, 0xa34, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(lcd_bl_en_pv1, RSVD0, RSVD1, RSVD2, RSVD3, 0x3200, N, N, N, 0xa30, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(lcd_rst_pv2, RSVD0, RSVD1, RSVD2, RSVD3, 0x3204, N, N, N, 0xa40, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(lcd_gpio1_pv3, DISPLAYB, RSVD1, RSVD2, RSVD3, 0x3208, N, N, N, 0xa38, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(lcd_gpio2_pv4, DISPLAYB, PWM1, RSVD2, SOR1, 0x320c, N, N, N, 0xa3c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(ap_ready_pv5, RSVD0, RSVD1, RSVD2, RSVD3, 0x3210, N, N, N, 0x8e8, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(touch_rst_pv6, RSVD0, RSVD1, RSVD2, RSVD3, 0x3214, N, N, N, 0xb18, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(touch_clk_pv7, TOUCH, RSVD1, RSVD2, RSVD3, 0x3218, N, N, N, 0xb10, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(modem_wake_ap_px0, RSVD0, RSVD1, RSVD2, RSVD3, 0x321c, N, N, N, 0xa48, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(touch_int_px1, RSVD0, RSVD1, RSVD2, RSVD3, 0x3220, N, N, N, 0xb14, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(motion_int_px2, RSVD0, RSVD1, RSVD2, RSVD3, 0x3224, N, N, N, 0xa4c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(als_prox_int_px3, RSVD0, RSVD1, RSVD2, RSVD3, 0x3228, N, N, N, 0x8e4, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(temp_alert_px4, RSVD0, RSVD1, RSVD2, RSVD3, 0x322c, N, N, N, 0xb0c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(button_power_on_px5, RSVD0, RSVD1, RSVD2, RSVD3, 0x3230, N, N, N, 0x908, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(button_vol_up_px6, RSVD0, RSVD1, RSVD2, RSVD3, 0x3234, N, N, N, 0x914, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(button_vol_down_px7, RSVD0, RSVD1, RSVD2, RSVD3, 0x3238, N, N, N, 0x910, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(button_slide_sw_py0, RSVD0, RSVD1, RSVD2, RSVD3, 0x323c, N, N, N, 0x90c, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(button_home_py1, RSVD0, RSVD1, RSVD2, RSVD3, 0x3240, N, N, N, 0x904, 12, 5, 20, 5, -1, -1, -1, -1), + PINGROUP(pa6, SATA, RSVD1, RSVD2, RSVD3, 0x3244, N, N, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pe6, RSVD0, I2S5A, PWM2, RSVD3, 0x3248, N, N, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pe7, RSVD0, I2S5A, PWM3, RSVD3, 0x324c, N, N, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(ph6, RSVD0, RSVD1, RSVD2, RSVD3, 0x3250, N, N, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pk0, IQC0, I2S5B, RSVD2, RSVD3, 0x3254, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pk1, IQC0, I2S5B, RSVD2, RSVD3, 0x3258, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pk2, IQC0, I2S5B, RSVD2, RSVD3, 0x325c, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pk3, IQC0, I2S5B, RSVD2, RSVD3, 0x3260, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pk4, IQC1, RSVD1, RSVD2, RSVD3, 0x3264, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pk5, IQC1, RSVD1, RSVD2, RSVD3, 0x3268, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pk6, IQC1, RSVD1, RSVD2, RSVD3, 0x326c, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pk7, IQC1, RSVD1, RSVD2, RSVD3, 0x3270, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pl0, RSVD0, RSVD1, RSVD2, RSVD3, 0x3274, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pl1, SOC, RSVD1, RSVD2, RSVD3, 0x3278, Y, Y, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pz0, VIMCLK2, RSVD1, RSVD2, RSVD3, 0x327c, N, N, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pz1, VIMCLK2, SDMMC1, RSVD2, RSVD3, 0x3280, N, N, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pz2, SDMMC3, CCLA, RSVD2, RSVD3, 0x3284, N, N, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pz3, SDMMC3, RSVD1, RSVD2, RSVD3, 0x3288, N, N, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pz4, SDMMC1, RSVD1, RSVD2, RSVD3, 0x328c, N, N, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + PINGROUP(pz5, SOC, RSVD1, RSVD2, RSVD3, 0x3290, N, N, N, -1, -1, -1, -1, -1, -1, -1, -1, -1), + + /* pg_name, r, drvdn_b, drvdn_w, drvup_b, drvup_w, slwr_b, slwr_w, slwf_b, slwf_w */ + DRV_PINGROUP(pa6, 0x9c0, 12, 5, 20, 5, -1, -1, -1, -1), + DRV_PINGROUP(pcc7, 0x9c4, 12, 5, 20, 5, -1, -1, -1, -1), + DRV_PINGROUP(pe6, 0x9c8, 12, 5, 20, 5, -1, -1, -1, -1), + DRV_PINGROUP(pe7, 0x9cc, 12, 5, 20, 5, -1, -1, -1, -1), + DRV_PINGROUP(ph6, 0x9d0, 12, 5, 20, 5, -1, -1, -1, -1), + DRV_PINGROUP(pk0, 0x9d4, -1, -1, -1, -1, 28, 2, 30, 2), + DRV_PINGROUP(pk1, 0x9d8, -1, -1, -1, -1, 28, 2, 30, 2), + DRV_PINGROUP(pk2, 0x9dc, -1, -1, -1, -1, 28, 2, 30, 2), + DRV_PINGROUP(pk3, 0x9e0, -1, -1, -1, -1, 28, 2, 30, 2), + DRV_PINGROUP(pk4, 0x9e4, -1, -1, -1, -1, 28, 2, 30, 2), + DRV_PINGROUP(pk5, 0x9e8, -1, -1, -1, -1, 28, 2, 30, 2), + DRV_PINGROUP(pk6, 0x9ec, -1, -1, -1, -1, 28, 2, 30, 2), + DRV_PINGROUP(pk7, 0x9f0, -1, -1, -1, -1, 28, 2, 30, 2), + DRV_PINGROUP(pl0, 0x9f4, -1, -1, -1, -1, 28, 2, 30, 2), + DRV_PINGROUP(pl1, 0x9f8, -1, -1, -1, -1, 28, 2, 30, 2), + DRV_PINGROUP(pz0, 0x9fc, 12, 7, 20, 7, -1, -1, -1, -1), + DRV_PINGROUP(pz1, 0xa00, 12, 7, 20, 7, -1, -1, -1, -1), + DRV_PINGROUP(pz2, 0xa04, 12, 7, 20, 7, -1, -1, -1, -1), + DRV_PINGROUP(pz3, 0xa08, 12, 7, 20, 7, -1, -1, -1, -1), + DRV_PINGROUP(pz4, 0xa0c, 12, 7, 20, 7, -1, -1, -1, -1), + DRV_PINGROUP(pz5, 0xa10, 12, 7, 20, 7, -1, -1, -1, -1), + DRV_PINGROUP(sdmmc1, 0xa98, 12, 7, 20, 7, 28, 2, 30, 2), + DRV_PINGROUP(sdmmc2, 0xa9c, 2, 6, 8, 6, 28, 2, 30, 2), + DRV_PINGROUP(sdmmc3, 0xab0, 12, 7, 20, 7, 28, 2, 30, 2), + DRV_PINGROUP(sdmmc4, 0xab4, 2, 6, 8, 6, 28, 2, 30, 2), +}; + +static const struct tegra_pinctrl_soc_data tegra210_pinctrl = { + .ngpios = NUM_GPIOS, + .pins = tegra210_pins, + .npins = ARRAY_SIZE(tegra210_pins), + .functions = tegra210_functions, + .nfunctions = ARRAY_SIZE(tegra210_functions), + .groups = tegra210_groups, + .ngroups = ARRAY_SIZE(tegra210_groups), + .hsm_in_mux = true, + .schmitt_in_mux = true, + .drvtype_in_mux = true, +}; + +static int tegra210_pinctrl_probe(struct platform_device *pdev) +{ + return tegra_pinctrl_probe(pdev, &tegra210_pinctrl); +} + +static const struct of_device_id tegra210_pinctrl_of_match[] = { + { .compatible = "nvidia,tegra210-pinmux", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra210_pinctrl_of_match); + +static struct platform_driver tegra210_pinctrl_driver = { + .driver = { + .name = "tegra210-pinctrl", + .of_match_table = tegra210_pinctrl_of_match, + }, + .probe = tegra210_pinctrl_probe, + .remove = tegra_pinctrl_remove, +}; +module_platform_driver(tegra210_pinctrl_driver); + +MODULE_AUTHOR("NVIDIA"); +MODULE_DESCRIPTION("NVIDIA Tegra210 pinctrl driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/pinctrl-tegra30.c b/drivers/pinctrl/pinctrl-tegra30.c index f6edc2ff5494d5..47b2fd8bb2e9da 100644 --- a/drivers/pinctrl/pinctrl-tegra30.c +++ b/drivers/pinctrl/pinctrl-tegra30.c @@ -2108,70 +2108,69 @@ static struct tegra_function tegra30_functions[] = { #define DRV_PINGROUP_REG_A 0x868 /* bank 0 */ #define PINGROUP_REG_A 0x3000 /* bank 1 */ +#define DRV_PINGROUP_REG(r) ((r) - DRV_PINGROUP_REG_A) #define PINGROUP_REG(r) ((r) - PINGROUP_REG_A) #define PINGROUP_BIT_Y(b) (b) #define PINGROUP_BIT_N(b) (-1) -#define PINGROUP(pg_name, f0, f1, f2, f3, r, od, ior) \ - { \ - .name = #pg_name, \ - .pins = pg_name##_pins, \ - .npins = ARRAY_SIZE(pg_name##_pins), \ - .funcs = { \ - TEGRA_MUX_##f0, \ - TEGRA_MUX_##f1, \ - TEGRA_MUX_##f2, \ - TEGRA_MUX_##f3, \ - }, \ - .mux_reg = PINGROUP_REG(r), \ - .mux_bank = 1, \ - .mux_bit = 0, \ - .pupd_reg = PINGROUP_REG(r), \ - .pupd_bank = 1, \ - .pupd_bit = 2, \ - .tri_reg = PINGROUP_REG(r), \ - .tri_bank = 1, \ - .tri_bit = 4, \ - .einput_bit = PINGROUP_BIT_Y(5), \ - .odrain_bit = PINGROUP_BIT_##od(6), \ - .lock_bit = PINGROUP_BIT_Y(7), \ - .ioreset_bit = PINGROUP_BIT_##ior(8), \ - .rcv_sel_bit = -1, \ - .drv_reg = -1, \ +#define PINGROUP(pg_name, f0, f1, f2, f3, r, od, ior) \ + { \ + .name = #pg_name, \ + .pins = pg_name##_pins, \ + .npins = ARRAY_SIZE(pg_name##_pins), \ + .funcs = { \ + TEGRA_MUX_##f0, \ + TEGRA_MUX_##f1, \ + TEGRA_MUX_##f2, \ + TEGRA_MUX_##f3, \ + }, \ + .mux_reg = PINGROUP_REG(r), \ + .mux_bank = 1, \ + .mux_bit = 0, \ + .pupd_reg = PINGROUP_REG(r), \ + .pupd_bank = 1, \ + .pupd_bit = 2, \ + .tri_reg = PINGROUP_REG(r), \ + .tri_bank = 1, \ + .tri_bit = 4, \ + .einput_bit = 5, \ + .odrain_bit = PINGROUP_BIT_##od(6), \ + .lock_bit = 7, \ + .ioreset_bit = PINGROUP_BIT_##ior(8), \ + .rcv_sel_bit = -1, \ + .drv_reg = -1, \ } -#define DRV_PINGROUP_REG(r) ((r) - DRV_PINGROUP_REG_A) - -#define DRV_PINGROUP(pg_name, r, hsm_b, schmitt_b, lpmd_b, \ - drvdn_b, drvdn_w, drvup_b, drvup_w, \ - slwr_b, slwr_w, slwf_b, slwf_w) \ - { \ - .name = "drive_" #pg_name, \ - .pins = drive_##pg_name##_pins, \ - .npins = ARRAY_SIZE(drive_##pg_name##_pins), \ - .mux_reg = -1, \ - .pupd_reg = -1, \ - .tri_reg = -1, \ - .einput_bit = -1, \ - .odrain_bit = -1, \ - .lock_bit = -1, \ - .ioreset_bit = -1, \ - .rcv_sel_bit = -1, \ - .drv_reg = DRV_PINGROUP_REG(r), \ - .drv_bank = 0, \ - .hsm_bit = hsm_b, \ - .schmitt_bit = schmitt_b, \ - .lpmd_bit = lpmd_b, \ - .drvdn_bit = drvdn_b, \ - .drvdn_width = drvdn_w, \ - .drvup_bit = drvup_b, \ - .drvup_width = drvup_w, \ - .slwr_bit = slwr_b, \ - .slwr_width = slwr_w, \ - .slwf_bit = slwf_b, \ - .slwf_width = slwf_w, \ - .drvtype_bit = -1, \ +#define DRV_PINGROUP(pg_name, r, hsm_b, schmitt_b, lpmd_b, drvdn_b, \ + drvdn_w, drvup_b, drvup_w, slwr_b, slwr_w, \ + slwf_b, slwf_w) \ + { \ + .name = "drive_" #pg_name, \ + .pins = drive_##pg_name##_pins, \ + .npins = ARRAY_SIZE(drive_##pg_name##_pins), \ + .mux_reg = -1, \ + .pupd_reg = -1, \ + .tri_reg = -1, \ + .einput_bit = -1, \ + .odrain_bit = -1, \ + .lock_bit = -1, \ + .ioreset_bit = -1, \ + .rcv_sel_bit = -1, \ + .drv_reg = DRV_PINGROUP_REG(r), \ + .drv_bank = 0, \ + .hsm_bit = hsm_b, \ + .schmitt_bit = schmitt_b, \ + .lpmd_bit = lpmd_b, \ + .drvdn_bit = drvdn_b, \ + .drvdn_width = drvdn_w, \ + .drvup_bit = drvup_b, \ + .drvup_width = drvup_w, \ + .slwr_bit = slwr_b, \ + .slwr_width = slwr_w, \ + .slwf_bit = slwf_b, \ + .slwf_width = slwf_w, \ + .drvtype_bit = -1, \ } static const struct tegra_pingroup tegra30_groups[] = { @@ -2477,6 +2476,9 @@ static const struct tegra_pinctrl_soc_data tegra30_pinctrl = { .nfunctions = ARRAY_SIZE(tegra30_functions), .groups = tegra30_groups, .ngroups = ARRAY_SIZE(tegra30_groups), + .hsm_in_mux = false, + .schmitt_in_mux = false, + .drvtype_in_mux = false, }; static int tegra30_pinctrl_probe(struct platform_device *pdev) diff --git a/drivers/pinctrl/pinctrl-tz1090-pdc.c b/drivers/pinctrl/pinctrl-tz1090-pdc.c index fab6aafa6a9f44..8a8911bb883a20 100644 --- a/drivers/pinctrl/pinctrl-tz1090-pdc.c +++ b/drivers/pinctrl/pinctrl-tz1090-pdc.c @@ -969,7 +969,7 @@ static int tz1090_pdc_pinctrl_remove(struct platform_device *pdev) return 0; } -static struct of_device_id tz1090_pdc_pinctrl_of_match[] = { +static const struct of_device_id tz1090_pdc_pinctrl_of_match[] = { { .compatible = "img,tz1090-pdc-pinctrl", }, { }, }; diff --git a/drivers/pinctrl/pinctrl-tz1090.c b/drivers/pinctrl/pinctrl-tz1090.c index 8bd73075f9dd3e..fc5594a530c2f9 100644 --- a/drivers/pinctrl/pinctrl-tz1090.c +++ b/drivers/pinctrl/pinctrl-tz1090.c @@ -1984,7 +1984,7 @@ static int tz1090_pinctrl_remove(struct platform_device *pdev) return 0; } -static struct of_device_id tz1090_pinctrl_of_match[] = { +static const struct of_device_id tz1090_pinctrl_of_match[] = { { .compatible = "img,tz1090-pinctrl", }, { }, }; diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index a535f9c23678b1..f3d800f796c2f4 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -193,11 +193,11 @@ static int msm_config_reg(struct msm_pinctrl *pctrl, *mask = 7; break; case PIN_CONFIG_OUTPUT: + case PIN_CONFIG_INPUT_ENABLE: *bit = g->oe_bit; *mask = 1; break; default: - dev_err(pctrl->dev, "Invalid config param %04x\n", param); return -ENOTSUPP; } @@ -261,10 +261,14 @@ static int msm_config_group_get(struct pinctrl_dev *pctldev, val = readl(pctrl->regs + g->io_reg); arg = !!(val & BIT(g->in_bit)); break; + case PIN_CONFIG_INPUT_ENABLE: + /* Pin is output */ + if (arg) + return -EINVAL; + arg = 1; + break; default: - dev_err(pctrl->dev, "Unsupported config parameter: %x\n", - param); - return -EINVAL; + return -ENOTSUPP; } *config = pinconf_to_config_packed(param, arg); @@ -333,6 +337,10 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, /* enable output */ arg = 1; break; + case PIN_CONFIG_INPUT_ENABLE: + /* disable output */ + arg = 0; + break; default: dev_err(pctrl->dev, "Unsupported config parameter: %x\n", param); @@ -357,6 +365,7 @@ static int msm_config_group_set(struct pinctrl_dev *pctldev, } static const struct pinconf_ops msm_pinconf_ops = { + .is_generic = true, .pin_config_group_get = msm_config_group_get, .pin_config_group_set = msm_config_group_set, }; diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c index 0f11a26d932b61..b2d22218a2582f 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c @@ -810,6 +810,7 @@ static int pmic_gpio_remove(struct platform_device *pdev) } static const struct of_device_id pmic_gpio_of_match[] = { + { .compatible = "qcom,pm8916-gpio" }, /* 4 GPIO's */ { .compatible = "qcom,pm8941-gpio" }, /* 36 GPIO's */ { .compatible = "qcom,pma8084-gpio" }, /* 22 GPIO's */ { }, diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c b/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c index a8924dba335e51..8f36c5f9194903 100644 --- a/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c +++ b/drivers/pinctrl/qcom/pinctrl-spmi-mpp.c @@ -925,6 +925,7 @@ static int pmic_mpp_remove(struct platform_device *pdev) static const struct of_device_id pmic_mpp_of_match[] = { { .compatible = "qcom,pm8841-mpp" }, /* 4 MPP's */ + { .compatible = "qcom,pm8916-mpp" }, /* 4 MPP's */ { .compatible = "qcom,pm8941-mpp" }, /* 8 MPP's */ { .compatible = "qcom,pma8084-mpp" }, /* 8 MPP's */ { }, diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c index c8f83f96546c07..0b7afa50121a24 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos.c +++ b/drivers/pinctrl/samsung/pinctrl-exynos.c @@ -1240,6 +1240,159 @@ const struct samsung_pin_ctrl exynos5420_pin_ctrl[] __initconst = { }, }; +/* pin banks of exynos5433 pin-controller - ALIVE */ +static const struct samsung_pin_bank_data exynos5433_pin_banks0[] = { + EXYNOS_PIN_BANK_EINTW(8, 0x000, "gpa0", 0x00), + EXYNOS_PIN_BANK_EINTW(8, 0x020, "gpa1", 0x04), + EXYNOS_PIN_BANK_EINTW(8, 0x040, "gpa2", 0x08), + EXYNOS_PIN_BANK_EINTW(8, 0x060, "gpa3", 0x0c), +}; + +/* pin banks of exynos5433 pin-controller - AUD */ +static const struct samsung_pin_bank_data exynos5433_pin_banks1[] = { + EXYNOS_PIN_BANK_EINTG(7, 0x000, "gpz0", 0x00), + EXYNOS_PIN_BANK_EINTG(4, 0x020, "gpz1", 0x04), +}; + +/* pin banks of exynos5433 pin-controller - CPIF */ +static const struct samsung_pin_bank_data exynos5433_pin_banks2[] = { + EXYNOS_PIN_BANK_EINTG(2, 0x000, "gpv6", 0x00), +}; + +/* pin banks of exynos5433 pin-controller - eSE */ +static const struct samsung_pin_bank_data exynos5433_pin_banks3[] = { + EXYNOS_PIN_BANK_EINTG(3, 0x000, "gpj2", 0x00), +}; + +/* pin banks of exynos5433 pin-controller - FINGER */ +static const struct samsung_pin_bank_data exynos5433_pin_banks4[] = { + EXYNOS_PIN_BANK_EINTG(4, 0x000, "gpd5", 0x00), +}; + +/* pin banks of exynos5433 pin-controller - FSYS */ +static const struct samsung_pin_bank_data exynos5433_pin_banks5[] = { + EXYNOS_PIN_BANK_EINTG(6, 0x000, "gph1", 0x00), + EXYNOS_PIN_BANK_EINTG(7, 0x020, "gpr4", 0x04), + EXYNOS_PIN_BANK_EINTG(5, 0x040, "gpr0", 0x08), + EXYNOS_PIN_BANK_EINTG(8, 0x060, "gpr1", 0x0c), + EXYNOS_PIN_BANK_EINTG(2, 0x080, "gpr2", 0x10), + EXYNOS_PIN_BANK_EINTG(8, 0x0a0, "gpr3", 0x14), +}; + +/* pin banks of exynos5433 pin-controller - IMEM */ +static const struct samsung_pin_bank_data exynos5433_pin_banks6[] = { + EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpf0", 0x00), +}; + +/* pin banks of exynos5433 pin-controller - NFC */ +static const struct samsung_pin_bank_data exynos5433_pin_banks7[] = { + EXYNOS_PIN_BANK_EINTG(3, 0x000, "gpj0", 0x00), +}; + +/* pin banks of exynos5433 pin-controller - PERIC */ +static const struct samsung_pin_bank_data exynos5433_pin_banks8[] = { + EXYNOS_PIN_BANK_EINTG(6, 0x000, "gpv7", 0x00), + EXYNOS_PIN_BANK_EINTG(5, 0x020, "gpb0", 0x04), + EXYNOS_PIN_BANK_EINTG(8, 0x040, "gpc0", 0x08), + EXYNOS_PIN_BANK_EINTG(2, 0x060, "gpc1", 0x0c), + EXYNOS_PIN_BANK_EINTG(6, 0x080, "gpc2", 0x10), + EXYNOS_PIN_BANK_EINTG(8, 0x0a0, "gpc3", 0x14), + EXYNOS_PIN_BANK_EINTG(2, 0x0c0, "gpg0", 0x18), + EXYNOS_PIN_BANK_EINTG(4, 0x0e0, "gpd0", 0x1c), + EXYNOS_PIN_BANK_EINTG(6, 0x100, "gpd1", 0x20), + EXYNOS_PIN_BANK_EINTG(8, 0x120, "gpd2", 0x24), + EXYNOS_PIN_BANK_EINTG(5, 0x140, "gpd4", 0x28), + EXYNOS_PIN_BANK_EINTG(2, 0x160, "gpd8", 0x2c), + EXYNOS_PIN_BANK_EINTG(7, 0x180, "gpd6", 0x30), + EXYNOS_PIN_BANK_EINTG(3, 0x1a0, "gpd7", 0x34), + EXYNOS_PIN_BANK_EINTG(5, 0x1c0, "gpg1", 0x38), + EXYNOS_PIN_BANK_EINTG(2, 0x1e0, "gpg2", 0x3c), + EXYNOS_PIN_BANK_EINTG(8, 0x200, "gpg3", 0x40), +}; + +/* pin banks of exynos5433 pin-controller - TOUCH */ +static const struct samsung_pin_bank_data exynos5433_pin_banks9[] = { + EXYNOS_PIN_BANK_EINTG(3, 0x000, "gpj1", 0x00), +}; + +/* + * Samsung pinctrl driver data for Exynos5433 SoC. Exynos5433 SoC includes + * ten gpio/pin-mux/pinconfig controllers. + */ +const struct samsung_pin_ctrl exynos5433_pin_ctrl[] = { + { + /* pin-controller instance 0 data */ + .pin_banks = exynos5433_pin_banks0, + .nr_banks = ARRAY_SIZE(exynos5433_pin_banks0), + .eint_wkup_init = exynos_eint_wkup_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 1 data */ + .pin_banks = exynos5433_pin_banks1, + .nr_banks = ARRAY_SIZE(exynos5433_pin_banks1), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 2 data */ + .pin_banks = exynos5433_pin_banks2, + .nr_banks = ARRAY_SIZE(exynos5433_pin_banks2), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 3 data */ + .pin_banks = exynos5433_pin_banks3, + .nr_banks = ARRAY_SIZE(exynos5433_pin_banks3), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 4 data */ + .pin_banks = exynos5433_pin_banks4, + .nr_banks = ARRAY_SIZE(exynos5433_pin_banks4), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 5 data */ + .pin_banks = exynos5433_pin_banks5, + .nr_banks = ARRAY_SIZE(exynos5433_pin_banks5), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 6 data */ + .pin_banks = exynos5433_pin_banks6, + .nr_banks = ARRAY_SIZE(exynos5433_pin_banks6), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 7 data */ + .pin_banks = exynos5433_pin_banks7, + .nr_banks = ARRAY_SIZE(exynos5433_pin_banks7), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 8 data */ + .pin_banks = exynos5433_pin_banks8, + .nr_banks = ARRAY_SIZE(exynos5433_pin_banks8), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, { + /* pin-controller instance 9 data */ + .pin_banks = exynos5433_pin_banks9, + .nr_banks = ARRAY_SIZE(exynos5433_pin_banks9), + .eint_gpio_init = exynos_eint_gpio_init, + .suspend = exynos_pinctrl_suspend, + .resume = exynos_pinctrl_resume, + }, +}; + /* pin banks of exynos7 pin-controller - ALIVE */ static const struct samsung_pin_bank_data exynos7_pin_banks0[] __initconst = { EXYNOS_PIN_BANK_EINTW(8, 0x000, "gpa0", 0x00), @@ -1324,7 +1477,6 @@ const struct samsung_pin_ctrl exynos7_pin_ctrl[] __initconst = { /* pin-controller instance 0 Alive data */ .pin_banks = exynos7_pin_banks0, .nr_banks = ARRAY_SIZE(exynos7_pin_banks0), - .eint_gpio_init = exynos_eint_gpio_init, .eint_wkup_init = exynos_eint_wkup_init, }, { /* pin-controller instance 1 BUS0 data */ diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c index ec580af3585635..ed165ba2eb2f18 100644 --- a/drivers/pinctrl/samsung/pinctrl-samsung.c +++ b/drivers/pinctrl/samsung/pinctrl-samsung.c @@ -1239,6 +1239,8 @@ static const struct of_device_id samsung_pinctrl_dt_match[] = { .data = (void *)exynos5260_pin_ctrl }, { .compatible = "samsung,exynos5420-pinctrl", .data = (void *)exynos5420_pin_ctrl }, + { .compatible = "samsung,exynos5433-pinctrl", + .data = (void *)exynos5433_pin_ctrl }, { .compatible = "samsung,s5pv210-pinctrl", .data = (void *)s5pv210_pin_ctrl }, { .compatible = "samsung,exynos7-pinctrl", diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.h b/drivers/pinctrl/samsung/pinctrl-samsung.h index 1b8c0139d604b1..c1239ff6157d0d 100644 --- a/drivers/pinctrl/samsung/pinctrl-samsung.h +++ b/drivers/pinctrl/samsung/pinctrl-samsung.h @@ -271,6 +271,7 @@ extern const struct samsung_pin_ctrl exynos4415_pin_ctrl[]; extern const struct samsung_pin_ctrl exynos5250_pin_ctrl[]; extern const struct samsung_pin_ctrl exynos5260_pin_ctrl[]; extern const struct samsung_pin_ctrl exynos5420_pin_ctrl[]; +extern const struct samsung_pin_ctrl exynos5433_pin_ctrl[]; extern const struct samsung_pin_ctrl exynos7_pin_ctrl[]; extern const struct samsung_pin_ctrl s3c64xx_pin_ctrl[]; extern const struct samsung_pin_ctrl s3c2412_pin_ctrl[]; diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c index a56280814a3f88..7b2c9495c38324 100644 --- a/drivers/pinctrl/sh-pfc/core.c +++ b/drivers/pinctrl/sh-pfc/core.c @@ -92,10 +92,10 @@ static int sh_pfc_map_resources(struct sh_pfc *pfc, return 0; } -static void __iomem *sh_pfc_phys_to_virt(struct sh_pfc *pfc, - unsigned long address) +static void __iomem *sh_pfc_phys_to_virt(struct sh_pfc *pfc, u32 reg) { struct sh_pfc_window *window; + phys_addr_t address = reg; unsigned int i; /* scan through physical windows and convert address */ @@ -144,8 +144,7 @@ static int sh_pfc_enum_in_range(u16 enum_id, const struct pinmux_range *r) return 1; } -unsigned long sh_pfc_read_raw_reg(void __iomem *mapped_reg, - unsigned long reg_width) +u32 sh_pfc_read_raw_reg(void __iomem *mapped_reg, unsigned int reg_width) { switch (reg_width) { case 8: @@ -160,8 +159,8 @@ unsigned long sh_pfc_read_raw_reg(void __iomem *mapped_reg, return 0; } -void sh_pfc_write_raw_reg(void __iomem *mapped_reg, unsigned long reg_width, - unsigned long data) +void sh_pfc_write_raw_reg(void __iomem *mapped_reg, unsigned int reg_width, + u32 data) { switch (reg_width) { case 8: @@ -180,10 +179,9 @@ void sh_pfc_write_raw_reg(void __iomem *mapped_reg, unsigned long reg_width, static void sh_pfc_config_reg_helper(struct sh_pfc *pfc, const struct pinmux_cfg_reg *crp, - unsigned long in_pos, - void __iomem **mapped_regp, - unsigned long *maskp, - unsigned long *posp) + unsigned int in_pos, + void __iomem **mapped_regp, u32 *maskp, + unsigned int *posp) { unsigned int k; @@ -202,15 +200,16 @@ static void sh_pfc_config_reg_helper(struct sh_pfc *pfc, static void sh_pfc_write_config_reg(struct sh_pfc *pfc, const struct pinmux_cfg_reg *crp, - unsigned long field, unsigned long value) + unsigned int field, u32 value) { void __iomem *mapped_reg; - unsigned long mask, pos, data; + unsigned int pos; + u32 mask, data; sh_pfc_config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); - dev_dbg(pfc->dev, "write_reg addr = %lx, value = %ld, field = %ld, " - "r_width = %ld, f_width = %ld\n", + dev_dbg(pfc->dev, "write_reg addr = %x, value = 0x%x, field = %u, " + "r_width = %u, f_width = %u\n", crp->reg, value, field, crp->reg_width, crp->field_width); mask = ~(mask << pos); @@ -229,26 +228,28 @@ static void sh_pfc_write_config_reg(struct sh_pfc *pfc, } static int sh_pfc_get_config_reg(struct sh_pfc *pfc, u16 enum_id, - const struct pinmux_cfg_reg **crp, int *fieldp, - int *valuep) + const struct pinmux_cfg_reg **crp, + unsigned int *fieldp, u32 *valuep) { - const struct pinmux_cfg_reg *config_reg; - unsigned long r_width, f_width, curr_width, ncomb; - unsigned int k, m, n, pos, bit_pos; + unsigned int k = 0; - k = 0; while (1) { - config_reg = pfc->info->cfg_regs + k; - - r_width = config_reg->reg_width; - f_width = config_reg->field_width; + const struct pinmux_cfg_reg *config_reg = + pfc->info->cfg_regs + k; + unsigned int r_width = config_reg->reg_width; + unsigned int f_width = config_reg->field_width; + unsigned int curr_width; + unsigned int bit_pos; + unsigned int pos = 0; + unsigned int m = 0; if (!r_width) break; - pos = 0; - m = 0; for (bit_pos = 0; bit_pos < r_width; bit_pos += curr_width) { + u32 ncomb; + u32 n; + if (f_width) curr_width = f_width; else @@ -297,11 +298,8 @@ static int sh_pfc_mark_to_enum(struct sh_pfc *pfc, u16 mark, int pos, int sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type) { - const struct pinmux_cfg_reg *cr = NULL; - u16 enum_id; const struct pinmux_range *range; - int in_range, pos, field, value; - int ret; + int pos = 0; switch (pinmux_type) { case PINMUX_TYPE_GPIO: @@ -321,13 +319,15 @@ int sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type) return -EINVAL; } - pos = 0; - enum_id = 0; - field = 0; - value = 0; - /* Iterate over all the configuration fields we need to update. */ while (1) { + const struct pinmux_cfg_reg *cr; + unsigned int field; + u16 enum_id; + u32 value; + int in_range; + int ret; + pos = sh_pfc_mark_to_enum(pfc, mark, pos, &enum_id); if (pos < 0) return pos; @@ -579,9 +579,6 @@ static int sh_pfc_remove(struct platform_device *pdev) } static const struct platform_device_id sh_pfc_id_table[] = { -#ifdef CONFIG_PINCTRL_PFC_EMEV2 - { "pfc-emev2", (kernel_ulong_t)&emev2_pinmux_info }, -#endif #ifdef CONFIG_PINCTRL_PFC_R8A73A4 { "pfc-r8a73a4", (kernel_ulong_t)&r8a73a4_pinmux_info }, #endif @@ -594,12 +591,6 @@ static const struct platform_device_id sh_pfc_id_table[] = { #ifdef CONFIG_PINCTRL_PFC_R8A7779 { "pfc-r8a7779", (kernel_ulong_t)&r8a7779_pinmux_info }, #endif -#ifdef CONFIG_PINCTRL_PFC_R8A7790 - { "pfc-r8a7790", (kernel_ulong_t)&r8a7790_pinmux_info }, -#endif -#ifdef CONFIG_PINCTRL_PFC_R8A7791 - { "pfc-r8a7791", (kernel_ulong_t)&r8a7791_pinmux_info }, -#endif #ifdef CONFIG_PINCTRL_PFC_SH7203 { "pfc-sh7203", (kernel_ulong_t)&sh7203_pinmux_info }, #endif diff --git a/drivers/pinctrl/sh-pfc/core.h b/drivers/pinctrl/sh-pfc/core.h index 6b59d63b9c01e7..6dc8a6fc27468b 100644 --- a/drivers/pinctrl/sh-pfc/core.h +++ b/drivers/pinctrl/sh-pfc/core.h @@ -57,10 +57,9 @@ int sh_pfc_unregister_gpiochip(struct sh_pfc *pfc); int sh_pfc_register_pinctrl(struct sh_pfc *pfc); int sh_pfc_unregister_pinctrl(struct sh_pfc *pfc); -unsigned long sh_pfc_read_raw_reg(void __iomem *mapped_reg, - unsigned long reg_width); -void sh_pfc_write_raw_reg(void __iomem *mapped_reg, unsigned long reg_width, - unsigned long data); +u32 sh_pfc_read_raw_reg(void __iomem *mapped_reg, unsigned int reg_width); +void sh_pfc_write_raw_reg(void __iomem *mapped_reg, unsigned int reg_width, + u32 data); int sh_pfc_get_pin_index(struct sh_pfc *pfc, unsigned int pin); int sh_pfc_config_mux(struct sh_pfc *pfc, unsigned mark, int pinmux_type); diff --git a/drivers/pinctrl/sh-pfc/gpio.c b/drivers/pinctrl/sh-pfc/gpio.c index 80f641ee4dea31..ba353735ecf2be 100644 --- a/drivers/pinctrl/sh-pfc/gpio.c +++ b/drivers/pinctrl/sh-pfc/gpio.c @@ -21,7 +21,7 @@ struct sh_pfc_gpio_data_reg { const struct pinmux_data_reg *info; - unsigned long shadow; + u32 shadow; }; struct sh_pfc_gpio_pin { @@ -59,19 +59,20 @@ static void gpio_get_data_reg(struct sh_pfc_chip *chip, unsigned int offset, *bit = gpio_pin->dbit; } -static unsigned long gpio_read_data_reg(struct sh_pfc_chip *chip, - const struct pinmux_data_reg *dreg) +static u32 gpio_read_data_reg(struct sh_pfc_chip *chip, + const struct pinmux_data_reg *dreg) { - void __iomem *mem = dreg->reg - chip->mem->phys + chip->mem->virt; + phys_addr_t address = dreg->reg; + void __iomem *mem = address - chip->mem->phys + chip->mem->virt; return sh_pfc_read_raw_reg(mem, dreg->reg_width); } static void gpio_write_data_reg(struct sh_pfc_chip *chip, - const struct pinmux_data_reg *dreg, - unsigned long value) + const struct pinmux_data_reg *dreg, u32 value) { - void __iomem *mem = dreg->reg - chip->mem->phys + chip->mem->virt; + phys_addr_t address = dreg->reg; + void __iomem *mem = address - chip->mem->phys + chip->mem->virt; sh_pfc_write_raw_reg(mem, dreg->reg_width, value); } @@ -85,7 +86,7 @@ static void gpio_setup_data_reg(struct sh_pfc_chip *chip, unsigned idx) unsigned int bit; unsigned int i; - for (i = 0, dreg = pfc->info->data_regs; dreg->reg; ++i, ++dreg) { + for (i = 0, dreg = pfc->info->data_regs; dreg->reg_width; ++i, ++dreg) { for (bit = 0; bit < dreg->reg_width; bit++) { if (dreg->enum_ids[bit] == pin->enum_id) { gpio_pin->dreg = i; @@ -154,17 +155,17 @@ static void gpio_pin_set_value(struct sh_pfc_chip *chip, unsigned offset, int value) { struct sh_pfc_gpio_data_reg *reg; - unsigned long pos; unsigned int bit; + unsigned int pos; gpio_get_data_reg(chip, offset, ®, &bit); pos = reg->info->reg_width - (bit + 1); if (value) - set_bit(pos, ®->shadow); + reg->shadow |= BIT(pos); else - clear_bit(pos, ®->shadow); + reg->shadow &= ~BIT(pos); gpio_write_data_reg(chip, reg->info, reg->shadow); } @@ -186,8 +187,8 @@ static int gpio_pin_get(struct gpio_chip *gc, unsigned offset) { struct sh_pfc_chip *chip = gpio_to_pfc_chip(gc); struct sh_pfc_gpio_data_reg *reg; - unsigned long pos; unsigned int bit; + unsigned int pos; gpio_get_data_reg(chip, offset, ®, &bit); @@ -341,6 +342,7 @@ sh_pfc_add_gpiochip(struct sh_pfc *pfc, int(*setup)(struct sh_pfc_chip *), int sh_pfc_register_gpiochip(struct sh_pfc *pfc) { struct sh_pfc_chip *chip; + phys_addr_t address; unsigned int i; int ret; @@ -352,11 +354,12 @@ int sh_pfc_register_gpiochip(struct sh_pfc *pfc) * that covers the data registers. In that case don't try to handle * GPIOs. */ + address = pfc->info->data_regs[0].reg; for (i = 0; i < pfc->num_windows; ++i) { struct sh_pfc_window *window = &pfc->windows[i]; - if (pfc->info->data_regs[0].reg >= window->phys && - pfc->info->data_regs[0].reg < window->phys + window->size) + if (address >= window->phys && + address < window->phys + window->size) break; } diff --git a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c index 80c1843bb6ad63..22a5470889f533 100644 --- a/drivers/pinctrl/sh-pfc/pfc-r8a7790.c +++ b/drivers/pinctrl/sh-pfc/pfc-r8a7790.c @@ -1799,6 +1799,81 @@ static const unsigned int audio_clkout_d_pins[] = { static const unsigned int audio_clkout_d_mux[] = { AUDIO_CLKOUT_D_MARK, }; +/* - AVB -------------------------------------------------------------------- */ +static const unsigned int avb_link_pins[] = { + RCAR_GP_PIN(3, 11), +}; +static const unsigned int avb_link_mux[] = { + AVB_LINK_MARK, +}; +static const unsigned int avb_magic_pins[] = { + RCAR_GP_PIN(2, 14), +}; +static const unsigned int avb_magic_mux[] = { + AVB_MAGIC_MARK, +}; +static const unsigned int avb_phy_int_pins[] = { + RCAR_GP_PIN(2, 15), +}; +static const unsigned int avb_phy_int_mux[] = { + AVB_PHY_INT_MARK, +}; +static const unsigned int avb_mdio_pins[] = { + RCAR_GP_PIN(2, 11), RCAR_GP_PIN(2, 12), +}; +static const unsigned int avb_mdio_mux[] = { + AVB_MDC_MARK, AVB_MDIO_MARK, +}; +static const unsigned int avb_mii_pins[] = { + RCAR_GP_PIN(0, 8), RCAR_GP_PIN(0, 9), RCAR_GP_PIN(0, 10), + RCAR_GP_PIN(0, 11), + + RCAR_GP_PIN(3, 13), RCAR_GP_PIN(2, 0), RCAR_GP_PIN(2, 1), + RCAR_GP_PIN(2, 2), + + RCAR_GP_PIN(2, 7), RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9), + RCAR_GP_PIN(2, 10), RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 10), + RCAR_GP_PIN(3, 12), +}; +static const unsigned int avb_mii_mux[] = { + AVB_TXD0_MARK, AVB_TXD1_MARK, AVB_TXD2_MARK, + AVB_TXD3_MARK, + + AVB_RXD0_MARK, AVB_RXD1_MARK, AVB_RXD2_MARK, + AVB_RXD3_MARK, + + AVB_RX_ER_MARK, AVB_RX_CLK_MARK, AVB_RX_DV_MARK, + AVB_CRS_MARK, AVB_TX_EN_MARK, AVB_TX_CLK_MARK, + AVB_COL_MARK, +}; +static const unsigned int avb_gmii_pins[] = { + RCAR_GP_PIN(0, 8), RCAR_GP_PIN(0, 9), RCAR_GP_PIN(0, 10), + RCAR_GP_PIN(0, 11), RCAR_GP_PIN(0, 12), RCAR_GP_PIN(0, 13), + RCAR_GP_PIN(0, 14), RCAR_GP_PIN(0, 15), + + RCAR_GP_PIN(3, 13), RCAR_GP_PIN(2, 0), RCAR_GP_PIN(2, 1), + RCAR_GP_PIN(2, 2), RCAR_GP_PIN(2, 3), RCAR_GP_PIN(2, 4), + RCAR_GP_PIN(2, 5), RCAR_GP_PIN(2, 6), + + RCAR_GP_PIN(2, 7), RCAR_GP_PIN(2, 8), RCAR_GP_PIN(2, 9), + RCAR_GP_PIN(2, 10), RCAR_GP_PIN(2, 13), RCAR_GP_PIN(2, 16), + RCAR_GP_PIN(3, 8), RCAR_GP_PIN(3, 9), RCAR_GP_PIN(3, 10), + RCAR_GP_PIN(3, 12), +}; +static const unsigned int avb_gmii_mux[] = { + AVB_TXD0_MARK, AVB_TXD1_MARK, AVB_TXD2_MARK, + AVB_TXD3_MARK, AVB_TXD4_MARK, AVB_TXD5_MARK, + AVB_TXD6_MARK, AVB_TXD7_MARK, + + AVB_RXD0_MARK, AVB_RXD1_MARK, AVB_RXD2_MARK, + AVB_RXD3_MARK, AVB_RXD4_MARK, AVB_RXD5_MARK, + AVB_RXD6_MARK, AVB_RXD7_MARK, + + AVB_RX_ER_MARK, AVB_RX_CLK_MARK, AVB_RX_DV_MARK, + AVB_CRS_MARK, AVB_GTX_CLK_MARK, AVB_GTXREFCLK_MARK, + AVB_TX_EN_MARK, AVB_TX_ER_MARK, AVB_TX_CLK_MARK, + AVB_COL_MARK, +}; /* - DU RGB ----------------------------------------------------------------- */ static const unsigned int du_rgb666_pins[] = { /* R[7:2], G[7:2], B[7:2] */ @@ -3823,6 +3898,12 @@ static const struct sh_pfc_pin_group pinmux_groups[] = { SH_PFC_PIN_GROUP(audio_clkout_b), SH_PFC_PIN_GROUP(audio_clkout_c), SH_PFC_PIN_GROUP(audio_clkout_d), + SH_PFC_PIN_GROUP(avb_link), + SH_PFC_PIN_GROUP(avb_magic), + SH_PFC_PIN_GROUP(avb_phy_int), + SH_PFC_PIN_GROUP(avb_mdio), + SH_PFC_PIN_GROUP(avb_mii), + SH_PFC_PIN_GROUP(avb_gmii), SH_PFC_PIN_GROUP(du_rgb666), SH_PFC_PIN_GROUP(du_rgb888), SH_PFC_PIN_GROUP(du_clk_out_0), @@ -4101,6 +4182,15 @@ static const char * const audio_clk_groups[] = { "audio_clkout_d", }; +static const char * const avb_groups[] = { + "avb_link", + "avb_magic", + "avb_phy_int", + "avb_mdio", + "avb_mii", + "avb_gmii", +}; + static const char * const du_groups[] = { "du_rgb666", "du_rgb888", @@ -4507,6 +4597,7 @@ static const char * const vin3_groups[] = { static const struct sh_pfc_function pinmux_functions[] = { SH_PFC_FUNCTION(audio_clk), + SH_PFC_FUNCTION(avb), SH_PFC_FUNCTION(du), SH_PFC_FUNCTION(du0), SH_PFC_FUNCTION(du1), diff --git a/drivers/pinctrl/sh-pfc/sh_pfc.h b/drivers/pinctrl/sh-pfc/sh_pfc.h index c83728626906c1..c7508d5f688613 100644 --- a/drivers/pinctrl/sh-pfc/sh_pfc.h +++ b/drivers/pinctrl/sh-pfc/sh_pfc.h @@ -69,9 +69,10 @@ struct pinmux_func { }; struct pinmux_cfg_reg { - unsigned long reg, reg_width, field_width; + u32 reg; + u8 reg_width, field_width; const u16 *enum_ids; - const unsigned long *var_field_width; + const u8 *var_field_width; }; #define PINMUX_CFG_REG(name, r, r_width, f_width) \ @@ -80,12 +81,13 @@ struct pinmux_cfg_reg { #define PINMUX_CFG_REG_VAR(name, r, r_width, var_fw0, var_fwn...) \ .reg = r, .reg_width = r_width, \ - .var_field_width = (const unsigned long [r_width]) \ + .var_field_width = (const u8 [r_width]) \ { var_fw0, var_fwn, 0 }, \ .enum_ids = (const u16 []) struct pinmux_data_reg { - unsigned long reg, reg_width; + u32 reg; + u8 reg_width; const u16 *enum_ids; }; @@ -148,7 +150,7 @@ struct sh_pfc_soc_info { const struct pinmux_irq *gpio_irq; unsigned int gpio_irq_size; - unsigned long unlock_reg; + u32 unlock_reg; }; /* ----------------------------------------------------------------------------- @@ -302,20 +304,21 @@ struct sh_pfc_soc_info { /* * PORTnCR macro */ -#define _PCRH(in, in_pd, in_pu, out) \ - 0, (out), (in), 0, \ - 0, 0, 0, 0, \ - 0, 0, (in_pd), 0, \ - 0, 0, (in_pu), 0 - #define PORTCR(nr, reg) \ { \ - PINMUX_CFG_REG("PORT" nr "CR", reg, 8, 4) { \ - _PCRH(PORT##nr##_IN, 0, 0, PORT##nr##_OUT), \ - PORT##nr##_FN0, PORT##nr##_FN1, \ - PORT##nr##_FN2, PORT##nr##_FN3, \ - PORT##nr##_FN4, PORT##nr##_FN5, \ - PORT##nr##_FN6, PORT##nr##_FN7 } \ + PINMUX_CFG_REG_VAR("PORT" nr "CR", reg, 8, 2, 2, 1, 3) {\ + /* PULMD[1:0], handled by .set_bias() */ \ + 0, 0, 0, 0, \ + /* IE and OE */ \ + 0, PORT##nr##_OUT, PORT##nr##_IN, 0, \ + /* SEC, not supported */ \ + 0, 0, \ + /* PTMD[2:0] */ \ + PORT##nr##_FN0, PORT##nr##_FN1, \ + PORT##nr##_FN2, PORT##nr##_FN3, \ + PORT##nr##_FN4, PORT##nr##_FN5, \ + PORT##nr##_FN6, PORT##nr##_FN7 \ + } \ } #endif /* __SH_PFC_H */ diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-sirf.c index 2a1f07249b2fd6..e2efbbae406130 100644 --- a/drivers/pinctrl/sirf/pinctrl-sirf.c +++ b/drivers/pinctrl/sirf/pinctrl-sirf.c @@ -39,10 +39,9 @@ struct sirfsoc_gpio_bank { struct sirfsoc_gpio_chip { struct of_mm_gpio_chip chip; struct sirfsoc_gpio_bank sgpio_bank[SIRFSOC_GPIO_NO_OF_BANKS]; + spinlock_t lock; }; -static DEFINE_SPINLOCK(sgpio_lock); - static struct sirfsoc_pin_group *sirfsoc_pin_groups; static int sirfsoc_pingrp_cnt; @@ -427,13 +426,13 @@ static void sirfsoc_gpio_irq_ack(struct irq_data *d) offset = SIRFSOC_GPIO_CTRL(bank->id, idx); - spin_lock_irqsave(&sgpio_lock, flags); + spin_lock_irqsave(&sgpio->lock, flags); val = readl(sgpio->chip.regs + offset); writel(val, sgpio->chip.regs + offset); - spin_unlock_irqrestore(&sgpio_lock, flags); + spin_unlock_irqrestore(&sgpio->lock, flags); } static void __sirfsoc_gpio_irq_mask(struct sirfsoc_gpio_chip *sgpio, @@ -445,14 +444,14 @@ static void __sirfsoc_gpio_irq_mask(struct sirfsoc_gpio_chip *sgpio, offset = SIRFSOC_GPIO_CTRL(bank->id, idx); - spin_lock_irqsave(&sgpio_lock, flags); + spin_lock_irqsave(&sgpio->lock, flags); val = readl(sgpio->chip.regs + offset); val &= ~SIRFSOC_GPIO_CTL_INTR_EN_MASK; val &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK; writel(val, sgpio->chip.regs + offset); - spin_unlock_irqrestore(&sgpio_lock, flags); + spin_unlock_irqrestore(&sgpio->lock, flags); } static void sirfsoc_gpio_irq_mask(struct irq_data *d) @@ -475,14 +474,14 @@ static void sirfsoc_gpio_irq_unmask(struct irq_data *d) offset = SIRFSOC_GPIO_CTRL(bank->id, idx); - spin_lock_irqsave(&sgpio_lock, flags); + spin_lock_irqsave(&sgpio->lock, flags); val = readl(sgpio->chip.regs + offset); val &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK; val |= SIRFSOC_GPIO_CTL_INTR_EN_MASK; writel(val, sgpio->chip.regs + offset); - spin_unlock_irqrestore(&sgpio_lock, flags); + spin_unlock_irqrestore(&sgpio->lock, flags); } static int sirfsoc_gpio_irq_type(struct irq_data *d, unsigned type) @@ -496,7 +495,7 @@ static int sirfsoc_gpio_irq_type(struct irq_data *d, unsigned type) offset = SIRFSOC_GPIO_CTRL(bank->id, idx); - spin_lock_irqsave(&sgpio_lock, flags); + spin_lock_irqsave(&sgpio->lock, flags); val = readl(sgpio->chip.regs + offset); val &= ~(SIRFSOC_GPIO_CTL_INTR_STS_MASK | SIRFSOC_GPIO_CTL_OUT_EN_MASK); @@ -533,7 +532,7 @@ static int sirfsoc_gpio_irq_type(struct irq_data *d, unsigned type) writel(val, sgpio->chip.regs + offset); - spin_unlock_irqrestore(&sgpio_lock, flags); + spin_unlock_irqrestore(&sgpio->lock, flags); return 0; } @@ -568,7 +567,7 @@ static void sirfsoc_gpio_handle_irq(unsigned int irq, struct irq_desc *desc) status = readl(sgpio->chip.regs + SIRFSOC_GPIO_INT_STATUS(bank->id)); if (!status) { printk(KERN_WARNING - "%s: gpio id %d status %#x no interrupt is flaged\n", + "%s: gpio id %d status %#x no interrupt is flagged\n", __func__, bank->id, status); handle_bad_irq(irq, desc); return; @@ -697,11 +696,11 @@ static int sirfsoc_gpio_direction_output(struct gpio_chip *chip, offset = SIRFSOC_GPIO_CTRL(bank->id, idx); - spin_lock_irqsave(&sgpio_lock, flags); + spin_lock_irqsave(&sgpio->lock, flags); sirfsoc_gpio_set_output(sgpio, bank, offset, value); - spin_unlock_irqrestore(&sgpio_lock, flags); + spin_unlock_irqrestore(&sgpio->lock, flags); return 0; } @@ -793,6 +792,7 @@ static int sirfsoc_gpio_probe(struct device_node *np) sgpio = devm_kzalloc(&pdev->dev, sizeof(*sgpio), GFP_KERNEL); if (!sgpio) return -ENOMEM; + spin_lock_init(&sgpio->lock); regs = of_iomap(np, 0); if (!regs) diff --git a/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c b/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c index 3c68a8e5e0dd4d..7376a97b5e65c3 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c @@ -1020,7 +1020,7 @@ static int sun4i_a10_pinctrl_probe(struct platform_device *pdev) &sun4i_a10_pinctrl_data); } -static struct of_device_id sun4i_a10_pinctrl_match[] = { +static const struct of_device_id sun4i_a10_pinctrl_match[] = { { .compatible = "allwinner,sun4i-a10-pinctrl", }, {} }; diff --git a/drivers/pinctrl/sunxi/pinctrl-sun5i-a10s.c b/drivers/pinctrl/sunxi/pinctrl-sun5i-a10s.c index 45a351affa59d0..63676617bc5997 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun5i-a10s.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun5i-a10s.c @@ -670,7 +670,7 @@ static int sun5i_a10s_pinctrl_probe(struct platform_device *pdev) &sun5i_a10s_pinctrl_data); } -static struct of_device_id sun5i_a10s_pinctrl_match[] = { +static const struct of_device_id sun5i_a10s_pinctrl_match[] = { { .compatible = "allwinner,sun5i-a10s-pinctrl", }, {} }; diff --git a/drivers/pinctrl/sunxi/pinctrl-sun5i-a13.c b/drivers/pinctrl/sunxi/pinctrl-sun5i-a13.c index 4bd23471412cca..2bb07b38834f74 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun5i-a13.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun5i-a13.c @@ -388,7 +388,7 @@ static int sun5i_a13_pinctrl_probe(struct platform_device *pdev) &sun5i_a13_pinctrl_data); } -static struct of_device_id sun5i_a13_pinctrl_match[] = { +static const struct of_device_id sun5i_a13_pinctrl_match[] = { { .compatible = "allwinner,sun5i-a13-pinctrl", }, {} }; diff --git a/drivers/pinctrl/sunxi/pinctrl-sun6i-a31-r.c b/drivers/pinctrl/sunxi/pinctrl-sun6i-a31-r.c index 02174fa57997e4..9596b0a3df6be7 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun6i-a31-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun6i-a31-r.c @@ -120,7 +120,7 @@ static int sun6i_a31_r_pinctrl_probe(struct platform_device *pdev) return ret; } -static struct of_device_id sun6i_a31_r_pinctrl_match[] = { +static const struct of_device_id sun6i_a31_r_pinctrl_match[] = { { .compatible = "allwinner,sun6i-a31-r-pinctrl", }, {} }; diff --git a/drivers/pinctrl/sunxi/pinctrl-sun6i-a31.c b/drivers/pinctrl/sunxi/pinctrl-sun6i-a31.c index 18038f0d6b5222..022863ab0c5824 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun6i-a31.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun6i-a31.c @@ -922,7 +922,7 @@ static int sun6i_a31_pinctrl_probe(struct platform_device *pdev) &sun6i_a31_pinctrl_data); } -static struct of_device_id sun6i_a31_pinctrl_match[] = { +static const struct of_device_id sun6i_a31_pinctrl_match[] = { { .compatible = "allwinner,sun6i-a31-pinctrl", }, {} }; diff --git a/drivers/pinctrl/sunxi/pinctrl-sun6i-a31s.c b/drivers/pinctrl/sunxi/pinctrl-sun6i-a31s.c index 9b5a91f610c7e4..d3725dcd6979b8 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun6i-a31s.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun6i-a31s.c @@ -794,7 +794,7 @@ static int sun6i_a31s_pinctrl_probe(struct platform_device *pdev) &sun6i_a31s_pinctrl_data); } -static struct of_device_id sun6i_a31s_pinctrl_match[] = { +static const struct of_device_id sun6i_a31s_pinctrl_match[] = { { .compatible = "allwinner,sun6i-a31s-pinctrl", }, {} }; diff --git a/drivers/pinctrl/sunxi/pinctrl-sun7i-a20.c b/drivers/pinctrl/sunxi/pinctrl-sun7i-a20.c index 6af6cc8547b0f3..cf1ce0c02600e6 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun7i-a20.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun7i-a20.c @@ -1045,7 +1045,7 @@ static int sun7i_a20_pinctrl_probe(struct platform_device *pdev) &sun7i_a20_pinctrl_data); } -static struct of_device_id sun7i_a20_pinctrl_match[] = { +static const struct of_device_id sun7i_a20_pinctrl_match[] = { { .compatible = "allwinner,sun7i-a20-pinctrl", }, {} }; diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a23-r.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a23-r.c index 327e03ff7c4dc3..05628763587335 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a23-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a23-r.c @@ -119,7 +119,7 @@ static int sun8i_a23_r_pinctrl_probe(struct platform_device *pdev) return ret; } -static struct of_device_id sun8i_a23_r_pinctrl_match[] = { +static const struct of_device_id sun8i_a23_r_pinctrl_match[] = { { .compatible = "allwinner,sun8i-a23-r-pinctrl", }, {} }; diff --git a/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c b/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c index 62695c9a92c21b..55083d278bb1eb 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun8i-a23.c @@ -571,7 +571,7 @@ static int sun8i_a23_pinctrl_probe(struct platform_device *pdev) &sun8i_a23_pinctrl_data); } -static struct of_device_id sun8i_a23_pinctrl_match[] = { +static const struct of_device_id sun8i_a23_pinctrl_match[] = { { .compatible = "allwinner,sun8i-a23-pinctrl", }, {} }; diff --git a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c index adb29422efc97a..1b580ba764531a 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun9i-a80.c @@ -729,7 +729,7 @@ static int sun9i_a80_pinctrl_probe(struct platform_device *pdev) &sun9i_a80_pinctrl_data); } -static struct of_device_id sun9i_a80_pinctrl_match[] = { +static const struct of_device_id sun9i_a80_pinctrl_match[] = { { .compatible = "allwinner,sun9i-a80-pinctrl", }, {} }; diff --git a/drivers/pinctrl/vt8500/pinctrl-vt8500.c b/drivers/pinctrl/vt8500/pinctrl-vt8500.c index cf8bbc946ff70f..ca946b3dbdb4f7 100644 --- a/drivers/pinctrl/vt8500/pinctrl-vt8500.c +++ b/drivers/pinctrl/vt8500/pinctrl-vt8500.c @@ -478,7 +478,7 @@ static int vt8500_pinctrl_remove(struct platform_device *pdev) return wmt_pinctrl_remove(pdev); } -static struct of_device_id wmt_pinctrl_of_match[] = { +static const struct of_device_id wmt_pinctrl_of_match[] = { { .compatible = "via,vt8500-pinctrl" }, { /* sentinel */ }, }; diff --git a/drivers/pinctrl/vt8500/pinctrl-wm8505.c b/drivers/pinctrl/vt8500/pinctrl-wm8505.c index 3f9c32dcb3d04c..626fc7ec017462 100644 --- a/drivers/pinctrl/vt8500/pinctrl-wm8505.c +++ b/drivers/pinctrl/vt8500/pinctrl-wm8505.c @@ -509,7 +509,7 @@ static int wm8505_pinctrl_remove(struct platform_device *pdev) return wmt_pinctrl_remove(pdev); } -static struct of_device_id wmt_pinctrl_of_match[] = { +static const struct of_device_id wmt_pinctrl_of_match[] = { { .compatible = "wm,wm8505-pinctrl" }, { /* sentinel */ }, }; diff --git a/drivers/pinctrl/vt8500/pinctrl-wm8650.c b/drivers/pinctrl/vt8500/pinctrl-wm8650.c index 4e80f98c2ba7c2..8953aba8bfc226 100644 --- a/drivers/pinctrl/vt8500/pinctrl-wm8650.c +++ b/drivers/pinctrl/vt8500/pinctrl-wm8650.c @@ -347,7 +347,7 @@ static int wm8650_pinctrl_remove(struct platform_device *pdev) return wmt_pinctrl_remove(pdev); } -static struct of_device_id wmt_pinctrl_of_match[] = { +static const struct of_device_id wmt_pinctrl_of_match[] = { { .compatible = "wm,wm8650-pinctrl" }, { /* sentinel */ }, }; diff --git a/drivers/pinctrl/vt8500/pinctrl-wm8750.c b/drivers/pinctrl/vt8500/pinctrl-wm8750.c index 47b52a7cacac04..c79053d430db5a 100644 --- a/drivers/pinctrl/vt8500/pinctrl-wm8750.c +++ b/drivers/pinctrl/vt8500/pinctrl-wm8750.c @@ -386,7 +386,7 @@ static int wm8750_pinctrl_remove(struct platform_device *pdev) return wmt_pinctrl_remove(pdev); } -static struct of_device_id wmt_pinctrl_of_match[] = { +static const struct of_device_id wmt_pinctrl_of_match[] = { { .compatible = "wm,wm8750-pinctrl" }, { /* sentinel */ }, }; diff --git a/drivers/pinctrl/vt8500/pinctrl-wm8850.c b/drivers/pinctrl/vt8500/pinctrl-wm8850.c index 8bbb38c931f659..f232b163c735cf 100644 --- a/drivers/pinctrl/vt8500/pinctrl-wm8850.c +++ b/drivers/pinctrl/vt8500/pinctrl-wm8850.c @@ -365,7 +365,7 @@ static int wm8850_pinctrl_remove(struct platform_device *pdev) return wmt_pinctrl_remove(pdev); } -static struct of_device_id wmt_pinctrl_of_match[] = { +static const struct of_device_id wmt_pinctrl_of_match[] = { { .compatible = "wm,wm8850-pinctrl" }, { /* sentinel */ }, }; diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 53bdbb01bd3f37..baea077a02ccc5 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -59,7 +59,7 @@ static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = { KEY_RESERVED, KEY_SCROLLDOWN, KEY_SCROLLUP, - KEY_DIRECTION, + KEY_ROTATE_DISPLAY, KEY_LEFTCTRL, KEY_BRIGHTNESSUP, KEY_BRIGHTNESSDOWN, @@ -116,7 +116,7 @@ static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = { KEY_RESERVED, KEY_PROG1, KEY_PROG2, - KEY_DIRECTION, + KEY_ROTATE_DISPLAY, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, @@ -153,7 +153,7 @@ static unsigned short keymap_Stylistic_ST5xxx[KEYMAP_LEN] __initdata = { KEY_RESERVED, KEY_RESERVED, KEY_MAIL, - KEY_DIRECTION, + KEY_ROTATE_DISPLAY, KEY_ESC, KEY_ENTER, KEY_BRIGHTNESSUP, diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 0ab2b377a778aa..06697315a0887f 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -144,7 +144,7 @@ static const struct key_entry hp_wmi_keymap[] = { { KE_KEY, 0x20e8, { KEY_MEDIA } }, { KE_KEY, 0x2142, { KEY_MEDIA } }, { KE_KEY, 0x213b, { KEY_INFO } }, - { KE_KEY, 0x2169, { KEY_DIRECTION } }, + { KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } }, { KE_KEY, 0x216a, { KEY_SETUP } }, { KE_KEY, 0x231b, { KEY_HELP } }, { KE_END, 0 } diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index 4116e74effa4e5..fcb98dbda8379c 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -246,6 +246,7 @@ static int arizona_micsupp_probe(struct platform_device *pdev) */ switch (arizona->type) { case WM5110: + case WM8280: desc = &arizona_micsupp_ext; micsupp->init_data = arizona_micsupp_ext_default; break; diff --git a/drivers/rtc/rtc-hid-sensor-time.c b/drivers/rtc/rtc-hid-sensor-time.c index ae7c2ba440cf45..af4f85a66b39f0 100644 --- a/drivers/rtc/rtc-hid-sensor-time.c +++ b/drivers/rtc/rtc-hid-sensor-time.c @@ -213,7 +213,7 @@ static int hid_rtc_read_time(struct device *dev, struct rtc_time *tm) /* get a report with all values through requesting one value */ sensor_hub_input_attr_get_raw_value(time_state->common_attributes.hsdev, HID_USAGE_SENSOR_TIME, hid_time_addresses[0], - time_state->info[0].report_id); + time_state->info[0].report_id, SENSOR_HUB_SYNC); /* wait for all values (event) */ ret = wait_for_completion_killable_timeout( &time_state->comp_last_time, HZ*6); diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c index 89ac1d5083c660..4008b84246ca75 100644 --- a/drivers/rtc/rtc-s5m.c +++ b/drivers/rtc/rtc-s5m.c @@ -48,8 +48,6 @@ struct s5m_rtc_reg_config { unsigned int alarm0; /* First register for alarm 1, seconds */ unsigned int alarm1; - /* SMPL/WTSR register */ - unsigned int smpl_wtsr; /* * Register for update flag (UDR). Typically setting UDR field to 1 * will enable update of time or alarm register. Then it will be @@ -67,7 +65,6 @@ static const struct s5m_rtc_reg_config s5m_rtc_regs = { .ctrl = S5M_ALARM1_CONF, .alarm0 = S5M_ALARM0_SEC, .alarm1 = S5M_ALARM1_SEC, - .smpl_wtsr = S5M_WTSR_SMPL_CNTL, .rtc_udr_update = S5M_RTC_UDR_CON, .rtc_udr_mask = S5M_RTC_UDR_MASK, }; @@ -82,7 +79,6 @@ static const struct s5m_rtc_reg_config s2mps_rtc_regs = { .ctrl = S2MPS_RTC_CTRL, .alarm0 = S2MPS_ALARM0_SEC, .alarm1 = S2MPS_ALARM1_SEC, - .smpl_wtsr = S2MPS_WTSR_SMPL_CNTL, .rtc_udr_update = S2MPS_RTC_UDR_CON, .rtc_udr_mask = S2MPS_RTC_WUDR_MASK, }; @@ -96,7 +92,6 @@ struct s5m_rtc_info { int irq; int device_type; int rtc_24hr_mode; - bool wtsr_smpl; const struct s5m_rtc_reg_config *regs; }; @@ -597,28 +592,6 @@ static const struct rtc_class_ops s5m_rtc_ops = { .alarm_irq_enable = s5m_rtc_alarm_irq_enable, }; -static void s5m_rtc_enable_wtsr(struct s5m_rtc_info *info, bool enable) -{ - int ret; - ret = regmap_update_bits(info->regmap, info->regs->smpl_wtsr, - WTSR_ENABLE_MASK, - enable ? WTSR_ENABLE_MASK : 0); - if (ret < 0) - dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n", - __func__, ret); -} - -static void s5m_rtc_enable_smpl(struct s5m_rtc_info *info, bool enable) -{ - int ret; - ret = regmap_update_bits(info->regmap, info->regs->smpl_wtsr, - SMPL_ENABLE_MASK, - enable ? SMPL_ENABLE_MASK : 0); - if (ret < 0) - dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n", - __func__, ret); -} - static int s5m8767_rtc_init_reg(struct s5m_rtc_info *info) { u8 data[2]; @@ -715,7 +688,6 @@ static int s5m_rtc_probe(struct platform_device *pdev) info->dev = &pdev->dev; info->s5m87xx = s5m87xx; info->device_type = s5m87xx->device_type; - info->wtsr_smpl = s5m87xx->wtsr_smpl; if (s5m87xx->irq_data) { info->irq = regmap_irq_get_virq(s5m87xx->irq_data, alarm_irq); @@ -731,11 +703,6 @@ static int s5m_rtc_probe(struct platform_device *pdev) ret = s5m8767_rtc_init_reg(info); - if (info->wtsr_smpl) { - s5m_rtc_enable_wtsr(info, true); - s5m_rtc_enable_smpl(info, true); - } - device_init_wakeup(&pdev->dev, 1); info->rtc_dev = devm_rtc_device_register(&pdev->dev, "s5m-rtc", @@ -768,36 +735,10 @@ static int s5m_rtc_probe(struct platform_device *pdev) return ret; } -static void s5m_rtc_shutdown(struct platform_device *pdev) -{ - struct s5m_rtc_info *info = platform_get_drvdata(pdev); - int i; - unsigned int val = 0; - if (info->wtsr_smpl) { - for (i = 0; i < 3; i++) { - s5m_rtc_enable_wtsr(info, false); - regmap_read(info->regmap, info->regs->smpl_wtsr, &val); - pr_debug("%s: WTSR_SMPL reg(0x%02x)\n", __func__, val); - if (val & WTSR_ENABLE_MASK) - pr_emerg("%s: fail to disable WTSR\n", - __func__); - else { - pr_info("%s: success to disable WTSR\n", - __func__); - break; - } - } - } - /* Disable SMPL when power off */ - s5m_rtc_enable_smpl(info, false); -} - static int s5m_rtc_remove(struct platform_device *pdev) { struct s5m_rtc_info *info = platform_get_drvdata(pdev); - /* Perform also all shutdown steps when removing */ - s5m_rtc_shutdown(pdev); i2c_unregister_device(info->i2c); return 0; @@ -842,7 +783,6 @@ static struct platform_driver s5m_rtc_driver = { }, .probe = s5m_rtc_probe, .remove = s5m_rtc_remove, - .shutdown = s5m_rtc_shutdown, .id_table = s5m_rtc_id, }; diff --git a/drivers/scsi/be2iscsi/be_cmds.c b/drivers/scsi/be2iscsi/be_cmds.c index 80d97f3d2ed9b3..1028760b8a2214 100644 --- a/drivers/scsi/be2iscsi/be_cmds.c +++ b/drivers/scsi/be2iscsi/be_cmds.c @@ -237,7 +237,7 @@ int beiscsi_mccq_compl(struct beiscsi_hba *phba, beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_INIT | BEISCSI_LOG_EH | BEISCSI_LOG_CONFIG, - "BC_%d : Insufficent Buffer Error " + "BC_%d : Insufficient Buffer Error " "Resp_Len : %d Actual_Resp_Len : %d\n", mbx_resp_hdr->response_length, mbx_resp_hdr->actual_resp_len); diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c index 0045742fab7d22..dad959fcf6d8e2 100644 --- a/drivers/scsi/ch.c +++ b/drivers/scsi/ch.c @@ -339,7 +339,7 @@ ch_readconfig(scsi_changer *ch) ch->firsts[CHET_DT], ch->counts[CHET_DT]); } else { - VPRINTK(KERN_INFO, "reading element address assigment page failed!\n"); + VPRINTK(KERN_INFO, "reading element address assignment page failed!\n"); } /* vendor specific element types */ diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c index 488c3929f19a95..0cccd6033feb6b 100644 --- a/drivers/scsi/osd/osd_initiator.c +++ b/drivers/scsi/osd/osd_initiator.c @@ -186,7 +186,7 @@ static int _osd_get_print_system_info(struct osd_dev *od, if (unlikely(len > sizeof(odi->systemid))) { OSD_ERR("OSD Target error: OSD_SYSTEM_ID too long(%d). " - "device idetification might not work\n", len); + "device identification might not work\n", len); len = sizeof(odi->systemid); } odi->systemid_len = len; diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index e59f25bff7abbb..5bb57c5282c94e 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -5364,7 +5364,7 @@ qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) blob = qla2x00_request_firmware(vha); if (!blob) { ql_log(ql_log_info, vha, 0x0083, - "Fimware image unavailable.\n"); + "Firmware image unavailable.\n"); ql_log(ql_log_info, vha, 0x0084, "Firmware images can be retrieved from: "QLA_FW_URL ".\n"); return QLA_FUNCTION_FAILED; @@ -5467,7 +5467,7 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) blob = qla2x00_request_firmware(vha); if (!blob) { ql_log(ql_log_warn, vha, 0x0090, - "Fimware image unavailable.\n"); + "Firmware image unavailable.\n"); ql_log(ql_log_warn, vha, 0x0091, "Firmware images can be retrieved from: " QLA_FW_URL ".\n"); diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index 5c2e0317f1c0a3..ca3804e34833eb 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -788,7 +788,7 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, rsp->msix = &ha->msix_entries[que_id + 1]; else ql_log(ql_log_warn, base_vha, 0x00e3, - "MSIX not enalbled.\n"); + "MSIX not enabled.\n"); ha->rsp_q_map[que_id] = rsp; rsp->rid = rid; diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c index 54cb2ac9339bdd..7d2b18f2675ce2 100644 --- a/drivers/scsi/qla2xxx/qla_nx.c +++ b/drivers/scsi/qla2xxx/qla_nx.c @@ -1274,7 +1274,7 @@ qla82xx_pinit_from_rom(scsi_qla_host_t *vha) if (off == ADDR_ERROR) { ql_log(ql_log_fatal, vha, 0x0116, - "Unknow addr: 0x%08lx.\n", buf[i].addr); + "Unknown addr: 0x%08lx.\n", buf[i].addr); continue; } diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index cce1cbc1a9276f..5319b3cb219e33 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -446,11 +446,11 @@ static int qla25xx_setup_mode(struct scsi_qla_host *vha) } ha->flags.cpu_affinity_enabled = 1; ql_dbg(ql_dbg_multiq, vha, 0xc007, - "CPU affinity mode enalbed, " + "CPU affinity mode enabled, " "no. of response queues:%d no. of request queues:%d.\n", ha->max_rsp_queues, ha->max_req_queues); ql_dbg(ql_dbg_init, vha, 0x00e9, - "CPU affinity mode enalbed, " + "CPU affinity mode enabled, " "no. of response queues:%d no. of request queues:%d.\n", ha->max_rsp_queues, ha->max_req_queues); } @@ -4102,7 +4102,7 @@ qla83xx_schedule_work(scsi_qla_host_t *base_vha, int work_code) break; default: ql_log(ql_log_warn, base_vha, 0xb05f, - "Unknow work-code=0x%x.\n", work_code); + "Unknown work-code=0x%x.\n", work_code); } return; @@ -4702,7 +4702,7 @@ qla83xx_idc_state_handler(scsi_qla_host_t *base_vha) break; default: ql_log(ql_log_warn, base_vha, 0xb071, - "Unknow Device State: %x.\n", dev_state); + "Unknown Device State: %x.\n", dev_state); qla83xx_idc_unlock(base_vha, 0); qla8xxx_dev_failed_handler(base_vha); rval = QLA_FUNCTION_FAILED; diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 2270bd51f9c2c2..9d7b7db75e4b96 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -33,7 +33,6 @@ static int sg_version_num = 30536; /* 2 digits for each component */ #include #include #include -#include #include #include #include @@ -51,6 +50,7 @@ static int sg_version_num = 30536; /* 2 digits for each component */ #include #include #include +#include #include "scsi.h" #include @@ -1745,17 +1745,14 @@ sg_start_req(Sg_request *srp, unsigned char *cmd) } if (iov_count) { - int size = sizeof(struct iovec) * iov_count; - struct iovec *iov; + struct iovec *iov = NULL; struct iov_iter i; - iov = memdup_user(hp->dxferp, size); - if (IS_ERR(iov)) - return PTR_ERR(iov); + res = import_iovec(rw, hp->dxferp, iov_count, 0, &iov, &i); + if (res < 0) + return res; - iov_iter_init(&i, rw, iov, iov_count, - min_t(size_t, hp->dxfer_len, - iov_length(iov, iov_count))); + iov_iter_truncate(&i, hp->dxfer_len); res = blk_rq_map_user_iov(q, rq, md, &i, GFP_ATOMIC); kfree(iov); diff --git a/drivers/staging/unisys/include/timskmod.h b/drivers/staging/unisys/include/timskmod.h index 5a933d7bf39fa7..cde2494ad896df 100644 --- a/drivers/staging/unisys/include/timskmod.h +++ b/drivers/staging/unisys/include/timskmod.h @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index 967b2c2b7cf106..0655fecf824059 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -229,7 +229,6 @@ static int goldfish_tty_probe(struct platform_device *pdev) { struct goldfish_tty *qtty; int ret = -EINVAL; - int i; struct resource *r; struct device *ttydev; void __iomem *base; @@ -293,7 +292,6 @@ static int goldfish_tty_probe(struct platform_device *pdev) mutex_unlock(&goldfish_tty_lock); return 0; - tty_unregister_device(goldfish_tty_driver, i); err_tty_register_device_failed: free_irq(irq, pdev); err_request_irq_failed: diff --git a/drivers/tty/ipwireless/hardware.c b/drivers/tty/ipwireless/hardware.c index 5c77e1eac4eea7..ad7031a4f3c4dd 100644 --- a/drivers/tty/ipwireless/hardware.c +++ b/drivers/tty/ipwireless/hardware.c @@ -1455,7 +1455,7 @@ static void __handle_setup_get_version_rsp(struct ipw_hardware *hw) return; } - set_RTS(hw, PRIO_SETUP, channel_idx, + ret = set_RTS(hw, PRIO_SETUP, channel_idx, (hw->control_lines [channel_idx] & IPW_CONTROL_LINE_RTS) != 0); if (ret) { diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 175c9956cbe3a3..a12315a78248d4 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -655,9 +656,10 @@ static void ffs_user_copy_worker(struct work_struct *work) unuse_mm(io_data->mm); } - aio_complete(io_data->kiocb, ret, ret); + io_data->kiocb->ki_complete(io_data->kiocb, ret, ret); - if (io_data->ffs->ffs_eventfd && !io_data->kiocb->ki_eventfd) + if (io_data->ffs->ffs_eventfd && + !(io_data->kiocb->ki_flags & IOCB_EVENTFD)) eventfd_signal(io_data->ffs->ffs_eventfd, 1); usb_ep_free_request(io_data->ep, io_data->req); diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 200f9a584064fd..662ef2c1c62b67 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -469,7 +470,7 @@ static void ep_user_copy_worker(struct work_struct *work) ret = -EFAULT; /* completing the iocb can drop the ctx and mm, don't touch mm after */ - aio_complete(iocb, ret, ret); + iocb->ki_complete(iocb, ret, ret); kfree(priv->buf); kfree(priv->to_free); @@ -497,7 +498,8 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req) kfree(priv); iocb->private = NULL; /* aio_complete() reports bytes-transferred _and_ faults */ - aio_complete(iocb, req->actual ? req->actual : req->status, + + iocb->ki_complete(iocb, req->actual ? req->actual : req->status, req->status); } else { /* ep_copy_to_user() won't report both; we hide some faults */ diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index dde3959b7a33a1..59c05653b2ea21 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -14,6 +14,13 @@ #undef TRACE_SYSTEM #define TRACE_SYSTEM xhci-hcd +/* + * The TRACE_SYSTEM_VAR defaults to TRACE_SYSTEM, but must be a + * legitimate C variable. It is not exported to user space. + */ +#undef TRACE_SYSTEM_VAR +#define TRACE_SYSTEM_VAR xhci_hcd + #if !defined(__XHCI_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) #define __XHCI_TRACE_H diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig index 14e27ab3245686..7d092ddc811958 100644 --- a/drivers/vfio/Kconfig +++ b/drivers/vfio/Kconfig @@ -13,6 +13,11 @@ config VFIO_SPAPR_EEH depends on EEH && VFIO_IOMMU_SPAPR_TCE default n +config VFIO_VIRQFD + tristate + depends on VFIO && EVENTFD + default n + menuconfig VFIO tristate "VFIO Non-Privileged userspace driver framework" depends on IOMMU_API @@ -27,3 +32,4 @@ menuconfig VFIO If you don't know what to do here, say N. source "drivers/vfio/pci/Kconfig" +source "drivers/vfio/platform/Kconfig" diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile index 0b035b12600a7b..7b8a31f63fea81 100644 --- a/drivers/vfio/Makefile +++ b/drivers/vfio/Makefile @@ -1,5 +1,9 @@ +vfio_virqfd-y := virqfd.o + obj-$(CONFIG_VFIO) += vfio.o +obj-$(CONFIG_VFIO_VIRQFD) += vfio_virqfd.o obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o obj-$(CONFIG_VFIO_SPAPR_EEH) += vfio_spapr_eeh.o obj-$(CONFIG_VFIO_PCI) += pci/ +obj-$(CONFIG_VFIO_PLATFORM) += platform/ diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig index c6bb5da2d2a718..579d83bf53583f 100644 --- a/drivers/vfio/pci/Kconfig +++ b/drivers/vfio/pci/Kconfig @@ -1,6 +1,7 @@ config VFIO_PCI tristate "VFIO support for PCI devices" depends on VFIO && PCI && EVENTFD + select VFIO_VIRQFD help Support for the PCI VFIO bus driver. This is required to make use of PCI drivers using the VFIO framework. diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index f8a186381ae872..69fab0fd15aec4 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -11,6 +11,8 @@ * Author: Tom Lyon, pugs@cisco.com */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -25,6 +27,7 @@ #include #include #include +#include #include "vfio_pci_private.h" @@ -32,13 +35,81 @@ #define DRIVER_AUTHOR "Alex Williamson " #define DRIVER_DESC "VFIO PCI - User Level meta-driver" +static char ids[1024] __initdata; +module_param_string(ids, ids, sizeof(ids), 0); +MODULE_PARM_DESC(ids, "Initial PCI IDs to add to the vfio driver, format is \"vendor:device[:subvendor[:subdevice[:class[:class_mask]]]]\" and multiple comma separated entries can be specified"); + static bool nointxmask; module_param_named(nointxmask, nointxmask, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(nointxmask, "Disable support for PCI 2.3 style INTx masking. If this resolves problems for specific devices, report lspci -vvvxxx to linux-pci@vger.kernel.org so the device can be fixed automatically via the broken_intx_masking flag."); +#ifdef CONFIG_VFIO_PCI_VGA +static bool disable_vga; +module_param(disable_vga, bool, S_IRUGO); +MODULE_PARM_DESC(disable_vga, "Disable VGA resource access through vfio-pci"); +#endif + +static bool disable_idle_d3; +module_param(disable_idle_d3, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(disable_idle_d3, + "Disable using the PCI D3 low power state for idle, unused devices"); + static DEFINE_MUTEX(driver_lock); +static inline bool vfio_vga_disabled(void) +{ +#ifdef CONFIG_VFIO_PCI_VGA + return disable_vga; +#else + return true; +#endif +} + +/* + * Our VGA arbiter participation is limited since we don't know anything + * about the device itself. However, if the device is the only VGA device + * downstream of a bridge and VFIO VGA support is disabled, then we can + * safely return legacy VGA IO and memory as not decoded since the user + * has no way to get to it and routing can be disabled externally at the + * bridge. + */ +static unsigned int vfio_pci_set_vga_decode(void *opaque, bool single_vga) +{ + struct vfio_pci_device *vdev = opaque; + struct pci_dev *tmp = NULL, *pdev = vdev->pdev; + unsigned char max_busnr; + unsigned int decodes; + + if (single_vga || !vfio_vga_disabled() || pci_is_root_bus(pdev->bus)) + return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM | + VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM; + + max_busnr = pci_bus_max_busnr(pdev->bus); + decodes = VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; + + while ((tmp = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, tmp)) != NULL) { + if (tmp == pdev || + pci_domain_nr(tmp->bus) != pci_domain_nr(pdev->bus) || + pci_is_root_bus(tmp->bus)) + continue; + + if (tmp->bus->number >= pdev->bus->number && + tmp->bus->number <= max_busnr) { + pci_dev_put(tmp); + decodes |= VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM; + break; + } + } + + return decodes; +} + +static inline bool vfio_pci_is_vga(struct pci_dev *pdev) +{ + return (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA; +} + static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev); static int vfio_pci_enable(struct vfio_pci_device *vdev) @@ -48,6 +119,8 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) u16 cmd; u8 msix_pos; + pci_set_power_state(pdev, PCI_D0); + /* Don't allow our initial saved state to include busmaster */ pci_clear_master(pdev); @@ -93,10 +166,8 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) } else vdev->msix_bar = 0xFF; -#ifdef CONFIG_VFIO_PCI_VGA - if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) + if (!vfio_vga_disabled() && vfio_pci_is_vga(pdev)) vdev->has_vga = true; -#endif return 0; } @@ -153,20 +224,17 @@ static void vfio_pci_disable(struct vfio_pci_device *vdev) * Try to reset the device. The success of this is dependent on * being able to lock the device, which is not always possible. */ - if (vdev->reset_works) { - int ret = pci_try_reset_function(pdev); - if (ret) - pr_warn("%s: Failed to reset device %s (%d)\n", - __func__, dev_name(&pdev->dev), ret); - else - vdev->needs_reset = false; - } + if (vdev->reset_works && !pci_try_reset_function(pdev)) + vdev->needs_reset = false; pci_restore_state(pdev); out: pci_disable_device(pdev); vfio_pci_try_bus_reset(vdev); + + if (!disable_idle_d3) + pci_set_power_state(pdev, PCI_D3hot); } static void vfio_pci_release(void *device_data) @@ -885,6 +953,27 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) { iommu_group_put(group); kfree(vdev); + return ret; + } + + if (vfio_pci_is_vga(pdev)) { + vga_client_register(pdev, vdev, NULL, vfio_pci_set_vga_decode); + vga_set_legacy_decoding(pdev, + vfio_pci_set_vga_decode(vdev, false)); + } + + if (!disable_idle_d3) { + /* + * pci-core sets the device power state to an unknown value at + * bootup and after being removed from a driver. The only + * transition it allows from this unknown state is to D0, which + * typically happens when a driver calls pci_enable_device(). + * We're not ready to enable the device yet, but we do want to + * be able to get to D3. Therefore first do a D0 transition + * before going to D3. + */ + pci_set_power_state(pdev, PCI_D0); + pci_set_power_state(pdev, PCI_D3hot); } return ret; @@ -895,10 +984,21 @@ static void vfio_pci_remove(struct pci_dev *pdev) struct vfio_pci_device *vdev; vdev = vfio_del_group_dev(&pdev->dev); - if (vdev) { - iommu_group_put(pdev->dev.iommu_group); - kfree(vdev); + if (!vdev) + return; + + iommu_group_put(pdev->dev.iommu_group); + kfree(vdev); + + if (vfio_pci_is_vga(pdev)) { + vga_client_register(pdev, NULL, NULL, NULL); + vga_set_legacy_decoding(pdev, + VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM | + VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM); } + + if (!disable_idle_d3) + pci_set_power_state(pdev, PCI_D0); } static pci_ers_result_t vfio_pci_aer_err_detected(struct pci_dev *pdev, @@ -1017,10 +1117,13 @@ static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev) put_devs: for (i = 0; i < devs.cur_index; i++) { - if (!ret) { - tmp = vfio_device_data(devs.devices[i]); + tmp = vfio_device_data(devs.devices[i]); + if (!ret) tmp->needs_reset = false; - } + + if (!tmp->refcnt && !disable_idle_d3) + pci_set_power_state(tmp->pdev, PCI_D3hot); + vfio_device_put(devs.devices[i]); } @@ -1030,10 +1133,50 @@ static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev) static void __exit vfio_pci_cleanup(void) { pci_unregister_driver(&vfio_pci_driver); - vfio_pci_virqfd_exit(); vfio_pci_uninit_perm_bits(); } +static void __init vfio_pci_fill_ids(void) +{ + char *p, *id; + int rc; + + /* no ids passed actually */ + if (ids[0] == '\0') + return; + + /* add ids specified in the module parameter */ + p = ids; + while ((id = strsep(&p, ","))) { + unsigned int vendor, device, subvendor = PCI_ANY_ID, + subdevice = PCI_ANY_ID, class = 0, class_mask = 0; + int fields; + + if (!strlen(id)) + continue; + + fields = sscanf(id, "%x:%x:%x:%x:%x:%x", + &vendor, &device, &subvendor, &subdevice, + &class, &class_mask); + + if (fields < 2) { + pr_warn("invalid id string \"%s\"\n", id); + continue; + } + + rc = pci_add_dynid(&vfio_pci_driver, vendor, device, + subvendor, subdevice, class, class_mask, 0); + if (rc) + pr_warn("failed to add dynamic id [%04hx:%04hx[%04hx:%04hx]] class %#08x/%08x (%d)\n", + vendor, device, subvendor, subdevice, + class, class_mask, rc); + else + pr_info("add [%04hx:%04hx[%04hx:%04hx]] class %#08x/%08x\n", + vendor, device, subvendor, subdevice, + class, class_mask); + } +} + static int __init vfio_pci_init(void) { int ret; @@ -1043,21 +1186,16 @@ static int __init vfio_pci_init(void) if (ret) return ret; - /* Start the virqfd cleanup handler */ - ret = vfio_pci_virqfd_init(); - if (ret) - goto out_virqfd; - /* Register and scan for devices */ ret = pci_register_driver(&vfio_pci_driver); if (ret) goto out_driver; + vfio_pci_fill_ids(); + return 0; out_driver: - vfio_pci_virqfd_exit(); -out_virqfd: vfio_pci_uninit_perm_bits(); return ret; } diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c index 2027a27546ef4f..1f577b4ac12602 100644 --- a/drivers/vfio/pci/vfio_pci_intrs.c +++ b/drivers/vfio/pci/vfio_pci_intrs.c @@ -19,230 +19,19 @@ #include #include #include -#include #include #include -#include #include #include "vfio_pci_private.h" -/* - * IRQfd - generic - */ -struct virqfd { - struct vfio_pci_device *vdev; - struct eventfd_ctx *eventfd; - int (*handler)(struct vfio_pci_device *, void *); - void (*thread)(struct vfio_pci_device *, void *); - void *data; - struct work_struct inject; - wait_queue_t wait; - poll_table pt; - struct work_struct shutdown; - struct virqfd **pvirqfd; -}; - -static struct workqueue_struct *vfio_irqfd_cleanup_wq; - -int __init vfio_pci_virqfd_init(void) -{ - vfio_irqfd_cleanup_wq = - create_singlethread_workqueue("vfio-irqfd-cleanup"); - if (!vfio_irqfd_cleanup_wq) - return -ENOMEM; - - return 0; -} - -void vfio_pci_virqfd_exit(void) -{ - destroy_workqueue(vfio_irqfd_cleanup_wq); -} - -static void virqfd_deactivate(struct virqfd *virqfd) -{ - queue_work(vfio_irqfd_cleanup_wq, &virqfd->shutdown); -} - -static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key) -{ - struct virqfd *virqfd = container_of(wait, struct virqfd, wait); - unsigned long flags = (unsigned long)key; - - if (flags & POLLIN) { - /* An event has been signaled, call function */ - if ((!virqfd->handler || - virqfd->handler(virqfd->vdev, virqfd->data)) && - virqfd->thread) - schedule_work(&virqfd->inject); - } - - if (flags & POLLHUP) { - unsigned long flags; - spin_lock_irqsave(&virqfd->vdev->irqlock, flags); - - /* - * The eventfd is closing, if the virqfd has not yet been - * queued for release, as determined by testing whether the - * vdev pointer to it is still valid, queue it now. As - * with kvm irqfds, we know we won't race against the virqfd - * going away because we hold wqh->lock to get here. - */ - if (*(virqfd->pvirqfd) == virqfd) { - *(virqfd->pvirqfd) = NULL; - virqfd_deactivate(virqfd); - } - - spin_unlock_irqrestore(&virqfd->vdev->irqlock, flags); - } - - return 0; -} - -static void virqfd_ptable_queue_proc(struct file *file, - wait_queue_head_t *wqh, poll_table *pt) -{ - struct virqfd *virqfd = container_of(pt, struct virqfd, pt); - add_wait_queue(wqh, &virqfd->wait); -} - -static void virqfd_shutdown(struct work_struct *work) -{ - struct virqfd *virqfd = container_of(work, struct virqfd, shutdown); - u64 cnt; - - eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt); - flush_work(&virqfd->inject); - eventfd_ctx_put(virqfd->eventfd); - - kfree(virqfd); -} - -static void virqfd_inject(struct work_struct *work) -{ - struct virqfd *virqfd = container_of(work, struct virqfd, inject); - if (virqfd->thread) - virqfd->thread(virqfd->vdev, virqfd->data); -} - -static int virqfd_enable(struct vfio_pci_device *vdev, - int (*handler)(struct vfio_pci_device *, void *), - void (*thread)(struct vfio_pci_device *, void *), - void *data, struct virqfd **pvirqfd, int fd) -{ - struct fd irqfd; - struct eventfd_ctx *ctx; - struct virqfd *virqfd; - int ret = 0; - unsigned int events; - - virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL); - if (!virqfd) - return -ENOMEM; - - virqfd->pvirqfd = pvirqfd; - virqfd->vdev = vdev; - virqfd->handler = handler; - virqfd->thread = thread; - virqfd->data = data; - - INIT_WORK(&virqfd->shutdown, virqfd_shutdown); - INIT_WORK(&virqfd->inject, virqfd_inject); - - irqfd = fdget(fd); - if (!irqfd.file) { - ret = -EBADF; - goto err_fd; - } - - ctx = eventfd_ctx_fileget(irqfd.file); - if (IS_ERR(ctx)) { - ret = PTR_ERR(ctx); - goto err_ctx; - } - - virqfd->eventfd = ctx; - - /* - * virqfds can be released by closing the eventfd or directly - * through ioctl. These are both done through a workqueue, so - * we update the pointer to the virqfd under lock to avoid - * pushing multiple jobs to release the same virqfd. - */ - spin_lock_irq(&vdev->irqlock); - - if (*pvirqfd) { - spin_unlock_irq(&vdev->irqlock); - ret = -EBUSY; - goto err_busy; - } - *pvirqfd = virqfd; - - spin_unlock_irq(&vdev->irqlock); - - /* - * Install our own custom wake-up handling so we are notified via - * a callback whenever someone signals the underlying eventfd. - */ - init_waitqueue_func_entry(&virqfd->wait, virqfd_wakeup); - init_poll_funcptr(&virqfd->pt, virqfd_ptable_queue_proc); - - events = irqfd.file->f_op->poll(irqfd.file, &virqfd->pt); - - /* - * Check if there was an event already pending on the eventfd - * before we registered and trigger it as if we didn't miss it. - */ - if (events & POLLIN) { - if ((!handler || handler(vdev, data)) && thread) - schedule_work(&virqfd->inject); - } - - /* - * Do not drop the file until the irqfd is fully initialized, - * otherwise we might race against the POLLHUP. - */ - fdput(irqfd); - - return 0; -err_busy: - eventfd_ctx_put(ctx); -err_ctx: - fdput(irqfd); -err_fd: - kfree(virqfd); - - return ret; -} - -static void virqfd_disable(struct vfio_pci_device *vdev, - struct virqfd **pvirqfd) -{ - unsigned long flags; - - spin_lock_irqsave(&vdev->irqlock, flags); - - if (*pvirqfd) { - virqfd_deactivate(*pvirqfd); - *pvirqfd = NULL; - } - - spin_unlock_irqrestore(&vdev->irqlock, flags); - - /* - * Block until we know all outstanding shutdown jobs have completed. - * Even if we don't queue the job, flush the wq to be sure it's - * been released. - */ - flush_workqueue(vfio_irqfd_cleanup_wq); -} - /* * INTx */ -static void vfio_send_intx_eventfd(struct vfio_pci_device *vdev, void *unused) +static void vfio_send_intx_eventfd(void *opaque, void *unused) { + struct vfio_pci_device *vdev = opaque; + if (likely(is_intx(vdev) && !vdev->virq_disabled)) eventfd_signal(vdev->ctx[0].trigger, 1); } @@ -285,9 +74,9 @@ void vfio_pci_intx_mask(struct vfio_pci_device *vdev) * a signal is necessary, which can then be handled via a work queue * or directly depending on the caller. */ -static int vfio_pci_intx_unmask_handler(struct vfio_pci_device *vdev, - void *unused) +static int vfio_pci_intx_unmask_handler(void *opaque, void *unused) { + struct vfio_pci_device *vdev = opaque; struct pci_dev *pdev = vdev->pdev; unsigned long flags; int ret = 0; @@ -440,8 +229,8 @@ static int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd) static void vfio_intx_disable(struct vfio_pci_device *vdev) { vfio_intx_set_signal(vdev, -1); - virqfd_disable(vdev, &vdev->ctx[0].unmask); - virqfd_disable(vdev, &vdev->ctx[0].mask); + vfio_virqfd_disable(&vdev->ctx[0].unmask); + vfio_virqfd_disable(&vdev->ctx[0].mask); vdev->irq_type = VFIO_PCI_NUM_IRQS; vdev->num_ctx = 0; kfree(vdev->ctx); @@ -605,8 +394,8 @@ static void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix) vfio_msi_set_block(vdev, 0, vdev->num_ctx, NULL, msix); for (i = 0; i < vdev->num_ctx; i++) { - virqfd_disable(vdev, &vdev->ctx[i].unmask); - virqfd_disable(vdev, &vdev->ctx[i].mask); + vfio_virqfd_disable(&vdev->ctx[i].unmask); + vfio_virqfd_disable(&vdev->ctx[i].mask); } if (msix) { @@ -639,11 +428,12 @@ static int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev, } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { int32_t fd = *(int32_t *)data; if (fd >= 0) - return virqfd_enable(vdev, vfio_pci_intx_unmask_handler, - vfio_send_intx_eventfd, NULL, - &vdev->ctx[0].unmask, fd); + return vfio_virqfd_enable((void *) vdev, + vfio_pci_intx_unmask_handler, + vfio_send_intx_eventfd, NULL, + &vdev->ctx[0].unmask, fd); - virqfd_disable(vdev, &vdev->ctx[0].unmask); + vfio_virqfd_disable(&vdev->ctx[0].unmask); } return 0; diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h index c9f9b323f15273..ae0e1b4c17114a 100644 --- a/drivers/vfio/pci/vfio_pci_private.h +++ b/drivers/vfio/pci/vfio_pci_private.h @@ -87,9 +87,6 @@ extern ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf, extern int vfio_pci_init_perm_bits(void); extern void vfio_pci_uninit_perm_bits(void); -extern int vfio_pci_virqfd_init(void); -extern void vfio_pci_virqfd_exit(void); - extern int vfio_config_init(struct vfio_pci_device *vdev); extern void vfio_config_free(struct vfio_pci_device *vdev); #endif /* VFIO_PCI_PRIVATE_H */ diff --git a/drivers/vfio/platform/Kconfig b/drivers/vfio/platform/Kconfig new file mode 100644 index 00000000000000..9a4403e2a36c81 --- /dev/null +++ b/drivers/vfio/platform/Kconfig @@ -0,0 +1,20 @@ +config VFIO_PLATFORM + tristate "VFIO support for platform devices" + depends on VFIO && EVENTFD && ARM + select VFIO_VIRQFD + help + Support for platform devices with VFIO. This is required to make + use of platform devices present on the system using the VFIO + framework. + + If you don't know what to do here, say N. + +config VFIO_AMBA + tristate "VFIO support for AMBA devices" + depends on VFIO_PLATFORM && ARM_AMBA + help + Support for ARM AMBA devices with VFIO. This is required to make + use of ARM AMBA devices present on the system using the VFIO + framework. + + If you don't know what to do here, say N. diff --git a/drivers/vfio/platform/Makefile b/drivers/vfio/platform/Makefile new file mode 100644 index 00000000000000..81de144c0eaa02 --- /dev/null +++ b/drivers/vfio/platform/Makefile @@ -0,0 +1,8 @@ + +vfio-platform-y := vfio_platform.o vfio_platform_common.o vfio_platform_irq.o + +obj-$(CONFIG_VFIO_PLATFORM) += vfio-platform.o + +vfio-amba-y := vfio_amba.o + +obj-$(CONFIG_VFIO_AMBA) += vfio-amba.o diff --git a/drivers/vfio/platform/vfio_amba.c b/drivers/vfio/platform/vfio_amba.c new file mode 100644 index 00000000000000..ff0331f72526b3 --- /dev/null +++ b/drivers/vfio/platform/vfio_amba.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2013 - Virtual Open Systems + * Author: Antonios Motakis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "vfio_platform_private.h" + +#define DRIVER_VERSION "0.10" +#define DRIVER_AUTHOR "Antonios Motakis " +#define DRIVER_DESC "VFIO for AMBA devices - User Level meta-driver" + +/* probing devices from the AMBA bus */ + +static struct resource *get_amba_resource(struct vfio_platform_device *vdev, + int i) +{ + struct amba_device *adev = (struct amba_device *) vdev->opaque; + + if (i == 0) + return &adev->res; + + return NULL; +} + +static int get_amba_irq(struct vfio_platform_device *vdev, int i) +{ + struct amba_device *adev = (struct amba_device *) vdev->opaque; + int ret = 0; + + if (i < AMBA_NR_IRQS) + ret = adev->irq[i]; + + /* zero is an unset IRQ for AMBA devices */ + return ret ? ret : -ENXIO; +} + +static int vfio_amba_probe(struct amba_device *adev, const struct amba_id *id) +{ + struct vfio_platform_device *vdev; + int ret; + + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) + return -ENOMEM; + + vdev->name = kasprintf(GFP_KERNEL, "vfio-amba-%08x", adev->periphid); + if (!vdev->name) { + kfree(vdev); + return -ENOMEM; + } + + vdev->opaque = (void *) adev; + vdev->flags = VFIO_DEVICE_FLAGS_AMBA; + vdev->get_resource = get_amba_resource; + vdev->get_irq = get_amba_irq; + + ret = vfio_platform_probe_common(vdev, &adev->dev); + if (ret) { + kfree(vdev->name); + kfree(vdev); + } + + return ret; +} + +static int vfio_amba_remove(struct amba_device *adev) +{ + struct vfio_platform_device *vdev; + + vdev = vfio_platform_remove_common(&adev->dev); + if (vdev) { + kfree(vdev->name); + kfree(vdev); + return 0; + } + + return -EINVAL; +} + +static struct amba_id pl330_ids[] = { + { 0, 0 }, +}; + +MODULE_DEVICE_TABLE(amba, pl330_ids); + +static struct amba_driver vfio_amba_driver = { + .probe = vfio_amba_probe, + .remove = vfio_amba_remove, + .id_table = pl330_ids, + .drv = { + .name = "vfio-amba", + .owner = THIS_MODULE, + }, +}; + +module_amba_driver(vfio_amba_driver); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/vfio/platform/vfio_platform.c b/drivers/vfio/platform/vfio_platform.c new file mode 100644 index 00000000000000..cef645c8399679 --- /dev/null +++ b/drivers/vfio/platform/vfio_platform.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2013 - Virtual Open Systems + * Author: Antonios Motakis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "vfio_platform_private.h" + +#define DRIVER_VERSION "0.10" +#define DRIVER_AUTHOR "Antonios Motakis " +#define DRIVER_DESC "VFIO for platform devices - User Level meta-driver" + +/* probing devices from the linux platform bus */ + +static struct resource *get_platform_resource(struct vfio_platform_device *vdev, + int num) +{ + struct platform_device *dev = (struct platform_device *) vdev->opaque; + int i; + + for (i = 0; i < dev->num_resources; i++) { + struct resource *r = &dev->resource[i]; + + if (resource_type(r) & (IORESOURCE_MEM|IORESOURCE_IO)) { + if (!num) + return r; + + num--; + } + } + return NULL; +} + +static int get_platform_irq(struct vfio_platform_device *vdev, int i) +{ + struct platform_device *pdev = (struct platform_device *) vdev->opaque; + + return platform_get_irq(pdev, i); +} + +static int vfio_platform_probe(struct platform_device *pdev) +{ + struct vfio_platform_device *vdev; + int ret; + + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) + return -ENOMEM; + + vdev->opaque = (void *) pdev; + vdev->name = pdev->name; + vdev->flags = VFIO_DEVICE_FLAGS_PLATFORM; + vdev->get_resource = get_platform_resource; + vdev->get_irq = get_platform_irq; + + ret = vfio_platform_probe_common(vdev, &pdev->dev); + if (ret) + kfree(vdev); + + return ret; +} + +static int vfio_platform_remove(struct platform_device *pdev) +{ + struct vfio_platform_device *vdev; + + vdev = vfio_platform_remove_common(&pdev->dev); + if (vdev) { + kfree(vdev); + return 0; + } + + return -EINVAL; +} + +static struct platform_driver vfio_platform_driver = { + .probe = vfio_platform_probe, + .remove = vfio_platform_remove, + .driver = { + .name = "vfio-platform", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(vfio_platform_driver); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/vfio/platform/vfio_platform_common.c b/drivers/vfio/platform/vfio_platform_common.c new file mode 100644 index 00000000000000..abcff7a1aa667b --- /dev/null +++ b/drivers/vfio/platform/vfio_platform_common.c @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2013 - Virtual Open Systems + * Author: Antonios Motakis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vfio_platform_private.h" + +static DEFINE_MUTEX(driver_lock); + +static int vfio_platform_regions_init(struct vfio_platform_device *vdev) +{ + int cnt = 0, i; + + while (vdev->get_resource(vdev, cnt)) + cnt++; + + vdev->regions = kcalloc(cnt, sizeof(struct vfio_platform_region), + GFP_KERNEL); + if (!vdev->regions) + return -ENOMEM; + + for (i = 0; i < cnt; i++) { + struct resource *res = + vdev->get_resource(vdev, i); + + if (!res) + goto err; + + vdev->regions[i].addr = res->start; + vdev->regions[i].size = resource_size(res); + vdev->regions[i].flags = 0; + + switch (resource_type(res)) { + case IORESOURCE_MEM: + vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_MMIO; + vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_READ; + if (!(res->flags & IORESOURCE_READONLY)) + vdev->regions[i].flags |= + VFIO_REGION_INFO_FLAG_WRITE; + + /* + * Only regions addressed with PAGE granularity may be + * MMAPed securely. + */ + if (!(vdev->regions[i].addr & ~PAGE_MASK) && + !(vdev->regions[i].size & ~PAGE_MASK)) + vdev->regions[i].flags |= + VFIO_REGION_INFO_FLAG_MMAP; + + break; + case IORESOURCE_IO: + vdev->regions[i].type = VFIO_PLATFORM_REGION_TYPE_PIO; + break; + default: + goto err; + } + } + + vdev->num_regions = cnt; + + return 0; +err: + kfree(vdev->regions); + return -EINVAL; +} + +static void vfio_platform_regions_cleanup(struct vfio_platform_device *vdev) +{ + int i; + + for (i = 0; i < vdev->num_regions; i++) + iounmap(vdev->regions[i].ioaddr); + + vdev->num_regions = 0; + kfree(vdev->regions); +} + +static void vfio_platform_release(void *device_data) +{ + struct vfio_platform_device *vdev = device_data; + + mutex_lock(&driver_lock); + + if (!(--vdev->refcnt)) { + vfio_platform_regions_cleanup(vdev); + vfio_platform_irq_cleanup(vdev); + } + + mutex_unlock(&driver_lock); + + module_put(THIS_MODULE); +} + +static int vfio_platform_open(void *device_data) +{ + struct vfio_platform_device *vdev = device_data; + int ret; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + mutex_lock(&driver_lock); + + if (!vdev->refcnt) { + ret = vfio_platform_regions_init(vdev); + if (ret) + goto err_reg; + + ret = vfio_platform_irq_init(vdev); + if (ret) + goto err_irq; + } + + vdev->refcnt++; + + mutex_unlock(&driver_lock); + return 0; + +err_irq: + vfio_platform_regions_cleanup(vdev); +err_reg: + mutex_unlock(&driver_lock); + module_put(THIS_MODULE); + return ret; +} + +static long vfio_platform_ioctl(void *device_data, + unsigned int cmd, unsigned long arg) +{ + struct vfio_platform_device *vdev = device_data; + unsigned long minsz; + + if (cmd == VFIO_DEVICE_GET_INFO) { + struct vfio_device_info info; + + minsz = offsetofend(struct vfio_device_info, num_irqs); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + info.flags = vdev->flags; + info.num_regions = vdev->num_regions; + info.num_irqs = vdev->num_irqs; + + return copy_to_user((void __user *)arg, &info, minsz); + + } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) { + struct vfio_region_info info; + + minsz = offsetofend(struct vfio_region_info, offset); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + if (info.index >= vdev->num_regions) + return -EINVAL; + + /* map offset to the physical address */ + info.offset = VFIO_PLATFORM_INDEX_TO_OFFSET(info.index); + info.size = vdev->regions[info.index].size; + info.flags = vdev->regions[info.index].flags; + + return copy_to_user((void __user *)arg, &info, minsz); + + } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) { + struct vfio_irq_info info; + + minsz = offsetofend(struct vfio_irq_info, count); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + if (info.index >= vdev->num_irqs) + return -EINVAL; + + info.flags = vdev->irqs[info.index].flags; + info.count = vdev->irqs[info.index].count; + + return copy_to_user((void __user *)arg, &info, minsz); + + } else if (cmd == VFIO_DEVICE_SET_IRQS) { + struct vfio_irq_set hdr; + u8 *data = NULL; + int ret = 0; + + minsz = offsetofend(struct vfio_irq_set, count); + + if (copy_from_user(&hdr, (void __user *)arg, minsz)) + return -EFAULT; + + if (hdr.argsz < minsz) + return -EINVAL; + + if (hdr.index >= vdev->num_irqs) + return -EINVAL; + + if (hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK | + VFIO_IRQ_SET_ACTION_TYPE_MASK)) + return -EINVAL; + + if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) { + size_t size; + + if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL) + size = sizeof(uint8_t); + else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD) + size = sizeof(int32_t); + else + return -EINVAL; + + if (hdr.argsz - minsz < size) + return -EINVAL; + + data = memdup_user((void __user *)(arg + minsz), size); + if (IS_ERR(data)) + return PTR_ERR(data); + } + + mutex_lock(&vdev->igate); + + ret = vfio_platform_set_irqs_ioctl(vdev, hdr.flags, hdr.index, + hdr.start, hdr.count, data); + mutex_unlock(&vdev->igate); + kfree(data); + + return ret; + + } else if (cmd == VFIO_DEVICE_RESET) + return -EINVAL; + + return -ENOTTY; +} + +static ssize_t vfio_platform_read_mmio(struct vfio_platform_region reg, + char __user *buf, size_t count, + loff_t off) +{ + unsigned int done = 0; + + if (!reg.ioaddr) { + reg.ioaddr = + ioremap_nocache(reg.addr, reg.size); + + if (!reg.ioaddr) + return -ENOMEM; + } + + while (count) { + size_t filled; + + if (count >= 4 && !(off % 4)) { + u32 val; + + val = ioread32(reg.ioaddr + off); + if (copy_to_user(buf, &val, 4)) + goto err; + + filled = 4; + } else if (count >= 2 && !(off % 2)) { + u16 val; + + val = ioread16(reg.ioaddr + off); + if (copy_to_user(buf, &val, 2)) + goto err; + + filled = 2; + } else { + u8 val; + + val = ioread8(reg.ioaddr + off); + if (copy_to_user(buf, &val, 1)) + goto err; + + filled = 1; + } + + + count -= filled; + done += filled; + off += filled; + buf += filled; + } + + return done; +err: + return -EFAULT; +} + +static ssize_t vfio_platform_read(void *device_data, char __user *buf, + size_t count, loff_t *ppos) +{ + struct vfio_platform_device *vdev = device_data; + unsigned int index = VFIO_PLATFORM_OFFSET_TO_INDEX(*ppos); + loff_t off = *ppos & VFIO_PLATFORM_OFFSET_MASK; + + if (index >= vdev->num_regions) + return -EINVAL; + + if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ)) + return -EINVAL; + + if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO) + return vfio_platform_read_mmio(vdev->regions[index], + buf, count, off); + else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO) + return -EINVAL; /* not implemented */ + + return -EINVAL; +} + +static ssize_t vfio_platform_write_mmio(struct vfio_platform_region reg, + const char __user *buf, size_t count, + loff_t off) +{ + unsigned int done = 0; + + if (!reg.ioaddr) { + reg.ioaddr = + ioremap_nocache(reg.addr, reg.size); + + if (!reg.ioaddr) + return -ENOMEM; + } + + while (count) { + size_t filled; + + if (count >= 4 && !(off % 4)) { + u32 val; + + if (copy_from_user(&val, buf, 4)) + goto err; + iowrite32(val, reg.ioaddr + off); + + filled = 4; + } else if (count >= 2 && !(off % 2)) { + u16 val; + + if (copy_from_user(&val, buf, 2)) + goto err; + iowrite16(val, reg.ioaddr + off); + + filled = 2; + } else { + u8 val; + + if (copy_from_user(&val, buf, 1)) + goto err; + iowrite8(val, reg.ioaddr + off); + + filled = 1; + } + + count -= filled; + done += filled; + off += filled; + buf += filled; + } + + return done; +err: + return -EFAULT; +} + +static ssize_t vfio_platform_write(void *device_data, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct vfio_platform_device *vdev = device_data; + unsigned int index = VFIO_PLATFORM_OFFSET_TO_INDEX(*ppos); + loff_t off = *ppos & VFIO_PLATFORM_OFFSET_MASK; + + if (index >= vdev->num_regions) + return -EINVAL; + + if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE)) + return -EINVAL; + + if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO) + return vfio_platform_write_mmio(vdev->regions[index], + buf, count, off); + else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO) + return -EINVAL; /* not implemented */ + + return -EINVAL; +} + +static int vfio_platform_mmap_mmio(struct vfio_platform_region region, + struct vm_area_struct *vma) +{ + u64 req_len, pgoff, req_start; + + req_len = vma->vm_end - vma->vm_start; + pgoff = vma->vm_pgoff & + ((1U << (VFIO_PLATFORM_OFFSET_SHIFT - PAGE_SHIFT)) - 1); + req_start = pgoff << PAGE_SHIFT; + + if (region.size < PAGE_SIZE || req_start + req_len > region.size) + return -EINVAL; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_pgoff = (region.addr >> PAGE_SHIFT) + pgoff; + + return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + req_len, vma->vm_page_prot); +} + +static int vfio_platform_mmap(void *device_data, struct vm_area_struct *vma) +{ + struct vfio_platform_device *vdev = device_data; + unsigned int index; + + index = vma->vm_pgoff >> (VFIO_PLATFORM_OFFSET_SHIFT - PAGE_SHIFT); + + if (vma->vm_end < vma->vm_start) + return -EINVAL; + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + if (index >= vdev->num_regions) + return -EINVAL; + if (vma->vm_start & ~PAGE_MASK) + return -EINVAL; + if (vma->vm_end & ~PAGE_MASK) + return -EINVAL; + + if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_MMAP)) + return -EINVAL; + + if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ) + && (vma->vm_flags & VM_READ)) + return -EINVAL; + + if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE) + && (vma->vm_flags & VM_WRITE)) + return -EINVAL; + + vma->vm_private_data = vdev; + + if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO) + return vfio_platform_mmap_mmio(vdev->regions[index], vma); + + else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO) + return -EINVAL; /* not implemented */ + + return -EINVAL; +} + +static const struct vfio_device_ops vfio_platform_ops = { + .name = "vfio-platform", + .open = vfio_platform_open, + .release = vfio_platform_release, + .ioctl = vfio_platform_ioctl, + .read = vfio_platform_read, + .write = vfio_platform_write, + .mmap = vfio_platform_mmap, +}; + +int vfio_platform_probe_common(struct vfio_platform_device *vdev, + struct device *dev) +{ + struct iommu_group *group; + int ret; + + if (!vdev) + return -EINVAL; + + group = iommu_group_get(dev); + if (!group) { + pr_err("VFIO: No IOMMU group for device %s\n", vdev->name); + return -EINVAL; + } + + ret = vfio_add_group_dev(dev, &vfio_platform_ops, vdev); + if (ret) { + iommu_group_put(group); + return ret; + } + + mutex_init(&vdev->igate); + + return 0; +} +EXPORT_SYMBOL_GPL(vfio_platform_probe_common); + +struct vfio_platform_device *vfio_platform_remove_common(struct device *dev) +{ + struct vfio_platform_device *vdev; + + vdev = vfio_del_group_dev(dev); + if (vdev) + iommu_group_put(dev->iommu_group); + + return vdev; +} +EXPORT_SYMBOL_GPL(vfio_platform_remove_common); diff --git a/drivers/vfio/platform/vfio_platform_irq.c b/drivers/vfio/platform/vfio_platform_irq.c new file mode 100644 index 00000000000000..88bba57b30a8b2 --- /dev/null +++ b/drivers/vfio/platform/vfio_platform_irq.c @@ -0,0 +1,336 @@ +/* + * VFIO platform devices interrupt handling + * + * Copyright (C) 2013 - Virtual Open Systems + * Author: Antonios Motakis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "vfio_platform_private.h" + +static void vfio_platform_mask(struct vfio_platform_irq *irq_ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&irq_ctx->lock, flags); + + if (!irq_ctx->masked) { + disable_irq_nosync(irq_ctx->hwirq); + irq_ctx->masked = true; + } + + spin_unlock_irqrestore(&irq_ctx->lock, flags); +} + +static int vfio_platform_mask_handler(void *opaque, void *unused) +{ + struct vfio_platform_irq *irq_ctx = opaque; + + vfio_platform_mask(irq_ctx); + + return 0; +} + +static int vfio_platform_set_irq_mask(struct vfio_platform_device *vdev, + unsigned index, unsigned start, + unsigned count, uint32_t flags, + void *data) +{ + if (start != 0 || count != 1) + return -EINVAL; + + if (!(vdev->irqs[index].flags & VFIO_IRQ_INFO_MASKABLE)) + return -EINVAL; + + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int32_t fd = *(int32_t *)data; + + if (fd >= 0) + return vfio_virqfd_enable((void *) &vdev->irqs[index], + vfio_platform_mask_handler, + NULL, NULL, + &vdev->irqs[index].mask, fd); + + vfio_virqfd_disable(&vdev->irqs[index].mask); + return 0; + } + + if (flags & VFIO_IRQ_SET_DATA_NONE) { + vfio_platform_mask(&vdev->irqs[index]); + + } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { + uint8_t mask = *(uint8_t *)data; + + if (mask) + vfio_platform_mask(&vdev->irqs[index]); + } + + return 0; +} + +static void vfio_platform_unmask(struct vfio_platform_irq *irq_ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&irq_ctx->lock, flags); + + if (irq_ctx->masked) { + enable_irq(irq_ctx->hwirq); + irq_ctx->masked = false; + } + + spin_unlock_irqrestore(&irq_ctx->lock, flags); +} + +static int vfio_platform_unmask_handler(void *opaque, void *unused) +{ + struct vfio_platform_irq *irq_ctx = opaque; + + vfio_platform_unmask(irq_ctx); + + return 0; +} + +static int vfio_platform_set_irq_unmask(struct vfio_platform_device *vdev, + unsigned index, unsigned start, + unsigned count, uint32_t flags, + void *data) +{ + if (start != 0 || count != 1) + return -EINVAL; + + if (!(vdev->irqs[index].flags & VFIO_IRQ_INFO_MASKABLE)) + return -EINVAL; + + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int32_t fd = *(int32_t *)data; + + if (fd >= 0) + return vfio_virqfd_enable((void *) &vdev->irqs[index], + vfio_platform_unmask_handler, + NULL, NULL, + &vdev->irqs[index].unmask, + fd); + + vfio_virqfd_disable(&vdev->irqs[index].unmask); + return 0; + } + + if (flags & VFIO_IRQ_SET_DATA_NONE) { + vfio_platform_unmask(&vdev->irqs[index]); + + } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { + uint8_t unmask = *(uint8_t *)data; + + if (unmask) + vfio_platform_unmask(&vdev->irqs[index]); + } + + return 0; +} + +static irqreturn_t vfio_automasked_irq_handler(int irq, void *dev_id) +{ + struct vfio_platform_irq *irq_ctx = dev_id; + unsigned long flags; + int ret = IRQ_NONE; + + spin_lock_irqsave(&irq_ctx->lock, flags); + + if (!irq_ctx->masked) { + ret = IRQ_HANDLED; + + /* automask maskable interrupts */ + disable_irq_nosync(irq_ctx->hwirq); + irq_ctx->masked = true; + } + + spin_unlock_irqrestore(&irq_ctx->lock, flags); + + if (ret == IRQ_HANDLED) + eventfd_signal(irq_ctx->trigger, 1); + + return ret; +} + +static irqreturn_t vfio_irq_handler(int irq, void *dev_id) +{ + struct vfio_platform_irq *irq_ctx = dev_id; + + eventfd_signal(irq_ctx->trigger, 1); + + return IRQ_HANDLED; +} + +static int vfio_set_trigger(struct vfio_platform_device *vdev, int index, + int fd, irq_handler_t handler) +{ + struct vfio_platform_irq *irq = &vdev->irqs[index]; + struct eventfd_ctx *trigger; + int ret; + + if (irq->trigger) { + free_irq(irq->hwirq, irq); + kfree(irq->name); + eventfd_ctx_put(irq->trigger); + irq->trigger = NULL; + } + + if (fd < 0) /* Disable only */ + return 0; + + irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)", + irq->hwirq, vdev->name); + if (!irq->name) + return -ENOMEM; + + trigger = eventfd_ctx_fdget(fd); + if (IS_ERR(trigger)) { + kfree(irq->name); + return PTR_ERR(trigger); + } + + irq->trigger = trigger; + + irq_set_status_flags(irq->hwirq, IRQ_NOAUTOEN); + ret = request_irq(irq->hwirq, handler, 0, irq->name, irq); + if (ret) { + kfree(irq->name); + eventfd_ctx_put(trigger); + irq->trigger = NULL; + return ret; + } + + if (!irq->masked) + enable_irq(irq->hwirq); + + return 0; +} + +static int vfio_platform_set_irq_trigger(struct vfio_platform_device *vdev, + unsigned index, unsigned start, + unsigned count, uint32_t flags, + void *data) +{ + struct vfio_platform_irq *irq = &vdev->irqs[index]; + irq_handler_t handler; + + if (vdev->irqs[index].flags & VFIO_IRQ_INFO_AUTOMASKED) + handler = vfio_automasked_irq_handler; + else + handler = vfio_irq_handler; + + if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) + return vfio_set_trigger(vdev, index, -1, handler); + + if (start != 0 || count != 1) + return -EINVAL; + + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { + int32_t fd = *(int32_t *)data; + + return vfio_set_trigger(vdev, index, fd, handler); + } + + if (flags & VFIO_IRQ_SET_DATA_NONE) { + handler(irq->hwirq, irq); + + } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { + uint8_t trigger = *(uint8_t *)data; + + if (trigger) + handler(irq->hwirq, irq); + } + + return 0; +} + +int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev, + uint32_t flags, unsigned index, unsigned start, + unsigned count, void *data) +{ + int (*func)(struct vfio_platform_device *vdev, unsigned index, + unsigned start, unsigned count, uint32_t flags, + void *data) = NULL; + + switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { + case VFIO_IRQ_SET_ACTION_MASK: + func = vfio_platform_set_irq_mask; + break; + case VFIO_IRQ_SET_ACTION_UNMASK: + func = vfio_platform_set_irq_unmask; + break; + case VFIO_IRQ_SET_ACTION_TRIGGER: + func = vfio_platform_set_irq_trigger; + break; + } + + if (!func) + return -ENOTTY; + + return func(vdev, index, start, count, flags, data); +} + +int vfio_platform_irq_init(struct vfio_platform_device *vdev) +{ + int cnt = 0, i; + + while (vdev->get_irq(vdev, cnt) >= 0) + cnt++; + + vdev->irqs = kcalloc(cnt, sizeof(struct vfio_platform_irq), GFP_KERNEL); + if (!vdev->irqs) + return -ENOMEM; + + for (i = 0; i < cnt; i++) { + int hwirq = vdev->get_irq(vdev, i); + + if (hwirq < 0) + goto err; + + spin_lock_init(&vdev->irqs[i].lock); + + vdev->irqs[i].flags = VFIO_IRQ_INFO_EVENTFD; + + if (irq_get_trigger_type(hwirq) & IRQ_TYPE_LEVEL_MASK) + vdev->irqs[i].flags |= VFIO_IRQ_INFO_MASKABLE + | VFIO_IRQ_INFO_AUTOMASKED; + + vdev->irqs[i].count = 1; + vdev->irqs[i].hwirq = hwirq; + vdev->irqs[i].masked = false; + } + + vdev->num_irqs = cnt; + + return 0; +err: + kfree(vdev->irqs); + return -EINVAL; +} + +void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev) +{ + int i; + + for (i = 0; i < vdev->num_irqs; i++) + vfio_set_trigger(vdev, i, -1, NULL); + + vdev->num_irqs = 0; + kfree(vdev->irqs); +} diff --git a/drivers/vfio/platform/vfio_platform_private.h b/drivers/vfio/platform/vfio_platform_private.h new file mode 100644 index 00000000000000..5d31e04734062e --- /dev/null +++ b/drivers/vfio/platform/vfio_platform_private.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2013 - Virtual Open Systems + * Author: Antonios Motakis + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef VFIO_PLATFORM_PRIVATE_H +#define VFIO_PLATFORM_PRIVATE_H + +#include +#include + +#define VFIO_PLATFORM_OFFSET_SHIFT 40 +#define VFIO_PLATFORM_OFFSET_MASK (((u64)(1) << VFIO_PLATFORM_OFFSET_SHIFT) - 1) + +#define VFIO_PLATFORM_OFFSET_TO_INDEX(off) \ + (off >> VFIO_PLATFORM_OFFSET_SHIFT) + +#define VFIO_PLATFORM_INDEX_TO_OFFSET(index) \ + ((u64)(index) << VFIO_PLATFORM_OFFSET_SHIFT) + +struct vfio_platform_irq { + u32 flags; + u32 count; + int hwirq; + char *name; + struct eventfd_ctx *trigger; + bool masked; + spinlock_t lock; + struct virqfd *unmask; + struct virqfd *mask; +}; + +struct vfio_platform_region { + u64 addr; + resource_size_t size; + u32 flags; + u32 type; +#define VFIO_PLATFORM_REGION_TYPE_MMIO 1 +#define VFIO_PLATFORM_REGION_TYPE_PIO 2 + void __iomem *ioaddr; +}; + +struct vfio_platform_device { + struct vfio_platform_region *regions; + u32 num_regions; + struct vfio_platform_irq *irqs; + u32 num_irqs; + int refcnt; + struct mutex igate; + + /* + * These fields should be filled by the bus specific binder + */ + void *opaque; + const char *name; + uint32_t flags; + /* callbacks to discover device resources */ + struct resource* + (*get_resource)(struct vfio_platform_device *vdev, int i); + int (*get_irq)(struct vfio_platform_device *vdev, int i); +}; + +extern int vfio_platform_probe_common(struct vfio_platform_device *vdev, + struct device *dev); +extern struct vfio_platform_device *vfio_platform_remove_common + (struct device *dev); + +extern int vfio_platform_irq_init(struct vfio_platform_device *vdev); +extern void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev); + +extern int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev, + uint32_t flags, unsigned index, + unsigned start, unsigned count, + void *data); + +#endif /* VFIO_PLATFORM_PRIVATE_H */ diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 4cde8550144406..0d336625ac7113 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -234,22 +234,21 @@ static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group) mutex_lock(&vfio.group_lock); - minor = vfio_alloc_group_minor(group); - if (minor < 0) { - vfio_group_unlock_and_free(group); - return ERR_PTR(minor); - } - /* Did we race creating this group? */ list_for_each_entry(tmp, &vfio.group_list, vfio_next) { if (tmp->iommu_group == iommu_group) { vfio_group_get(tmp); - vfio_free_group_minor(minor); vfio_group_unlock_and_free(group); return tmp; } } + minor = vfio_alloc_group_minor(group); + if (minor < 0) { + vfio_group_unlock_and_free(group); + return ERR_PTR(minor); + } + dev = device_create(vfio.class, NULL, MKDEV(MAJOR(vfio.group_devt), minor), group, "%d", iommu_group_id(iommu_group)); diff --git a/drivers/vfio/virqfd.c b/drivers/vfio/virqfd.c new file mode 100644 index 00000000000000..27c89cd5d70b3c --- /dev/null +++ b/drivers/vfio/virqfd.c @@ -0,0 +1,226 @@ +/* + * VFIO generic eventfd code for IRQFD support. + * Derived from drivers/vfio/pci/vfio_pci_intrs.c + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "0.1" +#define DRIVER_AUTHOR "Alex Williamson " +#define DRIVER_DESC "IRQFD support for VFIO bus drivers" + +static struct workqueue_struct *vfio_irqfd_cleanup_wq; +static DEFINE_SPINLOCK(virqfd_lock); + +static int __init vfio_virqfd_init(void) +{ + vfio_irqfd_cleanup_wq = + create_singlethread_workqueue("vfio-irqfd-cleanup"); + if (!vfio_irqfd_cleanup_wq) + return -ENOMEM; + + return 0; +} + +static void __exit vfio_virqfd_exit(void) +{ + destroy_workqueue(vfio_irqfd_cleanup_wq); +} + +static void virqfd_deactivate(struct virqfd *virqfd) +{ + queue_work(vfio_irqfd_cleanup_wq, &virqfd->shutdown); +} + +static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key) +{ + struct virqfd *virqfd = container_of(wait, struct virqfd, wait); + unsigned long flags = (unsigned long)key; + + if (flags & POLLIN) { + /* An event has been signaled, call function */ + if ((!virqfd->handler || + virqfd->handler(virqfd->opaque, virqfd->data)) && + virqfd->thread) + schedule_work(&virqfd->inject); + } + + if (flags & POLLHUP) { + unsigned long flags; + spin_lock_irqsave(&virqfd_lock, flags); + + /* + * The eventfd is closing, if the virqfd has not yet been + * queued for release, as determined by testing whether the + * virqfd pointer to it is still valid, queue it now. As + * with kvm irqfds, we know we won't race against the virqfd + * going away because we hold the lock to get here. + */ + if (*(virqfd->pvirqfd) == virqfd) { + *(virqfd->pvirqfd) = NULL; + virqfd_deactivate(virqfd); + } + + spin_unlock_irqrestore(&virqfd_lock, flags); + } + + return 0; +} + +static void virqfd_ptable_queue_proc(struct file *file, + wait_queue_head_t *wqh, poll_table *pt) +{ + struct virqfd *virqfd = container_of(pt, struct virqfd, pt); + add_wait_queue(wqh, &virqfd->wait); +} + +static void virqfd_shutdown(struct work_struct *work) +{ + struct virqfd *virqfd = container_of(work, struct virqfd, shutdown); + u64 cnt; + + eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt); + flush_work(&virqfd->inject); + eventfd_ctx_put(virqfd->eventfd); + + kfree(virqfd); +} + +static void virqfd_inject(struct work_struct *work) +{ + struct virqfd *virqfd = container_of(work, struct virqfd, inject); + if (virqfd->thread) + virqfd->thread(virqfd->opaque, virqfd->data); +} + +int vfio_virqfd_enable(void *opaque, + int (*handler)(void *, void *), + void (*thread)(void *, void *), + void *data, struct virqfd **pvirqfd, int fd) +{ + struct fd irqfd; + struct eventfd_ctx *ctx; + struct virqfd *virqfd; + int ret = 0; + unsigned int events; + + virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL); + if (!virqfd) + return -ENOMEM; + + virqfd->pvirqfd = pvirqfd; + virqfd->opaque = opaque; + virqfd->handler = handler; + virqfd->thread = thread; + virqfd->data = data; + + INIT_WORK(&virqfd->shutdown, virqfd_shutdown); + INIT_WORK(&virqfd->inject, virqfd_inject); + + irqfd = fdget(fd); + if (!irqfd.file) { + ret = -EBADF; + goto err_fd; + } + + ctx = eventfd_ctx_fileget(irqfd.file); + if (IS_ERR(ctx)) { + ret = PTR_ERR(ctx); + goto err_ctx; + } + + virqfd->eventfd = ctx; + + /* + * virqfds can be released by closing the eventfd or directly + * through ioctl. These are both done through a workqueue, so + * we update the pointer to the virqfd under lock to avoid + * pushing multiple jobs to release the same virqfd. + */ + spin_lock_irq(&virqfd_lock); + + if (*pvirqfd) { + spin_unlock_irq(&virqfd_lock); + ret = -EBUSY; + goto err_busy; + } + *pvirqfd = virqfd; + + spin_unlock_irq(&virqfd_lock); + + /* + * Install our own custom wake-up handling so we are notified via + * a callback whenever someone signals the underlying eventfd. + */ + init_waitqueue_func_entry(&virqfd->wait, virqfd_wakeup); + init_poll_funcptr(&virqfd->pt, virqfd_ptable_queue_proc); + + events = irqfd.file->f_op->poll(irqfd.file, &virqfd->pt); + + /* + * Check if there was an event already pending on the eventfd + * before we registered and trigger it as if we didn't miss it. + */ + if (events & POLLIN) { + if ((!handler || handler(opaque, data)) && thread) + schedule_work(&virqfd->inject); + } + + /* + * Do not drop the file until the irqfd is fully initialized, + * otherwise we might race against the POLLHUP. + */ + fdput(irqfd); + + return 0; +err_busy: + eventfd_ctx_put(ctx); +err_ctx: + fdput(irqfd); +err_fd: + kfree(virqfd); + + return ret; +} +EXPORT_SYMBOL_GPL(vfio_virqfd_enable); + +void vfio_virqfd_disable(struct virqfd **pvirqfd) +{ + unsigned long flags; + + spin_lock_irqsave(&virqfd_lock, flags); + + if (*pvirqfd) { + virqfd_deactivate(*pvirqfd); + *pvirqfd = NULL; + } + + spin_unlock_irqrestore(&virqfd_lock, flags); + + /* + * Block until we know all outstanding shutdown jobs have completed. + * Even if we don't queue the job, flush the wq to be sure it's + * been released. + */ + flush_workqueue(vfio_irqfd_cleanup_wq); +} +EXPORT_SYMBOL_GPL(vfio_virqfd_disable); + +module_init(vfio_virqfd_init); +module_exit(vfio_virqfd_exit); + +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c index 9a23698b6fe839..2da5862876d1fe 100644 --- a/drivers/video/backlight/88pm860x_bl.c +++ b/drivers/video/backlight/88pm860x_bl.c @@ -168,10 +168,7 @@ static int pm860x_backlight_dt_init(struct platform_device *pdev, struct device_node *nproot, *np; int iset = 0; - nproot = of_node_get(pdev->dev.parent->of_node); - if (!nproot) - return -ENODEV; - nproot = of_find_node_by_name(nproot, "backlights"); + nproot = of_get_child_by_name(pdev->dev.parent->of_node, "backlights"); if (!nproot) { dev_err(&pdev->dev, "failed to find backlights node\n"); return -ENODEV; diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index efb09046a8cf7b..2d9923a60076e3 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -408,6 +408,16 @@ config BACKLIGHT_PANDORA If you have a Pandora console, say Y to enable the backlight driver. +config BACKLIGHT_SKY81452 + tristate "Backlight driver for SKY81452" + depends on BACKLIGHT_CLASS_DEVICE && MFD_SKY81452 + help + If you have a Skyworks SKY81452, say Y to enable the + backlight driver. + + To compile this driver as a module, choose M here: the module will + be called sky81452-backlight + config BACKLIGHT_TPS65217 tristate "TPS65217 Backlight" depends on BACKLIGHT_CLASS_DEVICE && MFD_TPS65217 diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index fcd50b73216516..d67073f9d421f9 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o +obj-$(CONFIG_BACKLIGHT_SKY81452) += sky81452-backlight.o obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o diff --git a/drivers/video/backlight/da9052_bl.c b/drivers/video/backlight/da9052_bl.c index d4bd74bd507070..b1943e7735a128 100644 --- a/drivers/video/backlight/da9052_bl.c +++ b/drivers/video/backlight/da9052_bl.c @@ -165,6 +165,7 @@ static struct platform_device_id da9052_wled_ids[] = { .name = "da9052-wled3", .driver_data = DA9052_TYPE_WLED3, }, + { }, }; static struct platform_driver da9052_wled_driver = { diff --git a/drivers/video/backlight/sky81452-backlight.c b/drivers/video/backlight/sky81452-backlight.c new file mode 100644 index 00000000000000..052fa1bac03de1 --- /dev/null +++ b/drivers/video/backlight/sky81452-backlight.c @@ -0,0 +1,353 @@ +/* + * sky81452-backlight.c SKY81452 backlight driver + * + * Copyright 2014 Skyworks Solutions Inc. + * Author : Gyungoh Yoo + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* registers */ +#define SKY81452_REG0 0x00 +#define SKY81452_REG1 0x01 +#define SKY81452_REG2 0x02 +#define SKY81452_REG4 0x04 +#define SKY81452_REG5 0x05 + +/* bit mask */ +#define SKY81452_CS 0xFF +#define SKY81452_EN 0x3F +#define SKY81452_IGPW 0x20 +#define SKY81452_PWMMD 0x10 +#define SKY81452_PHASE 0x08 +#define SKY81452_ILIM 0x04 +#define SKY81452_VSHRT 0x03 +#define SKY81452_OCP 0x80 +#define SKY81452_OTMP 0x40 +#define SKY81452_SHRT 0x3F +#define SKY81452_OPN 0x3F + +#define SKY81452_DEFAULT_NAME "lcd-backlight" +#define SKY81452_MAX_BRIGHTNESS (SKY81452_CS + 1) + +#define CTZ(b) __builtin_ctz(b) + +static int sky81452_bl_update_status(struct backlight_device *bd) +{ + const struct sky81452_bl_platform_data *pdata = + dev_get_platdata(bd->dev.parent); + const unsigned int brightness = (unsigned int)bd->props.brightness; + struct regmap *regmap = bl_get_data(bd); + int ret; + + if (brightness > 0) { + ret = regmap_write(regmap, SKY81452_REG0, brightness - 1); + if (IS_ERR_VALUE(ret)) + return ret; + + return regmap_update_bits(regmap, SKY81452_REG1, SKY81452_EN, + pdata->enable << CTZ(SKY81452_EN)); + } + + return regmap_update_bits(regmap, SKY81452_REG1, SKY81452_EN, 0); +} + +static const struct backlight_ops sky81452_bl_ops = { + .update_status = sky81452_bl_update_status, +}; + +static ssize_t sky81452_bl_store_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct regmap *regmap = bl_get_data(to_backlight_device(dev)); + unsigned long value; + int ret; + + ret = kstrtoul(buf, 16, &value); + if (IS_ERR_VALUE(ret)) + return ret; + + ret = regmap_update_bits(regmap, SKY81452_REG1, SKY81452_EN, + value << CTZ(SKY81452_EN)); + if (IS_ERR_VALUE(ret)) + return ret; + + return count; +} + +static ssize_t sky81452_bl_show_open_short(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regmap *regmap = bl_get_data(to_backlight_device(dev)); + unsigned int reg, value = 0; + char tmp[3]; + int i, ret; + + reg = !strcmp(attr->attr.name, "open") ? SKY81452_REG5 : SKY81452_REG4; + ret = regmap_read(regmap, reg, &value); + if (IS_ERR_VALUE(ret)) + return ret; + + if (value & SKY81452_SHRT) { + *buf = 0; + for (i = 0; i < 6; i++) { + if (value & 0x01) { + sprintf(tmp, "%d ", i + 1); + strcat(buf, tmp); + } + value >>= 1; + } + strcat(buf, "\n"); + } else { + strcpy(buf, "none\n"); + } + + return strlen(buf); +} + +static ssize_t sky81452_bl_show_fault(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regmap *regmap = bl_get_data(to_backlight_device(dev)); + unsigned int value = 0; + int ret; + + ret = regmap_read(regmap, SKY81452_REG4, &value); + if (IS_ERR_VALUE(ret)) + return ret; + + *buf = 0; + + if (value & SKY81452_OCP) + strcat(buf, "over-current "); + + if (value & SKY81452_OTMP) + strcat(buf, "over-temperature"); + + strcat(buf, "\n"); + return strlen(buf); +} + +static DEVICE_ATTR(enable, S_IWGRP | S_IWUSR, NULL, sky81452_bl_store_enable); +static DEVICE_ATTR(open, S_IRUGO, sky81452_bl_show_open_short, NULL); +static DEVICE_ATTR(short, S_IRUGO, sky81452_bl_show_open_short, NULL); +static DEVICE_ATTR(fault, S_IRUGO, sky81452_bl_show_fault, NULL); + +static struct attribute *sky81452_bl_attribute[] = { + &dev_attr_enable.attr, + &dev_attr_open.attr, + &dev_attr_short.attr, + &dev_attr_fault.attr, + NULL +}; + +static const struct attribute_group sky81452_bl_attr_group = { + .attrs = sky81452_bl_attribute, +}; + +#ifdef CONFIG_OF +static struct sky81452_bl_platform_data *sky81452_bl_parse_dt( + struct device *dev) +{ + struct device_node *np = of_node_get(dev->of_node); + struct sky81452_bl_platform_data *pdata; + int num_entry; + unsigned int sources[6]; + int ret; + + if (!np) { + dev_err(dev, "backlight node not found.\n"); + return ERR_PTR(-ENODATA); + } + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + of_node_put(np); + return ERR_PTR(-ENOMEM); + } + + of_property_read_string(np, "name", &pdata->name); + pdata->ignore_pwm = of_property_read_bool(np, "skyworks,ignore-pwm"); + pdata->dpwm_mode = of_property_read_bool(np, "skyworks,dpwm-mode"); + pdata->phase_shift = of_property_read_bool(np, "skyworks,phase-shift"); + pdata->gpio_enable = of_get_gpio(np, 0); + + ret = of_property_count_u32_elems(np, "led-sources"); + if (IS_ERR_VALUE(ret)) { + pdata->enable = SKY81452_EN >> CTZ(SKY81452_EN); + } else { + num_entry = ret; + if (num_entry > 6) + num_entry = 6; + + ret = of_property_read_u32_array(np, "led-sources", sources, + num_entry); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "led-sources node is invalid.\n"); + return ERR_PTR(-EINVAL); + } + + pdata->enable = 0; + while (--num_entry) + pdata->enable |= (1 << sources[num_entry]); + } + + ret = of_property_read_u32(np, + "skyworks,short-detection-threshold-volt", + &pdata->short_detection_threshold); + if (IS_ERR_VALUE(ret)) + pdata->short_detection_threshold = 7; + + ret = of_property_read_u32(np, "skyworks,current-limit-mA", + &pdata->boost_current_limit); + if (IS_ERR_VALUE(ret)) + pdata->boost_current_limit = 2750; + + of_node_put(np); + return pdata; +} +#else +static struct sky81452_bl_platform_data *sky81452_bl_parse_dt( + struct device *dev) +{ + return ERR_PTR(-EINVAL); +} +#endif + +static int sky81452_bl_init_device(struct regmap *regmap, + struct sky81452_bl_platform_data *pdata) +{ + unsigned int value; + + value = pdata->ignore_pwm ? SKY81452_IGPW : 0; + value |= pdata->dpwm_mode ? SKY81452_PWMMD : 0; + value |= pdata->phase_shift ? 0 : SKY81452_PHASE; + + if (pdata->boost_current_limit == 2300) + value |= SKY81452_ILIM; + else if (pdata->boost_current_limit != 2750) + return -EINVAL; + + if (pdata->short_detection_threshold < 4 || + pdata->short_detection_threshold > 7) + return -EINVAL; + value |= (7 - pdata->short_detection_threshold) << CTZ(SKY81452_VSHRT); + + return regmap_write(regmap, SKY81452_REG2, value); +} + +static int sky81452_bl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct regmap *regmap = dev_get_drvdata(dev->parent); + struct sky81452_bl_platform_data *pdata = dev_get_platdata(dev); + struct backlight_device *bd; + struct backlight_properties props; + const char *name; + int ret; + + if (!pdata) { + pdata = sky81452_bl_parse_dt(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + + if (gpio_is_valid(pdata->gpio_enable)) { + ret = devm_gpio_request_one(dev, pdata->gpio_enable, + GPIOF_OUT_INIT_HIGH, "sky81452-en"); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "failed to request GPIO. err=%d\n", ret); + return ret; + } + } + + ret = sky81452_bl_init_device(regmap, pdata); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "failed to initialize. err=%d\n", ret); + return ret; + } + + memset(&props, 0, sizeof(props)); + props.max_brightness = SKY81452_MAX_BRIGHTNESS, + name = pdata->name ? pdata->name : SKY81452_DEFAULT_NAME; + bd = devm_backlight_device_register(dev, name, dev, regmap, + &sky81452_bl_ops, &props); + if (IS_ERR(bd)) { + dev_err(dev, "failed to register. err=%ld\n", PTR_ERR(bd)); + return PTR_ERR(bd); + } + + platform_set_drvdata(pdev, bd); + + ret = sysfs_create_group(&bd->dev.kobj, &sky81452_bl_attr_group); + if (IS_ERR_VALUE(ret)) { + dev_err(dev, "failed to create attribute. err=%d\n", ret); + return ret; + } + + return ret; +} + +static int sky81452_bl_remove(struct platform_device *pdev) +{ + const struct sky81452_bl_platform_data *pdata = + dev_get_platdata(&pdev->dev); + struct backlight_device *bd = platform_get_drvdata(pdev); + + sysfs_remove_group(&bd->dev.kobj, &sky81452_bl_attr_group); + + bd->props.power = FB_BLANK_UNBLANK; + bd->props.brightness = 0; + backlight_update_status(bd); + + if (gpio_is_valid(pdata->gpio_enable)) + gpio_set_value_cansleep(pdata->gpio_enable, 0); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id sky81452_bl_of_match[] = { + { .compatible = "skyworks,sky81452-backlight", }, + { } +}; +MODULE_DEVICE_TABLE(of, sky81452_bl_of_match); +#endif + +static struct platform_driver sky81452_bl_driver = { + .driver = { + .name = "sky81452-backlight", + .of_match_table = of_match_ptr(sky81452_bl_of_match), + }, + .probe = sky81452_bl_probe, + .remove = sky81452_bl_remove, +}; + +module_platform_driver(sky81452_bl_driver); + +MODULE_DESCRIPTION("Skyworks SKY81452 backlight driver"); +MODULE_AUTHOR("Gyungoh Yoo "); +MODULE_LICENSE("GPL v2"); diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c index eb14e055ea83e8..ff1a5bac420098 100644 --- a/fs/9p/vfs_addr.c +++ b/fs/9p/vfs_addr.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/fs/Makefile b/fs/Makefile index a88ac4838c9e2b..cb92fd4c31729c 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -118,6 +118,7 @@ obj-$(CONFIG_HOSTFS) += hostfs/ obj-$(CONFIG_HPPFS) += hppfs/ obj-$(CONFIG_CACHEFILES) += cachefiles/ obj-$(CONFIG_DEBUG_FS) += debugfs/ +obj-$(CONFIG_TRACING) += tracefs/ obj-$(CONFIG_OCFS2_FS) += ocfs2/ obj-$(CONFIG_BTRFS_FS) += btrfs/ obj-$(CONFIG_GFS2_FS) += gfs2/ diff --git a/fs/affs/file.c b/fs/affs/file.c index a91795e01a7ff0..3aa7eb66547ea3 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -12,7 +12,7 @@ * affs regular file handling primitives */ -#include +#include #include "affs.h" static struct buffer_head *affs_get_extblock_slow(struct inode *inode, u32 ext); diff --git a/fs/afs/write.c b/fs/afs/write.c index c13cb08964eda9..0714abcd7f3232 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -14,7 +14,6 @@ #include #include #include -#include #include "internal.h" static int afs_write_back_from_locked_page(struct afs_writeback *wb, diff --git a/fs/aio.c b/fs/aio.c index a793f7023755dc..1ab60010cf6c75 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -151,6 +151,38 @@ struct kioctx { unsigned id; }; +/* + * We use ki_cancel == KIOCB_CANCELLED to indicate that a kiocb has been either + * cancelled or completed (this makes a certain amount of sense because + * successful cancellation - io_cancel() - does deliver the completion to + * userspace). + * + * And since most things don't implement kiocb cancellation and we'd really like + * kiocb completion to be lockless when possible, we use ki_cancel to + * synchronize cancellation and completion - we only set it to KIOCB_CANCELLED + * with xchg() or cmpxchg(), see batch_complete_aio() and kiocb_cancel(). + */ +#define KIOCB_CANCELLED ((void *) (~0ULL)) + +struct aio_kiocb { + struct kiocb common; + + struct kioctx *ki_ctx; + kiocb_cancel_fn *ki_cancel; + + struct iocb __user *ki_user_iocb; /* user's aiocb */ + __u64 ki_user_data; /* user's data for completion */ + + struct list_head ki_list; /* the aio core uses this + * for cancellation */ + + /* + * If the aio_resfd field of the userspace iocb is not zero, + * this is the underlying eventfd context to deliver events to. + */ + struct eventfd_ctx *ki_eventfd; +}; + /*------ sysctl variables----*/ static DEFINE_SPINLOCK(aio_nr_lock); unsigned long aio_nr; /* current system wide number of aio requests */ @@ -220,7 +252,7 @@ static int __init aio_setup(void) if (IS_ERR(aio_mnt)) panic("Failed to create aio fs mount."); - kiocb_cachep = KMEM_CACHE(kiocb, SLAB_HWCACHE_ALIGN|SLAB_PANIC); + kiocb_cachep = KMEM_CACHE(aio_kiocb, SLAB_HWCACHE_ALIGN|SLAB_PANIC); kioctx_cachep = KMEM_CACHE(kioctx,SLAB_HWCACHE_ALIGN|SLAB_PANIC); pr_debug("sizeof(struct page) = %zu\n", sizeof(struct page)); @@ -484,8 +516,9 @@ static int aio_setup_ring(struct kioctx *ctx) #define AIO_EVENTS_FIRST_PAGE ((PAGE_SIZE - sizeof(struct aio_ring)) / sizeof(struct io_event)) #define AIO_EVENTS_OFFSET (AIO_EVENTS_PER_PAGE - AIO_EVENTS_FIRST_PAGE) -void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel) +void kiocb_set_cancel_fn(struct kiocb *iocb, kiocb_cancel_fn *cancel) { + struct aio_kiocb *req = container_of(iocb, struct aio_kiocb, common); struct kioctx *ctx = req->ki_ctx; unsigned long flags; @@ -500,7 +533,7 @@ void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel) } EXPORT_SYMBOL(kiocb_set_cancel_fn); -static int kiocb_cancel(struct kiocb *kiocb) +static int kiocb_cancel(struct aio_kiocb *kiocb) { kiocb_cancel_fn *old, *cancel; @@ -518,7 +551,7 @@ static int kiocb_cancel(struct kiocb *kiocb) cancel = cmpxchg(&kiocb->ki_cancel, old, KIOCB_CANCELLED); } while (cancel != old); - return cancel(kiocb); + return cancel(&kiocb->common); } static void free_ioctx(struct work_struct *work) @@ -554,13 +587,13 @@ static void free_ioctx_reqs(struct percpu_ref *ref) static void free_ioctx_users(struct percpu_ref *ref) { struct kioctx *ctx = container_of(ref, struct kioctx, users); - struct kiocb *req; + struct aio_kiocb *req; spin_lock_irq(&ctx->ctx_lock); while (!list_empty(&ctx->active_reqs)) { req = list_first_entry(&ctx->active_reqs, - struct kiocb, ki_list); + struct aio_kiocb, ki_list); list_del_init(&req->ki_list); kiocb_cancel(req); @@ -786,22 +819,6 @@ static int kill_ioctx(struct mm_struct *mm, struct kioctx *ctx, return 0; } -/* wait_on_sync_kiocb: - * Waits on the given sync kiocb to complete. - */ -ssize_t wait_on_sync_kiocb(struct kiocb *req) -{ - while (!req->ki_ctx) { - set_current_state(TASK_UNINTERRUPTIBLE); - if (req->ki_ctx) - break; - io_schedule(); - } - __set_current_state(TASK_RUNNING); - return req->ki_user_data; -} -EXPORT_SYMBOL(wait_on_sync_kiocb); - /* * exit_aio: called when the last user of mm goes away. At this point, there is * no way for any new requests to be submited or any of the io_* syscalls to be @@ -956,9 +973,9 @@ static void user_refill_reqs_available(struct kioctx *ctx) * Allocate a slot for an aio request. * Returns NULL if no requests are free. */ -static inline struct kiocb *aio_get_req(struct kioctx *ctx) +static inline struct aio_kiocb *aio_get_req(struct kioctx *ctx) { - struct kiocb *req; + struct aio_kiocb *req; if (!get_reqs_available(ctx)) { user_refill_reqs_available(ctx); @@ -979,10 +996,10 @@ static inline struct kiocb *aio_get_req(struct kioctx *ctx) return NULL; } -static void kiocb_free(struct kiocb *req) +static void kiocb_free(struct aio_kiocb *req) { - if (req->ki_filp) - fput(req->ki_filp); + if (req->common.ki_filp) + fput(req->common.ki_filp); if (req->ki_eventfd != NULL) eventfd_ctx_put(req->ki_eventfd); kmem_cache_free(kiocb_cachep, req); @@ -1018,8 +1035,9 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id) /* aio_complete * Called when the io request on the given iocb is complete. */ -void aio_complete(struct kiocb *iocb, long res, long res2) +static void aio_complete(struct kiocb *kiocb, long res, long res2) { + struct aio_kiocb *iocb = container_of(kiocb, struct aio_kiocb, common); struct kioctx *ctx = iocb->ki_ctx; struct aio_ring *ring; struct io_event *ev_page, *event; @@ -1033,13 +1051,7 @@ void aio_complete(struct kiocb *iocb, long res, long res2) * ref, no other paths have a way to get another ref * - the sync task helpfully left a reference to itself in the iocb */ - if (is_sync_kiocb(iocb)) { - iocb->ki_user_data = res; - smp_wmb(); - iocb->ki_ctx = ERR_PTR(-EXDEV); - wake_up_process(iocb->ki_obj.tsk); - return; - } + BUG_ON(is_sync_kiocb(kiocb)); if (iocb->ki_list.next) { unsigned long flags; @@ -1065,7 +1077,7 @@ void aio_complete(struct kiocb *iocb, long res, long res2) ev_page = kmap_atomic(ctx->ring_pages[pos / AIO_EVENTS_PER_PAGE]); event = ev_page + pos % AIO_EVENTS_PER_PAGE; - event->obj = (u64)(unsigned long)iocb->ki_obj.user; + event->obj = (u64)(unsigned long)iocb->ki_user_iocb; event->data = iocb->ki_user_data; event->res = res; event->res2 = res2; @@ -1074,7 +1086,7 @@ void aio_complete(struct kiocb *iocb, long res, long res2) flush_dcache_page(ctx->ring_pages[pos / AIO_EVENTS_PER_PAGE]); pr_debug("%p[%u]: %p: %p %Lx %lx %lx\n", - ctx, tail, iocb, iocb->ki_obj.user, iocb->ki_user_data, + ctx, tail, iocb, iocb->ki_user_iocb, iocb->ki_user_data, res, res2); /* after flagging the request as done, we @@ -1121,7 +1133,6 @@ void aio_complete(struct kiocb *iocb, long res, long res2) percpu_ref_put(&ctx->reqs); } -EXPORT_SYMBOL(aio_complete); /* aio_read_events_ring * Pull an event off of the ioctx's event ring. Returns the number of @@ -1349,46 +1360,19 @@ typedef ssize_t (aio_rw_op)(struct kiocb *, const struct iovec *, unsigned long, loff_t); typedef ssize_t (rw_iter_op)(struct kiocb *, struct iov_iter *); -static ssize_t aio_setup_vectored_rw(struct kiocb *kiocb, - int rw, char __user *buf, - unsigned long *nr_segs, - struct iovec **iovec, - bool compat) +static int aio_setup_vectored_rw(int rw, char __user *buf, size_t len, + struct iovec **iovec, + bool compat, + struct iov_iter *iter) { - ssize_t ret; - - *nr_segs = kiocb->ki_nbytes; - #ifdef CONFIG_COMPAT if (compat) - ret = compat_rw_copy_check_uvector(rw, + return compat_import_iovec(rw, (struct compat_iovec __user *)buf, - *nr_segs, UIO_FASTIOV, *iovec, iovec); - else + len, UIO_FASTIOV, iovec, iter); #endif - ret = rw_copy_check_uvector(rw, - (struct iovec __user *)buf, - *nr_segs, UIO_FASTIOV, *iovec, iovec); - if (ret < 0) - return ret; - - /* ki_nbytes now reflect bytes instead of segs */ - kiocb->ki_nbytes = ret; - return 0; -} - -static ssize_t aio_setup_single_vector(struct kiocb *kiocb, - int rw, char __user *buf, - unsigned long *nr_segs, - struct iovec *iovec) -{ - if (unlikely(!access_ok(!rw, buf, kiocb->ki_nbytes))) - return -EFAULT; - - iovec->iov_base = buf; - iovec->iov_len = kiocb->ki_nbytes; - *nr_segs = 1; - return 0; + return import_iovec(rw, (struct iovec __user *)buf, + len, UIO_FASTIOV, iovec, iter); } /* @@ -1396,11 +1380,10 @@ static ssize_t aio_setup_single_vector(struct kiocb *kiocb, * Performs the initial checks and io submission. */ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, - char __user *buf, bool compat) + char __user *buf, size_t len, bool compat) { struct file *file = req->ki_filp; ssize_t ret; - unsigned long nr_segs; int rw; fmode_t mode; aio_rw_op *rw_op; @@ -1431,21 +1414,22 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, if (!rw_op && !iter_op) return -EINVAL; - ret = (opcode == IOCB_CMD_PREADV || - opcode == IOCB_CMD_PWRITEV) - ? aio_setup_vectored_rw(req, rw, buf, &nr_segs, - &iovec, compat) - : aio_setup_single_vector(req, rw, buf, &nr_segs, - iovec); + if (opcode == IOCB_CMD_PREADV || opcode == IOCB_CMD_PWRITEV) + ret = aio_setup_vectored_rw(rw, buf, len, + &iovec, compat, &iter); + else { + ret = import_single_range(rw, buf, len, iovec, &iter); + iovec = NULL; + } if (!ret) - ret = rw_verify_area(rw, file, &req->ki_pos, req->ki_nbytes); + ret = rw_verify_area(rw, file, &req->ki_pos, + iov_iter_count(&iter)); if (ret < 0) { - if (iovec != inline_vecs) - kfree(iovec); + kfree(iovec); return ret; } - req->ki_nbytes = ret; + len = ret; /* XXX: move/kill - rw_verify_area()? */ /* This matches the pread()/pwrite() logic */ @@ -1458,14 +1442,14 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, file_start_write(file); if (iter_op) { - iov_iter_init(&iter, rw, iovec, nr_segs, req->ki_nbytes); ret = iter_op(req, &iter); } else { - ret = rw_op(req, iovec, nr_segs, req->ki_pos); + ret = rw_op(req, iter.iov, iter.nr_segs, req->ki_pos); } if (rw == WRITE) file_end_write(file); + kfree(iovec); break; case IOCB_CMD_FDSYNC: @@ -1487,9 +1471,6 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, return -EINVAL; } - if (iovec != inline_vecs) - kfree(iovec); - if (ret != -EIOCBQUEUED) { /* * There's no easy way to restart the syscall since other AIO's @@ -1508,7 +1489,7 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, struct iocb *iocb, bool compat) { - struct kiocb *req; + struct aio_kiocb *req; ssize_t ret; /* enforce forwards compatibility on users */ @@ -1531,11 +1512,14 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, if (unlikely(!req)) return -EAGAIN; - req->ki_filp = fget(iocb->aio_fildes); - if (unlikely(!req->ki_filp)) { + req->common.ki_filp = fget(iocb->aio_fildes); + if (unlikely(!req->common.ki_filp)) { ret = -EBADF; goto out_put_req; } + req->common.ki_pos = iocb->aio_offset; + req->common.ki_complete = aio_complete; + req->common.ki_flags = 0; if (iocb->aio_flags & IOCB_FLAG_RESFD) { /* @@ -1550,6 +1534,8 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, req->ki_eventfd = NULL; goto out_put_req; } + + req->common.ki_flags |= IOCB_EVENTFD; } ret = put_user(KIOCB_KEY, &user_iocb->aio_key); @@ -1558,13 +1544,12 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, goto out_put_req; } - req->ki_obj.user = user_iocb; + req->ki_user_iocb = user_iocb; req->ki_user_data = iocb->aio_data; - req->ki_pos = iocb->aio_offset; - req->ki_nbytes = iocb->aio_nbytes; - ret = aio_run_iocb(req, iocb->aio_lio_opcode, + ret = aio_run_iocb(&req->common, iocb->aio_lio_opcode, (char __user *)(unsigned long)iocb->aio_buf, + iocb->aio_nbytes, compat); if (ret) goto out_put_req; @@ -1651,10 +1636,10 @@ SYSCALL_DEFINE3(io_submit, aio_context_t, ctx_id, long, nr, /* lookup_kiocb * Finds a given iocb for cancellation. */ -static struct kiocb *lookup_kiocb(struct kioctx *ctx, struct iocb __user *iocb, - u32 key) +static struct aio_kiocb * +lookup_kiocb(struct kioctx *ctx, struct iocb __user *iocb, u32 key) { - struct list_head *pos; + struct aio_kiocb *kiocb; assert_spin_locked(&ctx->ctx_lock); @@ -1662,9 +1647,8 @@ static struct kiocb *lookup_kiocb(struct kioctx *ctx, struct iocb __user *iocb, return NULL; /* TODO: use a hash or array, this sucks. */ - list_for_each(pos, &ctx->active_reqs) { - struct kiocb *kiocb = list_kiocb(pos); - if (kiocb->ki_obj.user == iocb) + list_for_each_entry(kiocb, &ctx->active_reqs, ki_list) { + if (kiocb->ki_user_iocb == iocb) return kiocb; } return NULL; @@ -1684,7 +1668,7 @@ SYSCALL_DEFINE3(io_cancel, aio_context_t, ctx_id, struct iocb __user *, iocb, struct io_event __user *, result) { struct kioctx *ctx; - struct kiocb *kiocb; + struct aio_kiocb *kiocb; u32 key; int ret; diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index 90bc079d998292..fdcb4d69f430db 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "bfs.h" diff --git a/fs/block_dev.c b/fs/block_dev.c index 975266be67d319..2e522aed6584d3 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include "internal.h" diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 30982bbd31c30c..aee18f84e3159c 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -32,6 +31,7 @@ #include #include #include +#include #include "ctree.h" #include "disk-io.h" #include "transaction.h" diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index d2e732d7af5246..686331f22b15ce 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -43,6 +42,7 @@ #include #include #include +#include #include "ctree.h" #include "disk-io.h" #include "transaction.h" diff --git a/fs/ceph/file.c b/fs/ceph/file.c index d533075a823d5e..139f2fea91a0fe 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include "super.h" @@ -808,7 +807,7 @@ static ssize_t ceph_read_iter(struct kiocb *iocb, struct iov_iter *to) { struct file *filp = iocb->ki_filp; struct ceph_file_info *fi = filp->private_data; - size_t len = iocb->ki_nbytes; + size_t len = iov_iter_count(to); struct inode *inode = file_inode(filp); struct ceph_inode_info *ci = ceph_inode(inode); struct page *pinned_page = NULL; diff --git a/fs/dcache.c b/fs/dcache.c index c71e3732e53bce..d99736a63e3cf6 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2690,7 +2690,7 @@ static int __d_unalias(struct inode *inode, struct dentry *dentry, struct dentry *alias) { struct mutex *m1 = NULL, *m2 = NULL; - int ret = -EBUSY; + int ret = -ESTALE; /* If alias and dentry share a parent, then no extra locks required */ if (alias->d_parent == dentry->d_parent) diff --git a/fs/direct-io.c b/fs/direct-io.c index e181b6b2e297fb..6fb00e3f105979 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -37,7 +37,6 @@ #include #include #include -#include /* * How many user pages to map in one call to get_user_pages(). This determines @@ -265,7 +264,7 @@ static ssize_t dio_complete(struct dio *dio, loff_t offset, ssize_t ret, ret = err; } - aio_complete(dio->iocb, ret, 0); + dio->iocb->ki_complete(dio->iocb, ret, 0); } kmem_cache_free(dio_cache, dio); @@ -1056,7 +1055,7 @@ static inline int drop_refcount(struct dio *dio) * operation. AIO can if it was a broken operation described above or * in fact if all the bios race to complete before we get here. In * that case dio_complete() translates the EIOCBQUEUED into the proper - * return code that the caller will hand to aio_complete(). + * return code that the caller will hand to ->complete(). * * This is managed by the bio_lock instead of being an atomic_t so that * completion paths can drop their ref and use the remaining count to diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index fd39bad6f1bdf8..79675089443df9 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -31,7 +31,6 @@ #include #include #include -#include #include "ecryptfs_kernel.h" /** @@ -52,12 +51,6 @@ static ssize_t ecryptfs_read_update_atime(struct kiocb *iocb, struct file *file = iocb->ki_filp; rc = generic_file_read_iter(iocb, to); - /* - * Even though this is a async interface, we need to wait - * for IO to finish to update atime - */ - if (-EIOCBQUEUED == rc) - rc = wait_on_sync_kiocb(iocb); if (rc >= 0) { path = ecryptfs_dentry_to_lower_path(file->f_path.dentry); touch_atime(path); diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 6434bc00012517..df9d6afbc5d5eb 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include "ext2.h" #include "acl.h" #include "xattr.h" diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 2c6ccc49ba279c..db07ffbe7c85cd 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include "ext3.h" #include "xattr.h" #include "acl.h" diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 33a09da16c9ce1..598abbbe678619 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -23,9 +23,9 @@ #include #include #include -#include #include #include +#include #include "ext4.h" #include "ext4_jbd2.h" #include "xattr.h" diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index 45fe924f82bce2..740c7871c11770 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c @@ -20,9 +20,9 @@ * (sct@redhat.com), 1993, 1998 */ -#include #include "ext4_jbd2.h" #include "truncate.h" +#include #include diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 5cb9a212b86f3e..a3f451370bef4b 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include "ext4_jbd2.h" diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c index b24a2541a9baaa..464984261e698a 100644 --- a/fs/ext4/page-io.c +++ b/fs/ext4/page-io.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 985ed023a75017..497f8515d20562 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -12,12 +12,12 @@ #include #include #include -#include #include #include #include #include #include +#include #include "f2fs.h" #include "node.h" diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 497c7c5263c7ca..8521207de22935 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index 28d0c7abba1c2f..b3fa0503223411 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -48,6 +47,7 @@ #include #include #include +#include #include "fuse_i.h" diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 39706c57ad3cb1..95a2797eef66d8 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -19,7 +19,6 @@ #include #include #include -#include MODULE_ALIAS_MISCDEV(FUSE_MINOR); MODULE_ALIAS("devname:fuse"); diff --git a/fs/fuse/file.c b/fs/fuse/file.c index c01ec3bdcfd810..ff102cbf16eab4 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -15,8 +15,8 @@ #include #include #include -#include #include +#include static const struct file_operations fuse_direct_io_file_operations; @@ -528,6 +528,17 @@ static void fuse_release_user_pages(struct fuse_req *req, int write) } } +static ssize_t fuse_get_res_by_io(struct fuse_io_priv *io) +{ + if (io->err) + return io->err; + + if (io->bytes >= 0 && io->write) + return -EIO; + + return io->bytes < 0 ? io->size : io->bytes; +} + /** * In case of short read, the caller sets 'pos' to the position of * actual end of fuse request in IO request. Otherwise, if bytes_requested @@ -546,6 +557,7 @@ static void fuse_release_user_pages(struct fuse_req *req, int write) */ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos) { + bool is_sync = is_sync_kiocb(io->iocb); int left; spin_lock(&io->lock); @@ -555,30 +567,24 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos) io->bytes = pos; left = --io->reqs; + if (!left && is_sync) + complete(io->done); spin_unlock(&io->lock); - if (!left) { - long res; + if (!left && !is_sync) { + ssize_t res = fuse_get_res_by_io(io); - if (io->err) - res = io->err; - else if (io->bytes >= 0 && io->write) - res = -EIO; - else { - res = io->bytes < 0 ? io->size : io->bytes; + if (res >= 0) { + struct inode *inode = file_inode(io->iocb->ki_filp); + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_inode *fi = get_fuse_inode(inode); - if (!is_sync_kiocb(io->iocb)) { - struct inode *inode = file_inode(io->iocb->ki_filp); - struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_inode *fi = get_fuse_inode(inode); - - spin_lock(&fc->lock); - fi->attr_version = ++fc->attr_version; - spin_unlock(&fc->lock); - } + spin_lock(&fc->lock); + fi->attr_version = ++fc->attr_version; + spin_unlock(&fc->lock); } - aio_complete(io->iocb, res, 0); + io->iocb->ki_complete(io->iocb, res, 0); kfree(io); } } @@ -2801,6 +2807,7 @@ static ssize_t fuse_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, loff_t offset) { + DECLARE_COMPLETION_ONSTACK(wait); ssize_t ret = 0; struct file *file = iocb->ki_filp; struct fuse_file *ff = file->private_data; @@ -2852,6 +2859,9 @@ fuse_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, if (!is_sync_kiocb(iocb) && (offset + count > i_size) && rw == WRITE) io->async = false; + if (io->async && is_sync_kiocb(iocb)) + io->done = &wait; + if (rw == WRITE) ret = __fuse_direct_write(io, iter, &pos); else @@ -2864,11 +2874,12 @@ fuse_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, if (!is_sync_kiocb(iocb)) return -EIOCBQUEUED; - ret = wait_on_sync_kiocb(iocb); - } else { - kfree(io); + wait_for_completion(&wait); + ret = fuse_get_res_by_io(io); } + kfree(io); + if (rw == WRITE) { if (ret > 0) fuse_write_update_size(inode, pos); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 1cdfb07c1376b4..7354dc142a5084 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -263,6 +263,7 @@ struct fuse_io_priv { int err; struct kiocb *iocb; struct file *file; + struct completion *done; }; /** diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c index 7b3143064af113..1be3b061c05c92 100644 --- a/fs/gfs2/acl.c +++ b/fs/gfs2/acl.c @@ -110,11 +110,7 @@ int gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type) error = __gfs2_xattr_set(inode, name, data, len, 0, GFS2_EATYPE_SYS); if (error) goto out; - - if (acl) - set_cached_acl(inode, type, acl); - else - forget_cached_acl(inode, type); + set_cached_acl(inode, type, acl); out: kfree(data); return error; diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 4ad4f94edebe25..a6e6990aea395e 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include "gfs2.h" @@ -671,12 +671,12 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping, if (alloc_required) { struct gfs2_alloc_parms ap = { .aflags = 0, }; - error = gfs2_quota_lock_check(ip); + requested = data_blocks + ind_blocks; + ap.target = requested; + error = gfs2_quota_lock_check(ip, &ap); if (error) goto out_unlock; - requested = data_blocks + ind_blocks; - ap.target = requested; error = gfs2_inplace_reserve(ip, &ap); if (error) goto out_qunlock; diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index f0b945ab853e48..61296ecbd0e20a 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1224,7 +1224,7 @@ static int do_grow(struct inode *inode, u64 size) if (gfs2_is_stuffed(ip) && (size > (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)))) { - error = gfs2_quota_lock_check(ip); + error = gfs2_quota_lock_check(ip, &ap); if (error) return error; diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 3e32bb8e2d7e57..8ec43ab5babf47 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include "gfs2.h" @@ -429,11 +428,11 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) if (ret) goto out_unlock; - ret = gfs2_quota_lock_check(ip); - if (ret) - goto out_unlock; gfs2_write_calc_reserv(ip, PAGE_CACHE_SIZE, &data_blocks, &ind_blocks); ap.target = data_blocks + ind_blocks; + ret = gfs2_quota_lock_check(ip, &ap); + if (ret) + goto out_unlock; ret = gfs2_inplace_reserve(ip, &ap); if (ret) goto out_quota_unlock; @@ -765,22 +764,30 @@ static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len, brelse(dibh); return error; } - -static void calc_max_reserv(struct gfs2_inode *ip, loff_t max, loff_t *len, - unsigned int *data_blocks, unsigned int *ind_blocks) +/** + * calc_max_reserv() - Reverse of write_calc_reserv. Given a number of + * blocks, determine how many bytes can be written. + * @ip: The inode in question. + * @len: Max cap of bytes. What we return in *len must be <= this. + * @data_blocks: Compute and return the number of data blocks needed + * @ind_blocks: Compute and return the number of indirect blocks needed + * @max_blocks: The total blocks available to work with. + * + * Returns: void, but @len, @data_blocks and @ind_blocks are filled in. + */ +static void calc_max_reserv(struct gfs2_inode *ip, loff_t *len, + unsigned int *data_blocks, unsigned int *ind_blocks, + unsigned int max_blocks) { + loff_t max = *len; const struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - unsigned int max_blocks = ip->i_rgd->rd_free_clone; unsigned int tmp, max_data = max_blocks - 3 * (sdp->sd_max_height - 1); for (tmp = max_data; tmp > sdp->sd_diptrs;) { tmp = DIV_ROUND_UP(tmp, sdp->sd_inptrs); max_data -= tmp; } - /* This calculation isn't the exact reverse of gfs2_write_calc_reserve, - so it might end up with fewer data blocks */ - if (max_data <= *data_blocks) - return; + *data_blocks = max_data; *ind_blocks = max_blocks - max_data; *len = ((loff_t)max_data - 3) << sdp->sd_sb.sb_bsize_shift; @@ -797,7 +804,7 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_alloc_parms ap = { .aflags = 0, }; unsigned int data_blocks = 0, ind_blocks = 0, rblocks; - loff_t bytes, max_bytes; + loff_t bytes, max_bytes, max_blks = UINT_MAX; int error; const loff_t pos = offset; const loff_t count = len; @@ -819,6 +826,9 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t gfs2_size_hint(file, offset, len); + gfs2_write_calc_reserv(ip, PAGE_SIZE, &data_blocks, &ind_blocks); + ap.min_target = data_blocks + ind_blocks; + while (len > 0) { if (len < bytes) bytes = len; @@ -827,27 +837,41 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t offset += bytes; continue; } - error = gfs2_quota_lock_check(ip); + + /* We need to determine how many bytes we can actually + * fallocate without exceeding quota or going over the + * end of the fs. We start off optimistically by assuming + * we can write max_bytes */ + max_bytes = (len > max_chunk_size) ? max_chunk_size : len; + + /* Since max_bytes is most likely a theoretical max, we + * calculate a more realistic 'bytes' to serve as a good + * starting point for the number of bytes we may be able + * to write */ + gfs2_write_calc_reserv(ip, bytes, &data_blocks, &ind_blocks); + ap.target = data_blocks + ind_blocks; + + error = gfs2_quota_lock_check(ip, &ap); if (error) return error; -retry: - gfs2_write_calc_reserv(ip, bytes, &data_blocks, &ind_blocks); + /* ap.allowed tells us how many blocks quota will allow + * us to write. Check if this reduces max_blks */ + if (ap.allowed && ap.allowed < max_blks) + max_blks = ap.allowed; - ap.target = data_blocks + ind_blocks; error = gfs2_inplace_reserve(ip, &ap); - if (error) { - if (error == -ENOSPC && bytes > sdp->sd_sb.sb_bsize) { - bytes >>= 1; - bytes &= bsize_mask; - if (bytes == 0) - bytes = sdp->sd_sb.sb_bsize; - goto retry; - } + if (error) goto out_qunlock; - } - max_bytes = bytes; - calc_max_reserv(ip, (len > max_chunk_size)? max_chunk_size: len, - &max_bytes, &data_blocks, &ind_blocks); + + /* check if the selected rgrp limits our max_blks further */ + if (ap.allowed && ap.allowed < max_blks) + max_blks = ap.allowed; + + /* Almost done. Calculate bytes that can be written using + * max_blks. We also recompute max_bytes, data_blocks and + * ind_blocks */ + calc_max_reserv(ip, &max_bytes, &data_blocks, + &ind_blocks, max_blks); rblocks = RES_DINODE + ind_blocks + RES_STATFS + RES_QUOTA + RES_RG_HDR + gfs2_rg_blocks(ip, data_blocks + ind_blocks); @@ -931,6 +955,22 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t le return ret; } +static ssize_t gfs2_file_splice_write(struct pipe_inode_info *pipe, + struct file *out, loff_t *ppos, + size_t len, unsigned int flags) +{ + int error; + struct gfs2_inode *ip = GFS2_I(out->f_mapping->host); + + error = gfs2_rs_alloc(ip); + if (error) + return (ssize_t)error; + + gfs2_size_hint(out, *ppos, len); + + return iter_file_splice_write(pipe, out, ppos, len, flags); +} + #ifdef CONFIG_GFS2_FS_LOCKING_DLM /** @@ -1077,7 +1117,7 @@ const struct file_operations gfs2_file_fops = { .lock = gfs2_lock, .flock = gfs2_flock, .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, + .splice_write = gfs2_file_splice_write, .setlease = simple_nosetlease, .fallocate = gfs2_fallocate, }; @@ -1107,7 +1147,7 @@ const struct file_operations gfs2_file_fops_nolock = { .release = gfs2_release, .fsync = gfs2_fsync, .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, + .splice_write = gfs2_file_splice_write, .setlease = generic_setlease, .fallocate = gfs2_fallocate, }; diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index f42dffba056ab0..0fa8062f85a7ab 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -2047,34 +2047,41 @@ static const struct file_operations gfs2_sbstats_fops = { int gfs2_create_debugfs_file(struct gfs2_sbd *sdp) { - sdp->debugfs_dir = debugfs_create_dir(sdp->sd_table_name, gfs2_root); - if (!sdp->debugfs_dir) - return -ENOMEM; - sdp->debugfs_dentry_glocks = debugfs_create_file("glocks", - S_IFREG | S_IRUGO, - sdp->debugfs_dir, sdp, - &gfs2_glocks_fops); - if (!sdp->debugfs_dentry_glocks) + struct dentry *dent; + + dent = debugfs_create_dir(sdp->sd_table_name, gfs2_root); + if (IS_ERR_OR_NULL(dent)) + goto fail; + sdp->debugfs_dir = dent; + + dent = debugfs_create_file("glocks", + S_IFREG | S_IRUGO, + sdp->debugfs_dir, sdp, + &gfs2_glocks_fops); + if (IS_ERR_OR_NULL(dent)) goto fail; + sdp->debugfs_dentry_glocks = dent; - sdp->debugfs_dentry_glstats = debugfs_create_file("glstats", - S_IFREG | S_IRUGO, - sdp->debugfs_dir, sdp, - &gfs2_glstats_fops); - if (!sdp->debugfs_dentry_glstats) + dent = debugfs_create_file("glstats", + S_IFREG | S_IRUGO, + sdp->debugfs_dir, sdp, + &gfs2_glstats_fops); + if (IS_ERR_OR_NULL(dent)) goto fail; + sdp->debugfs_dentry_glstats = dent; - sdp->debugfs_dentry_sbstats = debugfs_create_file("sbstats", - S_IFREG | S_IRUGO, - sdp->debugfs_dir, sdp, - &gfs2_sbstats_fops); - if (!sdp->debugfs_dentry_sbstats) + dent = debugfs_create_file("sbstats", + S_IFREG | S_IRUGO, + sdp->debugfs_dir, sdp, + &gfs2_sbstats_fops); + if (IS_ERR_OR_NULL(dent)) goto fail; + sdp->debugfs_dentry_sbstats = dent; return 0; fail: gfs2_delete_debugfs_file(sdp); - return -ENOMEM; + return dent ? PTR_ERR(dent) : -ENOMEM; } void gfs2_delete_debugfs_file(struct gfs2_sbd *sdp) @@ -2100,6 +2107,8 @@ void gfs2_delete_debugfs_file(struct gfs2_sbd *sdp) int gfs2_register_debugfs(void) { gfs2_root = debugfs_create_dir("gfs2", NULL); + if (IS_ERR(gfs2_root)) + return PTR_ERR(gfs2_root); return gfs2_root ? 0 : -ENOMEM; } diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 7a2dbbc0d6348e..58b75abf6ab2f4 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -301,8 +301,10 @@ struct gfs2_blkreserv { * to the allocation code. */ struct gfs2_alloc_parms { - u32 target; + u64 target; + u32 min_target; u32 aflags; + u64 allowed; }; enum { diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 73c72253faac07..08bc84d7e768e7 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -382,7 +382,7 @@ static int alloc_dinode(struct gfs2_inode *ip, u32 flags, unsigned *dblocks) struct gfs2_alloc_parms ap = { .target = *dblocks, .aflags = flags, }; int error; - error = gfs2_quota_lock_check(ip); + error = gfs2_quota_lock_check(ip, &ap); if (error) goto out; @@ -525,7 +525,7 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name, int error; if (da->nr_blocks) { - error = gfs2_quota_lock_check(dip); + error = gfs2_quota_lock_check(dip, &ap); if (error) goto fail_quota_locks; @@ -953,7 +953,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir, if (da.nr_blocks) { struct gfs2_alloc_parms ap = { .target = da.nr_blocks, }; - error = gfs2_quota_lock_check(dip); + error = gfs2_quota_lock_check(dip, &ap); if (error) goto out_gunlock; @@ -1470,7 +1470,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, if (da.nr_blocks) { struct gfs2_alloc_parms ap = { .target = da.nr_blocks, }; - error = gfs2_quota_lock_check(ndip); + error = gfs2_quota_lock_check(ndip, &ap); if (error) goto out_gunlock; @@ -1669,6 +1669,7 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) kuid_t ouid, nuid; kgid_t ogid, ngid; int error; + struct gfs2_alloc_parms ap; ouid = inode->i_uid; ogid = inode->i_gid; @@ -1696,9 +1697,11 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) if (error) goto out; + ap.target = gfs2_get_inode_blocks(&ip->i_inode); + if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) || !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) { - error = gfs2_quota_check(ip, nuid, ngid); + error = gfs2_quota_check(ip, nuid, ngid, &ap); if (error) goto out_gunlock_q; } @@ -1713,9 +1716,8 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) || !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) { - u64 blocks = gfs2_get_inode_blocks(&ip->i_inode); - gfs2_quota_change(ip, -blocks, ouid, ogid); - gfs2_quota_change(ip, blocks, nuid, ngid); + gfs2_quota_change(ip, -ap.target, ouid, ogid); + gfs2_quota_change(ip, ap.target, nuid, ngid); } out_end_trans: diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 3aa17d4d1cfcbf..5c27e48aa76f57 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -923,6 +923,9 @@ static int do_glock(struct gfs2_quota_data *qd, int force_refresh, if (error) return error; + if (test_and_clear_bit(QDF_REFRESH, &qd->qd_flags)) + force_refresh = FORCE; + qd->qd_qb = *(struct gfs2_quota_lvb *)qd->qd_gl->gl_lksb.sb_lvbptr; if (force_refresh || qd->qd_qb.qb_magic != cpu_to_be32(GFS2_MAGIC)) { @@ -974,11 +977,8 @@ int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) sizeof(struct gfs2_quota_data *), sort_qd, NULL); for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) { - int force = NO_FORCE; qd = ip->i_res->rs_qa_qd[x]; - if (test_and_clear_bit(QDF_REFRESH, &qd->qd_flags)) - force = FORCE; - error = do_glock(qd, force, &ip->i_res->rs_qa_qd_ghs[x]); + error = do_glock(qd, NO_FORCE, &ip->i_res->rs_qa_qd_ghs[x]); if (error) break; } @@ -1094,14 +1094,33 @@ static int print_message(struct gfs2_quota_data *qd, char *type) return 0; } -int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) +/** + * gfs2_quota_check - check if allocating new blocks will exceed quota + * @ip: The inode for which this check is being performed + * @uid: The uid to check against + * @gid: The gid to check against + * @ap: The allocation parameters. ap->target contains the requested + * blocks. ap->min_target, if set, contains the minimum blks + * requested. + * + * Returns: 0 on success. + * min_req = ap->min_target ? ap->min_target : ap->target; + * quota must allow atleast min_req blks for success and + * ap->allowed is set to the number of blocks allowed + * + * -EDQUOT otherwise, quota violation. ap->allowed is set to number + * of blocks available. + */ +int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid, + struct gfs2_alloc_parms *ap) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_quota_data *qd; - s64 value; + s64 value, warn, limit; unsigned int x; int error = 0; + ap->allowed = UINT_MAX; /* Assume we are permitted a whole lot */ if (!test_bit(GIF_QD_LOCKED, &ip->i_flags)) return 0; @@ -1115,30 +1134,37 @@ int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) qid_eq(qd->qd_id, make_kqid_gid(gid)))) continue; + warn = (s64)be64_to_cpu(qd->qd_qb.qb_warn); + limit = (s64)be64_to_cpu(qd->qd_qb.qb_limit); value = (s64)be64_to_cpu(qd->qd_qb.qb_value); spin_lock(&qd_lock); value += qd->qd_change; spin_unlock(&qd_lock); - if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) { - print_message(qd, "exceeded"); - quota_send_warning(qd->qd_id, - sdp->sd_vfs->s_dev, QUOTA_NL_BHARDWARN); - - error = -EDQUOT; - break; - } else if (be64_to_cpu(qd->qd_qb.qb_warn) && - (s64)be64_to_cpu(qd->qd_qb.qb_warn) < value && + if (limit > 0 && (limit - value) < ap->allowed) + ap->allowed = limit - value; + /* If we can't meet the target */ + if (limit && limit < (value + (s64)ap->target)) { + /* If no min_target specified or we don't meet + * min_target, return -EDQUOT */ + if (!ap->min_target || ap->min_target > ap->allowed) { + print_message(qd, "exceeded"); + quota_send_warning(qd->qd_id, + sdp->sd_vfs->s_dev, + QUOTA_NL_BHARDWARN); + error = -EDQUOT; + break; + } + } else if (warn && warn < value && time_after_eq(jiffies, qd->qd_last_warn + - gfs2_tune_get(sdp, - gt_quota_warn_period) * HZ)) { + gfs2_tune_get(sdp, gt_quota_warn_period) + * HZ)) { quota_send_warning(qd->qd_id, sdp->sd_vfs->s_dev, QUOTA_NL_BSOFTWARN); error = print_message(qd, "warning"); qd->qd_last_warn = jiffies; } } - return error; } diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h index 55d506eb3c4a31..ad04b3acae2b9d 100644 --- a/fs/gfs2/quota.h +++ b/fs/gfs2/quota.h @@ -24,7 +24,8 @@ extern void gfs2_quota_unhold(struct gfs2_inode *ip); extern int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid); extern void gfs2_quota_unlock(struct gfs2_inode *ip); -extern int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid); +extern int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid, + struct gfs2_alloc_parms *ap); extern void gfs2_quota_change(struct gfs2_inode *ip, s64 change, kuid_t uid, kgid_t gid); @@ -37,7 +38,8 @@ extern int gfs2_quotad(void *data); extern void gfs2_wake_up_statfs(struct gfs2_sbd *sdp); -static inline int gfs2_quota_lock_check(struct gfs2_inode *ip) +static inline int gfs2_quota_lock_check(struct gfs2_inode *ip, + struct gfs2_alloc_parms *ap) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); int ret; @@ -48,7 +50,7 @@ static inline int gfs2_quota_lock_check(struct gfs2_inode *ip) return ret; if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON) return 0; - ret = gfs2_quota_check(ip, ip->i_inode.i_uid, ip->i_inode.i_gid); + ret = gfs2_quota_check(ip, ip->i_inode.i_uid, ip->i_inode.i_gid, ap); if (ret) gfs2_quota_unlock(ip); return ret; diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 9150207f365cdf..6af2396a317c3d 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -1946,10 +1946,18 @@ static inline int fast_to_acquire(struct gfs2_rgrpd *rgd) * @ip: the inode to reserve space for * @ap: the allocation parameters * - * Returns: errno + * We try our best to find an rgrp that has at least ap->target blocks + * available. After a couple of passes (loops == 2), the prospects of finding + * such an rgrp diminish. At this stage, we return the first rgrp that has + * atleast ap->min_target blocks available. Either way, we set ap->allowed to + * the number of blocks available in the chosen rgrp. + * + * Returns: 0 on success, + * -ENOMEM if a suitable rgrp can't be found + * errno otherwise */ -int gfs2_inplace_reserve(struct gfs2_inode *ip, const struct gfs2_alloc_parms *ap) +int gfs2_inplace_reserve(struct gfs2_inode *ip, struct gfs2_alloc_parms *ap) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_rgrpd *begin = NULL; @@ -2012,7 +2020,7 @@ int gfs2_inplace_reserve(struct gfs2_inode *ip, const struct gfs2_alloc_parms *a /* Skip unuseable resource groups */ if ((rs->rs_rbm.rgd->rd_flags & (GFS2_RGF_NOALLOC | GFS2_RDF_ERROR)) || - (ap->target > rs->rs_rbm.rgd->rd_extfail_pt)) + (loops == 0 && ap->target > rs->rs_rbm.rgd->rd_extfail_pt)) goto skip_rgrp; if (sdp->sd_args.ar_rgrplvb) @@ -2027,11 +2035,13 @@ int gfs2_inplace_reserve(struct gfs2_inode *ip, const struct gfs2_alloc_parms *a goto check_rgrp; /* If rgrp has enough free space, use it */ - if (rs->rs_rbm.rgd->rd_free_clone >= ap->target) { + if (rs->rs_rbm.rgd->rd_free_clone >= ap->target || + (loops == 2 && ap->min_target && + rs->rs_rbm.rgd->rd_free_clone >= ap->min_target)) { ip->i_rgd = rs->rs_rbm.rgd; + ap->allowed = ip->i_rgd->rd_free_clone; return 0; } - check_rgrp: /* Check for unlinked inodes which can be reclaimed */ if (rs->rs_rbm.rgd->rd_flags & GFS2_RDF_CHECK) diff --git a/fs/gfs2/rgrp.h b/fs/gfs2/rgrp.h index b104f4af3afdb8..68972ecfbb0190 100644 --- a/fs/gfs2/rgrp.h +++ b/fs/gfs2/rgrp.h @@ -41,7 +41,8 @@ extern void gfs2_rgrp_go_unlock(struct gfs2_holder *gh); extern struct gfs2_alloc *gfs2_alloc_get(struct gfs2_inode *ip); #define GFS2_AF_ORLOV 1 -extern int gfs2_inplace_reserve(struct gfs2_inode *ip, const struct gfs2_alloc_parms *ap); +extern int gfs2_inplace_reserve(struct gfs2_inode *ip, + struct gfs2_alloc_parms *ap); extern void gfs2_inplace_release(struct gfs2_inode *ip); extern int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *n, diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c index 0b81f783f78724..fd260ce8869a86 100644 --- a/fs/gfs2/xattr.c +++ b/fs/gfs2/xattr.c @@ -732,7 +732,7 @@ static int ea_alloc_skeleton(struct gfs2_inode *ip, struct gfs2_ea_request *er, if (error) return error; - error = gfs2_quota_lock_check(ip); + error = gfs2_quota_lock_check(ip, &ap); if (error) return error; diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index d0929bc817826e..98d4ea45bb70aa 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include "hfs_fs.h" #include "btree.h" diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 0cf786f2d046f9..f541196d4ee910 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include "hfsplus_fs.h" #include "hfsplus_raw.h" diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index d72817ac51f646..762c7a3cf43d1b 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -195,7 +195,7 @@ static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_dat /* unchecked xdatum is chained with c->xattr_unchecked */ list_del_init(&xd->xindex); - dbg_xattr("success on verfying xdatum (xid=%u, version=%u)\n", + dbg_xattr("success on verifying xdatum (xid=%u, version=%u)\n", xd->xid, xd->version); return 0; diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index bd3df1ca3c9b7f..3197aed106148d 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -22,8 +22,8 @@ #include #include #include +#include #include -#include #include "jfs_incore.h" #include "jfs_inode.h" #include "jfs_filsys.h" diff --git a/fs/jfs/super.c b/fs/jfs/super.c index 5d30c56ae07506..4cd9798f4948c6 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -102,7 +102,7 @@ void jfs_error(struct super_block *sb, const char *fmt, ...) vaf.fmt = fmt; vaf.va = &args; - pr_err("ERROR: (device %s): %pf: %pV\n", + pr_err("ERROR: (device %s): %ps: %pV\n", sb->s_id, __builtin_return_address(0), &vaf); va_end(args); diff --git a/fs/namei.c b/fs/namei.c index c83145af4bfc0e..76fb76a0818bc2 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -119,15 +119,14 @@ * PATH_MAX includes the nul terminator --RR. */ -#define EMBEDDED_NAME_MAX (PATH_MAX - sizeof(struct filename)) +#define EMBEDDED_NAME_MAX (PATH_MAX - offsetof(struct filename, iname)) struct filename * getname_flags(const char __user *filename, int flags, int *empty) { - struct filename *result, *err; - int len; - long max; + struct filename *result; char *kname; + int len; result = audit_reusename(filename); if (result) @@ -136,22 +135,18 @@ getname_flags(const char __user *filename, int flags, int *empty) result = __getname(); if (unlikely(!result)) return ERR_PTR(-ENOMEM); - result->refcnt = 1; /* * First, try to embed the struct filename inside the names_cache * allocation */ - kname = (char *)result + sizeof(*result); + kname = (char *)result->iname; result->name = kname; - result->separate = false; - max = EMBEDDED_NAME_MAX; -recopy: - len = strncpy_from_user(kname, filename, max); + len = strncpy_from_user(kname, filename, EMBEDDED_NAME_MAX); if (unlikely(len < 0)) { - err = ERR_PTR(len); - goto error; + __putname(result); + return ERR_PTR(len); } /* @@ -160,43 +155,49 @@ getname_flags(const char __user *filename, int flags, int *empty) * names_cache allocation for the pathname, and re-do the copy from * userland. */ - if (len == EMBEDDED_NAME_MAX && max == EMBEDDED_NAME_MAX) { + if (unlikely(len == EMBEDDED_NAME_MAX)) { + const size_t size = offsetof(struct filename, iname[1]); kname = (char *)result; - result = kzalloc(sizeof(*result), GFP_KERNEL); - if (!result) { - err = ERR_PTR(-ENOMEM); - result = (struct filename *)kname; - goto error; + /* + * size is chosen that way we to guarantee that + * result->iname[0] is within the same object and that + * kname can't be equal to result->iname, no matter what. + */ + result = kzalloc(size, GFP_KERNEL); + if (unlikely(!result)) { + __putname(kname); + return ERR_PTR(-ENOMEM); } result->name = kname; - result->separate = true; - result->refcnt = 1; - max = PATH_MAX; - goto recopy; + len = strncpy_from_user(kname, filename, PATH_MAX); + if (unlikely(len < 0)) { + __putname(kname); + kfree(result); + return ERR_PTR(len); + } + if (unlikely(len == PATH_MAX)) { + __putname(kname); + kfree(result); + return ERR_PTR(-ENAMETOOLONG); + } } + result->refcnt = 1; /* The empty path is special. */ if (unlikely(!len)) { if (empty) *empty = 1; - err = ERR_PTR(-ENOENT); - if (!(flags & LOOKUP_EMPTY)) - goto error; + if (!(flags & LOOKUP_EMPTY)) { + putname(result); + return ERR_PTR(-ENOENT); + } } - err = ERR_PTR(-ENAMETOOLONG); - if (unlikely(len >= PATH_MAX)) - goto error; - result->uptr = filename; result->aname = NULL; audit_getname(result); return result; - -error: - putname(result); - return err; } struct filename * @@ -216,8 +217,7 @@ getname_kernel(const char * filename) return ERR_PTR(-ENOMEM); if (len <= EMBEDDED_NAME_MAX) { - result->name = (char *)(result) + sizeof(*result); - result->separate = false; + result->name = (char *)result->iname; } else if (len <= PATH_MAX) { struct filename *tmp; @@ -227,7 +227,6 @@ getname_kernel(const char * filename) return ERR_PTR(-ENOMEM); } tmp->name = (char *)result; - tmp->separate = true; result = tmp; } else { __putname(result); @@ -249,7 +248,7 @@ void putname(struct filename *name) if (--name->refcnt > 0) return; - if (name->separate) { + if (name->name != name->iname) { __putname(name->name); kfree(name); } else @@ -1851,10 +1850,11 @@ static int link_path_walk(const char *name, struct nameidata *nd) return err; } -static int path_init(int dfd, const char *name, unsigned int flags, +static int path_init(int dfd, const struct filename *name, unsigned int flags, struct nameidata *nd) { int retval = 0; + const char *s = name->name; nd->last_type = LAST_ROOT; /* if there are only slashes... */ nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT; @@ -1863,7 +1863,7 @@ static int path_init(int dfd, const char *name, unsigned int flags, if (flags & LOOKUP_ROOT) { struct dentry *root = nd->root.dentry; struct inode *inode = root->d_inode; - if (*name) { + if (*s) { if (!d_can_lookup(root)) return -ENOTDIR; retval = inode_permission(inode, MAY_EXEC); @@ -1885,7 +1885,7 @@ static int path_init(int dfd, const char *name, unsigned int flags, nd->root.mnt = NULL; nd->m_seq = read_seqbegin(&mount_lock); - if (*name=='/') { + if (*s == '/') { if (flags & LOOKUP_RCU) { rcu_read_lock(); nd->seq = set_root_rcu(nd); @@ -1919,7 +1919,7 @@ static int path_init(int dfd, const char *name, unsigned int flags, dentry = f.file->f_path.dentry; - if (*name) { + if (*s) { if (!d_can_lookup(dentry)) { fdput(f); return -ENOTDIR; @@ -1949,7 +1949,7 @@ static int path_init(int dfd, const char *name, unsigned int flags, return -ECHILD; done: current->total_link_count = 0; - return link_path_walk(name, nd); + return link_path_walk(s, nd); } static void path_cleanup(struct nameidata *nd) @@ -1972,7 +1972,7 @@ static inline int lookup_last(struct nameidata *nd, struct path *path) } /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ -static int path_lookupat(int dfd, const char *name, +static int path_lookupat(int dfd, const struct filename *name, unsigned int flags, struct nameidata *nd) { struct path path; @@ -2027,31 +2027,17 @@ static int path_lookupat(int dfd, const char *name, static int filename_lookup(int dfd, struct filename *name, unsigned int flags, struct nameidata *nd) { - int retval = path_lookupat(dfd, name->name, flags | LOOKUP_RCU, nd); + int retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd); if (unlikely(retval == -ECHILD)) - retval = path_lookupat(dfd, name->name, flags, nd); + retval = path_lookupat(dfd, name, flags, nd); if (unlikely(retval == -ESTALE)) - retval = path_lookupat(dfd, name->name, - flags | LOOKUP_REVAL, nd); + retval = path_lookupat(dfd, name, flags | LOOKUP_REVAL, nd); if (likely(!retval)) audit_inode(name, nd->path.dentry, flags & LOOKUP_PARENT); return retval; } -static int do_path_lookup(int dfd, const char *name, - unsigned int flags, struct nameidata *nd) -{ - struct filename *filename = getname_kernel(name); - int retval = PTR_ERR(filename); - - if (!IS_ERR(filename)) { - retval = filename_lookup(dfd, filename, flags, nd); - putname(filename); - } - return retval; -} - /* does lookup, returns the object with parent locked */ struct dentry *kern_path_locked(const char *name, struct path *path) { @@ -2089,9 +2075,15 @@ struct dentry *kern_path_locked(const char *name, struct path *path) int kern_path(const char *name, unsigned int flags, struct path *path) { struct nameidata nd; - int res = do_path_lookup(AT_FDCWD, name, flags, &nd); - if (!res) - *path = nd.path; + struct filename *filename = getname_kernel(name); + int res = PTR_ERR(filename); + + if (!IS_ERR(filename)) { + res = filename_lookup(AT_FDCWD, filename, flags, &nd); + putname(filename); + if (!res) + *path = nd.path; + } return res; } EXPORT_SYMBOL(kern_path); @@ -2108,15 +2100,22 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, const char *name, unsigned int flags, struct path *path) { - struct nameidata nd; - int err; - nd.root.dentry = dentry; - nd.root.mnt = mnt; + struct filename *filename = getname_kernel(name); + int err = PTR_ERR(filename); + BUG_ON(flags & LOOKUP_PARENT); - /* the first argument of do_path_lookup() is ignored with LOOKUP_ROOT */ - err = do_path_lookup(AT_FDCWD, name, flags | LOOKUP_ROOT, &nd); - if (!err) - *path = nd.path; + + /* the first argument of filename_lookup() is ignored with LOOKUP_ROOT */ + if (!IS_ERR(filename)) { + struct nameidata nd; + nd.root.dentry = dentry; + nd.root.mnt = mnt; + err = filename_lookup(AT_FDCWD, filename, + flags | LOOKUP_ROOT, &nd); + if (!err) + *path = nd.path; + putname(filename); + } return err; } EXPORT_SYMBOL(vfs_path_lookup); @@ -2138,9 +2137,7 @@ static struct dentry *lookup_hash(struct nameidata *nd) * @len: maximum length @len should be interpreted to * * Note that this routine is purely a helper for filesystem usage and should - * not be called by generic code. Also note that by using this function the - * nameidata argument is passed to the filesystem methods and a filesystem - * using this helper needs to be prepared for that. + * not be called by generic code. */ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) { @@ -2341,7 +2338,8 @@ mountpoint_last(struct nameidata *nd, struct path *path) * Returns 0 and "path" will be valid on success; Returns error otherwise. */ static int -path_mountpoint(int dfd, const char *name, struct path *path, unsigned int flags) +path_mountpoint(int dfd, const struct filename *name, struct path *path, + unsigned int flags) { struct nameidata nd; int err; @@ -2370,20 +2368,20 @@ path_mountpoint(int dfd, const char *name, struct path *path, unsigned int flags } static int -filename_mountpoint(int dfd, struct filename *s, struct path *path, +filename_mountpoint(int dfd, struct filename *name, struct path *path, unsigned int flags) { int error; - if (IS_ERR(s)) - return PTR_ERR(s); - error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_RCU); + if (IS_ERR(name)) + return PTR_ERR(name); + error = path_mountpoint(dfd, name, path, flags | LOOKUP_RCU); if (unlikely(error == -ECHILD)) - error = path_mountpoint(dfd, s->name, path, flags); + error = path_mountpoint(dfd, name, path, flags); if (unlikely(error == -ESTALE)) - error = path_mountpoint(dfd, s->name, path, flags | LOOKUP_REVAL); + error = path_mountpoint(dfd, name, path, flags | LOOKUP_REVAL); if (likely(!error)) - audit_inode(s, path->dentry, 0); - putname(s); + audit_inode(name, path->dentry, 0); + putname(name); return error; } @@ -3156,7 +3154,7 @@ static int do_tmpfile(int dfd, struct filename *pathname, static const struct qstr name = QSTR_INIT("/", 1); struct dentry *dentry, *child; struct inode *dir; - int error = path_lookupat(dfd, pathname->name, + int error = path_lookupat(dfd, pathname, flags | LOOKUP_DIRECTORY, nd); if (unlikely(error)) return error; @@ -3229,7 +3227,7 @@ static struct file *path_openat(int dfd, struct filename *pathname, goto out; } - error = path_init(dfd, pathname->name, flags, nd); + error = path_init(dfd, pathname, flags, nd); if (unlikely(error)) goto out; diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index e907c8cf732e3c..c3929fb2ab26c2 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -265,7 +265,7 @@ ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, loff_t return -EINVAL; #else - VM_BUG_ON(iocb->ki_nbytes != PAGE_SIZE); + VM_BUG_ON(iov_iter_count(iter) != PAGE_SIZE); if (rw == READ) return nfs_file_direct_read(iocb, iter, pos); @@ -393,7 +393,7 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq, bool write) long res = (long) dreq->error; if (!res) res = (long) dreq->count; - aio_complete(dreq->iocb, res, 0); + dreq->iocb->ki_complete(dreq->iocb, res, 0); } complete_all(&dreq->completion); diff --git a/fs/nfs/file.c b/fs/nfs/file.c index e679d24c39d3a5..37b15582e0de96 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 8b5969538f3922..ab4987bc637f8b 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include "nilfs.h" #include "btnode.h" #include "segment.h" diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile index 36ae529511c491..2ff263e6d363db 100644 --- a/fs/ntfs/Makefile +++ b/fs/ntfs/Makefile @@ -8,7 +8,7 @@ ntfs-y := aops.o attrib.o collate.o compress.o debug.o dir.o file.o \ ntfs-$(CONFIG_NTFS_RW) += bitmap.o lcnalloc.o logfile.o quota.o usnjrnl.o -ccflags-y := -DNTFS_VERSION=\"2.1.31\" +ccflags-y := -DNTFS_VERSION=\"2.1.32\" ccflags-$(CONFIG_NTFS_DEBUG) += -DDEBUG ccflags-$(CONFIG_NTFS_RW) += -DNTFS_RW diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index 1da9b2d184dc4e..c1da78dad1afb3 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -1,7 +1,7 @@ /* * file.c - NTFS kernel file operations. Part of the Linux-NTFS project. * - * Copyright (c) 2001-2014 Anton Altaparmakov and Tuxera Inc. + * Copyright (c) 2001-2015 Anton Altaparmakov and Tuxera Inc. * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -329,62 +328,168 @@ static int ntfs_attr_extend_initialized(ntfs_inode *ni, const s64 new_init_size) return err; } -/** - * ntfs_fault_in_pages_readable - - * - * Fault a number of userspace pages into pagetables. - * - * Unlike include/linux/pagemap.h::fault_in_pages_readable(), this one copes - * with more than two userspace pages as well as handling the single page case - * elegantly. - * - * If you find this difficult to understand, then think of the while loop being - * the following code, except that we do without the integer variable ret: - * - * do { - * ret = __get_user(c, uaddr); - * uaddr += PAGE_SIZE; - * } while (!ret && uaddr < end); - * - * Note, the final __get_user() may well run out-of-bounds of the user buffer, - * but _not_ out-of-bounds of the page the user buffer belongs to, and since - * this is only a read and not a write, and since it is still in the same page, - * it should not matter and this makes the code much simpler. - */ -static inline void ntfs_fault_in_pages_readable(const char __user *uaddr, - int bytes) +static ssize_t ntfs_prepare_file_for_write(struct file *file, loff_t *ppos, + size_t *count) { - const char __user *end; - volatile char c; - - /* Set @end to the first byte outside the last page we care about. */ - end = (const char __user*)PAGE_ALIGN((unsigned long)uaddr + bytes); - - while (!__get_user(c, uaddr) && (uaddr += PAGE_SIZE, uaddr < end)) - ; -} - -/** - * ntfs_fault_in_pages_readable_iovec - - * - * Same as ntfs_fault_in_pages_readable() but operates on an array of iovecs. - */ -static inline void ntfs_fault_in_pages_readable_iovec(const struct iovec *iov, - size_t iov_ofs, int bytes) -{ - do { - const char __user *buf; - unsigned len; + loff_t pos; + s64 end, ll; + ssize_t err; + unsigned long flags; + struct inode *vi = file_inode(file); + ntfs_inode *base_ni, *ni = NTFS_I(vi); + ntfs_volume *vol = ni->vol; - buf = iov->iov_base + iov_ofs; - len = iov->iov_len - iov_ofs; - if (len > bytes) - len = bytes; - ntfs_fault_in_pages_readable(buf, len); - bytes -= len; - iov++; - iov_ofs = 0; - } while (bytes); + ntfs_debug("Entering for i_ino 0x%lx, attribute type 0x%x, pos " + "0x%llx, count 0x%lx.", vi->i_ino, + (unsigned)le32_to_cpu(ni->type), + (unsigned long long)*ppos, (unsigned long)*count); + /* We can write back this queue in page reclaim. */ + current->backing_dev_info = inode_to_bdi(vi); + err = generic_write_checks(file, ppos, count, S_ISBLK(vi->i_mode)); + if (unlikely(err)) + goto out; + /* + * All checks have passed. Before we start doing any writing we want + * to abort any totally illegal writes. + */ + BUG_ON(NInoMstProtected(ni)); + BUG_ON(ni->type != AT_DATA); + /* If file is encrypted, deny access, just like NT4. */ + if (NInoEncrypted(ni)) { + /* Only $DATA attributes can be encrypted. */ + /* + * Reminder for later: Encrypted files are _always_ + * non-resident so that the content can always be encrypted. + */ + ntfs_debug("Denying write access to encrypted file."); + err = -EACCES; + goto out; + } + if (NInoCompressed(ni)) { + /* Only unnamed $DATA attribute can be compressed. */ + BUG_ON(ni->name_len); + /* + * Reminder for later: If resident, the data is not actually + * compressed. Only on the switch to non-resident does + * compression kick in. This is in contrast to encrypted files + * (see above). + */ + ntfs_error(vi->i_sb, "Writing to compressed files is not " + "implemented yet. Sorry."); + err = -EOPNOTSUPP; + goto out; + } + if (*count == 0) + goto out; + base_ni = ni; + if (NInoAttr(ni)) + base_ni = ni->ext.base_ntfs_ino; + err = file_remove_suid(file); + if (unlikely(err)) + goto out; + /* + * Our ->update_time method always succeeds thus file_update_time() + * cannot fail either so there is no need to check the return code. + */ + file_update_time(file); + pos = *ppos; + /* The first byte after the last cluster being written to. */ + end = (pos + *count + vol->cluster_size_mask) & + ~(u64)vol->cluster_size_mask; + /* + * If the write goes beyond the allocated size, extend the allocation + * to cover the whole of the write, rounded up to the nearest cluster. + */ + read_lock_irqsave(&ni->size_lock, flags); + ll = ni->allocated_size; + read_unlock_irqrestore(&ni->size_lock, flags); + if (end > ll) { + /* + * Extend the allocation without changing the data size. + * + * Note we ensure the allocation is big enough to at least + * write some data but we do not require the allocation to be + * complete, i.e. it may be partial. + */ + ll = ntfs_attr_extend_allocation(ni, end, -1, pos); + if (likely(ll >= 0)) { + BUG_ON(pos >= ll); + /* If the extension was partial truncate the write. */ + if (end > ll) { + ntfs_debug("Truncating write to inode 0x%lx, " + "attribute type 0x%x, because " + "the allocation was only " + "partially extended.", + vi->i_ino, (unsigned) + le32_to_cpu(ni->type)); + *count = ll - pos; + } + } else { + err = ll; + read_lock_irqsave(&ni->size_lock, flags); + ll = ni->allocated_size; + read_unlock_irqrestore(&ni->size_lock, flags); + /* Perform a partial write if possible or fail. */ + if (pos < ll) { + ntfs_debug("Truncating write to inode 0x%lx " + "attribute type 0x%x, because " + "extending the allocation " + "failed (error %d).", + vi->i_ino, (unsigned) + le32_to_cpu(ni->type), + (int)-err); + *count = ll - pos; + } else { + if (err != -ENOSPC) + ntfs_error(vi->i_sb, "Cannot perform " + "write to inode " + "0x%lx, attribute " + "type 0x%x, because " + "extending the " + "allocation failed " + "(error %ld).", + vi->i_ino, (unsigned) + le32_to_cpu(ni->type), + (long)-err); + else + ntfs_debug("Cannot perform write to " + "inode 0x%lx, " + "attribute type 0x%x, " + "because there is not " + "space left.", + vi->i_ino, (unsigned) + le32_to_cpu(ni->type)); + goto out; + } + } + } + /* + * If the write starts beyond the initialized size, extend it up to the + * beginning of the write and initialize all non-sparse space between + * the old initialized size and the new one. This automatically also + * increments the vfs inode->i_size to keep it above or equal to the + * initialized_size. + */ + read_lock_irqsave(&ni->size_lock, flags); + ll = ni->initialized_size; + read_unlock_irqrestore(&ni->size_lock, flags); + if (pos > ll) { + /* + * Wait for ongoing direct i/o to complete before proceeding. + * New direct i/o cannot start as we hold i_mutex. + */ + inode_dio_wait(vi); + err = ntfs_attr_extend_initialized(ni, pos); + if (unlikely(err < 0)) + ntfs_error(vi->i_sb, "Cannot perform write to inode " + "0x%lx, attribute type 0x%x, because " + "extending the initialized size " + "failed (error %d).", vi->i_ino, + (unsigned)le32_to_cpu(ni->type), + (int)-err); + } +out: + return err; } /** @@ -421,8 +526,8 @@ static inline int __ntfs_grab_cache_pages(struct address_space *mapping, goto err_out; } } - err = add_to_page_cache_lru(*cached_page, mapping, index, - GFP_KERNEL); + err = add_to_page_cache_lru(*cached_page, mapping, + index, GFP_KERNEL); if (unlikely(err)) { if (err == -EEXIST) continue; @@ -1268,180 +1373,6 @@ static int ntfs_prepare_pages_for_non_resident_write(struct page **pages, return err; } -/* - * Copy as much as we can into the pages and return the number of bytes which - * were successfully copied. If a fault is encountered then clear the pages - * out to (ofs + bytes) and return the number of bytes which were copied. - */ -static inline size_t ntfs_copy_from_user(struct page **pages, - unsigned nr_pages, unsigned ofs, const char __user *buf, - size_t bytes) -{ - struct page **last_page = pages + nr_pages; - char *addr; - size_t total = 0; - unsigned len; - int left; - - do { - len = PAGE_CACHE_SIZE - ofs; - if (len > bytes) - len = bytes; - addr = kmap_atomic(*pages); - left = __copy_from_user_inatomic(addr + ofs, buf, len); - kunmap_atomic(addr); - if (unlikely(left)) { - /* Do it the slow way. */ - addr = kmap(*pages); - left = __copy_from_user(addr + ofs, buf, len); - kunmap(*pages); - if (unlikely(left)) - goto err_out; - } - total += len; - bytes -= len; - if (!bytes) - break; - buf += len; - ofs = 0; - } while (++pages < last_page); -out: - return total; -err_out: - total += len - left; - /* Zero the rest of the target like __copy_from_user(). */ - while (++pages < last_page) { - bytes -= len; - if (!bytes) - break; - len = PAGE_CACHE_SIZE; - if (len > bytes) - len = bytes; - zero_user(*pages, 0, len); - } - goto out; -} - -static size_t __ntfs_copy_from_user_iovec_inatomic(char *vaddr, - const struct iovec *iov, size_t iov_ofs, size_t bytes) -{ - size_t total = 0; - - while (1) { - const char __user *buf = iov->iov_base + iov_ofs; - unsigned len; - size_t left; - - len = iov->iov_len - iov_ofs; - if (len > bytes) - len = bytes; - left = __copy_from_user_inatomic(vaddr, buf, len); - total += len; - bytes -= len; - vaddr += len; - if (unlikely(left)) { - total -= left; - break; - } - if (!bytes) - break; - iov++; - iov_ofs = 0; - } - return total; -} - -static inline void ntfs_set_next_iovec(const struct iovec **iovp, - size_t *iov_ofsp, size_t bytes) -{ - const struct iovec *iov = *iovp; - size_t iov_ofs = *iov_ofsp; - - while (bytes) { - unsigned len; - - len = iov->iov_len - iov_ofs; - if (len > bytes) - len = bytes; - bytes -= len; - iov_ofs += len; - if (iov->iov_len == iov_ofs) { - iov++; - iov_ofs = 0; - } - } - *iovp = iov; - *iov_ofsp = iov_ofs; -} - -/* - * This has the same side-effects and return value as ntfs_copy_from_user(). - * The difference is that on a fault we need to memset the remainder of the - * pages (out to offset + bytes), to emulate ntfs_copy_from_user()'s - * single-segment behaviour. - * - * We call the same helper (__ntfs_copy_from_user_iovec_inatomic()) both when - * atomic and when not atomic. This is ok because it calls - * __copy_from_user_inatomic() and it is ok to call this when non-atomic. In - * fact, the only difference between __copy_from_user_inatomic() and - * __copy_from_user() is that the latter calls might_sleep() and the former - * should not zero the tail of the buffer on error. And on many architectures - * __copy_from_user_inatomic() is just defined to __copy_from_user() so it - * makes no difference at all on those architectures. - */ -static inline size_t ntfs_copy_from_user_iovec(struct page **pages, - unsigned nr_pages, unsigned ofs, const struct iovec **iov, - size_t *iov_ofs, size_t bytes) -{ - struct page **last_page = pages + nr_pages; - char *addr; - size_t copied, len, total = 0; - - do { - len = PAGE_CACHE_SIZE - ofs; - if (len > bytes) - len = bytes; - addr = kmap_atomic(*pages); - copied = __ntfs_copy_from_user_iovec_inatomic(addr + ofs, - *iov, *iov_ofs, len); - kunmap_atomic(addr); - if (unlikely(copied != len)) { - /* Do it the slow way. */ - addr = kmap(*pages); - copied = __ntfs_copy_from_user_iovec_inatomic(addr + - ofs, *iov, *iov_ofs, len); - if (unlikely(copied != len)) - goto err_out; - kunmap(*pages); - } - total += len; - ntfs_set_next_iovec(iov, iov_ofs, len); - bytes -= len; - if (!bytes) - break; - ofs = 0; - } while (++pages < last_page); -out: - return total; -err_out: - BUG_ON(copied > len); - /* Zero the rest of the target like __copy_from_user(). */ - memset(addr + ofs + copied, 0, len - copied); - kunmap(*pages); - total += copied; - ntfs_set_next_iovec(iov, iov_ofs, copied); - while (++pages < last_page) { - bytes -= len; - if (!bytes) - break; - len = PAGE_CACHE_SIZE; - if (len > bytes) - len = bytes; - zero_user(*pages, 0, len); - } - goto out; -} - static inline void ntfs_flush_dcache_pages(struct page **pages, unsigned nr_pages) { @@ -1762,86 +1693,83 @@ static int ntfs_commit_pages_after_write(struct page **pages, return err; } -static void ntfs_write_failed(struct address_space *mapping, loff_t to) +/* + * Copy as much as we can into the pages and return the number of bytes which + * were successfully copied. If a fault is encountered then clear the pages + * out to (ofs + bytes) and return the number of bytes which were copied. + */ +static size_t ntfs_copy_from_user_iter(struct page **pages, unsigned nr_pages, + unsigned ofs, struct iov_iter *i, size_t bytes) { - struct inode *inode = mapping->host; + struct page **last_page = pages + nr_pages; + size_t total = 0; + struct iov_iter data = *i; + unsigned len, copied; - if (to > inode->i_size) { - truncate_pagecache(inode, inode->i_size); - ntfs_truncate_vfs(inode); - } + do { + len = PAGE_CACHE_SIZE - ofs; + if (len > bytes) + len = bytes; + copied = iov_iter_copy_from_user_atomic(*pages, &data, ofs, + len); + total += copied; + bytes -= copied; + if (!bytes) + break; + iov_iter_advance(&data, copied); + if (copied < len) + goto err; + ofs = 0; + } while (++pages < last_page); +out: + return total; +err: + /* Zero the rest of the target like __copy_from_user(). */ + len = PAGE_CACHE_SIZE - copied; + do { + if (len > bytes) + len = bytes; + zero_user(*pages, copied, len); + bytes -= len; + copied = 0; + len = PAGE_CACHE_SIZE; + } while (++pages < last_page); + goto out; } /** - * ntfs_file_buffered_write - - * - * Locking: The vfs is holding ->i_mutex on the inode. + * ntfs_perform_write - perform buffered write to a file + * @file: file to write to + * @i: iov_iter with data to write + * @pos: byte offset in file at which to begin writing to */ -static ssize_t ntfs_file_buffered_write(struct kiocb *iocb, - const struct iovec *iov, unsigned long nr_segs, - loff_t pos, loff_t *ppos, size_t count) +static ssize_t ntfs_perform_write(struct file *file, struct iov_iter *i, + loff_t pos) { - struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; struct inode *vi = mapping->host; ntfs_inode *ni = NTFS_I(vi); ntfs_volume *vol = ni->vol; struct page *pages[NTFS_MAX_PAGES_PER_CLUSTER]; struct page *cached_page = NULL; - char __user *buf = NULL; - s64 end, ll; VCN last_vcn; LCN lcn; - unsigned long flags; - size_t bytes, iov_ofs = 0; /* Offset in the current iovec. */ - ssize_t status, written; + size_t bytes; + ssize_t status, written = 0; unsigned nr_pages; - int err; - ntfs_debug("Entering for i_ino 0x%lx, attribute type 0x%x, " - "pos 0x%llx, count 0x%lx.", - vi->i_ino, (unsigned)le32_to_cpu(ni->type), - (unsigned long long)pos, (unsigned long)count); - if (unlikely(!count)) - return 0; - BUG_ON(NInoMstProtected(ni)); - /* - * If the attribute is not an index root and it is encrypted or - * compressed, we cannot write to it yet. Note we need to check for - * AT_INDEX_ALLOCATION since this is the type of both directory and - * index inodes. - */ - if (ni->type != AT_INDEX_ALLOCATION) { - /* If file is encrypted, deny access, just like NT4. */ - if (NInoEncrypted(ni)) { - /* - * Reminder for later: Encrypted files are _always_ - * non-resident so that the content can always be - * encrypted. - */ - ntfs_debug("Denying write access to encrypted file."); - return -EACCES; - } - if (NInoCompressed(ni)) { - /* Only unnamed $DATA attribute can be compressed. */ - BUG_ON(ni->type != AT_DATA); - BUG_ON(ni->name_len); - /* - * Reminder for later: If resident, the data is not - * actually compressed. Only on the switch to non- - * resident does compression kick in. This is in - * contrast to encrypted files (see above). - */ - ntfs_error(vi->i_sb, "Writing to compressed files is " - "not implemented yet. Sorry."); - return -EOPNOTSUPP; - } - } + ntfs_debug("Entering for i_ino 0x%lx, attribute type 0x%x, pos " + "0x%llx, count 0x%lx.", vi->i_ino, + (unsigned)le32_to_cpu(ni->type), + (unsigned long long)pos, + (unsigned long)iov_iter_count(i)); /* * If a previous ntfs_truncate() failed, repeat it and abort if it * fails again. */ if (unlikely(NInoTruncateFailed(ni))) { + int err; + inode_dio_wait(vi); err = ntfs_truncate(vi); if (err || NInoTruncateFailed(ni)) { @@ -1855,81 +1783,6 @@ static ssize_t ntfs_file_buffered_write(struct kiocb *iocb, return err; } } - /* The first byte after the write. */ - end = pos + count; - /* - * If the write goes beyond the allocated size, extend the allocation - * to cover the whole of the write, rounded up to the nearest cluster. - */ - read_lock_irqsave(&ni->size_lock, flags); - ll = ni->allocated_size; - read_unlock_irqrestore(&ni->size_lock, flags); - if (end > ll) { - /* Extend the allocation without changing the data size. */ - ll = ntfs_attr_extend_allocation(ni, end, -1, pos); - if (likely(ll >= 0)) { - BUG_ON(pos >= ll); - /* If the extension was partial truncate the write. */ - if (end > ll) { - ntfs_debug("Truncating write to inode 0x%lx, " - "attribute type 0x%x, because " - "the allocation was only " - "partially extended.", - vi->i_ino, (unsigned) - le32_to_cpu(ni->type)); - end = ll; - count = ll - pos; - } - } else { - err = ll; - read_lock_irqsave(&ni->size_lock, flags); - ll = ni->allocated_size; - read_unlock_irqrestore(&ni->size_lock, flags); - /* Perform a partial write if possible or fail. */ - if (pos < ll) { - ntfs_debug("Truncating write to inode 0x%lx, " - "attribute type 0x%x, because " - "extending the allocation " - "failed (error code %i).", - vi->i_ino, (unsigned) - le32_to_cpu(ni->type), err); - end = ll; - count = ll - pos; - } else { - ntfs_error(vol->sb, "Cannot perform write to " - "inode 0x%lx, attribute type " - "0x%x, because extending the " - "allocation failed (error " - "code %i).", vi->i_ino, - (unsigned) - le32_to_cpu(ni->type), err); - return err; - } - } - } - written = 0; - /* - * If the write starts beyond the initialized size, extend it up to the - * beginning of the write and initialize all non-sparse space between - * the old initialized size and the new one. This automatically also - * increments the vfs inode->i_size to keep it above or equal to the - * initialized_size. - */ - read_lock_irqsave(&ni->size_lock, flags); - ll = ni->initialized_size; - read_unlock_irqrestore(&ni->size_lock, flags); - if (pos > ll) { - err = ntfs_attr_extend_initialized(ni, pos); - if (err < 0) { - ntfs_error(vol->sb, "Cannot perform write to inode " - "0x%lx, attribute type 0x%x, because " - "extending the initialized size " - "failed (error code %i).", vi->i_ino, - (unsigned)le32_to_cpu(ni->type), err); - status = err; - goto err_out; - } - } /* * Determine the number of pages per cluster for non-resident * attributes. @@ -1937,10 +1790,7 @@ static ssize_t ntfs_file_buffered_write(struct kiocb *iocb, nr_pages = 1; if (vol->cluster_size > PAGE_CACHE_SIZE && NInoNonResident(ni)) nr_pages = vol->cluster_size >> PAGE_CACHE_SHIFT; - /* Finally, perform the actual write. */ last_vcn = -1; - if (likely(nr_segs == 1)) - buf = iov->iov_base; do { VCN vcn; pgoff_t idx, start_idx; @@ -1965,10 +1815,10 @@ static ssize_t ntfs_file_buffered_write(struct kiocb *iocb, vol->cluster_size_bits, false); up_read(&ni->runlist.lock); if (unlikely(lcn < LCN_HOLE)) { - status = -EIO; if (lcn == LCN_ENOMEM) status = -ENOMEM; - else + else { + status = -EIO; ntfs_error(vol->sb, "Cannot " "perform write to " "inode 0x%lx, " @@ -1977,6 +1827,7 @@ static ssize_t ntfs_file_buffered_write(struct kiocb *iocb, "is corrupt.", vi->i_ino, (unsigned) le32_to_cpu(ni->type)); + } break; } if (lcn == LCN_HOLE) { @@ -1989,8 +1840,9 @@ static ssize_t ntfs_file_buffered_write(struct kiocb *iocb, } } } - if (bytes > count) - bytes = count; + if (bytes > iov_iter_count(i)) + bytes = iov_iter_count(i); +again: /* * Bring in the user page(s) that we will copy from _first_. * Otherwise there is a nasty deadlock on copying from the same @@ -1999,10 +1851,10 @@ static ssize_t ntfs_file_buffered_write(struct kiocb *iocb, * pages being swapped out between us bringing them into memory * and doing the actual copying. */ - if (likely(nr_segs == 1)) - ntfs_fault_in_pages_readable(buf, bytes); - else - ntfs_fault_in_pages_readable_iovec(iov, iov_ofs, bytes); + if (unlikely(iov_iter_fault_in_multipages_readable(i, bytes))) { + status = -EFAULT; + break; + } /* Get and lock @do_pages starting at index @start_idx. */ status = __ntfs_grab_cache_pages(mapping, start_idx, do_pages, pages, &cached_page); @@ -2018,56 +1870,57 @@ static ssize_t ntfs_file_buffered_write(struct kiocb *iocb, status = ntfs_prepare_pages_for_non_resident_write( pages, do_pages, pos, bytes); if (unlikely(status)) { - loff_t i_size; - do { unlock_page(pages[--do_pages]); page_cache_release(pages[do_pages]); } while (do_pages); - /* - * The write preparation may have instantiated - * allocated space outside i_size. Trim this - * off again. We can ignore any errors in this - * case as we will just be waisting a bit of - * allocated space, which is not a disaster. - */ - i_size = i_size_read(vi); - if (pos + bytes > i_size) { - ntfs_write_failed(mapping, pos + bytes); - } break; } } u = (pos >> PAGE_CACHE_SHIFT) - pages[0]->index; - if (likely(nr_segs == 1)) { - copied = ntfs_copy_from_user(pages + u, do_pages - u, - ofs, buf, bytes); - buf += copied; - } else - copied = ntfs_copy_from_user_iovec(pages + u, - do_pages - u, ofs, &iov, &iov_ofs, - bytes); + copied = ntfs_copy_from_user_iter(pages + u, do_pages - u, ofs, + i, bytes); ntfs_flush_dcache_pages(pages + u, do_pages - u); - status = ntfs_commit_pages_after_write(pages, do_pages, pos, - bytes); - if (likely(!status)) { - written += copied; - count -= copied; - pos += copied; - if (unlikely(copied != bytes)) - status = -EFAULT; + status = 0; + if (likely(copied == bytes)) { + status = ntfs_commit_pages_after_write(pages, do_pages, + pos, bytes); + if (!status) + status = bytes; } do { unlock_page(pages[--do_pages]); page_cache_release(pages[do_pages]); } while (do_pages); - if (unlikely(status)) + if (unlikely(status < 0)) break; - balance_dirty_pages_ratelimited(mapping); + copied = status; cond_resched(); - } while (count); -err_out: - *ppos = pos; + if (unlikely(!copied)) { + size_t sc; + + /* + * We failed to copy anything. Fall back to single + * segment length write. + * + * This is needed to avoid possible livelock in the + * case that all segments in the iov cannot be copied + * at once without a pagefault. + */ + sc = iov_iter_single_seg_count(i); + if (bytes > sc) + bytes = sc; + goto again; + } + iov_iter_advance(i, copied); + pos += copied; + written += copied; + balance_dirty_pages_ratelimited(mapping); + if (fatal_signal_pending(current)) { + status = -EINTR; + break; + } + } while (iov_iter_count(i)); if (cached_page) page_cache_release(cached_page); ntfs_debug("Done. Returning %s (written 0x%lx, status %li).", @@ -2077,59 +1930,56 @@ static ssize_t ntfs_file_buffered_write(struct kiocb *iocb, } /** - * ntfs_file_aio_write_nolock - + * ntfs_file_write_iter_nolock - write data to a file + * @iocb: IO state structure (file, offset, etc.) + * @from: iov_iter with data to write + * + * Basically the same as __generic_file_write_iter() except that it ends + * up calling ntfs_perform_write() instead of generic_perform_write() and that + * O_DIRECT is not implemented. */ -static ssize_t ntfs_file_aio_write_nolock(struct kiocb *iocb, - const struct iovec *iov, unsigned long nr_segs, loff_t *ppos) +static ssize_t ntfs_file_write_iter_nolock(struct kiocb *iocb, + struct iov_iter *from) { struct file *file = iocb->ki_filp; - struct address_space *mapping = file->f_mapping; - struct inode *inode = mapping->host; - loff_t pos; - size_t count; /* after file limit checks */ - ssize_t written, err; + loff_t pos = iocb->ki_pos; + ssize_t written = 0; + ssize_t err; + size_t count = iov_iter_count(from); - count = iov_length(iov, nr_segs); - pos = *ppos; - /* We can write back this queue in page reclaim. */ - current->backing_dev_info = inode_to_bdi(inode); - written = 0; - err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode)); - if (err) - goto out; - if (!count) - goto out; - err = file_remove_suid(file); - if (err) - goto out; - err = file_update_time(file); - if (err) - goto out; - written = ntfs_file_buffered_write(iocb, iov, nr_segs, pos, ppos, - count); -out: + err = ntfs_prepare_file_for_write(file, &pos, &count); + if (count && !err) { + iov_iter_truncate(from, count); + written = ntfs_perform_write(file, from, pos); + if (likely(written >= 0)) + iocb->ki_pos = pos + written; + } current->backing_dev_info = NULL; return written ? written : err; } /** - * ntfs_file_aio_write - + * ntfs_file_write_iter - simple wrapper for ntfs_file_write_iter_nolock() + * @iocb: IO state structure + * @from: iov_iter with data to write + * + * Basically the same as generic_file_write_iter() except that it ends up + * calling ntfs_file_write_iter_nolock() instead of + * __generic_file_write_iter(). */ -static ssize_t ntfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) +static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; - struct address_space *mapping = file->f_mapping; - struct inode *inode = mapping->host; + struct inode *vi = file_inode(file); ssize_t ret; - BUG_ON(iocb->ki_pos != pos); - - mutex_lock(&inode->i_mutex); - ret = ntfs_file_aio_write_nolock(iocb, iov, nr_segs, &iocb->ki_pos); - mutex_unlock(&inode->i_mutex); + mutex_lock(&vi->i_mutex); + ret = ntfs_file_write_iter_nolock(iocb, from); + mutex_unlock(&vi->i_mutex); if (ret > 0) { - int err = generic_write_sync(file, iocb->ki_pos - ret, ret); + ssize_t err; + + err = generic_write_sync(file, iocb->ki_pos - ret, ret); if (err < 0) ret = err; } @@ -2197,37 +2047,17 @@ static int ntfs_file_fsync(struct file *filp, loff_t start, loff_t end, #endif /* NTFS_RW */ const struct file_operations ntfs_file_ops = { - .llseek = generic_file_llseek, /* Seek inside file. */ - .read = new_sync_read, /* Read from file. */ - .read_iter = generic_file_read_iter, /* Async read from file. */ + .llseek = generic_file_llseek, + .read = new_sync_read, + .read_iter = generic_file_read_iter, #ifdef NTFS_RW - .write = do_sync_write, /* Write to file. */ - .aio_write = ntfs_file_aio_write, /* Async write to file. */ - /*.release = ,*/ /* Last file is closed. See - fs/ext2/file.c:: - ext2_release_file() for - how to use this to discard - preallocated space for - write opened files. */ - .fsync = ntfs_file_fsync, /* Sync a file to disk. */ - /*.aio_fsync = ,*/ /* Sync all outstanding async - i/o operations on a - kiocb. */ + .write = new_sync_write, + .write_iter = ntfs_file_write_iter, + .fsync = ntfs_file_fsync, #endif /* NTFS_RW */ - /*.ioctl = ,*/ /* Perform function on the - mounted filesystem. */ - .mmap = generic_file_mmap, /* Mmap file. */ - .open = ntfs_file_open, /* Open file. */ - .splice_read = generic_file_splice_read /* Zero-copy data send with - the data source being on - the ntfs partition. We do - not need to care about the - data destination. */ - /*.sendpage = ,*/ /* Zero-copy data send with - the data destination being - on the ntfs partition. We - do not need to care about - the data source. */ + .mmap = generic_file_mmap, + .open = ntfs_file_open, + .splice_read = generic_file_splice_read, }; const struct inode_operations ntfs_file_inode_ops = { diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 898b9949d36357..1d0c21df0d805c 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -28,7 +28,6 @@ #include #include #include -#include #include "aops.h" #include "attrib.h" diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 1b0463a92b17cf..8d2bc840c28874 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -29,6 +29,7 @@ #include #include #include +#include #include diff --git a/fs/ocfs2/aops.h b/fs/ocfs2/aops.h index 6cae155d54df0d..dd59599b022d5a 100644 --- a/fs/ocfs2/aops.h +++ b/fs/ocfs2/aops.h @@ -22,7 +22,7 @@ #ifndef OCFS2_AOPS_H #define OCFS2_AOPS_H -#include +#include handle_t *ocfs2_start_walk_page_trans(struct inode *inode, struct page *page, diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index ba1790e52ff236..91f03ce981087c 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2280,7 +2280,7 @@ static ssize_t ocfs2_file_write_iter(struct kiocb *iocb, file->f_path.dentry->d_name.name, (unsigned int)from->nr_segs); /* GRRRRR */ - if (iocb->ki_nbytes == 0) + if (count == 0) return 0; appending = file->f_flags & O_APPEND ? 1 : 0; @@ -2330,8 +2330,7 @@ static ssize_t ocfs2_file_write_iter(struct kiocb *iocb, } can_do_direct = direct_io; - ret = ocfs2_prepare_inode_for_write(file, ppos, - iocb->ki_nbytes, appending, + ret = ocfs2_prepare_inode_for_write(file, ppos, count, appending, &can_do_direct, &has_refcount); if (ret < 0) { mlog_errno(ret); @@ -2339,8 +2338,7 @@ static ssize_t ocfs2_file_write_iter(struct kiocb *iocb, } if (direct_io && !is_sync_kiocb(iocb)) - unaligned_dio = ocfs2_is_io_unaligned(inode, iocb->ki_nbytes, - *ppos); + unaligned_dio = ocfs2_is_io_unaligned(inode, count, *ppos); /* * We can't complete the direct I/O as requested, fall back to diff --git a/fs/open.c b/fs/open.c index 33f9cbf2610b39..6a83c47d59040d 100644 --- a/fs/open.c +++ b/fs/open.c @@ -570,6 +570,7 @@ static int chown_common(struct path *path, uid_t user, gid_t group) uid = make_kuid(current_user_ns(), user); gid = make_kgid(current_user_ns(), group); +retry_deleg: newattrs.ia_valid = ATTR_CTIME; if (user != (uid_t) -1) { if (!uid_valid(uid)) @@ -586,7 +587,6 @@ static int chown_common(struct path *path, uid_t user, gid_t group) if (!S_ISDIR(inode->i_mode)) newattrs.ia_valid |= ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; -retry_deleg: mutex_lock(&inode->i_mutex); error = security_path_chown(path, uid, gid); if (!error) @@ -988,9 +988,6 @@ struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt, return ERR_PTR(err); if (flags & O_CREAT) return ERR_PTR(-EINVAL); - if (!filename && (flags & O_DIRECTORY)) - if (!dentry->d_inode->i_op->lookup) - return ERR_PTR(-ENOTDIR); return do_file_open_root(dentry, mnt, filename, &op); } EXPORT_SYMBOL(file_open_root); diff --git a/fs/pipe.c b/fs/pipe.c index 21981e58e2a634..2d084f2d0b83c6 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 39d1373128e94a..44a549beeafa10 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -539,6 +539,9 @@ static int ramoops_probe(struct platform_device *pdev) mem_address = pdata->mem_address; record_size = pdata->record_size; dump_oops = pdata->dump_oops; + ramoops_console_size = pdata->console_size; + ramoops_pmsg_size = pdata->pmsg_size; + ramoops_ftrace_size = pdata->ftrace_size; pr_info("attached 0x%lx@0x%llx, ecc: %d/%d\n", cxt->size, (unsigned long long)cxt->phys_addr, diff --git a/fs/read_write.c b/fs/read_write.c index 8e1b68786d663d..69128b3786469b 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -343,13 +342,10 @@ ssize_t vfs_iter_read(struct file *file, struct iov_iter *iter, loff_t *ppos) init_sync_kiocb(&kiocb, file); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = iov_iter_count(iter); iter->type |= READ; ret = file->f_op->read_iter(&kiocb, iter); - if (ret == -EIOCBQUEUED) - ret = wait_on_sync_kiocb(&kiocb); - + BUG_ON(ret == -EIOCBQUEUED); if (ret > 0) *ppos = kiocb.ki_pos; return ret; @@ -366,13 +362,10 @@ ssize_t vfs_iter_write(struct file *file, struct iov_iter *iter, loff_t *ppos) init_sync_kiocb(&kiocb, file); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = iov_iter_count(iter); iter->type |= WRITE; ret = file->f_op->write_iter(&kiocb, iter); - if (ret == -EIOCBQUEUED) - ret = wait_on_sync_kiocb(&kiocb); - + BUG_ON(ret == -EIOCBQUEUED); if (ret > 0) *ppos = kiocb.ki_pos; return ret; @@ -426,11 +419,9 @@ ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *pp init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = len; ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&kiocb); + BUG_ON(ret == -EIOCBQUEUED); *ppos = kiocb.ki_pos; return ret; } @@ -446,12 +437,10 @@ ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *p init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = len; iov_iter_init(&iter, READ, &iov, 1, len); ret = filp->f_op->read_iter(&kiocb, &iter); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&kiocb); + BUG_ON(ret == -EIOCBQUEUED); *ppos = kiocb.ki_pos; return ret; } @@ -510,11 +499,9 @@ ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, lof init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = len; ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&kiocb); + BUG_ON(ret == -EIOCBQUEUED); *ppos = kiocb.ki_pos; return ret; } @@ -530,12 +517,10 @@ ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, lo init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = len; iov_iter_init(&iter, WRITE, &iov, 1, len); ret = filp->f_op->write_iter(&kiocb, &iter); - if (-EIOCBQUEUED == ret) - ret = wait_on_sync_kiocb(&kiocb); + BUG_ON(ret == -EIOCBQUEUED); *ppos = kiocb.ki_pos; return ret; } @@ -710,60 +695,47 @@ unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to) } EXPORT_SYMBOL(iov_shorten); -static ssize_t do_iter_readv_writev(struct file *filp, int rw, const struct iovec *iov, - unsigned long nr_segs, size_t len, loff_t *ppos, iter_fn_t fn) +static ssize_t do_iter_readv_writev(struct file *filp, struct iov_iter *iter, + loff_t *ppos, iter_fn_t fn) { struct kiocb kiocb; - struct iov_iter iter; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = len; - iov_iter_init(&iter, rw, iov, nr_segs, len); - ret = fn(&kiocb, &iter); - if (ret == -EIOCBQUEUED) - ret = wait_on_sync_kiocb(&kiocb); + ret = fn(&kiocb, iter); + BUG_ON(ret == -EIOCBQUEUED); *ppos = kiocb.ki_pos; return ret; } -static ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov, - unsigned long nr_segs, size_t len, loff_t *ppos, iov_fn_t fn) +static ssize_t do_sync_readv_writev(struct file *filp, struct iov_iter *iter, + loff_t *ppos, iov_fn_t fn) { struct kiocb kiocb; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_nbytes = len; - ret = fn(&kiocb, iov, nr_segs, kiocb.ki_pos); - if (ret == -EIOCBQUEUED) - ret = wait_on_sync_kiocb(&kiocb); + ret = fn(&kiocb, iter->iov, iter->nr_segs, kiocb.ki_pos); + BUG_ON(ret == -EIOCBQUEUED); *ppos = kiocb.ki_pos; return ret; } /* Do it by hand, with file-ops */ -static ssize_t do_loop_readv_writev(struct file *filp, struct iovec *iov, - unsigned long nr_segs, loff_t *ppos, io_fn_t fn) +static ssize_t do_loop_readv_writev(struct file *filp, struct iov_iter *iter, + loff_t *ppos, io_fn_t fn) { - struct iovec *vector = iov; ssize_t ret = 0; - while (nr_segs > 0) { - void __user *base; - size_t len; + while (iov_iter_count(iter)) { + struct iovec iovec = iov_iter_iovec(iter); ssize_t nr; - base = vector->iov_base; - len = vector->iov_len; - vector++; - nr_segs--; - - nr = fn(filp, base, len, ppos); + nr = fn(filp, iovec.iov_base, iovec.iov_len, ppos); if (nr < 0) { if (!ret) @@ -771,8 +743,9 @@ static ssize_t do_loop_readv_writev(struct file *filp, struct iovec *iov, break; } ret += nr; - if (nr != len) + if (nr != iovec.iov_len) break; + iov_iter_advance(iter, nr); } return ret; @@ -863,17 +836,20 @@ static ssize_t do_readv_writev(int type, struct file *file, size_t tot_len; struct iovec iovstack[UIO_FASTIOV]; struct iovec *iov = iovstack; + struct iov_iter iter; ssize_t ret; io_fn_t fn; iov_fn_t fnv; iter_fn_t iter_fn; - ret = rw_copy_check_uvector(type, uvector, nr_segs, - ARRAY_SIZE(iovstack), iovstack, &iov); - if (ret <= 0) - goto out; + ret = import_iovec(type, uvector, nr_segs, + ARRAY_SIZE(iovstack), &iov, &iter); + if (ret < 0) + return ret; - tot_len = ret; + tot_len = iov_iter_count(&iter); + if (!tot_len) + goto out; ret = rw_verify_area(type, file, pos, tot_len); if (ret < 0) goto out; @@ -891,20 +867,17 @@ static ssize_t do_readv_writev(int type, struct file *file, } if (iter_fn) - ret = do_iter_readv_writev(file, type, iov, nr_segs, tot_len, - pos, iter_fn); + ret = do_iter_readv_writev(file, &iter, pos, iter_fn); else if (fnv) - ret = do_sync_readv_writev(file, iov, nr_segs, tot_len, - pos, fnv); + ret = do_sync_readv_writev(file, &iter, pos, fnv); else - ret = do_loop_readv_writev(file, iov, nr_segs, pos, fn); + ret = do_loop_readv_writev(file, &iter, pos, fn); if (type != READ) file_end_write(file); out: - if (iov != iovstack) - kfree(iov); + kfree(iov); if ((ret + (type == READ)) > 0) { if (type == READ) fsnotify_access(file); @@ -1043,17 +1016,20 @@ static ssize_t compat_do_readv_writev(int type, struct file *file, compat_ssize_t tot_len; struct iovec iovstack[UIO_FASTIOV]; struct iovec *iov = iovstack; + struct iov_iter iter; ssize_t ret; io_fn_t fn; iov_fn_t fnv; iter_fn_t iter_fn; - ret = compat_rw_copy_check_uvector(type, uvector, nr_segs, - UIO_FASTIOV, iovstack, &iov); - if (ret <= 0) - goto out; + ret = compat_import_iovec(type, uvector, nr_segs, + UIO_FASTIOV, &iov, &iter); + if (ret < 0) + return ret; - tot_len = ret; + tot_len = iov_iter_count(&iter); + if (!tot_len) + goto out; ret = rw_verify_area(type, file, pos, tot_len); if (ret < 0) goto out; @@ -1071,20 +1047,17 @@ static ssize_t compat_do_readv_writev(int type, struct file *file, } if (iter_fn) - ret = do_iter_readv_writev(file, type, iov, nr_segs, tot_len, - pos, iter_fn); + ret = do_iter_readv_writev(file, &iter, pos, iter_fn); else if (fnv) - ret = do_sync_readv_writev(file, iov, nr_segs, tot_len, - pos, fnv); + ret = do_sync_readv_writev(file, &iter, pos, fnv); else - ret = do_loop_readv_writev(file, iov, nr_segs, pos, fn); + ret = do_loop_readv_writev(file, &iter, pos, fn); if (type != READ) file_end_write(file); out: - if (iov != iovstack) - kfree(iov); + kfree(iov); if ((ret + (type == READ)) > 0) { if (type == READ) fsnotify_access(file); diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index e72401e1f99562..9312b7842e036f 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include int reiserfs_commit_write(struct file *f, struct page *page, unsigned from, unsigned to); diff --git a/fs/splice.c b/fs/splice.c index 7968da96bebbb5..41cbb16299e094 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -32,7 +32,6 @@ #include #include #include -#include #include "internal.h" /* @@ -1534,34 +1533,29 @@ static long vmsplice_to_user(struct file *file, const struct iovec __user *uiov, struct iovec iovstack[UIO_FASTIOV]; struct iovec *iov = iovstack; struct iov_iter iter; - ssize_t count; pipe = get_pipe_info(file); if (!pipe) return -EBADF; - ret = rw_copy_check_uvector(READ, uiov, nr_segs, - ARRAY_SIZE(iovstack), iovstack, &iov); - if (ret <= 0) - goto out; - - count = ret; - iov_iter_init(&iter, READ, iov, nr_segs, count); + ret = import_iovec(READ, uiov, nr_segs, + ARRAY_SIZE(iovstack), &iov, &iter); + if (ret < 0) + return ret; + sd.total_len = iov_iter_count(&iter); sd.len = 0; - sd.total_len = count; sd.flags = flags; sd.u.data = &iter; sd.pos = 0; - pipe_lock(pipe); - ret = __splice_from_pipe(pipe, &sd, pipe_to_user); - pipe_unlock(pipe); - -out: - if (iov != iovstack) - kfree(iov); + if (sd.total_len) { + pipe_lock(pipe); + ret = __splice_from_pipe(pipe, &sd, pipe_to_user); + pipe_unlock(pipe); + } + kfree(iov); return ret; } diff --git a/fs/stat.c b/fs/stat.c index ae0c3cef9927e6..19636af5e75cc1 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -66,7 +66,7 @@ int vfs_getattr(struct path *path, struct kstat *stat) { int retval; - retval = security_inode_getattr(path->mnt, path->dentry); + retval = security_inode_getattr(path); if (retval) return retval; return vfs_getattr_nosec(path, stat); diff --git a/fs/tracefs/Makefile b/fs/tracefs/Makefile new file mode 100644 index 00000000000000..82fa35b656c489 --- /dev/null +++ b/fs/tracefs/Makefile @@ -0,0 +1,4 @@ +tracefs-objs := inode.o + +obj-$(CONFIG_TRACING) += tracefs.o + diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c new file mode 100644 index 00000000000000..d92bdf3b079a79 --- /dev/null +++ b/fs/tracefs/inode.c @@ -0,0 +1,650 @@ +/* + * inode.c - part of tracefs, a pseudo file system for activating tracing + * + * Based on debugfs by: Greg Kroah-Hartman + * + * Copyright (C) 2014 Red Hat Inc, author: Steven Rostedt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * tracefs is the file system that is used by the tracing infrastructure. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TRACEFS_DEFAULT_MODE 0700 + +static struct vfsmount *tracefs_mount; +static int tracefs_mount_count; +static bool tracefs_registered; + +static ssize_t default_read_file(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + return 0; +} + +static ssize_t default_write_file(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return count; +} + +static const struct file_operations tracefs_file_operations = { + .read = default_read_file, + .write = default_write_file, + .open = simple_open, + .llseek = noop_llseek, +}; + +static struct tracefs_dir_ops { + int (*mkdir)(const char *name); + int (*rmdir)(const char *name); +} tracefs_ops; + +static char *get_dname(struct dentry *dentry) +{ + const char *dname; + char *name; + int len = dentry->d_name.len; + + dname = dentry->d_name.name; + name = kmalloc(len + 1, GFP_KERNEL); + if (!name) + return NULL; + memcpy(name, dname, len); + name[len] = 0; + return name; +} + +static int tracefs_syscall_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode) +{ + char *name; + int ret; + + name = get_dname(dentry); + if (!name) + return -ENOMEM; + + /* + * The mkdir call can call the generic functions that create + * the files within the tracefs system. It is up to the individual + * mkdir routine to handle races. + */ + mutex_unlock(&inode->i_mutex); + ret = tracefs_ops.mkdir(name); + mutex_lock(&inode->i_mutex); + + kfree(name); + + return ret; +} + +static int tracefs_syscall_rmdir(struct inode *inode, struct dentry *dentry) +{ + char *name; + int ret; + + name = get_dname(dentry); + if (!name) + return -ENOMEM; + + /* + * The rmdir call can call the generic functions that create + * the files within the tracefs system. It is up to the individual + * rmdir routine to handle races. + * This time we need to unlock not only the parent (inode) but + * also the directory that is being deleted. + */ + mutex_unlock(&inode->i_mutex); + mutex_unlock(&dentry->d_inode->i_mutex); + + ret = tracefs_ops.rmdir(name); + + mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT); + mutex_lock(&dentry->d_inode->i_mutex); + + kfree(name); + + return ret; +} + +static const struct inode_operations tracefs_dir_inode_operations = { + .lookup = simple_lookup, + .mkdir = tracefs_syscall_mkdir, + .rmdir = tracefs_syscall_rmdir, +}; + +static struct inode *tracefs_get_inode(struct super_block *sb) +{ + struct inode *inode = new_inode(sb); + if (inode) { + inode->i_ino = get_next_ino(); + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + } + return inode; +} + +struct tracefs_mount_opts { + kuid_t uid; + kgid_t gid; + umode_t mode; +}; + +enum { + Opt_uid, + Opt_gid, + Opt_mode, + Opt_err +}; + +static const match_table_t tokens = { + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_mode, "mode=%o"}, + {Opt_err, NULL} +}; + +struct tracefs_fs_info { + struct tracefs_mount_opts mount_opts; +}; + +static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts) +{ + substring_t args[MAX_OPT_ARGS]; + int option; + int token; + kuid_t uid; + kgid_t gid; + char *p; + + opts->mode = TRACEFS_DEFAULT_MODE; + + while ((p = strsep(&data, ",")) != NULL) { + if (!*p) + continue; + + token = match_token(p, tokens, args); + switch (token) { + case Opt_uid: + if (match_int(&args[0], &option)) + return -EINVAL; + uid = make_kuid(current_user_ns(), option); + if (!uid_valid(uid)) + return -EINVAL; + opts->uid = uid; + break; + case Opt_gid: + if (match_int(&args[0], &option)) + return -EINVAL; + gid = make_kgid(current_user_ns(), option); + if (!gid_valid(gid)) + return -EINVAL; + opts->gid = gid; + break; + case Opt_mode: + if (match_octal(&args[0], &option)) + return -EINVAL; + opts->mode = option & S_IALLUGO; + break; + /* + * We might like to report bad mount options here; + * but traditionally tracefs has ignored all mount options + */ + } + } + + return 0; +} + +static int tracefs_apply_options(struct super_block *sb) +{ + struct tracefs_fs_info *fsi = sb->s_fs_info; + struct inode *inode = sb->s_root->d_inode; + struct tracefs_mount_opts *opts = &fsi->mount_opts; + + inode->i_mode &= ~S_IALLUGO; + inode->i_mode |= opts->mode; + + inode->i_uid = opts->uid; + inode->i_gid = opts->gid; + + return 0; +} + +static int tracefs_remount(struct super_block *sb, int *flags, char *data) +{ + int err; + struct tracefs_fs_info *fsi = sb->s_fs_info; + + sync_filesystem(sb); + err = tracefs_parse_options(data, &fsi->mount_opts); + if (err) + goto fail; + + tracefs_apply_options(sb); + +fail: + return err; +} + +static int tracefs_show_options(struct seq_file *m, struct dentry *root) +{ + struct tracefs_fs_info *fsi = root->d_sb->s_fs_info; + struct tracefs_mount_opts *opts = &fsi->mount_opts; + + if (!uid_eq(opts->uid, GLOBAL_ROOT_UID)) + seq_printf(m, ",uid=%u", + from_kuid_munged(&init_user_ns, opts->uid)); + if (!gid_eq(opts->gid, GLOBAL_ROOT_GID)) + seq_printf(m, ",gid=%u", + from_kgid_munged(&init_user_ns, opts->gid)); + if (opts->mode != TRACEFS_DEFAULT_MODE) + seq_printf(m, ",mode=%o", opts->mode); + + return 0; +} + +static const struct super_operations tracefs_super_operations = { + .statfs = simple_statfs, + .remount_fs = tracefs_remount, + .show_options = tracefs_show_options, +}; + +static int trace_fill_super(struct super_block *sb, void *data, int silent) +{ + static struct tree_descr trace_files[] = {{""}}; + struct tracefs_fs_info *fsi; + int err; + + save_mount_options(sb, data); + + fsi = kzalloc(sizeof(struct tracefs_fs_info), GFP_KERNEL); + sb->s_fs_info = fsi; + if (!fsi) { + err = -ENOMEM; + goto fail; + } + + err = tracefs_parse_options(data, &fsi->mount_opts); + if (err) + goto fail; + + err = simple_fill_super(sb, TRACEFS_MAGIC, trace_files); + if (err) + goto fail; + + sb->s_op = &tracefs_super_operations; + + tracefs_apply_options(sb); + + return 0; + +fail: + kfree(fsi); + sb->s_fs_info = NULL; + return err; +} + +static struct dentry *trace_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) +{ + return mount_single(fs_type, flags, data, trace_fill_super); +} + +static struct file_system_type trace_fs_type = { + .owner = THIS_MODULE, + .name = "tracefs", + .mount = trace_mount, + .kill_sb = kill_litter_super, +}; +MODULE_ALIAS_FS("tracefs"); + +static struct dentry *start_creating(const char *name, struct dentry *parent) +{ + struct dentry *dentry; + int error; + + pr_debug("tracefs: creating file '%s'\n",name); + + error = simple_pin_fs(&trace_fs_type, &tracefs_mount, + &tracefs_mount_count); + if (error) + return ERR_PTR(error); + + /* If the parent is not specified, we create it in the root. + * We need the root dentry to do this, which is in the super + * block. A pointer to that is in the struct vfsmount that we + * have around. + */ + if (!parent) + parent = tracefs_mount->mnt_root; + + mutex_lock(&parent->d_inode->i_mutex); + dentry = lookup_one_len(name, parent, strlen(name)); + if (!IS_ERR(dentry) && dentry->d_inode) { + dput(dentry); + dentry = ERR_PTR(-EEXIST); + } + if (IS_ERR(dentry)) + mutex_unlock(&parent->d_inode->i_mutex); + return dentry; +} + +static struct dentry *failed_creating(struct dentry *dentry) +{ + mutex_unlock(&dentry->d_parent->d_inode->i_mutex); + dput(dentry); + simple_release_fs(&tracefs_mount, &tracefs_mount_count); + return NULL; +} + +static struct dentry *end_creating(struct dentry *dentry) +{ + mutex_unlock(&dentry->d_parent->d_inode->i_mutex); + return dentry; +} + +/** + * tracefs_create_file - create a file in the tracefs filesystem + * @name: a pointer to a string containing the name of the file to create. + * @mode: the permission that the file should have. + * @parent: a pointer to the parent dentry for this file. This should be a + * directory dentry if set. If this parameter is NULL, then the + * file will be created in the root of the tracefs filesystem. + * @data: a pointer to something that the caller will want to get to later + * on. The inode.i_private pointer will point to this value on + * the open() call. + * @fops: a pointer to a struct file_operations that should be used for + * this file. + * + * This is the basic "create a file" function for tracefs. It allows for a + * wide range of flexibility in creating a file, or a directory (if you want + * to create a directory, the tracefs_create_dir() function is + * recommended to be used instead.) + * + * This function will return a pointer to a dentry if it succeeds. This + * pointer must be passed to the tracefs_remove() function when the file is + * to be removed (no automatic cleanup happens if your module is unloaded, + * you are responsible here.) If an error occurs, %NULL will be returned. + * + * If tracefs is not enabled in the kernel, the value -%ENODEV will be + * returned. + */ +struct dentry *tracefs_create_file(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops) +{ + struct dentry *dentry; + struct inode *inode; + + if (!(mode & S_IFMT)) + mode |= S_IFREG; + BUG_ON(!S_ISREG(mode)); + dentry = start_creating(name, parent); + + if (IS_ERR(dentry)) + return NULL; + + inode = tracefs_get_inode(dentry->d_sb); + if (unlikely(!inode)) + return failed_creating(dentry); + + inode->i_mode = mode; + inode->i_fop = fops ? fops : &tracefs_file_operations; + inode->i_private = data; + d_instantiate(dentry, inode); + fsnotify_create(dentry->d_parent->d_inode, dentry); + return end_creating(dentry); +} + +static struct dentry *__create_dir(const char *name, struct dentry *parent, + const struct inode_operations *ops) +{ + struct dentry *dentry = start_creating(name, parent); + struct inode *inode; + + if (IS_ERR(dentry)) + return NULL; + + inode = tracefs_get_inode(dentry->d_sb); + if (unlikely(!inode)) + return failed_creating(dentry); + + inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; + inode->i_op = ops; + inode->i_fop = &simple_dir_operations; + + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inc_nlink(inode); + d_instantiate(dentry, inode); + inc_nlink(dentry->d_parent->d_inode); + fsnotify_mkdir(dentry->d_parent->d_inode, dentry); + return end_creating(dentry); +} + +/** + * tracefs_create_dir - create a directory in the tracefs filesystem + * @name: a pointer to a string containing the name of the directory to + * create. + * @parent: a pointer to the parent dentry for this file. This should be a + * directory dentry if set. If this parameter is NULL, then the + * directory will be created in the root of the tracefs filesystem. + * + * This function creates a directory in tracefs with the given name. + * + * This function will return a pointer to a dentry if it succeeds. This + * pointer must be passed to the tracefs_remove() function when the file is + * to be removed. If an error occurs, %NULL will be returned. + * + * If tracing is not enabled in the kernel, the value -%ENODEV will be + * returned. + */ +struct dentry *tracefs_create_dir(const char *name, struct dentry *parent) +{ + return __create_dir(name, parent, &simple_dir_inode_operations); +} + +/** + * tracefs_create_instance_dir - create the tracing instances directory + * @name: The name of the instances directory to create + * @parent: The parent directory that the instances directory will exist + * @mkdir: The function to call when a mkdir is performed. + * @rmdir: The function to call when a rmdir is performed. + * + * Only one instances directory is allowed. + * + * The instances directory is special as it allows for mkdir and rmdir to + * to be done by userspace. When a mkdir or rmdir is performed, the inode + * locks are released and the methhods passed in (@mkdir and @rmdir) are + * called without locks and with the name of the directory being created + * within the instances directory. + * + * Returns the dentry of the instances directory. + */ +struct dentry *tracefs_create_instance_dir(const char *name, struct dentry *parent, + int (*mkdir)(const char *name), + int (*rmdir)(const char *name)) +{ + struct dentry *dentry; + + /* Only allow one instance of the instances directory. */ + if (WARN_ON(tracefs_ops.mkdir || tracefs_ops.rmdir)) + return NULL; + + dentry = __create_dir(name, parent, &tracefs_dir_inode_operations); + if (!dentry) + return NULL; + + tracefs_ops.mkdir = mkdir; + tracefs_ops.rmdir = rmdir; + + return dentry; +} + +static inline int tracefs_positive(struct dentry *dentry) +{ + return dentry->d_inode && !d_unhashed(dentry); +} + +static int __tracefs_remove(struct dentry *dentry, struct dentry *parent) +{ + int ret = 0; + + if (tracefs_positive(dentry)) { + if (dentry->d_inode) { + dget(dentry); + switch (dentry->d_inode->i_mode & S_IFMT) { + case S_IFDIR: + ret = simple_rmdir(parent->d_inode, dentry); + break; + default: + simple_unlink(parent->d_inode, dentry); + break; + } + if (!ret) + d_delete(dentry); + dput(dentry); + } + } + return ret; +} + +/** + * tracefs_remove - removes a file or directory from the tracefs filesystem + * @dentry: a pointer to a the dentry of the file or directory to be + * removed. + * + * This function removes a file or directory in tracefs that was previously + * created with a call to another tracefs function (like + * tracefs_create_file() or variants thereof.) + */ +void tracefs_remove(struct dentry *dentry) +{ + struct dentry *parent; + int ret; + + if (IS_ERR_OR_NULL(dentry)) + return; + + parent = dentry->d_parent; + if (!parent || !parent->d_inode) + return; + + mutex_lock(&parent->d_inode->i_mutex); + ret = __tracefs_remove(dentry, parent); + mutex_unlock(&parent->d_inode->i_mutex); + if (!ret) + simple_release_fs(&tracefs_mount, &tracefs_mount_count); +} + +/** + * tracefs_remove_recursive - recursively removes a directory + * @dentry: a pointer to a the dentry of the directory to be removed. + * + * This function recursively removes a directory tree in tracefs that + * was previously created with a call to another tracefs function + * (like tracefs_create_file() or variants thereof.) + */ +void tracefs_remove_recursive(struct dentry *dentry) +{ + struct dentry *child, *parent; + + if (IS_ERR_OR_NULL(dentry)) + return; + + parent = dentry->d_parent; + if (!parent || !parent->d_inode) + return; + + parent = dentry; + down: + mutex_lock(&parent->d_inode->i_mutex); + loop: + /* + * The parent->d_subdirs is protected by the d_lock. Outside that + * lock, the child can be unlinked and set to be freed which can + * use the d_u.d_child as the rcu head and corrupt this list. + */ + spin_lock(&parent->d_lock); + list_for_each_entry(child, &parent->d_subdirs, d_child) { + if (!tracefs_positive(child)) + continue; + + /* perhaps simple_empty(child) makes more sense */ + if (!list_empty(&child->d_subdirs)) { + spin_unlock(&parent->d_lock); + mutex_unlock(&parent->d_inode->i_mutex); + parent = child; + goto down; + } + + spin_unlock(&parent->d_lock); + + if (!__tracefs_remove(child, parent)) + simple_release_fs(&tracefs_mount, &tracefs_mount_count); + + /* + * The parent->d_lock protects agaist child from unlinking + * from d_subdirs. When releasing the parent->d_lock we can + * no longer trust that the next pointer is valid. + * Restart the loop. We'll skip this one with the + * tracefs_positive() check. + */ + goto loop; + } + spin_unlock(&parent->d_lock); + + mutex_unlock(&parent->d_inode->i_mutex); + child = parent; + parent = parent->d_parent; + mutex_lock(&parent->d_inode->i_mutex); + + if (child != dentry) + /* go up */ + goto loop; + + if (!__tracefs_remove(child, parent)) + simple_release_fs(&tracefs_mount, &tracefs_mount_count); + mutex_unlock(&parent->d_inode->i_mutex); +} + +/** + * tracefs_initialized - Tells whether tracefs has been registered + */ +bool tracefs_initialized(void) +{ + return tracefs_registered; +} + +static struct kobject *trace_kobj; + +static int __init tracefs_init(void) +{ + int retval; + + trace_kobj = kobject_create_and_add("tracing", kernel_kobj); + if (!trace_kobj) + return -EINVAL; + + retval = register_filesystem(&trace_fs_type); + if (!retval) + tracefs_registered = true; + + return retval; +} +core_initcall(tracefs_init); diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index e627c0acf6264f..c3d15fe834033d 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -50,7 +50,6 @@ */ #include "ubifs.h" -#include #include #include #include diff --git a/fs/udf/file.c b/fs/udf/file.c index 08f3555fbeac3f..7f885cc8b0b798 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include "udf_i.h" #include "udf_sb.h" @@ -122,7 +122,7 @@ static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from) struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); int err, pos; - size_t count = iocb->ki_nbytes; + size_t count = iov_iter_count(from); struct udf_inode_info *iinfo = UDF_I(inode); mutex_lock(&inode->i_mutex); diff --git a/fs/udf/inode.c b/fs/udf/inode.c index a445d599098d7a..9c1fbd23913db5 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include "udf_i.h" #include "udf_sb.h" diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c index 3a9b7a1b8704be..4f8cdc59bc3815 100644 --- a/fs/xfs/xfs_aops.c +++ b/fs/xfs/xfs_aops.c @@ -31,7 +31,6 @@ #include "xfs_bmap.h" #include "xfs_bmap_util.h" #include "xfs_bmap_btree.h" -#include #include #include #include diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index a2e1cb8a568bf9..f44212fae65327 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -38,7 +38,6 @@ #include "xfs_icache.h" #include "xfs_pnfs.h" -#include #include #include #include diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index ac78910d7416c5..f8e8b34dc4274e 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -124,7 +124,10 @@ #define FTRACE_EVENTS() . = ALIGN(8); \ VMLINUX_SYMBOL(__start_ftrace_events) = .; \ *(_ftrace_events) \ - VMLINUX_SYMBOL(__stop_ftrace_events) = .; + VMLINUX_SYMBOL(__stop_ftrace_events) = .; \ + VMLINUX_SYMBOL(__start_ftrace_enum_maps) = .; \ + *(_ftrace_enum_map) \ + VMLINUX_SYMBOL(__stop_ftrace_enum_maps) = .; #else #define FTRACE_EVENTS() #endif diff --git a/include/dt-bindings/gpio/meson8b-gpio.h b/include/dt-bindings/gpio/meson8b-gpio.h new file mode 100644 index 00000000000000..c38cb20d7182f7 --- /dev/null +++ b/include/dt-bindings/gpio/meson8b-gpio.h @@ -0,0 +1,32 @@ +/* + * GPIO definitions for Amlogic Meson8b SoCs + * + * Copyright (C) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _DT_BINDINGS_MESON8B_GPIO_H +#define _DT_BINDINGS_MESON8B_GPIO_H + +#include + +/* GPIO Bank DIF */ +#define DIF_0_P 120 +#define DIF_0_N 121 +#define DIF_1_P 122 +#define DIF_1_N 123 +#define DIF_2_P 124 +#define DIF_2_N 125 +#define DIF_3_P 126 +#define DIF_3_N 127 +#define DIF_4_P 128 +#define DIF_4_N 129 + +#endif /* _DT_BINDINGS_MESON8B_GPIO_H */ diff --git a/include/linux/mfd/arizona/gpio.h b/include/dt-bindings/mfd/arizona.h similarity index 54% rename from include/linux/mfd/arizona/gpio.h rename to include/dt-bindings/mfd/arizona.h index d2146bb74f8971..c7af7c7ef79324 100644 --- a/include/linux/mfd/arizona/gpio.h +++ b/include/dt-bindings/mfd/arizona.h @@ -1,7 +1,7 @@ /* - * GPIO configuration for Arizona devices + * Device Tree defines for Arizona devices * - * Copyright 2013 Wolfson Microelectronics. PLC. + * Copyright 2015 Cirrus Logic Inc. * * Author: Charles Keepax * @@ -10,9 +10,10 @@ * published by the Free Software Foundation. */ -#ifndef _ARIZONA_GPIO_H -#define _ARIZONA_GPIO_H +#ifndef _DT_BINDINGS_MFD_ARIZONA_H +#define _DT_BINDINGS_MFD_ARIZONA_H +/* GPIO Function Definitions */ #define ARIZONA_GP_FN_TXLRCLK 0x00 #define ARIZONA_GP_FN_GPIO 0x01 #define ARIZONA_GP_FN_IRQ1 0x02 @@ -61,36 +62,32 @@ #define ARIZONA_GP_FN_SYSCLK_ENA_STATUS 0x4B #define ARIZONA_GP_FN_ASYNCCLK_ENA_STATUS 0x4C -#define ARIZONA_GPN_DIR 0x8000 /* GPN_DIR */ -#define ARIZONA_GPN_DIR_MASK 0x8000 /* GPN_DIR */ -#define ARIZONA_GPN_DIR_SHIFT 15 /* GPN_DIR */ -#define ARIZONA_GPN_DIR_WIDTH 1 /* GPN_DIR */ -#define ARIZONA_GPN_PU 0x4000 /* GPN_PU */ -#define ARIZONA_GPN_PU_MASK 0x4000 /* GPN_PU */ -#define ARIZONA_GPN_PU_SHIFT 14 /* GPN_PU */ -#define ARIZONA_GPN_PU_WIDTH 1 /* GPN_PU */ -#define ARIZONA_GPN_PD 0x2000 /* GPN_PD */ -#define ARIZONA_GPN_PD_MASK 0x2000 /* GPN_PD */ -#define ARIZONA_GPN_PD_SHIFT 13 /* GPN_PD */ -#define ARIZONA_GPN_PD_WIDTH 1 /* GPN_PD */ -#define ARIZONA_GPN_LVL 0x0800 /* GPN_LVL */ -#define ARIZONA_GPN_LVL_MASK 0x0800 /* GPN_LVL */ -#define ARIZONA_GPN_LVL_SHIFT 11 /* GPN_LVL */ -#define ARIZONA_GPN_LVL_WIDTH 1 /* GPN_LVL */ -#define ARIZONA_GPN_POL 0x0400 /* GPN_POL */ -#define ARIZONA_GPN_POL_MASK 0x0400 /* GPN_POL */ -#define ARIZONA_GPN_POL_SHIFT 10 /* GPN_POL */ -#define ARIZONA_GPN_POL_WIDTH 1 /* GPN_POL */ -#define ARIZONA_GPN_OP_CFG 0x0200 /* GPN_OP_CFG */ -#define ARIZONA_GPN_OP_CFG_MASK 0x0200 /* GPN_OP_CFG */ -#define ARIZONA_GPN_OP_CFG_SHIFT 9 /* GPN_OP_CFG */ -#define ARIZONA_GPN_OP_CFG_WIDTH 1 /* GPN_OP_CFG */ -#define ARIZONA_GPN_DB 0x0100 /* GPN_DB */ -#define ARIZONA_GPN_DB_MASK 0x0100 /* GPN_DB */ -#define ARIZONA_GPN_DB_SHIFT 8 /* GPN_DB */ -#define ARIZONA_GPN_DB_WIDTH 1 /* GPN_DB */ -#define ARIZONA_GPN_FN_MASK 0x007F /* GPN_DB */ -#define ARIZONA_GPN_FN_SHIFT 0 /* GPN_DB */ -#define ARIZONA_GPN_FN_WIDTH 7 /* GPN_DB */ +/* GPIO Configuration Bits */ +#define ARIZONA_GPN_DIR 0x8000 +#define ARIZONA_GPN_PU 0x4000 +#define ARIZONA_GPN_PD 0x2000 +#define ARIZONA_GPN_LVL 0x0800 +#define ARIZONA_GPN_POL 0x0400 +#define ARIZONA_GPN_OP_CFG 0x0200 +#define ARIZONA_GPN_DB 0x0100 + +/* Provide some defines for the most common configs */ +#define ARIZONA_GP_DEFAULT 0xffffffff +#define ARIZONA_GP_OUTPUT (ARIZONA_GP_FN_GPIO) +#define ARIZONA_GP_INPUT (ARIZONA_GP_FN_GPIO | \ + ARIZONA_GPN_DIR) + +#define ARIZONA_32KZ_MCLK1 1 +#define ARIZONA_32KZ_MCLK2 2 +#define ARIZONA_32KZ_NONE 3 + +#define ARIZONA_DMIC_MICVDD 0 +#define ARIZONA_DMIC_MICBIAS1 1 +#define ARIZONA_DMIC_MICBIAS2 2 +#define ARIZONA_DMIC_MICBIAS3 3 + +#define ARIZONA_INMODE_DIFF 0 +#define ARIZONA_INMODE_SE 1 +#define ARIZONA_INMODE_DMIC 2 #endif diff --git a/include/dt-bindings/mfd/qcom-rpm.h b/include/dt-bindings/mfd/qcom-rpm.h index 388a6f3d6165e8..13a9d4bf2662a1 100644 --- a/include/dt-bindings/mfd/qcom-rpm.h +++ b/include/dt-bindings/mfd/qcom-rpm.h @@ -141,6 +141,12 @@ #define QCOM_RPM_SYS_FABRIC_MODE 131 #define QCOM_RPM_USB_OTG_SWITCH 132 #define QCOM_RPM_VDDMIN_GPIO 133 +#define QCOM_RPM_NSS_FABRIC_0_CLK 134 +#define QCOM_RPM_NSS_FABRIC_1_CLK 135 +#define QCOM_RPM_SMB208_S1a 136 +#define QCOM_RPM_SMB208_S1b 137 +#define QCOM_RPM_SMB208_S2a 138 +#define QCOM_RPM_SMB208_S2b 139 /* * Constants used to select force mode for regulators. diff --git a/include/dt-bindings/pinctrl/mt65xx.h b/include/dt-bindings/pinctrl/mt65xx.h new file mode 100644 index 00000000000000..1198f4541327c0 --- /dev/null +++ b/include/dt-bindings/pinctrl/mt65xx.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Hongzhou.Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DT_BINDINGS_PINCTRL_MT65XX_H +#define _DT_BINDINGS_PINCTRL_MT65XX_H + +#define MTK_PIN_NO(x) ((x) << 8) +#define MTK_GET_PIN_NO(x) ((x) >> 8) +#define MTK_GET_PIN_FUNC(x) ((x) & 0xf) + +#define MTK_PUPD_SET_R1R0_00 100 +#define MTK_PUPD_SET_R1R0_01 101 +#define MTK_PUPD_SET_R1R0_10 102 +#define MTK_PUPD_SET_R1R0_11 103 + +#define MTK_DRIVE_2mA 2 +#define MTK_DRIVE_4mA 4 +#define MTK_DRIVE_6mA 6 +#define MTK_DRIVE_8mA 8 +#define MTK_DRIVE_10mA 10 +#define MTK_DRIVE_12mA 12 +#define MTK_DRIVE_14mA 14 +#define MTK_DRIVE_16mA 16 +#define MTK_DRIVE_20mA 20 +#define MTK_DRIVE_24mA 24 +#define MTK_DRIVE_28mA 28 +#define MTK_DRIVE_32mA 32 + +#endif /* _DT_BINDINGS_PINCTRL_MT65XX_H */ diff --git a/include/dt-bindings/pinctrl/qcom,pmic-gpio.h b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h index fa74d7cc960cbf..aafa76cb569dfc 100644 --- a/include/dt-bindings/pinctrl/qcom,pmic-gpio.h +++ b/include/dt-bindings/pinctrl/qcom,pmic-gpio.h @@ -48,6 +48,14 @@ #define PM8058_GPIO_L5 6 #define PM8058_GPIO_L2 7 +/* + * Note: PM8916 GPIO1 and GPIO2 are supporting + * only L2(1.15V) and L5(1.8V) options + */ +#define PM8916_GPIO_VPH 0 +#define PM8916_GPIO_L2 2 +#define PM8916_GPIO_L5 3 + #define PM8917_GPIO_VPH 0 #define PM8917_GPIO_S4 2 #define PM8917_GPIO_L15 3 @@ -115,6 +123,13 @@ #define PM8058_GPIO39_MP3_CLK PMIC_GPIO_FUNC_FUNC1 #define PM8058_GPIO40_EXT_BB_EN PMIC_GPIO_FUNC_FUNC1 +#define PM8916_GPIO1_BAT_ALRM_OUT PMIC_GPIO_FUNC_FUNC1 +#define PM8916_GPIO1_KEYP_DRV PMIC_GPIO_FUNC_FUNC2 +#define PM8916_GPIO2_DIV_CLK PMIC_GPIO_FUNC_FUNC1 +#define PM8916_GPIO2_SLEEP_CLK PMIC_GPIO_FUNC_FUNC2 +#define PM8916_GPIO3_KEYP_DRV PMIC_GPIO_FUNC_FUNC1 +#define PM8916_GPIO4_KEYP_DRV PMIC_GPIO_FUNC_FUNC2 + #define PM8917_GPIO9_18_KEYP_DRV PMIC_GPIO_FUNC_FUNC1 #define PM8917_GPIO20_BAT_ALRM_OUT PMIC_GPIO_FUNC_FUNC1 #define PM8917_GPIO21_23_UART_TX PMIC_GPIO_FUNC_FUNC2 diff --git a/include/dt-bindings/pinctrl/qcom,pmic-mpp.h b/include/dt-bindings/pinctrl/qcom,pmic-mpp.h index d2c7dabe322316..c10205491f8d65 100644 --- a/include/dt-bindings/pinctrl/qcom,pmic-mpp.h +++ b/include/dt-bindings/pinctrl/qcom,pmic-mpp.h @@ -10,6 +10,10 @@ #define PM8841_MPP_VPH 0 #define PM8841_MPP_S3 2 +#define PM8916_MPP_VPH 0 +#define PM8916_MPP_L2 2 +#define PM8916_MPP_L5 3 + #define PM8941_MPP_VPH 0 #define PM8941_MPP_L1 1 #define PM8941_MPP_S3 2 diff --git a/include/linux/aio.h b/include/linux/aio.h index d9c92daa3944e4..9eb42dbc5582ac 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -1,86 +1,23 @@ #ifndef __LINUX__AIO_H #define __LINUX__AIO_H -#include -#include #include -#include -#include - -#include struct kioctx; struct kiocb; +struct mm_struct; #define KIOCB_KEY 0 -/* - * We use ki_cancel == KIOCB_CANCELLED to indicate that a kiocb has been either - * cancelled or completed (this makes a certain amount of sense because - * successful cancellation - io_cancel() - does deliver the completion to - * userspace). - * - * And since most things don't implement kiocb cancellation and we'd really like - * kiocb completion to be lockless when possible, we use ki_cancel to - * synchronize cancellation and completion - we only set it to KIOCB_CANCELLED - * with xchg() or cmpxchg(), see batch_complete_aio() and kiocb_cancel(). - */ -#define KIOCB_CANCELLED ((void *) (~0ULL)) - typedef int (kiocb_cancel_fn)(struct kiocb *); -struct kiocb { - struct file *ki_filp; - struct kioctx *ki_ctx; /* NULL for sync ops */ - kiocb_cancel_fn *ki_cancel; - void *private; - - union { - void __user *user; - struct task_struct *tsk; - } ki_obj; - - __u64 ki_user_data; /* user's data for completion */ - loff_t ki_pos; - size_t ki_nbytes; /* copy of iocb->aio_nbytes */ - - struct list_head ki_list; /* the aio core uses this - * for cancellation */ - - /* - * If the aio_resfd field of the userspace iocb is not zero, - * this is the underlying eventfd context to deliver events to. - */ - struct eventfd_ctx *ki_eventfd; -}; - -static inline bool is_sync_kiocb(struct kiocb *kiocb) -{ - return kiocb->ki_ctx == NULL; -} - -static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp) -{ - *kiocb = (struct kiocb) { - .ki_ctx = NULL, - .ki_filp = filp, - .ki_obj.tsk = current, - }; -} - /* prototypes */ #ifdef CONFIG_AIO -extern ssize_t wait_on_sync_kiocb(struct kiocb *iocb); -extern void aio_complete(struct kiocb *iocb, long res, long res2); -struct mm_struct; extern void exit_aio(struct mm_struct *mm); extern long do_io_submit(aio_context_t ctx_id, long nr, struct iocb __user *__user *iocbpp, bool compat); void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel); #else -static inline ssize_t wait_on_sync_kiocb(struct kiocb *iocb) { return 0; } -static inline void aio_complete(struct kiocb *iocb, long res, long res2) { } -struct mm_struct; static inline void exit_aio(struct mm_struct *mm) { } static inline long do_io_submit(aio_context_t ctx_id, long nr, struct iocb __user * __user *iocbpp, @@ -89,11 +26,6 @@ static inline void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel) { } #endif /* CONFIG_AIO */ -static inline struct kiocb *list_kiocb(struct list_head *h) -{ - return list_entry(h, struct kiocb, ki_list); -} - /* for sysctl: */ extern unsigned long aio_nr; extern unsigned long aio_max_nr; diff --git a/include/linux/bpf.h b/include/linux/bpf.h index bbfceb7564523b..c2e21113ecc053 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -113,8 +113,6 @@ struct bpf_prog_type_list { enum bpf_prog_type type; }; -void bpf_register_prog_type(struct bpf_prog_type_list *tl); - struct bpf_prog; struct bpf_prog_aux { @@ -129,11 +127,25 @@ struct bpf_prog_aux { }; #ifdef CONFIG_BPF_SYSCALL +void bpf_register_prog_type(struct bpf_prog_type_list *tl); + void bpf_prog_put(struct bpf_prog *prog); +struct bpf_prog *bpf_prog_get(u32 ufd); #else -static inline void bpf_prog_put(struct bpf_prog *prog) {} +static inline void bpf_register_prog_type(struct bpf_prog_type_list *tl) +{ +} + +static inline struct bpf_prog *bpf_prog_get(u32 ufd) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void bpf_prog_put(struct bpf_prog *prog) +{ +} #endif -struct bpf_prog *bpf_prog_get(u32 ufd); + /* verify correctness of eBPF program */ int bpf_check(struct bpf_prog *fp, union bpf_attr *attr); diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index 37b81bd51ec0e4..2821838256b4f7 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -10,6 +10,8 @@ #ifdef CONFIG_CONTEXT_TRACKING extern void context_tracking_cpu_set(int cpu); +extern void context_tracking_enter(enum ctx_state state); +extern void context_tracking_exit(enum ctx_state state); extern void context_tracking_user_enter(void); extern void context_tracking_user_exit(void); extern void __context_tracking_task_switch(struct task_struct *prev, @@ -35,7 +37,8 @@ static inline enum ctx_state exception_enter(void) return 0; prev_ctx = this_cpu_read(context_tracking.state); - context_tracking_user_exit(); + if (prev_ctx != CONTEXT_KERNEL) + context_tracking_exit(prev_ctx); return prev_ctx; } @@ -43,8 +46,8 @@ static inline enum ctx_state exception_enter(void) static inline void exception_exit(enum ctx_state prev_ctx) { if (context_tracking_is_enabled()) { - if (prev_ctx == IN_USER) - context_tracking_user_enter(); + if (prev_ctx != CONTEXT_KERNEL) + context_tracking_enter(prev_ctx); } } @@ -78,10 +81,16 @@ static inline void guest_enter(void) vtime_guest_enter(current); else current->flags |= PF_VCPU; + + if (context_tracking_is_enabled()) + context_tracking_enter(CONTEXT_GUEST); } static inline void guest_exit(void) { + if (context_tracking_is_enabled()) + context_tracking_exit(CONTEXT_GUEST); + if (vtime_accounting_enabled()) vtime_guest_exit(current); else diff --git a/include/linux/context_tracking_state.h b/include/linux/context_tracking_state.h index 97a81225d03739..6b7b96a32b753a 100644 --- a/include/linux/context_tracking_state.h +++ b/include/linux/context_tracking_state.h @@ -13,8 +13,9 @@ struct context_tracking { */ bool active; enum ctx_state { - IN_KERNEL = 0, - IN_USER, + CONTEXT_KERNEL = 0, + CONTEXT_USER, + CONTEXT_GUEST, } state; }; @@ -34,11 +35,13 @@ static inline bool context_tracking_cpu_is_enabled(void) static inline bool context_tracking_in_user(void) { - return __this_cpu_read(context_tracking.state) == IN_USER; + return __this_cpu_read(context_tracking.state) == CONTEXT_USER; } #else static inline bool context_tracking_in_user(void) { return false; } static inline bool context_tracking_active(void) { return false; } +static inline bool context_tracking_is_enabled(void) { return false; } +static inline bool context_tracking_cpu_is_enabled(void) { return false; } #endif /* CONFIG_CONTEXT_TRACKING */ #endif diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 4260e8594bd77d..c0fb6b1b4712c3 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -73,6 +73,7 @@ enum { /* migration should happen before other stuff but after perf */ CPU_PRI_PERF = 20, CPU_PRI_MIGRATION = 10, + CPU_PRI_SMPBOOT = 9, /* bring up workqueues before normal notifiers and down after */ CPU_PRI_WORKQUEUE_UP = 5, CPU_PRI_WORKQUEUE_DOWN = -5, @@ -95,6 +96,10 @@ enum { * Called on the new cpu, just before * enabling interrupts. Must not sleep, * must not fail */ +#define CPU_DYING_IDLE 0x000B /* CPU (unsigned)v dying, reached + * idle loop. */ +#define CPU_BROKEN 0x000C /* CPU (unsigned)v did not die properly, + * perhaps due to preemption. */ /* Used for CPU hotplug events occurring while tasks are frozen due to a suspend * operation in progress @@ -161,6 +166,7 @@ static inline void __unregister_cpu_notifier(struct notifier_block *nb) } #endif +void smpboot_thread_init(void); int cpu_up(unsigned int cpu); void notify_cpu_starting(unsigned int cpu); extern void cpu_maps_update_begin(void); @@ -208,6 +214,10 @@ static inline void cpu_notifier_register_done(void) { } +static inline void smpboot_thread_init(void) +{ +} + #endif /* CONFIG_SMP */ extern struct bus_type cpu_subsys; @@ -271,4 +281,14 @@ void arch_cpu_idle_enter(void); void arch_cpu_idle_exit(void); void arch_cpu_idle_dead(void); +DECLARE_PER_CPU(bool, cpu_dead_idle); + +int cpu_report_state(int cpu); +int cpu_check_up_prepare(int cpu); +void cpu_set_state_online(int cpu); +#ifdef CONFIG_HOTPLUG_CPU +bool cpu_wait_death(unsigned int cpu, int seconds); +bool cpu_report_death(void); +#endif /* #ifdef CONFIG_HOTPLUG_CPU */ + #endif /* _LINUX_CPU_H_ */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 52cc4492cb3a1b..d502e5436c8475 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -314,6 +314,28 @@ struct page; struct address_space; struct writeback_control; +#define IOCB_EVENTFD (1 << 0) + +struct kiocb { + struct file *ki_filp; + loff_t ki_pos; + void (*ki_complete)(struct kiocb *iocb, long ret, long ret2); + void *private; + int ki_flags; +}; + +static inline bool is_sync_kiocb(struct kiocb *kiocb) +{ + return kiocb->ki_complete == NULL; +} + +static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp) +{ + *kiocb = (struct kiocb) { + .ki_filp = filp, + }; +} + /* * "descriptor" for what we're up to with a read. * This allows us to use the same read code yet @@ -2145,7 +2167,7 @@ struct filename { const __user char *uptr; /* original userland pointer */ struct audit_names *aname; int refcnt; - bool separate; /* should "name" be freed? */ + const char iname[]; }; extern long vfs_truncate(struct path *, loff_t); diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index c674ee8f7fca50..46e83c2156c667 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -13,6 +13,7 @@ struct trace_array; struct trace_buffer; struct tracer; struct dentry; +struct bpf_prog; struct trace_print_flags { unsigned long mask; @@ -202,7 +203,7 @@ enum trace_reg { struct ftrace_event_call; struct ftrace_event_class { - char *system; + const char *system; void *probe; #ifdef CONFIG_PERF_EVENTS void *perf_probe; @@ -252,6 +253,7 @@ enum { TRACE_EVENT_FL_WAS_ENABLED_BIT, TRACE_EVENT_FL_USE_CALL_FILTER_BIT, TRACE_EVENT_FL_TRACEPOINT_BIT, + TRACE_EVENT_FL_KPROBE_BIT, }; /* @@ -265,6 +267,7 @@ enum { * it is best to clear the buffers that used it). * USE_CALL_FILTER - For ftrace internal events, don't use file filter * TRACEPOINT - Event is a tracepoint + * KPROBE - Event is a kprobe */ enum { TRACE_EVENT_FL_FILTERED = (1 << TRACE_EVENT_FL_FILTERED_BIT), @@ -274,6 +277,7 @@ enum { TRACE_EVENT_FL_WAS_ENABLED = (1 << TRACE_EVENT_FL_WAS_ENABLED_BIT), TRACE_EVENT_FL_USE_CALL_FILTER = (1 << TRACE_EVENT_FL_USE_CALL_FILTER_BIT), TRACE_EVENT_FL_TRACEPOINT = (1 << TRACE_EVENT_FL_TRACEPOINT_BIT), + TRACE_EVENT_FL_KPROBE = (1 << TRACE_EVENT_FL_KPROBE_BIT), }; struct ftrace_event_call { @@ -285,7 +289,7 @@ struct ftrace_event_call { struct tracepoint *tp; }; struct trace_event event; - const char *print_fmt; + char *print_fmt; struct event_filter *filter; void *mod; void *data; @@ -303,6 +307,7 @@ struct ftrace_event_call { #ifdef CONFIG_PERF_EVENTS int perf_refcount; struct hlist_head __percpu *perf_events; + struct bpf_prog *prog; int (*perf_perm)(struct ftrace_event_call *, struct perf_event *); @@ -548,6 +553,15 @@ event_trigger_unlock_commit_regs(struct ftrace_event_file *file, event_triggers_post_call(file, tt); } +#ifdef CONFIG_BPF_SYSCALL +unsigned int trace_call_bpf(struct bpf_prog *prog, void *ctx); +#else +static inline unsigned int trace_call_bpf(struct bpf_prog *prog, void *ctx) +{ + return 1; +} +#endif + enum { FILTER_OTHER = 0, FILTER_STATIC_STRING, diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h index 4173a8fdad9efd..0408421d885f94 100644 --- a/include/linux/hid-sensor-hub.h +++ b/include/linux/hid-sensor-hub.h @@ -48,20 +48,44 @@ struct hid_sensor_hub_attribute_info { s32 logical_maximum; }; +/** + * struct sensor_hub_pending - Synchronous read pending information + * @status: Pending status true/false. + * @ready: Completion synchronization data. + * @usage_id: Usage id for physical device, E.g. Gyro usage id. + * @attr_usage_id: Usage Id of a field, E.g. X-AXIS for a gyro. + * @raw_size: Response size for a read request. + * @raw_data: Place holder for received response. + */ +struct sensor_hub_pending { + bool status; + struct completion ready; + u32 usage_id; + u32 attr_usage_id; + int raw_size; + u8 *raw_data; +}; + /** * struct hid_sensor_hub_device - Stores the hub instance data * @hdev: Stores the hid instance. * @vendor_id: Vendor id of hub device. * @product_id: Product id of hub device. + * @usage: Usage id for this hub device instance. * @start_collection_index: Starting index for a phy type collection * @end_collection_index: Last index for a phy type collection + * @mutex: synchronizing mutex. + * @pending: Holds information of pending sync read request. */ struct hid_sensor_hub_device { struct hid_device *hdev; u32 vendor_id; u32 product_id; + u32 usage; int start_collection_index; int end_collection_index; + struct mutex mutex; + struct sensor_hub_pending pending; }; /** @@ -152,40 +176,51 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev, * @usage_id: Attribute usage id of parent physical device as per spec * @attr_usage_id: Attribute usage id as per spec * @report_id: Report id to look for +* @flag: Synchronous or asynchronous read * -* Issues a synchronous read request for an input attribute. Returns -* data upto 32 bits. Since client can get events, so this call should -* not be used for data paths, this will impact performance. +* Issues a synchronous or asynchronous read request for an input attribute. +* Returns data upto 32 bits. */ +enum sensor_hub_read_flags { + SENSOR_HUB_SYNC, + SENSOR_HUB_ASYNC, +}; + int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev, - u32 usage_id, - u32 attr_usage_id, u32 report_id); + u32 usage_id, + u32 attr_usage_id, u32 report_id, + enum sensor_hub_read_flags flag +); + /** * sensor_hub_set_feature() - Feature set request * @hsdev: Hub device instance. * @report_id: Report id to look for * @field_index: Field index inside a report -* @value: Value to set +* @buffer_size: size of the buffer +* @buffer: buffer to use in the feature set * * Used to set a field in feature report. For example this can set polling * interval, sensitivity, activate/deactivate state. */ int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, - u32 field_index, s32 value); + u32 field_index, int buffer_size, void *buffer); /** * sensor_hub_get_feature() - Feature get request * @hsdev: Hub device instance. * @report_id: Report id to look for * @field_index: Field index inside a report -* @value: Place holder for return value +* @buffer_size: size of the buffer +* @buffer: buffer to copy output * * Used to get a field in feature report. For example this can get polling -* interval, sensitivity, activate/deactivate state. +* interval, sensitivity, activate/deactivate state. On success it returns +* number of bytes copied to buffer. On failure, it returns value < 0. */ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, - u32 field_index, s32 *value); + u32 field_index, int buffer_size, void *buffer); /* hid-sensor-attributes */ diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h index 109f0e633e01f1..f2ee90aed0c2e8 100644 --- a/include/linux/hid-sensor-ids.h +++ b/include/linux/hid-sensor-ids.h @@ -21,6 +21,8 @@ #define HID_MAX_PHY_DEVICES 0xFF +#define HID_USAGE_SENSOR_COLLECTION 0x200001 + /* Accel 3D (200073) */ #define HID_USAGE_SENSOR_ACCEL_3D 0x200073 #define HID_USAGE_SENSOR_DATA_ACCELERATION 0x200452 diff --git a/include/linux/hid.h b/include/linux/hid.h index f94cf28e4b7c29..176b43670e5da9 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -159,6 +159,7 @@ struct hid_item { #define HID_UP_LED 0x00080000 #define HID_UP_BUTTON 0x00090000 #define HID_UP_ORDINAL 0x000a0000 +#define HID_UP_TELEPHONY 0x000b0000 #define HID_UP_CONSUMER 0x000c0000 #define HID_UP_DIGITIZER 0x000d0000 #define HID_UP_PID 0x000f0000 @@ -269,6 +270,7 @@ struct hid_item { #define HID_DG_DEVICEINDEX 0x000d0053 #define HID_DG_CONTACTCOUNT 0x000d0054 #define HID_DG_CONTACTMAX 0x000d0055 +#define HID_DG_BUTTONTYPE 0x000d0059 #define HID_DG_BARRELSWITCH2 0x000d005a #define HID_DG_TOOLSERIALNUMBER 0x000d005b diff --git a/include/linux/i2c.h b/include/linux/i2c.h index f17da50402a4da..898033f41d767d 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -253,10 +253,10 @@ static inline void i2c_set_clientdata(struct i2c_client *dev, void *data) #if IS_ENABLED(CONFIG_I2C_SLAVE) enum i2c_slave_event { - I2C_SLAVE_REQ_READ_START, - I2C_SLAVE_REQ_READ_END, - I2C_SLAVE_REQ_WRITE_START, - I2C_SLAVE_REQ_WRITE_END, + I2C_SLAVE_READ_REQUESTED, + I2C_SLAVE_WRITE_REQUESTED, + I2C_SLAVE_READ_PROCESSED, + I2C_SLAVE_WRITE_RECEIVED, I2C_SLAVE_STOP, }; @@ -435,8 +435,8 @@ struct i2c_bus_recovery_info { void (*set_scl)(struct i2c_adapter *, int val); int (*get_sda)(struct i2c_adapter *); - void (*prepare_recovery)(struct i2c_bus_recovery_info *bri); - void (*unprepare_recovery)(struct i2c_bus_recovery_info *bri); + void (*prepare_recovery)(struct i2c_adapter *); + void (*unprepare_recovery)(struct i2c_adapter *); /* gpio recovery */ int scl_gpio; @@ -449,6 +449,48 @@ int i2c_recover_bus(struct i2c_adapter *adap); int i2c_generic_gpio_recovery(struct i2c_adapter *adap); int i2c_generic_scl_recovery(struct i2c_adapter *adap); +/** + * struct i2c_adapter_quirks - describe flaws of an i2c adapter + * @flags: see I2C_AQ_* for possible flags and read below + * @max_num_msgs: maximum number of messages per transfer + * @max_write_len: maximum length of a write message + * @max_read_len: maximum length of a read message + * @max_comb_1st_msg_len: maximum length of the first msg in a combined message + * @max_comb_2nd_msg_len: maximum length of the second msg in a combined message + * + * Note about combined messages: Some I2C controllers can only send one message + * per transfer, plus something called combined message or write-then-read. + * This is (usually) a small write message followed by a read message and + * barely enough to access register based devices like EEPROMs. There is a flag + * to support this mode. It implies max_num_msg = 2 and does the length checks + * with max_comb_*_len because combined message mode usually has its own + * limitations. Because of HW implementations, some controllers can actually do + * write-then-anything or other variants. To support that, write-then-read has + * been broken out into smaller bits like write-first and read-second which can + * be combined as needed. + */ + +struct i2c_adapter_quirks { + u64 flags; + int max_num_msgs; + u16 max_write_len; + u16 max_read_len; + u16 max_comb_1st_msg_len; + u16 max_comb_2nd_msg_len; +}; + +/* enforce max_num_msgs = 2 and use max_comb_*_len for length checks */ +#define I2C_AQ_COMB BIT(0) +/* first combined message must be write */ +#define I2C_AQ_COMB_WRITE_FIRST BIT(1) +/* second combined message must be read */ +#define I2C_AQ_COMB_READ_SECOND BIT(2) +/* both combined messages must have the same target address */ +#define I2C_AQ_COMB_SAME_ADDR BIT(3) +/* convenience macro for typical write-then read case */ +#define I2C_AQ_COMB_WRITE_THEN_READ (I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | \ + I2C_AQ_COMB_READ_SECOND | I2C_AQ_COMB_SAME_ADDR) + /* * i2c_adapter is the structure used to identify a physical i2c bus along * with the access algorithms necessary to access it. @@ -474,6 +516,7 @@ struct i2c_adapter { struct list_head userspace_clients; struct i2c_bus_recovery_info *bus_recovery_info; + const struct i2c_adapter_quirks *quirks; }; #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 82af5d0b996e7f..ad45054309a0fc 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -779,7 +779,8 @@ static inline void kvm_guest_enter(void) * one time slice). Lets treat guest mode as quiescent state, just like * we do with user-mode execution. */ - rcu_virt_note_context_switch(smp_processor_id()); + if (!context_tracking_cpu_is_enabled()) + rcu_virt_note_context_switch(smp_processor_id()); } static inline void kvm_guest_exit(void) diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index 95023fd8b00dd8..ee6dbb39a80981 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h @@ -123,10 +123,10 @@ struct klp_patch { enum klp_state state; }; -extern int klp_register_patch(struct klp_patch *); -extern int klp_unregister_patch(struct klp_patch *); -extern int klp_enable_patch(struct klp_patch *); -extern int klp_disable_patch(struct klp_patch *); +int klp_register_patch(struct klp_patch *); +int klp_unregister_patch(struct klp_patch *); +int klp_enable_patch(struct klp_patch *); +int klp_disable_patch(struct klp_patch *); #endif /* CONFIG_LIVEPATCH */ diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 74ab23176e9b0a..066ba4157541a9 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -531,8 +531,13 @@ do { \ # define might_lock_read(lock) do { } while (0) #endif -#ifdef CONFIG_PROVE_RCU +#ifdef CONFIG_LOCKDEP void lockdep_rcu_suspicious(const char *file, const int line, const char *s); +#else +static inline void +lockdep_rcu_suspicious(const char *file, const int line, const char *s) +{ +} #endif #endif /* __LINUX_LOCKDEP_H */ diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 910e3aa1e96528..f97010576f5618 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -24,6 +24,7 @@ enum arizona_type { WM5102 = 1, WM5110 = 2, WM8997 = 3, + WM8280 = 4, }; #define ARIZONA_IRQ_GP1 0 diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 4578c72c9b86c5..1789cb0f4f1701 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -11,31 +11,26 @@ #ifndef _ARIZONA_PDATA_H #define _ARIZONA_PDATA_H -#define ARIZONA_GPN_DIR 0x8000 /* GPN_DIR */ +#include + #define ARIZONA_GPN_DIR_MASK 0x8000 /* GPN_DIR */ #define ARIZONA_GPN_DIR_SHIFT 15 /* GPN_DIR */ #define ARIZONA_GPN_DIR_WIDTH 1 /* GPN_DIR */ -#define ARIZONA_GPN_PU 0x4000 /* GPN_PU */ #define ARIZONA_GPN_PU_MASK 0x4000 /* GPN_PU */ #define ARIZONA_GPN_PU_SHIFT 14 /* GPN_PU */ #define ARIZONA_GPN_PU_WIDTH 1 /* GPN_PU */ -#define ARIZONA_GPN_PD 0x2000 /* GPN_PD */ #define ARIZONA_GPN_PD_MASK 0x2000 /* GPN_PD */ #define ARIZONA_GPN_PD_SHIFT 13 /* GPN_PD */ #define ARIZONA_GPN_PD_WIDTH 1 /* GPN_PD */ -#define ARIZONA_GPN_LVL 0x0800 /* GPN_LVL */ #define ARIZONA_GPN_LVL_MASK 0x0800 /* GPN_LVL */ #define ARIZONA_GPN_LVL_SHIFT 11 /* GPN_LVL */ #define ARIZONA_GPN_LVL_WIDTH 1 /* GPN_LVL */ -#define ARIZONA_GPN_POL 0x0400 /* GPN_POL */ #define ARIZONA_GPN_POL_MASK 0x0400 /* GPN_POL */ #define ARIZONA_GPN_POL_SHIFT 10 /* GPN_POL */ #define ARIZONA_GPN_POL_WIDTH 1 /* GPN_POL */ -#define ARIZONA_GPN_OP_CFG 0x0200 /* GPN_OP_CFG */ #define ARIZONA_GPN_OP_CFG_MASK 0x0200 /* GPN_OP_CFG */ #define ARIZONA_GPN_OP_CFG_SHIFT 9 /* GPN_OP_CFG */ #define ARIZONA_GPN_OP_CFG_WIDTH 1 /* GPN_OP_CFG */ -#define ARIZONA_GPN_DB 0x0100 /* GPN_DB */ #define ARIZONA_GPN_DB_MASK 0x0100 /* GPN_DB */ #define ARIZONA_GPN_DB_SHIFT 8 /* GPN_DB */ #define ARIZONA_GPN_DB_WIDTH 1 /* GPN_DB */ @@ -45,23 +40,10 @@ #define ARIZONA_MAX_GPIO 5 -#define ARIZONA_32KZ_MCLK1 1 -#define ARIZONA_32KZ_MCLK2 2 -#define ARIZONA_32KZ_NONE 3 - #define ARIZONA_MAX_INPUT 4 -#define ARIZONA_DMIC_MICVDD 0 -#define ARIZONA_DMIC_MICBIAS1 1 -#define ARIZONA_DMIC_MICBIAS2 2 -#define ARIZONA_DMIC_MICBIAS3 3 - #define ARIZONA_MAX_MICBIAS 3 -#define ARIZONA_INMODE_DIFF 0 -#define ARIZONA_INMODE_SE 1 -#define ARIZONA_INMODE_DMIC 2 - #define ARIZONA_MAX_OUTPUT 6 #define ARIZONA_MAX_AIF 3 @@ -112,7 +94,7 @@ struct arizona_pdata { int gpio_base; /** Pin state for GPIO pins */ - int gpio_defaults[ARIZONA_MAX_GPIO]; + unsigned int gpio_defaults[ARIZONA_MAX_GPIO]; /** * Maximum number of channels clocks will be generated for, diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h index 955dd990beafaf..51633ea6f9103c 100644 --- a/include/linux/mfd/max77693-private.h +++ b/include/linux/mfd/max77693-private.h @@ -87,6 +87,7 @@ enum max77693_pmic_reg { /* MAX77693 ITORCH register */ #define TORCH_IOUT1_SHIFT 0 #define TORCH_IOUT2_SHIFT 4 +#define TORCH_IOUT_MASK(x) (0xf << (x)) #define TORCH_IOUT_MIN 15625 #define TORCH_IOUT_MAX 250000 #define TORCH_IOUT_STEP 15625 @@ -113,8 +114,8 @@ enum max77693_pmic_reg { #define FLASH_EN_FLASH 0x1 #define FLASH_EN_TORCH 0x2 #define FLASH_EN_ON 0x3 -#define FLASH_EN_SHIFT(x) (6 - ((x) - 1) * 2) -#define TORCH_EN_SHIFT(x) (2 - ((x) - 1) * 2) +#define FLASH_EN_SHIFT(x) (6 - (x) * 2) +#define TORCH_EN_SHIFT(x) (2 - (x) * 2) /* MAX77693 MAX_FLASH1 register */ #define MAX_FLASH1_MAX_FL_EN 0x80 diff --git a/include/linux/mfd/max77693.h b/include/linux/mfd/max77693.h index 09a4dedaeea85f..d450f687301bd9 100644 --- a/include/linux/mfd/max77693.h +++ b/include/linux/mfd/max77693.h @@ -81,19 +81,6 @@ enum max77693_led_boost_mode { MAX77693_LED_BOOST_FIXED, }; -struct max77693_led_platform_data { - u32 fleds[2]; - u32 iout_torch[2]; - u32 iout_flash[2]; - u32 trigger[2]; - u32 trigger_type[2]; - u32 num_leds; - u32 boost_mode; - u32 flash_timeout; - u32 boost_vout; - u32 low_vsys; -}; - /* MAX77693 */ struct max77693_platform_data { diff --git a/include/linux/mfd/max77843-private.h b/include/linux/mfd/max77843-private.h new file mode 100644 index 00000000000000..7178ace8379e77 --- /dev/null +++ b/include/linux/mfd/max77843-private.h @@ -0,0 +1,454 @@ +/* + * Common variables for the Maxim MAX77843 driver + * + * Copyright (C) 2015 Samsung Electronics + * Author: Jaewon Kim + * Author: Beomho Seo + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __MAX77843_PRIVATE_H_ +#define __MAX77843_PRIVATE_H_ + +#include +#include + +#define I2C_ADDR_TOPSYS (0xCC >> 1) +#define I2C_ADDR_CHG (0xD2 >> 1) +#define I2C_ADDR_FG (0x6C >> 1) +#define I2C_ADDR_MUIC (0x4A >> 1) + +/* Topsys, Haptic and LED registers */ +enum max77843_sys_reg { + MAX77843_SYS_REG_PMICID = 0x00, + MAX77843_SYS_REG_PMICREV = 0x01, + MAX77843_SYS_REG_MAINCTRL1 = 0x02, + MAX77843_SYS_REG_INTSRC = 0x22, + MAX77843_SYS_REG_INTSRCMASK = 0x23, + MAX77843_SYS_REG_SYSINTSRC = 0x24, + MAX77843_SYS_REG_SYSINTMASK = 0x26, + MAX77843_SYS_REG_TOPSYS_STAT = 0x28, + MAX77843_SYS_REG_SAFEOUTCTRL = 0xC6, + + MAX77843_SYS_REG_END, +}; + +enum max77843_haptic_reg { + MAX77843_HAP_REG_MCONFIG = 0x10, + + MAX77843_HAP_REG_END, +}; + +enum max77843_led_reg { + MAX77843_LED_REG_LEDEN = 0x30, + MAX77843_LED_REG_LED0BRT = 0x31, + MAX77843_LED_REG_LED1BRT = 0x32, + MAX77843_LED_REG_LED2BRT = 0x33, + MAX77843_LED_REG_LED3BRT = 0x34, + MAX77843_LED_REG_LEDBLNK = 0x38, + MAX77843_LED_REG_LEDRAMP = 0x36, + + MAX77843_LED_REG_END, +}; + +/* Charger registers */ +enum max77843_charger_reg { + MAX77843_CHG_REG_CHG_INT = 0xB0, + MAX77843_CHG_REG_CHG_INT_MASK = 0xB1, + MAX77843_CHG_REG_CHG_INT_OK = 0xB2, + MAX77843_CHG_REG_CHG_DTLS_00 = 0xB3, + MAX77843_CHG_REG_CHG_DTLS_01 = 0xB4, + MAX77843_CHG_REG_CHG_DTLS_02 = 0xB5, + MAX77843_CHG_REG_CHG_CNFG_00 = 0xB7, + MAX77843_CHG_REG_CHG_CNFG_01 = 0xB8, + MAX77843_CHG_REG_CHG_CNFG_02 = 0xB9, + MAX77843_CHG_REG_CHG_CNFG_03 = 0xBA, + MAX77843_CHG_REG_CHG_CNFG_04 = 0xBB, + MAX77843_CHG_REG_CHG_CNFG_06 = 0xBD, + MAX77843_CHG_REG_CHG_CNFG_07 = 0xBE, + MAX77843_CHG_REG_CHG_CNFG_09 = 0xC0, + MAX77843_CHG_REG_CHG_CNFG_10 = 0xC1, + MAX77843_CHG_REG_CHG_CNFG_11 = 0xC2, + MAX77843_CHG_REG_CHG_CNFG_12 = 0xC3, + + MAX77843_CHG_REG_END, +}; + +/* Fuel gauge registers */ +enum max77843_fuelgauge { + MAX77843_FG_REG_STATUS = 0x00, + MAX77843_FG_REG_VALRT_TH = 0x01, + MAX77843_FG_REG_TALRT_TH = 0x02, + MAX77843_FG_REG_SALRT_TH = 0x03, + MAX77843_FG_RATE_AT_RATE = 0x04, + MAX77843_FG_REG_REMCAP_REP = 0x05, + MAX77843_FG_REG_SOCREP = 0x06, + MAX77843_FG_REG_AGE = 0x07, + MAX77843_FG_REG_TEMP = 0x08, + MAX77843_FG_REG_VCELL = 0x09, + MAX77843_FG_REG_CURRENT = 0x0A, + MAX77843_FG_REG_AVG_CURRENT = 0x0B, + MAX77843_FG_REG_SOCMIX = 0x0D, + MAX77843_FG_REG_SOCAV = 0x0E, + MAX77843_FG_REG_REMCAP_MIX = 0x0F, + MAX77843_FG_REG_FULLCAP = 0x10, + MAX77843_FG_REG_AVG_TEMP = 0x16, + MAX77843_FG_REG_CYCLES = 0x17, + MAX77843_FG_REG_AVG_VCELL = 0x19, + MAX77843_FG_REG_CONFIG = 0x1D, + MAX77843_FG_REG_REMCAP_AV = 0x1F, + MAX77843_FG_REG_FULLCAP_NOM = 0x23, + MAX77843_FG_REG_MISCCFG = 0x2B, + MAX77843_FG_REG_RCOMP = 0x38, + MAX77843_FG_REG_FSTAT = 0x3D, + MAX77843_FG_REG_DQACC = 0x45, + MAX77843_FG_REG_DPACC = 0x46, + MAX77843_FG_REG_OCV = 0xEE, + MAX77843_FG_REG_VFOCV = 0xFB, + MAX77843_FG_SOCVF = 0xFF, + + MAX77843_FG_END, +}; + +/* MUIC registers */ +enum max77843_muic_reg { + MAX77843_MUIC_REG_ID = 0x00, + MAX77843_MUIC_REG_INT1 = 0x01, + MAX77843_MUIC_REG_INT2 = 0x02, + MAX77843_MUIC_REG_INT3 = 0x03, + MAX77843_MUIC_REG_STATUS1 = 0x04, + MAX77843_MUIC_REG_STATUS2 = 0x05, + MAX77843_MUIC_REG_STATUS3 = 0x06, + MAX77843_MUIC_REG_INTMASK1 = 0x07, + MAX77843_MUIC_REG_INTMASK2 = 0x08, + MAX77843_MUIC_REG_INTMASK3 = 0x09, + MAX77843_MUIC_REG_CDETCTRL1 = 0x0A, + MAX77843_MUIC_REG_CDETCTRL2 = 0x0B, + MAX77843_MUIC_REG_CONTROL1 = 0x0C, + MAX77843_MUIC_REG_CONTROL2 = 0x0D, + MAX77843_MUIC_REG_CONTROL3 = 0x0E, + MAX77843_MUIC_REG_CONTROL4 = 0x16, + MAX77843_MUIC_REG_HVCONTROL1 = 0x17, + MAX77843_MUIC_REG_HVCONTROL2 = 0x18, + + MAX77843_MUIC_REG_END, +}; + +enum max77843_irq { + /* Topsys: SYSTEM */ + MAX77843_SYS_IRQ_SYSINTSRC_SYSUVLO_INT, + MAX77843_SYS_IRQ_SYSINTSRC_SYSOVLO_INT, + MAX77843_SYS_IRQ_SYSINTSRC_TSHDN_INT, + MAX77843_SYS_IRQ_SYSINTSRC_TM_INT, + + /* Charger: CHG_INT */ + MAX77843_CHG_IRQ_CHG_INT_BYP_I, + MAX77843_CHG_IRQ_CHG_INT_BATP_I, + MAX77843_CHG_IRQ_CHG_INT_BAT_I, + MAX77843_CHG_IRQ_CHG_INT_CHG_I, + MAX77843_CHG_IRQ_CHG_INT_WCIN_I, + MAX77843_CHG_IRQ_CHG_INT_CHGIN_I, + MAX77843_CHG_IRQ_CHG_INT_AICL_I, + + MAX77843_IRQ_NUM, +}; + +enum max77843_irq_muic { + /* MUIC: INT1 */ + MAX77843_MUIC_IRQ_INT1_ADC, + MAX77843_MUIC_IRQ_INT1_ADCERROR, + MAX77843_MUIC_IRQ_INT1_ADC1K, + + /* MUIC: INT2 */ + MAX77843_MUIC_IRQ_INT2_CHGTYP, + MAX77843_MUIC_IRQ_INT2_CHGDETRUN, + MAX77843_MUIC_IRQ_INT2_DCDTMR, + MAX77843_MUIC_IRQ_INT2_DXOVP, + MAX77843_MUIC_IRQ_INT2_VBVOLT, + + /* MUIC: INT3 */ + MAX77843_MUIC_IRQ_INT3_VBADC, + MAX77843_MUIC_IRQ_INT3_VDNMON, + MAX77843_MUIC_IRQ_INT3_DNRES, + MAX77843_MUIC_IRQ_INT3_MPNACK, + MAX77843_MUIC_IRQ_INT3_MRXBUFOW, + MAX77843_MUIC_IRQ_INT3_MRXTRF, + MAX77843_MUIC_IRQ_INT3_MRXPERR, + MAX77843_MUIC_IRQ_INT3_MRXRDY, + + MAX77843_MUIC_IRQ_NUM, +}; + +/* MAX77843 interrupts */ +#define MAX77843_SYS_IRQ_SYSUVLO_INT BIT(0) +#define MAX77843_SYS_IRQ_SYSOVLO_INT BIT(1) +#define MAX77843_SYS_IRQ_TSHDN_INT BIT(2) +#define MAX77843_SYS_IRQ_TM_INT BIT(3) + +/* MAX77843 MAINCTRL1 register */ +#define MAINCTRL1_BIASEN_SHIFT 7 +#define MAX77843_MAINCTRL1_BIASEN_MASK BIT(MAINCTRL1_BIASEN_SHIFT) + +/* MAX77843 MCONFIG register */ +#define MCONFIG_MODE_SHIFT 7 +#define MCONFIG_MEN_SHIFT 6 +#define MCONFIG_PDIV_SHIFT 0 + +#define MAX77843_MCONFIG_MODE_MASK BIT(MCONFIG_MODE_SHIFT) +#define MAX77843_MCONFIG_MEN_MASK BIT(MCONFIG_MEN_SHIFT) +#define MAX77843_MCONFIG_PDIV_MASK (0x3 << MCONFIG_PDIV_SHIFT) + +/* Max77843 charger insterrupts */ +#define MAX77843_CHG_BYP_I BIT(0) +#define MAX77843_CHG_BATP_I BIT(2) +#define MAX77843_CHG_BAT_I BIT(3) +#define MAX77843_CHG_CHG_I BIT(4) +#define MAX77843_CHG_WCIN_I BIT(5) +#define MAX77843_CHG_CHGIN_I BIT(6) +#define MAX77843_CHG_AICL_I BIT(7) + +/* MAX77843 CHG_INT_OK register */ +#define MAX77843_CHG_BYP_OK BIT(0) +#define MAX77843_CHG_BATP_OK BIT(2) +#define MAX77843_CHG_BAT_OK BIT(3) +#define MAX77843_CHG_CHG_OK BIT(4) +#define MAX77843_CHG_WCIN_OK BIT(5) +#define MAX77843_CHG_CHGIN_OK BIT(6) +#define MAX77843_CHG_AICL_OK BIT(7) + +/* MAX77843 CHG_DETAILS_00 register */ +#define MAX77843_CHG_BAT_DTLS BIT(0) + +/* MAX77843 CHG_DETAILS_01 register */ +#define MAX77843_CHG_DTLS_MASK 0x0f +#define MAX77843_CHG_PQ_MODE 0x00 +#define MAX77843_CHG_CC_MODE 0x01 +#define MAX77843_CHG_CV_MODE 0x02 +#define MAX77843_CHG_TO_MODE 0x03 +#define MAX77843_CHG_DO_MODE 0x04 +#define MAX77843_CHG_HT_MODE 0x05 +#define MAX77843_CHG_TF_MODE 0x06 +#define MAX77843_CHG_TS_MODE 0x07 +#define MAX77843_CHG_OFF_MODE 0x08 + +#define MAX77843_CHG_BAT_DTLS_MASK 0xf0 +#define MAX77843_CHG_NO_BAT (0x00 << 4) +#define MAX77843_CHG_LOW_VOLT_BAT (0x01 << 4) +#define MAX77843_CHG_LONG_BAT_TIME (0x02 << 4) +#define MAX77843_CHG_OK_BAT (0x03 << 4) +#define MAX77843_CHG_OK_LOW_VOLT_BAT (0x04 << 4) +#define MAX77843_CHG_OVER_VOLT_BAT (0x05 << 4) +#define MAX77843_CHG_OVER_CURRENT_BAT (0x06 << 4) + +/* MAX77843 CHG_CNFG_00 register */ +#define MAX77843_CHG_DISABLE 0x00 +#define MAX77843_CHG_ENABLE 0x05 +#define MAX77843_CHG_MASK 0x01 +#define MAX77843_CHG_BUCK_MASK 0x04 + +/* MAX77843 CHG_CNFG_01 register */ +#define MAX77843_CHG_RESTART_THRESHOLD_100 0x00 +#define MAX77843_CHG_RESTART_THRESHOLD_150 0x10 +#define MAX77843_CHG_RESTART_THRESHOLD_200 0x20 +#define MAX77843_CHG_RESTART_THRESHOLD_DISABLE 0x30 + +/* MAX77843 CHG_CNFG_02 register */ +#define MAX77843_CHG_FAST_CHG_CURRENT_MIN 100000 +#define MAX77843_CHG_FAST_CHG_CURRENT_MAX 3150000 +#define MAX77843_CHG_FAST_CHG_CURRENT_STEP 50000 +#define MAX77843_CHG_FAST_CHG_CURRENT_MASK 0x3f +#define MAX77843_CHG_OTG_ILIMIT_500 (0x00 << 6) +#define MAX77843_CHG_OTG_ILIMIT_900 (0x01 << 6) +#define MAX77843_CHG_OTG_ILIMIT_1200 (0x02 << 6) +#define MAX77843_CHG_OTG_ILIMIT_1500 (0x03 << 6) +#define MAX77843_CHG_OTG_ILIMIT_MASK 0xc0 + +/* MAX77843 CHG_CNFG_03 register */ +#define MAX77843_CHG_TOP_OFF_CURRENT_MIN 125000 +#define MAX77843_CHG_TOP_OFF_CURRENT_MAX 650000 +#define MAX77843_CHG_TOP_OFF_CURRENT_STEP 75000 +#define MAX77843_CHG_TOP_OFF_CURRENT_MASK 0x07 + +/* MAX77843 CHG_CNFG_06 register */ +#define MAX77843_CHG_WRITE_CAP_BLOCK 0x10 +#define MAX77843_CHG_WRITE_CAP_UNBLOCK 0x0C + +/* MAX77843_CHG_CNFG_09_register */ +#define MAX77843_CHG_INPUT_CURRENT_LIMIT_MIN 100000 +#define MAX77843_CHG_INPUT_CURRENT_LIMIT_MAX 4000000 +#define MAX77843_CHG_INPUT_CURRENT_LIMIT_REF 3367000 +#define MAX77843_CHG_INPUT_CURRENT_LIMIT_STEP 33000 + +#define MAX77843_MUIC_ADC BIT(0) +#define MAX77843_MUIC_ADCERROR BIT(2) +#define MAX77843_MUIC_ADC1K BIT(3) + +#define MAX77843_MUIC_CHGTYP BIT(0) +#define MAX77843_MUIC_CHGDETRUN BIT(1) +#define MAX77843_MUIC_DCDTMR BIT(2) +#define MAX77843_MUIC_DXOVP BIT(3) +#define MAX77843_MUIC_VBVOLT BIT(4) + +#define MAX77843_MUIC_VBADC BIT(0) +#define MAX77843_MUIC_VDNMON BIT(1) +#define MAX77843_MUIC_DNRES BIT(2) +#define MAX77843_MUIC_MPNACK BIT(3) +#define MAX77843_MUIC_MRXBUFOW BIT(4) +#define MAX77843_MUIC_MRXTRF BIT(5) +#define MAX77843_MUIC_MRXPERR BIT(6) +#define MAX77843_MUIC_MRXRDY BIT(7) + +/* MAX77843 INTSRCMASK register */ +#define MAX77843_INTSRCMASK_CHGR 0 +#define MAX77843_INTSRCMASK_SYS 1 +#define MAX77843_INTSRCMASK_FG 2 +#define MAX77843_INTSRCMASK_MUIC 3 + +#define MAX77843_INTSRCMASK_CHGR_MASK BIT(MAX77843_INTSRCMASK_CHGR) +#define MAX77843_INTSRCMASK_SYS_MASK BIT(MAX77843_INTSRCMASK_SYS) +#define MAX77843_INTSRCMASK_FG_MASK BIT(MAX77843_INTSRCMASK_FG) +#define MAX77843_INTSRCMASK_MUIC_MASK BIT(MAX77843_INTSRCMASK_MUIC) + +#define MAX77843_INTSRC_MASK_MASK \ + (MAX77843_INTSRCMASK_MUIC_MASK | MAX77843_INTSRCMASK_FG_MASK | \ + MAX77843_INTSRCMASK_SYS_MASK | MAX77843_INTSRCMASK_CHGR_MASK) + +/* MAX77843 STATUS register*/ +#define STATUS1_ADC_SHIFT 0 +#define STATUS1_ADCERROR_SHIFT 6 +#define STATUS1_ADC1K_SHIFT 7 +#define STATUS2_CHGTYP_SHIFT 0 +#define STATUS2_CHGDETRUN_SHIFT 3 +#define STATUS2_DCDTMR_SHIFT 4 +#define STATUS2_DXOVP_SHIFT 5 +#define STATUS2_VBVOLT_SHIFT 6 +#define STATUS3_VBADC_SHIFT 0 +#define STATUS3_VDNMON_SHIFT 4 +#define STATUS3_DNRES_SHIFT 5 +#define STATUS3_MPNACK_SHIFT 6 + +#define MAX77843_MUIC_STATUS1_ADC_MASK (0x1f << STATUS1_ADC_SHIFT) +#define MAX77843_MUIC_STATUS1_ADCERROR_MASK BIT(STATUS1_ADCERROR_SHIFT) +#define MAX77843_MUIC_STATUS1_ADC1K_MASK BIT(STATUS1_ADC1K_SHIFT) +#define MAX77843_MUIC_STATUS2_CHGTYP_MASK (0x7 << STATUS2_CHGTYP_SHIFT) +#define MAX77843_MUIC_STATUS2_CHGDETRUN_MASK BIT(STATUS2_CHGDETRUN_SHIFT) +#define MAX77843_MUIC_STATUS2_DCDTMR_MASK BIT(STATUS2_DCDTMR_SHIFT) +#define MAX77843_MUIC_STATUS2_DXOVP_MASK BIT(STATUS2_DXOVP_SHIFT) +#define MAX77843_MUIC_STATUS2_VBVOLT_MASK BIT(STATUS2_VBVOLT_SHIFT) +#define MAX77843_MUIC_STATUS3_VBADC_MASK (0xf << STATUS3_VBADC_SHIFT) +#define MAX77843_MUIC_STATUS3_VDNMON_MASK BIT(STATUS3_VDNMON_SHIFT) +#define MAX77843_MUIC_STATUS3_DNRES_MASK BIT(STATUS3_DNRES_SHIFT) +#define MAX77843_MUIC_STATUS3_MPNACK_MASK BIT(STATUS3_MPNACK_SHIFT) + +/* MAX77843 CONTROL register */ +#define CONTROL1_COMP1SW_SHIFT 0 +#define CONTROL1_COMP2SW_SHIFT 3 +#define CONTROL1_IDBEN_SHIFT 7 +#define CONTROL2_LOWPWR_SHIFT 0 +#define CONTROL2_ADCEN_SHIFT 1 +#define CONTROL2_CPEN_SHIFT 2 +#define CONTROL2_ACC_DET_SHIFT 5 +#define CONTROL2_USBCPINT_SHIFT 6 +#define CONTROL2_RCPS_SHIFT 7 +#define CONTROL3_JIGSET_SHIFT 0 +#define CONTROL4_ADCDBSET_SHIFT 0 +#define CONTROL4_USBAUTO_SHIFT 4 +#define CONTROL4_FCTAUTO_SHIFT 5 +#define CONTROL4_ADCMODE_SHIFT 6 + +#define MAX77843_MUIC_CONTROL1_COMP1SW_MASK (0x7 << CONTROL1_COMP1SW_SHIFT) +#define MAX77843_MUIC_CONTROL1_COMP2SW_MASK (0x7 << CONTROL1_COMP2SW_SHIFT) +#define MAX77843_MUIC_CONTROL1_IDBEN_MASK BIT(CONTROL1_IDBEN_SHIFT) +#define MAX77843_MUIC_CONTROL2_LOWPWR_MASK BIT(CONTROL2_LOWPWR_SHIFT) +#define MAX77843_MUIC_CONTROL2_ADCEN_MASK BIT(CONTROL2_ADCEN_SHIFT) +#define MAX77843_MUIC_CONTROL2_CPEN_MASK BIT(CONTROL2_CPEN_SHIFT) +#define MAX77843_MUIC_CONTROL2_ACC_DET_MASK BIT(CONTROL2_ACC_DET_SHIFT) +#define MAX77843_MUIC_CONTROL2_USBCPINT_MASK BIT(CONTROL2_USBCPINT_SHIFT) +#define MAX77843_MUIC_CONTROL2_RCPS_MASK BIT(CONTROL2_RCPS_SHIFT) +#define MAX77843_MUIC_CONTROL3_JIGSET_MASK (0x3 << CONTROL3_JIGSET_SHIFT) +#define MAX77843_MUIC_CONTROL4_ADCDBSET_MASK (0x3 << CONTROL4_ADCDBSET_SHIFT) +#define MAX77843_MUIC_CONTROL4_USBAUTO_MASK BIT(CONTROL4_USBAUTO_SHIFT) +#define MAX77843_MUIC_CONTROL4_FCTAUTO_MASK BIT(CONTROL4_FCTAUTO_SHIFT) +#define MAX77843_MUIC_CONTROL4_ADCMODE_MASK (0x3 << CONTROL4_ADCMODE_SHIFT) + +/* MAX77843 switch port */ +#define COM_OPEN 0 +#define COM_USB 1 +#define COM_AUDIO 2 +#define COM_UART 3 +#define COM_AUX_USB 4 +#define COM_AUX_UART 5 + +#define CONTROL1_COM_SW \ + ((MAX77843_MUIC_CONTROL1_COMP1SW_MASK | \ + MAX77843_MUIC_CONTROL1_COMP2SW_MASK)) + +#define CONTROL1_SW_OPEN \ + ((COM_OPEN << CONTROL1_COMP1SW_SHIFT | \ + COM_OPEN << CONTROL1_COMP2SW_SHIFT)) +#define CONTROL1_SW_USB \ + ((COM_USB << CONTROL1_COMP1SW_SHIFT | \ + COM_USB << CONTROL1_COMP2SW_SHIFT)) +#define CONTROL1_SW_AUDIO \ + ((COM_AUDIO << CONTROL1_COMP1SW_SHIFT | \ + COM_AUDIO << CONTROL1_COMP2SW_SHIFT)) +#define CONTROL1_SW_UART \ + ((COM_UART << CONTROL1_COMP1SW_SHIFT | \ + COM_UART << CONTROL1_COMP2SW_SHIFT)) +#define CONTROL1_SW_AUX_USB \ + ((COM_AUX_USB << CONTROL1_COMP1SW_SHIFT | \ + COM_AUX_USB << CONTROL1_COMP2SW_SHIFT)) +#define CONTROL1_SW_AUX_UART \ + ((COM_AUX_UART << CONTROL1_COMP1SW_SHIFT | \ + COM_AUX_UART << CONTROL1_COMP2SW_SHIFT)) + +#define MAX77843_DISABLE 0 +#define MAX77843_ENABLE 1 + +#define CONTROL4_AUTO_DISABLE \ + ((MAX77843_DISABLE << CONTROL4_USBAUTO_SHIFT) | \ + (MAX77843_DISABLE << CONTROL4_FCTAUTO_SHIFT)) +#define CONTROL4_AUTO_ENABLE \ + ((MAX77843_ENABLE << CONTROL4_USBAUTO_SHIFT) | \ + (MAX77843_ENABLE << CONTROL4_FCTAUTO_SHIFT)) + +/* MAX77843 SAFEOUT LDO Control register */ +#define SAFEOUTCTRL_SAFEOUT1_SHIFT 0 +#define SAFEOUTCTRL_SAFEOUT2_SHIFT 2 +#define SAFEOUTCTRL_ENSAFEOUT1_SHIFT 6 +#define SAFEOUTCTRL_ENSAFEOUT2_SHIFT 7 + +#define MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT1 \ + BIT(SAFEOUTCTRL_ENSAFEOUT1_SHIFT) +#define MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT2 \ + BIT(SAFEOUTCTRL_ENSAFEOUT2_SHIFT) +#define MAX77843_REG_SAFEOUTCTRL_SAFEOUT1_MASK \ + (0x3 << SAFEOUTCTRL_SAFEOUT1_SHIFT) +#define MAX77843_REG_SAFEOUTCTRL_SAFEOUT2_MASK \ + (0x3 << SAFEOUTCTRL_SAFEOUT2_SHIFT) + +struct max77843 { + struct device *dev; + + struct i2c_client *i2c; + struct i2c_client *i2c_chg; + struct i2c_client *i2c_fuel; + struct i2c_client *i2c_muic; + + struct regmap *regmap; + struct regmap *regmap_chg; + struct regmap *regmap_fuel; + struct regmap *regmap_muic; + + struct regmap_irq_chip_data *irq_data; + struct regmap_irq_chip_data *irq_data_chg; + struct regmap_irq_chip_data *irq_data_fuel; + struct regmap_irq_chip_data *irq_data_muic; + + int irq; +}; +#endif /* __MAX77843_H__ */ diff --git a/include/linux/mfd/menelaus.h b/include/linux/mfd/menelaus.h index f097e89134cbf5..9e85ac06da89d9 100644 --- a/include/linux/mfd/menelaus.h +++ b/include/linux/mfd/menelaus.h @@ -24,7 +24,6 @@ extern int menelaus_set_vaux(unsigned int mV); extern int menelaus_set_vdcdc(int dcdc, unsigned int mV); extern int menelaus_set_slot_sel(int enable); extern int menelaus_get_slot_pin_states(void); -extern int menelaus_set_vcore_sw(unsigned int mV); extern int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV); #define EN_VPLL_SLEEP (1 << 7) @@ -38,10 +37,4 @@ extern int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV); extern int menelaus_set_regulator_sleep(int enable, u32 val); -#if defined(CONFIG_ARCH_OMAP2) && defined(CONFIG_MENELAUS) -#define omap_has_menelaus() 1 -#else -#define omap_has_menelaus() 0 -#endif - #endif diff --git a/include/linux/mfd/mt6397/core.h b/include/linux/mfd/mt6397/core.h new file mode 100644 index 00000000000000..cf5265b0d1c189 --- /dev/null +++ b/include/linux/mfd/mt6397/core.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu, MediaTek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MFD_MT6397_CORE_H__ +#define __MFD_MT6397_CORE_H__ + +enum mt6397_irq_numbers { + MT6397_IRQ_SPKL_AB = 0, + MT6397_IRQ_SPKR_AB, + MT6397_IRQ_SPKL, + MT6397_IRQ_SPKR, + MT6397_IRQ_BAT_L, + MT6397_IRQ_BAT_H, + MT6397_IRQ_FG_BAT_L, + MT6397_IRQ_FG_BAT_H, + MT6397_IRQ_WATCHDOG, + MT6397_IRQ_PWRKEY, + MT6397_IRQ_THR_L, + MT6397_IRQ_THR_H, + MT6397_IRQ_VBATON_UNDET, + MT6397_IRQ_BVALID_DET, + MT6397_IRQ_CHRDET, + MT6397_IRQ_OV, + MT6397_IRQ_LDO, + MT6397_IRQ_HOMEKEY, + MT6397_IRQ_ACCDET, + MT6397_IRQ_AUDIO, + MT6397_IRQ_RTC, + MT6397_IRQ_PWRKEY_RSTB, + MT6397_IRQ_HDMI_SIFM, + MT6397_IRQ_HDMI_CEC, + MT6397_IRQ_VCA15, + MT6397_IRQ_VSRMCA15, + MT6397_IRQ_VCORE, + MT6397_IRQ_VGPU, + MT6397_IRQ_VIO18, + MT6397_IRQ_VPCA7, + MT6397_IRQ_VSRMCA7, + MT6397_IRQ_VDRM, + MT6397_IRQ_NR, +}; + +struct mt6397_chip { + struct device *dev; + struct regmap *regmap; + int irq; + struct irq_domain *irq_domain; + struct mutex irqlock; + u16 irq_masks_cur[2]; + u16 irq_masks_cache[2]; +}; + +#endif /* __MFD_MT6397_CORE_H__ */ diff --git a/include/linux/mfd/mt6397/registers.h b/include/linux/mfd/mt6397/registers.h new file mode 100644 index 00000000000000..f23a0a60a87729 --- /dev/null +++ b/include/linux/mfd/mt6397/registers.h @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu, MediaTek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MFD_MT6397_REGISTERS_H__ +#define __MFD_MT6397_REGISTERS_H__ + +/* PMIC Registers */ +#define MT6397_CID 0x0100 +#define MT6397_TOP_CKPDN 0x0102 +#define MT6397_TOP_CKPDN_SET 0x0104 +#define MT6397_TOP_CKPDN_CLR 0x0106 +#define MT6397_TOP_CKPDN2 0x0108 +#define MT6397_TOP_CKPDN2_SET 0x010A +#define MT6397_TOP_CKPDN2_CLR 0x010C +#define MT6397_TOP_GPIO_CKPDN 0x010E +#define MT6397_TOP_RST_CON 0x0114 +#define MT6397_WRP_CKPDN 0x011A +#define MT6397_WRP_RST_CON 0x0120 +#define MT6397_TOP_RST_MISC 0x0126 +#define MT6397_TOP_CKCON1 0x0128 +#define MT6397_TOP_CKCON2 0x012A +#define MT6397_TOP_CKTST1 0x012C +#define MT6397_TOP_CKTST2 0x012E +#define MT6397_OC_DEG_EN 0x0130 +#define MT6397_OC_CTL0 0x0132 +#define MT6397_OC_CTL1 0x0134 +#define MT6397_OC_CTL2 0x0136 +#define MT6397_INT_RSV 0x0138 +#define MT6397_TEST_CON0 0x013A +#define MT6397_TEST_CON1 0x013C +#define MT6397_STATUS0 0x013E +#define MT6397_STATUS1 0x0140 +#define MT6397_PGSTATUS 0x0142 +#define MT6397_CHRSTATUS 0x0144 +#define MT6397_OCSTATUS0 0x0146 +#define MT6397_OCSTATUS1 0x0148 +#define MT6397_OCSTATUS2 0x014A +#define MT6397_HDMI_PAD_IE 0x014C +#define MT6397_TEST_OUT_L 0x014E +#define MT6397_TEST_OUT_H 0x0150 +#define MT6397_TDSEL_CON 0x0152 +#define MT6397_RDSEL_CON 0x0154 +#define MT6397_GPIO_SMT_CON0 0x0156 +#define MT6397_GPIO_SMT_CON1 0x0158 +#define MT6397_GPIO_SMT_CON2 0x015A +#define MT6397_GPIO_SMT_CON3 0x015C +#define MT6397_DRV_CON0 0x015E +#define MT6397_DRV_CON1 0x0160 +#define MT6397_DRV_CON2 0x0162 +#define MT6397_DRV_CON3 0x0164 +#define MT6397_DRV_CON4 0x0166 +#define MT6397_DRV_CON5 0x0168 +#define MT6397_DRV_CON6 0x016A +#define MT6397_DRV_CON7 0x016C +#define MT6397_DRV_CON8 0x016E +#define MT6397_DRV_CON9 0x0170 +#define MT6397_DRV_CON10 0x0172 +#define MT6397_DRV_CON11 0x0174 +#define MT6397_DRV_CON12 0x0176 +#define MT6397_INT_CON0 0x0178 +#define MT6397_INT_CON1 0x017E +#define MT6397_INT_STATUS0 0x0184 +#define MT6397_INT_STATUS1 0x0186 +#define MT6397_FQMTR_CON0 0x0188 +#define MT6397_FQMTR_CON1 0x018A +#define MT6397_FQMTR_CON2 0x018C +#define MT6397_EFUSE_DOUT_0_15 0x01C4 +#define MT6397_EFUSE_DOUT_16_31 0x01C6 +#define MT6397_EFUSE_DOUT_32_47 0x01C8 +#define MT6397_EFUSE_DOUT_48_63 0x01CA +#define MT6397_SPI_CON 0x01CC +#define MT6397_TOP_CKPDN3 0x01CE +#define MT6397_TOP_CKCON3 0x01D4 +#define MT6397_EFUSE_DOUT_64_79 0x01D6 +#define MT6397_EFUSE_DOUT_80_95 0x01D8 +#define MT6397_EFUSE_DOUT_96_111 0x01DA +#define MT6397_EFUSE_DOUT_112_127 0x01DC +#define MT6397_EFUSE_DOUT_128_143 0x01DE +#define MT6397_EFUSE_DOUT_144_159 0x01E0 +#define MT6397_EFUSE_DOUT_160_175 0x01E2 +#define MT6397_EFUSE_DOUT_176_191 0x01E4 +#define MT6397_EFUSE_DOUT_192_207 0x01E6 +#define MT6397_EFUSE_DOUT_208_223 0x01E8 +#define MT6397_EFUSE_DOUT_224_239 0x01EA +#define MT6397_EFUSE_DOUT_240_255 0x01EC +#define MT6397_EFUSE_DOUT_256_271 0x01EE +#define MT6397_EFUSE_DOUT_272_287 0x01F0 +#define MT6397_EFUSE_DOUT_288_300 0x01F2 +#define MT6397_EFUSE_DOUT_304_319 0x01F4 +#define MT6397_BUCK_CON0 0x0200 +#define MT6397_BUCK_CON1 0x0202 +#define MT6397_BUCK_CON2 0x0204 +#define MT6397_BUCK_CON3 0x0206 +#define MT6397_BUCK_CON4 0x0208 +#define MT6397_BUCK_CON5 0x020A +#define MT6397_BUCK_CON6 0x020C +#define MT6397_BUCK_CON7 0x020E +#define MT6397_BUCK_CON8 0x0210 +#define MT6397_BUCK_CON9 0x0212 +#define MT6397_VCA15_CON0 0x0214 +#define MT6397_VCA15_CON1 0x0216 +#define MT6397_VCA15_CON2 0x0218 +#define MT6397_VCA15_CON3 0x021A +#define MT6397_VCA15_CON4 0x021C +#define MT6397_VCA15_CON5 0x021E +#define MT6397_VCA15_CON6 0x0220 +#define MT6397_VCA15_CON7 0x0222 +#define MT6397_VCA15_CON8 0x0224 +#define MT6397_VCA15_CON9 0x0226 +#define MT6397_VCA15_CON10 0x0228 +#define MT6397_VCA15_CON11 0x022A +#define MT6397_VCA15_CON12 0x022C +#define MT6397_VCA15_CON13 0x022E +#define MT6397_VCA15_CON14 0x0230 +#define MT6397_VCA15_CON15 0x0232 +#define MT6397_VCA15_CON16 0x0234 +#define MT6397_VCA15_CON17 0x0236 +#define MT6397_VCA15_CON18 0x0238 +#define MT6397_VSRMCA15_CON0 0x023A +#define MT6397_VSRMCA15_CON1 0x023C +#define MT6397_VSRMCA15_CON2 0x023E +#define MT6397_VSRMCA15_CON3 0x0240 +#define MT6397_VSRMCA15_CON4 0x0242 +#define MT6397_VSRMCA15_CON5 0x0244 +#define MT6397_VSRMCA15_CON6 0x0246 +#define MT6397_VSRMCA15_CON7 0x0248 +#define MT6397_VSRMCA15_CON8 0x024A +#define MT6397_VSRMCA15_CON9 0x024C +#define MT6397_VSRMCA15_CON10 0x024E +#define MT6397_VSRMCA15_CON11 0x0250 +#define MT6397_VSRMCA15_CON12 0x0252 +#define MT6397_VSRMCA15_CON13 0x0254 +#define MT6397_VSRMCA15_CON14 0x0256 +#define MT6397_VSRMCA15_CON15 0x0258 +#define MT6397_VSRMCA15_CON16 0x025A +#define MT6397_VSRMCA15_CON17 0x025C +#define MT6397_VSRMCA15_CON18 0x025E +#define MT6397_VSRMCA15_CON19 0x0260 +#define MT6397_VSRMCA15_CON20 0x0262 +#define MT6397_VSRMCA15_CON21 0x0264 +#define MT6397_VCORE_CON0 0x0266 +#define MT6397_VCORE_CON1 0x0268 +#define MT6397_VCORE_CON2 0x026A +#define MT6397_VCORE_CON3 0x026C +#define MT6397_VCORE_CON4 0x026E +#define MT6397_VCORE_CON5 0x0270 +#define MT6397_VCORE_CON6 0x0272 +#define MT6397_VCORE_CON7 0x0274 +#define MT6397_VCORE_CON8 0x0276 +#define MT6397_VCORE_CON9 0x0278 +#define MT6397_VCORE_CON10 0x027A +#define MT6397_VCORE_CON11 0x027C +#define MT6397_VCORE_CON12 0x027E +#define MT6397_VCORE_CON13 0x0280 +#define MT6397_VCORE_CON14 0x0282 +#define MT6397_VCORE_CON15 0x0284 +#define MT6397_VCORE_CON16 0x0286 +#define MT6397_VCORE_CON17 0x0288 +#define MT6397_VCORE_CON18 0x028A +#define MT6397_VGPU_CON0 0x028C +#define MT6397_VGPU_CON1 0x028E +#define MT6397_VGPU_CON2 0x0290 +#define MT6397_VGPU_CON3 0x0292 +#define MT6397_VGPU_CON4 0x0294 +#define MT6397_VGPU_CON5 0x0296 +#define MT6397_VGPU_CON6 0x0298 +#define MT6397_VGPU_CON7 0x029A +#define MT6397_VGPU_CON8 0x029C +#define MT6397_VGPU_CON9 0x029E +#define MT6397_VGPU_CON10 0x02A0 +#define MT6397_VGPU_CON11 0x02A2 +#define MT6397_VGPU_CON12 0x02A4 +#define MT6397_VGPU_CON13 0x02A6 +#define MT6397_VGPU_CON14 0x02A8 +#define MT6397_VGPU_CON15 0x02AA +#define MT6397_VGPU_CON16 0x02AC +#define MT6397_VGPU_CON17 0x02AE +#define MT6397_VGPU_CON18 0x02B0 +#define MT6397_VIO18_CON0 0x0300 +#define MT6397_VIO18_CON1 0x0302 +#define MT6397_VIO18_CON2 0x0304 +#define MT6397_VIO18_CON3 0x0306 +#define MT6397_VIO18_CON4 0x0308 +#define MT6397_VIO18_CON5 0x030A +#define MT6397_VIO18_CON6 0x030C +#define MT6397_VIO18_CON7 0x030E +#define MT6397_VIO18_CON8 0x0310 +#define MT6397_VIO18_CON9 0x0312 +#define MT6397_VIO18_CON10 0x0314 +#define MT6397_VIO18_CON11 0x0316 +#define MT6397_VIO18_CON12 0x0318 +#define MT6397_VIO18_CON13 0x031A +#define MT6397_VIO18_CON14 0x031C +#define MT6397_VIO18_CON15 0x031E +#define MT6397_VIO18_CON16 0x0320 +#define MT6397_VIO18_CON17 0x0322 +#define MT6397_VIO18_CON18 0x0324 +#define MT6397_VPCA7_CON0 0x0326 +#define MT6397_VPCA7_CON1 0x0328 +#define MT6397_VPCA7_CON2 0x032A +#define MT6397_VPCA7_CON3 0x032C +#define MT6397_VPCA7_CON4 0x032E +#define MT6397_VPCA7_CON5 0x0330 +#define MT6397_VPCA7_CON6 0x0332 +#define MT6397_VPCA7_CON7 0x0334 +#define MT6397_VPCA7_CON8 0x0336 +#define MT6397_VPCA7_CON9 0x0338 +#define MT6397_VPCA7_CON10 0x033A +#define MT6397_VPCA7_CON11 0x033C +#define MT6397_VPCA7_CON12 0x033E +#define MT6397_VPCA7_CON13 0x0340 +#define MT6397_VPCA7_CON14 0x0342 +#define MT6397_VPCA7_CON15 0x0344 +#define MT6397_VPCA7_CON16 0x0346 +#define MT6397_VPCA7_CON17 0x0348 +#define MT6397_VPCA7_CON18 0x034A +#define MT6397_VSRMCA7_CON0 0x034C +#define MT6397_VSRMCA7_CON1 0x034E +#define MT6397_VSRMCA7_CON2 0x0350 +#define MT6397_VSRMCA7_CON3 0x0352 +#define MT6397_VSRMCA7_CON4 0x0354 +#define MT6397_VSRMCA7_CON5 0x0356 +#define MT6397_VSRMCA7_CON6 0x0358 +#define MT6397_VSRMCA7_CON7 0x035A +#define MT6397_VSRMCA7_CON8 0x035C +#define MT6397_VSRMCA7_CON9 0x035E +#define MT6397_VSRMCA7_CON10 0x0360 +#define MT6397_VSRMCA7_CON11 0x0362 +#define MT6397_VSRMCA7_CON12 0x0364 +#define MT6397_VSRMCA7_CON13 0x0366 +#define MT6397_VSRMCA7_CON14 0x0368 +#define MT6397_VSRMCA7_CON15 0x036A +#define MT6397_VSRMCA7_CON16 0x036C +#define MT6397_VSRMCA7_CON17 0x036E +#define MT6397_VSRMCA7_CON18 0x0370 +#define MT6397_VSRMCA7_CON19 0x0372 +#define MT6397_VSRMCA7_CON20 0x0374 +#define MT6397_VSRMCA7_CON21 0x0376 +#define MT6397_VDRM_CON0 0x0378 +#define MT6397_VDRM_CON1 0x037A +#define MT6397_VDRM_CON2 0x037C +#define MT6397_VDRM_CON3 0x037E +#define MT6397_VDRM_CON4 0x0380 +#define MT6397_VDRM_CON5 0x0382 +#define MT6397_VDRM_CON6 0x0384 +#define MT6397_VDRM_CON7 0x0386 +#define MT6397_VDRM_CON8 0x0388 +#define MT6397_VDRM_CON9 0x038A +#define MT6397_VDRM_CON10 0x038C +#define MT6397_VDRM_CON11 0x038E +#define MT6397_VDRM_CON12 0x0390 +#define MT6397_VDRM_CON13 0x0392 +#define MT6397_VDRM_CON14 0x0394 +#define MT6397_VDRM_CON15 0x0396 +#define MT6397_VDRM_CON16 0x0398 +#define MT6397_VDRM_CON17 0x039A +#define MT6397_VDRM_CON18 0x039C +#define MT6397_BUCK_K_CON0 0x039E +#define MT6397_BUCK_K_CON1 0x03A0 +#define MT6397_ANALDO_CON0 0x0400 +#define MT6397_ANALDO_CON1 0x0402 +#define MT6397_ANALDO_CON2 0x0404 +#define MT6397_ANALDO_CON3 0x0406 +#define MT6397_ANALDO_CON4 0x0408 +#define MT6397_ANALDO_CON5 0x040A +#define MT6397_ANALDO_CON6 0x040C +#define MT6397_ANALDO_CON7 0x040E +#define MT6397_DIGLDO_CON0 0x0410 +#define MT6397_DIGLDO_CON1 0x0412 +#define MT6397_DIGLDO_CON2 0x0414 +#define MT6397_DIGLDO_CON3 0x0416 +#define MT6397_DIGLDO_CON4 0x0418 +#define MT6397_DIGLDO_CON5 0x041A +#define MT6397_DIGLDO_CON6 0x041C +#define MT6397_DIGLDO_CON7 0x041E +#define MT6397_DIGLDO_CON8 0x0420 +#define MT6397_DIGLDO_CON9 0x0422 +#define MT6397_DIGLDO_CON10 0x0424 +#define MT6397_DIGLDO_CON11 0x0426 +#define MT6397_DIGLDO_CON12 0x0428 +#define MT6397_DIGLDO_CON13 0x042A +#define MT6397_DIGLDO_CON14 0x042C +#define MT6397_DIGLDO_CON15 0x042E +#define MT6397_DIGLDO_CON16 0x0430 +#define MT6397_DIGLDO_CON17 0x0432 +#define MT6397_DIGLDO_CON18 0x0434 +#define MT6397_DIGLDO_CON19 0x0436 +#define MT6397_DIGLDO_CON20 0x0438 +#define MT6397_DIGLDO_CON21 0x043A +#define MT6397_DIGLDO_CON22 0x043C +#define MT6397_DIGLDO_CON23 0x043E +#define MT6397_DIGLDO_CON24 0x0440 +#define MT6397_DIGLDO_CON25 0x0442 +#define MT6397_DIGLDO_CON26 0x0444 +#define MT6397_DIGLDO_CON27 0x0446 +#define MT6397_DIGLDO_CON28 0x0448 +#define MT6397_DIGLDO_CON29 0x044A +#define MT6397_DIGLDO_CON30 0x044C +#define MT6397_DIGLDO_CON31 0x044E +#define MT6397_DIGLDO_CON32 0x0450 +#define MT6397_DIGLDO_CON33 0x045A +#define MT6397_SPK_CON0 0x0600 +#define MT6397_SPK_CON1 0x0602 +#define MT6397_SPK_CON2 0x0604 +#define MT6397_SPK_CON3 0x0606 +#define MT6397_SPK_CON4 0x0608 +#define MT6397_SPK_CON5 0x060A +#define MT6397_SPK_CON6 0x060C +#define MT6397_SPK_CON7 0x060E +#define MT6397_SPK_CON8 0x0610 +#define MT6397_SPK_CON9 0x0612 +#define MT6397_SPK_CON10 0x0614 +#define MT6397_SPK_CON11 0x0616 +#define MT6397_AUDDAC_CON0 0x0700 +#define MT6397_AUDBUF_CFG0 0x0702 +#define MT6397_AUDBUF_CFG1 0x0704 +#define MT6397_AUDBUF_CFG2 0x0706 +#define MT6397_AUDBUF_CFG3 0x0708 +#define MT6397_AUDBUF_CFG4 0x070A +#define MT6397_IBIASDIST_CFG0 0x070C +#define MT6397_AUDACCDEPOP_CFG0 0x070E +#define MT6397_AUD_IV_CFG0 0x0710 +#define MT6397_AUDCLKGEN_CFG0 0x0712 +#define MT6397_AUDLDO_CFG0 0x0714 +#define MT6397_AUDLDO_CFG1 0x0716 +#define MT6397_AUDNVREGGLB_CFG0 0x0718 +#define MT6397_AUD_NCP0 0x071A +#define MT6397_AUDPREAMP_CON0 0x071C +#define MT6397_AUDADC_CON0 0x071E +#define MT6397_AUDADC_CON1 0x0720 +#define MT6397_AUDADC_CON2 0x0722 +#define MT6397_AUDADC_CON3 0x0724 +#define MT6397_AUDADC_CON4 0x0726 +#define MT6397_AUDADC_CON5 0x0728 +#define MT6397_AUDADC_CON6 0x072A +#define MT6397_AUDDIGMI_CON0 0x072C +#define MT6397_AUDLSBUF_CON0 0x072E +#define MT6397_AUDLSBUF_CON1 0x0730 +#define MT6397_AUDENCSPARE_CON0 0x0732 +#define MT6397_AUDENCCLKSQ_CON0 0x0734 +#define MT6397_AUDPREAMPGAIN_CON0 0x0736 +#define MT6397_ZCD_CON0 0x0738 +#define MT6397_ZCD_CON1 0x073A +#define MT6397_ZCD_CON2 0x073C +#define MT6397_ZCD_CON3 0x073E +#define MT6397_ZCD_CON4 0x0740 +#define MT6397_ZCD_CON5 0x0742 +#define MT6397_NCP_CLKDIV_CON0 0x0744 +#define MT6397_NCP_CLKDIV_CON1 0x0746 + +#endif /* __MFD_MT6397_REGISTERS_H__ */ diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h index fb09312d854bd7..441b6ee726910c 100644 --- a/include/linux/mfd/rk808.h +++ b/include/linux/mfd/rk808.h @@ -156,6 +156,9 @@ enum rk808_reg { #define BUCK2_RATE_MASK (3 << 3) #define MASK_ALL 0xff +#define BUCK_UV_ACT_MASK 0x0f +#define BUCK_UV_ACT_DISABLE 0 + #define SWITCH2_EN BIT(6) #define SWITCH1_EN BIT(5) #define DEV_OFF_RST BIT(3) diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 0c12628e91c6a9..ff843e7ca23ddf 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -28,74 +28,72 @@ #define MAX_RW_REG_CNT 1024 -/* PCI Operation Register Address */ #define RTSX_HCBAR 0x00 #define RTSX_HCBCTLR 0x04 +#define STOP_CMD (0x01 << 28) +#define READ_REG_CMD 0 +#define WRITE_REG_CMD 1 +#define CHECK_REG_CMD 2 + #define RTSX_HDBAR 0x08 +#define SG_INT 0x04 +#define SG_END 0x02 +#define SG_VALID 0x01 +#define SG_NO_OP 0x00 +#define SG_TRANS_DATA (0x02 << 4) +#define SG_LINK_DESC (0x03 << 4) #define RTSX_HDBCTLR 0x0C +#define SDMA_MODE 0x00 +#define ADMA_MODE (0x02 << 26) +#define STOP_DMA (0x01 << 28) +#define TRIG_DMA (0x01 << 31) + #define RTSX_HAIMR 0x10 -#define RTSX_BIPR 0x14 -#define RTSX_BIER 0x18 +#define HAIMR_TRANS_START (0x01 << 31) +#define HAIMR_READ 0x00 +#define HAIMR_WRITE (0x01 << 30) +#define HAIMR_READ_START (HAIMR_TRANS_START | HAIMR_READ) +#define HAIMR_WRITE_START (HAIMR_TRANS_START | HAIMR_WRITE) +#define HAIMR_TRANS_END (HAIMR_TRANS_START) -/* Host command buffer control register */ -#define STOP_CMD (0x01 << 28) - -/* Host data buffer control register */ -#define SDMA_MODE 0x00 -#define ADMA_MODE (0x02 << 26) -#define STOP_DMA (0x01 << 28) -#define TRIG_DMA (0x01 << 31) - -/* Host access internal memory register */ -#define HAIMR_TRANS_START (0x01 << 31) -#define HAIMR_READ 0x00 -#define HAIMR_WRITE (0x01 << 30) -#define HAIMR_READ_START (HAIMR_TRANS_START | HAIMR_READ) -#define HAIMR_WRITE_START (HAIMR_TRANS_START | HAIMR_WRITE) -#define HAIMR_TRANS_END (HAIMR_TRANS_START) - -/* Bus interrupt pending register */ -#define CMD_DONE_INT (1 << 31) -#define DATA_DONE_INT (1 << 30) -#define TRANS_OK_INT (1 << 29) -#define TRANS_FAIL_INT (1 << 28) -#define XD_INT (1 << 27) -#define MS_INT (1 << 26) -#define SD_INT (1 << 25) -#define GPIO0_INT (1 << 24) -#define OC_INT (1 << 23) -#define SD_WRITE_PROTECT (1 << 19) -#define XD_EXIST (1 << 18) -#define MS_EXIST (1 << 17) -#define SD_EXIST (1 << 16) -#define DELINK_INT GPIO0_INT -#define MS_OC_INT (1 << 23) -#define SD_OC_INT (1 << 22) +#define RTSX_BIPR 0x14 +#define CMD_DONE_INT (1 << 31) +#define DATA_DONE_INT (1 << 30) +#define TRANS_OK_INT (1 << 29) +#define TRANS_FAIL_INT (1 << 28) +#define XD_INT (1 << 27) +#define MS_INT (1 << 26) +#define SD_INT (1 << 25) +#define GPIO0_INT (1 << 24) +#define OC_INT (1 << 23) +#define SD_WRITE_PROTECT (1 << 19) +#define XD_EXIST (1 << 18) +#define MS_EXIST (1 << 17) +#define SD_EXIST (1 << 16) +#define DELINK_INT GPIO0_INT +#define MS_OC_INT (1 << 23) +#define SD_OC_INT (1 << 22) #define CARD_INT (XD_INT | MS_INT | SD_INT) #define NEED_COMPLETE_INT (DATA_DONE_INT | TRANS_OK_INT | TRANS_FAIL_INT) #define RTSX_INT (CMD_DONE_INT | NEED_COMPLETE_INT | \ CARD_INT | GPIO0_INT | OC_INT) - #define CARD_EXIST (XD_EXIST | MS_EXIST | SD_EXIST) -/* Bus interrupt enable register */ -#define CMD_DONE_INT_EN (1 << 31) -#define DATA_DONE_INT_EN (1 << 30) -#define TRANS_OK_INT_EN (1 << 29) -#define TRANS_FAIL_INT_EN (1 << 28) -#define XD_INT_EN (1 << 27) -#define MS_INT_EN (1 << 26) -#define SD_INT_EN (1 << 25) -#define GPIO0_INT_EN (1 << 24) -#define OC_INT_EN (1 << 23) -#define DELINK_INT_EN GPIO0_INT_EN -#define MS_OC_INT_EN (1 << 23) -#define SD_OC_INT_EN (1 << 22) - -#define READ_REG_CMD 0 -#define WRITE_REG_CMD 1 -#define CHECK_REG_CMD 2 +#define RTSX_BIER 0x18 +#define CMD_DONE_INT_EN (1 << 31) +#define DATA_DONE_INT_EN (1 << 30) +#define TRANS_OK_INT_EN (1 << 29) +#define TRANS_FAIL_INT_EN (1 << 28) +#define XD_INT_EN (1 << 27) +#define MS_INT_EN (1 << 26) +#define SD_INT_EN (1 << 25) +#define GPIO0_INT_EN (1 << 24) +#define OC_INT_EN (1 << 23) +#define DELINK_INT_EN GPIO0_INT_EN +#define MS_OC_INT_EN (1 << 23) +#define SD_OC_INT_EN (1 << 22) + /* * macros for easy use @@ -125,423 +123,68 @@ #define rtsx_pci_write_config_dword(pcr, where, val) \ pci_write_config_dword((pcr)->pci, where, val) -#define STATE_TRANS_NONE 0 -#define STATE_TRANS_CMD 1 -#define STATE_TRANS_BUF 2 -#define STATE_TRANS_SG 3 - -#define TRANS_NOT_READY 0 -#define TRANS_RESULT_OK 1 -#define TRANS_RESULT_FAIL 2 -#define TRANS_NO_DEVICE 3 - -#define RTSX_RESV_BUF_LEN 4096 -#define HOST_CMDS_BUF_LEN 1024 -#define HOST_SG_TBL_BUF_LEN (RTSX_RESV_BUF_LEN - HOST_CMDS_BUF_LEN) -#define HOST_SG_TBL_ITEMS (HOST_SG_TBL_BUF_LEN / 8) -#define MAX_SG_ITEM_LEN 0x80000 - -#define HOST_TO_DEVICE 0 -#define DEVICE_TO_HOST 1 - -#define RTSX_PHASE_MAX 32 -#define RX_TUNING_CNT 3 - -/* SG descriptor */ -#define SG_INT 0x04 -#define SG_END 0x02 -#define SG_VALID 0x01 - -#define SG_NO_OP 0x00 -#define SG_TRANS_DATA (0x02 << 4) -#define SG_LINK_DESC (0x03 << 4) - -/* Output voltage */ -#define OUTPUT_3V3 0 -#define OUTPUT_1V8 1 - -/* Card Clock Enable Register */ -#define SD_CLK_EN 0x04 -#define MS_CLK_EN 0x08 - -/* Card Select Register */ -#define SD_MOD_SEL 2 -#define MS_MOD_SEL 3 - -/* Card Output Enable Register */ -#define SD_OUTPUT_EN 0x04 -#define MS_OUTPUT_EN 0x08 - -/* CARD_SHARE_MODE */ -#define CARD_SHARE_MASK 0x0F -#define CARD_SHARE_MULTI_LUN 0x00 -#define CARD_SHARE_NORMAL 0x00 -#define CARD_SHARE_48_SD 0x04 -#define CARD_SHARE_48_MS 0x08 -/* CARD_SHARE_MODE for barossa */ -#define CARD_SHARE_BAROSSA_SD 0x01 -#define CARD_SHARE_BAROSSA_MS 0x02 - -/* CARD_DRIVE_SEL */ -#define MS_DRIVE_8mA (0x01 << 6) -#define MMC_DRIVE_8mA (0x01 << 4) -#define XD_DRIVE_8mA (0x01 << 2) -#define GPIO_DRIVE_8mA 0x01 -#define RTS5209_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | MMC_DRIVE_8mA |\ - XD_DRIVE_8mA | GPIO_DRIVE_8mA) -#define RTL8411_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | MMC_DRIVE_8mA |\ - XD_DRIVE_8mA) -#define RTSX_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | GPIO_DRIVE_8mA) +#define STATE_TRANS_NONE 0 +#define STATE_TRANS_CMD 1 +#define STATE_TRANS_BUF 2 +#define STATE_TRANS_SG 3 -/* SD30_DRIVE_SEL */ -#define DRIVER_TYPE_A 0x05 -#define DRIVER_TYPE_B 0x03 -#define DRIVER_TYPE_C 0x02 -#define DRIVER_TYPE_D 0x01 -#define CFG_DRIVER_TYPE_A 0x02 -#define CFG_DRIVER_TYPE_B 0x03 -#define CFG_DRIVER_TYPE_C 0x01 -#define CFG_DRIVER_TYPE_D 0x00 - -/* FPDCTL */ -#define SSC_POWER_DOWN 0x01 -#define SD_OC_POWER_DOWN 0x02 -#define ALL_POWER_DOWN 0x07 -#define OC_POWER_DOWN 0x06 - -/* CLK_CTL */ -#define CHANGE_CLK 0x01 - -/* LDO_CTL */ -#define BPP_ASIC_1V7 0x00 -#define BPP_ASIC_1V8 0x01 -#define BPP_ASIC_1V9 0x02 -#define BPP_ASIC_2V0 0x03 -#define BPP_ASIC_2V7 0x04 -#define BPP_ASIC_2V8 0x05 -#define BPP_ASIC_3V2 0x06 -#define BPP_ASIC_3V3 0x07 -#define BPP_REG_TUNED18 0x07 -#define BPP_TUNED18_SHIFT_8402 5 -#define BPP_TUNED18_SHIFT_8411 4 -#define BPP_PAD_MASK 0x04 -#define BPP_PAD_3V3 0x04 -#define BPP_PAD_1V8 0x00 -#define BPP_LDO_POWB 0x03 -#define BPP_LDO_ON 0x00 -#define BPP_LDO_SUSPEND 0x02 -#define BPP_LDO_OFF 0x03 - -/* CD_PAD_CTL */ -#define CD_DISABLE_MASK 0x07 -#define MS_CD_DISABLE 0x04 -#define SD_CD_DISABLE 0x02 -#define XD_CD_DISABLE 0x01 -#define CD_DISABLE 0x07 -#define CD_ENABLE 0x00 -#define MS_CD_EN_ONLY 0x03 -#define SD_CD_EN_ONLY 0x05 -#define XD_CD_EN_ONLY 0x06 -#define FORCE_CD_LOW_MASK 0x38 -#define FORCE_CD_XD_LOW 0x08 -#define FORCE_CD_SD_LOW 0x10 -#define FORCE_CD_MS_LOW 0x20 -#define CD_AUTO_DISABLE 0x40 - -/* SD_STAT1 */ -#define SD_CRC7_ERR 0x80 -#define SD_CRC16_ERR 0x40 -#define SD_CRC_WRITE_ERR 0x20 -#define SD_CRC_WRITE_ERR_MASK 0x1C -#define GET_CRC_TIME_OUT 0x02 -#define SD_TUNING_COMPARE_ERR 0x01 - -/* SD_STAT2 */ -#define SD_RSP_80CLK_TIMEOUT 0x01 - -/* SD_BUS_STAT */ -#define SD_CLK_TOGGLE_EN 0x80 -#define SD_CLK_FORCE_STOP 0x40 -#define SD_DAT3_STATUS 0x10 -#define SD_DAT2_STATUS 0x08 -#define SD_DAT1_STATUS 0x04 -#define SD_DAT0_STATUS 0x02 -#define SD_CMD_STATUS 0x01 - -/* SD_PAD_CTL */ -#define SD_IO_USING_1V8 0x80 -#define SD_IO_USING_3V3 0x7F -#define TYPE_A_DRIVING 0x00 -#define TYPE_B_DRIVING 0x01 -#define TYPE_C_DRIVING 0x02 -#define TYPE_D_DRIVING 0x03 - -/* SD_SAMPLE_POINT_CTL */ -#define DDR_FIX_RX_DAT 0x00 -#define DDR_VAR_RX_DAT 0x80 -#define DDR_FIX_RX_DAT_EDGE 0x00 -#define DDR_FIX_RX_DAT_14_DELAY 0x40 -#define DDR_FIX_RX_CMD 0x00 -#define DDR_VAR_RX_CMD 0x20 -#define DDR_FIX_RX_CMD_POS_EDGE 0x00 -#define DDR_FIX_RX_CMD_14_DELAY 0x10 -#define SD20_RX_POS_EDGE 0x00 -#define SD20_RX_14_DELAY 0x08 -#define SD20_RX_SEL_MASK 0x08 +#define TRANS_NOT_READY 0 +#define TRANS_RESULT_OK 1 +#define TRANS_RESULT_FAIL 2 +#define TRANS_NO_DEVICE 3 -/* SD_PUSH_POINT_CTL */ -#define DDR_FIX_TX_CMD_DAT 0x00 -#define DDR_VAR_TX_CMD_DAT 0x80 -#define DDR_FIX_TX_DAT_14_TSU 0x00 -#define DDR_FIX_TX_DAT_12_TSU 0x40 -#define DDR_FIX_TX_CMD_NEG_EDGE 0x00 -#define DDR_FIX_TX_CMD_14_AHEAD 0x20 -#define SD20_TX_NEG_EDGE 0x00 -#define SD20_TX_14_AHEAD 0x10 -#define SD20_TX_SEL_MASK 0x10 -#define DDR_VAR_SDCLK_POL_SWAP 0x01 - -/* SD_TRANSFER */ -#define SD_TRANSFER_START 0x80 -#define SD_TRANSFER_END 0x40 -#define SD_STAT_IDLE 0x20 -#define SD_TRANSFER_ERR 0x10 -/* SD Transfer Mode definition */ -#define SD_TM_NORMAL_WRITE 0x00 -#define SD_TM_AUTO_WRITE_3 0x01 -#define SD_TM_AUTO_WRITE_4 0x02 -#define SD_TM_AUTO_READ_3 0x05 -#define SD_TM_AUTO_READ_4 0x06 -#define SD_TM_CMD_RSP 0x08 -#define SD_TM_AUTO_WRITE_1 0x09 -#define SD_TM_AUTO_WRITE_2 0x0A -#define SD_TM_NORMAL_READ 0x0C -#define SD_TM_AUTO_READ_1 0x0D -#define SD_TM_AUTO_READ_2 0x0E -#define SD_TM_AUTO_TUNING 0x0F - -/* SD_VPTX_CTL / SD_VPRX_CTL */ -#define PHASE_CHANGE 0x80 -#define PHASE_NOT_RESET 0x40 - -/* SD_DCMPS_TX_CTL / SD_DCMPS_RX_CTL */ -#define DCMPS_CHANGE 0x80 -#define DCMPS_CHANGE_DONE 0x40 -#define DCMPS_ERROR 0x20 -#define DCMPS_CURRENT_PHASE 0x1F - -/* SD Configure 1 Register */ -#define SD_CLK_DIVIDE_0 0x00 -#define SD_CLK_DIVIDE_256 0xC0 -#define SD_CLK_DIVIDE_128 0x80 -#define SD_BUS_WIDTH_1BIT 0x00 -#define SD_BUS_WIDTH_4BIT 0x01 -#define SD_BUS_WIDTH_8BIT 0x02 -#define SD_ASYNC_FIFO_NOT_RST 0x10 -#define SD_20_MODE 0x00 -#define SD_DDR_MODE 0x04 -#define SD_30_MODE 0x08 - -#define SD_CLK_DIVIDE_MASK 0xC0 - -/* SD_CMD_STATE */ -#define SD_CMD_IDLE 0x80 - -/* SD_DATA_STATE */ -#define SD_DATA_IDLE 0x80 - -/* DCM_DRP_CTL */ -#define DCM_RESET 0x08 -#define DCM_LOCKED 0x04 -#define DCM_208M 0x00 -#define DCM_TX 0x01 -#define DCM_RX 0x02 - -/* DCM_DRP_TRIG */ -#define DRP_START 0x80 -#define DRP_DONE 0x40 - -/* DCM_DRP_CFG */ -#define DRP_WRITE 0x80 -#define DRP_READ 0x00 -#define DCM_WRITE_ADDRESS_50 0x50 -#define DCM_WRITE_ADDRESS_51 0x51 -#define DCM_READ_ADDRESS_00 0x00 -#define DCM_READ_ADDRESS_51 0x51 - -/* IRQSTAT0 */ -#define DMA_DONE_INT 0x80 -#define SUSPEND_INT 0x40 -#define LINK_RDY_INT 0x20 -#define LINK_DOWN_INT 0x10 - -/* DMACTL */ -#define DMA_RST 0x80 -#define DMA_BUSY 0x04 -#define DMA_DIR_TO_CARD 0x00 -#define DMA_DIR_FROM_CARD 0x02 -#define DMA_EN 0x01 -#define DMA_128 (0 << 4) -#define DMA_256 (1 << 4) -#define DMA_512 (2 << 4) -#define DMA_1024 (3 << 4) -#define DMA_PACK_SIZE_MASK 0x30 - -/* SSC_CTL1 */ -#define SSC_RSTB 0x80 -#define SSC_8X_EN 0x40 -#define SSC_FIX_FRAC 0x20 -#define SSC_SEL_1M 0x00 -#define SSC_SEL_2M 0x08 -#define SSC_SEL_4M 0x10 -#define SSC_SEL_8M 0x18 - -/* SSC_CTL2 */ -#define SSC_DEPTH_MASK 0x07 -#define SSC_DEPTH_DISALBE 0x00 -#define SSC_DEPTH_4M 0x01 -#define SSC_DEPTH_2M 0x02 -#define SSC_DEPTH_1M 0x03 -#define SSC_DEPTH_500K 0x04 -#define SSC_DEPTH_250K 0x05 - -/* System Clock Control Register */ -#define CLK_LOW_FREQ 0x01 - -/* System Clock Divider Register */ -#define CLK_DIV_1 0x01 -#define CLK_DIV_2 0x02 -#define CLK_DIV_4 0x03 -#define CLK_DIV_8 0x04 - -/* MS_CFG */ -#define SAMPLE_TIME_RISING 0x00 -#define SAMPLE_TIME_FALLING 0x80 -#define PUSH_TIME_DEFAULT 0x00 -#define PUSH_TIME_ODD 0x40 -#define NO_EXTEND_TOGGLE 0x00 -#define EXTEND_TOGGLE_CHK 0x20 -#define MS_BUS_WIDTH_1 0x00 -#define MS_BUS_WIDTH_4 0x10 -#define MS_BUS_WIDTH_8 0x18 -#define MS_2K_SECTOR_MODE 0x04 -#define MS_512_SECTOR_MODE 0x00 -#define MS_TOGGLE_TIMEOUT_EN 0x00 -#define MS_TOGGLE_TIMEOUT_DISEN 0x01 -#define MS_NO_CHECK_INT 0x02 +#define RTSX_RESV_BUF_LEN 4096 +#define HOST_CMDS_BUF_LEN 1024 +#define HOST_SG_TBL_BUF_LEN (RTSX_RESV_BUF_LEN - HOST_CMDS_BUF_LEN) +#define HOST_SG_TBL_ITEMS (HOST_SG_TBL_BUF_LEN / 8) +#define MAX_SG_ITEM_LEN 0x80000 +#define HOST_TO_DEVICE 0 +#define DEVICE_TO_HOST 1 -/* MS_TRANS_CFG */ -#define WAIT_INT 0x80 -#define NO_WAIT_INT 0x00 -#define NO_AUTO_READ_INT_REG 0x00 -#define AUTO_READ_INT_REG 0x40 -#define MS_CRC16_ERR 0x20 -#define MS_RDY_TIMEOUT 0x10 -#define MS_INT_CMDNK 0x08 -#define MS_INT_BREQ 0x04 -#define MS_INT_ERR 0x02 -#define MS_INT_CED 0x01 - -/* MS_TRANSFER */ -#define MS_TRANSFER_START 0x80 -#define MS_TRANSFER_END 0x40 -#define MS_TRANSFER_ERR 0x20 -#define MS_BS_STATE 0x10 -#define MS_TM_READ_BYTES 0x00 -#define MS_TM_NORMAL_READ 0x01 -#define MS_TM_WRITE_BYTES 0x04 -#define MS_TM_NORMAL_WRITE 0x05 -#define MS_TM_AUTO_READ 0x08 -#define MS_TM_AUTO_WRITE 0x0C - -/* SD Configure 2 Register */ -#define SD_CALCULATE_CRC7 0x00 -#define SD_NO_CALCULATE_CRC7 0x80 -#define SD_CHECK_CRC16 0x00 -#define SD_NO_CHECK_CRC16 0x40 -#define SD_NO_CHECK_WAIT_CRC_TO 0x20 -#define SD_WAIT_BUSY_END 0x08 -#define SD_NO_WAIT_BUSY_END 0x00 -#define SD_CHECK_CRC7 0x00 -#define SD_NO_CHECK_CRC7 0x04 -#define SD_RSP_LEN_0 0x00 -#define SD_RSP_LEN_6 0x01 -#define SD_RSP_LEN_17 0x02 -/* SD/MMC Response Type Definition */ -#define SD_RSP_TYPE_R0 0x04 -#define SD_RSP_TYPE_R1 0x01 -#define SD_RSP_TYPE_R1b 0x09 -#define SD_RSP_TYPE_R2 0x02 -#define SD_RSP_TYPE_R3 0x05 -#define SD_RSP_TYPE_R4 0x05 -#define SD_RSP_TYPE_R5 0x01 -#define SD_RSP_TYPE_R6 0x01 -#define SD_RSP_TYPE_R7 0x01 - -/* SD_CONFIGURE3 */ -#define SD_RSP_80CLK_TIMEOUT_EN 0x01 - -/* Card Transfer Reset Register */ -#define SPI_STOP 0x01 -#define XD_STOP 0x02 -#define SD_STOP 0x04 -#define MS_STOP 0x08 -#define SPI_CLR_ERR 0x10 -#define XD_CLR_ERR 0x20 -#define SD_CLR_ERR 0x40 -#define MS_CLR_ERR 0x80 - -/* Card Data Source Register */ -#define PINGPONG_BUFFER 0x01 -#define RING_BUFFER 0x00 - -/* Card Power Control Register */ -#define PMOS_STRG_MASK 0x10 -#define PMOS_STRG_800mA 0x10 -#define PMOS_STRG_400mA 0x00 -#define SD_POWER_OFF 0x03 -#define SD_PARTIAL_POWER_ON 0x01 -#define SD_POWER_ON 0x00 -#define SD_POWER_MASK 0x03 -#define MS_POWER_OFF 0x0C -#define MS_PARTIAL_POWER_ON 0x04 -#define MS_POWER_ON 0x00 -#define MS_POWER_MASK 0x0C -#define BPP_POWER_OFF 0x0F -#define BPP_POWER_5_PERCENT_ON 0x0E -#define BPP_POWER_10_PERCENT_ON 0x0C -#define BPP_POWER_15_PERCENT_ON 0x08 -#define BPP_POWER_ON 0x00 -#define BPP_POWER_MASK 0x0F -#define SD_VCC_PARTIAL_POWER_ON 0x02 -#define SD_VCC_POWER_ON 0x00 - -/* PWR_GATE_CTRL */ -#define PWR_GATE_EN 0x01 -#define LDO3318_PWR_MASK 0x06 -#define LDO_ON 0x00 -#define LDO_SUSPEND 0x04 -#define LDO_OFF 0x06 - -/* CARD_CLK_SOURCE */ -#define CRC_FIX_CLK (0x00 << 0) -#define CRC_VAR_CLK0 (0x01 << 0) -#define CRC_VAR_CLK1 (0x02 << 0) -#define SD30_FIX_CLK (0x00 << 2) -#define SD30_VAR_CLK0 (0x01 << 2) -#define SD30_VAR_CLK1 (0x02 << 2) -#define SAMPLE_FIX_CLK (0x00 << 4) -#define SAMPLE_VAR_CLK0 (0x01 << 4) -#define SAMPLE_VAR_CLK1 (0x02 << 4) - -/* HOST_SLEEP_STATE */ -#define HOST_ENTER_S1 1 -#define HOST_ENTER_S3 2 +#define OUTPUT_3V3 0 +#define OUTPUT_1V8 1 + +#define RTSX_PHASE_MAX 32 +#define RX_TUNING_CNT 3 #define MS_CFG 0xFD40 +#define SAMPLE_TIME_RISING 0x00 +#define SAMPLE_TIME_FALLING 0x80 +#define PUSH_TIME_DEFAULT 0x00 +#define PUSH_TIME_ODD 0x40 +#define NO_EXTEND_TOGGLE 0x00 +#define EXTEND_TOGGLE_CHK 0x20 +#define MS_BUS_WIDTH_1 0x00 +#define MS_BUS_WIDTH_4 0x10 +#define MS_BUS_WIDTH_8 0x18 +#define MS_2K_SECTOR_MODE 0x04 +#define MS_512_SECTOR_MODE 0x00 +#define MS_TOGGLE_TIMEOUT_EN 0x00 +#define MS_TOGGLE_TIMEOUT_DISEN 0x01 +#define MS_NO_CHECK_INT 0x02 #define MS_TPC 0xFD41 #define MS_TRANS_CFG 0xFD42 +#define WAIT_INT 0x80 +#define NO_WAIT_INT 0x00 +#define NO_AUTO_READ_INT_REG 0x00 +#define AUTO_READ_INT_REG 0x40 +#define MS_CRC16_ERR 0x20 +#define MS_RDY_TIMEOUT 0x10 +#define MS_INT_CMDNK 0x08 +#define MS_INT_BREQ 0x04 +#define MS_INT_ERR 0x02 +#define MS_INT_CED 0x01 #define MS_TRANSFER 0xFD43 +#define MS_TRANSFER_START 0x80 +#define MS_TRANSFER_END 0x40 +#define MS_TRANSFER_ERR 0x20 +#define MS_BS_STATE 0x10 +#define MS_TM_READ_BYTES 0x00 +#define MS_TM_NORMAL_READ 0x01 +#define MS_TM_WRITE_BYTES 0x04 +#define MS_TM_NORMAL_WRITE 0x05 +#define MS_TM_AUTO_READ 0x08 +#define MS_TM_AUTO_WRITE 0x0C #define MS_INT_REG 0xFD44 #define MS_BYTE_CNT 0xFD45 #define MS_SECTOR_CNT_L 0xFD46 @@ -549,14 +192,90 @@ #define MS_DBUS_H 0xFD48 #define SD_CFG1 0xFDA0 +#define SD_CLK_DIVIDE_0 0x00 +#define SD_CLK_DIVIDE_256 0xC0 +#define SD_CLK_DIVIDE_128 0x80 +#define SD_BUS_WIDTH_1BIT 0x00 +#define SD_BUS_WIDTH_4BIT 0x01 +#define SD_BUS_WIDTH_8BIT 0x02 +#define SD_ASYNC_FIFO_NOT_RST 0x10 +#define SD_20_MODE 0x00 +#define SD_DDR_MODE 0x04 +#define SD_30_MODE 0x08 +#define SD_CLK_DIVIDE_MASK 0xC0 #define SD_CFG2 0xFDA1 +#define SD_CALCULATE_CRC7 0x00 +#define SD_NO_CALCULATE_CRC7 0x80 +#define SD_CHECK_CRC16 0x00 +#define SD_NO_CHECK_CRC16 0x40 +#define SD_NO_CHECK_WAIT_CRC_TO 0x20 +#define SD_WAIT_BUSY_END 0x08 +#define SD_NO_WAIT_BUSY_END 0x00 +#define SD_CHECK_CRC7 0x00 +#define SD_NO_CHECK_CRC7 0x04 +#define SD_RSP_LEN_0 0x00 +#define SD_RSP_LEN_6 0x01 +#define SD_RSP_LEN_17 0x02 +#define SD_RSP_TYPE_R0 0x04 +#define SD_RSP_TYPE_R1 0x01 +#define SD_RSP_TYPE_R1b 0x09 +#define SD_RSP_TYPE_R2 0x02 +#define SD_RSP_TYPE_R3 0x05 +#define SD_RSP_TYPE_R4 0x05 +#define SD_RSP_TYPE_R5 0x01 +#define SD_RSP_TYPE_R6 0x01 +#define SD_RSP_TYPE_R7 0x01 #define SD_CFG3 0xFDA2 +#define SD_RSP_80CLK_TIMEOUT_EN 0x01 + #define SD_STAT1 0xFDA3 +#define SD_CRC7_ERR 0x80 +#define SD_CRC16_ERR 0x40 +#define SD_CRC_WRITE_ERR 0x20 +#define SD_CRC_WRITE_ERR_MASK 0x1C +#define GET_CRC_TIME_OUT 0x02 +#define SD_TUNING_COMPARE_ERR 0x01 #define SD_STAT2 0xFDA4 +#define SD_RSP_80CLK_TIMEOUT 0x01 + #define SD_BUS_STAT 0xFDA5 +#define SD_CLK_TOGGLE_EN 0x80 +#define SD_CLK_FORCE_STOP 0x40 +#define SD_DAT3_STATUS 0x10 +#define SD_DAT2_STATUS 0x08 +#define SD_DAT1_STATUS 0x04 +#define SD_DAT0_STATUS 0x02 +#define SD_CMD_STATUS 0x01 #define SD_PAD_CTL 0xFDA6 +#define SD_IO_USING_1V8 0x80 +#define SD_IO_USING_3V3 0x7F +#define TYPE_A_DRIVING 0x00 +#define TYPE_B_DRIVING 0x01 +#define TYPE_C_DRIVING 0x02 +#define TYPE_D_DRIVING 0x03 #define SD_SAMPLE_POINT_CTL 0xFDA7 +#define DDR_FIX_RX_DAT 0x00 +#define DDR_VAR_RX_DAT 0x80 +#define DDR_FIX_RX_DAT_EDGE 0x00 +#define DDR_FIX_RX_DAT_14_DELAY 0x40 +#define DDR_FIX_RX_CMD 0x00 +#define DDR_VAR_RX_CMD 0x20 +#define DDR_FIX_RX_CMD_POS_EDGE 0x00 +#define DDR_FIX_RX_CMD_14_DELAY 0x10 +#define SD20_RX_POS_EDGE 0x00 +#define SD20_RX_14_DELAY 0x08 +#define SD20_RX_SEL_MASK 0x08 #define SD_PUSH_POINT_CTL 0xFDA8 +#define DDR_FIX_TX_CMD_DAT 0x00 +#define DDR_VAR_TX_CMD_DAT 0x80 +#define DDR_FIX_TX_DAT_14_TSU 0x00 +#define DDR_FIX_TX_DAT_12_TSU 0x40 +#define DDR_FIX_TX_CMD_NEG_EDGE 0x00 +#define DDR_FIX_TX_CMD_14_AHEAD 0x20 +#define SD20_TX_NEG_EDGE 0x00 +#define SD20_TX_14_AHEAD 0x10 +#define SD20_TX_SEL_MASK 0x10 +#define DDR_VAR_SDCLK_POL_SWAP 0x01 #define SD_CMD0 0xFDA9 #define SD_CMD_START 0x40 #define SD_CMD1 0xFDAA @@ -569,60 +288,203 @@ #define SD_BLOCK_CNT_L 0xFDB1 #define SD_BLOCK_CNT_H 0xFDB2 #define SD_TRANSFER 0xFDB3 +#define SD_TRANSFER_START 0x80 +#define SD_TRANSFER_END 0x40 +#define SD_STAT_IDLE 0x20 +#define SD_TRANSFER_ERR 0x10 +#define SD_TM_NORMAL_WRITE 0x00 +#define SD_TM_AUTO_WRITE_3 0x01 +#define SD_TM_AUTO_WRITE_4 0x02 +#define SD_TM_AUTO_READ_3 0x05 +#define SD_TM_AUTO_READ_4 0x06 +#define SD_TM_CMD_RSP 0x08 +#define SD_TM_AUTO_WRITE_1 0x09 +#define SD_TM_AUTO_WRITE_2 0x0A +#define SD_TM_NORMAL_READ 0x0C +#define SD_TM_AUTO_READ_1 0x0D +#define SD_TM_AUTO_READ_2 0x0E +#define SD_TM_AUTO_TUNING 0x0F #define SD_CMD_STATE 0xFDB5 +#define SD_CMD_IDLE 0x80 + #define SD_DATA_STATE 0xFDB6 +#define SD_DATA_IDLE 0x80 #define SRCTL 0xFC13 -#define DCM_DRP_CTL 0xFC23 -#define DCM_DRP_TRIG 0xFC24 -#define DCM_DRP_CFG 0xFC25 -#define DCM_DRP_WR_DATA_L 0xFC26 -#define DCM_DRP_WR_DATA_H 0xFC27 -#define DCM_DRP_RD_DATA_L 0xFC28 -#define DCM_DRP_RD_DATA_H 0xFC29 +#define DCM_DRP_CTL 0xFC23 +#define DCM_RESET 0x08 +#define DCM_LOCKED 0x04 +#define DCM_208M 0x00 +#define DCM_TX 0x01 +#define DCM_RX 0x02 +#define DCM_DRP_TRIG 0xFC24 +#define DRP_START 0x80 +#define DRP_DONE 0x40 +#define DCM_DRP_CFG 0xFC25 +#define DRP_WRITE 0x80 +#define DRP_READ 0x00 +#define DCM_WRITE_ADDRESS_50 0x50 +#define DCM_WRITE_ADDRESS_51 0x51 +#define DCM_READ_ADDRESS_00 0x00 +#define DCM_READ_ADDRESS_51 0x51 +#define DCM_DRP_WR_DATA_L 0xFC26 +#define DCM_DRP_WR_DATA_H 0xFC27 +#define DCM_DRP_RD_DATA_L 0xFC28 +#define DCM_DRP_RD_DATA_H 0xFC29 #define SD_VPCLK0_CTL 0xFC2A #define SD_VPCLK1_CTL 0xFC2B #define SD_DCMPS0_CTL 0xFC2C #define SD_DCMPS1_CTL 0xFC2D #define SD_VPTX_CTL SD_VPCLK0_CTL #define SD_VPRX_CTL SD_VPCLK1_CTL +#define PHASE_CHANGE 0x80 +#define PHASE_NOT_RESET 0x40 #define SD_DCMPS_TX_CTL SD_DCMPS0_CTL #define SD_DCMPS_RX_CTL SD_DCMPS1_CTL +#define DCMPS_CHANGE 0x80 +#define DCMPS_CHANGE_DONE 0x40 +#define DCMPS_ERROR 0x20 +#define DCMPS_CURRENT_PHASE 0x1F #define CARD_CLK_SOURCE 0xFC2E - +#define CRC_FIX_CLK (0x00 << 0) +#define CRC_VAR_CLK0 (0x01 << 0) +#define CRC_VAR_CLK1 (0x02 << 0) +#define SD30_FIX_CLK (0x00 << 2) +#define SD30_VAR_CLK0 (0x01 << 2) +#define SD30_VAR_CLK1 (0x02 << 2) +#define SAMPLE_FIX_CLK (0x00 << 4) +#define SAMPLE_VAR_CLK0 (0x01 << 4) +#define SAMPLE_VAR_CLK1 (0x02 << 4) #define CARD_PWR_CTL 0xFD50 +#define PMOS_STRG_MASK 0x10 +#define PMOS_STRG_800mA 0x10 +#define PMOS_STRG_400mA 0x00 +#define SD_POWER_OFF 0x03 +#define SD_PARTIAL_POWER_ON 0x01 +#define SD_POWER_ON 0x00 +#define SD_POWER_MASK 0x03 +#define MS_POWER_OFF 0x0C +#define MS_PARTIAL_POWER_ON 0x04 +#define MS_POWER_ON 0x00 +#define MS_POWER_MASK 0x0C +#define BPP_POWER_OFF 0x0F +#define BPP_POWER_5_PERCENT_ON 0x0E +#define BPP_POWER_10_PERCENT_ON 0x0C +#define BPP_POWER_15_PERCENT_ON 0x08 +#define BPP_POWER_ON 0x00 +#define BPP_POWER_MASK 0x0F +#define SD_VCC_PARTIAL_POWER_ON 0x02 +#define SD_VCC_POWER_ON 0x00 #define CARD_CLK_SWITCH 0xFD51 #define RTL8411B_PACKAGE_MODE 0xFD51 #define CARD_SHARE_MODE 0xFD52 +#define CARD_SHARE_MASK 0x0F +#define CARD_SHARE_MULTI_LUN 0x00 +#define CARD_SHARE_NORMAL 0x00 +#define CARD_SHARE_48_SD 0x04 +#define CARD_SHARE_48_MS 0x08 +#define CARD_SHARE_BAROSSA_SD 0x01 +#define CARD_SHARE_BAROSSA_MS 0x02 #define CARD_DRIVE_SEL 0xFD53 +#define MS_DRIVE_8mA (0x01 << 6) +#define MMC_DRIVE_8mA (0x01 << 4) +#define XD_DRIVE_8mA (0x01 << 2) +#define GPIO_DRIVE_8mA 0x01 +#define RTS5209_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | MMC_DRIVE_8mA |\ + XD_DRIVE_8mA | GPIO_DRIVE_8mA) +#define RTL8411_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | MMC_DRIVE_8mA |\ + XD_DRIVE_8mA) +#define RTSX_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | GPIO_DRIVE_8mA) + #define CARD_STOP 0xFD54 +#define SPI_STOP 0x01 +#define XD_STOP 0x02 +#define SD_STOP 0x04 +#define MS_STOP 0x08 +#define SPI_CLR_ERR 0x10 +#define XD_CLR_ERR 0x20 +#define SD_CLR_ERR 0x40 +#define MS_CLR_ERR 0x80 #define CARD_OE 0xFD55 +#define SD_OUTPUT_EN 0x04 +#define MS_OUTPUT_EN 0x08 #define CARD_AUTO_BLINK 0xFD56 #define CARD_GPIO_DIR 0xFD57 #define CARD_GPIO 0xFD58 #define CARD_DATA_SOURCE 0xFD5B +#define PINGPONG_BUFFER 0x01 +#define RING_BUFFER 0x00 #define SD30_CLK_DRIVE_SEL 0xFD5A +#define DRIVER_TYPE_A 0x05 +#define DRIVER_TYPE_B 0x03 +#define DRIVER_TYPE_C 0x02 +#define DRIVER_TYPE_D 0x01 #define CARD_SELECT 0xFD5C +#define SD_MOD_SEL 2 +#define MS_MOD_SEL 3 #define SD30_DRIVE_SEL 0xFD5E +#define CFG_DRIVER_TYPE_A 0x02 +#define CFG_DRIVER_TYPE_B 0x03 +#define CFG_DRIVER_TYPE_C 0x01 +#define CFG_DRIVER_TYPE_D 0x00 #define SD30_CMD_DRIVE_SEL 0xFD5E #define SD30_DAT_DRIVE_SEL 0xFD5F #define CARD_CLK_EN 0xFD69 +#define SD_CLK_EN 0x04 +#define MS_CLK_EN 0x08 #define SDIO_CTRL 0xFD6B #define CD_PAD_CTL 0xFD73 - +#define CD_DISABLE_MASK 0x07 +#define MS_CD_DISABLE 0x04 +#define SD_CD_DISABLE 0x02 +#define XD_CD_DISABLE 0x01 +#define CD_DISABLE 0x07 +#define CD_ENABLE 0x00 +#define MS_CD_EN_ONLY 0x03 +#define SD_CD_EN_ONLY 0x05 +#define XD_CD_EN_ONLY 0x06 +#define FORCE_CD_LOW_MASK 0x38 +#define FORCE_CD_XD_LOW 0x08 +#define FORCE_CD_SD_LOW 0x10 +#define FORCE_CD_MS_LOW 0x20 +#define CD_AUTO_DISABLE 0x40 #define FPDCTL 0xFC00 +#define SSC_POWER_DOWN 0x01 +#define SD_OC_POWER_DOWN 0x02 +#define ALL_POWER_DOWN 0x07 +#define OC_POWER_DOWN 0x06 #define PDINFO 0xFC01 #define CLK_CTL 0xFC02 +#define CHANGE_CLK 0x01 +#define CLK_LOW_FREQ 0x01 + #define CLK_DIV 0xFC03 +#define CLK_DIV_1 0x01 +#define CLK_DIV_2 0x02 +#define CLK_DIV_4 0x03 +#define CLK_DIV_8 0x04 #define CLK_SEL 0xFC04 #define SSC_DIV_N_0 0xFC0F #define SSC_DIV_N_1 0xFC10 #define SSC_CTL1 0xFC11 +#define SSC_RSTB 0x80 +#define SSC_8X_EN 0x40 +#define SSC_FIX_FRAC 0x20 +#define SSC_SEL_1M 0x00 +#define SSC_SEL_2M 0x08 +#define SSC_SEL_4M 0x10 +#define SSC_SEL_8M 0x18 #define SSC_CTL2 0xFC12 - +#define SSC_DEPTH_MASK 0x07 +#define SSC_DEPTH_DISALBE 0x00 +#define SSC_DEPTH_4M 0x01 +#define SSC_DEPTH_2M 0x02 +#define SSC_DEPTH_1M 0x03 +#define SSC_DEPTH_500K 0x04 +#define SSC_DEPTH_250K 0x05 #define RCCTL 0xFC14 #define FPGA_PULL_CTL 0xFC1D @@ -630,6 +492,24 @@ #define GPIO_CTL 0xFC1F #define LDO_CTL 0xFC1E +#define BPP_ASIC_1V7 0x00 +#define BPP_ASIC_1V8 0x01 +#define BPP_ASIC_1V9 0x02 +#define BPP_ASIC_2V0 0x03 +#define BPP_ASIC_2V7 0x04 +#define BPP_ASIC_2V8 0x05 +#define BPP_ASIC_3V2 0x06 +#define BPP_ASIC_3V3 0x07 +#define BPP_REG_TUNED18 0x07 +#define BPP_TUNED18_SHIFT_8402 5 +#define BPP_TUNED18_SHIFT_8411 4 +#define BPP_PAD_MASK 0x04 +#define BPP_PAD_3V3 0x04 +#define BPP_PAD_1V8 0x00 +#define BPP_LDO_POWB 0x03 +#define BPP_LDO_ON 0x00 +#define BPP_LDO_SUSPEND 0x02 +#define BPP_LDO_OFF 0x03 #define SYS_VER 0xFC32 #define CARD_PULL_CTL1 0xFD60 @@ -642,6 +522,10 @@ /* PCI Express Related Registers */ #define IRQEN0 0xFE20 #define IRQSTAT0 0xFE21 +#define DMA_DONE_INT 0x80 +#define SUSPEND_INT 0x40 +#define LINK_RDY_INT 0x20 +#define LINK_DOWN_INT 0x10 #define IRQEN1 0xFE22 #define IRQSTAT1 0xFE23 #define TLPRIEN 0xFE24 @@ -653,6 +537,16 @@ #define DMATC2 0xFE2A #define DMATC3 0xFE2B #define DMACTL 0xFE2C +#define DMA_RST 0x80 +#define DMA_BUSY 0x04 +#define DMA_DIR_TO_CARD 0x00 +#define DMA_DIR_FROM_CARD 0x02 +#define DMA_EN 0x01 +#define DMA_128 (0 << 4) +#define DMA_256 (1 << 4) +#define DMA_512 (2 << 4) +#define DMA_1024 (3 << 4) +#define DMA_PACK_SIZE_MASK 0x30 #define BCTL 0xFE2D #define RBBC0 0xFE2E #define RBBC1 0xFE2F @@ -678,14 +572,21 @@ #define MSGTXDATA2 0xFE46 #define MSGTXDATA3 0xFE47 #define MSGTXCTL 0xFE48 -#define PETXCFG 0xFE49 #define LTR_CTL 0xFE4A #define OBFF_CFG 0xFE4C #define CDRESUMECTL 0xFE52 #define WAKE_SEL_CTL 0xFE54 +#define PCLK_CTL 0xFE55 +#define PCLK_MODE_SEL 0x20 #define PME_FORCE_CTL 0xFE56 + #define ASPM_FORCE_CTL 0xFE57 +#define FORCE_ASPM_CTL0 0x10 +#define FORCE_ASPM_VAL_MASK 0x03 +#define FORCE_ASPM_L1_EN 0x02 +#define FORCE_ASPM_L0_EN 0x01 +#define FORCE_ASPM_NO_ASPM 0x00 #define PM_CLK_FORCE_CTL 0xFE58 #define FUNC_FORCE_CTL 0xFE59 #define PERST_GLITCH_WIDTH 0xFE5C @@ -693,19 +594,36 @@ #define RESET_LOAD_REG 0xFE5E #define EFUSE_CONTENT 0xFE5F #define HOST_SLEEP_STATE 0xFE60 -#define SDIO_CFG 0xFE70 +#define HOST_ENTER_S1 1 +#define HOST_ENTER_S3 2 +#define SDIO_CFG 0xFE70 +#define PM_EVENT_DEBUG 0xFE71 +#define PME_DEBUG_0 0x08 #define NFTS_TX_CTRL 0xFE72 #define PWR_GATE_CTRL 0xFE75 +#define PWR_GATE_EN 0x01 +#define LDO3318_PWR_MASK 0x06 +#define LDO_ON 0x00 +#define LDO_SUSPEND 0x04 +#define LDO_OFF 0x06 #define PWD_SUSPEND_EN 0xFE76 #define LDO_PWR_SEL 0xFE78 +#define L1SUB_CONFIG1 0xFE8D +#define L1SUB_CONFIG2 0xFE8E +#define L1SUB_AUTO_CFG 0x02 +#define L1SUB_CONFIG3 0xFE8F + #define DUMMY_REG_RESET_0 0xFE90 #define AUTOLOAD_CFG_BASE 0xFF00 +#define PETXCFG 0xFF03 #define PM_CTRL1 0xFF44 +#define CD_RESUME_EN_MASK 0xF0 + #define PM_CTRL2 0xFF45 #define PM_CTRL3 0xFF46 #define SDIO_SEND_PME_EN 0x80 @@ -726,18 +644,125 @@ #define IMAGE_FLAG_ADDR0 0xCE80 #define IMAGE_FLAG_ADDR1 0xCE81 +#define RREF_CFG 0xFF6C +#define RREF_VBGSEL_MASK 0x38 +#define RREF_VBGSEL_1V25 0x28 + +#define OOBS_CONFIG 0xFF6E +#define OOBS_AUTOK_DIS 0x80 +#define OOBS_VAL_MASK 0x1F + +#define LDO_DV18_CFG 0xFF70 +#define LDO_DV18_SR_MASK 0xC0 +#define LDO_DV18_SR_DF 0x40 + +#define LDO_CONFIG2 0xFF71 +#define LDO_D3318_MASK 0x07 +#define LDO_D3318_33V 0x07 +#define LDO_D3318_18V 0x02 + +#define LDO_VCC_CFG0 0xFF72 +#define LDO_VCC_LMTVTH_MASK 0x30 +#define LDO_VCC_LMTVTH_2A 0x10 + +#define LDO_VCC_CFG1 0xFF73 +#define LDO_VCC_REF_TUNE_MASK 0x30 +#define LDO_VCC_REF_1V2 0x20 +#define LDO_VCC_TUNE_MASK 0x07 +#define LDO_VCC_1V8 0x04 +#define LDO_VCC_3V3 0x07 +#define LDO_VCC_LMT_EN 0x08 + +#define LDO_VIO_CFG 0xFF75 +#define LDO_VIO_SR_MASK 0xC0 +#define LDO_VIO_SR_DF 0x40 +#define LDO_VIO_REF_TUNE_MASK 0x30 +#define LDO_VIO_REF_1V2 0x20 +#define LDO_VIO_TUNE_MASK 0x07 +#define LDO_VIO_1V7 0x03 +#define LDO_VIO_1V8 0x04 +#define LDO_VIO_3V3 0x07 + +#define LDO_DV12S_CFG 0xFF76 +#define LDO_REF12_TUNE_MASK 0x18 +#define LDO_REF12_TUNE_DF 0x10 +#define LDO_D12_TUNE_MASK 0x07 +#define LDO_D12_TUNE_DF 0x04 + +#define LDO_AV12S_CFG 0xFF77 +#define LDO_AV12S_TUNE_MASK 0x07 +#define LDO_AV12S_TUNE_DF 0x04 + +#define SD40_LDO_CTL1 0xFE7D +#define SD40_VIO_TUNE_MASK 0x70 +#define SD40_VIO_TUNE_1V7 0x30 +#define SD_VIO_LDO_1V8 0x40 +#define SD_VIO_LDO_3V3 0x70 + /* Phy register */ #define PHY_PCR 0x00 +#define PHY_PCR_FORCE_CODE 0xB000 +#define PHY_PCR_OOBS_CALI_50 0x0800 +#define PHY_PCR_OOBS_VCM_08 0x0200 +#define PHY_PCR_OOBS_SEN_90 0x0040 +#define PHY_PCR_RSSI_EN 0x0002 +#define PHY_PCR_RX10K 0x0001 + #define PHY_RCR0 0x01 #define PHY_RCR1 0x02 +#define PHY_RCR1_ADP_TIME_4 0x0400 +#define PHY_RCR1_VCO_COARSE 0x001F +#define PHY_SSCCR2 0x02 +#define PHY_SSCCR2_PLL_NCODE 0x0A00 +#define PHY_SSCCR2_TIME0 0x001C +#define PHY_SSCCR2_TIME2_WIDTH 0x0003 + #define PHY_RCR2 0x03 +#define PHY_RCR2_EMPHASE_EN 0x8000 +#define PHY_RCR2_NADJR 0x4000 +#define PHY_RCR2_CDR_SR_2 0x0100 +#define PHY_RCR2_FREQSEL_12 0x0040 +#define PHY_RCR2_CDR_SC_12P 0x0010 +#define PHY_RCR2_CALIB_LATE 0x0002 +#define PHY_SSCCR3 0x03 +#define PHY_SSCCR3_STEP_IN 0x2740 +#define PHY_SSCCR3_CHECK_DELAY 0x0008 +#define _PHY_ANA03 0x03 +#define _PHY_ANA03_TIMER_MAX 0x2700 +#define _PHY_ANA03_OOBS_DEB_EN 0x0040 +#define _PHY_CMU_DEBUG_EN 0x0008 + #define PHY_RTCR 0x04 #define PHY_RDR 0x05 +#define PHY_RDR_RXDSEL_1_9 0x4000 +#define PHY_SSC_AUTO_PWD 0x0600 #define PHY_TCR0 0x06 #define PHY_TCR1 0x07 #define PHY_TUNE 0x08 +#define PHY_TUNE_TUNEREF_1_0 0x4000 +#define PHY_TUNE_VBGSEL_1252 0x0C00 +#define PHY_TUNE_SDBUS_33 0x0200 +#define PHY_TUNE_TUNED18 0x01C0 +#define PHY_TUNE_TUNED12 0X0020 +#define PHY_TUNE_TUNEA12 0x0004 +#define PHY_TUNE_VOLTAGE_MASK 0xFC3F +#define PHY_TUNE_VOLTAGE_3V3 0x03C0 +#define PHY_TUNE_D18_1V8 0x0100 +#define PHY_TUNE_D18_1V7 0x0080 +#define PHY_ANA08 0x08 +#define PHY_ANA08_RX_EQ_DCGAIN 0x5000 +#define PHY_ANA08_SEL_RX_EN 0x0400 +#define PHY_ANA08_RX_EQ_VAL 0x03C0 +#define PHY_ANA08_SCP 0x0020 +#define PHY_ANA08_SEL_IPI 0x0004 + #define PHY_IMR 0x09 #define PHY_BPCR 0x0A +#define PHY_BPCR_IBRXSEL 0x0400 +#define PHY_BPCR_IBTXSEL 0x0100 +#define PHY_BPCR_IB_FILTER 0x0080 +#define PHY_BPCR_CMIRROR_EN 0x0040 + #define PHY_BIST 0x0B #define PHY_RAW_L 0x0C #define PHY_RAW_H 0x0D @@ -745,6 +770,7 @@ #define PHY_HOST_CLK_CTRL 0x0F #define PHY_DMR 0x10 #define PHY_BACR 0x11 +#define PHY_BACR_BASIC_MASK 0xFFF3 #define PHY_IER 0x12 #define PHY_BCSR 0x13 #define PHY_BPR 0x14 @@ -752,80 +778,70 @@ #define PHY_BPNR 0x16 #define PHY_BRNR2 0x17 #define PHY_BENR 0x18 -#define PHY_REG_REV 0x19 +#define PHY_REV 0x19 +#define PHY_REV_RESV 0xE000 +#define PHY_REV_RXIDLE_LATCHED 0x1000 +#define PHY_REV_P1_EN 0x0800 +#define PHY_REV_RXIDLE_EN 0x0400 +#define PHY_REV_CLKREQ_TX_EN 0x0200 +#define PHY_REV_CLKREQ_RX_EN 0x0100 +#define PHY_REV_CLKREQ_DT_1_0 0x0040 +#define PHY_REV_STOP_CLKRD 0x0020 +#define PHY_REV_RX_PWST 0x0008 +#define PHY_REV_STOP_CLKWR 0x0004 +#define _PHY_REV0 0x19 +#define _PHY_REV0_FILTER_OUT 0x3800 +#define _PHY_REV0_CDR_BYPASS_PFD 0x0100 +#define _PHY_REV0_CDR_RX_IDLE_BYPASS 0x0002 + #define PHY_FLD0 0x1A +#define PHY_ANA1A 0x1A +#define PHY_ANA1A_TXR_LOOPBACK 0x2000 +#define PHY_ANA1A_RXT_BIST 0x0500 +#define PHY_ANA1A_TXR_BIST 0x0040 +#define PHY_ANA1A_REV 0x0006 #define PHY_FLD1 0x1B #define PHY_FLD2 0x1C #define PHY_FLD3 0x1D +#define PHY_FLD3_TIMER_4 0x0800 +#define PHY_FLD3_TIMER_6 0x0020 +#define PHY_FLD3_RXDELINK 0x0004 +#define PHY_ANA1D 0x1D +#define PHY_ANA1D_DEBUG_ADDR 0x0004 +#define _PHY_FLD0 0x1D +#define _PHY_FLD0_CLK_REQ_20C 0x8000 +#define _PHY_FLD0_RX_IDLE_EN 0x1000 +#define _PHY_FLD0_BIT_ERR_RSTN 0x0800 +#define _PHY_FLD0_BER_COUNT 0x01E0 +#define _PHY_FLD0_BER_TIMER 0x001E +#define _PHY_FLD0_CHECK_EN 0x0001 + #define PHY_FLD4 0x1E +#define PHY_FLD4_FLDEN_SEL 0x4000 +#define PHY_FLD4_REQ_REF 0x2000 +#define PHY_FLD4_RXAMP_OFF 0x1000 +#define PHY_FLD4_REQ_ADDA 0x0800 +#define PHY_FLD4_BER_COUNT 0x00E0 +#define PHY_FLD4_BER_TIMER 0x000A +#define PHY_FLD4_BER_CHK_EN 0x0001 +#define PHY_DIG1E 0x1E +#define PHY_DIG1E_REV 0x4000 +#define PHY_DIG1E_D0_X_D1 0x1000 +#define PHY_DIG1E_RX_ON_HOST 0x0800 +#define PHY_DIG1E_RCLK_REF_HOST 0x0400 +#define PHY_DIG1E_RCLK_TX_EN_KEEP 0x0040 +#define PHY_DIG1E_RCLK_TX_TERM_KEEP 0x0020 +#define PHY_DIG1E_RCLK_RX_EIDLE_ON 0x0010 +#define PHY_DIG1E_TX_TERM_KEEP 0x0008 +#define PHY_DIG1E_RX_TERM_KEEP 0x0004 +#define PHY_DIG1E_TX_EN_KEEP 0x0002 +#define PHY_DIG1E_RX_EN_KEEP 0x0001 #define PHY_DUM_REG 0x1F -#define LCTLR 0x80 -#define LCTLR_EXT_SYNC 0x80 -#define LCTLR_COMMON_CLOCK_CFG 0x40 -#define LCTLR_RETRAIN_LINK 0x20 -#define LCTLR_LINK_DISABLE 0x10 -#define LCTLR_RCB 0x08 -#define LCTLR_RESERVED 0x04 -#define LCTLR_ASPM_CTL_MASK 0x03 - #define PCR_SETTING_REG1 0x724 #define PCR_SETTING_REG2 0x814 #define PCR_SETTING_REG3 0x747 -/* Phy bits */ -#define PHY_PCR_FORCE_CODE 0xB000 -#define PHY_PCR_OOBS_CALI_50 0x0800 -#define PHY_PCR_OOBS_VCM_08 0x0200 -#define PHY_PCR_OOBS_SEN_90 0x0040 -#define PHY_PCR_RSSI_EN 0x0002 - -#define PHY_RCR1_ADP_TIME 0x0100 -#define PHY_RCR1_VCO_COARSE 0x001F - -#define PHY_RCR2_EMPHASE_EN 0x8000 -#define PHY_RCR2_NADJR 0x4000 -#define PHY_RCR2_CDR_CP_10 0x0400 -#define PHY_RCR2_CDR_SR_2 0x0100 -#define PHY_RCR2_FREQSEL_12 0x0040 -#define PHY_RCR2_CPADJEN 0x0020 -#define PHY_RCR2_CDR_SC_8 0x0008 -#define PHY_RCR2_CALIB_LATE 0x0002 - -#define PHY_RDR_RXDSEL_1_9 0x4000 - -#define PHY_TUNE_TUNEREF_1_0 0x4000 -#define PHY_TUNE_VBGSEL_1252 0x0C00 -#define PHY_TUNE_SDBUS_33 0x0200 -#define PHY_TUNE_TUNED18 0x01C0 -#define PHY_TUNE_TUNED12 0X0020 - -#define PHY_BPCR_IBRXSEL 0x0400 -#define PHY_BPCR_IBTXSEL 0x0100 -#define PHY_BPCR_IB_FILTER 0x0080 -#define PHY_BPCR_CMIRROR_EN 0x0040 - -#define PHY_REG_REV_RESV 0xE000 -#define PHY_REG_REV_RXIDLE_LATCHED 0x1000 -#define PHY_REG_REV_P1_EN 0x0800 -#define PHY_REG_REV_RXIDLE_EN 0x0400 -#define PHY_REG_REV_CLKREQ_DLY_TIMER_1_0 0x0040 -#define PHY_REG_REV_STOP_CLKRD 0x0020 -#define PHY_REG_REV_RX_PWST 0x0008 -#define PHY_REG_REV_STOP_CLKWR 0x0004 - -#define PHY_FLD3_TIMER_4 0x7800 -#define PHY_FLD3_TIMER_6 0x00E0 -#define PHY_FLD3_RXDELINK 0x0004 - -#define PHY_FLD4_FLDEN_SEL 0x4000 -#define PHY_FLD4_REQ_REF 0x2000 -#define PHY_FLD4_RXAMP_OFF 0x1000 -#define PHY_FLD4_REQ_ADDA 0x0800 -#define PHY_FLD4_BER_COUNT 0x00E0 -#define PHY_FLD4_BER_TIMER 0x000A -#define PHY_FLD4_BER_CHK_EN 0x0001 - #define rtsx_pci_init_cmd(pcr) ((pcr)->ci = 0) struct rtsx_pcr; @@ -835,6 +851,8 @@ struct pcr_handle { }; struct pcr_ops { + int (*write_phy)(struct rtsx_pcr *pcr, u8 addr, u16 val); + int (*read_phy)(struct rtsx_pcr *pcr, u8 addr, u16 *val); int (*extra_init_hw)(struct rtsx_pcr *pcr); int (*optimize_phy)(struct rtsx_pcr *pcr); int (*turn_on_led)(struct rtsx_pcr *pcr); @@ -856,6 +874,7 @@ enum PDEV_STAT {PDEV_STAT_IDLE, PDEV_STAT_RUN}; struct rtsx_pcr { struct pci_dev *pci; unsigned int id; + int pcie_cap; /* pci resources */ unsigned long addr; @@ -928,6 +947,8 @@ struct rtsx_pcr { const struct pcr_ops *ops; enum PDEV_STAT state; + u16 reg_pm_ctrl3; + int num_slots; struct rtsx_slot *slots; }; @@ -935,6 +956,10 @@ struct rtsx_pcr { #define CHK_PCI_PID(pcr, pid) ((pcr)->pci->device == (pid)) #define PCI_VID(pcr) ((pcr)->pci->vendor) #define PCI_PID(pcr) ((pcr)->pci->device) +#define is_version(pcr, pid, ver) \ + (CHK_PCI_PID(pcr, pid) && (pcr)->ic_version == (ver)) +#define pcr_dbg(pcr, fmt, arg...) \ + dev_dbg(&(pcr)->pci->dev, fmt, ##arg) #define SDR104_PHASE(val) ((val) & 0xFF) #define SDR50_PHASE(val) (((val) >> 8) & 0xFF) @@ -1004,4 +1029,17 @@ static inline void rtsx_pci_write_be32(struct rtsx_pcr *pcr, u16 reg, u32 val) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, reg + 3, 0xFF, val); } +static inline int rtsx_pci_update_phy(struct rtsx_pcr *pcr, u8 addr, + u16 mask, u16 append) +{ + int err; + u16 val; + + err = rtsx_pci_read_phy_register(pcr, addr, &val); + if (err < 0) + return err; + + return rtsx_pci_write_phy_register(pcr, addr, (val & mask) | append); +} + #endif diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h index 3fdb7cfbffb3ac..75115384f3fcf7 100644 --- a/include/linux/mfd/samsung/core.h +++ b/include/linux/mfd/samsung/core.h @@ -58,13 +58,7 @@ enum sec_device_type { * @irq_base: Base IRQ number for device, required for IRQs * @irq: Generic IRQ number for device * @irq_data: Runtime data structure for IRQ controller - * @ono: Power onoff IRQ number for s5m87xx * @wakeup: Whether or not this is a wakeup device - * @wtsr_smpl: Whether or not to enable in RTC driver the Watchdog - * Timer Software Reset (registers set to default value - * after PWRHOLD falling) and Sudden Momentary Power Loss - * (PMIC will enter power on sequence after short drop in - * VBATT voltage). */ struct sec_pmic_dev { struct device *dev; @@ -77,9 +71,7 @@ struct sec_pmic_dev { int irq; struct regmap_irq_chip_data *irq_data; - int ono; bool wakeup; - bool wtsr_smpl; }; int sec_irq_init(struct sec_pmic_dev *sec_pmic); @@ -95,7 +87,6 @@ struct sec_platform_data { int irq_base; int (*cfg_pmic_irq)(void); - int ono; bool wakeup; bool buck_voltage_lock; diff --git a/include/linux/mfd/samsung/irq.h b/include/linux/mfd/samsung/irq.h index f35af7361b606f..667aa40486dd41 100644 --- a/include/linux/mfd/samsung/irq.h +++ b/include/linux/mfd/samsung/irq.h @@ -74,8 +74,8 @@ enum s2mps11_irq { S2MPS11_IRQ_MRB, S2MPS11_IRQ_RTC60S, - S2MPS11_IRQ_RTCA0, S2MPS11_IRQ_RTCA1, + S2MPS11_IRQ_RTCA0, S2MPS11_IRQ_SMPL, S2MPS11_IRQ_RTC1S, S2MPS11_IRQ_WTSR, diff --git a/include/linux/mfd/sky81452.h b/include/linux/mfd/sky81452.h new file mode 100644 index 00000000000000..b0925fa3e9efb3 --- /dev/null +++ b/include/linux/mfd/sky81452.h @@ -0,0 +1,31 @@ +/* + * sky81452.h SKY81452 MFD driver + * + * Copyright 2014 Skyworks Solutions Inc. + * Author : Gyungoh Yoo + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef _SKY81452_H +#define _SKY81452_H + +#include +#include + +struct sky81452_platform_data { + struct sky81452_bl_platform_data *bl_pdata; + struct regulator_init_data *regulator_init_data; +}; + +#endif diff --git a/include/linux/mfd/tc3589x.h b/include/linux/mfd/tc3589x.h index c203c9c5677626..468c31a27fcfb6 100644 --- a/include/linux/mfd/tc3589x.h +++ b/include/linux/mfd/tc3589x.h @@ -140,36 +140,13 @@ extern int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val); #define TC_KPD_DEBOUNCE_PERIOD 0xA3 #define TC_KPD_SETTLE_TIME 0xA3 -/** - * struct tc35893_platform_data - data structure for platform specific data - * @keymap_data: matrix scan code table for keycodes - * @krow: mask for available rows, value is 0xFF - * @kcol: mask for available columns, value is 0xFF - * @debounce_period: platform specific debounce time - * @settle_time: platform specific settle down time - * @irqtype: type of interrupt, falling or rising edge - * @enable_wakeup: specifies if keypad event can wake up system from sleep - * @no_autorepeat: flag for auto repetition - */ -struct tc3589x_keypad_platform_data { - const struct matrix_keymap_data *keymap_data; - u8 krow; - u8 kcol; - u8 debounce_period; - u8 settle_time; - unsigned long irqtype; - bool enable_wakeup; - bool no_autorepeat; -}; /** * struct tc3589x_platform_data - TC3589x platform data * @block: bitmask of blocks to enable (use TC3589x_BLOCK_*) - * @keypad: keypad-specific platform data */ struct tc3589x_platform_data { unsigned int block; - const struct tc3589x_keypad_platform_data *keypad; }; #endif diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index 3f4e994ace2bc9..1fd50dcfe47c62 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -128,6 +128,7 @@ /* Sequencer Status */ #define SEQ_STATUS BIT(5) +#define CHARGE_STEP 0x11 #define ADC_CLK 3000000 #define TOTAL_STEPS 16 diff --git a/include/linux/module.h b/include/linux/module.h index b03485bcb82a0a..c883b86ea9649a 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -338,6 +338,8 @@ struct module { #ifdef CONFIG_EVENT_TRACING struct ftrace_event_call **trace_events; unsigned int num_trace_events; + struct trace_enum_map **trace_enums; + unsigned int num_trace_enums; #endif #ifdef CONFIG_FTRACE_MCOUNT_RECORD unsigned int num_ftrace_callsites; diff --git a/include/linux/of.h b/include/linux/of.h index dfde07e77a632b..9bfcc18ceab3bf 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -332,6 +332,7 @@ extern int of_count_phandle_with_args(const struct device_node *np, extern void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)); extern int of_alias_get_id(struct device_node *np, const char *stem); +extern int of_alias_get_highest_id(const char *stem); extern int of_machine_is_compatible(const char *compat); @@ -594,6 +595,11 @@ static inline int of_alias_get_id(struct device_node *np, const char *stem) return -ENOSYS; } +static inline int of_alias_get_highest_id(const char *stem) +{ + return -ENOSYS; +} + static inline int of_machine_is_compatible(const char *compat) { return 0; diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 2b621982938d46..61992cf2e97716 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -53,6 +53,7 @@ struct perf_guest_info_callbacks { #include #include #include +#include #include struct perf_callchain_entry { @@ -118,10 +119,19 @@ struct hw_perf_event { struct hrtimer hrtimer; }; struct { /* tracepoint */ - struct task_struct *tp_target; /* for tp_event->class */ struct list_head tp_list; }; + struct { /* intel_cqm */ + int cqm_state; + int cqm_rmid; + struct list_head cqm_events_entry; + struct list_head cqm_groups_entry; + struct list_head cqm_group_entry; + }; + struct { /* itrace */ + int itrace_started; + }; #ifdef CONFIG_HAVE_HW_BREAKPOINT struct { /* breakpoint */ /* @@ -129,12 +139,12 @@ struct hw_perf_event { * problem hw_breakpoint has with context * creation and event initalization. */ - struct task_struct *bp_target; struct arch_hw_breakpoint info; struct list_head bp_list; }; #endif }; + struct task_struct *target; int state; local64_t prev_count; u64 sample_period; @@ -166,6 +176,11 @@ struct perf_event; * pmu::capabilities flags */ #define PERF_PMU_CAP_NO_INTERRUPT 0x01 +#define PERF_PMU_CAP_NO_NMI 0x02 +#define PERF_PMU_CAP_AUX_NO_SG 0x04 +#define PERF_PMU_CAP_AUX_SW_DOUBLEBUF 0x08 +#define PERF_PMU_CAP_EXCLUSIVE 0x10 +#define PERF_PMU_CAP_ITRACE 0x20 /** * struct pmu - generic performance monitoring unit @@ -186,6 +201,7 @@ struct pmu { int * __percpu pmu_disable_count; struct perf_cpu_context * __percpu pmu_cpu_context; + atomic_t exclusive_cnt; /* < 0: cpu; > 0: tsk */ int task_ctx_nr; int hrtimer_interval_ms; @@ -262,9 +278,32 @@ struct pmu { int (*event_idx) (struct perf_event *event); /*optional */ /* - * flush branch stack on context-switches (needed in cpu-wide mode) + * context-switches callback + */ + void (*sched_task) (struct perf_event_context *ctx, + bool sched_in); + /* + * PMU specific data size + */ + size_t task_ctx_size; + + + /* + * Return the count value for a counter. + */ + u64 (*count) (struct perf_event *event); /*optional*/ + + /* + * Set up pmu-private data structures for an AUX area */ - void (*flush_branch_stack) (void); + void *(*setup_aux) (int cpu, void **pages, + int nr_pages, bool overwrite); + /* optional */ + + /* + * Free pmu-private AUX data structures + */ + void (*free_aux) (void *aux); /* optional */ }; /** @@ -300,6 +339,7 @@ struct swevent_hlist { #define PERF_ATTACH_CONTEXT 0x01 #define PERF_ATTACH_GROUP 0x02 #define PERF_ATTACH_TASK 0x04 +#define PERF_ATTACH_TASK_DATA 0x08 struct perf_cgroup; struct ring_buffer; @@ -438,6 +478,7 @@ struct perf_event { struct pid_namespace *ns; u64 id; + u64 (*clock)(void); perf_overflow_handler_t overflow_handler; void *overflow_handler_context; @@ -504,7 +545,7 @@ struct perf_event_context { u64 generation; int pin_count; int nr_cgroups; /* cgroup evts */ - int nr_branch_stack; /* branch_stack evt */ + void *task_ctx_data; /* pmu specific data */ struct rcu_head rcu_head; struct delayed_work orphans_remove; @@ -536,12 +577,52 @@ struct perf_output_handle { struct ring_buffer *rb; unsigned long wakeup; unsigned long size; - void *addr; + union { + void *addr; + unsigned long head; + }; int page; }; +#ifdef CONFIG_CGROUP_PERF + +/* + * perf_cgroup_info keeps track of time_enabled for a cgroup. + * This is a per-cpu dynamically allocated data structure. + */ +struct perf_cgroup_info { + u64 time; + u64 timestamp; +}; + +struct perf_cgroup { + struct cgroup_subsys_state css; + struct perf_cgroup_info __percpu *info; +}; + +/* + * Must ensure cgroup is pinned (css_get) before calling + * this function. In other words, we cannot call this function + * if there is no cgroup event for the current CPU context. + */ +static inline struct perf_cgroup * +perf_cgroup_from_task(struct task_struct *task) +{ + return container_of(task_css(task, perf_event_cgrp_id), + struct perf_cgroup, css); +} +#endif /* CONFIG_CGROUP_PERF */ + #ifdef CONFIG_PERF_EVENTS +extern void *perf_aux_output_begin(struct perf_output_handle *handle, + struct perf_event *event); +extern void perf_aux_output_end(struct perf_output_handle *handle, + unsigned long size, bool truncated); +extern int perf_aux_output_skip(struct perf_output_handle *handle, + unsigned long size); +extern void *perf_get_aux(struct perf_output_handle *handle); + extern int perf_pmu_register(struct pmu *pmu, const char *name, int type); extern void perf_pmu_unregister(struct pmu *pmu); @@ -558,6 +639,8 @@ extern void perf_event_delayed_put(struct task_struct *task); extern void perf_event_print_debug(void); extern void perf_pmu_disable(struct pmu *pmu); extern void perf_pmu_enable(struct pmu *pmu); +extern void perf_sched_cb_dec(struct pmu *pmu); +extern void perf_sched_cb_inc(struct pmu *pmu); extern int perf_event_task_disable(void); extern int perf_event_task_enable(void); extern int perf_event_refresh(struct perf_event *event, int refresh); @@ -731,6 +814,11 @@ static inline void perf_event_task_sched_out(struct task_struct *prev, __perf_event_task_sched_out(prev, next); } +static inline u64 __perf_event_count(struct perf_event *event) +{ + return local64_read(&event->count) + atomic64_read(&event->child_count); +} + extern void perf_event_mmap(struct vm_area_struct *vma); extern struct perf_guest_info_callbacks *perf_guest_cbs; extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); @@ -800,6 +888,16 @@ static inline bool has_branch_stack(struct perf_event *event) return event->attr.sample_type & PERF_SAMPLE_BRANCH_STACK; } +static inline bool needs_branch_stack(struct perf_event *event) +{ + return event->attr.branch_sample_type != 0; +} + +static inline bool has_aux(struct perf_event *event) +{ + return event->pmu->setup_aux; +} + extern int perf_output_begin(struct perf_output_handle *handle, struct perf_event *event, unsigned int size); extern void perf_output_end(struct perf_output_handle *handle); @@ -815,6 +913,17 @@ extern void perf_event_disable(struct perf_event *event); extern int __perf_event_disable(void *info); extern void perf_event_task_tick(void); #else /* !CONFIG_PERF_EVENTS: */ +static inline void * +perf_aux_output_begin(struct perf_output_handle *handle, + struct perf_event *event) { return NULL; } +static inline void +perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, + bool truncated) { } +static inline int +perf_aux_output_skip(struct perf_output_handle *handle, + unsigned long size) { return -EINVAL; } +static inline void * +perf_get_aux(struct perf_output_handle *handle) { return NULL; } static inline void perf_event_task_sched_in(struct task_struct *prev, struct task_struct *task) { } diff --git a/include/linux/platform_data/i2c-davinci.h b/include/linux/platform_data/i2c-davinci.h index 2312d197dfb746..89fd34727a24a8 100644 --- a/include/linux/platform_data/i2c-davinci.h +++ b/include/linux/platform_data/i2c-davinci.h @@ -18,6 +18,7 @@ struct davinci_i2c_platform_data { unsigned int bus_delay; /* post-transaction delay (usec) */ unsigned int sda_pin; /* GPIO pin ID to use for SDA */ unsigned int scl_pin; /* GPIO pin ID to use for SCL */ + bool has_pfunc; /*chip has a ICPFUNC register */ }; /* for board setup code */ diff --git a/include/linux/platform_data/sky81452-backlight.h b/include/linux/platform_data/sky81452-backlight.h new file mode 100644 index 00000000000000..1231e9bb00f1f2 --- /dev/null +++ b/include/linux/platform_data/sky81452-backlight.h @@ -0,0 +1,46 @@ +/* + * sky81452.h SKY81452 backlight driver + * + * Copyright 2014 Skyworks Solutions Inc. + * Author : Gyungoh Yoo + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef _SKY81452_BACKLIGHT_H +#define _SKY81452_BACKLIGHT_H + +/** + * struct sky81452_platform_data + * @name: backlight driver name. + If it is not defined, default name is lcd-backlight. + * @gpio_enable:GPIO number which control EN pin + * @enable: Enable mask for current sink channel 1, 2, 3, 4, 5 and 6. + * @ignore_pwm: true if DPWMI should be ignored. + * @dpwm_mode: true is DPWM dimming mode, otherwise Analog dimming mode. + * @phase_shift:true is phase shift mode. + * @short_detecion_threshold: It should be one of 4, 5, 6 and 7V. + * @boost_current_limit: It should be one of 2300, 2750mA. + */ +struct sky81452_bl_platform_data { + const char *name; + int gpio_enable; + unsigned int enable; + bool ignore_pwm; + bool dpwm_mode; + bool phase_shift; + unsigned int short_detection_threshold; + unsigned int boost_current_limit; +}; + +#endif diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 78097491cd99a6..573a5afd5ed884 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -48,6 +48,26 @@ extern int rcu_expedited; /* for sysctl */ +#ifdef CONFIG_TINY_RCU +/* Tiny RCU doesn't expedite, as its purpose in life is instead to be tiny. */ +static inline bool rcu_gp_is_expedited(void) /* Internal RCU use. */ +{ + return false; +} + +static inline void rcu_expedite_gp(void) +{ +} + +static inline void rcu_unexpedite_gp(void) +{ +} +#else /* #ifdef CONFIG_TINY_RCU */ +bool rcu_gp_is_expedited(void); /* Internal RCU use. */ +void rcu_expedite_gp(void); +void rcu_unexpedite_gp(void); +#endif /* #else #ifdef CONFIG_TINY_RCU */ + enum rcutorture_type { RCU_FLAVOR, RCU_BH_FLAVOR, @@ -195,6 +215,15 @@ void call_rcu_sched(struct rcu_head *head, void synchronize_sched(void); +/* + * Structure allowing asynchronous waiting on RCU. + */ +struct rcu_synchronize { + struct rcu_head head; + struct completion completion; +}; +void wakeme_after_rcu(struct rcu_head *head); + /** * call_rcu_tasks() - Queue an RCU for invocation task-based grace period * @head: structure to be used for queueing the RCU updates. @@ -258,6 +287,7 @@ static inline int rcu_preempt_depth(void) /* Internal to kernel */ void rcu_init(void); +void rcu_end_inkernel_boot(void); void rcu_sched_qs(void); void rcu_bh_qs(void); void rcu_check_callbacks(int user); @@ -266,6 +296,8 @@ void rcu_idle_enter(void); void rcu_idle_exit(void); void rcu_irq_enter(void); void rcu_irq_exit(void); +int rcu_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu); #ifdef CONFIG_RCU_STALL_COMMON void rcu_sysrq_start(void); @@ -720,7 +752,7 @@ static inline void rcu_preempt_sleep_check(void) * annotated as __rcu. */ #define rcu_dereference_check(p, c) \ - __rcu_dereference_check((p), rcu_read_lock_held() || (c), __rcu) + __rcu_dereference_check((p), (c) || rcu_read_lock_held(), __rcu) /** * rcu_dereference_bh_check() - rcu_dereference_bh with debug checking @@ -730,7 +762,7 @@ static inline void rcu_preempt_sleep_check(void) * This is the RCU-bh counterpart to rcu_dereference_check(). */ #define rcu_dereference_bh_check(p, c) \ - __rcu_dereference_check((p), rcu_read_lock_bh_held() || (c), __rcu) + __rcu_dereference_check((p), (c) || rcu_read_lock_bh_held(), __rcu) /** * rcu_dereference_sched_check() - rcu_dereference_sched with debug checking @@ -740,7 +772,7 @@ static inline void rcu_preempt_sleep_check(void) * This is the RCU-sched counterpart to rcu_dereference_check(). */ #define rcu_dereference_sched_check(p, c) \ - __rcu_dereference_check((p), rcu_read_lock_sched_held() || (c), \ + __rcu_dereference_check((p), (c) || rcu_read_lock_sched_held(), \ __rcu) #define rcu_dereference_raw(p) rcu_dereference_check(p, 1) /*@@@ needed? @@@*/ @@ -933,9 +965,9 @@ static inline void rcu_read_unlock(void) { rcu_lockdep_assert(rcu_is_watching(), "rcu_read_unlock() used illegally while idle"); - rcu_lock_release(&rcu_lock_map); __release(RCU); __rcu_read_unlock(); + rcu_lock_release(&rcu_lock_map); /* Keep acq info for rls diags. */ } /** diff --git a/include/linux/security.h b/include/linux/security.h index a1b7dbd127ffc7..4e14e3d6309f2d 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1556,7 +1556,7 @@ struct security_operations { int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd); int (*inode_permission) (struct inode *inode, int mask); int (*inode_setattr) (struct dentry *dentry, struct iattr *attr); - int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry); + int (*inode_getattr) (const struct path *path); int (*inode_setxattr) (struct dentry *dentry, const char *name, const void *value, size_t size, int flags); void (*inode_post_setxattr) (struct dentry *dentry, const char *name, @@ -1843,7 +1843,7 @@ int security_inode_readlink(struct dentry *dentry); int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd); int security_inode_permission(struct inode *inode, int mask); int security_inode_setattr(struct dentry *dentry, struct iattr *attr); -int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry); +int security_inode_getattr(const struct path *path); int security_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); void security_inode_post_setxattr(struct dentry *dentry, const char *name, @@ -2259,8 +2259,7 @@ static inline int security_inode_setattr(struct dentry *dentry, return 0; } -static inline int security_inode_getattr(struct vfsmount *mnt, - struct dentry *dentry) +static inline int security_inode_getattr(const struct path *path) { return 0; } diff --git a/include/linux/smpboot.h b/include/linux/smpboot.h index 13e929679550ab..d600afb2192673 100644 --- a/include/linux/smpboot.h +++ b/include/linux/smpboot.h @@ -47,6 +47,5 @@ struct smp_hotplug_thread { int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread); void smpboot_unregister_percpu_thread(struct smp_hotplug_thread *plug_thread); -int smpboot_thread_schedule(void); #endif diff --git a/include/linux/srcu.h b/include/linux/srcu.h index 9cfd9623fb0325..bdeb4567b71e55 100644 --- a/include/linux/srcu.h +++ b/include/linux/srcu.h @@ -182,7 +182,7 @@ static inline int srcu_read_lock_held(struct srcu_struct *sp) * lockdep_is_held() calls. */ #define srcu_dereference_check(p, sp, c) \ - __rcu_dereference_check((p), srcu_read_lock_held(sp) || (c), __rcu) + __rcu_dereference_check((p), (c) || srcu_read_lock_held(sp), __rcu) /** * srcu_dereference - fetch SRCU-protected pointer for later dereferencing diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h index 669045ab73f30d..0a34489a46b62a 100644 --- a/include/linux/stacktrace.h +++ b/include/linux/stacktrace.h @@ -7,8 +7,6 @@ struct task_struct; struct pt_regs; #ifdef CONFIG_STACKTRACE -struct task_struct; - struct stack_trace { unsigned int nr_entries, max_entries; unsigned long *entries; diff --git a/include/linux/tracefs.h b/include/linux/tracefs.h new file mode 100644 index 00000000000000..5b727a17beee84 --- /dev/null +++ b/include/linux/tracefs.h @@ -0,0 +1,45 @@ +/* + * tracefs.h - a pseudo file system for activating tracing + * + * Based on debugfs by: 2004 Greg Kroah-Hartman + * + * Copyright (C) 2014 Red Hat Inc, author: Steven Rostedt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * tracefs is the file system that is used by the tracing infrastructure. + * + */ + +#ifndef _TRACEFS_H_ +#define _TRACEFS_H_ + +#include +#include + +#include + +struct file_operations; + +#ifdef CONFIG_TRACING + +struct dentry *tracefs_create_file(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops); + +struct dentry *tracefs_create_dir(const char *name, struct dentry *parent); + +void tracefs_remove(struct dentry *dentry); +void tracefs_remove_recursive(struct dentry *dentry); + +struct dentry *tracefs_create_instance_dir(const char *name, struct dentry *parent, + int (*mkdir)(const char *name), + int (*rmdir)(const char *name)); + +bool tracefs_initialized(void); + +#endif /* CONFIG_TRACING */ + +#endif diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index c72851328ca9cc..a5f7f3ecafa3a7 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -36,6 +36,12 @@ struct tracepoint { struct tracepoint_func __rcu *funcs; }; +struct trace_enum_map { + const char *system; + const char *enum_string; + unsigned long enum_value; +}; + extern int tracepoint_probe_register(struct tracepoint *tp, void *probe, void *data); extern int @@ -87,6 +93,8 @@ extern void syscall_unregfunc(void); #define PARAMS(args...) args +#define TRACE_DEFINE_ENUM(x) + #endif /* _LINUX_TRACEPOINT_H */ /* diff --git a/include/linux/uio.h b/include/linux/uio.h index 71880299ed487b..15f11fb9fff6fe 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -76,6 +76,7 @@ size_t iov_iter_copy_from_user_atomic(struct page *page, struct iov_iter *i, unsigned long offset, size_t bytes); void iov_iter_advance(struct iov_iter *i, size_t bytes); int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes); +int iov_iter_fault_in_multipages_readable(struct iov_iter *i, size_t bytes); size_t iov_iter_single_seg_count(const struct iov_iter *i); size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, struct iov_iter *i); @@ -139,4 +140,18 @@ static inline void iov_iter_reexpand(struct iov_iter *i, size_t count) size_t csum_and_copy_to_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i); size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i); +int import_iovec(int type, const struct iovec __user * uvector, + unsigned nr_segs, unsigned fast_segs, + struct iovec **iov, struct iov_iter *i); + +#ifdef CONFIG_COMPAT +struct compat_iovec; +int compat_import_iovec(int type, const struct compat_iovec __user * uvector, + unsigned nr_segs, unsigned fast_segs, + struct iovec **iov, struct iov_iter *i); +#endif + +int import_single_range(int type, void __user *buf, size_t len, + struct iovec *iov, struct iov_iter *i); + #endif diff --git a/include/linux/vfio.h b/include/linux/vfio.h index 049b2f497bc79c..ddb44097538245 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -14,6 +14,8 @@ #include #include +#include +#include #include /** @@ -110,4 +112,27 @@ static inline long vfio_spapr_iommu_eeh_ioctl(struct iommu_group *group, return -ENOTTY; } #endif /* CONFIG_EEH */ + +/* + * IRQfd - generic + */ +struct virqfd { + void *opaque; + struct eventfd_ctx *eventfd; + int (*handler)(void *, void *); + void (*thread)(void *, void *); + void *data; + struct work_struct inject; + wait_queue_t wait; + poll_table pt; + struct work_struct shutdown; + struct virqfd **pvirqfd; +}; + +extern int vfio_virqfd_enable(void *opaque, + int (*handler)(void *, void *), + void (*thread)(void *, void *), + void *data, struct virqfd **pvirqfd, int fd); +extern void vfio_virqfd_disable(struct virqfd **pvirqfd); + #endif /* VFIO_H */ diff --git a/include/linux/vgaarb.h b/include/linux/vgaarb.h index c37bd4d0673903..8c3b412d84df38 100644 --- a/include/linux/vgaarb.h +++ b/include/linux/vgaarb.h @@ -65,8 +65,13 @@ struct pci_dev; * out of the arbitration process (and can be safe to take * interrupts at any time. */ +#if defined(CONFIG_VGA_ARB) extern void vga_set_legacy_decoding(struct pci_dev *pdev, unsigned int decodes); +#else +static inline void vga_set_legacy_decoding(struct pci_dev *pdev, + unsigned int decodes) { }; +#endif /** * vga_get - acquire & locks VGA resources diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 395b70e0eccf76..a746bf5216f896 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -137,4 +137,12 @@ extern int watchdog_init_timeout(struct watchdog_device *wdd, extern int watchdog_register_device(struct watchdog_device *); extern void watchdog_unregister_device(struct watchdog_device *); +#ifdef CONFIG_HARDLOCKUP_DETECTOR +void watchdog_nmi_disable_all(void); +void watchdog_nmi_enable_all(void); +#else +static inline void watchdog_nmi_disable_all(void) {} +static inline void watchdog_nmi_enable_all(void) {} +#endif + #endif /* ifndef _LINUX_WATCHDOG_H */ diff --git a/include/net/sock.h b/include/net/sock.h index e4079c28e6b858..81c81ead9a35c1 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -57,7 +57,6 @@ #include #include #include -#include #include #include diff --git a/include/trace/events/9p.h b/include/trace/events/9p.h index a0666362c111ae..633ee9ee977822 100644 --- a/include/trace/events/9p.h +++ b/include/trace/events/9p.h @@ -6,76 +6,95 @@ #include +#define P9_MSG_T \ + EM( P9_TLERROR, "P9_TLERROR" ) \ + EM( P9_RLERROR, "P9_RLERROR" ) \ + EM( P9_TSTATFS, "P9_TSTATFS" ) \ + EM( P9_RSTATFS, "P9_RSTATFS" ) \ + EM( P9_TLOPEN, "P9_TLOPEN" ) \ + EM( P9_RLOPEN, "P9_RLOPEN" ) \ + EM( P9_TLCREATE, "P9_TLCREATE" ) \ + EM( P9_RLCREATE, "P9_RLCREATE" ) \ + EM( P9_TSYMLINK, "P9_TSYMLINK" ) \ + EM( P9_RSYMLINK, "P9_RSYMLINK" ) \ + EM( P9_TMKNOD, "P9_TMKNOD" ) \ + EM( P9_RMKNOD, "P9_RMKNOD" ) \ + EM( P9_TRENAME, "P9_TRENAME" ) \ + EM( P9_RRENAME, "P9_RRENAME" ) \ + EM( P9_TREADLINK, "P9_TREADLINK" ) \ + EM( P9_RREADLINK, "P9_RREADLINK" ) \ + EM( P9_TGETATTR, "P9_TGETATTR" ) \ + EM( P9_RGETATTR, "P9_RGETATTR" ) \ + EM( P9_TSETATTR, "P9_TSETATTR" ) \ + EM( P9_RSETATTR, "P9_RSETATTR" ) \ + EM( P9_TXATTRWALK, "P9_TXATTRWALK" ) \ + EM( P9_RXATTRWALK, "P9_RXATTRWALK" ) \ + EM( P9_TXATTRCREATE, "P9_TXATTRCREATE" ) \ + EM( P9_RXATTRCREATE, "P9_RXATTRCREATE" ) \ + EM( P9_TREADDIR, "P9_TREADDIR" ) \ + EM( P9_RREADDIR, "P9_RREADDIR" ) \ + EM( P9_TFSYNC, "P9_TFSYNC" ) \ + EM( P9_RFSYNC, "P9_RFSYNC" ) \ + EM( P9_TLOCK, "P9_TLOCK" ) \ + EM( P9_RLOCK, "P9_RLOCK" ) \ + EM( P9_TGETLOCK, "P9_TGETLOCK" ) \ + EM( P9_RGETLOCK, "P9_RGETLOCK" ) \ + EM( P9_TLINK, "P9_TLINK" ) \ + EM( P9_RLINK, "P9_RLINK" ) \ + EM( P9_TMKDIR, "P9_TMKDIR" ) \ + EM( P9_RMKDIR, "P9_RMKDIR" ) \ + EM( P9_TRENAMEAT, "P9_TRENAMEAT" ) \ + EM( P9_RRENAMEAT, "P9_RRENAMEAT" ) \ + EM( P9_TUNLINKAT, "P9_TUNLINKAT" ) \ + EM( P9_RUNLINKAT, "P9_RUNLINKAT" ) \ + EM( P9_TVERSION, "P9_TVERSION" ) \ + EM( P9_RVERSION, "P9_RVERSION" ) \ + EM( P9_TAUTH, "P9_TAUTH" ) \ + EM( P9_RAUTH, "P9_RAUTH" ) \ + EM( P9_TATTACH, "P9_TATTACH" ) \ + EM( P9_RATTACH, "P9_RATTACH" ) \ + EM( P9_TERROR, "P9_TERROR" ) \ + EM( P9_RERROR, "P9_RERROR" ) \ + EM( P9_TFLUSH, "P9_TFLUSH" ) \ + EM( P9_RFLUSH, "P9_RFLUSH" ) \ + EM( P9_TWALK, "P9_TWALK" ) \ + EM( P9_RWALK, "P9_RWALK" ) \ + EM( P9_TOPEN, "P9_TOPEN" ) \ + EM( P9_ROPEN, "P9_ROPEN" ) \ + EM( P9_TCREATE, "P9_TCREATE" ) \ + EM( P9_RCREATE, "P9_RCREATE" ) \ + EM( P9_TREAD, "P9_TREAD" ) \ + EM( P9_RREAD, "P9_RREAD" ) \ + EM( P9_TWRITE, "P9_TWRITE" ) \ + EM( P9_RWRITE, "P9_RWRITE" ) \ + EM( P9_TCLUNK, "P9_TCLUNK" ) \ + EM( P9_RCLUNK, "P9_RCLUNK" ) \ + EM( P9_TREMOVE, "P9_TREMOVE" ) \ + EM( P9_RREMOVE, "P9_RREMOVE" ) \ + EM( P9_TSTAT, "P9_TSTAT" ) \ + EM( P9_RSTAT, "P9_RSTAT" ) \ + EM( P9_TWSTAT, "P9_TWSTAT" ) \ + EMe(P9_RWSTAT, "P9_RWSTAT" ) + +/* Define EM() to export the enums to userspace via TRACE_DEFINE_ENUM() */ +#undef EM +#undef EMe +#define EM(a, b) TRACE_DEFINE_ENUM(a); +#define EMe(a, b) TRACE_DEFINE_ENUM(a); + +P9_MSG_T + +/* + * Now redefine the EM() and EMe() macros to map the enums to the strings + * that will be printed in the output. + */ +#undef EM +#undef EMe +#define EM(a, b) { a, b }, +#define EMe(a, b) { a, b } + #define show_9p_op(type) \ - __print_symbolic(type, \ - { P9_TLERROR, "P9_TLERROR" }, \ - { P9_RLERROR, "P9_RLERROR" }, \ - { P9_TSTATFS, "P9_TSTATFS" }, \ - { P9_RSTATFS, "P9_RSTATFS" }, \ - { P9_TLOPEN, "P9_TLOPEN" }, \ - { P9_RLOPEN, "P9_RLOPEN" }, \ - { P9_TLCREATE, "P9_TLCREATE" }, \ - { P9_RLCREATE, "P9_RLCREATE" }, \ - { P9_TSYMLINK, "P9_TSYMLINK" }, \ - { P9_RSYMLINK, "P9_RSYMLINK" }, \ - { P9_TMKNOD, "P9_TMKNOD" }, \ - { P9_RMKNOD, "P9_RMKNOD" }, \ - { P9_TRENAME, "P9_TRENAME" }, \ - { P9_RRENAME, "P9_RRENAME" }, \ - { P9_TREADLINK, "P9_TREADLINK" }, \ - { P9_RREADLINK, "P9_RREADLINK" }, \ - { P9_TGETATTR, "P9_TGETATTR" }, \ - { P9_RGETATTR, "P9_RGETATTR" }, \ - { P9_TSETATTR, "P9_TSETATTR" }, \ - { P9_RSETATTR, "P9_RSETATTR" }, \ - { P9_TXATTRWALK, "P9_TXATTRWALK" }, \ - { P9_RXATTRWALK, "P9_RXATTRWALK" }, \ - { P9_TXATTRCREATE, "P9_TXATTRCREATE" }, \ - { P9_RXATTRCREATE, "P9_RXATTRCREATE" }, \ - { P9_TREADDIR, "P9_TREADDIR" }, \ - { P9_RREADDIR, "P9_RREADDIR" }, \ - { P9_TFSYNC, "P9_TFSYNC" }, \ - { P9_RFSYNC, "P9_RFSYNC" }, \ - { P9_TLOCK, "P9_TLOCK" }, \ - { P9_RLOCK, "P9_RLOCK" }, \ - { P9_TGETLOCK, "P9_TGETLOCK" }, \ - { P9_RGETLOCK, "P9_RGETLOCK" }, \ - { P9_TLINK, "P9_TLINK" }, \ - { P9_RLINK, "P9_RLINK" }, \ - { P9_TMKDIR, "P9_TMKDIR" }, \ - { P9_RMKDIR, "P9_RMKDIR" }, \ - { P9_TRENAMEAT, "P9_TRENAMEAT" }, \ - { P9_RRENAMEAT, "P9_RRENAMEAT" }, \ - { P9_TUNLINKAT, "P9_TUNLINKAT" }, \ - { P9_RUNLINKAT, "P9_RUNLINKAT" }, \ - { P9_TVERSION, "P9_TVERSION" }, \ - { P9_RVERSION, "P9_RVERSION" }, \ - { P9_TAUTH, "P9_TAUTH" }, \ - { P9_RAUTH, "P9_RAUTH" }, \ - { P9_TATTACH, "P9_TATTACH" }, \ - { P9_RATTACH, "P9_RATTACH" }, \ - { P9_TERROR, "P9_TERROR" }, \ - { P9_RERROR, "P9_RERROR" }, \ - { P9_TFLUSH, "P9_TFLUSH" }, \ - { P9_RFLUSH, "P9_RFLUSH" }, \ - { P9_TWALK, "P9_TWALK" }, \ - { P9_RWALK, "P9_RWALK" }, \ - { P9_TOPEN, "P9_TOPEN" }, \ - { P9_ROPEN, "P9_ROPEN" }, \ - { P9_TCREATE, "P9_TCREATE" }, \ - { P9_RCREATE, "P9_RCREATE" }, \ - { P9_TREAD, "P9_TREAD" }, \ - { P9_RREAD, "P9_RREAD" }, \ - { P9_TWRITE, "P9_TWRITE" }, \ - { P9_RWRITE, "P9_RWRITE" }, \ - { P9_TCLUNK, "P9_TCLUNK" }, \ - { P9_RCLUNK, "P9_RCLUNK" }, \ - { P9_TREMOVE, "P9_TREMOVE" }, \ - { P9_RREMOVE, "P9_RREMOVE" }, \ - { P9_TSTAT, "P9_TSTAT" }, \ - { P9_RSTAT, "P9_RSTAT" }, \ - { P9_TWSTAT, "P9_TWSTAT" }, \ - { P9_RWSTAT, "P9_RWSTAT" }) + __print_symbolic(type, P9_MSG_T) TRACE_EVENT(9p_client_req, TP_PROTO(struct p9_client *clnt, int8_t type, int tag), diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 1faecea101f3e8..572e6503394a4c 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -962,7 +962,7 @@ TRACE_EVENT(alloc_extent_state, __entry->ip = IP ), - TP_printk("state=%p; mask = %s; caller = %pF", __entry->state, + TP_printk("state=%p; mask = %s; caller = %pS", __entry->state, show_gfp_flags(__entry->mask), (void *)__entry->ip) ); @@ -982,7 +982,7 @@ TRACE_EVENT(free_extent_state, __entry->ip = IP ), - TP_printk(" state=%p; caller = %pF", __entry->state, + TP_printk(" state=%p; caller = %pS", __entry->state, (void *)__entry->ip) ); diff --git a/include/trace/events/ext3.h b/include/trace/events/ext3.h index 6797b9de90edea..7f20707849bbaa 100644 --- a/include/trace/events/ext3.h +++ b/include/trace/events/ext3.h @@ -144,7 +144,7 @@ TRACE_EVENT(ext3_mark_inode_dirty, __entry->ip = IP; ), - TP_printk("dev %d,%d ino %lu caller %pF", + TP_printk("dev %d,%d ino %lu caller %pS", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, (void *)__entry->ip) ); diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 6e5abd6d38a2b7..47fca36ee426ab 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -240,7 +240,7 @@ TRACE_EVENT(ext4_mark_inode_dirty, __entry->ip = IP; ), - TP_printk("dev %d,%d ino %lu caller %pF", + TP_printk("dev %d,%d ino %lu caller %pS", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, (void *)__entry->ip) ); @@ -1762,7 +1762,7 @@ TRACE_EVENT(ext4_journal_start, __entry->rsv_blocks = rsv_blocks; ), - TP_printk("dev %d,%d blocks, %d rsv_blocks, %d caller %pF", + TP_printk("dev %d,%d blocks, %d rsv_blocks, %d caller %pS", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->blocks, __entry->rsv_blocks, (void *)__entry->ip) ); @@ -1784,7 +1784,7 @@ TRACE_EVENT(ext4_journal_start_reserved, __entry->blocks = blocks; ), - TP_printk("dev %d,%d blocks, %d caller %pF", + TP_printk("dev %d,%d blocks, %d caller %pS", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->blocks, (void *)__entry->ip) ); diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 5422dbfaf97d65..36f4536b614905 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -9,6 +9,36 @@ #define show_dev(entry) MAJOR(entry->dev), MINOR(entry->dev) #define show_dev_ino(entry) show_dev(entry), (unsigned long)entry->ino +TRACE_DEFINE_ENUM(NODE); +TRACE_DEFINE_ENUM(DATA); +TRACE_DEFINE_ENUM(META); +TRACE_DEFINE_ENUM(META_FLUSH); +TRACE_DEFINE_ENUM(CURSEG_HOT_DATA); +TRACE_DEFINE_ENUM(CURSEG_WARM_DATA); +TRACE_DEFINE_ENUM(CURSEG_COLD_DATA); +TRACE_DEFINE_ENUM(CURSEG_HOT_NODE); +TRACE_DEFINE_ENUM(CURSEG_WARM_NODE); +TRACE_DEFINE_ENUM(CURSEG_COLD_NODE); +TRACE_DEFINE_ENUM(NO_CHECK_TYPE); +TRACE_DEFINE_ENUM(GC_GREEDY); +TRACE_DEFINE_ENUM(GC_CB); +TRACE_DEFINE_ENUM(FG_GC); +TRACE_DEFINE_ENUM(BG_GC); +TRACE_DEFINE_ENUM(LFS); +TRACE_DEFINE_ENUM(SSR); +TRACE_DEFINE_ENUM(__REQ_RAHEAD); +TRACE_DEFINE_ENUM(__REQ_WRITE); +TRACE_DEFINE_ENUM(__REQ_SYNC); +TRACE_DEFINE_ENUM(__REQ_NOIDLE); +TRACE_DEFINE_ENUM(__REQ_FLUSH); +TRACE_DEFINE_ENUM(__REQ_FUA); +TRACE_DEFINE_ENUM(__REQ_PRIO); +TRACE_DEFINE_ENUM(__REQ_META); +TRACE_DEFINE_ENUM(CP_UMOUNT); +TRACE_DEFINE_ENUM(CP_FASTBOOT); +TRACE_DEFINE_ENUM(CP_SYNC); +TRACE_DEFINE_ENUM(CP_DISCARD); + #define show_block_type(type) \ __print_symbolic(type, \ { NODE, "NODE" }, \ diff --git a/include/trace/events/intel-sst.h b/include/trace/events/intel-sst.h index 76c72d3f190297..edc24e6dea1bab 100644 --- a/include/trace/events/intel-sst.h +++ b/include/trace/events/intel-sst.h @@ -1,6 +1,13 @@ #undef TRACE_SYSTEM #define TRACE_SYSTEM intel-sst +/* + * The TRACE_SYSTEM_VAR defaults to TRACE_SYSTEM, but must be a + * legitimate C variable. It is not exported to user space. + */ +#undef TRACE_SYSTEM_VAR +#define TRACE_SYSTEM_VAR intel_sst + #if !defined(_TRACE_INTEL_SST_H) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_INTEL_SST_H diff --git a/include/trace/events/irq.h b/include/trace/events/irq.h index 3608bebd3d9c5e..ff8f6c091a1504 100644 --- a/include/trace/events/irq.h +++ b/include/trace/events/irq.h @@ -9,19 +9,34 @@ struct irqaction; struct softirq_action; -#define softirq_name(sirq) { sirq##_SOFTIRQ, #sirq } +#define SOFTIRQ_NAME_LIST \ + softirq_name(HI) \ + softirq_name(TIMER) \ + softirq_name(NET_TX) \ + softirq_name(NET_RX) \ + softirq_name(BLOCK) \ + softirq_name(BLOCK_IOPOLL) \ + softirq_name(TASKLET) \ + softirq_name(SCHED) \ + softirq_name(HRTIMER) \ + softirq_name_end(RCU) + +#undef softirq_name +#undef softirq_name_end + +#define softirq_name(sirq) TRACE_DEFINE_ENUM(sirq##_SOFTIRQ); +#define softirq_name_end(sirq) TRACE_DEFINE_ENUM(sirq##_SOFTIRQ); + +SOFTIRQ_NAME_LIST + +#undef softirq_name +#undef softirq_name_end + +#define softirq_name(sirq) { sirq##_SOFTIRQ, #sirq }, +#define softirq_name_end(sirq) { sirq##_SOFTIRQ, #sirq } + #define show_softirq_name(val) \ - __print_symbolic(val, \ - softirq_name(HI), \ - softirq_name(TIMER), \ - softirq_name(NET_TX), \ - softirq_name(NET_RX), \ - softirq_name(BLOCK), \ - softirq_name(BLOCK_IOPOLL), \ - softirq_name(TASKLET), \ - softirq_name(SCHED), \ - softirq_name(HRTIMER), \ - softirq_name(RCU)) + __print_symbolic(val, SOFTIRQ_NAME_LIST) /** * irq_handler_entry - called immediately before the irq action handler diff --git a/include/trace/events/migrate.h b/include/trace/events/migrate.h index dd2b5467d90581..539b25a76111fc 100644 --- a/include/trace/events/migrate.h +++ b/include/trace/events/migrate.h @@ -7,18 +7,40 @@ #include #define MIGRATE_MODE \ - {MIGRATE_ASYNC, "MIGRATE_ASYNC"}, \ - {MIGRATE_SYNC_LIGHT, "MIGRATE_SYNC_LIGHT"}, \ - {MIGRATE_SYNC, "MIGRATE_SYNC"} + EM( MIGRATE_ASYNC, "MIGRATE_ASYNC") \ + EM( MIGRATE_SYNC_LIGHT, "MIGRATE_SYNC_LIGHT") \ + EMe(MIGRATE_SYNC, "MIGRATE_SYNC") + #define MIGRATE_REASON \ - {MR_COMPACTION, "compaction"}, \ - {MR_MEMORY_FAILURE, "memory_failure"}, \ - {MR_MEMORY_HOTPLUG, "memory_hotplug"}, \ - {MR_SYSCALL, "syscall_or_cpuset"}, \ - {MR_MEMPOLICY_MBIND, "mempolicy_mbind"}, \ - {MR_NUMA_MISPLACED, "numa_misplaced"}, \ - {MR_CMA, "cma"} + EM( MR_COMPACTION, "compaction") \ + EM( MR_MEMORY_FAILURE, "memory_failure") \ + EM( MR_MEMORY_HOTPLUG, "memory_hotplug") \ + EM( MR_SYSCALL, "syscall_or_cpuset") \ + EM( MR_MEMPOLICY_MBIND, "mempolicy_mbind") \ + EM( MR_NUMA_MISPLACED, "numa_misplaced") \ + EMe(MR_CMA, "cma") + +/* + * First define the enums in the above macros to be exported to userspace + * via TRACE_DEFINE_ENUM(). + */ +#undef EM +#undef EMe +#define EM(a, b) TRACE_DEFINE_ENUM(a); +#define EMe(a, b) TRACE_DEFINE_ENUM(a); + +MIGRATE_MODE +MIGRATE_REASON + +/* + * Now redefine the EM() and EMe() macros to map the enums to the strings + * that will be printed in the output. + */ +#undef EM +#undef EMe +#define EM(a, b) {a, b}, +#define EMe(a, b) {a, b} TRACE_EVENT(mm_migrate_pages, diff --git a/include/trace/events/module.h b/include/trace/events/module.h index 81c4c183d348b0..28c45997e451f4 100644 --- a/include/trace/events/module.h +++ b/include/trace/events/module.h @@ -84,7 +84,7 @@ DECLARE_EVENT_CLASS(module_refcnt, __assign_str(name, mod->name); ), - TP_printk("%s call_site=%pf refcnt=%d", + TP_printk("%s call_site=%ps refcnt=%d", __get_str(name), (void *)__entry->ip, __entry->refcnt) ); @@ -121,7 +121,7 @@ TRACE_EVENT(module_request, __assign_str(name, name); ), - TP_printk("%s wait=%d call_site=%pf", + TP_printk("%s wait=%d call_site=%ps", __get_str(name), (int)__entry->wait, (void *)__entry->ip) ); diff --git a/include/trace/events/random.h b/include/trace/events/random.h index 805af6db41cc60..4684de344c5d96 100644 --- a/include/trace/events/random.h +++ b/include/trace/events/random.h @@ -22,7 +22,7 @@ TRACE_EVENT(add_device_randomness, __entry->IP = IP; ), - TP_printk("bytes %d caller %pF", + TP_printk("bytes %d caller %pS", __entry->bytes, (void *)__entry->IP) ); @@ -43,7 +43,7 @@ DECLARE_EVENT_CLASS(random__mix_pool_bytes, __entry->IP = IP; ), - TP_printk("%s pool: bytes %d caller %pF", + TP_printk("%s pool: bytes %d caller %pS", __entry->pool_name, __entry->bytes, (void *)__entry->IP) ); @@ -82,7 +82,7 @@ TRACE_EVENT(credit_entropy_bits, ), TP_printk("%s pool: bits %d entropy_count %d entropy_total %d " - "caller %pF", __entry->pool_name, __entry->bits, + "caller %pS", __entry->pool_name, __entry->bits, __entry->entropy_count, __entry->entropy_total, (void *)__entry->IP) ); @@ -207,7 +207,7 @@ DECLARE_EVENT_CLASS(random__get_random_bytes, __entry->IP = IP; ), - TP_printk("nbytes %d caller %pF", __entry->nbytes, (void *)__entry->IP) + TP_printk("nbytes %d caller %pS", __entry->nbytes, (void *)__entry->IP) ); DEFINE_EVENT(random__get_random_bytes, get_random_bytes, @@ -242,7 +242,7 @@ DECLARE_EVENT_CLASS(random__extract_entropy, __entry->IP = IP; ), - TP_printk("%s pool: nbytes %d entropy_count %d caller %pF", + TP_printk("%s pool: nbytes %d entropy_count %d caller %pS", __entry->pool_name, __entry->nbytes, __entry->entropy_count, (void *)__entry->IP) ); diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index b9c1dc6c825ad1..fd1a02cb3c8235 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -179,27 +179,53 @@ DEFINE_EVENT(rpc_task_queued, rpc_task_wakeup, ); +/* + * First define the enums in the below macros to be exported to userspace + * via TRACE_DEFINE_ENUM(). + */ +#undef EM +#undef EMe +#define EM(a, b) TRACE_DEFINE_ENUM(a); +#define EMe(a, b) TRACE_DEFINE_ENUM(a); + +#define RPC_SHOW_SOCKET \ + EM( SS_FREE, "FREE" ) \ + EM( SS_UNCONNECTED, "UNCONNECTED" ) \ + EM( SS_CONNECTING, "CONNECTING," ) \ + EM( SS_CONNECTED, "CONNECTED," ) \ + EMe(SS_DISCONNECTING, "DISCONNECTING" ) + #define rpc_show_socket_state(state) \ - __print_symbolic(state, \ - { SS_FREE, "FREE" }, \ - { SS_UNCONNECTED, "UNCONNECTED" }, \ - { SS_CONNECTING, "CONNECTING," }, \ - { SS_CONNECTED, "CONNECTED," }, \ - { SS_DISCONNECTING, "DISCONNECTING" }) + __print_symbolic(state, RPC_SHOW_SOCKET) + +RPC_SHOW_SOCKET + +#define RPC_SHOW_SOCK \ + EM( TCP_ESTABLISHED, "ESTABLISHED" ) \ + EM( TCP_SYN_SENT, "SYN_SENT" ) \ + EM( TCP_SYN_RECV, "SYN_RECV" ) \ + EM( TCP_FIN_WAIT1, "FIN_WAIT1" ) \ + EM( TCP_FIN_WAIT2, "FIN_WAIT2" ) \ + EM( TCP_TIME_WAIT, "TIME_WAIT" ) \ + EM( TCP_CLOSE, "CLOSE" ) \ + EM( TCP_CLOSE_WAIT, "CLOSE_WAIT" ) \ + EM( TCP_LAST_ACK, "LAST_ACK" ) \ + EM( TCP_LISTEN, "LISTEN" ) \ + EMe( TCP_CLOSING, "CLOSING" ) #define rpc_show_sock_state(state) \ - __print_symbolic(state, \ - { TCP_ESTABLISHED, "ESTABLISHED" }, \ - { TCP_SYN_SENT, "SYN_SENT" }, \ - { TCP_SYN_RECV, "SYN_RECV" }, \ - { TCP_FIN_WAIT1, "FIN_WAIT1" }, \ - { TCP_FIN_WAIT2, "FIN_WAIT2" }, \ - { TCP_TIME_WAIT, "TIME_WAIT" }, \ - { TCP_CLOSE, "CLOSE" }, \ - { TCP_CLOSE_WAIT, "CLOSE_WAIT" }, \ - { TCP_LAST_ACK, "LAST_ACK" }, \ - { TCP_LISTEN, "LISTEN" }, \ - { TCP_CLOSING, "CLOSING" }) + __print_symbolic(state, RPC_SHOW_SOCK) + +RPC_SHOW_SOCK + +/* + * Now redefine the EM() and EMe() macros to map the enums to the strings + * that will be printed in the output. + */ +#undef EM +#undef EMe +#define EM(a, b) {a, b}, +#define EMe(a, b) {a, b} DECLARE_EVENT_CLASS(xs_socket_event, diff --git a/include/trace/events/tlb.h b/include/trace/events/tlb.h index 0e763576515398..4250f364a6caad 100644 --- a/include/trace/events/tlb.h +++ b/include/trace/events/tlb.h @@ -7,11 +7,31 @@ #include #include -#define TLB_FLUSH_REASON \ - { TLB_FLUSH_ON_TASK_SWITCH, "flush on task switch" }, \ - { TLB_REMOTE_SHOOTDOWN, "remote shootdown" }, \ - { TLB_LOCAL_SHOOTDOWN, "local shootdown" }, \ - { TLB_LOCAL_MM_SHOOTDOWN, "local mm shootdown" } +#define TLB_FLUSH_REASON \ + EM( TLB_FLUSH_ON_TASK_SWITCH, "flush on task switch" ) \ + EM( TLB_REMOTE_SHOOTDOWN, "remote shootdown" ) \ + EM( TLB_LOCAL_SHOOTDOWN, "local shootdown" ) \ + EMe( TLB_LOCAL_MM_SHOOTDOWN, "local mm shootdown" ) + +/* + * First define the enums in TLB_FLUSH_REASON to be exported to userspace + * via TRACE_DEFINE_ENUM(). + */ +#undef EM +#undef EMe +#define EM(a,b) TRACE_DEFINE_ENUM(a); +#define EMe(a,b) TRACE_DEFINE_ENUM(a); + +TLB_FLUSH_REASON + +/* + * Now redefine the EM() and EMe() macros to map the enums to the strings + * that will be printed in the output. + */ +#undef EM +#undef EMe +#define EM(a,b) { a, b }, +#define EMe(a,b) { a, b } TRACE_EVENT_CONDITION(tlb_flush, diff --git a/include/trace/events/v4l2.h b/include/trace/events/v4l2.h index b9bb1f2046938d..20112170ff1105 100644 --- a/include/trace/events/v4l2.h +++ b/include/trace/events/v4l2.h @@ -6,33 +6,58 @@ #include -#define show_type(type) \ - __print_symbolic(type, \ - { V4L2_BUF_TYPE_VIDEO_CAPTURE, "VIDEO_CAPTURE" }, \ - { V4L2_BUF_TYPE_VIDEO_OUTPUT, "VIDEO_OUTPUT" }, \ - { V4L2_BUF_TYPE_VIDEO_OVERLAY, "VIDEO_OVERLAY" }, \ - { V4L2_BUF_TYPE_VBI_CAPTURE, "VBI_CAPTURE" }, \ - { V4L2_BUF_TYPE_VBI_OUTPUT, "VBI_OUTPUT" }, \ - { V4L2_BUF_TYPE_SLICED_VBI_CAPTURE, "SLICED_VBI_CAPTURE" }, \ - { V4L2_BUF_TYPE_SLICED_VBI_OUTPUT, "SLICED_VBI_OUTPUT" }, \ - { V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY, "VIDEO_OUTPUT_OVERLAY" },\ - { V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, "VIDEO_CAPTURE_MPLANE" },\ - { V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, "VIDEO_OUTPUT_MPLANE" }, \ - { V4L2_BUF_TYPE_SDR_CAPTURE, "SDR_CAPTURE" }, \ - { V4L2_BUF_TYPE_PRIVATE, "PRIVATE" }) +/* Enums require being exported to userspace, for user tool parsing */ +#undef EM +#undef EMe +#define EM(a, b) TRACE_DEFINE_ENUM(a); +#define EMe(a, b) TRACE_DEFINE_ENUM(a); + +#define show_type(type) \ + __print_symbolic(type, SHOW_TYPE) + +#define SHOW_TYPE \ + EM( V4L2_BUF_TYPE_VIDEO_CAPTURE, "VIDEO_CAPTURE" ) \ + EM( V4L2_BUF_TYPE_VIDEO_OUTPUT, "VIDEO_OUTPUT" ) \ + EM( V4L2_BUF_TYPE_VIDEO_OVERLAY, "VIDEO_OVERLAY" ) \ + EM( V4L2_BUF_TYPE_VBI_CAPTURE, "VBI_CAPTURE" ) \ + EM( V4L2_BUF_TYPE_VBI_OUTPUT, "VBI_OUTPUT" ) \ + EM( V4L2_BUF_TYPE_SLICED_VBI_CAPTURE, "SLICED_VBI_CAPTURE" ) \ + EM( V4L2_BUF_TYPE_SLICED_VBI_OUTPUT, "SLICED_VBI_OUTPUT" ) \ + EM( V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY, "VIDEO_OUTPUT_OVERLAY" ) \ + EM( V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, "VIDEO_CAPTURE_MPLANE" ) \ + EM( V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, "VIDEO_OUTPUT_MPLANE" ) \ + EM( V4L2_BUF_TYPE_SDR_CAPTURE, "SDR_CAPTURE" ) \ + EMe(V4L2_BUF_TYPE_PRIVATE, "PRIVATE" ) + +SHOW_TYPE #define show_field(field) \ - __print_symbolic(field, \ - { V4L2_FIELD_ANY, "ANY" }, \ - { V4L2_FIELD_NONE, "NONE" }, \ - { V4L2_FIELD_TOP, "TOP" }, \ - { V4L2_FIELD_BOTTOM, "BOTTOM" }, \ - { V4L2_FIELD_INTERLACED, "INTERLACED" }, \ - { V4L2_FIELD_SEQ_TB, "SEQ_TB" }, \ - { V4L2_FIELD_SEQ_BT, "SEQ_BT" }, \ - { V4L2_FIELD_ALTERNATE, "ALTERNATE" }, \ - { V4L2_FIELD_INTERLACED_TB, "INTERLACED_TB" }, \ - { V4L2_FIELD_INTERLACED_BT, "INTERLACED_BT" }) + __print_symbolic(field, SHOW_FIELD) + +#define SHOW_FIELD \ + EM( V4L2_FIELD_ANY, "ANY" ) \ + EM( V4L2_FIELD_NONE, "NONE" ) \ + EM( V4L2_FIELD_TOP, "TOP" ) \ + EM( V4L2_FIELD_BOTTOM, "BOTTOM" ) \ + EM( V4L2_FIELD_INTERLACED, "INTERLACED" ) \ + EM( V4L2_FIELD_SEQ_TB, "SEQ_TB" ) \ + EM( V4L2_FIELD_SEQ_BT, "SEQ_BT" ) \ + EM( V4L2_FIELD_ALTERNATE, "ALTERNATE" ) \ + EM( V4L2_FIELD_INTERLACED_TB, "INTERLACED_TB" ) \ + EMe( V4L2_FIELD_INTERLACED_BT, "INTERLACED_BT" ) + +SHOW_FIELD + +/* + * Now redefine the EM() and EMe() macros to map the enums to the strings + * that will be printed in the output. + */ +#undef EM +#undef EMe +#define EM(a, b) {a, b}, +#define EMe(a, b) {a, b} + +/* V4L2_TC_TYPE_* are macros, not defines, they do not need processing */ #define show_timecode_type(type) \ __print_symbolic(type, \ diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 5a14ead59696a9..880dd743717299 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -23,15 +23,32 @@ {I_REFERENCED, "I_REFERENCED"} \ ) +/* enums need to be exported to user space */ +#undef EM +#undef EMe +#define EM(a,b) TRACE_DEFINE_ENUM(a); +#define EMe(a,b) TRACE_DEFINE_ENUM(a); + #define WB_WORK_REASON \ - {WB_REASON_BACKGROUND, "background"}, \ - {WB_REASON_TRY_TO_FREE_PAGES, "try_to_free_pages"}, \ - {WB_REASON_SYNC, "sync"}, \ - {WB_REASON_PERIODIC, "periodic"}, \ - {WB_REASON_LAPTOP_TIMER, "laptop_timer"}, \ - {WB_REASON_FREE_MORE_MEM, "free_more_memory"}, \ - {WB_REASON_FS_FREE_SPACE, "fs_free_space"}, \ - {WB_REASON_FORKER_THREAD, "forker_thread"} + EM( WB_REASON_BACKGROUND, "background") \ + EM( WB_REASON_TRY_TO_FREE_PAGES, "try_to_free_pages") \ + EM( WB_REASON_SYNC, "sync") \ + EM( WB_REASON_PERIODIC, "periodic") \ + EM( WB_REASON_LAPTOP_TIMER, "laptop_timer") \ + EM( WB_REASON_FREE_MORE_MEM, "free_more_memory") \ + EM( WB_REASON_FS_FREE_SPACE, "fs_free_space") \ + EMe(WB_REASON_FORKER_THREAD, "forker_thread") + +WB_WORK_REASON + +/* + * Now redefine the EM() and EMe() macros to map the enums to the strings + * that will be printed in the output. + */ +#undef EM +#undef EMe +#define EM(a,b) { a, b }, +#define EMe(a,b) { a, b } struct wb_writeback_work; diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 41bf65f04dd9e5..37d4b10b111d81 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -18,6 +18,34 @@ #include +#ifndef TRACE_SYSTEM_VAR +#define TRACE_SYSTEM_VAR TRACE_SYSTEM +#endif + +#define __app__(x, y) str__##x##y +#define __app(x, y) __app__(x, y) + +#define TRACE_SYSTEM_STRING __app(TRACE_SYSTEM_VAR,__trace_system_name) + +#define TRACE_MAKE_SYSTEM_STR() \ + static const char TRACE_SYSTEM_STRING[] = \ + __stringify(TRACE_SYSTEM) + +TRACE_MAKE_SYSTEM_STR(); + +#undef TRACE_DEFINE_ENUM +#define TRACE_DEFINE_ENUM(a) \ + static struct trace_enum_map __used __initdata \ + __##TRACE_SYSTEM##_##a = \ + { \ + .system = TRACE_SYSTEM_STRING, \ + .enum_string = #a, \ + .enum_value = a \ + }; \ + static struct trace_enum_map __used \ + __attribute__((section("_ftrace_enum_map"))) \ + *TRACE_SYSTEM##_##a = &__##TRACE_SYSTEM##_##a + /* * DECLARE_EVENT_CLASS can be used to add a generic function * handlers for events. That is, if all events have the same @@ -105,7 +133,6 @@ #include TRACE_INCLUDE(TRACE_INCLUDE_FILE) - /* * Stage 2 of the trace events. * @@ -122,6 +149,9 @@ * The size of an array is also encoded, in the higher 16 bits of . */ +#undef TRACE_DEFINE_ENUM +#define TRACE_DEFINE_ENUM(a) + #undef __field #define __field(type, item) @@ -539,7 +569,7 @@ static inline notrace int ftrace_get_offsets_##call( \ * .trace = ftrace_raw_output_, <-- stage 2 * }; * - * static const char print_fmt_[] = ; + * static char print_fmt_[] = ; * * static struct ftrace_event_class __used event_class_