diff --git a/.gitignore b/.gitignore index 0c320bf..32a6b8c 100644 --- a/.gitignore +++ b/.gitignore @@ -112,3 +112,7 @@ all.config # Kdevelop4 *.kdev4 + +# dtb objects +*.dtb +*.dtbo diff --git b/Documentation/ABI/testing/sysfs-devices-platform-bone_capemgr b/Documentation/ABI/testing/sysfs-devices-platform-bone_capemgr new file mode 100644 index 0000000..e2df613 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-platform-bone_capemgr @@ -0,0 +1,63 @@ +What: /sys/devices/platform/bone_capemgr/slots +Date: May 2015 +KernelVersion: 4.0 +Contact: Pantelis Antoniou +Description: + READ: + Describe the state of all the slots of the beaglebone capemgr. + Each line of the output describes a slot: + The slot format is as following: + : [P-][F-][O-][l-][L-][D-] \ + ,, + , + + Where the flags are: + P: Slot has been probed + F: Slot has failed probing (i.e. no EEPROM detected) + O: Slot has been overridden by the user + l: Slot is current loading + L: Slot has completed loading and is ready + D: Slot has been disabled + + Example: + 0: P---L- -1 BeagleBone RS232 CAPE,00A1,Beagleboardtoys,BB-BONE-SERL-03 + 1: PF---- -1 + 2: PF---- -1 + 3: PF---- -1 + + WRITE: + Writing a string of the form [:version] issues a request to + load a firmware blob containing an overlay. The name of the firmware blob + is -[version|00A0].dtbo. This act is defined as a slot override. + + Writing a negative slot id removes the slot if it was an overridden one, or + unloads a slot that was probed. + +What: /sys/devices/platform/bone_capemgr/baseboard/ +Date: May 2015 +KernelVersion: 4.0 +Contact: Pantelis Antoniou +Description: Contains the probed base board EEPROM field; one of: + board-name - board-name as stored in cape EEPROM + dc-supplied - whether the cape draws or supplies DC + eeprom-format-revision - EEPROM format rev, only 00A0 supported + header - header; should be 'aa 55 33 ee' + manufacturer - manufacturer string + part-number - part-number of the cape + serial-number - serial number of the cape + version - version of the cape, i.e. 00A0 + number-of-pins - displayed but ignored + pin-usage - displayed but ignored + sys-5v - displayed but ignored + vdd-3v3exp - displayed but ignored + vdd-5v - displayed but ignored +What: /sys/devices/platform/bone_capemgr/slot-/ +Date: May 2015 +KernelVersion: 4.0 +Contact: Pantelis Antoniou +Description: Contains the probed cape's EEPROM field; the field is one of: + board-name - baseboard name i.e. A335BNLT + header - header; should be 'aa 55 33 ee' + revision - baseboard revision + serial-number - baseboard serial number + config-option - displayed but ignored diff --git b/Documentation/ABI/testing/sysfs-firmware-devicetree-overlays b/Documentation/ABI/testing/sysfs-firmware-devicetree-overlays new file mode 100644 index 0000000..88d1549 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-devicetree-overlays @@ -0,0 +1,52 @@ +What: /sys/firmware/devicetree/overlays/ +Date: October 2015 +Contact: Pantelis Antoniou +Description: + This directory contains the applied device tree overlays of + the running system, as directories of the overlay id. + +What: /sys/firmware/devicetree/overlays/enable +Date: October 2015 +Contact: Pantelis Antoniou +Description: + The master enable switch, by default is 1, and when + set to 0 it cannot be re-enabled for security reasons. + + The discussion about this switch takes place in: + http://comments.gmane.org/gmane.linux.drivers.devicetree/101871 + + Kees Cook: + "Coming from the perspective of drawing a bright line between + kernel and the root user (which tends to start with disabling + kernel module loading), I would say that there at least needs + to be a high-level one-way "off" switch for the interface so + that systems that have this interface can choose to turn it off + during initial boot, etc." + +What: /sys/firmware/devicetree/overlays/ +Date: October 2015 +Contact: Pantelis Antoniou +Description: + Each directory represents an applied overlay, containing + the following attribute files. + +What: /sys/firmware/devicetree/overlays//can_remove +Date: October 2015 +Contact: Pantelis Antoniou +Description: + The attribute set to 1 means that the overlay can be removed, + while 0 means that the overlay is being overlapped therefore + removal is prohibited. + +What: /sys/firmware/devicetree/overlays/// +Date: October 2015 +Contact: Pantelis Antoniou +Description: + Each of these directories contain information about of the + particular overlay fragment. + +What: /sys/firmware/devicetree/overlays///target +Date: October 2015 +Contact: Pantelis Antoniou +Description: + The full-path of the target of the fragment diff --git a/Documentation/devicetree/bindings/arm/omap/omap.txt b/Documentation/devicetree/bindings/arm/omap/omap.txt index 94b57f2..413aeed 100644 --- a/Documentation/devicetree/bindings/arm/omap/omap.txt +++ b/Documentation/devicetree/bindings/arm/omap/omap.txt @@ -24,6 +24,8 @@ Optional properties: - ti,no-reset-on-init: When present, the module should not be reset at init - ti,no-idle-on-init: When present, the module should not be idled at init - ti,no-idle: When present, the module is never allowed to idle. +- ti,deassert-hard-reset: list of hwmod and hardware reset line name pairs + (ascii strings) to be deasserted upon device instantiation. Example: diff --git b/Documentation/devicetree/bindings/cpufreq/ti-cpufreq.txt b/Documentation/devicetree/bindings/cpufreq/ti-cpufreq.txt new file mode 100644 index 0000000..f719b2d --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/ti-cpufreq.txt @@ -0,0 +1,89 @@ +Bindings for TI's CPUFreq driver +================================ + +The ti-cpufreq driver works with the operating-points-v2 binding described +at [../opp/opp.txt] to make sure the proper OPPs for a platform get enabled +and then creates a "cpufreq-dt" platform device to leverage the cpufreq-dt +driver described in [cpufreq-dt.txt]. + +Certain TI SoCs, like those in the am335x, am437x, am57xx, and dra7xx +families support different OPPs depending on the silicon variant in use. +The ti-cpufreq driver uses the revision and an efuse value from the SoC to +provide the OPP framework with supported hardware information. This is used +to determine which OPPs from the operating-points-v2 table get enabled. In +order to maintain backwards compatilibity if this information is not present +the "cpufreq-dt" platform device is still created to attempt to find an +operating-points (v1) table, otherwise no OPPs will be available because +safe OPPs cannot be determined. + +Required properties: +-------------------- +In 'cpus' nodes: +- operating-points-v2: Phandle to the operating-points-v2 table to use +- ti,syscon-efuse: Syscon phandle, offset to efuse register, efuse register + mask, and efuse register shift to get the relevant bits + that describe OPP availability +- ti,syscon-rev: Syscon and offset used to look up revision value on SoC + +In 'operating-points-v2' table: +- opp-supported-hw: Two bitfields indicating: + 1. Which revision of the SoC the OPP is supported by + 2. Which eFuse bits indicate this OPP is available + + A bitwise and is performed against these values and if any bit + matches, the OPP gets enabled. + +NOTE: Without the above, platform-device for "cpufreq-dt" is still created + but no determination of which OPPs should be available is done, but this + allows for use of a v1 operating-points table. + +Example: +-------- + +/* From arch/arm/boot/dts/am4372.dtsi */ +cpus { + cpu: cpu@0 { + ... + + operating-points-v2 = <&cpu0_opp_table>; + + ti,syscon-efuse = <&scm_conf 0x610 0x3f 0>; + ti,syscon-rev = <&scm_conf 0x600>; + + ... + }; +}; + +cpu0_opp_table: opp_table0 { + compatible = "operating-points-v2"; + opp50@300000000 { + opp-hz = /bits/ 64 <300000000>; + opp-microvolt = <950000>; + opp-supported-hw = <0xFF 0x01>; + opp-suspend; + }; + + opp100@600000000 { + opp-hz = /bits/ 64 <600000000>; + opp-microvolt = <1100000>; + opp-supported-hw = <0xFF 0x04>; + }; + + opp120@720000000 { + opp-hz = /bits/ 64 <720000000>; + opp-microvolt = <1200000>; + opp-supported-hw = <0xFF 0x08>; + }; + + oppturbo@800000000 { + opp-hz = /bits/ 64 <800000000>; + opp-microvolt = <1260000>; + opp-supported-hw = <0xFF 0x10>; + }; + + oppnitro@1000000000 { + opp-hz = /bits/ 64 <1000000000>; + opp-microvolt = <1325000>; + opp-supported-hw = <0xFF 0x20>; + }; +}; diff --git b/Documentation/devicetree/bindings/misc/bone_capemgr.txt b/Documentation/devicetree/bindings/misc/bone_capemgr.txt new file mode 100644 index 0000000..7e4fbc9 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/bone_capemgr.txt @@ -0,0 +1,111 @@ +* Beaglebone cape manager driver + +Required properties: +- compatible: "ti,bone-capemgr" +- eeprom: phandle to the EEPROM baseboard. + The EEPROM framework interface is use to obtain the data. + +Required children nodes: + +- baseboardmaps: Contains nodes, which each of the them defines a mapping from + the baseboard EEPROM board-name ID to a DT friendly compatible + string. + + - board-name: The baseboard EEPROM board name, i.e. A335BONE for the + original beaglebone white. + - compatible-name: The DT friendly compatible string to be used for matching + compatible capes, i.e. "ti,beaglebone" + + + - nvmem-cells: Defines the phandles of the nvmem cells of the baseboard and the + slots. + - nvmem-cells: Defines the names of the nvmem cells. Required to have at + least a baseboard cell name. + + - #slots: Defines how many slots are there. + +- Example of a beaglebone cape-manager: + +bone_capemgr { + compatible = "ti,bone-capemgr"; + status = "okay"; + + nvmem-cell = <&baseboard_data + &cape0_data &cape1_data &cape2_data &cape3_data>; + nvmem-cell-names = "baseboard", "slot0", "slot1", "slot2", "slot3"; + + #slots = <4>; + + /* map board revisions to compatible definitions */ + baseboardmaps { + baseboard_beaglebone: board@0 { + board-name = "A335BONE"; + compatible-name = "ti,beaglebone"; + }; + + baseboard_beaglebone_black: board@1 { + board-name = "A335BNLT"; + compatible-name = "ti,beaglebone-black"; + }; + }; +}; + +The format of the cape to be loaded is in a standard overlay format with +the following root properties that are interpreted by the cape manager: + +Required properties: + - compatible: Should be compatible to the baseboard according to the + baseboard map value, i.e. "ti,beaglebone". + - part-numer: Should contain the part-number as stored in the EEPROM. + - version: Should contain a list of all the version that are supported + by the single cape dtbo, i.e. "00A1". + +Optional properties: + - exclusive-use: A string list which state the resources this cape requires. + No processing or matching to anything regarding the internal + kernel state is performed; it's purpose is to guard against + conflicts with other capes. + - priority: A priority to be assigned when loading a cape. A lower value + has higher priority. The purpose of the priority is to control + which cape is loaded first in case of a conflict. + +- Example of a serial cape: + +/dts-v1/; +/plugin/; +/ { + compatible = "ti,beaglebone", "ti,beaglebone-black"; + + /* identification */ + part-number = "BB-BONE-SERL-03"; + version = "00A1"; + + /* state the resources this cape uses */ + exclusive-use = + /* the pin header uses */ + "P9.21", /* uart2_txd */ + "P9.22", /* uart2_rxd */ + /* the hardware ip uses */ + "uart2"; + + fragment@0 { + target = <&am33xx_pinmux>; + __overlay__ { + bb_uart2_pins: pinmux_bb_uart2_pins { + pinctrl-single,pins = < + 0x150 0x21 /* spi0_sclk.uart2_rxd | MODE1 */ + 0x154 0x01 /* spi0_d0.uart2_txd | MODE1 */ + >; + }; + }; + }; + + fragment@1 { + target = <&uart2>; + __overlay__ { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bb_uart2_pins>; + }; + }; +}; diff --git b/Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt new file mode 100644 index 0000000..e12f4e5 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt @@ -0,0 +1,86 @@ +Texas Instruments I/O Delay module configuration pinctrl definition + +Used in conjunction with Documentation/devicetree/bindings/pinctrl/ti,omap-pinctrl.txt + +Required Properties: +- compatible: Should be: + "ti,dra7-iodelay" - I/O delay configuration for DRA7 +- reg - must be the register address range of IODelay module +- #address-cells = <1>; +- #size-cells = <0>; + +Important note: Use of "ti,dra7-iodelay" compatible definition need to be +carefully evaluated due to the expectation of glitch during configuration. + +Example: + +dra7_iodelay_core: padconf@4844a000 { + compatible = "ti,dra7-iodelay"; + reg = <0x4844a000 0x0d1c>; + #address-cells = <1>; + #size-cells = <0>; +}; + +Configuration definition follows similar model as the pinctrl-single: +The groups of pin configuration are defined under "pinctrl-single,pins" + +&dra7_iodelay_core { + mmc2_iodelay_3v3_conf: mmc2_iodelay_3v3_conf { + pinctrl-single,pins = < + 0x18c (A_DELAY(0) | G_DELAY(120)) /* CFG_GPMC_A19_IN */ + 0x1a4 (A_DELAY(265) | G_DELAY(360)) /* CFG_GPMC_A20_IN */ + 0x1b0 (A_DELAY(0) | G_DELAY(120)) /* CFG_GPMC_A21_IN */ + 0x1bc (A_DELAY(0) | G_DELAY(120)) /* CFG_GPMC_A22_IN */ + 0x1c8 (A_DELAY(287) | G_DELAY(420)) /* CFG_GPMC_A23_IN */ + 0x1d4 (A_DELAY(144) | G_DELAY(240)) /* CFG_GPMC_A24_IN */ + 0x1e0 (A_DELAY(0) | G_DELAY(0)) /* CFG_GPMC_A25_IN */ + 0x1ec (A_DELAY(120) | G_DELAY(0)) /* CFG_GPMC_A26_IN */ + 0x1f8 (A_DELAY(120) | G_DELAY(180)) /* CFG_GPMC_A27_IN */ + 0x360 (A_DELAY(0) | G_DELAY(0)) /* CFG_GPMC_CS1_IN */ + >; + }; +}; + +Usage in conjunction with pinctrl single: + +For a complete description of the pins both the regular muxing as well as the +iodelay configuration is necessary. For example: + +&dra7_pmx_core { + mmc2_pins_default: mmc2_pins_default { + pinctrl-single,pins = < + 0x9c (PIN_INPUT_PULLUP | MANUAL_MODE | MUX_MODE1) /* gpmc_a23.mmc2_clk */ + 0xb0 (PIN_INPUT_PULLUP | MANUAL_MODE | MUX_MODE1) /* gpmc_cs1.mmc2_cmd */ + 0xa0 (PIN_INPUT_PULLUP | MANUAL_MODE | MUX_MODE1) /* gpmc_a24.mmc2_dat0 */ + 0xa4 (PIN_INPUT_PULLUP | MANUAL_MODE | MUX_MODE1) /* gpmc_a25.mmc2_dat1 */ + 0xa8 (PIN_INPUT_PULLUP | MANUAL_MODE | MUX_MODE1) /* gpmc_a26.mmc2_dat2 */ + 0xac (PIN_INPUT_PULLUP | MANUAL_MODE | MUX_MODE1) /* gpmc_a27.mmc2_dat3 */ + 0x8c (PIN_INPUT_PULLUP | MANUAL_MODE | MUX_MODE1) /* gpmc_a19.mmc2_dat4 */ + 0x90 (PIN_INPUT_PULLUP | MANUAL_MODE | MUX_MODE1) /* gpmc_a20.mmc2_dat5 */ + 0x94 (PIN_INPUT_PULLUP | MANUAL_MODE | MUX_MODE1) /* gpmc_a21.mmc2_dat6 */ + 0x98 (PIN_INPUT_PULLUP | MANUAL_MODE | MUX_MODE1) /* gpmc_a22.mmc2_dat7 */ + >; + }; +}; + +&dra7_iodelay_core { + mmc2_iodelay_3v3_conf: mmc2_iodelay_3v3_conf { + pinctrl-single,pins = < + 0x18c (A_DELAY(0) | G_DELAY(120)) /* CFG_GPMC_A19_IN */ + 0x1a4 (A_DELAY(265) | G_DELAY(360)) /* CFG_GPMC_A20_IN */ + 0x1b0 (A_DELAY(0) | G_DELAY(120)) /* CFG_GPMC_A21_IN */ + 0x1bc (A_DELAY(0) | G_DELAY(120)) /* CFG_GPMC_A22_IN */ + 0x1c8 (A_DELAY(287) | G_DELAY(420)) /* CFG_GPMC_A23_IN */ + 0x1d4 (A_DELAY(144) | G_DELAY(240)) /* CFG_GPMC_A24_IN */ + 0x1e0 (A_DELAY(0) | G_DELAY(0)) /* CFG_GPMC_A25_IN */ + 0x1ec (A_DELAY(120) | G_DELAY(0)) /* CFG_GPMC_A26_IN */ + 0x1f8 (A_DELAY(120) | G_DELAY(180)) /* CFG_GPMC_A27_IN */ + 0x360 (A_DELAY(0) | G_DELAY(0)) /* CFG_GPMC_CS1_IN */ + >; + }; +}; + +&mmc2 { + pinctrl-names = "default"; + pinctrl-0 = <&mmc2_pins_default &mmc2_iodelay_3v3_conf>; +}; diff --git b/Documentation/devicetree/bindings/usb/generic-onboard-device.txt b/Documentation/devicetree/bindings/usb/generic-onboard-device.txt new file mode 100644 index 0000000..4dd7bca --- /dev/null +++ b/Documentation/devicetree/bindings/usb/generic-onboard-device.txt @@ -0,0 +1,31 @@ +Generic Onboard USB Device + +The node should be located at USB host controller's node or +any USB HUB's node. + +Required properties: +- compatible: should be "generic-onboard-device" + +Optional properties: +- clocks: the input clock for USB device. +- clock-frequency: the frequency for device's clock. +- reset-gpios: Should specify the GPIO for reset. +- reset-duration-us: the duration for assert reset signal, the time unit + is microsecond. + +Example: + +&usbh1 { + vbus-supply = <®_usb_h1_vbus>; + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + hub: usb2415@01 { + compatible = "generic-onboard-device"; + reg = <0x01>; + clocks = <&clks IMX6QDL_CLK_CKO>; + reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>; + reset-duration-us = <10>; + }; +}; diff --git b/Documentation/devicetree/configfs-overlays.txt b/Documentation/devicetree/configfs-overlays.txt new file mode 100644 index 0000000..5fa43e0 --- /dev/null +++ b/Documentation/devicetree/configfs-overlays.txt @@ -0,0 +1,31 @@ +Howto use the configfs overlay interface. + +A device-tree configfs entry is created in /config/device-tree/overlays +and and it is manipulated using standard file system I/O. +Note that this is a debug level interface, for use by developers and +not necessarily something accessed by normal users due to the +security implications of having direct access to the kernel's device tree. + +* To create an overlay you mkdir the directory: + + # mkdir /config/device-tree/overlays/foo + +* Either you echo the overlay firmware file to the path property file. + + # echo foo.dtbo >/config/device-tree/overlays/foo/path + +* Or you cat the contents of the overlay to the dtbo file + + # cat foo.dtbo >/config/device-tree/overlays/foo/dtbo + +The overlay file will be applied, and devices will be created/destroyed +as required. + +To remove it simply rmdir the directory. + + # rmdir /config/device-tree/overlays/foo + +The rationalle of the dual interface (firmware & direct copy) is that each is +better suited to different use patterns. The firmware interface is what's +intended to be used by hardware managers in the kernel, while the copy interface +make sense for developers (since it avoids problems with namespaces). diff --git a/Documentation/devicetree/overlay-notes.txt b/Documentation/devicetree/overlay-notes.txt index d418a6c..3e8df30 100644 --- a/Documentation/devicetree/overlay-notes.txt +++ b/Documentation/devicetree/overlay-notes.txt @@ -100,6 +100,14 @@ Finally, if you need to remove all overlays in one-go, just call of_overlay_destroy_all() which will remove every single one in the correct order. +If your board has multiple slots/places where a single overlay can work +and each slot is defined by a node, you can use the +of_overlay_create_target_index() method to select the target. + +For overlays on probeable busses, use the of_overlay_create_target_root() method +in which you supply a device node as a target root, and which all target +references in the overlay are performed relative to that node. + Overlay DTS Format ------------------ @@ -110,9 +118,11 @@ The DTS of an overlay should have the following format: fragment@0 { /* first child node */ - target=; /* phandle target of the overlay */ + /* phandle target of the overlay */ + target= [, , ...]; or - target-path="/path"; /* target path of the overlay */ + /* target path of the overlay */ + target-path="/path" [ , "/path", ...]; __overlay__ { property-a; /* add property-a to the target */ @@ -131,3 +141,11 @@ Using the non-phandle based target method allows one to use a base DT which does not contain a __symbols__ node, i.e. it was not compiled with the -@ option. The __symbols__ node is only required for the target= method, since it contains the information required to map from a phandle to a tree location. + +Using a target index requires the use of a selector target on the call to +of_overlay_create_target_index(). I.e. passing an index of 0 will select the +target in the foo node, an index of 1 the bar node, etc. + +Note that when using the target root create method all target references must +lie under the target root node. I.e. the overlay is not allowed to 'break' out +of the root. diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 82b42c9..7134c37 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -90,6 +90,7 @@ parameter is applicable: NET Appropriate network support is enabled. NUMA NUMA support is enabled. NFS Appropriate NFS support is enabled. + OF Open Firmware support (device tree) is enabled. OSS OSS sound support is enabled. PV_OPS A paravirtualized kernel is enabled. PARIDE The ParIDE (parallel port IDE) subsystem is enabled. @@ -2759,6 +2760,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted. This can be set from sysctl after boot. See Documentation/sysctl/vm.txt for details. + of_overlay_disable [OF] Disable device tree overlays at boot time. + ohci1394_dma=early [HW] enable debugging via the ohci1394 driver. See Documentation/debugging-via-ohci1394.txt for more info. diff --git b/Documentation/misc-devices/bone_capemgr.txt b/Documentation/misc-devices/bone_capemgr.txt new file mode 100644 index 0000000..2a8c766 --- /dev/null +++ b/Documentation/misc-devices/bone_capemgr.txt @@ -0,0 +1,63 @@ +--------------------------- + Beaglebone Cape-Manager +--------------------------- + +The beaglebone cape manager driver allows the automatic use of external +peripheral capes to be automatically supported by Linux without any manual +setup required by the user. + +Each beaglebone cape should contain an EEPROM that describes +it in a fixed I2C address on the i2c2 bus of the baseboard. +The format of the EEPROM is defined in the beaglebone reference +manual at: +http://beagleboard.org/static/beaglebone/latest/Docs/Hardware/BONE_SRM.pdf + +Reading the part number and revision information the manager +requests a firmware file formatted as a device tree overlay blob. + +Applying the overlay the devices are instantiated and the cape is +ready to be used. + +For instance if the part-number is BB-BONE-SERL-03 and the version is 00A1 +the firmware file requested will be BB-BONE-SERL-03-00A1-00A1.dtbo +It will be located by the in-kernel firmware +loader in the usual place, i.e. /lib/firmware/`uname -r`, /lib/firmware etc. + +The driver supports the following parameters (either as part of the kernel +command line or supplied at module insertion time). + +disable_partno: A comma delimited list of PART-NUMBER[:REV] of + disabled capes. +enable_partno: A comma delimited list of PART-NUMBER[:REV[:PRIO]] of + enabled capes. +boot_scan_period: The boot scan period in ms. When the cape manager is built-in + the kernel image, the firmware loader cannot find the files + before the rootfs is mounted. This parameter controls the + period with which the boot state is checked in that case. + +There's a sysfs control interface which is defined at the ABI documentation +area. + +Theory of operation: +-------------------- + +On driver probe the I2C EEPROM of the baseboard is read and information about +the current baseboard is retrieved. This information includes the mapping from +baseboard board name to DT friendly compatible string. I.e. the "A335BONE" board +name from EEPROM is mapped to the "ti,beaglebone" compatible string which should +be present in the dtbo to be loaded. + +Afterwards the EEPROMs declared in each slot are probed, and the EEPROMs found +are decoded keeping track the cape part-number and version data. + +Using the part-number and version a firmware file is requested (the firmware +file requested is -.dtbo). + +The dtbo is unflattend and the resulting device tree is matched against a +compatible baseboard, and in case of multiple parallel loading capes the +priorities defined are honored. That means that when there are multiple capes +being loaded in parallel the ones with the lowest priority number are loaded +first. + +Applying the device tree overlay makes the cape operational, as if it was part +of the kernel's booting device tree. diff --git a/Documentation/printk-formats.txt b/Documentation/printk-formats.txt index 5962949..d1ba882 100644 --- a/Documentation/printk-formats.txt +++ b/Documentation/printk-formats.txt @@ -324,10 +324,49 @@ Network device features: Passed by reference. +Command from struct task_struct + + %pT ls + + For printing executable name excluding path from struct + task_struct. + + Passed by reference. + +Device tree nodes: + + %pO[fnpPcCFr] + + For printing device tree nodes. The optional arguments are: + f device node full_name + n device node name + p device node phandle + P device node path spec (name + @unit) + F device node flags + c major compatible string + C full compatible string + r node reference count + Without any arguments prints full_name (same as %pOf) + The separator when using multiple arguments is '|' + + Examples: + + %pO /foo/bar@0 - Node full name + %pOf /foo/bar@0 - Same as above + %pOfp /foo/bar@0|10 - Node full name + phandle + %pOfcF /foo/bar@0|foo,device|--P- - Node full name + + major compatible string + + node flags + D - dynamic + d - detached + P - Populated + B - Populated bus + + Passed by reference + If you add other %p extensions, please extend lib/test_printf.c with one or more test cases, if at all feasible. - Thank you for your cooperation and attention. diff --git a/MAINTAINERS b/MAINTAINERS index 8c20323..82277f6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2320,6 +2320,14 @@ W: https://linuxtv.org S: Supported F: drivers/media/platform/sti/bdisp +BEAGLEBONE CAPEMANAGER +M: Pantelis Antoniou +S: Maintained +F: drivers/misc/beaglebone-capemgr.c +F: Documentation/misc-devices/bone_capemgr.txt +F: Documentation/devicetree/bindings/misc/bone_capemgr.txt +F: Documentation/ABI/testing/sysfs-devices-platform-bone_capemgr + BEFS FILE SYSTEM S: Orphan F: Documentation/filesystems/befs.txt @@ -11901,6 +11909,13 @@ S: Maintained F: Documentation/usb/ohci.txt F: drivers/usb/host/ohci* +USB Generic Onboard Device Driver +M: Peter Chen +T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git +L: linux-usb@vger.kernel.org +S: Maintained +F: drivers/usb/misc/generic_onboard_device.c + USB OTG FSM (Finite State Machine) M: Peter Chen T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 274e8a6..ecc9e1a 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -13,7 +13,12 @@ # Ensure linker flags are correct LDFLAGS := -LDFLAGS_vmlinux :=-p --no-undefined -X --pic-veneer +GCCVERSIONISGTE5 := $(shell expr `$(HOSTCC) -dumpversion | cut -f1 -d.` \>= 5) +ifeq "$(GCCVERSIONISGTE5)" "1" +LDFLAGS_vmlinux :=-p --no-undefined -X +else +LDFLAGS_vmlinux :=-p --no-undefined -X --pic-veneer +endif ifeq ($(CONFIG_CPU_ENDIAN_BE8),y) LDFLAGS_vmlinux += --be8 LDFLAGS_MODULE += --be8 diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index 5be33a2..dbca2e9 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -27,6 +27,10 @@ export ZRELADDR INITRD_PHYS PARAMS_PHYS targets := Image zImage xipImage bootpImage uImage +ifeq ($(CONFIG_OF_OVERLAY),y) +DTC_FLAGS += -@ +endif + ifeq ($(CONFIG_XIP_KERNEL),y) $(obj)/xipImage: vmlinux FORCE diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 414b427..efd0034 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -1,11 +1,17 @@ ifeq ($(CONFIG_OF),y) +ifeq ($(CONFIG_OF_OVERLAY),y) +DTC_FLAGS += -@ +endif + dtb-$(CONFIG_ARCH_ALPINE) += \ alpine-db.dtb dtb-$(CONFIG_MACH_ARTPEC6) += \ artpec6-devboard.dtb + dtb-$(CONFIG_MACH_ASM9260) += \ alphascale-asm9260-devkit.dtb + # Keep at91 dtb files sorted alphabetically for each SoC dtb-$(CONFIG_SOC_SAM_V4_V5) += \ at91rm9200ek.dtb \ @@ -138,6 +144,7 @@ dtb-$(CONFIG_ARCH_EXYNOS5) += \ exynos5420-arndale-octa.dtb \ exynos5420-peach-pit.dtb \ exynos5420-smdk5420.dtb \ + exynos5422-artik10-eval.dtb \ exynos5422-odroidxu3.dtb \ exynos5422-odroidxu3-lite.dtb \ exynos5422-odroidxu4.dtb \ @@ -327,6 +334,7 @@ dtb-$(CONFIG_SOC_IMX6Q) += \ imx6dl-sabreauto.dtb \ imx6dl-sabrelite.dtb \ imx6dl-sabresd.dtb \ + imx6dl-sabresd-wl1835.dtb \ imx6dl-tx6dl-comtft.dtb \ imx6dl-tx6s-8034.dtb \ imx6dl-tx6s-8035.dtb \ @@ -344,6 +352,7 @@ dtb-$(CONFIG_SOC_IMX6Q) += \ imx6q-b650v3.dtb \ imx6q-b850v3.dtb \ imx6q-cm-fx6.dtb \ + imx6q-ccimx6sbc.dtb \ imx6q-cubox-i.dtb \ imx6q-dfi-fs700-m60.dtb \ imx6q-dmo-edmqmx6.dtb \ @@ -367,6 +376,7 @@ dtb-$(CONFIG_SOC_IMX6Q) += \ imx6q-sabreauto.dtb \ imx6q-sabrelite.dtb \ imx6q-sabresd.dtb \ + imx6q-sabresd-wl1835.dtb \ imx6q-sbc6x.dtb \ imx6q-tbs2910.dtb \ imx6q-tx6q-1010.dtb \ @@ -384,6 +394,7 @@ dtb-$(CONFIG_SOC_IMX6Q) += \ imx6qp-sabresd.dtb dtb-$(CONFIG_SOC_IMX6SL) += \ imx6sl-evk.dtb \ + imx6sl-evk-wl1835.dtb \ imx6sl-warp.dtb dtb-$(CONFIG_SOC_IMX6SX) += \ imx6sx-nitrogen6sx.dtb \ @@ -393,6 +404,7 @@ dtb-$(CONFIG_SOC_IMX6SX) += \ imx6sx-sdb.dtb dtb-$(CONFIG_SOC_IMX6UL) += \ imx6ul-14x14-evk.dtb \ + imx6ul-14x14-evk-ism43362-b81-evb.dtb \ imx6ul-pico-hobbit.dtb \ imx6ul-tx6ul-0010.dtb \ imx6ul-tx6ul-0011.dtb \ @@ -515,6 +527,22 @@ dtb-$(CONFIG_SOC_AM33XX) += \ am335x-base0033.dtb \ am335x-bone.dtb \ am335x-boneblack.dtb \ + am335x-sancloud-bbe.dtb \ + am335x-boneblack-bbb-exp-r.dtb \ + am335x-boneblack-bbb-exp-c.dtb \ + am335x-boneblack-bbbmini.dtb \ + am335x-boneblack-wl1835mod.dtb \ + am335x-boneblack-cape-bone-argus.dtb \ + am335x-bone-cape-bone-argus.dtb \ + am335x-arduino-tre.dtb \ + am335x-bonegreen-wireless.dtb \ + am335x-olimex-som.dtb \ + am335x-abbbi.dtb \ + am335x-bonegreen-overlay.dtb \ + am335x-boneblack-overlay.dtb \ + am335x-boneblack-nhdmi-overlay.dtb \ + am335x-boneblack-hdmi-overlay.dtb \ + am335x-boneblack-emmc-overlay.dtb \ am335x-bonegreen.dtb \ am335x-chiliboard.dtb \ am335x-cm-t335.dtb \ @@ -534,6 +562,7 @@ dtb-$(CONFIG_ARCH_OMAP4) += \ omap4-panda.dtb \ omap4-panda-a4.dtb \ omap4-panda-es.dtb \ + omap4-panda-es-b3.dtb \ omap4-sdp.dtb \ omap4-sdp-es23plus.dtb \ omap4-var-dvk-om44.dtb \ diff --git b/arch/arm/boot/dts/am335x-abbbi.dts b/arch/arm/boot/dts/am335x-abbbi.dts new file mode 100644 index 0000000..accf950 --- /dev/null +++ b/arch/arm/boot/dts/am335x-abbbi.dts @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright 2015 Konsulko Group + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am33xx-overlay-edma-fix.dtsi" + +/ { + model = "Arrow BeagleBone Black Industrial"; + compatible = "arrow,am335x-abbbi", "ti,am335x-bone", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&mmc2 { + vmmc-supply = <&vmmcsd_fixed>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_pins>; + bus-width = <8>; + status = "okay"; +}; + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro@1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + +&am33xx_pinmux { + adi_hdmi_bbbi_pins: adi_hdmi_bbbi_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr0 */ + AM33XX_IOPAD(0x8a0, PIN_OUTPUT | MUX_MODE0) /* lcd_data0.lcd_data0 */ + AM33XX_IOPAD(0x8a4, PIN_OUTPUT | MUX_MODE0) /* lcd_data1.lcd_data1 */ + AM33XX_IOPAD(0x8a8, PIN_OUTPUT | MUX_MODE0) /* lcd_data2.lcd_data2 */ + AM33XX_IOPAD(0x8ac, PIN_OUTPUT | MUX_MODE0) /* lcd_data3.lcd_data3 */ + AM33XX_IOPAD(0x8b0, PIN_OUTPUT | MUX_MODE0) /* lcd_data4.lcd_data4 */ + AM33XX_IOPAD(0x8b4, PIN_OUTPUT | MUX_MODE0) /* lcd_data5.lcd_data5 */ + AM33XX_IOPAD(0x8b8, PIN_OUTPUT | MUX_MODE0) /* lcd_data6.lcd_data6 */ + AM33XX_IOPAD(0x8bc, PIN_OUTPUT | MUX_MODE0) /* lcd_data7.lcd_data7 */ + AM33XX_IOPAD(0x8c0, PIN_OUTPUT | MUX_MODE0) /* lcd_data8.lcd_data8 */ + AM33XX_IOPAD(0x8c4, PIN_OUTPUT | MUX_MODE0) /* lcd_data9.lcd_data9 */ + AM33XX_IOPAD(0x8c8, PIN_OUTPUT | MUX_MODE0) /* lcd_data10.lcd_data10 */ + AM33XX_IOPAD(0x8cc, PIN_OUTPUT | MUX_MODE0) /* lcd_data11.lcd_data11 */ + AM33XX_IOPAD(0x8d0, PIN_OUTPUT | MUX_MODE0) /* lcd_data12.lcd_data12 */ + AM33XX_IOPAD(0x8d4, PIN_OUTPUT | MUX_MODE0) /* lcd_data13.lcd_data13 */ + AM33XX_IOPAD(0x8d8, PIN_OUTPUT | MUX_MODE0) /* lcd_data14.lcd_data14 */ + AM33XX_IOPAD(0x8dc, PIN_OUTPUT | MUX_MODE0) /* lcd_data15.lcd_data15 */ + AM33XX_IOPAD(0x8e0, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_vsync.lcd_vsync */ + AM33XX_IOPAD(0x8e4, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_hsync.lcd_hsync */ + AM33XX_IOPAD(0x8e8, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_pclk.lcd_pclk */ + AM33XX_IOPAD(0x8ec, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_ac_bias_en.lcd_ac_bias_en */ + >; + }; + adi_hdmi_bbbi_off_pins: adi_hdmi_bbbi_off_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr0 */ + >; + }; + + mcasp0_pins: mcasp0_pins { + pinctrl-single,pins = < + 0x1ac (PIN_INPUT_PULLUP | MUX_MODE0) /* mcasp0_ahclkx.mcasp0_ahclkx */ + 0x19c (PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2 */ + 0x194 (PIN_OUTPUT_PULLUP | MUX_MODE0) /* mcasp0_fsx.mcasp0_fsx */ + 0x190 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mcasp0_aclkx.mcasp0_aclkx */ + 0x06c (PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpmc_a11.GPIO1_27 */ + >; + }; + + mcasp0_pins_sleep: mcasp0_pins_sleep { + pinctrl-single,pins = < + 0x1ac (PIN_INPUT_PULLDOWN | MUX_MODE7) /* mcasp0_ahclkx.mcasp0_ahclkx */ + 0x19c (PIN_INPUT_PULLDOWN | MUX_MODE7) /* mcasp0_ahclkr.mcasp0_axr2 */ + 0x194 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* mcasp0_fsx.mcasp0_fsx */ + 0x190 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* mcasp0_aclkx.mcasp0_aclkx */ + 0x06c (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_a11.GPIO1_27 */ + >; + }; +}; + +&lcdc { + status = "okay"; + port { + lcdc_0: endpoint@0 { + remote-endpoint = <&hdmi_0>; + }; + }; +}; + +&i2c0 { + adv7511w { + compatible = "adi,adv7511w"; + reg = <0x39>; + pinctrl-names = "default", "off"; + pinctrl-0 = <&adi_hdmi_bbbi_pins>; + pinctrl-1 = <&adi_hdmi_bbbi_off_pins>; + + port { + hdmi_0: endpoint@0 { + remote-endpoint = <&lcdc_0>; + }; + }; + }; +}; + +&mcasp0 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&mcasp0_pins>; + pinctrl-1 = <&mcasp0_pins_sleep>; + status = "okay"; + op-mode = <0>; /* MCASP_IIS_MODE */ + tdm-slots = <2>; + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ + 0 0 1 0 + >; + tx-num-evt = <1>; + rx-num-evt = <1>; +}; + +/ { + clk_mcasp0_fixed: clk_mcasp0_fixed { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <24576000>; + }; + + clk_mcasp0: clk_mcasp0 { + #clock-cells = <0>; + compatible = "gpio-gate-clock"; + clocks = <&clk_mcasp0_fixed>; + enable-gpios = <&gpio1 27 0>; /* BeagleBone Black Clk enable on GPIO1_27 */ + }; + + hdmi_audio: hdmi_audio@0 { + compatible = "linux,hdmi-audio"; + status = "okay"; + }; + + sound { + compatible = "ti,beaglebone-black-audio"; + ti,model = "TI BeagleBone Black"; + ti,audio-codec = <&hdmi_audio>; + ti,mcasp-controller = <&mcasp0>; + ti,audio-routing = + "HDMI Out", "TX"; + clocks = <&clk_mcasp0>; + clock-names = "mclk"; + }; +}; + +&rtc { + system-power-controller; +}; diff --git b/arch/arm/boot/dts/am335x-arduino-tre.dts b/arch/arm/boot/dts/am335x-arduino-tre.dts new file mode 100644 index 0000000..9e27df0 --- /dev/null +++ b/arch/arm/boot/dts/am335x-arduino-tre.dts @@ -0,0 +1,576 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" + +/ { + model = "TI AM335x Arduino Tre"; + compatible = "ti,am335x-arduino-tre", "ti,am335x-boneblack", "ti,am335x-bone", "ti,am33xx"; + + cpus { + cpu@0 { + cpu0-supply = <&dcdc2_reg>; + }; + }; + + memory { + device_type = "memory"; + reg = <0x80000000 0x10000000>; /* 512 MB */ + }; + + leds { + pinctrl-names = "default"; + pinctrl-0 = <&userled_pins>; + + compatible = "gpio-leds"; + + led@0 { + label = "arduino_tre:yel:usr0"; + gpios = <&gpio1 21 0>; + linux,default-trigger = "mmc0"; + default-state = "off"; + }; + + led@1 { + label = "arduino_tre:red:usr1"; + gpios = <&gpio1 22 0>; + linux,default-trigger = "none"; + default-state = "off"; + }; + + led@2 { + label = "arduino_tre:blu:usr2"; + gpios = <&gpio1 23 0>; + linux,default-trigger = "none"; + default-state = "off"; + }; + + led@3 { + label = "arduino_tre:grn:usr3"; + gpios = <&gpio1 24 0>; + linux,default-trigger = "cpu0"; + default-state = "off"; + }; + }; + + hdmi { + compatible = "ti,tilcdc,slave"; + i2c = <&i2c0>; + pinctrl-names = "default", "off"; + pinctrl-0 = <&nxp_hdmi_bonelt_pins>; + pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; + status = "okay"; + }; + + sound { + compatible = "ti,da830-evm-audio"; + ti,model = "DA830 EVM"; + ti,audio-codec = <&tlv320aic3x>; + ti,mcasp-controller = <&mcasp0>; + ti,codec-clock-rate = <12000000>; + ti,audio-routing = + "Headphone Jack", "HPLOUT", + "Headphone Jack", "HPROUT", + "LINE2L", "Line In", + "LINE2R", "Line In"; + }; + + vmmcsd_fixed: fixedregulator@0 { + compatible = "regulator-fixed"; + regulator-name = "vmmcsd_fixed"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; +}; + +&am33xx_pinmux { + pinctrl-names = "default"; + pinctrl-0 = <&userled_pins>; + + userled_pins: pinmux_userled_pins { + pinctrl-single,pins = < + 0x54 0x7 /* gpmc_a5.gpio1_21, OUTPUT | MODE7 */ + 0x58 0x17 /* gpmc_a6.gpio1_22, OUTPUT_PULLUP | MODE7 */ + 0x5c 0x7 /* gpmc_a7.gpio1_23, OUTPUT | MODE7 */ + 0x60 0x17 /* gpmc_a8.gpio1_24, OUTPUT_PULLUP | MODE7 */ + >; + }; + + can_bus_pins: pinmux_can_bus_pins { + pinctrl-single,pins = < + 0x120 0x31 /* DCAN0_RX MODE1 */ + 0x11c 0x01 /* DCAN0_TX MODE1 */ + >; + }; + + cpsw_default: cpsw_default { + pinctrl-single,pins = < + /* Slave 1 */ + 0x110 (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxerr.mii1_rxerr */ + 0x114 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txen.mii1_txen */ + 0x118 (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxdv.mii1_rxdv */ + 0x11c (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd3.mii1_txd3 */ + 0x120 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd2.mii1_txd2 */ + 0x124 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd1.mii1_txd1 */ + 0x128 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd0.mii1_txd0 */ + 0x12c (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_txclk.mii1_txclk */ + 0x130 (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxclk.mii1_rxclk */ + 0x134 (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd3.mii1_rxd3 */ + 0x138 (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd2.mii1_rxd2 */ + 0x13c (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd1.mii1_rxd1 */ + 0x140 (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd0.mii1_rxd0 */ + >; + }; + + cpsw_sleep: cpsw_sleep { + pinctrl-single,pins = < + /* Slave 1 reset value */ + 0x110 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x114 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x118 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x11c (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x120 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x124 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x128 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x12c (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x130 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x134 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x138 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x13c (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x140 (PIN_INPUT_PULLDOWN | MUX_MODE7) + >; + }; + + emac_rmii1_pins: pinmux_emac_rmii1_pins { + pinctrl-single,pins = < + 0x10c (PIN_INPUT_PULLDOWN | MUX_MODE1) /* mii1_crs.rmii1_crs_dv */ + 0x110 (PIN_INPUT_PULLDOWN | MUX_MODE1) /* mii1_rxerr.rmii1_rxerr */ + 0x114 (PIN_OUTPUT | MUX_MODE1) /* mii1_txen.rmii1_txen */ + 0x124 (PIN_OUTPUT | MUX_MODE1) /* mii1_txd1.rmii1_txd1 */ + 0x128 (PIN_OUTPUT | MUX_MODE1) /* mii1_txd0.rmii1_txd0 */ + 0x13c (PIN_INPUT_PULLDOWN | MUX_MODE1) /* mii1_rxd1.rmii1_rxd1 */ + 0x140 (PIN_INPUT_PULLDOWN | MUX_MODE1) /* mii1_rxd0.rmii1_rxd0 */ + 0x144 (PIN_INPUT_PULLDOWN | MUX_MODE0) /* rmii1_refclk.rmii1_refclk */ + >; + }; + + davinci_mdio_default: davinci_mdio_default { + pinctrl-single,pins = < + /* MDIO */ + 0x148 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE0) /* mdio_data.mdio_data */ + 0x14c (PIN_OUTPUT_PULLUP | MUX_MODE0) /* mdio_clk.mdio_clk */ + >; + }; + + davinci_mdio_sleep: davinci_mdio_sleep { + pinctrl-single,pins = < + /* MDIO reset value */ + 0x148 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x14c (PIN_INPUT_PULLDOWN | MUX_MODE7) + >; + }; + + i2c0_pins: pinmux_i2c0_pins { + pinctrl-single,pins = < + 0x188 0x70 /* i2c0_sda, SLEWCTRL_SLOW | INPUT_PULLUP | MODE0 */ + 0x18c 0x70 /* i2c0_scl, SLEWCTRL_SLOW | INPUT_PULLUP | MODE0 */ + >; + }; + + i2c1_pins: pinmux_i2c1_pins { + pinctrl-single,pins = < + 0x158 0x72 /*spi0_d1-i2c1_sda,SLEWCTRL_SLOW | INPUT_PULLUP | MODE2*/ + 0x15c 0x72 /*spi0_cs0-i2c1_scl,SLEWCTRL_SLOW | INPUT_PULLUP |MODE2*/ + >; + }; + + i2c2_pins: pinmux_i2c2_pins { + pinctrl-single,pins = < + 0x150 0x72 /*spi0_scl.i2c2_sda,SLEWCTRL_SLOW | INPUT_PULLUP |MODE2*/ + 0x154 0x72 /*spi0_d0.i2c2_scl,SLEWCTRL_SLOW | INPUT_PULLUP | MODE2*/ + >; + }; + + mmc1_pins_default: pinmux_mmc1_pins { + pinctrl-single,pins = < + 0x0F0 (PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_dat3.mmc0_dat3 */ + 0x0F4 (PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_dat2.mmc0_dat2 */ + 0x0F8 (PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_dat1.mmc0_dat1 */ + 0x0FC (PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_dat0.mmc0_dat0 */ + 0x100 (PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_clk.mmc0_clk */ + 0x104 (PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_cmd.mmc0_cmd */ + 0x1A0 (PIN_INPUT_PULLUP | MUX_MODE7) /* mcasp0_aclkr.gpio3_18 */ + 0x160 (PIN_INPUT | MUX_MODE7) /* spi0_cs1.gpio0_6 */ + >; + }; + + mmc1_pins_sleep: pinmux_mmc1_pins_sleep { + pinctrl-single,pins = < + 0x0F0 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x0F4 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x0F8 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x0FC (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x100 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x104 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x1A0 (PIN_INPUT_PULLDOWN | MUX_MODE7) + >; + }; + + tre_ehrpwm1_pins: pinmux_tre_ehrpwm1_pins { + pinctrl-single,pins = < + 0x48 0x06 /* PWM1A ~102 MODE6 */ + 0x4c 0x06 /* PWM1B ~103 MODE6 */ + >; + }; + + tre_ehrpwm2_pins: pinmux_tre_ehrpwm2_pins { + pinctrl-single,pins = < + 0x20 0x04 /* PWM2A ~100 MODE4 */ + 0x24 0x04 /* PWM2B ~101 MODE4 */ + >; + }; + + nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { + pinctrl-single,pins = < + 0x1b0 0x03 /* xdma_event_intr0, OMAP_MUX_MODE3 | AM33XX_PIN_OUTPUT */ + 0xa0 0x08 /* lcd_data0.lcd_data0, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xa4 0x08 /* lcd_data1.lcd_data1, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xa8 0x08 /* lcd_data2.lcd_data2, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xac 0x08 /* lcd_data3.lcd_data3, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xb0 0x08 /* lcd_data4.lcd_data4, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xb4 0x08 /* lcd_data5.lcd_data5, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xb8 0x08 /* lcd_data6.lcd_data6, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xbc 0x08 /* lcd_data7.lcd_data7, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xc0 0x08 /* lcd_data8.lcd_data8, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xc4 0x08 /* lcd_data9.lcd_data9, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xc8 0x08 /* lcd_data10.lcd_data10, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xcc 0x08 /* lcd_data11.lcd_data11, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xd0 0x08 /* lcd_data12.lcd_data12, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xd4 0x08 /* lcd_data13.lcd_data13, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xd8 0x08 /* lcd_data14.lcd_data14, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xdc 0x08 /* lcd_data15.lcd_data15, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xe0 0x00 /* lcd_vsync.lcd_vsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */ + 0xe4 0x00 /* lcd_hsync.lcd_hsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */ + 0xe8 0x00 /* lcd_pclk.lcd_pclk, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */ + 0xec 0x00 /* lcd_ac_bias_en.lcd_ac_bias_en, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */ + >; + }; + + nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { + pinctrl-single,pins = < + 0x1b0 0x03 /* xdma_event_intr0, OMAP_MUX_MODE3 | AM33XX_PIN_OUTPUT */ + >; + }; + + tre_audio_pins: pinmux_tre_audio_pins { + pinctrl-single,pins = < + 0x1ac 0x00 /*mcasp0_ahclkx (AUD_MCLK)->12MHz, INPUT | MODE0*/ + 0x190 0x20 /* mcasp0_aclkx (AUD_BCLK)->, INPUT | MODE0*/ + 0x194 0x20 /* mcasp0_fsx (AUD_FSX)-> , INPUT | MODE0*/ + 0x198 0x20 /* mcasp0_axr0 (AUD_DIN)<-, INPUT | MODE0*/ + 0x19c 0x22 /* mcasp0_ahclkr-_axr2 (AUD_DOUT)->, | MODE2*/ + >; + }; + + spi1_pins: pinmux_spi1_pins { + pinctrl-single,pins = < + 0x168 0x14 /* MOSI1 OUTPUT_PULLUP | MODE0 */ + 0x16c 0x34 /* MISO1 INPUT_PULLUP | MODE0 */ + 0x108 0x12 /* SCK1 OUTPUT_PULLUP | MODE0 */ + 0x164 0x12 /* SS1 OUTPUT_PULLUP | MODE0 */ + >; + }; + + uart0_pins: pinmux_uart0_pins { + pinctrl-single,pins = < + 0x170 (PIN_INPUT_PULLUP | MUX_MODE0) /* uart0_rxd.uart0_rxd */ + 0x174 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* uart0_txd.uart0_txd */ + >; + }; + + uart1_pins: pinmux_uart1_pins { + pinctrl-single,pins = < + 0x180 0x30 /* UART1_rxd PULL_UP | MODE0 */ + 0x184 0x00 /* UART1_txd MODE0 */ + >; + }; + + uart2_pins: pinmux_uart2_pins { + pinctrl-single,pins = < + 0x12c 0x31 /* UART2_rxd PULL_UP | MODE1 */ + 0x130 0x01 /* UART2_txd MODE1 */ + >; + }; + + uart4_pins: pinmux_uart4_pins { + pinctrl-single,pins = < + 0x70 0x36 /* UART4_rxd PULL_UP | MODE6 */ + 0x74 0x06 /* UART4_txd MODE6 */ + >; + }; +}; + +&dcan0 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&can_bus_pins>; +}; + +&i2c0 { + status = "okay"; + clock-frequency = <400000>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + + tps: tps@24 { + reg = <0x24>; + }; + + rtc@6f { + compatible = "microchip,mcp7941x"; + reg = <0x6f>; + }; + + tlv320aic3x: tlv320aic3x@18 { + compatible = "ti,tlv320aic3x"; + reg = <0x18>; + status = "okay"; + }; +}; + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + + clock-frequency = <100000>; +}; + +&i2c2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; + + clock-frequency = <100000>; +}; + +&epwmss1 { + status = "okay"; +}; + +&ehrpwm1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&tre_ehrpwm1_pins>; +}; + +&epwmss2 { + status = "okay"; +}; + +&ehrpwm2 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&tre_ehrpwm2_pins>; +}; + +&lcdc { + status = "okay"; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; + + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&uart2_pins>; + + status = "okay"; +}; + +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&uart4_pins>; + + status = "okay"; +}; + +&usb { + status = "okay"; + + control@44e10620 { + status = "okay"; + }; + + usb-phy@47401300 { + status = "okay"; + }; + + usb-phy@47401b00 { + status = "okay"; + }; + + usb@47401000 { + status = "okay"; + dr_mode = "peripheral"; + }; + + usb@47401800 { + status = "okay"; + dr_mode = "host"; + }; + + dma-controller@47402000 { + status = "okay"; + }; +}; + +&tps { + compatible = "ti,tps65217"; + ti,pmic-shutdown-controller; + + interrupt-parent = <&intc>; + interrupts = <7>; /* NNMI */ + + regulators { + #address-cells = <1>; + #size-cells = <0>; + + dcdc1_reg: regulator@0 { + reg = <0>; + regulator-name = "vdd_ddr"; + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + regulator-boot-on; + regulator-always-on; + }; + + dcdc2_reg: regulator@1 { + reg = <1>; + /* VDD_MPU voltage limits 0.95V - 1.26V with +/-4% tolerance */ + regulator-name = "vdd_mpu"; + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <1325000>; + regulator-boot-on; + regulator-always-on; + }; + + dcdc3_reg: regulator@2 { + reg = <2>; + /* VDD_CORE voltage limits 0.95V - 1.1V with +/-4% tolerance */ + regulator-name = "vdd_core"; + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <1150000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo1_reg: regulator@3 { + reg = <3>; + regulator-always-on; + }; + + ldo2_reg: regulator@4 { + reg = <4>; + regulator-always-on; + }; + + ldo3_reg: regulator@5 { + reg = <5>; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo4_reg: regulator@6 { + reg = <6>; + regulator-always-on; + }; + + rtc@44e3e000 { + ti,system-power-controller; + }; + }; +}; + +&mcasp0 { + pinctrl-names = "default"; + pinctrl-0 = <&tre_audio_pins>; + + status = "okay"; + + op-mode = <0>; /* MCASP_IIS_MODE */ + tdm-slots = <2>; + num-serializer = <16>; + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ + 2 0 1 0 + 0 0 0 0 + 0 0 0 0 + 0 0 0 0 + >; + tx-num-evt = <1>; + rx-num-evt = <1>; +}; + +&mac { + slaves = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&emac_rmii1_pins>; + status = "okay"; +}; + +&davinci_mdio { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&davinci_mdio_default>; + pinctrl-1 = <&davinci_mdio_sleep>; + status = "okay"; +}; + +&cpsw_emac0 { + phy_id = <&davinci_mdio>, <0>; + phy-mode = "rmii"; +}; + +&phy_sel { + rmii-clock-ext; +}; + +&mmc1 { + status = "okay"; + bus-width = <0x4>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&mmc1_pins_default>; + pinctrl-1 = <&mmc1_pins_sleep>; + cd-gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>; + cd-inverted; + vmmc-supply = <&vmmcsd_fixed>; +}; + +&rtc { + system-power-controller; +}; + +&sham { + status = "okay"; +}; + +&aes { + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-bone-argus.dtsi b/arch/arm/boot/dts/am335x-bone-argus.dtsi new file mode 100644 index 0000000..21afad3 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-argus.dtsi @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 + +/ { + ocp { + P8_07_pinmux { + /* gpio2[2] */ + status = "disabled"; + }; + P8_08_pinmux { + /* gpio2[3] */ + status = "disabled"; + }; + P8_09_pinmux { + /* gpio2[5] */ + status = "disabled"; + }; + P8_10_pinmux { + /* gpio2[4] */ + status = "disabled"; + }; + P9_11_pinmux { + /* gpio0[30] */ + status = "disabled"; + }; + P9_17_pinmux { + /* gpio0[5] */ + status = "disabled"; + }; + P9_18_pinmux { + /* gpio0[4] */ + status = "disabled"; + }; + P9_41_pinmux { + /* gpio0[20] */ + status = "disabled"; + }; + P9_42_pinmux { + /* gpio0[7] */ + status = "disabled"; + }; + }; +}; + +/ { + argus-ups { + compatible = "argus-ups"; + status = "okay"; + + pinctrl-names = "default"; + pinctrl-0 = <&argus_ups_pins>; /* Refer to previous label */ + /* This section communicates the gpio numbers to the driver module */ + /* Note that gpio controllers appear to be numbered from 1-n here rather than 0-(n-1)????? */ + gpios = <&gpio0 30 0>, /* Request */ + <&gpio0 5 0>, /* Acknowledge */ + <&gpio0 4 0>, /* Watchdog */ + <&gpio2 2 0>, /* LED 1 Green */ + <&gpio2 3 0>, /* LED 1 Red */ + <&gpio2 5 0>, /* LED 2 Green */ + <&gpio2 4 0>, /* LED 2 Red */ + <&gpio0 20 0>, /* General Output #1 */ + <&gpio0 7 0>; /* General Output #2 */ + debug = <1>; + shutdown = <1>; + }; +}; + +&am33xx_pinmux { + argus_ups_pins: pinmux_argus_ups_pins { /* Set up pinmux */ + pinctrl-single,pins = < + 0x070 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_wait0.gpio0_30 */ + 0x15c (PIN_OUTPUT_PULLUP | MUX_MODE7) /* spi0_cs0.gpio0_5 */ + 0x158 (PIN_OUTPUT_PULLUP | MUX_MODE7) /* spi0_d1.gpio0_4 */ + 0x090 (PIN_OUTPUT_PULLUP | MUX_MODE7) /* gpmc_advn_ale.gpio_2 */ + 0x094 (PIN_OUTPUT_PULLUP | MUX_MODE7) /* gpmc_oen_ren.gpio2_3 */ + 0x09c (PIN_OUTPUT_PULLUP | MUX_MODE7) /* gpmc_ben0_cle.gpio2_5 */ + 0x098 (PIN_OUTPUT_PULLUP | MUX_MODE7) /* gpmc_gpmc_wen.gpio2_4 */ + 0x1b4 (PIN_OUTPUT_PULLUP | MUX_MODE7) /* xdma_event_intr1.gpio0_20 */ + 0x164 (PIN_OUTPUT_PULLUP | MUX_MODE7) /* ecap0_in_pwm0_out.gpio0_7 */ + >; + }; + + i2c2_pins: pinmux_i2c2_pins { + pinctrl-single,pins = < + BONE_P9_20 0x73 /* (SLEWCTRL_SLOW | PIN_INPUT_PULLUP | MUX_MODE3) uart1_ctsn.i2c2_sda */ + BONE_P9_19 0x73 /* (SLEWCTRL_SLOW | PIN_INPUT_PULLUP | MUX_MODE3) uart1_rtsn.i2c2_scl */ + >; + }; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; + + status = "okay"; + clock-frequency = <100000>; + + rtc@68 { + compatible = "maxim,ds1307"; + reg = <0x68>; + }; +}; diff --git b/arch/arm/boot/dts/am335x-bone-cape-bone-argus.dts b/arch/arm/boot/dts/am335x-bone-cape-bone-argus.dts new file mode 100644 index 0000000..82218f5 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-cape-bone-argus.dts @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common-no-capemgr.dtsi" + +/ { + model = "TI AM335x BeagleBone"; + compatible = "ti,am335x-bone", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&ldo3_reg>; +}; + +#include "am335x-bone-argus.dtsi" diff --git b/arch/arm/boot/dts/am335x-bone-common-no-capemgr.dtsi b/arch/arm/boot/dts/am335x-bone-common-no-capemgr.dtsi new file mode 100644 index 0000000..12464cc --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-common-no-capemgr.dtsi @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +/ { + cpus { + cpu@0 { + cpu0-supply = <&dcdc2_reg>; + }; + }; + + memory { + device_type = "memory"; + reg = <0x80000000 0x10000000>; /* 256 MB */ + }; + + leds { + pinctrl-names = "default"; + pinctrl-0 = <&user_leds_s0>; + + compatible = "gpio-leds"; + + led@2 { + label = "beaglebone:green:usr0"; + gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + + led@3 { + label = "beaglebone:green:usr1"; + gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc0"; + default-state = "off"; + }; + + led@4 { + label = "beaglebone:green:usr2"; + gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "cpu0"; + default-state = "off"; + }; + + led@5 { + label = "beaglebone:green:usr3"; + gpios = <&gpio1 24 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "mmc1"; + default-state = "off"; + }; + }; + + vmmcsd_fixed: fixedregulator@0 { + compatible = "regulator-fixed"; + regulator-name = "vmmcsd_fixed"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; +}; + +&am33xx_pinmux { + user_leds_s0: user_leds_s0 { + pinctrl-single,pins = < + AM33XX_IOPAD(0x854, PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpmc_a5.gpio1_21 */ + AM33XX_IOPAD(0x858, PIN_OUTPUT_PULLUP | MUX_MODE7) /* gpmc_a6.gpio1_22 */ + AM33XX_IOPAD(0x85c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpmc_a7.gpio1_23 */ + AM33XX_IOPAD(0x860, PIN_OUTPUT_PULLUP | MUX_MODE7) /* gpmc_a8.gpio1_24 */ + >; + }; + + i2c0_pins: pinmux_i2c0_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x988, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c0_sda.i2c0_sda */ + AM33XX_IOPAD(0x98c, PIN_INPUT_PULLUP | MUX_MODE0) /* i2c0_scl.i2c0_scl */ + >; + }; + + i2c2_pins: pinmux_i2c2_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x978, PIN_INPUT_PULLUP | MUX_MODE3) /* uart1_ctsn.i2c2_sda */ + AM33XX_IOPAD(0x97c, PIN_INPUT_PULLUP | MUX_MODE3) /* uart1_rtsn.i2c2_scl */ + >; + }; + + uart0_pins: pinmux_uart0_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x970, PIN_INPUT_PULLUP | MUX_MODE0) /* uart0_rxd.uart0_rxd */ + AM33XX_IOPAD(0x974, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* uart0_txd.uart0_txd */ + >; + }; + + cpsw_default: cpsw_default { + pinctrl-single,pins = < + /* Slave 1 */ + 0x108 (PIN_INPUT | MUX_MODE0) /* mii1_col.mii1_col */ + 0x10c (PIN_INPUT | MUX_MODE0) /* mii1_crs.mii1_crs */ + AM33XX_IOPAD(0x910, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxerr.mii1_rxerr */ + AM33XX_IOPAD(0x914, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txen.mii1_txen */ + AM33XX_IOPAD(0x918, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxdv.mii1_rxdv */ + AM33XX_IOPAD(0x91c, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd3.mii1_txd3 */ + AM33XX_IOPAD(0x920, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd2.mii1_txd2 */ + AM33XX_IOPAD(0x924, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd1.mii1_txd1 */ + AM33XX_IOPAD(0x928, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd0.mii1_txd0 */ + AM33XX_IOPAD(0x92c, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_txclk.mii1_txclk */ + AM33XX_IOPAD(0x930, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxclk.mii1_rxclk */ + AM33XX_IOPAD(0x934, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd3.mii1_rxd3 */ + AM33XX_IOPAD(0x938, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd2.mii1_rxd2 */ + AM33XX_IOPAD(0x93c, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd1.mii1_rxd1 */ + AM33XX_IOPAD(0x940, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd0.mii1_rxd0 */ + >; + }; + + cpsw_sleep: cpsw_sleep { + pinctrl-single,pins = < + /* Slave 1 reset value */ + 0x108 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x10c (PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x910, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x914, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x918, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x91c, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x920, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x924, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x928, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x92c, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x930, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x934, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x938, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x93c, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x940, PIN_INPUT_PULLDOWN | MUX_MODE7) + >; + }; + + davinci_mdio_default: davinci_mdio_default { + pinctrl-single,pins = < + /* MDIO */ + AM33XX_IOPAD(0x948, PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE0) /* mdio_data.mdio_data */ + AM33XX_IOPAD(0x94c, PIN_OUTPUT_PULLUP | MUX_MODE0) /* mdio_clk.mdio_clk */ + >; + }; + + davinci_mdio_sleep: davinci_mdio_sleep { + pinctrl-single,pins = < + /* MDIO reset value */ + AM33XX_IOPAD(0x948, PIN_INPUT_PULLDOWN | MUX_MODE7) + AM33XX_IOPAD(0x94c, PIN_INPUT_PULLDOWN | MUX_MODE7) + >; + }; + + mmc1_pins: pinmux_mmc1_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x960, PIN_INPUT | MUX_MODE7) /* GPIO0_6 */ + >; + }; + + emmc_pins: pinmux_emmc_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x880, PIN_INPUT_PULLUP | MUX_MODE2) /* gpmc_csn1.mmc1_clk */ + AM33XX_IOPAD(0x884, PIN_INPUT_PULLUP | MUX_MODE2) /* gpmc_csn2.mmc1_cmd */ + AM33XX_IOPAD(0x800, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad0.mmc1_dat0 */ + AM33XX_IOPAD(0x804, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad1.mmc1_dat1 */ + AM33XX_IOPAD(0x808, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad2.mmc1_dat2 */ + AM33XX_IOPAD(0x80c, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad3.mmc1_dat3 */ + AM33XX_IOPAD(0x810, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad4.mmc1_dat4 */ + AM33XX_IOPAD(0x814, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad5.mmc1_dat5 */ + AM33XX_IOPAD(0x818, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad6.mmc1_dat6 */ + AM33XX_IOPAD(0x81c, PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad7.mmc1_dat7 */ + >; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; + + status = "okay"; +}; + +&usb { + status = "okay"; +}; + +&usb_ctrl_mod { + status = "okay"; +}; + +&usb0_phy { + status = "okay"; +}; + +&usb1_phy { + status = "okay"; +}; + +&usb0 { + status = "okay"; + dr_mode = "peripheral"; +}; + +&usb1 { + status = "okay"; + dr_mode = "host"; +}; + +&cppi41dma { + status = "okay"; +}; + +&i2c0 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + + status = "okay"; + clock-frequency = <400000>; + + tps: tps@24 { + reg = <0x24>; + }; + + baseboard_eeprom: baseboard_eeprom@50 { + compatible = "at,24c256"; + reg = <0x50>; + + #address-cells = <1>; + #size-cells = <1>; + baseboard_data: baseboard_data@0 { + reg = <0 0x100>; + }; + }; +}; + +/include/ "tps65217.dtsi" + +&tps { + /* + * Configure pmic to enter OFF-state instead of SLEEP-state ("RTC-only + * mode") at poweroff. Most BeagleBone versions do not support RTC-only + * mode and risk hardware damage if this mode is entered. + * + * For details, see linux-omap mailing list May 2015 thread + * [PATCH] ARM: dts: am335x-bone* enable pmic-shutdown-controller + * In particular, messages: + * http://www.spinics.net/lists/linux-omap/msg118585.html + * http://www.spinics.net/lists/linux-omap/msg118615.html + * + * You can override this later with + * &tps { /delete-property/ ti,pmic-shutdown-controller; } + * if you want to use RTC-only mode and made sure you are not affected + * by the hardware problems. (Tip: double-check by performing a current + * measurement after shutdown: it should be less than 1 mA.) + */ + ti,pmic-shutdown-controller; + + interrupt-parent = <&intc>; + interrupts = <7>; /* NNMI */ + + regulators { + dcdc1_reg: regulator@0 { + regulator-name = "vdds_dpr"; + regulator-always-on; + }; + + dcdc2_reg: regulator@1 { + /* VDD_MPU voltage limits 0.95V - 1.26V with +/-4% tolerance */ + regulator-name = "vdd_mpu"; + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <1351500>; + regulator-boot-on; + regulator-always-on; + }; + + dcdc3_reg: regulator@2 { + /* VDD_CORE voltage limits 0.95V - 1.1V with +/-4% tolerance */ + regulator-name = "vdd_core"; + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <1150000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo1_reg: regulator@3 { + regulator-name = "vio,vrtc,vdds"; + regulator-always-on; + }; + + ldo2_reg: regulator@4 { + regulator-name = "vdd_3v3aux"; + regulator-always-on; + }; + + ldo3_reg: regulator@5 { + regulator-name = "vdd_1v8"; + regulator-always-on; + }; + + ldo4_reg: regulator@6 { + regulator-name = "vdd_3v3a"; + regulator-always-on; + }; + }; +}; + +&cpsw_emac0 { + phy_id = <&davinci_mdio>, <0>; + phy-mode = "mii"; +}; + +&mac { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&cpsw_default>; + pinctrl-1 = <&cpsw_sleep>; + slaves = <1>; + status = "okay"; +}; + +&davinci_mdio { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&davinci_mdio_default>; + pinctrl-1 = <&davinci_mdio_sleep>; + status = "okay"; +}; + +&mmc1 { + status = "okay"; + bus-width = <0x4>; + pinctrl-names = "default"; + pinctrl-0 = <&mmc1_pins>; + cd-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>; +}; + +&aes { + status = "okay"; +}; + +&sham { + status = "okay"; +}; + +&rtc { + system-power-controller; +}; diff --git b/arch/arm/boot/dts/am335x-bone-common-universal.dtsi b/arch/arm/boot/dts/am335x-bone-common-universal.dtsi new file mode 100644 index 0000000..781e33f --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-common-universal.dtsi @@ -0,0 +1,2052 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +&am33xx_pinmux { + /************************/ + /* P8 Header */ + /************************/ + + /* P8_01 GND */ + /* P8_02 GND */ + /* P8_03 (ZCZ ball R9 ) emmc */ + /* P8_04 (ZCZ ball T9 ) emmc */ + /* P8_05 (ZCZ ball R8 ) emmc */ + /* P8_06 (ZCZ ball T8 ) emmc */ + + /* P8_07 (ZCZ ball R7 ) */ + P8_07_default_pin: pinmux_P8_07_default_pin { + pinctrl-single,pins = <0x090 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_07_gpio_pin: pinmux_P8_07_gpio_pin { + pinctrl-single,pins = <0x090 0x2F>; }; /* Mode 7, RxActive */ + P8_07_gpio_pu_pin: pinmux_P8_07_gpio_pu_pin { + pinctrl-single,pins = <0x090 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_07_gpio_pd_pin: pinmux_P8_07_gpio_pd_pin { + pinctrl-single,pins = <0x090 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_07_timer_pin: pinmux_P8_07_timer_pin { + pinctrl-single,pins = <0x090 0x32>; }; /* Mode 2, Pull-Up, RxActive */ + + /* P8_08 (ZCZ ball T7 ) */ + P8_08_default_pin: pinmux_P8_08_default_pin { + pinctrl-single,pins = <0x094 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_08_gpio_pin: pinmux_P8_08_gpio_pin { + pinctrl-single,pins = <0x094 0x2F>; }; /* Mode 7, RxActive */ + P8_08_gpio_pu_pin: pinmux_P8_08_gpio_pu_pin { + pinctrl-single,pins = <0x094 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_08_gpio_pd_pin: pinmux_P8_08_gpio_pd_pin { + pinctrl-single,pins = <0x094 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_08_timer_pin: pinmux_P8_08_timer_pin { + pinctrl-single,pins = <0x094 0x32>; }; /* Mode 2, Pull-Up, RxActive */ + + /* P8_09 (ZCZ ball T6 ) */ + P8_09_default_pin: pinmux_P8_09_default_pin { + pinctrl-single,pins = <0x09c 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_09_gpio_pin: pinmux_P8_09_gpio_pin { + pinctrl-single,pins = <0x09c 0x2F>; }; /* Mode 7, RxActive */ + P8_09_gpio_pu_pin: pinmux_P8_09_gpio_pu_pin { + pinctrl-single,pins = <0x09c 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_09_gpio_pd_pin: pinmux_P8_09_gpio_pd_pin { + pinctrl-single,pins = <0x09c 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_09_timer_pin: pinmux_P8_09_timer_pin { + pinctrl-single,pins = <0x09c 0x32>; }; /* Mode 2, Pull-Up, RxActive */ + + /* P8_10 (ZCZ ball U6 ) */ + P8_10_default_pin: pinmux_P8_10_default_pin { + pinctrl-single,pins = <0x098 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_10_gpio_pin: pinmux_P8_10_gpio_pin { + pinctrl-single,pins = <0x098 0x2F>; }; /* Mode 7, RxActive */ + P8_10_gpio_pu_pin: pinmux_P8_10_gpio_pu_pin { + pinctrl-single,pins = <0x098 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_10_gpio_pd_pin: pinmux_P8_10_gpio_pd_pin { + pinctrl-single,pins = <0x098 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_10_timer_pin: pinmux_P8_10_timer_pin { + pinctrl-single,pins = <0x098 0x32>; }; /* Mode 2, Pull-Up, RxActive */ + + /* P8_11 (ZCZ ball R12) */ + P8_11_default_pin: pinmux_P8_11_default_pin { + pinctrl-single,pins = <0x034 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_11_gpio_pin: pinmux_P8_11_gpio_pin { + pinctrl-single,pins = <0x034 0x2F>; }; /* Mode 7, RxActive */ + P8_11_gpio_pu_pin: pinmux_P8_11_gpio_pu_pin { + pinctrl-single,pins = <0x034 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_11_gpio_pd_pin: pinmux_P8_11_gpio_pd_pin { + pinctrl-single,pins = <0x034 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_11_pruout_pin: pinmux_P8_11_pruout_pin { + pinctrl-single,pins = <0x034 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_11_qep_pin: pinmux_P8_11_qep_pin { + pinctrl-single,pins = <0x034 0x24>; }; /* Mode 4, Pull-Down, RxActive */ + + /* P8_12 (ZCZ ball T12) */ + P8_12_default_pin: pinmux_P8_12_default_pin { + pinctrl-single,pins = <0x030 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_12_gpio_pin: pinmux_P8_12_gpio_pin { + pinctrl-single,pins = <0x030 0x2F>; }; /* Mode 7, RxActive */ + P8_12_gpio_pu_pin: pinmux_P8_12_gpio_pu_pin { + pinctrl-single,pins = <0x030 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_12_gpio_pd_pin: pinmux_P8_12_gpio_pd_pin { + pinctrl-single,pins = <0x030 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_12_pruout_pin: pinmux_P8_12_pruout_pin { + pinctrl-single,pins = <0x030 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_12_qep_pin: pinmux_P8_12_qep_pin { + pinctrl-single,pins = <0x030 0x24>; }; /* Mode 4, Pull-Down, RxActive */ + + /* P8_13 (ZCZ ball T10) */ + P8_13_default_pin: pinmux_P8_13_default_pin { + pinctrl-single,pins = <0x024 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_13_gpio_pin: pinmux_P8_13_gpio_pin { + pinctrl-single,pins = <0x024 0x2F>; }; /* Mode 7, RxActive */ + P8_13_gpio_pu_pin: pinmux_P8_13_gpio_pu_pin { + pinctrl-single,pins = <0x024 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_13_gpio_pd_pin: pinmux_P8_13_gpio_pd_pin { + pinctrl-single,pins = <0x024 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_13_pwm_pin: pinmux_P8_13_pwm_pin { + pinctrl-single,pins = <0x024 0x24>; }; /* Mode 4, Pull-Down, RxActive */ + + /* P8_14 (ZCZ ball T11) */ + P8_14_default_pin: pinmux_P8_14_default_pin { + pinctrl-single,pins = <0x028 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_14_gpio_pin: pinmux_P8_14_gpio_pin { + pinctrl-single,pins = <0x028 0x2F>; }; /* Mode 7, RxActive */ + P8_14_gpio_pu_pin: pinmux_P8_14_gpio_pu_pin { + pinctrl-single,pins = <0x028 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_14_gpio_pd_pin: pinmux_P8_14_gpio_pd_pin { + pinctrl-single,pins = <0x028 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_14_pwm_pin: pinmux_P8_14_pwm_pin { + pinctrl-single,pins = <0x028 0x24>; }; /* Mode 4, Pull-Down, RxActive */ + + /* P8_15 (ZCZ ball U13) */ + P8_15_default_pin: pinmux_P8_15_default_pin { + pinctrl-single,pins = <0x03c 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_15_gpio_pin: pinmux_P8_15_gpio_pin { + pinctrl-single,pins = <0x03c 0x2F>; }; /* Mode 7, RxActive */ + P8_15_gpio_pu_pin: pinmux_P8_15_gpio_pu_pin { + pinctrl-single,pins = <0x03c 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_15_gpio_pd_pin: pinmux_P8_15_gpio_pd_pin { + pinctrl-single,pins = <0x03c 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_15_pruin_pin: pinmux_P8_15_pruin_pin { + pinctrl-single,pins = <0x03c 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_15_qep_pin: pinmux_P8_15_qep_pin { + pinctrl-single,pins = <0x03c 0x24>; }; /* Mode 4, Pull-Down, RxActive */ + + /* P8_16 (ZCZ ball V13) */ + P8_16_default_pin: pinmux_P8_16_default_pin { + pinctrl-single,pins = <0x038 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_16_gpio_pin: pinmux_P8_16_gpio_pin { + pinctrl-single,pins = <0x038 0x2F>; }; /* Mode 7, RxActive */ + P8_16_gpio_pu_pin: pinmux_P8_16_gpio_pu_pin { + pinctrl-single,pins = <0x038 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_16_gpio_pd_pin: pinmux_P8_16_gpio_pd_pin { + pinctrl-single,pins = <0x038 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_16_pruin_pin: pinmux_P8_16_pruin_pin { + pinctrl-single,pins = <0x038 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_16_qep_pin: pinmux_P8_16_qep_pin { + pinctrl-single,pins = <0x038 0x24>; }; /* Mode 4, Pull-Down, RxActive */ + + /* P8_17 (ZCZ ball U12) */ + P8_17_default_pin: pinmux_P8_17_default_pin { + pinctrl-single,pins = <0x02c 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_17_gpio_pin: pinmux_P8_17_gpio_pin { + pinctrl-single,pins = <0x02c 0x2F>; }; /* Mode 7, RxActive */ + P8_17_gpio_pu_pin: pinmux_P8_17_gpio_pu_pin { + pinctrl-single,pins = <0x02c 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_17_gpio_pd_pin: pinmux_P8_17_gpio_pd_pin { + pinctrl-single,pins = <0x02c 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_17_pwm_pin: pinmux_P8_17_pwm_pin { + pinctrl-single,pins = <0x02c 0x24>; }; /* Mode 4, Pull-Down, RxActive */ + + /* P8_18 (ZCZ ball V12) */ + P8_18_default_pin: pinmux_P8_18_default_pin { + pinctrl-single,pins = <0x08c 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_18_gpio_pin: pinmux_P8_18_gpio_pin { + pinctrl-single,pins = <0x08c 0x2F>; }; /* Mode 7, RxActive */ + P8_18_gpio_pu_pin: pinmux_P8_18_gpio_pu_pin { + pinctrl-single,pins = <0x08c 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_18_gpio_pd_pin: pinmux_P8_18_gpio_pd_pin { + pinctrl-single,pins = <0x08c 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + + /* P8_19 (ZCZ ball U10) */ + P8_19_default_pin: pinmux_P8_19_default_pin { + pinctrl-single,pins = <0x020 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_19_gpio_pin: pinmux_P8_19_gpio_pin { + pinctrl-single,pins = <0x020 0x2F>; }; /* Mode 7, RxActive */ + P8_19_gpio_pu_pin: pinmux_P8_19_gpio_pu_pin { + pinctrl-single,pins = <0x020 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_19_gpio_pd_pin: pinmux_P8_19_gpio_pd_pin { + pinctrl-single,pins = <0x020 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_19_pwm_pin: pinmux_P8_19_pwm_pin { + pinctrl-single,pins = <0x020 0x24>; }; /* Mode 4, Pull-Down, RxActive */ + + /* P8_20 (ZCZ ball V9 ) emmc */ + /* P8_21 (ZCZ ball U9 ) emmc */ + /* P8_22 (ZCZ ball V8 ) emmc */ + /* P8_23 (ZCZ ball U8 ) emmc */ + /* P8_24 (ZCZ ball V7 ) emmc */ + /* P8_25 (ZCZ ball U7 ) emmc */ + + /* P8_26 (ZCZ ball V6 ) */ + P8_26_default_pin: pinmux_P8_26_default_pin { + pinctrl-single,pins = <0x07c 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_26_gpio_pin: pinmux_P8_26_gpio_pin { + pinctrl-single,pins = <0x07c 0x2F>; }; /* Mode 7, RxActive */ + P8_26_gpio_pu_pin: pinmux_P8_26_gpio_pu_pin { + pinctrl-single,pins = <0x07c 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_26_gpio_pd_pin: pinmux_P8_26_gpio_pd_pin { + pinctrl-single,pins = <0x07c 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + + /* P8_27 (ZCZ ball U5 ) hdmi */ + P8_27_default_pin: pinmux_P8_27_default_pin { + pinctrl-single,pins = <0x0e0 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_27_gpio_pin: pinmux_P8_27_gpio_pin { + pinctrl-single,pins = <0x0e0 0x2F>; }; /* Mode 7, RxActive */ + P8_27_gpio_pu_pin: pinmux_P8_27_gpio_pu_pin { + pinctrl-single,pins = <0x0e0 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_27_gpio_pd_pin: pinmux_P8_27_gpio_pd_pin { + pinctrl-single,pins = <0x0e0 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_27_pruout_pin: pinmux_P8_27_pruout_pin { + pinctrl-single,pins = <0x0e0 0x05>; }; /* Mode 5, Pull-Down*/ + P8_27_pruin_pin: pinmux_P8_27_pruin_pin { + pinctrl-single,pins = <0x0e0 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_27_hdmi_pin: pinmux_P8_27_hdmi_pin { + pinctrl-single,pins = <0x0e0 0x00>; }; /* lcd_vsync.lcd_vsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */ + + /* P8_28 (ZCZ ball V5 ) hdmi */ + P8_28_default_pin: pinmux_P8_28_default_pin { + pinctrl-single,pins = <0x0e8 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_28_gpio_pin: pinmux_P8_28_gpio_pin { + pinctrl-single,pins = <0x0e8 0x2F>; }; /* Mode 7, RxActive */ + P8_28_gpio_pu_pin: pinmux_P8_28_gpio_pu_pin { + pinctrl-single,pins = <0x0e8 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_28_gpio_pd_pin: pinmux_P8_28_gpio_pd_pin { + pinctrl-single,pins = <0x0e8 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_28_pruout_pin: pinmux_P8_28_pruout_pin { + pinctrl-single,pins = <0x0e8 0x05>; }; /* Mode 5, Pull-Down */ + P8_28_pruin_pin: pinmux_P8_28_pruin_pin { + pinctrl-single,pins = <0x0e8 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_28_hdmi_pin: pinmux_P8_28_hdmi_pin { + pinctrl-single,pins = <0x0e8 0x00>; }; /* lcd_pclk.lcd_pclk, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */ + + /* P8_29 (ZCZ ball R5 ) hdmi */ + P8_29_default_pin: pinmux_P8_29_default_pin { + pinctrl-single,pins = <0x0e4 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_29_gpio_pin: pinmux_P8_29_gpio_pin { + pinctrl-single,pins = <0x0e4 0x2F>; }; /* Mode 7, RxActive */ + P8_29_gpio_pu_pin: pinmux_P8_29_gpio_pu_pin { + pinctrl-single,pins = <0x0e4 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_29_gpio_pd_pin: pinmux_P8_29_gpio_pd_pin { + pinctrl-single,pins = <0x0e4 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_29_pruout_pin: pinmux_P8_29_pruout_pin { + pinctrl-single,pins = <0x0e4 0x05>; }; /* Mode 5, Pull-Down*/ + P8_29_pruin_pin: pinmux_P8_29_pruin_pin { + pinctrl-single,pins = <0x0e4 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_29_hdmi_pin: pinmux_P8_29_hdmi_pin { + pinctrl-single,pins = <0x0e4 0x00>; }; /* lcd_hsync.lcd_hsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */ + + /* P8_30 (ZCZ ball R6 ) hdmi */ + P8_30_default_pin: pinmux_P8_30_default_pin { + pinctrl-single,pins = <0x0ec 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_30_gpio_pin: pinmux_P8_30_gpio_pin { + pinctrl-single,pins = <0x0ec 0x2F>; }; /* Mode 7, RxActive */ + P8_30_gpio_pu_pin: pinmux_P8_30_gpio_pu_pin { + pinctrl-single,pins = <0x0ec 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_30_gpio_pd_pin: pinmux_P8_30_gpio_pd_pin { + pinctrl-single,pins = <0x0ec 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_30_pruout_pin: pinmux_P8_30_pruout_pin { + pinctrl-single,pins = <0x0ec 0x05>; }; /* Mode 5, Pull-Down*/ + P8_30_pruin_pin: pinmux_P8_30_pruin_pin { + pinctrl-single,pins = <0x0ec 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_30_hdmi_pin: pinmux_P8_30_hdmi_pin { + pinctrl-single,pins = <0x0ec 0x00>; }; /* lcd_ac_bias_en.lcd_ac_bias_en, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */ + + /* P8_31 (ZCZ ball V4 ) hdmi */ + P8_31_default_pin: pinmux_P8_31_default_pin { + pinctrl-single,pins = <0x0d8 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_31_gpio_pin: pinmux_P8_31_gpio_pin { + pinctrl-single,pins = <0x0d8 0x2F>; }; /* Mode 7, RxActive */ + P8_31_gpio_pu_pin: pinmux_P8_31_gpio_pu_pin { + pinctrl-single,pins = <0x0d8 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_31_gpio_pd_pin: pinmux_P8_31_gpio_pd_pin { + pinctrl-single,pins = <0x0d8 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_31_uart_pin: pinmux_P8_31_uart_pin { + pinctrl-single,pins = <0x0d8 0x24>; }; /* Mode 4, Pull-Down, RxActive */ + P8_31_hdmi_pin: pinmux_P8_31_hdmi_pin { + pinctrl-single,pins = <0x0d8 0x08>; }; /* lcd_data14.lcd_data14, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + /* P8_32 (ZCZ ball T5 ) hdmi */ + P8_32_default_pin: pinmux_P8_32_default_pin { + pinctrl-single,pins = <0x0dc 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_32_gpio_pin: pinmux_P8_32_gpio_pin { + pinctrl-single,pins = <0x0dc 0x2F>; }; /* Mode 7, RxActive */ + P8_32_gpio_pu_pin: pinmux_P8_32_gpio_pu_pin { + pinctrl-single,pins = <0x0dc 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_32_gpio_pd_pin: pinmux_P8_32_gpio_pd_pin { + pinctrl-single,pins = <0x0dc 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_32_uart_pin: pinmux_P8_32_uart_pin { + pinctrl-single,pins = <0x0dc 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_32_hdmi_pin: pinmux_P8_32_hdmi_pin { + pinctrl-single,pins = <0x0dc 0x08>; }; /* lcd_data15.lcd_data15, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + /* P8_33 (ZCZ ball V3 ) hdmi */ + P8_33_default_pin: pinmux_P8_33_default_pin { + pinctrl-single,pins = <0x0d4 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_33_gpio_pin: pinmux_P8_33_gpio_pin { + pinctrl-single,pins = <0x0d4 0x2F>; }; /* Mode 7, RxActive */ + P8_33_gpio_pu_pin: pinmux_P8_33_gpio_pu_pin { + pinctrl-single,pins = <0x0d4 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_33_gpio_pd_pin: pinmux_P8_33_gpio_pd_pin { + pinctrl-single,pins = <0x0d4 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_33_hdmi_pin: pinmux_P8_33_hdmi_pin { + pinctrl-single,pins = <0x0d4 0x08>; }; /* lcd_data13.lcd_data13, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + /* P8_34 (ZCZ ball U4 ) hdmi */ + P8_34_default_pin: pinmux_P8_34_default_pin { + pinctrl-single,pins = <0x0cc 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_34_gpio_pin: pinmux_P8_34_gpio_pin { + pinctrl-single,pins = <0x0cc 0x2F>; }; /* Mode 7, RxActive */ + P8_34_gpio_pu_pin: pinmux_P8_34_gpio_pu_pin { + pinctrl-single,pins = <0x0cc 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_34_gpio_pd_pin: pinmux_P8_34_gpio_pd_pin { + pinctrl-single,pins = <0x0cc 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_34_pwm_pin: pinmux_P8_34_pwm_pin { + pinctrl-single,pins = <0x0cc 0x22>; }; /* Mode 2, Pull-Down, RxActive */ + P8_34_hdmi_pin: pinmux_P8_34_hdmi_pin { + pinctrl-single,pins = <0x0cc 0x08>; }; /* lcd_data11.lcd_data11, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + /* P8_35 (ZCZ ball V2 ) hdmi */ + P8_35_default_pin: pinmux_P8_35_default_pin { + pinctrl-single,pins = <0x0d0 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_35_gpio_pin: pinmux_P8_35_gpio_pin { + pinctrl-single,pins = <0x0d0 0x2F>; }; /* Mode 7, RxActive */ + P8_35_gpio_pu_pin: pinmux_P8_35_gpio_pu_pin { + pinctrl-single,pins = <0x0d0 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_35_gpio_pd_pin: pinmux_P8_35_gpio_pd_pin { + pinctrl-single,pins = <0x0d0 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_35_hdmi_pin: pinmux_P8_35_hdmi_pin { + pinctrl-single,pins = <0x0d0 0x08>; }; /* lcd_data12.lcd_data12, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + /* P8_36 (ZCZ ball U3 ) hdmi */ + P8_36_default_pin: pinmux_P8_36_default_pin { + pinctrl-single,pins = <0x0c8 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_36_gpio_pin: pinmux_P8_36_gpio_pin { + pinctrl-single,pins = <0x0c8 0x2F>; }; /* Mode 7, RxActive */ + P8_36_gpio_pu_pin: pinmux_P8_36_gpio_pu_pin { + pinctrl-single,pins = <0x0c8 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_36_gpio_pd_pin: pinmux_P8_36_gpio_pd_pin { + pinctrl-single,pins = <0x0c8 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_36_pwm_pin: pinmux_P8_36_pwm_pin { + pinctrl-single,pins = <0x0c8 0x22>; }; /* Mode 2, Pull-Down, RxActive */ + P8_36_hdmi_pin: pinmux_P8_36_hdmi_pin { + pinctrl-single,pins = <0x0c8 0x08>; }; /* lcd_data10.lcd_data10, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + /* P8_37 (ZCZ ball U1 ) hdmi */ + P8_37_default_pin: pinmux_P8_37_default_pin { + pinctrl-single,pins = <0x0c0 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_37_gpio_pin: pinmux_P8_37_gpio_pin { + pinctrl-single,pins = <0x0c0 0x2F>; }; /* Mode 7, RxActive */ + P8_37_gpio_pu_pin: pinmux_P8_37_gpio_pu_pin { + pinctrl-single,pins = <0x0c0 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_37_gpio_pd_pin: pinmux_P8_37_gpio_pd_pin { + pinctrl-single,pins = <0x0c0 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_37_uart_pin: pinmux_P8_37_uart_pin { + pinctrl-single,pins = <0x0c0 0x04>; }; /* Mode 4, Pull-Down*/ + P8_37_pwm_pin: pinmux_P8_37_pwm_pin { + pinctrl-single,pins = <0x0c0 0x02>; }; /* Mode 2, Pull-Down*/ + P8_37_hdmi_pin: pinmux_P8_37_hdmi_pin { + pinctrl-single,pins = <0x0c0 0x08>; }; /* lcd_data8.lcd_data8, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + + /* P8_38 (ZCZ ball U2 ) hdmi */ + P8_38_default_pin: pinmux_P8_38_default_pin { + pinctrl-single,pins = <0x0c4 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_38_gpio_pin: pinmux_P8_38_gpio_pin { + pinctrl-single,pins = <0x0c4 0x2F>; }; /* Mode 7, RxActive */ + P8_38_gpio_pu_pin: pinmux_P8_38_gpio_pu_pin { + pinctrl-single,pins = <0x0c4 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_38_gpio_pd_pin: pinmux_P8_38_gpio_pd_pin { + pinctrl-single,pins = <0x0c4 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_38_uart_pin: pinmux_P8_38_uart_pin { + pinctrl-single,pins = <0x0c4 0x24>; }; /* Mode 4, Pull-Down, RxActive */ + P8_38_pwm_pin: pinmux_P8_38_pwm_pin { + pinctrl-single,pins = <0x0c4 0x22>; }; /* Mode 2, Pull-Down, RxActive */ + P8_38_hdmi_pin: pinmux_P8_38_hdmi_pin { + pinctrl-single,pins = <0x0c4 0x08>; }; /* lcd_data9.lcd_data9, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + + /* P8_39 (ZCZ ball T3 ) hdmi */ + P8_39_default_pin: pinmux_P8_39_default_pin { + pinctrl-single,pins = <0x0b8 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_39_gpio_pin: pinmux_P8_39_gpio_pin { + pinctrl-single,pins = <0x0b8 0x2F>; }; /* Mode 7, RxActive */ + P8_39_gpio_pu_pin: pinmux_P8_39_gpio_pu_pin { + pinctrl-single,pins = <0x0b8 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_39_gpio_pd_pin: pinmux_P8_39_gpio_pd_pin { + pinctrl-single,pins = <0x0b8 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_39_pruout_pin: pinmux_P8_39_pruout_pin { + pinctrl-single,pins = <0x0b8 0x05>; }; /* Mode 5, Pull-Down*/ + P8_39_pruin_pin: pinmux_P8_39_pruin_pin { + pinctrl-single,pins = <0x0b8 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_39_hdmi_pin: pinmux_P8_39_hdmi_pin { + pinctrl-single,pins = <0x0b8 0x08>; }; /* lcd_data6.lcd_data6, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + /* P8_40 (ZCZ ball T4 ) hdmi */ + P8_40_default_pin: pinmux_P8_40_default_pin { + pinctrl-single,pins = <0x0bc 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_40_gpio_pin: pinmux_P8_40_gpio_pin { + pinctrl-single,pins = <0x0bc 0x2F>; }; /* Mode 7, RxActive */ + P8_40_gpio_pu_pin: pinmux_P8_40_gpio_pu_pin { + pinctrl-single,pins = <0x0bc 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_40_gpio_pd_pin: pinmux_P8_40_gpio_pd_pin { + pinctrl-single,pins = <0x0bc 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_40_pruout_pin: pinmux_P8_40_pruout_pin { + pinctrl-single,pins = <0x0bc 0x05>; }; /* Mode 5, Pull-Down*/ + P8_40_pruin_pin: pinmux_P8_40_pruin_pin { + pinctrl-single,pins = <0x0bc 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_40_hdmi_pin: pinmux_P8_40_hdmi_pin { + pinctrl-single,pins = <0x0bc 0x08>; }; /* lcd_data7.lcd_data7, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + /* P8_41 (ZCZ ball T1 ) hdmi */ + P8_41_default_pin: pinmux_P8_41_default_pin { + pinctrl-single,pins = <0x0b0 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_41_gpio_pin: pinmux_P8_41_gpio_pin { + pinctrl-single,pins = <0x0b0 0x2F>; }; /* Mode 7, RxActive */ + P8_41_gpio_pu_pin: pinmux_P8_41_gpio_pu_pin { + pinctrl-single,pins = <0x0b0 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_41_gpio_pd_pin: pinmux_P8_41_gpio_pd_pin { + pinctrl-single,pins = <0x0b0 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_41_pruout_pin: pinmux_P8_41_pruout_pin { + pinctrl-single,pins = <0x0b0 0x05>; }; /* Mode 5, Pull-Down*/ + P8_41_pruin_pin: pinmux_P8_41_pruin_pin { + pinctrl-single,pins = <0x0b0 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_41_hdmi_pin: pinmux_P8_41_hdmi_pin { + pinctrl-single,pins = <0x0b0 0x08>; }; /* lcd_data4.lcd_data4, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + /* P8_42 (ZCZ ball T2 ) hdmi */ + P8_42_default_pin: pinmux_P8_42_default_pin { + pinctrl-single,pins = <0x0b4 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_42_gpio_pin: pinmux_P8_42_gpio_pin { + pinctrl-single,pins = <0x0b4 0x2F>; }; /* Mode 7, RxActive */ + P8_42_gpio_pu_pin: pinmux_P8_42_gpio_pu_pin { + pinctrl-single,pins = <0x0b4 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_42_gpio_pd_pin: pinmux_P8_42_gpio_pd_pin { + pinctrl-single,pins = <0x0b4 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_42_pruout_pin: pinmux_P8_42_pruout_pin { + pinctrl-single,pins = <0x0b4 0x05>; }; /* Mode 5, Pull-Down*/ + P8_42_pruin_pin: pinmux_P8_42_pruin_pin { + pinctrl-single,pins = <0x0b4 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_42_hdmi_pin: pinmux_P8_42_hdmi_pin { + pinctrl-single,pins = <0x0b4 0x08>; }; /* lcd_data5.lcd_data5, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + /* P8_43 (ZCZ ball R3 ) hdmi */ + P8_43_default_pin: pinmux_P8_43_default_pin { + pinctrl-single,pins = <0x0a8 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_43_gpio_pin: pinmux_P8_43_gpio_pin { + pinctrl-single,pins = <0x0a8 0x2F>; }; /* Mode 7, RxActive */ + P8_43_gpio_pu_pin: pinmux_P8_43_gpio_pu_pin { + pinctrl-single,pins = <0x0a8 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_43_gpio_pd_pin: pinmux_P8_43_gpio_pd_pin { + pinctrl-single,pins = <0x0a8 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_43_pruout_pin: pinmux_P8_43_pruout_pin { + pinctrl-single,pins = <0x0a8 0x05>; }; /* Mode 5, Pull-Down*/ + P8_43_pruin_pin: pinmux_P8_43_pruin_pin { + pinctrl-single,pins = <0x0a8 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_43_pwm_pin: pinmux_P8_43_pwm_pin { + pinctrl-single,pins = <0x0a8 0x03>; }; /* Mode 3, Pull-Down */ + P8_43_hdmi_pin: pinmux_P8_43_hdmi_pin { + pinctrl-single,pins = <0x0a8 0x08>; }; /* lcd_data2.lcd_data2, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + /* P8_44 (ZCZ ball R4 ) hdmi */ + P8_44_default_pin: pinmux_P8_44_default_pin { + pinctrl-single,pins = <0x0ac 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_44_gpio_pin: pinmux_P8_44_gpio_pin { + pinctrl-single,pins = <0x0ac 0x2F>; }; /* Mode 7, RxActive */ + P8_44_gpio_pu_pin: pinmux_P8_44_gpio_pu_pin { + pinctrl-single,pins = <0x0ac 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_44_gpio_pd_pin: pinmux_P8_44_gpio_pd_pin { + pinctrl-single,pins = <0x0ac 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_44_pruout_pin: pinmux_P8_44_pruout_pin { + pinctrl-single,pins = <0x0ac 0x05>; }; /* Mode 5, Pull-Down*/ + P8_44_pruin_pin: pinmux_P8_44_pruin_pin { + pinctrl-single,pins = <0x0ac 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_44_pwm_pin: pinmux_P8_44_pwm_pin { + pinctrl-single,pins = <0x0ac 0x23>; }; /* Mode 3, Pull-Down, RxActive */ + P8_44_hdmi_pin: pinmux_P8_44_hdmi_pin { + pinctrl-single,pins = <0x0ac 0x08>; }; /* lcd_data3.lcd_data3, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + /* P8_45 (ZCZ ball R1 ) hdmi */ + P8_45_default_pin: pinmux_P8_45_default_pin { + pinctrl-single,pins = <0x0a0 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_45_gpio_pin: pinmux_P8_45_gpio_pin { + pinctrl-single,pins = <0x0a0 0x2F>; }; /* Mode 7, RxActive */ + P8_45_gpio_pu_pin: pinmux_P8_45_gpio_pu_pin { + pinctrl-single,pins = <0x0a0 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_45_gpio_pd_pin: pinmux_P8_45_gpio_pd_pin { + pinctrl-single,pins = <0x0a0 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_45_pruout_pin: pinmux_P8_45_pruout_pin { + pinctrl-single,pins = <0x0a0 0x05>; }; /* Mode 5, Pull-Down*/ + P8_45_pruin_pin: pinmux_P8_45_pruin_pin { + pinctrl-single,pins = <0x0a0 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_45_pwm_pin: pinmux_P8_45_pwm_pin { + pinctrl-single,pins = <0x0a0 0x03>; }; /* Mode 3, Pull-Down*/ + P8_45_hdmi_pin: pinmux_P8_45_hdmi_pin { + pinctrl-single,pins = <0x0a0 0x08>; }; /* lcd_data0.lcd_data0, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + /* P8_46 (ZCZ ball R2 ) hdmi */ + P8_46_default_pin: pinmux_P8_46_default_pin { + pinctrl-single,pins = <0x0a4 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_46_gpio_pin: pinmux_P8_46_gpio_pin { + pinctrl-single,pins = <0x0a4 0x2F>; }; /* Mode 7, RxActive */ + P8_46_gpio_pu_pin: pinmux_P8_46_gpio_pu_pin { + pinctrl-single,pins = <0x0a4 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P8_46_gpio_pd_pin: pinmux_P8_46_gpio_pd_pin { + pinctrl-single,pins = <0x0a4 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P8_46_pruout_pin: pinmux_P8_46_pruout_pin { + pinctrl-single,pins = <0x0a4 0x05>; }; /* Mode 5, Pull-Down*/ + P8_46_pruin_pin: pinmux_P8_46_pruin_pin { + pinctrl-single,pins = <0x0a4 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P8_46_pwm_pin: pinmux_P8_46_pwm_pin { + pinctrl-single,pins = <0x0a4 0x03>; }; /* Mode 3, Pull-Down*/ + P8_46_hdmi_pin: pinmux_P8_46_hdmi_pin { + pinctrl-single,pins = <0x0a4 0x08>; }; /* lcd_data1.lcd_data1, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + + /************************/ + /* P9 Header */ + /************************/ + + /* P9_01 GND */ + /* P9_02 GND */ + /* P9_03 3.3V */ + /* P9_04 3.3V */ + /* P9_05 VDD_5V */ + /* P9_06 VDD_5V */ + /* P9_07 SYS_5V */ + /* P9_08 SYS_5V */ + /* P9_09 PWR_BUT */ + /* P9_10 (ZCZ ball A10) RESETn */ + + /* P9_11 (ZCZ ball T17) */ + P9_11_default_pin: pinmux_P9_11_default_pin { + pinctrl-single,pins = <0x070 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_11_gpio_pin: pinmux_P9_11_gpio_pin { + pinctrl-single,pins = <0x070 0x2F>; }; /* Mode 7, RxActive */ + P9_11_gpio_pu_pin: pinmux_P9_11_gpio_pu_pin { + pinctrl-single,pins = <0x070 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_11_gpio_pd_pin: pinmux_P9_11_gpio_pd_pin { + pinctrl-single,pins = <0x070 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_11_uart_pin: pinmux_P9_11_uart_pin { + pinctrl-single,pins = <0x070 0x36>; }; /* Mode 6, Pull-Up, RxActive */ + + /* P9_12 (ZCZ ball U18) */ + P9_12_default_pin: pinmux_P9_12_default_pin { + pinctrl-single,pins = <0x078 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_12_gpio_pin: pinmux_P9_12_gpio_pin { + pinctrl-single,pins = <0x078 0x2F>; }; /* Mode 7, RxActive */ + P9_12_gpio_pu_pin: pinmux_P9_12_gpio_pu_pin { + pinctrl-single,pins = <0x078 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_12_gpio_pd_pin: pinmux_P9_12_gpio_pd_pin { + pinctrl-single,pins = <0x078 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + + /* P9_13 (ZCZ ball U17) */ + P9_13_default_pin: pinmux_P9_13_default_pin { + pinctrl-single,pins = <0x074 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_13_gpio_pin: pinmux_P9_13_gpio_pin { + pinctrl-single,pins = <0x074 0x2F>; }; /* Mode 7, RxActive */ + P9_13_gpio_pu_pin: pinmux_P9_13_gpio_pu_pin { + pinctrl-single,pins = <0x074 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_13_gpio_pd_pin: pinmux_P9_13_gpio_pd_pin { + pinctrl-single,pins = <0x074 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_13_uart_pin: pinmux_P9_13_uart_pin { + pinctrl-single,pins = <0x074 0x36>; }; /* Mode 6, Pull-Up, RxActive */ + + /* P9_14 (ZCZ ball U14) */ + P9_14_default_pin: pinmux_P9_14_default_pin { + pinctrl-single,pins = <0x048 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_14_gpio_pin: pinmux_P9_14_gpio_pin { + pinctrl-single,pins = <0x048 0x2F>; }; /* Mode 7, RxActive */ + P9_14_gpio_pu_pin: pinmux_P9_14_gpio_pu_pin { + pinctrl-single,pins = <0x048 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_14_gpio_pd_pin: pinmux_P9_14_gpio_pd_pin { + pinctrl-single,pins = <0x048 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_14_pwm_pin: pinmux_P9_14_pwm_pin { + pinctrl-single,pins = <0x048 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + + /* P9_15 (ZCZ ball R13) */ + P9_15_default_pin: pinmux_P9_15_default_pin { + pinctrl-single,pins = <0x040 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_15_gpio_pin: pinmux_P9_15_gpio_pin { + pinctrl-single,pins = <0x040 0x2F>; }; /* Mode 7, RxActive */ + P9_15_gpio_pu_pin: pinmux_P9_15_gpio_pu_pin { + pinctrl-single,pins = <0x040 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_15_gpio_pd_pin: pinmux_P9_15_gpio_pd_pin { + pinctrl-single,pins = <0x040 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_15_pwm_pin: pinmux_P9_15_pwm_pin { + pinctrl-single,pins = <0x040 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + + /* P9_16 (ZCZ ball T14) */ + P9_16_default_pin: pinmux_P9_16_default_pin { + pinctrl-single,pins = <0x04c 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_16_gpio_pin: pinmux_P9_16_gpio_pin { + pinctrl-single,pins = <0x04c 0x2F>; }; /* Mode 7, RxActive */ + P9_16_gpio_pu_pin: pinmux_P9_16_gpio_pu_pin { + pinctrl-single,pins = <0x04c 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_16_gpio_pd_pin: pinmux_P9_16_gpio_pd_pin { + pinctrl-single,pins = <0x04c 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_16_pwm_pin: pinmux_P9_16_pwm_pin { + pinctrl-single,pins = <0x04c 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + + /* P9_17 (ZCZ ball A16) */ + P9_17_default_pin: pinmux_P9_17_default_pin { + pinctrl-single,pins = <0x15c 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_17_gpio_pin: pinmux_P9_17_gpio_pin { + pinctrl-single,pins = <0x15c 0x2F>; }; /* Mode 7, RxActive */ + P9_17_gpio_pu_pin: pinmux_P9_17_gpio_pu_pin { + pinctrl-single,pins = <0x15c 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_17_gpio_pd_pin: pinmux_P9_17_gpio_pd_pin { + pinctrl-single,pins = <0x15c 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_17_spi_pin: pinmux_P9_17_spi_pin { + pinctrl-single,pins = <0x15c 0x30>; }; /* Mode 0, Pull-Up, RxActive */ + P9_17_i2c_pin: pinmux_P9_17_i2c_pin { + pinctrl-single,pins = <0x15c 0x32>; }; /* Mode 2, Pull-Up, RxActive */ + P9_17_pwm_pin: pinmux_P9_17_pwm_pin { + pinctrl-single,pins = <0x15c 0x33>; }; /* Mode 3, Pull-Up, RxActive */ + + /* P9_18 (ZCZ ball B16) */ + P9_18_default_pin: pinmux_P9_18_default_pin { + pinctrl-single,pins = <0x158 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_18_gpio_pin: pinmux_P9_18_gpio_pin { + pinctrl-single,pins = <0x158 0x2F>; }; /* Mode 7, RxActive */ + P9_18_gpio_pu_pin: pinmux_P9_18_gpio_pu_pin { + pinctrl-single,pins = <0x158 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_18_gpio_pd_pin: pinmux_P9_18_gpio_pd_pin { + pinctrl-single,pins = <0x158 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_18_spi_pin: pinmux_P9_18_spi_pin { + pinctrl-single,pins = <0x158 0x30>; }; /* Mode 0, Pull-Up, RxActive */ + P9_18_i2c_pin: pinmux_P9_18_i2c_pin { + pinctrl-single,pins = <0x158 0x32>; }; /* Mode 2, Pull-Up, RxActive */ + P9_18_pwm_pin: pinmux_P9_18_pwm_pin { + pinctrl-single,pins = <0x158 0x33>; }; /* Mode 3, Pull-Up, RxActive */ + + /* P9_19 (ZCZ ball D17) */ + P9_19_default_pin: pinmux_P9_19_default_pin { + pinctrl-single,pins = <0x17c 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_19_gpio_pin: pinmux_P9_19_gpio_pin { + pinctrl-single,pins = <0x17c 0x2F>; }; /* Mode 7, RxActive */ + P9_19_gpio_pu_pin: pinmux_P9_19_gpio_pu_pin { + pinctrl-single,pins = <0x17c 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_19_gpio_pd_pin: pinmux_P9_19_gpio_pd_pin { + pinctrl-single,pins = <0x17c 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_19_can_pin: pinmux_P9_19_can_pin { + pinctrl-single,pins = <0x17c 0x32>; }; /* Mode 2, Pull-Up, RxActive */ + P9_19_i2c_pin: pinmux_P9_19_i2c_pin { + pinctrl-single,pins = <0x17c 0x73>; }; /* (SLEWCTRL_SLOW | PIN_INPUT_PULLUP | MUX_MODE3) */ + + /* P9_20 (ZCZ ball D18) */ + P9_20_default_pin: pinmux_P9_20_default_pin { + pinctrl-single,pins = <0x178 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_20_gpio_pin: pinmux_P9_20_gpio_pin { + pinctrl-single,pins = <0x178 0x2F>; }; /* Mode 7, RxActive */ + P9_20_gpio_pu_pin: pinmux_P9_20_gpio_pu_pin { + pinctrl-single,pins = <0x178 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_20_gpio_pd_pin: pinmux_P9_20_gpio_pd_pin { + pinctrl-single,pins = <0x178 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_20_can_pin: pinmux_P9_20_can_pin { + pinctrl-single,pins = <0x178 0x12>; }; /* Mode 2, Pull-Up, RxActive */ + P9_20_i2c_pin: pinmux_P9_20_i2c_pin { + pinctrl-single,pins = <0x178 0x73>; }; /* (SLEWCTRL_SLOW | PIN_INPUT_PULLUP | MUX_MODE3) */ + + /* P9_21 (ZCZ ball B17) */ + P9_21_default_pin: pinmux_P9_21_default_pin { + pinctrl-single,pins = <0x154 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_21_gpio_pin: pinmux_P9_21_gpio_pin { + pinctrl-single,pins = <0x154 0x2F>; }; /* Mode 7, RxActive */ + P9_21_gpio_pu_pin: pinmux_P9_21_gpio_pu_pin { + pinctrl-single,pins = <0x154 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_21_gpio_pd_pin: pinmux_P9_21_gpio_pd_pin { + pinctrl-single,pins = <0x154 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_21_spi_pin: pinmux_P9_21_spi_pin { + pinctrl-single,pins = <0x154 0x30>; }; /* Mode 0, Pull-Up, RxActive */ + P9_21_uart_pin: pinmux_P9_21_uart_pin { + pinctrl-single,pins = <0x154 0x31>; }; /* Mode 1, Pull-Up, RxActive */ + P9_21_i2c_pin: pinmux_P9_21_i2c_pin { + pinctrl-single,pins = <0x154 0x32>; }; /* Mode 2, Pull-Up, RxActive */ + P9_21_pwm_pin: pinmux_P9_21_pwm_pin { + pinctrl-single,pins = <0x154 0x33>; }; /* Mode 3, Pull-Up, RxActive */ + + /* P9_22 (ZCZ ball A17) */ + P9_22_default_pin: pinmux_P9_22_default_pin { + pinctrl-single,pins = <0x150 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_22_gpio_pin: pinmux_P9_22_gpio_pin { + pinctrl-single,pins = <0x150 0x2F>; }; /* Mode 7, RxActive */ + P9_22_gpio_pu_pin: pinmux_P9_22_gpio_pu_pin { + pinctrl-single,pins = <0x150 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_22_gpio_pd_pin: pinmux_P9_22_gpio_pd_pin { + pinctrl-single,pins = <0x150 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_22_spi_pin: pinmux_P9_22_spi_pin { + pinctrl-single,pins = <0x150 0x30>; }; /* Mode 0, Pull-Up, RxActive */ + P9_22_uart_pin: pinmux_P9_22_uart_pin { + pinctrl-single,pins = <0x150 0x31>; }; /* Mode 1, Pull-Up, RxActive */ + P9_22_i2c_pin: pinmux_P9_22_i2c_pin { + pinctrl-single,pins = <0x150 0x32>; }; /* Mode 2, Pull-Up, RxActive */ + P9_22_pwm_pin: pinmux_P9_22_pwm_pin { + pinctrl-single,pins = <0x150 0x33>; }; /* Mode 3, Pull-Up, RxActive */ + + /* P9_23 (ZCZ ball V14) */ + P9_23_default_pin: pinmux_P9_23_default_pin { + pinctrl-single,pins = <0x044 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_23_gpio_pin: pinmux_P9_23_gpio_pin { + pinctrl-single,pins = <0x044 0x2F>; }; /* Mode 7, RxActive */ + P9_23_gpio_pu_pin: pinmux_P9_23_gpio_pu_pin { + pinctrl-single,pins = <0x044 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_23_gpio_pd_pin: pinmux_P9_23_gpio_pd_pin { + pinctrl-single,pins = <0x044 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_23_pwm_pin: pinmux_P9_23_pwm_pin { + pinctrl-single,pins = <0x044 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + + /* P9_24 (ZCZ ball D15) */ + P9_24_default_pin: pinmux_P9_24_default_pin { + pinctrl-single,pins = <0x184 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_24_gpio_pin: pinmux_P9_24_gpio_pin { + pinctrl-single,pins = <0x184 0x2F>; }; /* Mode 7, RxActive */ + P9_24_gpio_pu_pin: pinmux_P9_24_gpio_pu_pin { + pinctrl-single,pins = <0x184 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_24_gpio_pd_pin: pinmux_P9_24_gpio_pd_pin { + pinctrl-single,pins = <0x184 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_24_uart_pin: pinmux_P9_24_uart_pin { + pinctrl-single,pins = <0x184 0x30>; }; /* Mode 0, Pull-Up, RxActive */ + P9_24_can_pin: pinmux_P9_24_can_pin { + pinctrl-single,pins = <0x184 0x32>; }; /* Mode 2, Pull-Up, RxActive */ + P9_24_i2c_pin: pinmux_P9_24_i2c_pin { + pinctrl-single,pins = <0x184 0x33>; }; /* Mode 3, Pull-Up, RxActive */ + P9_24_pruin_pin: pinmux_P9_24_pruin_pin { + pinctrl-single,pins = <0x184 0x36>; }; /* Mode 6, Pull-Up, RxActive */ + + /* P9_25 (ZCZ ball A14) Audio */ + P9_25_default_pin: pinmux_P9_25_default_pin { + pinctrl-single,pins = <0x1ac 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_25_gpio_pin: pinmux_P9_25_gpio_pin { + pinctrl-single,pins = <0x1ac 0x2F>; }; /* Mode 7, RxActive */ + P9_25_gpio_pu_pin: pinmux_P9_25_gpio_pu_pin { + pinctrl-single,pins = <0x1ac 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_25_gpio_pd_pin: pinmux_P9_25_gpio_pd_pin { + pinctrl-single,pins = <0x1ac 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_25_qep_pin: pinmux_P9_25_qep_pin { + pinctrl-single,pins = <0x1ac 0x21>; }; /* Mode 1, Pull-Down, RxActive */ + P9_25_pruout_pin: pinmux_P9_25_pruout_pin { + pinctrl-single,pins = <0x1ac 0x25>; }; /* Mode 5, Pull-Down, RxActive */ + P9_25_pruin_pin: pinmux_P9_25_pruin_pin { + pinctrl-single,pins = <0x1ac 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P9_25_audio_pin: pinmux_P9_25_audio_pin { + pinctrl-single,pins = <0x1ac (PIN_INPUT_PULLUP | MUX_MODE0)>; }; /* mcasp0_ahclkx.mcasp0_ahclkx */ + + /* P9_26 (ZCZ ball D16) */ + P9_26_default_pin: pinmux_P9_26_default_pin { + pinctrl-single,pins = <0x180 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_26_gpio_pin: pinmux_P9_26_gpio_pin { + pinctrl-single,pins = <0x180 0x2F>; }; /* Mode 7, RxActive */ + P9_26_gpio_pu_pin: pinmux_P9_26_gpio_pu_pin { + pinctrl-single,pins = <0x180 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_26_gpio_pd_pin: pinmux_P9_26_gpio_pd_pin { + pinctrl-single,pins = <0x180 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_26_uart_pin: pinmux_P9_26_uart_pin { + pinctrl-single,pins = <0x180 0x30>; }; /* Mode 0, Pull-Up, RxActive */ + P9_26_can_pin: pinmux_P9_26_can_pin { + pinctrl-single,pins = <0x180 0x12>; }; /* Mode 2, Pull-Up, RxActive */ + P9_26_i2c_pin: pinmux_P9_26_i2c_pin { + pinctrl-single,pins = <0x180 0x33>; }; /* Mode 3, Pull-Up, RxActive */ + P9_26_pruin_pin: pinmux_P9_26_pruin_pin { + pinctrl-single,pins = <0x180 0x36>; }; /* Mode 6, Pull-Up, RxActive */ + + /* P9_27 (ZCZ ball C13) */ + P9_27_default_pin: pinmux_P9_27_default_pin { + pinctrl-single,pins = <0x1a4 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_27_gpio_pin: pinmux_P9_27_gpio_pin { + pinctrl-single,pins = <0x1a4 0x2F>; }; /* Mode 7, RxActive */ + P9_27_gpio_pu_pin: pinmux_P9_27_gpio_pu_pin { + pinctrl-single,pins = <0x1a4 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_27_gpio_pd_pin: pinmux_P9_27_gpio_pd_pin { + pinctrl-single,pins = <0x1a4 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_27_qep_pin: pinmux_P9_27_qep_pin { + pinctrl-single,pins = <0x1a4 0x21>; }; /* Mode 1, Pull-Down, RxActive */ + P9_27_pruout_pin: pinmux_P9_27_pruout_pin { + pinctrl-single,pins = <0x1a4 0x25>; }; /* Mode 5, Pull-Down, RxActive */ + P9_27_pruin_pin: pinmux_P9_27_pruin_pin { + pinctrl-single,pins = <0x1a4 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + + /* P9_28 (ZCZ ball C12) Audio */ + P9_28_default_pin: pinmux_P9_28_default_pin { + pinctrl-single,pins = <0x19c 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_28_gpio_pin: pinmux_P9_28_gpio_pin { + pinctrl-single,pins = <0x19c 0x2F>; }; /* Mode 7, RxActive */ + P9_28_gpio_pu_pin: pinmux_P9_28_gpio_pu_pin { + pinctrl-single,pins = <0x19c 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_28_gpio_pd_pin: pinmux_P9_28_gpio_pd_pin { + pinctrl-single,pins = <0x19c 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_28_pwm_pin: pinmux_P9_28_pwm_pin { + pinctrl-single,pins = <0x19c 0x21>; }; /* Mode 1, Pull-Down, RxActive */ + P9_28_spi_pin: pinmux_P9_28_spi_pin { + pinctrl-single,pins = <0x19c 0x23>; }; /* Mode 3, Pull-Down, RxActive */ + P9_28_pwm2_pin: pinmux_P9_28_pwm2_pin { + pinctrl-single,pins = <0x19c 0x24>; }; /* Mode 4, Pull-Down, RxActive */ + P9_28_pruout_pin: pinmux_P9_28_pruout_pin { + pinctrl-single,pins = <0x19c 0x25>; }; /* Mode 5, Pull-Down, RxActive */ + P9_28_pruin_pin: pinmux_P9_28_pruin_pin { + pinctrl-single,pins = <0x19c 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P9_28_audio_pin: pinmux_P9_28_audio_pin { + pinctrl-single,pins = <0x19c (PIN_OUTPUT_PULLDOWN | MUX_MODE2)>; }; /* mcasp0_ahclkr.mcasp0_axr2 */ + + /* P9_29 (ZCZ ball B13) Audio */ + P9_29_default_pin: pinmux_P9_29_default_pin { + pinctrl-single,pins = <0x194 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_29_gpio_pin: pinmux_P9_29_gpio_pin { + pinctrl-single,pins = <0x194 0x2F>; }; /* Mode 7, RxActive */ + P9_29_gpio_pu_pin: pinmux_P9_29_gpio_pu_pin { + pinctrl-single,pins = <0x194 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_29_gpio_pd_pin: pinmux_P9_29_gpio_pd_pin { + pinctrl-single,pins = <0x194 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_29_pwm_pin: pinmux_P9_29_pwm_pin { + pinctrl-single,pins = <0x194 0x21>; }; /* Mode 1, Pull-Down, RxActive */ + P9_29_spi_pin: pinmux_P9_29_spi_pin { + pinctrl-single,pins = <0x194 0x23>; }; /* Mode 3, Pull-Down, RxActive */ + P9_29_pruout_pin: pinmux_P9_29_pruout_pin { + pinctrl-single,pins = <0x194 0x25>; }; /* Mode 5, Pull-Down, RxActive */ + P9_29_pruin_pin: pinmux_P9_29_pruin_pin { + pinctrl-single,pins = <0x194 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P9_29_audio_pin: pinmux_P9_29_audio_pin { + pinctrl-single,pins = <0x194 (PIN_OUTPUT_PULLUP | MUX_MODE0)>; }; /* mcasp0_fsx.mcasp0_fsx */ + + /* P9_30 (ZCZ ball D12) */ + P9_30_default_pin: pinmux_P9_30_default_pin { + pinctrl-single,pins = <0x198 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_30_gpio_pin: pinmux_P9_30_gpio_pin { + pinctrl-single,pins = <0x198 0x2F>; }; /* Mode 7, RxActive */ + P9_30_gpio_pu_pin: pinmux_P9_30_gpio_pu_pin { + pinctrl-single,pins = <0x198 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_30_gpio_pd_pin: pinmux_P9_30_gpio_pd_pin { + pinctrl-single,pins = <0x198 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_30_pwm_pin: pinmux_P9_30_pwm_pin { + pinctrl-single,pins = <0x198 0x21>; }; /* Mode 1, Pull-Down, RxActive */ + P9_30_spi_pin: pinmux_P9_30_spi_pin { + pinctrl-single,pins = <0x198 0x23>; }; /* Mode 3, Pull-Down, RxActive */ + P9_30_pruout_pin: pinmux_P9_30_pruout_pin { + pinctrl-single,pins = <0x198 0x25>; }; /* Mode 5, Pull-Down, RxActive */ + P9_30_pruin_pin: pinmux_P9_30_pruin_pin { + pinctrl-single,pins = <0x198 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + + /* P9_31 (ZCZ ball A13) Audio */ + P9_31_default_pin: pinmux_P9_31_default_pin { + pinctrl-single,pins = <0x190 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_31_gpio_pin: pinmux_P9_31_gpio_pin { + pinctrl-single,pins = <0x190 0x2F>; }; /* Mode 7, RxActive */ + P9_31_gpio_pu_pin: pinmux_P9_31_gpio_pu_pin { + pinctrl-single,pins = <0x190 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_31_gpio_pd_pin: pinmux_P9_31_gpio_pd_pin { + pinctrl-single,pins = <0x190 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_31_pwm_pin: pinmux_P9_31_pwm_pin { + pinctrl-single,pins = <0x190 0x21>; }; /* Mode 1, Pull-Down, RxActive */ + P9_31_spi_pin: pinmux_P9_31_spi_pin { + pinctrl-single,pins = <0x190 0x23>; }; /* Mode 3, Pull-Down, RxActive */ + P9_31_pruout_pin: pinmux_P9_31_pruout_pin { + pinctrl-single,pins = <0x190 0x25>; }; /* Mode 5, Pull-Down, RxActive */ + P9_31_pruin_pin: pinmux_P9_31_pruin_pin { + pinctrl-single,pins = <0x190 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + P9_31_audio_pin: pinmux_P9_31_audio_pin { + pinctrl-single,pins = <0x190 (PIN_OUTPUT_PULLDOWN | MUX_MODE0)>; }; /* mcasp0_aclkx.mcasp0_aclkx */ + + /* P9_32 VADC */ + /* P9_33 (ZCZ ball C8 ) AIN4 */ + /* P9_34 AGND */ + /* P9_35 (ZCZ ball A8 ) AIN6 */ + /* P9_36 (ZCZ ball B8 ) AIN5 */ + /* P9_37 (ZCZ ball B7 ) AIN2 */ + /* P9_38 (ZCZ ball A7 ) AIN3 */ + /* P9_39 (ZCZ ball B6 ) AIN0 */ + /* P9_40 (ZCZ ball C7 ) AIN1 */ + + /* P9_41 (ZCZ ball D14) */ + P9_41_default_pin: pinmux_P9_41_default_pin { + pinctrl-single,pins = <0x1b4 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_41_gpio_pin: pinmux_P9_41_gpio_pin { + pinctrl-single,pins = <0x1b4 0x2F>; }; /* Mode 7, RxActive */ + P9_41_gpio_pu_pin: pinmux_P9_41_gpio_pu_pin { + pinctrl-single,pins = <0x1b4 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_41_gpio_pd_pin: pinmux_P9_41_gpio_pd_pin { + pinctrl-single,pins = <0x1b4 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_41_timer_pin: pinmux_P9_41_timer_pin { + pinctrl-single,pins = <0x1b4 0x24>; }; /* Mode 4, Pull-Down, RxActive */ + P9_41_pruin_pin: pinmux_P9_41_pruin_pin { + pinctrl-single,pins = <0x1b4 0x25>; }; /* Mode 5, Pull-Down, RxActive */ + + /* P9_41.1 */ + /* P9_91 (ZCZ ball D13) */ + P9_91_default_pin: pinmux_P9_91_default_pin { + pinctrl-single,pins = <0x1a8 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_91_gpio_pin: pinmux_P9_91_gpio_pin { + pinctrl-single,pins = <0x1a8 0x2F>; }; /* Mode 7, RxActive */ + P9_91_gpio_pu_pin: pinmux_P9_91_gpio_pu_pin { + pinctrl-single,pins = <0x1a8 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_91_gpio_pd_pin: pinmux_P9_91_gpio_pd_pin { + pinctrl-single,pins = <0x1a8 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_91_qep_pin: pinmux_P9_91_qep_pin { + pinctrl-single,pins = <0x1a8 0x21>; }; /* Mode 1, Pull-Down, RxActive */ + P9_91_pruout_pin: pinmux_P9_91_pruout_pin { + pinctrl-single,pins = <0x1a8 0x25>; }; /* Mode 5, Pull-Down, RxActive */ + P9_91_pruin_pin: pinmux_P9_91_pruin_pin { + pinctrl-single,pins = <0x1a8 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + + /* P9_42 (ZCZ ball C18) */ + P9_42_default_pin: pinmux_P9_42_default_pin { + pinctrl-single,pins = <0x164 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_42_gpio_pin: pinmux_P9_42_gpio_pin { + pinctrl-single,pins = <0x164 0x2F>; }; /* Mode 7, RxActive */ + P9_42_gpio_pu_pin: pinmux_P9_42_gpio_pu_pin { + pinctrl-single,pins = <0x164 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_42_gpio_pd_pin: pinmux_P9_42_gpio_pd_pin { + pinctrl-single,pins = <0x164 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_42_pwm_pin: pinmux_P9_42_pwm_pin { + pinctrl-single,pins = <0x164 0x20>; }; /* Mode 0, Pull-Down, RxActive */ + P9_42_uart_pin: pinmux_P9_42_uart_pin { + pinctrl-single,pins = <0x164 0x21>; }; /* Mode 1, Pull-Down, RxActive */ + P9_42_spics_pin: pinmux_P9_42_spics_pin { + pinctrl-single,pins = <0x164 0x22>; }; /* Mode 2, Pull-Down, RxActive */ + P9_42_spiclk_pin: pinmux_P9_42_spiclk_pin { + pinctrl-single,pins = <0x164 0x24>; }; /* Mode 4, Pull-Down, RxActive */ + + /* P9_42.1 */ + /* P9_92 (ZCZ ball B12) */ + P9_92_default_pin: pinmux_P9_92_default_pin { + pinctrl-single,pins = <0x1a0 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_92_gpio_pin: pinmux_P9_92_gpio_pin { + pinctrl-single,pins = <0x1a0 0x2F>; }; /* Mode 7, RxActive */ + P9_92_gpio_pu_pin: pinmux_P9_92_gpio_pu_pin { + pinctrl-single,pins = <0x1a0 0x37>; }; /* Mode 7, Pull-Up, RxActive */ + P9_92_gpio_pd_pin: pinmux_P9_92_gpio_pd_pin { + pinctrl-single,pins = <0x1a0 0x27>; }; /* Mode 7, Pull-Down, RxActive */ + P9_92_qep_pin: pinmux_P9_92_qep_pin { + pinctrl-single,pins = <0x1a0 0x21>; }; /* Mode 1, Pull-Down, RxActive */ + P9_92_pruout_pin: pinmux_P9_92_pruout_pin { + pinctrl-single,pins = <0x1a0 0x25>; }; /* Mode 5, Pull-Down, RxActive */ + P9_92_pruin_pin: pinmux_P9_92_pruin_pin { + pinctrl-single,pins = <0x1a0 0x26>; }; /* Mode 6, Pull-Down, RxActive */ + + /* P9_43 GND */ + /* P9_44 GND */ + /* P9_45 GND */ + /* P9_46 GND */ +}; + +/**********************************************************************/ +/* Pin Multiplex Helpers */ +/* */ +/* These provide userspace runtime pin configuration for the */ +/* BeagleBone cape expansion headers */ +/**********************************************************************/ + +&ocp { + /************************/ + /* P8 Header */ + /************************/ + + P8_07_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer"; + pinctrl-0 = <&P8_07_default_pin>; + pinctrl-1 = <&P8_07_gpio_pin>; + pinctrl-2 = <&P8_07_gpio_pu_pin>; + pinctrl-3 = <&P8_07_gpio_pd_pin>; + pinctrl-4 = <&P8_07_timer_pin>; + }; + + P8_08_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer"; + pinctrl-0 = <&P8_08_default_pin>; + pinctrl-1 = <&P8_08_gpio_pin>; + pinctrl-2 = <&P8_08_gpio_pu_pin>; + pinctrl-3 = <&P8_08_gpio_pd_pin>; + pinctrl-4 = <&P8_08_timer_pin>; + }; + + P8_09_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer"; + pinctrl-0 = <&P8_09_default_pin>; + pinctrl-1 = <&P8_09_gpio_pin>; + pinctrl-2 = <&P8_09_gpio_pu_pin>; + pinctrl-3 = <&P8_09_gpio_pd_pin>; + pinctrl-4 = <&P8_09_timer_pin>; + }; + + P8_10_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer"; + pinctrl-0 = <&P8_10_default_pin>; + pinctrl-1 = <&P8_10_gpio_pin>; + pinctrl-2 = <&P8_10_gpio_pu_pin>; + pinctrl-3 = <&P8_10_gpio_pd_pin>; + pinctrl-4 = <&P8_10_timer_pin>; + }; + + P8_11_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "qep"; + pinctrl-0 = <&P8_11_default_pin>; + pinctrl-1 = <&P8_11_gpio_pin>; + pinctrl-2 = <&P8_11_gpio_pu_pin>; + pinctrl-3 = <&P8_11_gpio_pd_pin>; + pinctrl-4 = <&P8_11_pruout_pin>; + pinctrl-5 = <&P8_11_qep_pin>; + }; + + P8_12_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "qep"; + pinctrl-0 = <&P8_12_default_pin>; + pinctrl-1 = <&P8_12_gpio_pin>; + pinctrl-2 = <&P8_12_gpio_pu_pin>; + pinctrl-3 = <&P8_12_gpio_pd_pin>; + pinctrl-4 = <&P8_12_pruout_pin>; + pinctrl-5 = <&P8_12_qep_pin>; + }; + + P8_13_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P8_13_default_pin>; + pinctrl-1 = <&P8_13_gpio_pin>; + pinctrl-2 = <&P8_13_gpio_pu_pin>; + pinctrl-3 = <&P8_13_gpio_pd_pin>; + pinctrl-4 = <&P8_13_pwm_pin>; + }; + + P8_14_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P8_14_default_pin>; + pinctrl-1 = <&P8_14_gpio_pin>; + pinctrl-2 = <&P8_14_gpio_pu_pin>; + pinctrl-3 = <&P8_14_gpio_pd_pin>; + pinctrl-4 = <&P8_14_pwm_pin>; + }; + + P8_15_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruin", "qep"; + pinctrl-0 = <&P8_15_default_pin>; + pinctrl-1 = <&P8_15_gpio_pin>; + pinctrl-2 = <&P8_15_gpio_pu_pin>; + pinctrl-3 = <&P8_15_gpio_pd_pin>; + pinctrl-4 = <&P8_15_pruin_pin>; + pinctrl-5 = <&P8_15_qep_pin>; + }; + + P8_16_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruin", "qep"; + pinctrl-0 = <&P8_16_default_pin>; + pinctrl-1 = <&P8_16_gpio_pin>; + pinctrl-2 = <&P8_16_gpio_pu_pin>; + pinctrl-3 = <&P8_16_gpio_pd_pin>; + pinctrl-4 = <&P8_16_pruin_pin>; + pinctrl-5 = <&P8_16_qep_pin>; + }; + + P8_17_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P8_17_default_pin>; + pinctrl-1 = <&P8_17_gpio_pin>; + pinctrl-2 = <&P8_17_gpio_pu_pin>; + pinctrl-3 = <&P8_17_gpio_pd_pin>; + pinctrl-4 = <&P8_17_pwm_pin>; + }; + + P8_18_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&P8_18_default_pin>; + pinctrl-1 = <&P8_18_gpio_pin>; + pinctrl-2 = <&P8_18_gpio_pu_pin>; + pinctrl-3 = <&P8_18_gpio_pd_pin>; + }; + + P8_19_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P8_19_default_pin>; + pinctrl-1 = <&P8_19_gpio_pin>; + pinctrl-2 = <&P8_19_gpio_pu_pin>; + pinctrl-3 = <&P8_19_gpio_pd_pin>; + pinctrl-4 = <&P8_19_pwm_pin>; + }; + + P8_26_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&P8_26_default_pin>; + pinctrl-1 = <&P8_26_gpio_pin>; + pinctrl-2 = <&P8_26_gpio_pu_pin>; + pinctrl-3 = <&P8_26_gpio_pd_pin>; + }; + + P8_27_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin", "hdmi"; + pinctrl-0 = <&P8_27_default_pin>; + pinctrl-1 = <&P8_27_gpio_pin>; + pinctrl-2 = <&P8_27_gpio_pu_pin>; + pinctrl-3 = <&P8_27_gpio_pd_pin>; + pinctrl-4 = <&P8_27_pruout_pin>; + pinctrl-5 = <&P8_27_pruin_pin>; + pinctrl-6 = <&P8_27_hdmi_pin>; + }; + + P8_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin", "hdmi"; + pinctrl-0 = <&P8_28_default_pin>; + pinctrl-1 = <&P8_28_gpio_pin>; + pinctrl-2 = <&P8_28_gpio_pu_pin>; + pinctrl-3 = <&P8_28_gpio_pd_pin>; + pinctrl-4 = <&P8_28_pruout_pin>; + pinctrl-5 = <&P8_28_pruin_pin>; + pinctrl-6 = <&P8_28_hdmi_pin>; + }; + + P8_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin", "hdmi"; + pinctrl-0 = <&P8_29_default_pin>; + pinctrl-1 = <&P8_29_gpio_pin>; + pinctrl-2 = <&P8_29_gpio_pu_pin>; + pinctrl-3 = <&P8_29_gpio_pd_pin>; + pinctrl-4 = <&P8_29_pruout_pin>; + pinctrl-5 = <&P8_29_pruin_pin>; + pinctrl-6 = <&P8_29_hdmi_pin>; + }; + + P8_30_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin", "hdmi"; + pinctrl-0 = <&P8_30_default_pin>; + pinctrl-1 = <&P8_30_gpio_pin>; + pinctrl-2 = <&P8_30_gpio_pu_pin>; + pinctrl-3 = <&P8_30_gpio_pd_pin>; + pinctrl-4 = <&P8_30_pruout_pin>; + pinctrl-5 = <&P8_30_pruin_pin>; + pinctrl-6 = <&P8_30_hdmi_pin>; + }; + + P8_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd","uart", "hdmi"; + pinctrl-0 = <&P8_31_default_pin>; + pinctrl-1 = <&P8_31_gpio_pin>; + pinctrl-2 = <&P8_31_gpio_pu_pin>; + pinctrl-3 = <&P8_31_gpio_pd_pin>; + pinctrl-4 = <&P8_31_uart_pin>; + pinctrl-5 = <&P8_31_hdmi_pin>; + }; + + P8_32_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "hdmi"; + pinctrl-0 = <&P8_32_default_pin>; + pinctrl-1 = <&P8_32_gpio_pin>; + pinctrl-2 = <&P8_32_gpio_pu_pin>; + pinctrl-3 = <&P8_32_gpio_pd_pin>; + pinctrl-4 = <&P8_32_hdmi_pin>; + }; + + P8_33_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "hdmi"; + pinctrl-0 = <&P8_33_default_pin>; + pinctrl-1 = <&P8_33_gpio_pin>; + pinctrl-2 = <&P8_33_gpio_pu_pin>; + pinctrl-3 = <&P8_33_gpio_pd_pin>; + pinctrl-4 = <&P8_33_hdmi_pin>; + }; + + P8_34_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd","pwm", "hdmi"; + pinctrl-0 = <&P8_34_default_pin>; + pinctrl-1 = <&P8_34_gpio_pin>; + pinctrl-2 = <&P8_34_gpio_pu_pin>; + pinctrl-3 = <&P8_34_gpio_pd_pin>; + pinctrl-4 = <&P8_34_pwm_pin>; + pinctrl-5 = <&P8_34_hdmi_pin>; + }; + + P8_35_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "hdmi"; + pinctrl-0 = <&P8_35_default_pin>; + pinctrl-1 = <&P8_35_gpio_pin>; + pinctrl-2 = <&P8_35_gpio_pu_pin>; + pinctrl-3 = <&P8_35_gpio_pd_pin>; + pinctrl-4 = <&P8_35_hdmi_pin>; + }; + + P8_36_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd","pwm", "hdmi"; + pinctrl-0 = <&P8_36_default_pin>; + pinctrl-1 = <&P8_36_gpio_pin>; + pinctrl-2 = <&P8_36_gpio_pu_pin>; + pinctrl-3 = <&P8_36_gpio_pd_pin>; + pinctrl-4 = <&P8_36_pwm_pin>; + pinctrl-5 = <&P8_36_hdmi_pin>; + }; + + P8_37_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd","uart","pwm", "hdmi"; + pinctrl-0 = <&P8_37_default_pin>; + pinctrl-1 = <&P8_37_gpio_pin>; + pinctrl-2 = <&P8_37_gpio_pu_pin>; + pinctrl-3 = <&P8_37_gpio_pd_pin>; + pinctrl-4 = <&P8_37_uart_pin>; + pinctrl-5 = <&P8_37_pwm_pin>; + pinctrl-6 = <&P8_37_hdmi_pin>; + }; + + P8_38_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd","uart","pwm", "hdmi"; + pinctrl-0 = <&P8_38_default_pin>; + pinctrl-1 = <&P8_38_gpio_pin>; + pinctrl-2 = <&P8_38_gpio_pu_pin>; + pinctrl-3 = <&P8_38_gpio_pd_pin>; + pinctrl-4 = <&P8_38_uart_pin>; + pinctrl-5 = <&P8_38_pwm_pin>; + pinctrl-6 = <&P8_38_hdmi_pin>; + }; + + P8_39_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin", "hdmi"; + pinctrl-0 = <&P8_39_default_pin>; + pinctrl-1 = <&P8_39_gpio_pin>; + pinctrl-2 = <&P8_39_gpio_pu_pin>; + pinctrl-3 = <&P8_39_gpio_pd_pin>; + pinctrl-4 = <&P8_39_pruout_pin>; + pinctrl-5 = <&P8_39_pruin_pin>; + pinctrl-6 = <&P8_39_hdmi_pin>; + }; + + P8_40_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin", "hdmi"; + pinctrl-0 = <&P8_40_default_pin>; + pinctrl-1 = <&P8_40_gpio_pin>; + pinctrl-2 = <&P8_40_gpio_pu_pin>; + pinctrl-3 = <&P8_40_gpio_pd_pin>; + pinctrl-4 = <&P8_40_pruout_pin>; + pinctrl-5 = <&P8_40_pruin_pin>; + pinctrl-6 = <&P8_40_hdmi_pin>; + }; + + P8_41_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin", "hdmi"; + pinctrl-0 = <&P8_41_default_pin>; + pinctrl-1 = <&P8_41_gpio_pin>; + pinctrl-2 = <&P8_41_gpio_pu_pin>; + pinctrl-3 = <&P8_41_gpio_pd_pin>; + pinctrl-4 = <&P8_41_pruout_pin>; + pinctrl-5 = <&P8_41_pruin_pin>; + pinctrl-6 = <&P8_41_hdmi_pin>; + }; + + P8_42_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin", "hdmi"; + pinctrl-0 = <&P8_42_default_pin>; + pinctrl-1 = <&P8_42_gpio_pin>; + pinctrl-2 = <&P8_42_gpio_pu_pin>; + pinctrl-3 = <&P8_42_gpio_pd_pin>; + pinctrl-4 = <&P8_42_pruout_pin>; + pinctrl-5 = <&P8_42_pruin_pin>; + pinctrl-6 = <&P8_42_hdmi_pin>; + }; + + P8_43_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin","pwm", "hdmi"; + pinctrl-0 = <&P8_43_default_pin>; + pinctrl-1 = <&P8_43_gpio_pin>; + pinctrl-2 = <&P8_43_gpio_pu_pin>; + pinctrl-3 = <&P8_43_gpio_pd_pin>; + pinctrl-4 = <&P8_43_pruout_pin>; + pinctrl-5 = <&P8_43_pruin_pin>; + pinctrl-6 = <&P8_43_pwm_pin>; + pinctrl-7 = <&P8_43_hdmi_pin>; + }; + + P8_44_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin","pwm", "hdmi"; + pinctrl-0 = <&P8_44_default_pin>; + pinctrl-1 = <&P8_44_gpio_pin>; + pinctrl-2 = <&P8_44_gpio_pu_pin>; + pinctrl-3 = <&P8_44_gpio_pd_pin>; + pinctrl-4 = <&P8_44_pruout_pin>; + pinctrl-5 = <&P8_44_pruin_pin>; + pinctrl-6 = <&P8_44_pwm_pin>; + pinctrl-7 = <&P8_44_hdmi_pin>; + }; + + P8_45_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin","pwm", "hdmi"; + pinctrl-0 = <&P8_45_default_pin>; + pinctrl-1 = <&P8_45_gpio_pin>; + pinctrl-2 = <&P8_45_gpio_pu_pin>; + pinctrl-3 = <&P8_45_gpio_pd_pin>; + pinctrl-4 = <&P8_45_pruout_pin>; + pinctrl-5 = <&P8_45_pruin_pin>; + pinctrl-6 = <&P8_45_pwm_pin>; + pinctrl-7 = <&P8_45_hdmi_pin>; + }; + + P8_46_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pruout", "pruin","pwm", "hdmi"; + pinctrl-0 = <&P8_46_default_pin>; + pinctrl-1 = <&P8_46_gpio_pin>; + pinctrl-2 = <&P8_46_gpio_pu_pin>; + pinctrl-3 = <&P8_46_gpio_pd_pin>; + pinctrl-4 = <&P8_46_pruout_pin>; + pinctrl-5 = <&P8_46_pruin_pin>; + pinctrl-6 = <&P8_46_pwm_pin>; + pinctrl-7 = <&P8_46_hdmi_pin>; + }; + + /************************/ + /* P9 Header */ + /************************/ + + P9_11_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart"; + pinctrl-0 = <&P9_11_default_pin>; + pinctrl-1 = <&P9_11_gpio_pin>; + pinctrl-2 = <&P9_11_gpio_pu_pin>; + pinctrl-3 = <&P9_11_gpio_pd_pin>; + pinctrl-4 = <&P9_11_uart_pin>; + }; + + P9_12_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&P9_12_default_pin>; + pinctrl-1 = <&P9_12_gpio_pin>; + pinctrl-2 = <&P9_12_gpio_pu_pin>; + pinctrl-3 = <&P9_12_gpio_pd_pin>; + }; + + P9_13_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart"; + pinctrl-0 = <&P9_13_default_pin>; + pinctrl-1 = <&P9_13_gpio_pin>; + pinctrl-2 = <&P9_13_gpio_pu_pin>; + pinctrl-3 = <&P9_13_gpio_pd_pin>; + pinctrl-4 = <&P9_13_uart_pin>; + }; + + P9_14_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P9_14_default_pin>; + pinctrl-1 = <&P9_14_gpio_pin>; + pinctrl-2 = <&P9_14_gpio_pu_pin>; + pinctrl-3 = <&P9_14_gpio_pd_pin>; + pinctrl-4 = <&P9_14_pwm_pin>; + }; + + P9_15_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P9_15_default_pin>; + pinctrl-1 = <&P9_15_gpio_pin>; + pinctrl-2 = <&P9_15_gpio_pu_pin>; + pinctrl-3 = <&P9_15_gpio_pd_pin>; + pinctrl-4 = <&P9_15_pwm_pin>; + }; + + P9_16_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P9_16_default_pin>; + pinctrl-1 = <&P9_16_gpio_pin>; + pinctrl-2 = <&P9_16_gpio_pu_pin>; + pinctrl-3 = <&P9_16_gpio_pd_pin>; + pinctrl-4 = <&P9_16_pwm_pin>; + }; + + P9_17_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "i2c", "pwm"; + pinctrl-0 = <&P9_17_default_pin>; + pinctrl-1 = <&P9_17_gpio_pin>; + pinctrl-2 = <&P9_17_gpio_pu_pin>; + pinctrl-3 = <&P9_17_gpio_pd_pin>; + pinctrl-4 = <&P9_17_spi_pin>; + pinctrl-5 = <&P9_17_i2c_pin>; + pinctrl-6 = <&P9_17_pwm_pin>; + }; + + P9_18_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "i2c", "pwm"; + pinctrl-0 = <&P9_18_default_pin>; + pinctrl-1 = <&P9_18_gpio_pin>; + pinctrl-2 = <&P9_18_gpio_pu_pin>; + pinctrl-3 = <&P9_18_gpio_pd_pin>; + pinctrl-4 = <&P9_18_spi_pin>; + pinctrl-5 = <&P9_18_i2c_pin>; + pinctrl-6 = <&P9_18_pwm_pin>; + }; + + P9_19_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "can", "i2c"; + pinctrl-0 = <&P9_19_default_pin>; + pinctrl-1 = <&P9_19_gpio_pin>; + pinctrl-2 = <&P9_19_gpio_pu_pin>; + pinctrl-3 = <&P9_19_gpio_pd_pin>; + pinctrl-4 = <&P9_19_can_pin>; + pinctrl-5 = <&P9_19_i2c_pin>; + }; + + P9_20_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "can", "i2c"; + pinctrl-0 = <&P9_20_default_pin>; + pinctrl-1 = <&P9_20_gpio_pin>; + pinctrl-2 = <&P9_20_gpio_pu_pin>; + pinctrl-3 = <&P9_20_gpio_pd_pin>; + pinctrl-4 = <&P9_20_can_pin>; + pinctrl-5 = <&P9_20_i2c_pin>; + }; + + P9_21_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "uart", "i2c", "pwm"; + pinctrl-0 = <&P9_21_default_pin>; + pinctrl-1 = <&P9_21_gpio_pin>; + pinctrl-2 = <&P9_21_gpio_pu_pin>; + pinctrl-3 = <&P9_21_gpio_pd_pin>; + pinctrl-4 = <&P9_21_spi_pin>; + pinctrl-5 = <&P9_21_uart_pin>; + pinctrl-6 = <&P9_21_i2c_pin>; + pinctrl-7 = <&P9_21_pwm_pin>; + }; + + P9_22_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "spi", "uart", "i2c", "pwm"; + pinctrl-0 = <&P9_22_default_pin>; + pinctrl-1 = <&P9_22_gpio_pin>; + pinctrl-2 = <&P9_22_gpio_pu_pin>; + pinctrl-3 = <&P9_22_gpio_pd_pin>; + pinctrl-4 = <&P9_22_spi_pin>; + pinctrl-5 = <&P9_22_uart_pin>; + pinctrl-6 = <&P9_22_i2c_pin>; + pinctrl-7 = <&P9_22_pwm_pin>; + }; + + P9_23_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm"; + pinctrl-0 = <&P9_23_default_pin>; + pinctrl-1 = <&P9_23_gpio_pin>; + pinctrl-2 = <&P9_23_gpio_pu_pin>; + pinctrl-3 = <&P9_23_gpio_pd_pin>; + pinctrl-4 = <&P9_23_pwm_pin>; + }; + + P9_24_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "can", "i2c", "pruin"; + pinctrl-0 = <&P9_24_default_pin>; + pinctrl-1 = <&P9_24_gpio_pin>; + pinctrl-2 = <&P9_24_gpio_pu_pin>; + pinctrl-3 = <&P9_24_gpio_pd_pin>; + pinctrl-4 = <&P9_24_uart_pin>; + pinctrl-5 = <&P9_24_can_pin>; + pinctrl-6 = <&P9_24_i2c_pin>; + pinctrl-7 = <&P9_24_pruin_pin>; + }; + + P9_25_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "qep", "pruout", "pruin", "audio"; + pinctrl-0 = <&P9_25_default_pin>; + pinctrl-1 = <&P9_25_gpio_pin>; + pinctrl-2 = <&P9_25_gpio_pu_pin>; + pinctrl-3 = <&P9_25_gpio_pd_pin>; + pinctrl-4 = <&P9_25_qep_pin>; + pinctrl-5 = <&P9_25_pruout_pin>; + pinctrl-6 = <&P9_25_pruin_pin>; + pinctrl-7 = <&P9_25_audio_pin>; + }; + + P9_26_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "uart", "can", "i2c", "pruin"; + pinctrl-0 = <&P9_26_default_pin>; + pinctrl-1 = <&P9_26_gpio_pin>; + pinctrl-2 = <&P9_26_gpio_pu_pin>; + pinctrl-3 = <&P9_26_gpio_pd_pin>; + pinctrl-4 = <&P9_26_uart_pin>; + pinctrl-5 = <&P9_26_can_pin>; + pinctrl-6 = <&P9_26_i2c_pin>; + pinctrl-7 = <&P9_26_pruin_pin>; + }; + + P9_27_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "qep", "pruout", "pruin"; + pinctrl-0 = <&P9_27_default_pin>; + pinctrl-1 = <&P9_27_gpio_pin>; + pinctrl-2 = <&P9_27_gpio_pu_pin>; + pinctrl-3 = <&P9_27_gpio_pd_pin>; + pinctrl-4 = <&P9_27_qep_pin>; + pinctrl-5 = <&P9_27_pruout_pin>; + pinctrl-6 = <&P9_27_pruin_pin>; + }; + + P9_28_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "spi", "pwm2", "pruout", "pruin", "audio"; + pinctrl-0 = <&P9_28_default_pin>; + pinctrl-1 = <&P9_28_gpio_pin>; + pinctrl-2 = <&P9_28_gpio_pu_pin>; + pinctrl-3 = <&P9_28_gpio_pd_pin>; + pinctrl-4 = <&P9_28_pwm_pin>; + pinctrl-5 = <&P9_28_spi_pin>; + pinctrl-6 = <&P9_28_pwm2_pin>; + pinctrl-7 = <&P9_28_pruout_pin>; + pinctrl-8 = <&P9_28_pruin_pin>; + pinctrl-9 = <&P9_28_audio_pin>; + }; + + P9_29_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "spi", "pruout", "pruin", "audio"; + pinctrl-0 = <&P9_29_default_pin>; + pinctrl-1 = <&P9_29_gpio_pin>; + pinctrl-2 = <&P9_29_gpio_pu_pin>; + pinctrl-3 = <&P9_29_gpio_pd_pin>; + pinctrl-4 = <&P9_29_pwm_pin>; + pinctrl-5 = <&P9_29_spi_pin>; + pinctrl-6 = <&P9_29_pruout_pin>; + pinctrl-7 = <&P9_29_pruin_pin>; + pinctrl-8 = <&P9_29_audio_pin>; + }; + + P9_30_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "spi", "pruout", "pruin"; + pinctrl-0 = <&P9_30_default_pin>; + pinctrl-1 = <&P9_30_gpio_pin>; + pinctrl-2 = <&P9_30_gpio_pu_pin>; + pinctrl-3 = <&P9_30_gpio_pd_pin>; + pinctrl-4 = <&P9_30_pwm_pin>; + pinctrl-5 = <&P9_30_spi_pin>; + pinctrl-6 = <&P9_30_pruout_pin>; + pinctrl-7 = <&P9_30_pruin_pin>; + }; + + P9_31_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "spi", "pruout", "pruin", "audio"; + pinctrl-0 = <&P9_31_default_pin>; + pinctrl-1 = <&P9_31_gpio_pin>; + pinctrl-2 = <&P9_31_gpio_pu_pin>; + pinctrl-3 = <&P9_31_gpio_pd_pin>; + pinctrl-4 = <&P9_31_pwm_pin>; + pinctrl-5 = <&P9_31_spi_pin>; + pinctrl-6 = <&P9_31_pruout_pin>; + pinctrl-7 = <&P9_31_pruin_pin>; + pinctrl-8 = <&P9_31_audio_pin>; + }; + + P9_41_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "timer", "pruin"; + pinctrl-0 = <&P9_41_default_pin>; + pinctrl-1 = <&P9_41_gpio_pin>; + pinctrl-2 = <&P9_41_gpio_pu_pin>; + pinctrl-3 = <&P9_41_gpio_pd_pin>; + pinctrl-4 = <&P9_41_timer_pin>; + pinctrl-5 = <&P9_41_pruin_pin>; + }; + + P9_91_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "qep", "pruout", "pruin"; + pinctrl-0 = <&P9_91_default_pin>; + pinctrl-1 = <&P9_91_gpio_pin>; + pinctrl-2 = <&P9_91_gpio_pu_pin>; + pinctrl-3 = <&P9_91_gpio_pd_pin>; + pinctrl-4 = <&P9_91_qep_pin>; + pinctrl-5 = <&P9_91_pruout_pin>; + pinctrl-6 = <&P9_91_pruin_pin>; + }; + + P9_42_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "pwm", "uart", "spics", "spiclk"; + pinctrl-0 = <&P9_42_default_pin>; + pinctrl-1 = <&P9_42_gpio_pin>; + pinctrl-2 = <&P9_42_gpio_pu_pin>; + pinctrl-3 = <&P9_42_gpio_pd_pin>; + pinctrl-4 = <&P9_42_pwm_pin>; + pinctrl-5 = <&P9_42_uart_pin>; + pinctrl-6 = <&P9_42_spics_pin>; + pinctrl-7 = <&P9_42_spiclk_pin>; + }; + + P9_92_pinmux { + compatible = "bone-pinmux-helper"; + status = "okay"; + pinctrl-names = "default", "gpio", "gpio_pu", "gpio_pd", "qep", "pruout", "pruin"; + pinctrl-0 = <&P9_92_default_pin>; + pinctrl-1 = <&P9_92_gpio_pin>; + pinctrl-2 = <&P9_92_gpio_pu_pin>; + pinctrl-3 = <&P9_92_gpio_pd_pin>; + pinctrl-4 = <&P9_92_qep_pin>; + pinctrl-5 = <&P9_92_pruout_pin>; + pinctrl-6 = <&P9_92_pruin_pin>; + }; + + cape-universal { + compatible = "gpio-of-helper"; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + P8_07 { + gpio-name = "P8_07"; + gpio = <&gpio2 2 0>; + input; + dir-changeable; + }; + P8_08 { + gpio-name = "P8_08"; + gpio = <&gpio2 3 0>; + input; + dir-changeable; + }; + P8_09 { + gpio-name = "P8_09"; + gpio = <&gpio2 5 0>; + input; + dir-changeable; + }; + P8_10 { + gpio-name = "P8_10"; + gpio = <&gpio2 4 0>; + input; + dir-changeable; + }; + P8_11 { + gpio-name = "P8_11"; + gpio = <&gpio1 13 0>; + input; + dir-changeable; + }; + P8_12 { + gpio-name = "P8_12"; + gpio = <&gpio1 12 0>; + input; + dir-changeable; + }; + P8_13 { + gpio-name = "P8_13"; + gpio = <&gpio0 23 0>; + input; + dir-changeable; + }; + P8_14 { + gpio-name = "P8_14"; + gpio = <&gpio0 26 0>; + input; + dir-changeable; + }; + P8_15 { + gpio-name = "P8_15"; + gpio = <&gpio1 15 0>; + input; + dir-changeable; + }; + P8_16 { + gpio-name = "P8_16"; + gpio = <&gpio1 14 0>; + input; + dir-changeable; + }; + P8_17 { + gpio-name = "P8_17"; + gpio = <&gpio0 27 0>; + input; + dir-changeable; + }; + P8_18 { + gpio-name = "P8_18"; + gpio = <&gpio2 1 0>; + input; + dir-changeable; + }; + P8_19 { + gpio-name = "P8_19"; + gpio = <&gpio0 22 0>; + input; + dir-changeable; + }; + + P8_26 { + gpio-name = "P8_26"; + gpio = <&gpio1 29 0>; + input; + dir-changeable; + }; + P8_27 { + gpio-name = "P8_27"; + gpio = <&gpio2 22 0>; + input; + dir-changeable; + }; + P8_28 { + gpio-name = "P8_28"; + gpio = <&gpio2 24 0>; + input; + dir-changeable; + }; + P8_29 { + gpio-name = "P8_29"; + gpio = <&gpio2 23 0>; + input; + dir-changeable; + }; + P8_30 { + gpio-name = "P8_30"; + gpio = <&gpio2 25 0>; + input; + dir-changeable; + }; + P8_31 { + gpio-name = "P8_31"; + gpio = <&gpio0 10 0>; + input; + dir-changeable; + }; + P8_32 { + gpio-name = "P8_32"; + gpio = <&gpio0 11 0>; + input; + dir-changeable; + }; + P8_33 { + gpio-name = "P8_33"; + gpio = <&gpio0 9 0>; + input; + dir-changeable; + }; + P8_34 { + gpio-name = "P8_34"; + gpio = <&gpio2 17 0>; + input; + dir-changeable; + }; + P8_35 { + gpio-name = "P8_35"; + gpio = <&gpio0 8 0>; + input; + dir-changeable; + }; + P8_36 { + gpio-name = "P8_36"; + gpio = <&gpio2 16 0>; + input; + dir-changeable; + }; + P8_37 { + gpio-name = "P8_37"; + gpio = <&gpio2 14 0>; + input; + dir-changeable; + }; + P8_38 { + gpio-name = "P8_38"; + gpio = <&gpio2 15 0>; + input; + dir-changeable; + }; + P8_39 { + gpio-name = "P8_39"; + gpio = <&gpio2 12 0>; + input; + dir-changeable; + }; + P8_40 { + gpio-name = "P8_40"; + gpio = <&gpio2 13 0>; + input; + dir-changeable; + }; + P8_41 { + gpio-name = "P8_41"; + gpio = <&gpio2 10 0>; + input; + dir-changeable; + }; + P8_42 { + gpio-name = "P8_42"; + gpio = <&gpio2 11 0>; + input; + dir-changeable; + }; + P8_43 { + gpio-name = "P8_43"; + gpio = <&gpio2 8 0>; + input; + dir-changeable; + }; + P8_44 { + gpio-name = "P8_44"; + gpio = <&gpio2 9 0>; + input; + dir-changeable; + }; + P8_45 { + gpio-name = "P8_45"; + gpio = <&gpio2 6 0>; + input; + dir-changeable; + }; + P8_46 { + gpio-name = "P8_46"; + gpio = <&gpio2 7 0>; + input; + dir-changeable; + }; + + + P9_11 { + gpio-name = "P9_11"; + gpio = <&gpio0 30 0>; + input; + dir-changeable; + }; + P9_12 { + gpio-name = "P9_12"; + gpio = <&gpio1 28 0>; + input; + dir-changeable; + }; + P9_13 { + gpio-name = "P9_13"; + gpio = <&gpio0 31 0>; + input; + dir-changeable; + }; + P9_14 { + gpio-name = "P9_14"; + gpio = <&gpio1 18 0>; + input; + dir-changeable; + }; + P9_15 { + gpio-name = "P9_15"; + gpio = <&gpio1 16 0>; + input; + dir-changeable; + }; + P9_16 { + gpio-name = "P9_16"; + gpio = <&gpio1 19 0>; + input; + dir-changeable; + }; + P9_17 { + gpio-name = "P9_17"; + gpio = <&gpio0 5 0>; + input; + dir-changeable; + }; + P9_18 { + gpio-name = "P9_18"; + gpio = <&gpio0 4 0>; + input; + dir-changeable; + }; + P9_19 { + gpio-name = "P9_19"; + gpio = <&gpio0 13 0>; + input; + dir-changeable; + }; + P9_20 { + gpio-name = "P9_20"; + gpio = <&gpio0 12 0>; + input; + dir-changeable; + }; + P9_21 { + gpio-name = "P9_21"; + gpio = <&gpio0 3 0>; + input; + dir-changeable; + }; + P9_22 { + gpio-name = "P9_22"; + gpio = <&gpio0 2 0>; + input; + dir-changeable; + }; + P9_23 { + gpio-name = "P9_23"; + gpio = <&gpio1 17 0>; + input; + dir-changeable; + }; + P9_24 { + gpio-name = "P9_24"; + gpio = <&gpio0 15 0>; + input; + dir-changeable; + }; + P9_25 { + gpio-name = "P9_25"; + gpio = <&gpio3 21 0>; + input; + dir-changeable; + }; + P9_26 { + gpio-name = "P9_26"; + gpio = <&gpio0 14 0>; + input; + dir-changeable; + }; + P9_27 { + gpio-name = "P9_27"; + gpio = <&gpio3 19 0>; + input; + dir-changeable; + }; + P9_28 { + gpio-name = "P9_28"; + gpio = <&gpio3 17 0>; + input; + dir-changeable; + }; + P9_29 { + gpio-name = "P9_29"; + gpio = <&gpio3 15 0>; + input; + dir-changeable; + }; + P9_30 { + gpio-name = "P9_30"; + gpio = <&gpio3 16 0>; + input; + dir-changeable; + }; + P9_31 { + gpio-name = "P9_31"; + gpio = <&gpio3 14 0>; + input; + dir-changeable; + }; + P9_41 { + gpio-name = "P9_41"; + gpio = <&gpio0 20 0>; + input; + dir-changeable; + }; + P9_91 { + gpio-name = "P9_91"; + gpio = <&gpio3 20 0>; + input; + dir-changeable; + }; + P9_42 { + gpio-name = "P9_42"; + gpio = <&gpio0 7 0>; + input; + dir-changeable; + }; + P9_92 { + gpio-name = "P9_92"; + gpio = <&gpio3 18 0>; + input; + dir-changeable; + }; + }; +}; diff --git a/arch/arm/boot/dts/am335x-bone-common.dtsi b/arch/arm/boot/dts/am335x-bone-common.dtsi index 0cc150b..5d6b640 100644 --- a/arch/arm/boot/dts/am335x-bone-common.dtsi +++ b/arch/arm/boot/dts/am335x-bone-common.dtsi @@ -25,14 +25,14 @@ compatible = "gpio-leds"; led@2 { - label = "beaglebone:green:heartbeat"; + label = "beaglebone:green:usr0"; gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>; linux,default-trigger = "heartbeat"; default-state = "off"; }; led@3 { - label = "beaglebone:green:mmc0"; + label = "beaglebone:green:usr1"; gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>; linux,default-trigger = "mmc0"; default-state = "off"; @@ -62,9 +62,6 @@ }; &am33xx_pinmux { - pinctrl-names = "default"; - pinctrl-0 = <&clkout2_pin>; - user_leds_s0: user_leds_s0 { pinctrl-single,pins = < AM33XX_IOPAD(0x854, PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpmc_a5.gpio1_21 */ @@ -95,15 +92,11 @@ >; }; - clkout2_pin: pinmux_clkout2_pin { - pinctrl-single,pins = < - AM33XX_IOPAD(0x9b4, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr1.clkout2 */ - >; - }; - cpsw_default: cpsw_default { pinctrl-single,pins = < /* Slave 1 */ + 0x108 (PIN_INPUT | MUX_MODE0) /* mii1_col.mii1_col */ + 0x10c (PIN_INPUT | MUX_MODE0) /* mii1_crs.mii1_crs */ AM33XX_IOPAD(0x910, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxerr.mii1_rxerr */ AM33XX_IOPAD(0x914, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txen.mii1_txen */ AM33XX_IOPAD(0x918, PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxdv.mii1_rxdv */ @@ -123,6 +116,8 @@ cpsw_sleep: cpsw_sleep { pinctrl-single,pins = < /* Slave 1 reset value */ + 0x108 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x10c (PIN_INPUT_PULLDOWN | MUX_MODE7) AM33XX_IOPAD(0x910, PIN_INPUT_PULLDOWN | MUX_MODE7) AM33XX_IOPAD(0x914, PIN_INPUT_PULLDOWN | MUX_MODE7) AM33XX_IOPAD(0x918, PIN_INPUT_PULLDOWN | MUX_MODE7) @@ -308,6 +303,9 @@ */ ti,pmic-shutdown-controller; + interrupt-parent = <&intc>; + interrupts = <7>; /* NNMI */ + regulators { dcdc1_reg: regulator@0 { regulator-name = "vdds_dpr"; @@ -318,7 +316,7 @@ /* VDD_MPU voltage limits 0.95V - 1.26V with +/-4% tolerance */ regulator-name = "vdd_mpu"; regulator-min-microvolt = <925000>; - regulator-max-microvolt = <1325000>; + regulator-max-microvolt = <1351500>; regulator-boot-on; regulator-always-on; }; @@ -359,15 +357,11 @@ phy-mode = "mii"; }; -&cpsw_emac1 { - phy_id = <&davinci_mdio>, <1>; - phy-mode = "mii"; -}; - &mac { pinctrl-names = "default", "sleep"; pinctrl-0 = <&cpsw_default>; pinctrl-1 = <&cpsw_sleep>; + slaves = <1>; status = "okay"; }; @@ -393,3 +387,1791 @@ &sham { status = "okay"; }; + +&rtc { + system-power-controller; +}; + +/* the cape manager */ +/ { + bone_capemgr { + compatible = "ti,bone-capemgr"; + status = "okay"; + + nvmem-cells = <&baseboard_data &cape0_data &cape1_data &cape2_data &cape3_data>; + nvmem-cell-names = "baseboard", "slot0", "slot1", "slot2", "slot3"; + #slots = <4>; + + /* map board revisions to compatible definitions */ + baseboardmaps { + baseboard_beaglebone: board@0 { + board-name = "A335BONE"; + compatible-name = "ti,beaglebone"; + }; + + baseboard_beaglebone_black: board@1 { + board-name = "A335BNLT"; + compatible-name = "ti,beaglebone-black"; + }; + }; + }; + +}; + + +/* the muxes for the dtcon */ +&am33xx_pinmux { + /* P8.3 GPIO1_6 */ + gpio1_6_in: gpio1_6_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x818, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_6_out: gpio1_6_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x818, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.4 */ + gpio1_7_in: gpio1_7_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x81c, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_7_out: gpio1_7_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x81c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.5 */ + gpio1_2_in: gpio1_2_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x808, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + + gpio1_2_out: gpio1_2_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x808, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.6 */ + gpio1_3_in: gpio1_3_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x80c, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_3_out: gpio1_3_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x80c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.7 */ + gpio2_2_in: gpio2_2_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x890, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_2_out: gpio2_2_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x890, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.8 */ + gpio2_3_in: gpio2_3_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x894, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_3_out: gpio2_3_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x894, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.9 */ + gpio2_5_in: gpio2_5_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x89c, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_5_out: gpio2_5_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x89c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.10 */ + gpio2_4_in: gpio2_4_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x898, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_4_out: gpio2_4_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x898, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.11 */ + gpio1_13_in: gpio1_13_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x834, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_13_out: gpio1_13_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x834, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.12 */ + gpio1_12_in: gpio1_12_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x830, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_12_out: gpio1_12_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x830, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.13 */ + gpio0_23_in: gpio0_23_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x824, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_23_out: gpio0_23_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x824, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.14 */ + gpio0_26_in: gpio0_26_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x828, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_26_out: gpio0_26_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x828, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.15 */ + gpio1_15_in: gpio1_15_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x83c, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_15_out: gpio1_15_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x83c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.16 */ + gpio1_14_in: gpio1_14_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x838, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_14_out: gpio1_14_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x838, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.17 */ + gpio0_27_in: gpio0_27_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x82c, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_27_out: gpio0_27_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x82c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.18 */ + gpio2_1_in: gpio2_1_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x88c, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_1_out: gpio2_1_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x88c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.19 */ + gpio0_22_in: gpio0_22_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x820, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_22_out: gpio0_22_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x820, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.20 */ + gpio1_31_in: gpio1_31_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x884, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_31_out: gpio1_31_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x884, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.21 */ + gpio1_30_in: gpio1_30_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x880, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_30_out: gpio1_30_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x880, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.22 */ + gpio1_5_in: gpio1_5_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x814, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_5_out: gpio1_5_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x814, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.23 */ + gpio1_4_in: gpio1_4_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x810, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_4_out: gpio1_4_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x810, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.24 */ + gpio1_1_in: gpio1_1_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x804, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_1_out: gpio1_1_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x804, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.25 */ + gpio1_0_in: gpio1_0_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x800, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_0_out: gpio1_0_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x800, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.26 */ + gpio1_29_in: gpio1_29_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x87c, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_29_out: gpio1_29_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x87c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.27 */ + gpio2_22_in: gpio2_22_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8e0, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_22_out: gpio2_22_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8e0, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.28 */ + gpio2_24_in: gpio2_24_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8e8, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_24_out: gpio2_24_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8e8, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.29 */ + gpio2_23_in: gpio2_23_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8e4, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_23_out: gpio2_23_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8e4, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.30 */ + gpio2_25_in: gpio2_25_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8ec, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_25_out: gpio2_25_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8ec, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.31 */ + uart5_rxd_mux1: uart5_rxd_mux1 { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8d8, PIN_INPUT_PULLUP | MUX_MODE4) + >; + }; + uart5_ctsn: uart5_ctsn { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8d8, PIN_INPUT_PULLUP | MUX_MODE6) + >; + }; + gpio0_10_in: gpio0_10_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8d8, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_10_out: gpio0_10_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8d8, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.32 */ + uart5_rtsn: uart5_rtsn { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8dc, PIN_INPUT_PULLUP | MUX_MODE6) + >; + }; + gpio0_11_in: gpio0_11_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8dc, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_11_out: gpio0_11_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8dc, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.33 */ + gpio0_9_in: gpio0_9_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8d4, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_9_out: gpio0_9_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8d4, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.34 */ + gpio2_17_in: gpio2_17_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8cc, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_17_out: gpio2_17_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8cc, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.35 */ + gpio0_8_in: gpio0_8_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8d0, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_8_out: gpio0_8_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8d0, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.36 */ + gpio2_16_in: gpio2_16_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8c8, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_16_out: gpio2_16_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8c8, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.37 */ + uart5_txd: uart5_txd { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8c0, PIN_OUTPUT_PULLDOWN | MUX_MODE4) + >; + }; + uart2_ctsn: uart2_ctsn { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8c0, PIN_INPUT_PULLUP | MUX_MODE6) + >; + }; + gpio2_14_in: gpio2_14_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8c0, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_14_out: gpio2_14_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8c0, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.38 */ + uart5_rxd: uart5_rxd { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8c4, PIN_OUTPUT_PULLDOWN | MUX_MODE4) + >; + }; + uart2_rtsn: uart2_rtsn { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8c4, PIN_OUTPUT_PULLDOWN | MUX_MODE6) + >; + }; + gpio2_15_in: gpio2_15_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8c4, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_15_out: gpio2_15_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8c4, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.39 */ + gpio2_12_in: gpio2_12_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8b8, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_12_out: gpio2_12_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8b8, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.40 */ + gpio2_13_in: gpio2_13_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8bc, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + + gpio2_13_out: gpio2_13_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8bc, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.41 */ + gpio2_10_in: gpio2_10_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8b0, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + + gpio2_10_out: gpio2_10_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8b0, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.42 */ + gpio2_11_in: gpio2_11_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8b4, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_11_out: gpio2_11_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8b4, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.43 */ + gpio2_8_in: gpio2_8_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8a8, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_8_out: gpio2_8_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8a8, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.44 */ + gpio2_9_in: gpio2_9_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8ac, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_9_out: gpio2_9_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8ac, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.45 */ + gpio2_6_in: gpio2_6_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8a0, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + + gpio2_6_out: gpio2_6_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8a0, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P8.46 */ + gpio2_7_in: gpio2_7_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8a4, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio2_7_out: gpio2_7_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x8a4, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.11 */ + gpio0_30_in: gpio0_30_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x870, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_30_out: gpio0_30_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x870, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.12 */ + gpio1_28_in: gpio1_28_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x878, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_28_out: gpio1_28_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x878, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.13 */ + gpio0_31_in: gpio0_31_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x874, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_31_out: gpio0_31_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x874, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.14 */ + gpio1_18_in: gpio1_18_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x848, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_18_out: gpio1_18_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x848, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.15 */ + gpio1_16_in: gpio1_16_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x840, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_16_out: gpio1_16_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x840, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.16 */ + gpio0_19_in: gpio0_19_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x84c, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_19_out: gpio0_19_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x84c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.17 */ + i2c1_scl: i2c1_scl { + pinctrl-single,pins = < + AM33XX_IOPAD(0x95c, PIN_INPUT_PULLUP | MUX_MODE2) + >; + }; + gpio0_5_in: gpio0_5_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x95c, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_5_out: gpio0_5_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x95c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.18 */ + i2c1_sda: i2c1_sda { + pinctrl-single,pins = < + AM33XX_IOPAD(0x958, PIN_INPUT_PULLUP | MUX_MODE2) + >; + }; + gpio0_4_in: gpio0_4_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x958, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_4_out: gpio0_4_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x958, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.19 */ + uart1_rtsn: uart1_rtsn { + pinctrl-single,pins = < + AM33XX_IOPAD(0x87c, PIN_OUTPUT_PULLDOWN | MUX_MODE0) + >; + }; + gpio0_13_in: gpio0_13_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x97c, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_13_out: gpio0_13_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x97c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.20 */ + uart1_ctsn: uart1_ctsn { + pinctrl-single,pins = < + AM33XX_IOPAD(0x878, PIN_INPUT_PULLUP | MUX_MODE0) + >; + }; + gpio0_12_in: gpio0_12_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x978, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_12_out: gpio0_12_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x978, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.21 */ + uart2_txd: uart2_txd { + pinctrl-single,pins = < + AM33XX_IOPAD(0x954, PIN_OUTPUT_PULLDOWN | MUX_MODE1) + >; + }; + gpio0_3_in: gpio0_3_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x954, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_3_out: gpio0_3_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x954, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.22 */ + uart2_rxd: uart2_rxd { + pinctrl-single,pins = < + AM33XX_IOPAD(0x950, PIN_INPUT_PULLUP | MUX_MODE1) + >; + }; + gpio0_2_in: gpio0_2_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x950, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_2_out: gpio0_2_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x950, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.23 */ + gpio1_17_in: gpio1_17_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x844, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio1_17_out: gpio1_17_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x844, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.24 */ + uart1_txd: uart1_txd { + pinctrl-single = < + AM33XX_IOPAD(0x984, PIN_OUTPUT_PULLDOWN | MUX_MODE0) + >; + }; + i2c1_scl_mux1: i2c1_scl_mux1 { + pinctrl-single,pins = < + AM33XX_IOPAD(0x984, PIN_INPUT_PULLUP | MUX_MODE3) + >; + }; + gpio0_15_in: gpio0_15_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x984, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_15_out: gpio0_15_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x984, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + /* P9.25 */ + gpio3_21_in: gpio3_21_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9ac, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio3_21_out: gpio3_21_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9ac, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.26 */ + uart1_rxd: uart1_rxd { + pinctrl-single = < + AM33XX_IOPAD(0x980, PIN_INPUT_PULLUP | MUX_MODE0) + >; + }; + i2c1_sda_mux1: i2c1_sda_mux1 { + pinctrl-single,pins = < + AM33XX_IOPAD(0x980, PIN_INPUT_PULLUP | MUX_MODE3) + >; + }; + gpio0_14_in: gpio0_14_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x980, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_14_out: gpio0_14_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x980, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.27 */ + gpio3_19_in: gpio3_19_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9a4, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio3_19_out: gpio3_19_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9a4, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.28 */ + gpio3_17_in: gpio3_17_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x99c, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio3_17_out: gpio3_17_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x99c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.29 */ + gpio3_15_in: gpio3_15_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x994, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio3_15_out: gpio3_15_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x994, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.30 */ + gpio3_16_in: gpio3_16_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x998, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio3_16_out: gpio3_16_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x998, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.31 */ + gpio3_14_in: gpio3_14_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x990, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio3_14_out: gpio3_14_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x990, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.40 */ + gpio0_20_in: gpio0_20_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9b4, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_20_out: gpio0_20_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9b4, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.41 */ + gpio3_20_in: gpio3_20_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9a8, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio3_20_out: gpio3_20_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9a8, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + + /* P9.42 GPIO0_7 wired to GPIO3_18 */ + gpio0_7_in: gpio0_7_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x964, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio0_7_out: gpio0_7_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x964, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; + gpio3_18_in: gpio3_18_in { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9a0, PIN_INPUT_PULLUP | MUX_MODE7) + >; + }; + gpio3_18_out: gpio3_18_out { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9a0, PIN_OUTPUT_PULLDOWN | MUX_MODE7) + >; + }; +}; + +/* the device tree based connector */ +/ { + P8_P9 { + compatible = "extcon,dt-con"; + status = "okay"; + + connector { + + #address-cells = <2>; + #size-cells = <0>; + + GPIO1_6: GPIO1_6 { + reg = <8 3>; + pad = "R9"; + modes = "gpmc_ad6", "mmc1_dat6", "", "", "", "", "", "gpio1_6"; + + pinmuxes = <&gpio1_6_in &gpio1_6_out>; + gpio = <&gpio1 6>; + }; + GPIO1_7: GPIO1_7 { + reg = <8 4>; + pad = "T9"; + modes = "gpmc_ad7", "mmc1_dat7", "", "", "", "", "", "gpio1_7"; + pinmuxes = <&gpio1_7_in &gpio1_7_out>; + gpio = <&gpio1 7>; + }; + GPIO1_2: GPIO1_2 { + reg = <8 5>; + pad = "R8"; + modes = "gpmc_ad2", "mmc1_dat2", "", "", "", "", "", "gpio1_2"; + pinmuxes = <&gpio1_2_in &gpio1_2_out>; + gpio = <&gpio1 2>; + }; + GPIO1_3: GPIO1_3 { + reg = <8 6>; + pad = "T8"; + modes = "gpmc_ad3", "mmc1_dat3", "", "", "", "", "", "gpio1_3"; + pinmuxes = <&gpio1_3_in &gpio1_3_out>; + gpio = <&gpio1 3>; + }; + TIMER4: TIMER4 { + reg = <8 7>; + pad = "R7"; + modes = "gpmc_advn_ale", "", "timer4", "", "", "", "", "gpio2_2"; + pinmuxes = <&gpio2_2_in &gpio2_2_out>; + gpio = <&gpio2 2>; + }; + TIMER7: TIMER7 { + reg = <8 8>; + pad = "T7"; + modes = "gpmc_oen_ren", "", "timer7", "", "", "", "", "gpio2_3"; + pinmuxes = <&gpio2_3_in &gpio2_3_out>; + gpio = <&gpio2 3>; + }; + TIMER5: TIMER5 { + reg = <8 9>; + pad = "T6"; + modes = "gpmc_be0n_cle", "", "timer5", "", "", "", "", "gpio2_5"; + pinmuxes = <&gpio2_5_in &gpio2_5_out>; + gpio = <&gpio2 5>; + }; + TIMER6: TIMER6 { + reg = <8 10>; + pad = "U6"; + modes = "gpmc_wen", "", "timer6", "", "", "", "", "gpio2_4"; + pinmuxes = <&gpio2_4_in &gpio2_4_out>; + gpio = <&gpio2 4>; + }; + GPIO1_13: GPIO1_13 { + reg = <8 11>; + pad = "R12"; + modes = "gpmc_ad13", "lcd_data18", "mmc1_dat5", "mmc2_dat1", "eqep2b_in", "", "", "gpio1_13"; + pinmuxes = <&gpio1_13_in &gpio1_13_out>; + gpio = <&gpio1 13>; + }; + GPIO1_12: GPIO1_12 { + reg = <8 12>; + pad = "T12"; + modes = "gpmc_ad12", "lcd_data19", "mmc1_dat4", "mmc2_dat0", "eqep2a_in", "", "", "gpio1_12"; + pinmuxes = <&gpio1_12_in &gpio1_12_out>; + gpio = <&gpio1 12>; + }; + EHRPWM2B: EHRPWM2B { + reg = <8 13>; + pad = "T10"; + modes = "gpmc_ad9", "lcd_data22", "mmc1_dat1", "mmc2_dat5", "ehrpwm2B", "", "", "gpio0_23"; + pinmuxes = <&gpio0_23_in &gpio0_23_out>; + gpio = <&gpio0 23>; + }; + GPIO0_26: GPIO0_26 { + reg = <8 14>; + pad = "T11"; + modes = "gpmc_ad10", "lcd_data21", "mmc1_dat2", "mmc2_dat6", "ehrpwm2_tripzone_in", "", "", "gpio0_26"; + pinmuxes = <&gpio0_26_in &gpio0_26_out>; + gpio = <&gpio0 26>; + }; + GPIO1_15: GPIO1_15 { + reg = <8 15>; + pad = "U13"; + modes = "gpmc_ad15", "lcd_data16", "mmc1_dat7", "mmc2_dat3", "eqep2_strobe", "", "", "gpio1_15"; + pinmuxes = <&gpio1_15_in &gpio1_15_out>; + gpio = <&gpio1 15>; + }; + GPIO1_14: GPIO1_14 { + reg = <8 16>; + pad = "V13"; + modes = "gpmc_ad14", "lcd_data17", "mmc1_dat6", "mmc2_dat2", "eqep2_index", "", "", "gpio1_14"; + pinmuxes = <&gpio1_14_in &gpio1_14_out>; + gpio = <&gpio1 14>; + }; + GPIO0_27: GPIO0_27 { + reg = <8 17>; + pad = "U12"; + modes = "gpmc_ad11", "lcd_data20", "mmc1_dat3", "mmc2_dat7", "ehrpwm0_synco", "", "", "gpio0_27"; + pinmuxes = <&gpio0_27_in &gpio0_27_out>; + gpio = <&gpio0 27>; + }; + GPIO2_1: GPIO2_1 { + reg = <8 18>; + pad = "V12"; + modes = "gpmc_clk_mux0", "lcd_memory_clk", "gpmc_wait1", "mmc2_clk", "", "", "mcasp0_fsr", "gpio2_1"; + pinmuxes = <&gpio2_1_in &gpio2_1_out>; + gpio = <&gpio2 1>; + }; + EHRPWM2A: EHRPWM2A { + reg = <8 19>; + pad = "U10"; + modes = "gpmc_ad8", "lcd_data23", "mmc1_dat0", "mmc2_dat4", "ehrpwm2A", "", "", "gpio0_22"; + pinmuxes = <&gpio0_22_in &gpio0_22_out>; + gpio = <&gpio0 22>; + }; + GPIO1_31: GPIO1_31 { + reg = <8 20>; + pad = "V9"; + modes = "gpmc_csn2", "gpmc_be1n", "mmc1_cmd", "", "", "", "", "gpio1_31"; + pinmuxes = <&gpio1_31_in &gpio1_31_out>; + gpio = <&gpio1 31>; + }; + GPIO1_30: GPIO1_30 { + reg = <8 21>; + pad = "U9"; + modes = "gpmc_csn1", "gpmc_clk", "mmc1_clk", "", "", "", "", "gpio1_30"; + pinmuxes = <&gpio1_30_in &gpio1_30_out>; + gpio = <&gpio1 30>; + }; + GPIO1_5: GPIO1_5 { + reg = <8 22>; + pad = "V8"; + modes = "gpmc_ad5", "mmc1_dat5", "", "", "", "", "", "gpio1_5"; + pinmuxes = <&gpio1_5_in &gpio1_5_out>; + gpio = <&gpio1 5>; + }; + GPIO1_4: GPIO1_4 { + reg = <8 23>; + pad = "U8"; + modes = "gpmc_ad4", "mmc1_dat4", "", "", "", "", "", "gpio1_4"; + pinmuxes = <&gpio1_4_in &gpio1_4_out>; + gpio = <&gpio1 4>; + }; + GPIO1_1: GPIO1_1 { + reg = <8 24>; + pad = "V7"; + modes = "gpmc_ad1", "mmc1_dat1", "", "", "", "", "", "gpio1_1"; + pinmuxes = <&gpio1_1_in &gpio1_1_out>; + gpio = <&gpio1 1>; + }; + GPIO1_0: GPIO1_0 { + reg = <8 25>; + pad = "U7"; + modes = "gpmc_ad0", "mmc1_dat0", "", "", "", "", "", "gpio1_0"; + pinmuxes = <&gpio1_0_in &gpio1_0_out>; + gpio = <&gpio1 0>; + }; + GPIO1_29: GPIO1_29 { + reg = <8 26>; + pad = "V6"; + modes = "gpmc_csn0", "", "", "", "", "", "", "gpio1_29"; + pinmuxes = <&gpio1_29_in &gpio1_29_out>; + gpio = <&gpio1 29>; + }; + GPIO2_22: GPIO2_22 { + reg = <8 27>; + pad = "U5"; + modes = "lcd_vsync", "gpmc_a8", "", "", "", "", "", "gpio2_22"; + pinmuxes = <&gpio2_22_in &gpio2_22_out>; + gpio = <&gpio2 22>; + }; + GPIO2_24: GPIO2_24 { + reg = <8 28>; + pad = "V5"; + modes = "lcd_pclk", "gpmc_a10", "", "", "", "", "", "gpio2_24"; + pinmuxes = <&gpio2_24_in &gpio2_24_out>; + gpio = <&gpio2 24>; + }; + GPIO2_23: GPIO2_23 { + reg = <8 29>; + pad = "R5"; + modes = "lcd_hsync", "gpmc_a9", "", "", "", "", "", "gpio2_23"; + pinmuxes = <&gpio2_23_in &gpio2_23_out>; + gpio = <&gpio2 23>; + }; + GPIO2_25: GPIO2_25 { + reg = <8 30>; + pad = "R6"; + modes = "lcd_ac_bias_en", "gpmc_a11", "", "", "", "", "", "gpio2_25"; + pinmuxes = <&gpio2_25_in &gpio2_25_out>; + gpio = <&gpio2 25>; + }; + UART5_CTSN: UART5_CTSN { + reg = <8 31>; + pad = "V4"; + modes = "lcd_data14", "gpmc_a18", "eqep1_index", "mcasp0_axr1", "uart5_rxd_mux1", "", "uart5_ctsn", "gpio0_10"; + pinmuxes = <&uart5_rxd_mux1 &uart5_ctsn &gpio0_10_in &gpio0_10_out>; + gpio = <&gpio0 10>; + }; + UART5_RTSN: UART5_RTSN { + reg = <8 32>; + pad = "T5"; + modes = "lcd_data15", "gpmc_a19", "eqep1_strobe", "mcasp0_ahclkx", "mcasp0_axr3", "", "uart5_rtsn", "gpio0_11"; + pinmuxes = <&uart5_rtsn &gpio0_11_in &gpio0_11_out>; + gpio = <&gpio0 11>; + }; + UART4_RTSN: UART4_RTSN { + reg = <8 33>; + pad = "V3"; + modes = "lcd_data13", "gpmc_a17", "eqep1b_in", "mcasp0_fsr", "mcasp0_axr3", "", "uart4_rtsn", "gpio0_9"; + pinmuxes = <&gpio0_9_in &gpio0_9_out>; + gpio = <&gpio0 9>; + }; + UART3_RTSN: UART3_RTSN { + reg = <8 34>; + pad = "U4"; + modes = "lcd_data11", "gpmc_a15", "ehrpwm1B", "mcasp0_ahclkr", "mcasp0_axr2", "", "uart3_rtsn", "gpio2_17"; + pinmuxes = <&gpio2_17_in &gpio2_17_out>; + gpio = <&gpio2 17>; + }; + UART4_CTSN: UART4_CTSN { + reg = <8 35>; + pad = "V2"; + modes = "lcd_data12", "gpmc_a16", "eqep1a_in", "mcasp0_aclkr", "mcasp0_axr2", "", "uart4_ctsn", "gpio0_8"; + pinmuxes = <&gpio0_8_in &gpio0_8_out>; + gpio = <&gpio0 8>; + }; + UART3_CTSN: UART3_CTSN { + reg = <8 36>; + pad = "U3"; + modes = "lcd_data10", "gpmc_a14", "ehrpwm1A", "mcasp0_axr0", "", "", "uart3_ctsn", "gpio2_16"; + pinmuxes = <&gpio2_16_in &gpio2_16_out>; + gpio = <&gpio2 16>; + }; + UART5_TXD: UART5_TXD { + reg = <8 37>; + pad = "U1"; + modes = "lcd_data8", "gpmc_a12", "ehrpwm1_tripzone_in", "mcasp0_aclkx", "uart5_txd", "", "uart2_ctsn", "gpio2_14"; + pinmuxes = <&uart5_txd &uart2_ctsn &gpio2_14_in &gpio2_14_out>; + gpio = <&gpio2 14>; + }; + UART5_RXD: UART5_RXD { + reg = <8 38>; + pad = "U2"; + modes = "lcd_data9", "gpmc_a13", "ehrpwm0_synco", "mcasp0_fsx", "uart5_rxd", "", "uart2_rtsn", "gpio2_15"; + pinmuxes = <&uart5_rxd &uart2_rtsn &gpio2_15_in &gpio2_15_out>; + gpio = <&gpio2 15>; + }; + GPIO2_12: GPIO2_12 { + reg = <8 39>; + pad = "T3"; + modes = "lcd_data6", "gpmc_a6", "", "eqep2_index", "", "", "", "gpio2_12"; + pinmuxes = <&gpio2_12_in &gpio2_12_out>; + gpio = <&gpio2 12>; + }; + GPIO2_13: GPIO2_13 { + reg = <8 40>; + pad = "T4"; + modes = "lcd_data7", "gpmc_a7", "", "eqep2_strobe", "pr1_edio_data_out7", "", "", "gpio2_13"; + pinmuxes = <&gpio2_13_in &gpio2_13_out>; + gpio = <&gpio2 13>; + }; + GPIO2_10: GPIO2_10 { + reg = <8 41>; + pad = "T1"; + modes = "lcd_data4", "gpmc_a4", "", "eqep2a_in", "", "", "", "gpio2_10"; + pinmuxes = <&gpio2_10_in &gpio2_10_out>; + gpio = <&gpio2 10>; + }; + GPIO2_11: GPIO2_11 { + reg = <8 42>; + pad = "T2"; + modes = "lcd_data5", "gpmc_a5", "", "eqep2b_in", "", "", "", "gpio2_11"; + pinmuxes = <&gpio2_11_in &gpio2_11_out>; + gpio = <&gpio2 11>; + }; + GPIO2_8: GPIO2_8 { + reg = <8 43>; + pad = "R3"; + modes = "lcd_data2", "gpmc_a2", "", "ehrpwm2_tripzone_in", "", "", "", "gpio2_8"; + pinmuxes = <&gpio2_8_in &gpio2_8_out>; + gpio = <&gpio2 8>; + }; + GPIO2_9: GPIO2_9 { + reg = <8 44>; + pad = "R4"; + modes = "lcd_data3", "gpmc_a3", "", "ehrpwm0_synco", "", "", "", "gpio2_9"; + pinmuxes = <&gpio2_9_in &gpio2_9_out>; + gpio = <&gpio2 9>; + }; + GPIO2_6: GPIO2_6 { + reg = <8 45>; + pad = "R1"; + modes = "lcd_data0", "gpmc_a0", "", "ehrpwm2A", "", "", "", "gpio2_6"; + pinmuxes = <&gpio2_6_in &gpio2_6_out>; + gpio = <&gpio2 6>; + }; + GPIO2_7: GPIO2_7 { + reg = <8 46>; + pad = "R2"; + modes = "lcd_data1", "gpmc_a1", "", "ehrpwm2B", "", "", "", "gpio2_7"; + pinmuxes = <&gpio2_7_in &gpio2_7_out>; + gpio = <&gpio2 7>; + }; + + + /* P9 */ + + GND { + reg = <9 1>, <9 2>, <9 43>, <9 44>, <9 45>, <9 46>; + }; + DC_3_3V { + reg = <9 3>, <9 4>; + }; + VDD_5V { + reg = <9 5>, <9 6>; + }; + SYS_5V { + reg = <9 7>, <9 8>; + }; + PWR_BUT { + reg = <9 9>; + }; + SYS_RESETn: SYS_RESETn { + reg = <9 10>; + pad = "A10"; + modes = "RESET_OUT", "", "", "", "", "", "", ""; + }; + UART4_RXD: UART4_RXD { + reg = <9 11>; + pad = "T17"; + modes = "gpmc_wait0", "mii2_crs", "gpmc_csn4", "rmii2_crs_dv", "mmc1_sdcd", "", "uart4_rxd_mux2", "gpio0_30"; + pinmuxes = <&gpio0_30_in &gpio0_30_out>; + gpio = <&gpio0 30>; + }; + GPIO1_28: GPIO1_28 { + reg = <9 12>; + pad = "U18"; + modes = "gpmc_be1n", "mii2_col", "gpmc_csn6", "mmc2_dat3", "gpmc_dir", "", "mcasp0_aclkr_mux3", "gpio1_28"; + pinmuxes = <&gpio1_28_in &gpio1_28_out>; + gpio = <&gpio1 28>; + }; + UART4_TXD: UART4_TXD { + reg = <9 13>; + pad = "U17"; + modes = "gpmc_wpn", "mii2_rxerr", "gpmc_csn5", "rmii2_rxerr", "mmc2_sdcd", "", "uart4_txd_mux2", "gpio0_31"; + pinmuxes = <&gpio0_31_in &gpio0_31_out>; + gpio = <&gpio0 31>; + }; + EHRPWM1A: EHRPWM1A { + reg = <9 14>; + pad = "U14"; + modes = "gpmc_a2", "mii2_txd3", "rgmii2_td3", "mmc2_dat1", "gpmc_a18", "", "ehrpwm1A_mux1", "gpio1_18"; + pinmuxes = <&gpio1_18_in &gpio1_18_out>; + gpio = <&gpio1 18>; + }; + GPIO1_16: GPIO1_16 { + reg = <9 15>; + pad = "R13"; + modes = "gpmc_a0", "gmii2_txen", "rmii2_tctl", "mii2_txen", "gpmc_a16", "", "ehrpwm1_tripzone_input", "gpio1_16"; + pinmuxes = <&gpio1_16_in &gpio1_16_out>; + gpio = <&gpio1 16>; + }; + EHRPWM1B: EHRPWM1B { + reg = <9 16>; + pad = "T14"; + modes = "gpmc_a3", "mii2_txd2", "rgmii2_td2", "mmc2_dat2", "gpmc_a19", "", "ehrpwm1B_mux1", "gpio1_19"; + pinmuxes = <&gpio0_19_in &gpio0_19_out>; + gpio = <&gpio0 19>; + }; + I2C1_SCL: I2C1_SCL { + reg = <9 17>; + pad = "A16"; + modes = "spi0_cs0", "mmc2_sdwp", "i2c1_scl", "ehrpwm0_synci", "", "", "", "gpio0_5"; + pinmuxes = <&i2c1_scl &gpio0_5_in &gpio0_5_out>; + gpio = <&gpio0 5>; + }; + I2C1_SDA: I2C1_SDA { + reg = <9 18>; + pad = "B16"; + modes = "spi0_d1", "mmc1_sdwp", "i2c1_sda", "ehrpwm0_tripzone", "", "", "", "gpio0_4"; + pinmuxes = <&i2c1_sda &gpio0_4_in &gpio0_4_out>; + gpio = <&gpio0 4>; + }; + I2C2_SCL: I2C2_SCL { + reg = <9 19>; + pad = "D17"; + modes = "uart1_rtsn", "timer5", "dcan0_rx", "i2c2_scl", "spi1_cs1", "", "", "gpio0_13"; + pinmuxes = <&uart1_rtsn &gpio0_13_in &gpio0_13_out>; + gpio = <&gpio0 13>; + }; + I2C2_SDA: I2C2_SDA { + reg = <9 20>; + pad = "D18"; + modes = "uart1_ctsn", "timer6", "dcan0_tx", "i2c2_sda", "spi1_cs0", "", "", "gpio0_12"; + pinmuxes = <&uart1_ctsn &gpio0_12_in &gpio0_12_out>; + gpio = <&gpio0 12>; + }; + UART2_TXD: UART2_TXD { + reg = <9 21>; + pad = "B17"; + modes = "spi0_d0", "uart2_txd", "i2c2_scl", "ehrpwm0B", "", "", "emu3_mux1", "gpio0_3"; + pinmuxes = <&uart2_txd &gpio0_3_in &gpio0_3_out>; + gpio = <&gpio0 3>; + }; + UART2_RXD: UART2_RXD { + reg = <9 22>; + pad = "A17"; + modes = "spi0_sclk", "uart2_rxd", "i2c2_sda", "ehrpwm0A", "", "", "emu2_mux1", "gpio0_2"; + pinmuxes = <&uart2_rxd &gpio0_2_in &gpio0_2_out>; + gpio = <&gpio0 2>; + }; + GPIO1_17: GPIO1_17 { + reg = <9 23>; + pad = "V14"; + modes = "gpmc_a1", "gmii2_rxdv", "rgmii2_rxdv", "mmc2_dat0", "gpmc_a17", "", "ehrpwm0_synco", "gpio1_17"; + pinmuxes = <&gpio1_17_in &gpio1_17_out>; + gpio = <&gpio1 17>; + }; + UART1_TXD: UART1_TXD { + reg = <9 24>; + pad = "D15"; + modes = "uart1_txd", "mmc2_sdwp", "dcan1_rx", "i2c1_scl_mux1", "", "", "", "gpio0_15"; + pinmuxes = <&uart1_txd &i2c1_scl_mux1 &gpio0_15_in &gpio0_15_out>; + gpio = <&gpio0 15>; + }; + GPIO3_21: GPIO3_21 { + reg = <9 25>; + pad = "A14"; + modes = "mcasp0_ahclkx", "eqep0_strobe", "mcasp0_axr3", "mcasp1_axr1", "emu4_mux2", "", "", "gpio3_21"; + pinmuxes = <&gpio3_21_in &gpio3_21_out>; + gpio = <&gpio3 21>; + }; + UART1_RXD: UART1_RXD { + reg = <9 26>; + pad = "D16"; + modes = "uart1_rxd", "mmc1_sdwp", "dcan1_tx", "i2c1_sda_mux1", "", "", "", "gpio0_14"; + pinmuxes = <&uart1_rxd &i2c1_sda_mux1 &gpio0_14_in &gpio0_14_out>; + gpio = <&gpio0 14>; + }; + GPIO3_19: GPIO3_19 { + reg = <9 27>; + pad = "C13"; + modes = "mcasp0_fsr", "eqep0b_in", "mcasp0_axr3", "mcasp1_fsx", "emu2_mux2", "", "", "gpio3_19"; + pinmuxes = <&gpio3_19_in &gpio3_19_out>; + gpio = <&gpio3 19>; + }; + SPI1_CS0: SPI1_CS0 { + reg = <9 28>; + pad = "C12"; + modes = "mcasp0_ahclkr", "ehrpwm0_synci", "mcasp0_axr2", "spi1_cs0", "ecap2_in_pwm2_out", "", "", "gpio3_17"; + pinmuxes = <&gpio3_17_in &gpio3_17_out>; + gpio = <&gpio3 17>; + }; + SPI1_D0: SPI1_D0 { + reg = <9 29>; + pad = "B13"; + modes = "mcasp0_fsx", "ehrpwm0b", "", "spi1_d0", "mmc1_sdcd_mux1", "", "", "gpio3_15"; + pinmuxes = <&gpio3_15_in &gpio3_15_out>; + gpio = <&gpio3 15>; + }; + SPI1_D1: SPI1_D1 { + reg = <9 30>; + pad = "D12"; + modes = "mcasp0_axr0", "ehrpwm0_tripzone", "", "spi1_d1", "mmc2_sdcd_mux1", "", "", "gpio3_16"; + pinmuxes = <&gpio3_16_in &gpio3_16_out>; + gpio = <&gpio3 16>; + }; + SPI1_SCLK: SPI1_SCLK { + reg = <9 31>; + pad = "A13"; + modes = "mcasp0_aclkx", "ehrpwm0a", "", "spi1_sclk", "mmc0_sdcd_mux1", "", "", "gpio3_14"; + pinmuxes = <&gpio3_14_in &gpio3_14_out>; + gpio = <&gpio3 14>; + }; + VADC: VADC { + reg = <9 32>; + }; + AIN4: AIN4 { + reg = <9 33>; + pad = "C8"; + }; + AGND: AGND { + reg = <9 34>; + }; + AIN6: AIN6 { + reg = <9 35>; + pad = "A8"; + }; + AIN5: AIN5 { + reg = <9 36>; + pad = "B8"; + }; + AIN2: AIN2 { + reg = <9 37>; + pad = "B7"; + }; + AIN3: AIN3 { + reg = <9 38>; + pad = "A7"; + }; + AIN0: AIN0 { + reg = <9 39>; + pad = "B6"; + }; + AIN1: AIN1 { + reg = <9 40>; + pad = "C7"; + }; + CLKOUT2: CLKOUT2 { + /* wired with GPIO3_20 */ + reg = <9 41>; + pad = "D14"; + modes = "xdma_event_intr1", "", "tclkin", "clkout2", "timer7_mux1", "", "emu3_mux0", "gpio0_20"; + pinmuxes = <&gpio0_20_in &gpio0_20_out>; + gpio = <&gpio0 20>; + }; + GPIO3_20: GPIO3_20 { + /* wired with CLKOUT2 */ + reg = <9 41>; + pad = "D13"; + modes = "mcasp0_axr1", "eqep0_index", "", "mcasp1_axr0", "emu3", "", "", "gpio3_20"; + pinmuxes = <&gpio3_20_in &gpio3_20_out>; + gpio = <&gpio3 20>; + }; + GPIO0_7: GPIO0_7 { + /* wired with GPIO3_18 */ + reg = <9 42>; + pad = "C18"; + modes = "ecap0_in_pwm0_out", "uart3_txd", "spi1_cs1", "pr1_ecap0_ecap_capin_apwm_o", "spi1_sclk", "mmc0_sdwp", "xdma_event_intr2", "gpio0_7"; + pinmuxes = <&gpio0_7_in &gpio0_7_out>; + gpio = <&gpio0 7>; + }; + GPIO3_18: GPIO3_18 { + /* wired with GPIO0_7 */ + reg = <9 42>; + pad = "B12"; + modes = "mcasp0_aclkr", "eqep0a_in", "mcaspo_axr2", "mcasp1_aclkx", "", "", "", "gpio3_18"; + pinmuxes = <&gpio3_18_in &gpio3_18_out>; + gpio = <&gpio3 18>; + }; + }; + + functions { + + gpio { + /* base of allocation - this is global for the connector */ + gpio-base = <256>; + }; + + /* connector uart0 is uart2 of the bone */ + uart { + params { + + #param-cells = <2>; + generate-pinctrl; + + txd { + required; + connector-pin; + }; + + rxd { + required; + connector-pin; + }; + + cts { + optional; + connector-pin; + gpio-property = "cts-gpios"; + gpio-args = ; + }; + + rts { + optional; + connector-pin; + gpio-property = "rts-gpios"; + gpio-args = ; + }; + + dtr { + optional; + connector-pin; + gpio-property = "dtr-gpios"; + gpio-args = ; + }; + + dsr { + optional; + connector-pin; + gpio-property = "dsr-gpios"; + gpio-args = ; + }; + + dcd { + optional; + connector-pin; + gpio-property = "dcd-gpios"; + gpio-args = ; + }; + + rng { + optional; + connector-pin; + gpio-property = "rnd-gpios"; + gpio-args = ; + }; + }; + + uart1 { + device = <&uart1>; + mux@0 { + txd = <&UART1_TXD &uart1_txd>; + rxd = <&UART1_RXD &uart1_rxd>; + cts = <&I2C2_SDA &uart1_ctsn>; + rts = <&I2C2_SCL &uart1_rtsn>; + }; + }; + uart2 { + device = <&uart2>; + mux@0 { + txd = <&UART2_TXD &uart2_txd>; + rxd = <&UART2_RXD &uart2_rxd>; + cts = <&UART5_TXD &uart2_ctsn>; + rts = <&UART5_RXD &uart2_rtsn>; + }; + }; + uart5 { + device = <&uart5>; + mux@0 { + txd = <&UART5_TXD &uart5_txd>; + rxd = <&UART5_RXD &uart5_rxd>, + <&UART5_CTSN &uart5_rxd_mux1>; /* muxed */ + cts = <&UART5_CTSN &uart5_ctsn>; + rts = <&UART5_RTSN &uart5_rtsn>; + }; + }; + }; + + i2c { + params { + + #param-cells = <2>; + copy-subdevices; + generate-pinctrl; + + scl { + required; + connector-pin; + }; + + sda { + required; + connector-pin; + }; + + clock-frequency { + copy; + }; + + }; + + i2c1 { + device = <&i2c1>; + + mux@0 { + scl = <&I2C1_SCL &i2c1_scl>, + <&UART1_TXD &i2c1_scl_mux1>; + sda = <&I2C1_SDA &i2c1_sda>, + <&UART1_RXD &i2c1_sda_mux1>; + }; + }; + + i2c2 { + device = <&i2c2>; + + mux@0 { + /* I2C2 is preconfigured and available */ + scl = <&I2C2_SCL 0>; + sda = <&I2C2_SDA 0>; + pre-enabled; + }; + }; + }; + }; + + plugged { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + +#if 0 + BB_BONE_UART { + compatible = "dtcon-uart"; + status = "okay"; + txd = <9 21>; /* at P9.21 */ + rxd = <9 22>; /* at P9.22 */ + }; +#endif + +#if 1 + BB_RELAY_4PORT { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + gpio_relay_4port: gpio_relay_4port@0 { + compatible = "dtcon-gpio"; + status = "okay"; + + gpio-controller; + #gpio-cells = <2>; + + pin-list = <9 15>, /* #0 P9.15 */ + <9 23>, /* #1 P9.23 */ + <9 12>, /* #2 P9.12 */ + <9 27>, /* #3 P9.27 */ + <9 28>; /* #4 P9.28 */ + }; + + leds@0 { + compatible = "gpio-leds"; + status = "okay"; + + jp@1 { + label = "relay-jp1"; + gpios = <&gpio_relay_4port 0 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + + jp@2 { + label = "relay-jp2"; + gpios = <&gpio_relay_4port 1 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + + jp@3 { + label = "relay-jp3"; + gpios = <&gpio_relay_4port 2 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + + jp@4 { + label = "relay-jp4"; + gpios = <&gpio_relay_4port 3 GPIO_ACTIVE_HIGH>; + default-state = "keep"; + }; + }; + }; +#endif + +#if 0 + BB_BONE_UART_RTSCTS { + compatible = "dtcon-uart"; + status = "okay"; + + txd = <9 21>; /* at P9.21 */ + rxd = <9 22>; /* at P9.22 */ + rts = <8 38>; /* at P8.38 */ + cts = <8 37>; /* at P8.37 */ + dcd = <8 39>; + }; +#endif + +#if 0 + BB_RTC { + compatible = "dtcon-i2c"; + status = "okay"; + + scl = <9 17>; + sda = <9 18>; + + clock-frequency = <100000>; + + #address-cells = <1>; + #size-cells = <0>; + + /* MCP79410 RTC module */ + rtc@68 { + compatible = "maxim,ds1338"; + status = "okay"; + reg = <0x68>; + }; + }; +#endif + +#if 0 + BB_BONE_WTHR { + compatible = "dtcon-i2c"; + status = "okay"; + + scl = <9 19>; + sda = <9 20>; + + #address-cells = <1>; + #size-cells = <0>; + + /* Ambient light sensor */ + tsl2550@39 { + compatible = "tsl,tsl2550"; + reg = <0x39>; + status = "okay"; + }; + + /* Humidity Sensor */ + htu21@40 { + compatible = "meas-spec,htu21"; + reg = <0x40>; + status = "okay"; + }; + + /* Barometric pressure sensor */ + bmp085@77 { + compatible = "bosch,bmp085"; + reg = <0x77>; + status = "okay"; + }; + }; +#endif + }; + }; +}; diff --git b/arch/arm/boot/dts/am335x-bone-emmc-in-reset.dtsi b/arch/arm/boot/dts/am335x-bone-emmc-in-reset.dtsi new file mode 100644 index 0000000..7d8f673 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-emmc-in-reset.dtsi @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +/* standard */ + +&gpio1 { + emmc_rst { + gpio-hog; + gpios = <20 0>; + output-high; + line-name = "EMMC ResetN"; + }; +}; diff --git b/arch/arm/boot/dts/am335x-bone-jtag.dtsi b/arch/arm/boot/dts/am335x-bone-jtag.dtsi new file mode 100644 index 0000000..a92db8e --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-jtag.dtsi @@ -0,0 +1,20 @@ +/* + * Device Tree Source for bone jtag + * + * Copyright (C) 2015 Robert Nelson + * + * 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. + */ + +&am33xx_pinmux { + pinctrl-names = "default"; + pinctrl-0 = <&clkout2_pin>; + + clkout2_pin: pinmux_clkout2_pin { + pinctrl-single,pins = < + 0x1b4 (PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr1.clkout2 */ + >; + }; +}; diff --git b/arch/arm/boot/dts/am335x-bone-pinmux-can0.dtsi b/arch/arm/boot/dts/am335x-bone-pinmux-can0.dtsi new file mode 100644 index 0000000..0961216 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-pinmux-can0.dtsi @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 "am335x-peripheral-can0.dtsi" + +/* cape universal */ + +/* + *&ocp { + * P9_19_pinmux { + * mode = "can"; + * }; + * P9_20_pinmux { + * mode = "can"; + * }; + *}; + * + *&dcan0 { + * pinctrl-0 = <>; + *}; + * + */ + +/* standard */ + +&am33xx_pinmux { + dcan0_pins: pinmux_dcan0_pins { + pinctrl-single,pins = < + /* P9_20: uart1_ctsn.d_can0_tx */ + BONE_P9_20 (PIN_OUTPUT_PULLUP | MUX_MODE2) + /* P9_19: uart1_rtsn.d_can0_rx */ + BONE_P9_19 (PIN_INPUT_PULLUP | MUX_MODE2) + >; + }; +}; + +&dcan0 { + pinctrl-0 = <&dcan0_pins>; +}; diff --git b/arch/arm/boot/dts/am335x-bone-pinmux-can1.dtsi b/arch/arm/boot/dts/am335x-bone-pinmux-can1.dtsi new file mode 100644 index 0000000..9e26413 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-pinmux-can1.dtsi @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 "am335x-peripheral-can1.dtsi" + +/* cape universal */ + +/* + *&ocp { + * P9_24_pinmux { + * mode = "can"; + * }; + * P9_26_pinmux { + * mode = "can"; + * }; + *}; + * + *&dcan1 { + * pinctrl-0 = <>; + *}; + * + */ + +/* standard */ + +&am33xx_pinmux { + dcan1_pins: pinmux_dcan1_pins { + pinctrl-single,pins = < + /* P9_26: uart1_rxd.d_can1_tx */ + BONE_P9_26 (PIN_OUTPUT_PULLUP | MUX_MODE2) + /* P9_24: uart1_txd.d_can1_rx */ + BONE_P9_24 (PIN_INPUT_PULLUP | MUX_MODE2) + >; + }; +}; + +&dcan1 { + pinctrl-0 = <&dcan1_pins>; +}; diff --git b/arch/arm/boot/dts/am335x-bone-pinmux-emmc.dtsi b/arch/arm/boot/dts/am335x-bone-pinmux-emmc.dtsi new file mode 100644 index 0000000..22cf462 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-pinmux-emmc.dtsi @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +/* Testing */ +/* lsblk */ + +#include +#include "am335x-peripheral-emmc.dtsi" + +/* cape universal */ + +/* + *&ocp { + * P8_21_pinmux { + * state = "disabled"; + * }; + * P8_20_pinmux { + * state = "disabled"; + * }; + * P8_25_pinmux { + * state = "disabled"; + * }; + * P8_24_pinmux { + * state = "disabled"; + * }; + * P8_05_pinmux { + * state = "disabled"; + * }; + * P8_06_pinmux { + * state = "disabled"; + * }; + * P8_23_pinmux { + * state = "disabled"; + * }; + * P8_22_pinmux { + * state = "disabled"; + * }; + * P8_03_pinmux { + * state = "disabled"; + * }; + * P8_04_pinmux { + * state = "disabled"; + * }; + *}; + * + *&mmc2 { + * pinctrl-0 = <>; + *}; + * + */ + +/* standard */ + +&am33xx_pinmux { + emmc_pins: pinmux_emmc_pins { + pinctrl-single,pins = < + /* P8_21: gpmc_csn1.mmc1_clk */ + BONE_P8_21 (PIN_INPUT_PULLUP | MUX_MODE2) + /* P8_20: gpmc_csn2.mmc1_cmd */ + BONE_P8_20 (PIN_INPUT_PULLUP | MUX_MODE2) + /* P8_25: gpmc_ad0.mmc1_dat0 */ + BONE_P8_25 (PIN_INPUT_PULLUP | MUX_MODE1) + /* P8_24: gpmc_ad1.mmc1_dat1 */ + BONE_P8_24 (PIN_INPUT_PULLUP | MUX_MODE1) + /* P8_05: gpmc_ad2.mmc1_dat2 */ + BONE_P8_05 (PIN_INPUT_PULLUP | MUX_MODE1) + /* P8_06: gpmc_ad3.mmc1_dat3 */ + BONE_P8_06 (PIN_INPUT_PULLUP | MUX_MODE1) + /* P8_23: gpmc_ad4.mmc1_dat4 */ + BONE_P8_23 (PIN_INPUT_PULLUP | MUX_MODE1) + /* P8_22: gpmc_ad5.mmc1_dat5 */ + BONE_P8_22 (PIN_INPUT_PULLUP | MUX_MODE1) + /* P8_03: gpmc_ad6.mmc1_dat6 */ + BONE_P8_03 (PIN_INPUT_PULLUP | MUX_MODE1) + /* P8_04: gpmc_ad7.mmc1_dat7 */ + BONE_P8_04 (PIN_INPUT_PULLUP | MUX_MODE1) + >; + }; +}; + +&mmc2 { + pinctrl-0 = <&emmc_pins>; +}; diff --git b/arch/arm/boot/dts/am335x-bone-pinmux-i2c2.dtsi b/arch/arm/boot/dts/am335x-bone-pinmux-i2c2.dtsi new file mode 100644 index 0000000..abf3b57 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-pinmux-i2c2.dtsi @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 "am335x-peripheral-i2c2.dtsi" + +/* cape universal */ + +/* + *&ocp { + * P9_19_pinmux { + * mode = "i2c"; + * }; + * P9_20_pinmux { + * mode = "i2c"; + * }; + *}; + * + *&dcan0 { + * pinctrl-0 = <>; + *}; + * + */ + +/* standard */ + +&am33xx_pinmux { + i2c2_pins: pinmux_i2c2_pins { + pinctrl-single,pins = < + /* P9_20: uart1_ctsn.i2c2_sda */ + BONE_P9_20 (SLEWCTRL_SLOW | PIN_INPUT_PULLUP | MUX_MODE3) + /* P9_19: uart1_rtsn.i2c2_scl */ + BONE_P9_19 (SLEWCTRL_SLOW | PIN_INPUT_PULLUP | MUX_MODE3) + >; + }; +}; + +&i2c2 { + pinctrl-0 = <&i2c2_pins>; +}; diff --git b/arch/arm/boot/dts/am335x-bone-pinmux-nxp-hdmi.dtsi b/arch/arm/boot/dts/am335x-bone-pinmux-nxp-hdmi.dtsi new file mode 100644 index 0000000..5205fa0 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-pinmux-nxp-hdmi.dtsi @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 "am335x-peripheral-nxp-hdmi.dtsi" + +/* cape universal */ + +/* + *&ocp { + * P8_27_pinmux { + * state = "disabled"; + * }; + * P8_28_pinmux { + * state = "disabled"; + * }; + * P8_29_pinmux { + * state = "disabled"; + * }; + * P8_30_pinmux { + * state = "disabled"; + * }; + * P8_31_pinmux { + * state = "disabled"; + * }; + * P8_32_pinmux { + * state = "disabled"; + * }; + * P8_33_pinmux { + * state = "disabled"; + * }; + * P8_34_pinmux { + * state = "disabled"; + * }; + * P8_35_pinmux { + * state = "disabled"; + * }; + * P8_36_pinmux { + * state = "disabled"; + * }; + * P8_37_pinmux { + * state = "disabled"; + * }; + * P8_38_pinmux { + * state = "disabled"; + * }; + * P8_39_pinmux { + * state = "disabled"; + * }; + * P8_40_pinmux { + * state = "disabled"; + * }; + * P8_41_pinmux { + * state = "disabled"; + * }; + * P8_42_pinmux { + * state = "disabled"; + * }; + * P8_43_pinmux { + * state = "disabled"; + * }; + * P8_44_pinmux { + * state = "disabled"; + * }; + * P8_45_pinmux { + * state = "disabled"; + * }; + * P8_46_pinmux { + * state = "disabled"; + * }; + *}; + */ + +/* standard */ + +&am33xx_pinmux { + nxp_hdmi_pins: pinmux_nxp_hdmi_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr0 */ + AM33XX_IOPAD(0x8a0, PIN_OUTPUT | MUX_MODE0) /* lcd_data0.lcd_data0 */ + AM33XX_IOPAD(0x8a4, PIN_OUTPUT | MUX_MODE0) /* lcd_data1.lcd_data1 */ + AM33XX_IOPAD(0x8a8, PIN_OUTPUT | MUX_MODE0) /* lcd_data2.lcd_data2 */ + AM33XX_IOPAD(0x8ac, PIN_OUTPUT | MUX_MODE0) /* lcd_data3.lcd_data3 */ + AM33XX_IOPAD(0x8b0, PIN_OUTPUT | MUX_MODE0) /* lcd_data4.lcd_data4 */ + AM33XX_IOPAD(0x8b4, PIN_OUTPUT | MUX_MODE0) /* lcd_data5.lcd_data5 */ + AM33XX_IOPAD(0x8b8, PIN_OUTPUT | MUX_MODE0) /* lcd_data6.lcd_data6 */ + AM33XX_IOPAD(0x8bc, PIN_OUTPUT | MUX_MODE0) /* lcd_data7.lcd_data7 */ + AM33XX_IOPAD(0x8c0, PIN_OUTPUT | MUX_MODE0) /* lcd_data8.lcd_data8 */ + AM33XX_IOPAD(0x8c4, PIN_OUTPUT | MUX_MODE0) /* lcd_data9.lcd_data9 */ + AM33XX_IOPAD(0x8c8, PIN_OUTPUT | MUX_MODE0) /* lcd_data10.lcd_data10 */ + AM33XX_IOPAD(0x8cc, PIN_OUTPUT | MUX_MODE0) /* lcd_data11.lcd_data11 */ + AM33XX_IOPAD(0x8d0, PIN_OUTPUT | MUX_MODE0) /* lcd_data12.lcd_data12 */ + AM33XX_IOPAD(0x8d4, PIN_OUTPUT | MUX_MODE0) /* lcd_data13.lcd_data13 */ + AM33XX_IOPAD(0x8d8, PIN_OUTPUT | MUX_MODE0) /* lcd_data14.lcd_data14 */ + AM33XX_IOPAD(0x8dc, PIN_OUTPUT | MUX_MODE0) /* lcd_data15.lcd_data15 */ + AM33XX_IOPAD(0x8e0, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_vsync.lcd_vsync */ + AM33XX_IOPAD(0x8e4, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_hsync.lcd_hsync */ + AM33XX_IOPAD(0x8e8, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_pclk.lcd_pclk */ + AM33XX_IOPAD(0x8ec, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_ac_bias_en.lcd_ac_bias_en */ + >; + }; + + nxp_hdmi_off_pins: nxp_hdmi_off_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr0 */ + >; + }; +}; + +&i2c0 { + tda19988 { + pinctrl-names = "default", "off"; + pinctrl-0 = <&nxp_hdmi_bonelt_pins>; + pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; + }; +}; diff --git b/arch/arm/boot/dts/am335x-bone-pinmux-panel-1024x600-24bit.dtsi b/arch/arm/boot/dts/am335x-bone-pinmux-panel-1024x600-24bit.dtsi new file mode 100644 index 0000000..65e5fbb --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-pinmux-panel-1024x600-24bit.dtsi @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 "am335x-peripheral-panel-1024x600-24bit.dtsi" + +/* cape universal */ + +/* + *&ocp { + * P8_27_pinmux { + * state = "disabled"; + * }; + * P8_28_pinmux { + * state = "disabled"; + * }; + * P8_29_pinmux { + * state = "disabled"; + * }; + * P8_30_pinmux { + * state = "disabled"; + * }; + * P8_31_pinmux { + * state = "disabled"; + * }; + * P8_32_pinmux { + * state = "disabled"; + * }; + * P8_33_pinmux { + * state = "disabled"; + * }; + * P8_34_pinmux { + * state = "disabled"; + * }; + * P8_35_pinmux { + * state = "disabled"; + * }; + * P8_36_pinmux { + * state = "disabled"; + * }; + * P8_37_pinmux { + * state = "disabled"; + * }; + * P8_38_pinmux { + * state = "disabled"; + * }; + * P8_39_pinmux { + * state = "disabled"; + * }; + * P8_40_pinmux { + * state = "disabled"; + * }; + * P8_41_pinmux { + * state = "disabled"; + * }; + * P8_42_pinmux { + * state = "disabled"; + * }; + * P8_43_pinmux { + * state = "disabled"; + * }; + * P8_44_pinmux { + * state = "disabled"; + * }; + * P8_45_pinmux { + * state = "disabled"; + * }; + * P8_46_pinmux { + * state = "disabled"; + * }; + *}; + */ + +/* standard */ + +&am33xx_pinmux { + lcd_24bit_pins: pinmux_lcd_24bit_pins { + pinctrl-single,pins = < + + /* P8_45: lcd_data0.lcd_data0 */ + BONE_P8_45 (PIN_OUTPUT | MUX_MODE0) + /* P8_46: lcd_data1.lcd_data1 */ + BONE_P8_46 (PIN_OUTPUT | MUX_MODE0) + /* P8_43: lcd_data2.lcd_data2 */ + BONE_P8_43 (PIN_OUTPUT | MUX_MODE0) + /* P8_44: lcd_data3.lcd_data3 */ + BONE_P8_44 (PIN_OUTPUT | MUX_MODE0) + /* P8_41: lcd_data4.lcd_data4 */ + BONE_P8_41 (PIN_OUTPUT | MUX_MODE0) + /* P8_42: lcd_data5.lcd_data5 */ + BONE_P8_42 (PIN_OUTPUT | MUX_MODE0) + /* P8_39: lcd_data6.lcd_data6 */ + BONE_P8_39 (PIN_OUTPUT | MUX_MODE0) + /* P8_40: lcd_data7.lcd_data7 */ + BONE_P8_40 (PIN_OUTPUT | MUX_MODE0) + /* P8_37: lcd_data8.lcd_data8 */ + BONE_P8_37 (PIN_OUTPUT | MUX_MODE0) + /* P8_38: lcd_data9.lcd_data9 */ + BONE_P8_38 (PIN_OUTPUT | MUX_MODE0) + /* P8_36: lcd_data10.lcd_data10 */ + BONE_P8_36 (PIN_OUTPUT | MUX_MODE0) + /* P8_34: lcd_data11.lcd_data11 */ + BONE_P8_34 (PIN_OUTPUT | MUX_MODE0) + /* P8_35: lcd_data12.lcd_data12 */ + BONE_P8_35 (PIN_OUTPUT | MUX_MODE0) + /* P8_33: lcd_data13.lcd_data13 */ + BONE_P8_33 (PIN_OUTPUT | MUX_MODE0) + /* P8_31: lcd_data14.lcd_data14 */ + BONE_P8_31 (PIN_OUTPUT | MUX_MODE0) + /* P8_32: lcd_data15.lcd_data15 */ + BONE_P8_32 (PIN_OUTPUT | MUX_MODE0) + + /* gpmc_ad15.lcd_data16 */ + BONE_P8_15 (PIN_OUTPUT | MUX_MODE1) + /* gpmc_ad14.lcd_data17 */ + BONE_P8_16 (PIN_OUTPUT | MUX_MODE1) + /* gpmc_ad13.lcd_data18 */ + BONE_P8_11 (PIN_OUTPUT | MUX_MODE1) + /* gpmc_ad12.lcd_data19 */ + BONE_P8_12 (PIN_OUTPUT | MUX_MODE1) + /* gpmc_ad11.lcd_data20 */ + BONE_P8_17 (PIN_OUTPUT | MUX_MODE1) + /* gpmc_ad10.lcd_data21 */ + BONE_P8_14 (PIN_OUTPUT | MUX_MODE1) + /* gpmc_ad9.lcd_data22 */ + BONE_P8_13 (PIN_OUTPUT | MUX_MODE1) + /* gpmc_ad8.lcd_data23 */ + BONE_P8_19 (PIN_OUTPUT | MUX_MODE1) + + /* P8_27: lcd_vsync.lcd_vsync */ + BONE_P8_27 (PIN_OUTPUT | MUX_MODE0) + /* P8_29: lcd_hsync.lcd_hsync */ + BONE_P8_29 (PIN_OUTPUT | MUX_MODE0) + /* P8_28: lcd_pclk.lcd_pclk*/ + BONE_P8_28 (PIN_OUTPUT | MUX_MODE0) + /* P8_30: lcd_ac_bias_en.lcd_ac_bias_en */ + BONE_P8_30 (PIN_OUTPUT | MUX_MODE0) + >; + }; +}; + +/ { + panel { + pinctrl-0 = <&lcd_24bit_pins>; + }; +}; diff --git b/arch/arm/boot/dts/am335x-bone-pinmux-spi0.dtsi b/arch/arm/boot/dts/am335x-bone-pinmux-spi0.dtsi new file mode 100644 index 0000000..354e66a --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-pinmux-spi0.dtsi @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 "am335x-peripheral-spi0.dtsi" + +/* cape universal */ + +/* + *&ocp { + * P9_17_pinmux { + * status = "disabled"; + * }; + * P9_18_pinmux { + * status = "disabled"; + * }; + * P9_21_pinmux { + * status = "disabled"; + * }; + * P9_22_pinmux { + * status = "disabled"; + * }; + *}; + * + *&spi0 { + * pinctrl-0 = <>; + *}; + * + */ + +/* standard */ + +&am33xx_pinmux { + spi0_pins: pinmux_spi0_pins { + pinctrl-single,pins = < + 0x150 (PIN_INPUT_PULLUP | MUX_MODE0) /* spi0_sclk.spi0_sclk */ + 0x154 (PIN_INPUT_PULLUP | MUX_MODE0) /* spi0_d0.spi0_d0 */ + 0x158 (PIN_OUTPUT_PULLUP | MUX_MODE0) /* spi0_d1.spi0_d1 */ + 0x15c (PIN_OUTPUT_PULLUP | MUX_MODE0) /* spi0_cs0.spi0_cs0 */ + >; + }; +}; + +&spi0 { + pinctrl-0 = <&spi0_pins>; +}; diff --git b/arch/arm/boot/dts/am335x-bone-pinmux-spi1.dtsi b/arch/arm/boot/dts/am335x-bone-pinmux-spi1.dtsi new file mode 100644 index 0000000..bff7f8d --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-pinmux-spi1.dtsi @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 "am335x-peripheral-spi1.dtsi" + +/* standard */ + +&am33xx_pinmux { + spi1_pins: pinmux_spi1_pins { + pinctrl-single,pins = < + 0x190 0x33 /* mcasp0_aclkx.spi1_sclk, INPUT_PULLUP | MODE3 */ + 0x194 0x33 /* mcasp0_fsx.spi1_d0, INPUT_PULLUP | MODE3 */ + 0x198 0x13 /* mcasp0_axr0.spi1_d1, OUTPUT_PULLUP | MODE3 */ + 0x19c 0x13 /* mcasp0_ahclkr.spi1_cs0, OUTPUT_PULLUP | MODE3 */ + // 0x164 0x12 /* eCAP0_in_PWM0_out.spi1_cs1 OUTPUT_PULLUP | MODE2 */ >; + }; +}; + +&spi1 { + pinctrl-0 = <&spi1_pins>; +}; diff --git b/arch/arm/boot/dts/am335x-bone-pinmux-spi1a.dtsi b/arch/arm/boot/dts/am335x-bone-pinmux-spi1a.dtsi new file mode 100644 index 0000000..62874c8 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-pinmux-spi1a.dtsi @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 "am335x-peripheral-spi1.dtsi" + +/* standard */ + +&am33xx_pinmux { + spi1a_pins: pinmux_spi1a_pins { + pinctrl-single,pins = < + 0x164 0x34 /* eCAP0_in_PWM0_out.spi1_sclk, INPUT_PULLUP | MODE4 */ + /* NOTE: P9.42 is connected to two pads */ + // 0x1A0 0x27 /* set the other pad to gpio input */ + 0x194 0x33 /* mcasp0_fsx.spi1_d0, INPUT_PULLUP | MODE3 */ + 0x198 0x13 /* mcasp0_axr0.spi1_d1, OUTPUT_PULLUP | MODE3 */ + 0x178 0x14 /* uart1_ctsn.spi1_cs0, OUTPUT_PULLUP | MODE4 */ >; + }; +}; + +&spi1 { + pinctrl-0 = <&spi1a_pins>; +}; diff --git b/arch/arm/boot/dts/am335x-bone-pinmux-ttyS1.dtsi b/arch/arm/boot/dts/am335x-bone-pinmux-ttyS1.dtsi new file mode 100644 index 0000000..ae5b813 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-pinmux-ttyS1.dtsi @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +/* Testing */ +/* sudo /sbin/getty -L ttyS1 115200 vt102 */ + +#include +#include "am335x-peripheral-ttyS1.dtsi" + +/* cape universal */ + +/* + *&ocp { + * P9_24_pinmux { + * mode = "uart"; + * }; + * P9_26_pinmux { + * mode = "uart"; + * }; + *}; + * + *&uart1 { + * pinctrl-0 = <>; + *}; + * + */ + +/* standard */ + +&am33xx_pinmux { + uart1_pins: pinmux_uart1_pins { + pinctrl-single,pins = < + /* P9_24: uart1_txd.uart1_txd */ + BONE_P9_24 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) + /* P9_26: uart1_rxd.uart1_rxd */ + BONE_P9_26 (PIN_INPUT_PULLUP | MUX_MODE0) + >; + }; +}; + +&uart1 { + pinctrl-0 = <&uart1_pins>; +}; diff --git b/arch/arm/boot/dts/am335x-bone-pinmux-ttyS2.dtsi b/arch/arm/boot/dts/am335x-bone-pinmux-ttyS2.dtsi new file mode 100644 index 0000000..5fa593a --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-pinmux-ttyS2.dtsi @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +/* Testing */ +/* sudo /sbin/getty -L ttyS2 115200 vt102 */ + +#include +#include "am335x-peripheral-ttyS2.dtsi" + +/* cape universal */ + +/* + *&ocp { + * P9_21_pinmux { + * mode = "uart"; + * }; + * P9_22_pinmux { + * mode = "uart"; + * }; + *}; + * + *&uart2 { + * pinctrl-0 = <>; + *}; + * + */ + +/* standard */ + +&am33xx_pinmux { + uart2_pins: pinmux_uart2_pins { + pinctrl-single,pins = < + /* P9_21: spi0_d0.uart2_txd */ + BONE_P9_21 (PIN_OUTPUT_PULLDOWN | MUX_MODE1) + /* P9_22: spi0_sclk.uart2_rxd */ + BONE_P9_22 (PIN_INPUT_PULLUP | MUX_MODE1) + >; + }; +}; + +&uart2 { + pinctrl-0 = <&uart2_pins>; +}; diff --git b/arch/arm/boot/dts/am335x-bone-pinmux-ttyS4.dtsi b/arch/arm/boot/dts/am335x-bone-pinmux-ttyS4.dtsi new file mode 100644 index 0000000..1d22a95 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-pinmux-ttyS4.dtsi @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +/* Testing */ +/* sudo /sbin/getty -L ttyS4 115200 vt102 */ + +#include +#include "am335x-peripheral-ttyS4.dtsi" + +/* cape universal */ + +/* + *&ocp { + * P9_11_pinmux { + * mode = "uart"; + * }; + * P9_13_pinmux { + * mode = "uart"; + * }; + *}; + * + *&uart4 { + * pinctrl-0 = <>; + *}; + * + */ + +/* standard */ + +&am33xx_pinmux { + uart4_pins: pinmux_uart4_pins { + pinctrl-single,pins = < + /* P9_11: gpmc_wait0.uart4_rxd_mux2 */ + BONE_P9_11 (PIN_INPUT_PULLUP | MUX_MODE6) + /* P9_13: gpmc_wpn.uart4_txd_mux2 */ + BONE_P9_13 (PIN_OUTPUT_PULLDOWN | MUX_MODE6) + >; + }; +}; + +&uart4 { + pinctrl-0 = <&uart4_pins>; +}; diff --git b/arch/arm/boot/dts/am335x-bone-pinmux-ttyS5.dtsi b/arch/arm/boot/dts/am335x-bone-pinmux-ttyS5.dtsi new file mode 100644 index 0000000..01d0aec --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-pinmux-ttyS5.dtsi @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +/* Testing */ +/* sudo /sbin/getty -L ttyS5 115200 vt102 */ + +#include +#include "am335x-peripheral-ttyS5.dtsi" + +/* cape universal */ + +/* + *&ocp { + * P8_37_pinmux { + * mode = "uart"; + * }; + * P8_38_pinmux { + * mode = "uart"; + * }; + *}; + * + *&uart5 { + * pinctrl-0 = <>; + *}; + * + */ + +/* standard */ + +&am33xx_pinmux { + uart5_pins: pinmux_uart5_pins { + pinctrl-single,pins = < + /* P8_38: lcd_data9.uart5_rxd */ + BONE_P8_38 (PIN_INPUT_PULLUP | MUX_MODE4) + /* P8_37: lcd_data8.uart5_txd */ + BONE_P8_37 (PIN_OUTPUT_PULLDOWN | MUX_MODE4) + >; + }; +}; + +&uart5 { + pinctrl-0 = <&uart5_pins>; +}; diff --git a/arch/arm/boot/dts/am335x-bone.dts b/arch/arm/boot/dts/am335x-bone.dts index 6b84937..b195b9e 100644 --- a/arch/arm/boot/dts/am335x-bone.dts +++ b/arch/arm/boot/dts/am335x-bone.dts @@ -9,6 +9,7 @@ #include "am33xx.dtsi" #include "am335x-bone-common.dtsi" +#include "am33xx-overlay-edma-fix.dtsi" / { model = "TI AM335x BeagleBone"; diff --git b/arch/arm/boot/dts/am335x-boneblack-audio.dts b/arch/arm/boot/dts/am335x-boneblack-audio.dts new file mode 100644 index 0000000..44e9f9c --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-audio.dts @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" + +/ { + model = "TI AM335x BeagleBone Black"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; + + clk_mcasp0_fixed: clk_mcasp0_fixed { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <24576000>; + }; + + clk_mcasp0: clk_mcasp0 { + #clock-cells = <0>; + compatible = "gpio-gate-clock"; + clocks = <&clk_mcasp0_fixed>; + enable-gpios = <&gpio1 27 GPIO_ACTIVE_HIGH>; /* BeagleBone Black Clk enable on GPIO1_27 */ + }; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro@1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; diff --git b/arch/arm/boot/dts/am335x-boneblack-bbb-exp-c.dts b/arch/arm/boot/dts/am335x-boneblack-bbb-exp-c.dts new file mode 100644 index 0000000..8d795c0 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-bbb-exp-c.dts @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common-no-capemgr.dtsi" + +/ { + model = "TI AM335x BeagleBone Black"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&mmc2 { + vmmc-supply = <&vmmcsd_fixed>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_pins>; + bus-width = <8>; + status = "okay"; +}; + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro@1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + +#include "am335x-cape-bbb-exp-c.dtsi" diff --git b/arch/arm/boot/dts/am335x-boneblack-bbb-exp-r.dts b/arch/arm/boot/dts/am335x-boneblack-bbb-exp-r.dts new file mode 100644 index 0000000..5df881e --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-bbb-exp-r.dts @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common-no-capemgr.dtsi" + +/ { + model = "TI AM335x BeagleBone Black"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&mmc2 { + vmmc-supply = <&vmmcsd_fixed>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_pins>; + bus-width = <8>; + status = "okay"; +}; + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro@1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + +#include "am335x-cape-bbb-exp-r.dtsi" diff --git b/arch/arm/boot/dts/am335x-boneblack-bbbmini.dts b/arch/arm/boot/dts/am335x-boneblack-bbbmini.dts new file mode 100644 index 0000000..5ed89a2 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-bbbmini.dts @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * Modified by Mirko Denecke + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" + +#include +#include + +/ { + model = "TI AM335x BeagleBone Black"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&mmc2 { + vmmc-supply = <&vmmcsd_fixed>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_pins>; + bus-width = <8>; + status = "okay"; +}; + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro@1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + +&am33xx_pinmux { + dcan1_pins: pinmux_dcan1_pins { + pinctrl-single,pins = < + /* P9_26: uart1_rxd.d_can1_tx */ + BONE_P9_26 (PIN_OUTPUT_PULLUP | MUX_MODE2) + /* P9_24: uart1_txd.d_can1_rx */ + BONE_P9_24 (PIN_INPUT_PULLUP | MUX_MODE2) + >; + }; + + pru_pins: pinmux_pru_pins { + pinctrl-single,pins = < + 0x03c 0x35 /* ecap0_in_pwm0_out.pr1_ecap0_ecap_capin, MODE5 | INPUT_PULLUP | PRU, PPM-sum, SBUS, DSM */ + + 0x0e8 0x25 /* lcd_pclk.pr1_pru1_pru_r30_10, MODE5 | OUTPUT | PRU, CH_1 */ + 0x0e0 0x25 /* lcd_vsync.pr1_pru1_pru_r30_8, MODE5 | OUTPUT | PRU, CH_2 */ + 0x0ec 0x25 /* lcd_ac_bias_en.pr1_pru1_pru_r30_11, MODE5 | OUTPUT | PRU, CH_3 */ + 0x0e4 0x25 /* lcd_hsync.pr1_pru1_pru_r30_9, MODE5 | OUTPUT | PRU, CH_4 */ + 0x0bc 0x25 /* lcd_data7.pr1_pru1_pru_r30_7, MODE5 | OUTPUT | PRU, CH_5 */ + 0x0b8 0x25 /* lcd_data6.pr1_pru1_pru_r30_6, MODE5 | OUTPUT | PRU, CH_6 */ + 0x0b4 0x25 /* lcd_data5.pr1_pru1_pru_r30_5, MODE5 | OUTPUT | PRU, CH_7 */ + 0x0b0 0x25 /* lcd_data4.pr1_pru1_pru_r30_4, MODE5 | OUTPUT | PRU, CH_8 */ + 0x0ac 0x25 /* lcd_data3.pr1_pru1_pru_r30_3, MODE5 | OUTPUT | PRU, CH_9 */ + 0x0a8 0x25 /* lcd_data2.pr1_pru1_pru_r30_2, MODE5 | OUTPUT | PRU, CH_10 */ + 0x0a4 0x25 /* lcd_data1.pr1_pru1_pru_r30_1, MODE5 | OUTPUT | PRU, CH_11 */ + 0x0a0 0x25 /* lcd_data0.pr1_pru1_pru_r30_0, MODE5 | OUTPUT | PRU, CH_12 */ + + BONE_P8_12 (PIN_OUTPUT_PULLDOWN | MUX_MODE6) /* HC-SR04 TRIG */ + BONE_P8_16 (PIN_INPUT_PULLDOWN | MUX_MODE6) /* HC-SR04 ECHO */ + + BONE_P9_25 (PIN_INPUT_PULLDOWN | MUX_MODE6) /* MPU9250 INT */ + >; + }; + + spi0_pins: pinmux_spi0_pins { + pinctrl-single,pins = < + /* P9_22: spi0_sclk.spi0_sclk */ + BONE_P9_22 (PIN_INPUT_PULLUP | MUX_MODE0) + /* P9_21: spi0_d0.spi0_d0 */ + BONE_P9_21 (PIN_INPUT_PULLUP | MUX_MODE0) + /* P9_18: spi0_d1.spi0_d1 */ + BONE_P9_18 (PIN_OUTPUT_PULLUP | MUX_MODE0) + /* P9_17: spi0_cs0.spi0_cs0 */ + BONE_P9_17 (PIN_OUTPUT_PULLUP | MUX_MODE0) + >; + }; + + spi1_pins: pinmux_spi1_pins { + pinctrl-single,pins = < + /* P9_31: mcasp0_aclkx.spi1_sclk */ + BONE_P9_31 (PIN_INPUT_PULLUP | MUX_MODE3) + + /* P9_29: mcasp0_fsx.spi1_d0 */ + BONE_P9_29 (PIN_INPUT_PULLUP | MUX_MODE3) + + /* P9_30: mcasp0_axr0.spi1_d1 */ + BONE_P9_30 (PIN_OUTPUT_PULLUP | MUX_MODE3) + + /* P9_28: mcasp0_ahclkr.spi1_cs0 */ + BONE_P9_28 (PIN_OUTPUT_PULLUP | MUX_MODE3) + + /* P9_19: uart1_rtsn.spi1_cs1 */ +/* BONE_P9_19 (PIN_OUTPUT_PULLUP | MUX_MODE4)*/ + + /* P9_42: ecap0_in_pwm0_out.spi1_cs1 */ + BONE_P9_42A (PIN_OUTPUT_PULLUP | MUX_MODE2) + >; + }; + + uart4_pins: pinmux_uart4_pins { + pinctrl-single,pins = < + /* P9_11: gpmc_wait0.uart4_rxd_mux2 */ + BONE_P9_11 (PIN_INPUT_PULLUP | MUX_MODE6) + /* P9_13: gpmc_wpn.uart4_txd_mux2 */ + BONE_P9_13 (PIN_OUTPUT_PULLDOWN | MUX_MODE6) + >; + }; + + uart5_pins: pinmux_uart5_pins { + pinctrl-single,pins = < + /* P8_38: lcd_data9.uart5_rxd */ + BONE_P8_38 (PIN_INPUT_PULLUP | MUX_MODE4) + /* P8_37: lcd_data8.uart5_txd */ + BONE_P8_37 (PIN_OUTPUT_PULLDOWN | MUX_MODE4) + >; + }; +}; + +&dcan1 { + pinctrl-names = "default"; + pinctrl-0 = <&dcan1_pins>; + status = "okay"; +}; + +&i2c2 { + clock-frequency = <400000>; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins>; + status = "okay"; + + spi0_0 { + #address-cells = <1>; + #size-cells = <0>; + spi-max-frequency = <24000000>; + reg = <0>; + compatible = "spidev"; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&spi1_pins>; + status = "okay"; + + spi1_0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + spi-max-frequency = <24000000>; + compatible = "spidev"; + }; + + spi1_1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + spi-max-frequency = <24000000>; + compatible = "spidev"; + }; +}; + +&tscadc { + adc { + ti,adc-channels = <0 1>; + }; +}; + +&pruss { + pinctrl-names = "default"; + pinctrl-0 = <&pru_pins>; + status = "okay"; +}; + +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&uart4_pins>; + status = "okay"; +}; + +&uart5 { + pinctrl-names = "default"; + pinctrl-0 = <&uart5_pins>; + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-boneblack-cape-bone-argus.dts b/arch/arm/boot/dts/am335x-boneblack-cape-bone-argus.dts new file mode 100644 index 0000000..c97c912 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-cape-bone-argus.dts @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common-no-capemgr.dtsi" + +/ { + model = "TI AM335x BeagleBone Black"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&mmc2 { + vmmc-supply = <&vmmcsd_fixed>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_pins>; + bus-width = <8>; + status = "okay"; +}; + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro@1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + +&am33xx_pinmux { + nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr0 */ + AM33XX_IOPAD(0x8a0, PIN_OUTPUT | MUX_MODE0) /* lcd_data0.lcd_data0 */ + AM33XX_IOPAD(0x8a4, PIN_OUTPUT | MUX_MODE0) /* lcd_data1.lcd_data1 */ + AM33XX_IOPAD(0x8a8, PIN_OUTPUT | MUX_MODE0) /* lcd_data2.lcd_data2 */ + AM33XX_IOPAD(0x8ac, PIN_OUTPUT | MUX_MODE0) /* lcd_data3.lcd_data3 */ + AM33XX_IOPAD(0x8b0, PIN_OUTPUT | MUX_MODE0) /* lcd_data4.lcd_data4 */ + AM33XX_IOPAD(0x8b4, PIN_OUTPUT | MUX_MODE0) /* lcd_data5.lcd_data5 */ + AM33XX_IOPAD(0x8b8, PIN_OUTPUT | MUX_MODE0) /* lcd_data6.lcd_data6 */ + AM33XX_IOPAD(0x8bc, PIN_OUTPUT | MUX_MODE0) /* lcd_data7.lcd_data7 */ + AM33XX_IOPAD(0x8c0, PIN_OUTPUT | MUX_MODE0) /* lcd_data8.lcd_data8 */ + AM33XX_IOPAD(0x8c4, PIN_OUTPUT | MUX_MODE0) /* lcd_data9.lcd_data9 */ + AM33XX_IOPAD(0x8c8, PIN_OUTPUT | MUX_MODE0) /* lcd_data10.lcd_data10 */ + AM33XX_IOPAD(0x8cc, PIN_OUTPUT | MUX_MODE0) /* lcd_data11.lcd_data11 */ + AM33XX_IOPAD(0x8d0, PIN_OUTPUT | MUX_MODE0) /* lcd_data12.lcd_data12 */ + AM33XX_IOPAD(0x8d4, PIN_OUTPUT | MUX_MODE0) /* lcd_data13.lcd_data13 */ + AM33XX_IOPAD(0x8d8, PIN_OUTPUT | MUX_MODE0) /* lcd_data14.lcd_data14 */ + AM33XX_IOPAD(0x8dc, PIN_OUTPUT | MUX_MODE0) /* lcd_data15.lcd_data15 */ + AM33XX_IOPAD(0x8e0, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_vsync.lcd_vsync */ + AM33XX_IOPAD(0x8e4, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_hsync.lcd_hsync */ + AM33XX_IOPAD(0x8e8, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_pclk.lcd_pclk */ + AM33XX_IOPAD(0x8ec, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_ac_bias_en.lcd_ac_bias_en */ + >; + }; + nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr0 */ + >; + }; +}; + +&lcdc { + status = "okay"; + port { + lcdc_0: endpoint@0 { + remote-endpoint = <&hdmi_0>; + }; + }; +}; + +&i2c0 { + tda19988 { + compatible = "nxp,tda998x"; + reg = <0x70>; + pinctrl-names = "default", "off"; + pinctrl-0 = <&nxp_hdmi_bonelt_pins>; + pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; + + port { + hdmi_0: endpoint@0 { + remote-endpoint = <&lcdc_0>; + }; + }; + }; +}; + +#include "am335x-bone-argus.dtsi" diff --git b/arch/arm/boot/dts/am335x-boneblack-emmc-overlay.dts b/arch/arm/boot/dts/am335x-boneblack-emmc-overlay.dts new file mode 100644 index 0000000..3859359 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-emmc-overlay.dts @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am33xx-overlay-edma-fix.dtsi" + +/ { + model = "TI AM335x BeagleBone Black"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&mmc2 { + vmmc-supply = <&vmmcsd_fixed>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_pins>; + bus-width = <8>; + status = "okay"; +}; + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro@1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; diff --git b/arch/arm/boot/dts/am335x-boneblack-hdmi-overlay.dts b/arch/arm/boot/dts/am335x-boneblack-hdmi-overlay.dts new file mode 100644 index 0000000..4c3a477 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-hdmi-overlay.dts @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am33xx-overlay-edma-fix.dtsi" + +/ { + model = "TI AM335x BeagleBone Black"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +/* EMMC in reset */ +&gpio1 { + emmc_rst { + gpio-hog; + gpios = <20 0>; + output-high; + line-name = "EMMC ResetN"; + }; +}; + + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro@1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + +&am33xx_pinmux { + nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr0 */ + AM33XX_IOPAD(0x8a0, PIN_OUTPUT | MUX_MODE0) /* lcd_data0.lcd_data0 */ + AM33XX_IOPAD(0x8a4, PIN_OUTPUT | MUX_MODE0) /* lcd_data1.lcd_data1 */ + AM33XX_IOPAD(0x8a8, PIN_OUTPUT | MUX_MODE0) /* lcd_data2.lcd_data2 */ + AM33XX_IOPAD(0x8ac, PIN_OUTPUT | MUX_MODE0) /* lcd_data3.lcd_data3 */ + AM33XX_IOPAD(0x8b0, PIN_OUTPUT | MUX_MODE0) /* lcd_data4.lcd_data4 */ + AM33XX_IOPAD(0x8b4, PIN_OUTPUT | MUX_MODE0) /* lcd_data5.lcd_data5 */ + AM33XX_IOPAD(0x8b8, PIN_OUTPUT | MUX_MODE0) /* lcd_data6.lcd_data6 */ + AM33XX_IOPAD(0x8bc, PIN_OUTPUT | MUX_MODE0) /* lcd_data7.lcd_data7 */ + AM33XX_IOPAD(0x8c0, PIN_OUTPUT | MUX_MODE0) /* lcd_data8.lcd_data8 */ + AM33XX_IOPAD(0x8c4, PIN_OUTPUT | MUX_MODE0) /* lcd_data9.lcd_data9 */ + AM33XX_IOPAD(0x8c8, PIN_OUTPUT | MUX_MODE0) /* lcd_data10.lcd_data10 */ + AM33XX_IOPAD(0x8cc, PIN_OUTPUT | MUX_MODE0) /* lcd_data11.lcd_data11 */ + AM33XX_IOPAD(0x8d0, PIN_OUTPUT | MUX_MODE0) /* lcd_data12.lcd_data12 */ + AM33XX_IOPAD(0x8d4, PIN_OUTPUT | MUX_MODE0) /* lcd_data13.lcd_data13 */ + AM33XX_IOPAD(0x8d8, PIN_OUTPUT | MUX_MODE0) /* lcd_data14.lcd_data14 */ + AM33XX_IOPAD(0x8dc, PIN_OUTPUT | MUX_MODE0) /* lcd_data15.lcd_data15 */ + AM33XX_IOPAD(0x8e0, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_vsync.lcd_vsync */ + AM33XX_IOPAD(0x8e4, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_hsync.lcd_hsync */ + AM33XX_IOPAD(0x8e8, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_pclk.lcd_pclk */ + AM33XX_IOPAD(0x8ec, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_ac_bias_en.lcd_ac_bias_en */ + >; + }; + nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr0 */ + >; + }; +}; + +&lcdc { + status = "okay"; + port { + lcdc_0: endpoint@0 { + remote-endpoint = <&hdmi_0>; + }; + }; +}; + +&i2c0 { + tda19988 { + compatible = "nxp,tda998x"; + reg = <0x70>; + pinctrl-names = "default", "off"; + pinctrl-0 = <&nxp_hdmi_bonelt_pins>; + pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; + + port { + hdmi_0: endpoint@0 { + remote-endpoint = <&lcdc_0>; + }; + }; + }; +}; diff --git b/arch/arm/boot/dts/am335x-boneblack-nhdmi-overlay.dts b/arch/arm/boot/dts/am335x-boneblack-nhdmi-overlay.dts new file mode 100644 index 0000000..4c3a477 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-nhdmi-overlay.dts @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am33xx-overlay-edma-fix.dtsi" + +/ { + model = "TI AM335x BeagleBone Black"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +/* EMMC in reset */ +&gpio1 { + emmc_rst { + gpio-hog; + gpios = <20 0>; + output-high; + line-name = "EMMC ResetN"; + }; +}; + + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro@1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + +&am33xx_pinmux { + nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr0 */ + AM33XX_IOPAD(0x8a0, PIN_OUTPUT | MUX_MODE0) /* lcd_data0.lcd_data0 */ + AM33XX_IOPAD(0x8a4, PIN_OUTPUT | MUX_MODE0) /* lcd_data1.lcd_data1 */ + AM33XX_IOPAD(0x8a8, PIN_OUTPUT | MUX_MODE0) /* lcd_data2.lcd_data2 */ + AM33XX_IOPAD(0x8ac, PIN_OUTPUT | MUX_MODE0) /* lcd_data3.lcd_data3 */ + AM33XX_IOPAD(0x8b0, PIN_OUTPUT | MUX_MODE0) /* lcd_data4.lcd_data4 */ + AM33XX_IOPAD(0x8b4, PIN_OUTPUT | MUX_MODE0) /* lcd_data5.lcd_data5 */ + AM33XX_IOPAD(0x8b8, PIN_OUTPUT | MUX_MODE0) /* lcd_data6.lcd_data6 */ + AM33XX_IOPAD(0x8bc, PIN_OUTPUT | MUX_MODE0) /* lcd_data7.lcd_data7 */ + AM33XX_IOPAD(0x8c0, PIN_OUTPUT | MUX_MODE0) /* lcd_data8.lcd_data8 */ + AM33XX_IOPAD(0x8c4, PIN_OUTPUT | MUX_MODE0) /* lcd_data9.lcd_data9 */ + AM33XX_IOPAD(0x8c8, PIN_OUTPUT | MUX_MODE0) /* lcd_data10.lcd_data10 */ + AM33XX_IOPAD(0x8cc, PIN_OUTPUT | MUX_MODE0) /* lcd_data11.lcd_data11 */ + AM33XX_IOPAD(0x8d0, PIN_OUTPUT | MUX_MODE0) /* lcd_data12.lcd_data12 */ + AM33XX_IOPAD(0x8d4, PIN_OUTPUT | MUX_MODE0) /* lcd_data13.lcd_data13 */ + AM33XX_IOPAD(0x8d8, PIN_OUTPUT | MUX_MODE0) /* lcd_data14.lcd_data14 */ + AM33XX_IOPAD(0x8dc, PIN_OUTPUT | MUX_MODE0) /* lcd_data15.lcd_data15 */ + AM33XX_IOPAD(0x8e0, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_vsync.lcd_vsync */ + AM33XX_IOPAD(0x8e4, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_hsync.lcd_hsync */ + AM33XX_IOPAD(0x8e8, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_pclk.lcd_pclk */ + AM33XX_IOPAD(0x8ec, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* lcd_ac_bias_en.lcd_ac_bias_en */ + >; + }; + nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { + pinctrl-single,pins = < + AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3) /* xdma_event_intr0 */ + >; + }; +}; + +&lcdc { + status = "okay"; + port { + lcdc_0: endpoint@0 { + remote-endpoint = <&hdmi_0>; + }; + }; +}; + +&i2c0 { + tda19988 { + compatible = "nxp,tda998x"; + reg = <0x70>; + pinctrl-names = "default", "off"; + pinctrl-0 = <&nxp_hdmi_bonelt_pins>; + pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; + + port { + hdmi_0: endpoint@0 { + remote-endpoint = <&lcdc_0>; + }; + }; + }; +}; diff --git b/arch/arm/boot/dts/am335x-boneblack-overlay.dts b/arch/arm/boot/dts/am335x-boneblack-overlay.dts new file mode 100644 index 0000000..d8dece7 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-overlay.dts @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am33xx-overlay-edma-fix.dtsi" + +/ { + model = "TI AM335x BeagleBone Black"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +/* EMMC in reset */ +&gpio1 { + emmc_rst { + gpio-hog; + gpios = <20 0>; + output-high; + line-name = "EMMC ResetN"; + }; +}; + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro@1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; diff --git b/arch/arm/boot/dts/am335x-boneblack-wl1835mod-cape.dtsi b/arch/arm/boot/dts/am335x-boneblack-wl1835mod-cape.dtsi new file mode 100644 index 0000000..94caa22 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-wl1835mod-cape.dtsi @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 + +/ { + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + /* WL_EN */ + gpio = <&gpio0 26 0>; + enable-active-high; + }; + + kim { + compatible = "kim"; + nshutdown_gpio = <44>; /* Bank1, pin12 */ + dev_name = "/dev/ttyO4"; + flow_cntrl = <1>; + baud_rate = <3000000>; + }; + + btwilink { + compatible = "btwilink"; + }; +}; + +&am33xx_pinmux { + bt_pins: pinmux_bt_pins { + pinctrl-single,pins = < + 0x30 (PIN_OUTPUT_PULLUP | MUX_MODE7) /* gpmc_ad12.gpio1_12 */ + >; + }; + + mmc2_pins: pinmux_mmc2_pins { + pinctrl-single,pins = < + 0x80 (PIN_INPUT_PULLUP | MUX_MODE2) /* gpmc_csn1.mmc1_clk */ + 0x84 (PIN_INPUT_PULLUP | MUX_MODE2) /* gpmc_csn2.mmc1_cmd */ + 0x00 (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad0.mmc1_dat0 */ + 0x04 (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad1.mmc1_dat1 */ + 0x08 (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad2.mmc1_dat2 */ + 0x0c (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad3.mmc1_dat3 */ + >; + }; + + mmc2_pins_sleep: pinmux_mmc2_pins_sleep { + pinctrl-single,pins = < + 0x80 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_csn1.mmc1_clk */ + 0x84 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_csn2.mmc1_cmd */ + 0x00 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_ad0.mmc1_dat0 */ + 0x04 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_ad1.mmc1_dat1 */ + 0x08 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_ad2.mmc1_dat2 */ + 0x0c (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_ad3.mmc1_dat3 */ + >; + }; + + /* wl18xx card enable/irq GPIOs. */ + wlan_pins: pinmux_wlan_pins { + pinctrl-single,pins = < + 0x28 (PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpmc_ad10.gpio0_26 WL_EN*/ + 0x2C (PIN_INPUT_PULLUP | MUX_MODE7) /* gpmc_ad11.gpio0_27 WL_IRQ*/ + 0x7C (PIN_OUTPUT_PULLUP | MUX_MODE0) /* gpmc_csn0.gpio1_29 BF_EN*/ + >; + }; + + /* wl18xx card enable/irq GPIOs. */ + wlan_pins_sleep: pinmux_wlan_pins_sleep { + pinctrl-single,pins = < + 0x28 (PIN_OUTPUT_PULLUP | MUX_MODE7) /* gpmc_ad10.gpio0_26 WL_EN*/ + 0x2C (PIN_INPUT_PULLUP | MUX_MODE7) /* gpmc_ad11.gpio0_27 WL_IRQ*/ + 0x7C (PIN_OUTPUT_PULLUP | MUX_MODE0) /* gpmc_csn0.gpio1_29 BF_EN*/ + >; + }; + + uart4_pins_default: pinmux_uart4_pins_default { + pinctrl-single,pins = < + 0xD0 (PIN_INPUT | MUX_MODE6) /* lcd_data12.uart4_cts */ + 0xD4 (PIN_OUTPUT_PULLDOWN | MUX_MODE6) /* lcd_data13.uart4_rts */ + 0x70 (PIN_INPUT_PULLUP | MUX_MODE6) /* gpmc_wait0.uart4_rxd */ + 0x74 (PIN_OUTPUT_PULLDOWN | MUX_MODE6) /* gpmc_wpn.uart4_txd */ + >; + }; + + uart4_pins_sleep: pinmux_uart4_pins_sleep { + pinctrl-single,pins = < + 0xD0 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* lcd_data12.uart4_cts */ + 0xD4 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* lcd_data13.uart4_rts */ + 0x70 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_wait0.uart4_rxd */ + 0x74 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_wpn.uart4_txd */ + >; + }; +}; + +&mmc2 { + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&mmc2_pins &wlan_pins>; + pinctrl-1 = <&mmc2_pins_sleep &wlan_pins_sleep>; + ti,non-removable; + ti,needs-special-hs-handling; + cap-power-off-card; + keep-power-in-suspend; + + #address-cells = <1>; + #size-cells = <0>; + wlcore: wlcore@0 { + compatible = "ti,wl1835"; + reg = <2>; + interrupt-parent = <&gpio0>; + interrupts = <27 IRQ_TYPE_LEVEL_HIGH>; + }; +}; + +&uart4 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&uart4_pins_default>; + pinctrl-1 = <&uart4_pins_sleep>; + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-boneblack-wl1835mod.dts b/arch/arm/boot/dts/am335x-boneblack-wl1835mod.dts new file mode 100644 index 0000000..f35b64a --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-wl1835mod.dts @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common-no-capemgr.dtsi" + +/ { + model = "TI AM335x BeagleBone Black"; + compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +/* EMMC in reset */ +&gpio1 { + emmc_rst { + gpio-hog; + gpios = <20 0>; + output-high; + line-name = "EMMC ResetN"; + }; +}; + +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro@1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + +#include "am335x-boneblack-wl1835mod-cape.dtsi" diff --git a/arch/arm/boot/dts/am335x-boneblack.dts b/arch/arm/boot/dts/am335x-boneblack.dts index 55c0e95..944c16c 100644 --- a/arch/arm/boot/dts/am335x-boneblack.dts +++ b/arch/arm/boot/dts/am335x-boneblack.dts @@ -9,6 +9,8 @@ #include "am33xx.dtsi" #include "am335x-bone-common.dtsi" +#include "am33xx-overlay-edma-fix.dtsi" +/* #include "am335x-bone-jtag.dtsi" */ / { model = "TI AM335x BeagleBone Black"; @@ -33,6 +35,17 @@ status = "okay"; }; +&cpu0_opp_table { + /* + * All PG 2.0 silicon may not support 1GHz but some of the early + * BeagleBone Blacks have PG 2.0 silicon which is guaranteed + * to support 1GHz OPP so enable it for PG 2.0 on this board. + */ + oppnitro@1000000000 { + opp-supported-hw = <0x06 0x0100>; + }; +}; + &am33xx_pinmux { nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { pinctrl-single,pins = < @@ -90,7 +103,3 @@ }; }; }; - -&rtc { - system-power-controller; -}; diff --git b/arch/arm/boot/dts/am335x-bonegreen-overlay.dts b/arch/arm/boot/dts/am335x-bonegreen-overlay.dts new file mode 100644 index 0000000..da54bf3 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bonegreen-overlay.dts @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am33xx-overlay-edma-fix.dtsi" + +/ { + model = "TI AM335x BeagleBone Green"; + compatible = "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +/* EMMC in reset */ +&gpio1 { + emmc_rst { + gpio-hog; + gpios = <20 0>; + output-high; + line-name = "EMMC ResetN"; + }; +}; diff --git b/arch/arm/boot/dts/am335x-bonegreen-wireless.dts b/arch/arm/boot/dts/am335x-bonegreen-wireless.dts new file mode 100644 index 0000000..b4fabfe --- /dev/null +++ b/arch/arm/boot/dts/am335x-bonegreen-wireless.dts @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am335x-bonegreen-wl1835.dtsi" + +/ { + model = "TI AM335x BeagleBone Green Wireless"; + compatible = "ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&mmc2 { + vmmc-supply = <&vmmcsd_fixed>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_pins>; + bus-width = <8>; + status = "okay"; +}; + +&uart3 { + status = "okay"; +}; + +&mmc3 { + status = "okay"; +}; + +&mac { + status = "disabled"; +}; diff --git b/arch/arm/boot/dts/am335x-bonegreen-wl1835.dtsi b/arch/arm/boot/dts/am335x-bonegreen-wl1835.dtsi new file mode 100644 index 0000000..a1df687 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bonegreen-wl1835.dtsi @@ -0,0 +1,136 @@ + +#include + +/ { +// wlan_en_reg: fixedregulator@2 { +// compatible = "regulator-fixed"; +// regulator-name = "wlan-en-regulator"; +// regulator-min-microvolt = <1800000>; +// regulator-max-microvolt = <1800000>; + + /* WL_EN */ +// gpio = <&gpio0 26 0>; +// enable-active-high; +// }; + + tibt { + compatible = "tibt"; + nshutdown_gpio = <60>; + dev_name = "/dev/ttyS3"; + flow_cntrl = <1>; + baud_rate = <3000000>; + }; + + btwilink { + compatible = "btwilink"; + }; +}; + +&am33xx_pinmux { + bt_pins: pinmux_bt_pins { + pinctrl-single,pins = < + 0x78 (PIN_OUTPUT_PULLUP | MUX_MODE7) /* gpmc_ad12.gpio1_28 BT_EN*/ + >; + }; + + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + 0x8c ( PIN_INPUT_PULLUP | MUX_MODE3 ) /* gpio2_1 gpmc_clk.mmc2_clk */ + 0x88 ( PIN_INPUT_PULLUP | MUX_MODE3) /* gpio2_0 gpmc_csn3.mmc2_cmd */ + 0x30 ( PIN_INPUT_PULLUP | MUX_MODE3 ) /* gpio1_12 gpmc_ad12.mmc2_dat0 */ + 0x34 ( PIN_INPUT_PULLUP | MUX_MODE3 ) /* gpio1_13 gpmc_ad13.mmc2_dat1 */ + 0x38 ( PIN_INPUT_PULLUP | MUX_MODE3 ) /* gpio1_14 gpmc_ad14.mmc2_dat2 */ + 0x3c ( PIN_INPUT_PULLUP | MUX_MODE3 ) /* gpio1_15 gpmc_ad15.mmc2_dat3 */ + >; + }; + + mmc3_pins_sleep: pinmux_mmc3_pins_sleep { + pinctrl-single,pins = < + 0x8c (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpio2_1 gpmc_clk.mmc2_clk */ + 0x88 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpio2_0 gpmc_csn3.mmc2_cmd */ + 0x30 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpio1_12 gpmc_ad12.mmc2_dat0 */ + 0x34 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpio1_13 gpmc_ad13.mmc2_dat1 */ + 0x38 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpio1_14 gpmc_ad14.mmc2_dat2 */ + 0x3c (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpio1_15 gpmc_ad15.mmc2_dat3 */ + >; + }; + + /* wl18xx card enable/irq GPIOs. */ + wlan_pins: pinmux_wlan_pins { + pinctrl-single,pins = < + 0x28 (PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpmc_ad10.gpio0_26 WL_EN*/ + 0x2C (PIN_INPUT_PULLUP | MUX_MODE7) /* gpmc_ad11.gpio0_27 WL_IRQ*/ + 0x7C (PIN_OUTPUT_PULLUP | MUX_MODE0) /* gpmc_csn0.gpio1_29 Cape_Buffer_EN*/ + >; + }; + + /* wl18xx card enable/irq GPIOs. */ + wlan_pins_sleep: pinmux_wlan_pins_sleep { + pinctrl-single,pins = < + 0x28 (PIN_OUTPUT_PULLUP | MUX_MODE7) /* gpmc_ad10.gpio0_26 WL_EN*/ + 0x2C (PIN_INPUT_PULLUP | MUX_MODE7) /* gpmc_ad11.gpio0_27 WL_IRQ*/ + 0x7C (PIN_OUTPUT_PULLUP | MUX_MODE0) /* gpmc_csn0.gpio1_29 Cape_Buffer_EN*/ + >; + }; + + uart3_pins_default: pinmux_uart3_pins_default { + pinctrl-single,pins = < + 0x134 ( PIN_INPUT_PULLUP | MUX_MODE1 ) /* (L17) gmii1_rxd3.uart3_rxd */ + 0x138 ( PIN_OUTPUT_PULLDOWN | MUX_MODE1 ) /* (L16) gmii1_rxd2.uart3_txd */ + 0x148 ( PIN_INPUT | MUX_MODE3 ) /* (M17) mdio_data.uart3_ctsn */ + 0x14c ( PIN_OUTPUT_PULLDOWN | MUX_MODE3 ) /* (M18) mdio_clk.uart3_rtsn */ + >; + }; + + uart3_pins_sleep: pinmux_uart3_pins_sleep { + pinctrl-single,pins = < + 0x134 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* (L17) gmii1_rxd3.uart3_rxd */ + 0x138 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* (L16) gmii1_rxd2.uart3_txd */ + 0x148 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* (M17) mdio_data.uart3_ctsn */ + 0x14c (PIN_INPUT_PULLDOWN | MUX_MODE7) /* (M18) mdio_clk.uart3_rtsn */ + >; + }; +}; + +&gpio0 { + wl_en { + gpio-hog; + gpios = <26 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "WL_EN"; + }; +}; + +&mmc3 { + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + status = "okay"; + //rcn-ee: with gpio-hog wlan0 is coming up over soft-reboots.. + //vmmc-supply = <&wlan_en_reg>; + vmmc-supply = <&vmmcsd_fixed>; + bus-width = <4>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&mmc3_pins &wlan_pins>; + pinctrl-1 = <&mmc3_pins_sleep &wlan_pins_sleep>; + ti,non-removable; + ti,needs-special-hs-handling; + cap-power-off-card; + keep-power-in-suspend; + + #address-cells = <1>; + #size-cells = <0>; + wlcore: wlcore@0 { + compatible = "ti,wl1835"; + reg = <2>; + interrupt-parent = <&gpio0>; + interrupts = <27 IRQ_TYPE_LEVEL_HIGH>; + }; +}; + +&uart3 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&uart3_pins_default>; + pinctrl-1 = <&uart3_pins_sleep>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/am335x-bonegreen.dts b/arch/arm/boot/dts/am335x-bonegreen.dts index dce3c86..14b74cc 100644 --- a/arch/arm/boot/dts/am335x-bonegreen.dts +++ b/arch/arm/boot/dts/am335x-bonegreen.dts @@ -9,6 +9,7 @@ #include "am33xx.dtsi" #include "am335x-bone-common.dtsi" +#include "am33xx-overlay-edma-fix.dtsi" / { model = "TI AM335x BeagleBone Green"; @@ -32,22 +33,3 @@ bus-width = <8>; status = "okay"; }; - -&am33xx_pinmux { - uart2_pins: uart2_pins { - pinctrl-single,pins = < - AM33XX_IOPAD(0x950, PIN_INPUT | MUX_MODE1) /* spi0_sclk.uart2_rxd */ - AM33XX_IOPAD(0x954, PIN_OUTPUT | MUX_MODE1) /* spi0_d0.uart2_txd */ - >; - }; -}; - -&uart2 { - pinctrl-names = "default"; - pinctrl-0 = <&uart2_pins>; - status = "okay"; -}; - -&rtc { - system-power-controller; -}; diff --git b/arch/arm/boot/dts/am335x-cape-bbb-exp-c.dtsi b/arch/arm/boot/dts/am335x-cape-bbb-exp-c.dtsi new file mode 100644 index 0000000..01f9cde --- /dev/null +++ b/arch/arm/boot/dts/am335x-cape-bbb-exp-c.dtsi @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 "am335x-peripheral-can0.dtsi" +#include "am335x-bone-pinmux-can0.dtsi" + +#include "am335x-peripheral-ttyS1.dtsi" +#include "am335x-bone-pinmux-ttyS1.dtsi" + +#include "am335x-peripheral-ttyS2.dtsi" +#include "am335x-bone-pinmux-ttyS2.dtsi" + +#include "am335x-peripheral-ttyS4.dtsi" +#include "am335x-bone-pinmux-ttyS4.dtsi" + +&am33xx_pinmux { + user_leds_s1: user_leds_s1 { + pinctrl-single,pins = < + 0x98 0x7 /* gpmc_wen.gpio2_4, OUTPUT | MODE7 */ + 0x9c 0x7 /* gpmc_ben0_cle.gpio2_5, OUTPUT | MODE7 */ + >; + }; + + bb_lcd_pwm_backlight_pins: pinmux_bb_lcd_pwm_backlight_pins { + pinctrl-single,pins = < + BONE_P9_14 (PIN_OUTPUT_PULLDOWN | MUX_MODE6) /* gpmc_a2.ehrpwm1a */ + >; + }; + + keymap3_pins: pinmux_keymap3_pins { + pinctrl-single,pins = < + 0x040 0x2f /* KEY_UP gpmc_a0.gpio1_16, INPUT | PULLDIS | MODE7 */ + 0x04c 0x2f /* KEY_DOWN gpmc_a3.gpio1_19, INPUT | PULLDIS | MODE7 */ + 0x078 0x2f /* KEY_RIGHT gpmc_ben1.gpio1_28, INPUT | PULLDIS | MODE7 */ + 0x164 0x2f /* KEY_LEFT ecap0_in_pwm0_out.gpio0_7, INPUT | PULLDIS | MODE7 */ + 0x1a4 0x2f /* KEY_ENTER mcasp0_fxr.gpio3_19, INPUT | PULLDIS | MODE7 */ + >; + }; + + edt_ft5306_ts_pins: pinmux_edt_ft5306_ts_pins { + pinctrl-single,pins = < + /* CAP_TSC gpmc_a1.gpio1_17, INPUT | MODE7 */ + BONE_P9_23 (PIN_INPUT_PULLDOWN | MUX_MODE7) + >; + }; + + i2c1_pins: pinmux_i2c1_pins { + pinctrl-single,pins = < + /* spi0_d1.i2c1_sda, SLEWCTRL_SLOW | INPUT_PULLUP | MODE2 */ + BONE_P9_18 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE2) + /* spi0_cs0.i2c1_scl, SLEWCTRL_SLOW | INPUT_PULLUP | MODE2 */ + BONE_P9_17 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE2) + >; + }; + + mcasp0_pins: pinmux_mcasp0_pins { + pinctrl-single,pins = < + 0x190 0x20 /* mcasp0_aclkx.mcasp0_aclkx, INPUT | MODE0 */ + 0x194 0x20 /* mcasp0_fsx.mcasp0_fsx, INPUT | MODE0 */ + 0x198 0x20 /* mcasp0_axr0.mcasp0_axr0, INPUT | MODE0 */ + 0x19c 0x22 /* mcasp0_ahclkr.mcasp0_axr2, INPUT | MODE2 */ + >; + }; +}; + +&epwmss1 { + status = "okay"; +}; + + +&ehrpwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_pwm_backlight_pins>; + status = "okay"; +}; + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + clock-frequency = <400000>; + + edt-ft5306@38 { + status = "okay"; + compatible = "edt,edt-ft5306", "edt,edt-ft5x06"; + pinctrl-names = "default"; + pinctrl-0 = <&edt_ft5306_ts_pins>; + + reg = <0x38>; + interrupt-parent = <&gpio1>; + interrupts = <17 0>; + + touchscreen-size-x = <1024>; + touchscreen-size-y = <600>; + }; + + tlv320aic3x: tlv320aic3x@1b { + compatible = "ti,tlv320aic3x"; + reg = <0x1b>; + status = "okay"; + }; +}; + +&mcasp0 { + pinctrl-names = "default"; + pinctrl-0 = <&mcasp0_pins>; + + status = "okay"; + + op-mode = <0>; /* MCASP_IIS_MODE */ + tdm-slots = <2>; + num-serializer = <16>; + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ + 1 0 2 0 + 0 0 0 0 + 0 0 0 0 + 0 0 0 0 + >; + tx-num-evt = <1>; + rx-num-evt = <1>; +}; + +/ { + backlight { + status = "okay"; + compatible = "pwm-backlight"; + pwms = <&ehrpwm1 0 50000 0>; + brightness-levels = <0 51 53 56 62 75 101 152 255>; + default-brightness-level = <8>; + }; + + gpio_keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&keymap3_pins>; + + #address-cells = <1>; + #size-cells = <0>; + + button@1 { + debounce_interval = <50>; + linux,code = <105>; + label = "left"; + gpios = <&gpio0 7 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button@2 { + debounce_interval = <50>; + linux,code = <106>; + label = "right"; + gpios = <&gpio1 28 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button@3 { + debounce_interval = <50>; + linux,code = <103>; + label = "up"; + gpios = <&gpio1 16 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button@4 { + debounce_interval = <50>; + linux,code = <108>; + label = "down"; + gpios = <&gpio1 19 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button@5 { + debounce_interval = <50>; + linux,code = <28>; + label = "enter"; + gpios = <&gpio3 19 0x1>; + gpio-key,wakeup; + }; + }; + + gpio-leds-cape-lcd { + compatible = "gpio-leds"; + pinctrl-names = "default"; + + pinctrl-0 = <&user_leds_s1>; + + lcd-led0 { + label = "lcd:green:usr0"; + gpios = <&gpio2 4 0>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + + lcd-led1 { + label = "lcd:green:usr1"; + gpios = <&gpio2 5 0>; + linux,default-trigger = "mmc0"; + default-state = "off"; + }; + }; + + sound { + compatible = "ti,da830-evm-audio"; + ti,model = "DA830 EVM"; + ti,audio-codec = <&tlv320aic3x>; + ti,mcasp-controller = <&mcasp0>; + ti,codec-clock-rate = <12000000>; + ti,audio-routing = + "Headphone Jack", "HPLOUT", + "Headphone Jack", "HPROUT", + "MIC3L", "Mic Jack", + "MIC3R", "Mic Jack"; + }; +}; + +#include "am335x-peripheral-panel-1024x600-24bit.dtsi" +#include "am335x-bone-pinmux-panel-1024x600-24bit.dtsi" diff --git b/arch/arm/boot/dts/am335x-cape-bbb-exp-r.dtsi b/arch/arm/boot/dts/am335x-cape-bbb-exp-r.dtsi new file mode 100644 index 0000000..539409c --- /dev/null +++ b/arch/arm/boot/dts/am335x-cape-bbb-exp-r.dtsi @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 "am335x-peripheral-can0.dtsi" +#include "am335x-bone-pinmux-can0.dtsi" + +#include "am335x-peripheral-ttyS1.dtsi" +#include "am335x-bone-pinmux-ttyS1.dtsi" + +#include "am335x-peripheral-ttyS2.dtsi" +#include "am335x-bone-pinmux-ttyS2.dtsi" + +#include "am335x-peripheral-ttyS4.dtsi" +#include "am335x-bone-pinmux-ttyS4.dtsi" + +&am33xx_pinmux { + user_leds_s1: user_leds_s1 { + pinctrl-single,pins = < + 0x98 0x7 /* gpmc_wen.gpio2_4, OUTPUT | MODE7 */ + 0x9c 0x7 /* gpmc_ben0_cle.gpio2_5, OUTPUT | MODE7 */ + >; + }; + + bb_lcd_pwm_backlight_pins: pinmux_bb_lcd_pwm_backlight_pins { + pinctrl-single,pins = < + BONE_P9_14 (PIN_OUTPUT_PULLDOWN | MUX_MODE6) /* gpmc_a2.ehrpwm1a */ + >; + }; + + keymap3_pins: pinmux_keymap3_pins { + pinctrl-single,pins = < + 0x040 0x2f /* KEY_UP gpmc_a0.gpio1_16, INPUT | PULLDIS | MODE7 */ + 0x04c 0x2f /* KEY_DOWN gpmc_a3.gpio1_19, INPUT | PULLDIS | MODE7 */ + 0x078 0x2f /* KEY_RIGHT gpmc_ben1.gpio1_28, INPUT | PULLDIS | MODE7 */ + 0x164 0x2f /* KEY_LEFT ecap0_in_pwm0_out.gpio0_7, INPUT | PULLDIS | MODE7 */ + 0x1a4 0x2f /* KEY_ENTER mcasp0_fxr.gpio3_19, INPUT | PULLDIS | MODE7 */ + >; + }; + + i2c1_pins: pinmux_i2c1_pins { + pinctrl-single,pins = < + /* spi0_d1.i2c1_sda, SLEWCTRL_SLOW | INPUT_PULLUP | MODE2 */ + BONE_P9_18 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE2) + /* spi0_cs0.i2c1_scl, SLEWCTRL_SLOW | INPUT_PULLUP | MODE2 */ + BONE_P9_17 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE2) + >; + }; + + mcasp0_pins: pinmux_mcasp0_pins { + pinctrl-single,pins = < + 0x190 0x20 /* mcasp0_aclkx.mcasp0_aclkx, INPUT | MODE0 */ + 0x194 0x20 /* mcasp0_fsx.mcasp0_fsx, INPUT | MODE0 */ + 0x198 0x20 /* mcasp0_axr0.mcasp0_axr0, INPUT | MODE0 */ + 0x19c 0x22 /* mcasp0_ahclkr.mcasp0_axr2, INPUT | MODE2 */ + >; + }; +}; + +&epwmss1 { + status = "okay"; +}; + + +&ehrpwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&bb_lcd_pwm_backlight_pins>; + status = "okay"; +}; + +&i2c1 { + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&i2c1_pins>; + clock-frequency = <400000>; + + tlv320aic3x: tlv320aic3x@1b { + compatible = "ti,tlv320aic3x"; + reg = <0x1b>; + status = "okay"; + }; +}; + +&mcasp0 { + pinctrl-names = "default"; + pinctrl-0 = <&mcasp0_pins>; + + status = "okay"; + + op-mode = <0>; /* MCASP_IIS_MODE */ + tdm-slots = <2>; + num-serializer = <16>; + serial-dir = < /* 0: INACTIVE, 1: TX, 2: RX */ + 1 0 2 0 + 0 0 0 0 + 0 0 0 0 + 0 0 0 0 + >; + tx-num-evt = <1>; + rx-num-evt = <1>; +}; + +&tscadc { + status = "okay"; + tsc { + ti,wires = <4>; + ti,x-plate-resistance = <200>; + ti,coordinate-readouts = <5>; + ti,wire-config = <0x00 0x11 0x22 0x33>; + }; + + adc { + ti,adc-channels = <4 5 6 7>; + }; +}; + +/ { + backlight { + status = "okay"; + compatible = "pwm-backlight"; + pwms = <&ehrpwm1 0 50000 0>; + brightness-levels = <0 51 53 56 62 75 101 152 255>; + default-brightness-level = <8>; + }; + + gpio_keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&keymap3_pins>; + + #address-cells = <1>; + #size-cells = <0>; + + button@1 { + debounce_interval = <50>; + linux,code = <105>; + label = "left"; + gpios = <&gpio0 7 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button@2 { + debounce_interval = <50>; + linux,code = <106>; + label = "right"; + gpios = <&gpio1 28 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button@3 { + debounce_interval = <50>; + linux,code = <103>; + label = "up"; + gpios = <&gpio1 16 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button@4 { + debounce_interval = <50>; + linux,code = <108>; + label = "down"; + gpios = <&gpio1 19 0x1>; + gpio-key,wakeup; + autorepeat; + }; + button@5 { + debounce_interval = <50>; + linux,code = <28>; + label = "enter"; + gpios = <&gpio3 19 0x1>; + gpio-key,wakeup; + }; + }; + + gpio-leds-cape-lcd { + compatible = "gpio-leds"; + pinctrl-names = "default"; + + pinctrl-0 = <&user_leds_s1>; + + lcd-led0 { + label = "lcd:green:usr0"; + gpios = <&gpio2 4 0>; + linux,default-trigger = "heartbeat"; + default-state = "off"; + }; + + lcd-led1 { + label = "lcd:green:usr1"; + gpios = <&gpio2 5 0>; + linux,default-trigger = "mmc0"; + default-state = "off"; + }; + }; + + sound { + compatible = "ti,da830-evm-audio"; + ti,model = "DA830 EVM"; + ti,audio-codec = <&tlv320aic3x>; + ti,mcasp-controller = <&mcasp0>; + ti,codec-clock-rate = <12000000>; + ti,audio-routing = + "Headphone Jack", "HPLOUT", + "Headphone Jack", "HPROUT", + "MIC3L", "Mic Jack", + "MIC3R", "Mic Jack"; + }; +}; + +#include "am335x-peripheral-panel-1024x600-24bit.dtsi" +#include "am335x-bone-pinmux-panel-1024x600-24bit.dtsi" diff --git b/arch/arm/boot/dts/am335x-cape-rtc-ds1307.dtsi b/arch/arm/boot/dts/am335x-cape-rtc-ds1307.dtsi new file mode 100644 index 0000000..bce6ac5 --- /dev/null +++ b/arch/arm/boot/dts/am335x-cape-rtc-ds1307.dtsi @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 + +&am33xx_pinmux { + i2c2_pins: pinmux_i2c2_pins { + pinctrl-single,pins = < + BONE_P9_20 0x73 /* (SLEWCTRL_SLOW | PIN_INPUT_PULLUP | MUX_MODE3) uart1_ctsn.i2c2_sda */ + BONE_P9_19 0x73 /* (SLEWCTRL_SLOW | PIN_INPUT_PULLUP | MUX_MODE3) uart1_rtsn.i2c2_scl */ + >; + }; +}; + +&i2c2 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; + + status = "okay"; + clock-frequency = <100000>; + + rtc@68 { + compatible = "maxim,ds1307"; + reg = <0x68>; + }; +}; diff --git a/arch/arm/boot/dts/am335x-evm.dts b/arch/arm/boot/dts/am335x-evm.dts index 516673b..5d28712 100644 --- a/arch/arm/boot/dts/am335x-evm.dts +++ b/arch/arm/boot/dts/am335x-evm.dts @@ -640,7 +640,7 @@ /* VDD_MPU voltage limits 0.95V - 1.26V with +/-4% tolerance */ regulator-name = "vdd_mpu"; regulator-min-microvolt = <912500>; - regulator-max-microvolt = <1312500>; + regulator-max-microvolt = <1351500>; regulator-boot-on; regulator-always-on; }; diff --git a/arch/arm/boot/dts/am335x-evmsk.dts b/arch/arm/boot/dts/am335x-evmsk.dts index 282fe1b..09308d6 100644 --- a/arch/arm/boot/dts/am335x-evmsk.dts +++ b/arch/arm/boot/dts/am335x-evmsk.dts @@ -560,7 +560,7 @@ /* VDD_MPU voltage limits 0.95V - 1.26V with +/-4% tolerance */ regulator-name = "vdd_mpu"; regulator-min-microvolt = <912500>; - regulator-max-microvolt = <1312500>; + regulator-max-microvolt = <1351500>; regulator-boot-on; regulator-always-on; }; diff --git b/arch/arm/boot/dts/am335x-olimex-som.dts b/arch/arm/boot/dts/am335x-olimex-som.dts new file mode 100644 index 0000000..2b00ad2 --- /dev/null +++ b/arch/arm/boot/dts/am335x-olimex-som.dts @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-som-common.dtsi" + +/ { + model = "Olimex AM335x SOM"; + compatible = "olimex,am335x-olimex-som", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&am33xx_pinmux { + lcd_pins_default: lcd_pins_default { + pinctrl-single,pins = < + 0x20 0x01 /* gpmc_ad8.lcd_data16, OUTPUT | MODE1 */ + 0x24 0x01 /* gpmc_ad9.lcd_data17, OUTPUT | MODE1 */ + 0x28 0x01 /* gpmc_ad10.lcd_data18, OUTPUT | MODE1 */ + 0x2c 0x01 /* gpmc_ad11.lcd_data19, OUTPUT | MODE1 */ + 0x30 0x01 /* gpmc_ad12.lcd_data20, OUTPUT | MODE1 */ + 0x34 0x01 /* gpmc_ad13.lcd_data21, OUTPUT | MODE1 */ + 0x38 0x01 /* gpmc_ad14.lcd_data22, OUTPUT | MODE1 */ + 0x3c 0x01 /* gpmc_ad15.lcd_data23, OUTPUT | MODE1 */ + 0xa0 0x00 /* lcd_data0.lcd_data0, OUTPUT | MODE0 */ + 0xa4 0x00 /* lcd_data1.lcd_data1, OUTPUT | MODE0 */ + 0xa8 0x00 /* lcd_data2.lcd_data2, OUTPUT | MODE0 */ + 0xac 0x00 /* lcd_data3.lcd_data3, OUTPUT | MODE0 */ + 0xb0 0x00 /* lcd_data4.lcd_data4, OUTPUT | MODE0 */ + 0xb4 0x00 /* lcd_data5.lcd_data5, OUTPUT | MODE0 */ + 0xb8 0x00 /* lcd_data6.lcd_data6, OUTPUT | MODE0 */ + 0xbc 0x00 /* lcd_data7.lcd_data7, OUTPUT | MODE0 */ + 0xc0 0x00 /* lcd_data8.lcd_data8, OUTPUT | MODE0 */ + 0xc4 0x00 /* lcd_data9.lcd_data9, OUTPUT | MODE0 */ + 0xc8 0x00 /* lcd_data10.lcd_data10, OUTPUT | MODE0 */ + 0xcc 0x00 /* lcd_data11.lcd_data11, OUTPUT | MODE0 */ + 0xd0 0x00 /* lcd_data12.lcd_data12, OUTPUT | MODE0 */ + 0xd4 0x00 /* lcd_data13.lcd_data13, OUTPUT | MODE0 */ + 0xd8 0x00 /* lcd_data14.lcd_data14, OUTPUT | MODE0 */ + 0xdc 0x00 /* lcd_data15.lcd_data15, OUTPUT | MODE0 */ + 0xe0 0x00 /* lcd_vsync.lcd_vsync, OUTPUT | MODE0 */ + 0xe4 0x00 /* lcd_hsync.lcd_hsync, OUTPUT | MODE0 */ + 0xe8 0x00 /* lcd_pclk.lcd_pclk, OUTPUT | MODE0 */ + 0xec 0x00 /* lcd_ac_bias_en.lcd_ac_bias_en, OUTPUT | MODE0 */ + >; + }; + + lcd_pins_sleep: lcd_pins_sleep { + pinctrl-single,pins = < + 0x20 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_ad8.lcd_data16 */ + 0x24 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_ad9.lcd_data17 */ + 0x28 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_ad10.lcd_data18 */ + 0x2c (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_ad11.lcd_data19 */ + 0x30 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_ad12.lcd_data20 */ + 0x34 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_ad13.lcd_data21 */ + 0x38 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_ad14.lcd_data22 */ + 0x3c (PIN_INPUT_PULLDOWN | MUX_MODE7) /* gpmc_ad15.lcd_data23 */ + 0xa0 (PULL_DISABLE | MUX_MODE7) /* lcd_data0.lcd_data0 */ + 0xa4 (PULL_DISABLE | MUX_MODE7) /* lcd_data1.lcd_data1 */ + 0xa8 (PULL_DISABLE | MUX_MODE7) /* lcd_data2.lcd_data2 */ + 0xac (PULL_DISABLE | MUX_MODE7) /* lcd_data3.lcd_data3 */ + 0xb0 (PULL_DISABLE | MUX_MODE7) /* lcd_data4.lcd_data4 */ + 0xb4 (PULL_DISABLE | MUX_MODE7) /* lcd_data5.lcd_data5 */ + 0xb8 (PULL_DISABLE | MUX_MODE7) /* lcd_data6.lcd_data6 */ + 0xbc (PULL_DISABLE | MUX_MODE7) /* lcd_data7.lcd_data7 */ + 0xc0 (PULL_DISABLE | MUX_MODE7) /* lcd_data8.lcd_data8 */ + 0xc4 (PULL_DISABLE | MUX_MODE7) /* lcd_data9.lcd_data9 */ + 0xc8 (PULL_DISABLE | MUX_MODE7) /* lcd_data10.lcd_data10 */ + 0xcc (PULL_DISABLE | MUX_MODE7) /* lcd_data11.lcd_data11 */ + 0xd0 (PULL_DISABLE | MUX_MODE7) /* lcd_data12.lcd_data12 */ + 0xd4 (PULL_DISABLE | MUX_MODE7) /* lcd_data13.lcd_data13 */ + 0xd8 (PULL_DISABLE | MUX_MODE7) /* lcd_data14.lcd_data14 */ + 0xdc (PULL_DISABLE | MUX_MODE7) /* lcd_data15.lcd_data15 */ + /* lcd_vsync.lcd_vsync,OUTPUT | MODE0 */ + 0xe0 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0xe4 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* lcd_hsync.lcd_hsync */ + 0xe8 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* lcd_pclk.lcd_pclk */ + /* lcd_ac_bias_en.lcd_ac_bias_en */ + 0xec (PIN_INPUT_PULLDOWN | MUX_MODE7) + >; + }; + +}; + +&lcdc { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&lcd_pins_default>; + pinctrl-1 = <&lcd_pins_sleep>; + status = "okay"; + /* display-timings { + 480x272 { + hactive = <480>; + vactive = <272>; + hback-porch = <43>; + hfront-porch = <8>; + hsync-len = <4>; + vback-porch = <12>; + vfront-porch = <4>; + vsync-len = <10>; + clock-frequency = <9000000>; + hsync-active = <0>; + vsync-active = <0>; + }; + };*/ + + display-timings { + native-mode = <&vga1024x768>; + lcd4: 480x272 { + clock-frequency = <9000000>; + hactive = <480>; + vactive = <272>; + hfront-porch = <3>; + hback-porch = <40>; + vback-porch = <8>; + vfront-porch = <7>; + hsync-len = <2>; + vsync-len = <1>; + hsync-active = <0>; + vsync-active = <0>; + }; + lcd7: 800x480 { + clock-frequency = <33300000>; + hactive = <800>; + vactive = <480>; + hfront-porch = <210>; + hback-porch = <40>; + vback-porch = <23>; + vfront-porch = <20>; + hsync-len = <6>; + vsync-len = <2>; + hsync-active = <0>; + vsync-active = <0>; + }; + lcd10: 1024x600 { + clock-frequency = <51200000>; + hactive = <1024>; + vactive = <600>; + hfront-porch = <160>; + hback-porch = <140>; + vback-porch = <20>; + vfront-porch = <12>; + hsync-len = <20>; + vsync-len = <3>; + hsync-active = <0>; + vsync-active = <0>; + }; + + vga800x600: 800x600 { + clock-frequency = <40000000>; + hactive = <800>; + vactive = <600>; + hfront-porch = <40>; + hback-porch = <88>; + vfront-porch = <1>; + vback-porch = <23>; + hsync-len = <128>; + vsync-len = <4>; + hsync-active = <0>; + vsync-active = <0>; + }; + vga1024x768: 1024x768 { + clock-frequency = <65000000>; + hactive = <1024>; + hfront-porch = <24>; + hback-porch = <160>; + hsync-len = <136>; + vactive = <768>; + vfront-porch = <3>; + vback-porch = <29>; + vsync-len = <6>; + hsync-active = <0>; + vsync-active = <0>; + }; + }; +}; diff --git b/arch/arm/boot/dts/am335x-peripheral-can0.dtsi b/arch/arm/boot/dts/am335x-peripheral-can0.dtsi new file mode 100644 index 0000000..4335e39 --- /dev/null +++ b/arch/arm/boot/dts/am335x-peripheral-can0.dtsi @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +&dcan0 { + pinctrl-names = "default"; + + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-peripheral-can1.dtsi b/arch/arm/boot/dts/am335x-peripheral-can1.dtsi new file mode 100644 index 0000000..02b5bd1 --- /dev/null +++ b/arch/arm/boot/dts/am335x-peripheral-can1.dtsi @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +&dcan1 { + pinctrl-names = "default"; + + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-peripheral-emmc.dtsi b/arch/arm/boot/dts/am335x-peripheral-emmc.dtsi new file mode 100644 index 0000000..603f34e --- /dev/null +++ b/arch/arm/boot/dts/am335x-peripheral-emmc.dtsi @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +&mmc2 { + vmmc-supply = <&vmmcsd_fixed>; + pinctrl-names = "default"; + + bus-width = <8>; + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-peripheral-i2c2.dtsi b/arch/arm/boot/dts/am335x-peripheral-i2c2.dtsi new file mode 100644 index 0000000..ed9a0b5 --- /dev/null +++ b/arch/arm/boot/dts/am335x-peripheral-i2c2.dtsi @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +&i2c2 { + pinctrl-names = "default"; + + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-peripheral-nxp-hdmi.dtsi b/arch/arm/boot/dts/am335x-peripheral-nxp-hdmi.dtsi new file mode 100644 index 0000000..1dfd26a --- /dev/null +++ b/arch/arm/boot/dts/am335x-peripheral-nxp-hdmi.dtsi @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +&lcdc { + status = "okay"; + port { + lcdc_0: endpoint@0 { + remote-endpoint = <&hdmi_0>; + }; + }; +}; + +&i2c0 { + tda19988 { + compatible = "nxp,tda998x"; + reg = <0x70>; + + port { + hdmi_0: endpoint@0 { + remote-endpoint = <&lcdc_0>; + }; + }; + }; +}; diff --git b/arch/arm/boot/dts/am335x-peripheral-panel-1024x600-24bit.dtsi b/arch/arm/boot/dts/am335x-peripheral-panel-1024x600-24bit.dtsi new file mode 100644 index 0000000..74ddc12 --- /dev/null +++ b/arch/arm/boot/dts/am335x-peripheral-panel-1024x600-24bit.dtsi @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +&lcdc { + status = "okay"; +}; + +/ { + panel { + status = "okay"; + compatible = "ti,tilcdc,panel"; + pinctrl-names = "default"; + + panel-info { + ac-bias = <255>; + ac-bias-intrpt = <0>; + dma-burst-sz = <16>; + bpp = <32>; + fdd = <0x80>; + sync-edge = <0>; + sync-ctrl = <0>; + raster-order = <1>; + fifo-th = <0>; + }; + display-timings { + native-mode = <&timing0>; + timing0: 1024x600 { + clock-frequency = <36000000>; + hactive = <1024>; + vactive = <600>; + hfront-porch = <1>; + hback-porch = <45>; + hsync-len = <30>; + vback-porch = <22>; + vfront-porch = <12>; + vsync-len = <2>; + hsync-active = <1>; + vsync-active = <1>; + de-active = <1>; + pixelclk-active = <0>; + }; + }; + }; +}; diff --git b/arch/arm/boot/dts/am335x-peripheral-spi0.dtsi b/arch/arm/boot/dts/am335x-peripheral-spi0.dtsi new file mode 100644 index 0000000..969e352 --- /dev/null +++ b/arch/arm/boot/dts/am335x-peripheral-spi0.dtsi @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +&spi0 { + pinctrl-names = "default"; + + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-peripheral-spi1.dtsi b/arch/arm/boot/dts/am335x-peripheral-spi1.dtsi new file mode 100644 index 0000000..ac5fe97 --- /dev/null +++ b/arch/arm/boot/dts/am335x-peripheral-spi1.dtsi @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +&spi1 { + pinctrl-names = "default"; + + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-peripheral-spi1a.dtsi b/arch/arm/boot/dts/am335x-peripheral-spi1a.dtsi new file mode 100644 index 0000000..ac5fe97 --- /dev/null +++ b/arch/arm/boot/dts/am335x-peripheral-spi1a.dtsi @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +&spi1 { + pinctrl-names = "default"; + + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-peripheral-ttyS1.dtsi b/arch/arm/boot/dts/am335x-peripheral-ttyS1.dtsi new file mode 100644 index 0000000..f59fa4c --- /dev/null +++ b/arch/arm/boot/dts/am335x-peripheral-ttyS1.dtsi @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +&uart1 { + pinctrl-names = "default"; + + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-peripheral-ttyS2.dtsi b/arch/arm/boot/dts/am335x-peripheral-ttyS2.dtsi new file mode 100644 index 0000000..a25d6cf --- /dev/null +++ b/arch/arm/boot/dts/am335x-peripheral-ttyS2.dtsi @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +&uart2 { + pinctrl-names = "default"; + + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-peripheral-ttyS4.dtsi b/arch/arm/boot/dts/am335x-peripheral-ttyS4.dtsi new file mode 100644 index 0000000..adc89f0 --- /dev/null +++ b/arch/arm/boot/dts/am335x-peripheral-ttyS4.dtsi @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +&uart4 { + pinctrl-names = "default"; + + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-peripheral-ttyS5.dtsi b/arch/arm/boot/dts/am335x-peripheral-ttyS5.dtsi new file mode 100644 index 0000000..8b42fb0 --- /dev/null +++ b/arch/arm/boot/dts/am335x-peripheral-ttyS5.dtsi @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +&uart5 { + pinctrl-names = "default"; + + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-sancloud-bbe.dts b/arch/arm/boot/dts/am335x-sancloud-bbe.dts new file mode 100644 index 0000000..0cb95a8 --- /dev/null +++ b/arch/arm/boot/dts/am335x-sancloud-bbe.dts @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +#include "am33xx-overlay-edma-fix.dtsi" +#include + +/ { + model = "SanCloud BeagleBone Enhanced"; + compatible = "sancloud,am335x-boneenhanced", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"; +}; + +&ldo3_reg { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; +}; + +&mmc1 { + vmmc-supply = <&vmmcsd_fixed>; +}; + +&mmc2 { + vmmc-supply = <&vmmcsd_fixed>; + pinctrl-names = "default"; + pinctrl-0 = <&emmc_pins>; + bus-width = <8>; + status = "okay"; + ti,vcc-aux-disable-is-sleep; +}; + +&am33xx_pinmux { + pinctrl-names = "default"; + pinctrl-0 = <&usb_hub_ctrl>; + + nxp_hdmi_bonelt_pins: nxp_hdmi_bonelt_pins { + pinctrl-single,pins = < + 0x1b0 0x03 /* xdma_event_intr0, OMAP_MUX_MODE3 | AM33XX_PIN_OUTPUT */ + 0xa0 0x08 /* lcd_data0.lcd_data0, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xa4 0x08 /* lcd_data1.lcd_data1, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xa8 0x08 /* lcd_data2.lcd_data2, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xac 0x08 /* lcd_data3.lcd_data3, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xb0 0x08 /* lcd_data4.lcd_data4, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xb4 0x08 /* lcd_data5.lcd_data5, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xb8 0x08 /* lcd_data6.lcd_data6, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xbc 0x08 /* lcd_data7.lcd_data7, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xc0 0x08 /* lcd_data8.lcd_data8, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xc4 0x08 /* lcd_data9.lcd_data9, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xc8 0x08 /* lcd_data10.lcd_data10, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xcc 0x08 /* lcd_data11.lcd_data11, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xd0 0x08 /* lcd_data12.lcd_data12, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xd4 0x08 /* lcd_data13.lcd_data13, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xd8 0x08 /* lcd_data14.lcd_data14, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xdc 0x08 /* lcd_data15.lcd_data15, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT | AM33XX_PULL_DISA */ + 0xe0 0x00 /* lcd_vsync.lcd_vsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */ + 0xe4 0x00 /* lcd_hsync.lcd_hsync, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */ + 0xe8 0x00 /* lcd_pclk.lcd_pclk, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */ + 0xec 0x00 /* lcd_ac_bias_en.lcd_ac_bias_en, OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT */ + >; + }; + nxp_hdmi_bonelt_off_pins: nxp_hdmi_bonelt_off_pins { + pinctrl-single,pins = < + 0x1b0 0x03 /* xdma_event_intr0, OMAP_MUX_MODE3 | AM33XX_PIN_OUTPUT */ + >; + }; + + cpsw_default: cpsw_default { + pinctrl-single,pins = < + /* Slave 1 */ + 0x114 (PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* mii1_txen.rgmii1_tctl */ + 0x118 (PIN_INPUT_PULLDOWN | MUX_MODE2) /* mii1_rxdv.rgmii1_rctl */ + 0x11c (PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* mii1_txd3.rgmii1_td3 */ + 0x120 (PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* mii1_txd2.rgmii1_td2 */ + 0x124 (PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* mii1_txd1.rgmii1_td1 */ + 0x128 (PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* mii1_txd0.rgmii1_td0 */ + 0x12c (PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* mii1_txclk.rgmii1_tclk */ + 0x130 (PIN_INPUT_PULLDOWN | MUX_MODE2) /* mii1_rxclk.rgmii1_rclk */ + 0x134 (PIN_INPUT_PULLDOWN | MUX_MODE2) /* mii1_rxd3.rgmii1_rd3 */ + 0x138 (PIN_INPUT_PULLDOWN | MUX_MODE2) /* mii1_rxd2.rgmii1_rd2 */ + 0x13c (PIN_INPUT_PULLDOWN | MUX_MODE2) /* mii1_rxd1.rgmii1_rd1 */ + 0x140 (PIN_INPUT_PULLDOWN | MUX_MODE2) /* mii1_rxd0.rgmii1_rd0 */ + >; + }; + + cpsw_sleep: cpsw_sleep { + pinctrl-single,pins = < + /* Slave 1 reset value */ + 0x114 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x118 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x11c (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x120 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x124 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x128 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x12c (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x130 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x134 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x138 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x13c (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x140 (PIN_INPUT_PULLDOWN | MUX_MODE7) + >; + }; + + davinci_mdio_default: davinci_mdio_default { + pinctrl-single,pins = < + /* MDIO */ + 0x148 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE0) /* mdio_data.mdio_data */ + 0x14c (PIN_OUTPUT_PULLUP | MUX_MODE0) /* mdio_clk.mdio_clk */ + >; + }; + + davinci_mdio_sleep: davinci_mdio_sleep { + pinctrl-single,pins = < + /* MDIO reset value */ + 0x148 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x14c (PIN_INPUT_PULLDOWN | MUX_MODE7) + >; + }; + + usb_hub_ctrl: usb_hub_ctrl { + pinctrl-single,pins = < + 0x144 (PIN_OUTPUT_PULLUP | MUX_MODE7) /* mcasp0_ahclkr.gpio3_17 */ + >; + }; + + mpu6050_pins: pinmux_mpu6050_pins { + pinctrl-single,pins = < + 0x168 (PIN_INPUT | MUX_MODE7) /* spi0_sclk.gpio0_2 */ + >; + }; + + lps3331ap_pins: pinmux_lps3331ap_pins { + pinctrl-single,pins = < + 0x6C (PIN_INPUT | MUX_MODE7) /* conf_gpmc_a11.gpio1_27 */ + >; + }; +}; + +&lcdc { + status = "okay"; + port { + lcdc_0: endpoint@0 { + remote-endpoint = <&hdmi_0>; + }; + }; +}; + +&mac { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&cpsw_default>; + pinctrl-1 = <&cpsw_sleep>; +}; + +&davinci_mdio { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&davinci_mdio_default>; + pinctrl-1 = <&davinci_mdio_sleep>; +}; + +&cpsw_emac0 { + phy_id = <&davinci_mdio>, <0>; + phy-mode = "rgmii-txid"; +}; + +&i2c0 { + tda19988 { + compatible = "nxp,tda998x"; + reg = <0x70>; + pinctrl-names = "default", "off"; + pinctrl-0 = <&nxp_hdmi_bonelt_pins>; + pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>; + + port { + hdmi_0: endpoint@0 { + remote-endpoint = <&lcdc_0>; + }; + }; + }; + + lps331ap: lps331ap@5C { + compatible = "st,lps331ap"; + st,drdy-int-pin = <1>; + reg = <0x5C>; + interrupt-parent = <&gpio1>; + interrupts = <27 IRQ_TYPE_EDGE_RISING>; + }; + + mpu6050: mpu6050@68 { + compatible = "invensense,mpu6050"; + reg = <0x68>; + interrupt-parent = <&gpio0>; + interrupts = <2 IRQ_TYPE_EDGE_RISING>; + //orientation = <0xff 0 0 0 1 0 0 0 0xff>; + }; +}; diff --git b/arch/arm/boot/dts/am335x-som-common.dtsi b/arch/arm/boot/dts/am335x-som-common.dtsi new file mode 100644 index 0000000..fb4399b --- /dev/null +++ b/arch/arm/boot/dts/am335x-som-common.dtsi @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +/ { + + cpus { + cpu@0 { + cpu0-supply = <&dcdc2_fixed>; + }; + }; + + memory { + device_type = "memory"; + reg = <0x80000000 0x20000000>; /* 512 MB */ + }; + + ocp { + uart0: serial@44e09000 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; + + status = "okay"; + }; + uart1: serial@48022000 { + pinctrl-names = "default"; + pinctrl-0 = <&uart1_pins>; + status = "okay"; + + }; + uart4: serial@481a8000 { + pinctrl-names = "default"; + pinctrl-0 = <&uart4_pins>; + status = "okay"; + }; + + epwmss0: epwmss@48300000 { + status = "okay"; + + ecap0: ecap@48300100 { + status = "okay"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&ecap0_pins_default>; + pinctrl-1 = <&ecap0_pins_sleep>; + }; + }; + + musb: usb@47400000 { + status = "okay"; + + control@44e10000 { + status = "okay"; + }; + + usb-phy@47401300 { + status = "okay"; + }; + + usb-phy@47401b00 { + status = "okay"; + }; + + usb@47401000 { + status = "okay"; + dr_mode = "otg"; + }; + + usb@47401800 { + status = "okay"; + dr_mode = "host"; + }; + + dma-controller@07402000 { + status = "okay"; + }; + }; + + i2c0: i2c@44e0b000 { + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + status = "okay"; + clock-frequency = <100000>; + + tps: tps@24 { + reg = <0x24>; + }; + }; + }; + + vmmcsd_fixed: fixedregulator@0 { + compatible = "regulator-fixed"; + regulator-name = "vmmcsd_fixed"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + dcdc2_fixed: fixedregulator@1 { + /* VDD_MPU voltage limits 0.95V - 1.325V with +/-4% tolerance */ + compatible = "regulator-fixed"; + regulator-name = "dcdc2_fixed"; + + regulator-min-microvolt = <1378000>; + regulator-max-microvolt = <1378000>; + regulator-boot-on; + regulator-always-on; + }; + + leds { + pinctrl-names = "default"; + pinctrl-0 = <&user_leds_s0>; + + compatible = "gpio-leds"; + + led@1 { + label = "led1:green:heartbeat"; + gpios = <&gpio0 19 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + + led@2 { + label = "led2:red:heartbeat"; + gpios = <&gpio3 20 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + + led@3 { + label = "led3:yello:heartbeat"; + gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + + led@4 { + label = "bkl"; + gpios = <&gpio3 19 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "default-on"; + }; + }; + + backlight { + compatible = "pwm-backlight"; + pwms = <&ecap0 0 500000 1>; + brightness-levels = < + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 33 34 35 36 37 38 39 + 40 41 42 43 44 45 46 47 48 49 + 50 51 52 53 54 55 56 57 58 59 + 60 61 62 63 64 65 66 67 68 69 + 70 71 72 73 74 75 76 77 78 79 + 80 81 82 83 84 85 86 87 88 89 + 90 91 92 93 94 95 96 97 98 99 + 100 + >; + default-brightness-level = <50>; + }; +}; + +&am33xx_pinmux { + pinctrl-names = "default"; + pinctrl-0 = <&clkout2_pin>; + + user_leds_s0: user_leds_s0 { + pinctrl-single,pins = < + 0x1b0 (PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* xdma_event_intr0.gpio0_19 */ + 0x198 (PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* mcasp0_axr0.gpio3_20 */ + 0x1a8 (PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* mcasp0_axr1.gpio3_21 */ + 0x1a4 (PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* mcasp0_fsr.gpio3[19], INPUT_PULLDOWN | MODE7 */ + >; + }; + + i2c0_pins: pinmux_i2c0_pins { + pinctrl-single,pins = < + 0x188 (PIN_INPUT_PULLUP | MUX_MODE0) /* i2c0_sda.i2c0_sda */ + 0x18c (PIN_INPUT_PULLUP | MUX_MODE0) /* i2c0_scl.i2c0_scl */ + >; + }; + + uart0_pins: pinmux_uart0_pins { + pinctrl-single,pins = < + 0x170 (PIN_INPUT_PULLUP | MUX_MODE0) /* uart0_rxd.uart0_rxd */ + 0x174 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* uart0_txd.uart0_txd */ + >; + }; + + uart1_pins: pinmux_uart1_pins { + pinctrl-single,pins = < + 0x168 (PIN_INPUT_PULLUP | MUX_MODE1) + 0x16c (PIN_OUTPUT_PULLDOWN | MUX_MODE1) + >; + }; + + uart4_pins: pinmux_uart4_pins { + pinctrl-single,pins = < + 0x180 (PIN_INPUT_PULLUP | MUX_MODE0) + 0x184 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) + >; + }; + + + + clkout2_pin: pinmux_clkout2_pin { + pinctrl-single,pins = < + 0x1b4 (PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* xdma_event_intr1.clkout2 */ + >; + }; + + cpsw_default: cpsw_default { + pinctrl-single,pins = < + /* Slave 1 */ + 0x110 (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxerr.mii1_rxerr */ + 0x114 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txen.mii1_txen */ + 0x118 (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxdv.mii1_rxdv */ + 0x11c (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd3.mii1_txd3 */ + 0x120 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd2.mii1_txd2 */ + 0x124 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd1.mii1_txd1 */ + 0x128 (PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mii1_txd0.mii1_txd0 */ + 0x12c (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_txclk.mii1_txclk */ + 0x130 (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxclk.mii1_rxclk */ + 0x134 (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd3.mii1_rxd3 */ + 0x138 (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd2.mii1_rxd2 */ + 0x13c (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd1.mii1_rxd1 */ + 0x140 (PIN_INPUT_PULLUP | MUX_MODE0) /* mii1_rxd0.mii1_rxd0 */ + + 0x040 (PIN_OUTPUT_PULLDOWN | MUX_MODE1) /* gpmc_a0.gmii2_txen, OUTPUT_PULLDOWN | MODE1 */ + 0x044 (PIN_INPUT_PULLDOWN | MUX_MODE1 ) /* gpmc_a1.gmii2_rxdv, INPUT_PULLDOWN | MODE1 */ + 0x048 (PIN_OUTPUT_PULLDOWN | MUX_MODE1) /* gpmc_a2.gmii2_txd3, OUTPUT_PULLDOWN | MODE1 */ + 0x04c (PIN_OUTPUT_PULLDOWN | MUX_MODE1) /* gpmc_a3.gmii2_txd2, OUTPUT_PULLDOWN | MODE1 */ + 0x050 (PIN_OUTPUT_PULLDOWN | MUX_MODE1) /* gpmc_a4.gmii2_txd1, OUTPUT_PULLDOWN | MODE1 */ + 0x054 (PIN_OUTPUT_PULLDOWN | MUX_MODE1) /* gpmc_a5.gmii2_txd0, OUTPUT_PULLDOWN | MODE1 */ + 0x058 (PIN_INPUT_PULLDOWN | MUX_MODE1 ) /* gpmc_a6.gmii2_txclk, INPUT_PULLDOWN | MODE1 */ + 0x05c (PIN_INPUT_PULLDOWN | MUX_MODE1 ) /* gpmc_a7.gmii2_rxclk, INPUT_PULLDOWN | MODE1 */ + 0x060 (PIN_INPUT_PULLDOWN | MUX_MODE1 ) /* gpmc_a8.gmii2_rxd3, INPUT_PULLDOWN | MODE1 */ + 0x064 (PIN_INPUT_PULLDOWN | MUX_MODE1 ) /* gpmc_a9.gmii2_rxd2, INPUT_PULLDOWN | MODE1 */ + 0x068 (PIN_INPUT_PULLDOWN | MUX_MODE1 ) /* gpmc_a10.gmii2_rxd1, INPUT_PULLDOWN | MODE1 */ + 0x06c (PIN_INPUT_PULLDOWN | MUX_MODE1 ) /* gpmc_a11.gmii2_rxd0, INPUT_PULLDOWN | MODE1 */ + 0x070 (PIN_INPUT_PULLUP | MUX_MODE1 ) /* gpmc_wait0.gmii2_crs, INPUT_PULLUP | MODE1 */ + 0x074 (PIN_INPUT_PULLUP | MUX_MODE1 ) /* gpmc_wpn.gmii2_rxer, INPUT_PULLUP | MODE1 */ + 0x078 (PIN_INPUT_PULLUP | MUX_MODE1 ) /* gpmc_ben1.gmii2_col, INPUT_PULLUP | MODE1 */ + >; + }; + + cpsw_sleep: cpsw_sleep { + pinctrl-single,pins = < + /* Slave 1 reset value */ + 0x110 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x114 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x118 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x11c (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x120 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x124 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x128 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x12c (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x130 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x134 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x138 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x13c (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x140 (PIN_INPUT_PULLDOWN | MUX_MODE7) + + 0x40 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x44 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x48 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x4c (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x50 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x54 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x58 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x5c (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x60 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x64 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x68 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x6c (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x070 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x074 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x078 (PIN_INPUT_PULLDOWN | MUX_MODE7) + >; + }; + + davinci_mdio_default: davinci_mdio_default { + pinctrl-single,pins = < + /* MDIO */ + 0x148 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE0) /* mdio_data.mdio_data */ + 0x14c (PIN_OUTPUT_PULLUP | MUX_MODE0) /* mdio_clk.mdio_clk */ + >; + }; + + davinci_mdio_sleep: davinci_mdio_sleep { + pinctrl-single,pins = < + /* MDIO reset value */ + 0x148 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x14c (PIN_INPUT_PULLDOWN | MUX_MODE7) + >; + }; + + mmc1_pins_default: pinmux_mmc1_pins { + pinctrl-single,pins = < + 0x0F0 (PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_dat3.mmc0_dat3 */ + 0x0F4 (PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_dat2.mmc0_dat2 */ + 0x0F8 (PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_dat1.mmc0_dat1 */ + 0x0FC (PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_dat0.mmc0_dat0 */ + 0x100 (PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_clk.mmc0_clk */ + 0x104 (PIN_INPUT_PULLUP | MUX_MODE0) /* mmc0_cmd.mmc0_cmd */ + 0x1A0 (PIN_INPUT_PULLUP | MUX_MODE7) /* mcasp0_aclkr.gpio3_18 */ + 0x160 (PIN_INPUT | MUX_MODE7) /* spi0_cs1.gpio0_6 */ + >; + }; + + mmc1_pins_sleep: pinmux_mmc1_pins_sleep { + pinctrl-single,pins = < + 0x0F0 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x0F4 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x0F8 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x0FC (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x100 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x104 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x1A0 (PIN_INPUT_PULLDOWN | MUX_MODE7) + 0x160 (PIN_INPUT_PULLDOWN | MUX_MODE7) + >; + }; + + emmc_pins: pinmux_emmc_pins { + pinctrl-single,pins = < + 0x80 (PIN_INPUT_PULLUP | MUX_MODE2) /* gpmc_csn1.mmc1_clk */ + 0x84 (PIN_INPUT_PULLUP | MUX_MODE2) /* gpmc_csn2.mmc1_cmd */ + 0x00 (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad0.mmc1_dat0 */ + 0x04 (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad1.mmc1_dat1 */ + 0x08 (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad2.mmc1_dat2 */ + 0x0c (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad3.mmc1_dat3 */ + 0x10 (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad4.mmc1_dat4 */ + 0x14 (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad5.mmc1_dat5 */ + 0x18 (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad6.mmc1_dat6 */ + 0x1c (PIN_INPUT_PULLUP | MUX_MODE1) /* gpmc_ad7.mmc1_dat7 */ + >; + }; + + ecap0_pins_default: backlight_pins { + pinctrl-single,pins = < + 0x164 0x0 /* eCAP0_in_PWM0_out.eCAP0_in_PWM0_out MODE0 */ + >; + }; + + ecap0_pins_sleep: ecap0_pins_sleep { + pinctrl-single,pins = < + 0x164 (PULL_DISABLE | MUX_MODE7) /* eCAP0_in_PWM0_out.eCAP0_in_PWM0_out */ + >; + }; + dcan0_default: dcan0_default_pins { + pinctrl-single,pins = < + 0x178 0x0a /* uart1_ctsn.dcan0_tx_mux2, OUTPUT | MODE2 */ + 0x17c 0x2a /* uart1_rtsn.dcan0_rx_mux2, INPUT | MODE2 */ + >; + }; + }; + +&tps { + compatible = "ti,tps65217"; + regulators { + #address-cells = <1>; + #size-cells = <0>; + + dcdc1_reg: regulator@0 { + reg = <0>; + regulator-always-on; + }; + + dcdc2_reg: regulator@1 { + reg = <1>; + /* VDD_MPU voltage limits 0.95V - 1.325V with +/-4% tolerance */ + regulator-name = "vdd_mpu"; + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <1378000>; + regulator-boot-on; + regulator-always-on; + }; + + dcdc3_reg: regulator@2 { + reg = <2>; + /* VDD_CORE voltage limits 0.95V - 1.1V with +/-4% tolerance */ + regulator-name = "vdd_core"; + regulator-min-microvolt = <925000>; + regulator-max-microvolt = <1150000>; + regulator-boot-on; + regulator-always-on; + }; + + ldo1_reg: regulator@3 { + reg = <3>; + regulator-always-on; + }; + + ldo2_reg: regulator@4 { + reg = <4>; + regulator-always-on; + }; + + ldo3_reg: regulator@5 { + reg = <5>; + regulator-always-on; + }; + + ldo4_reg: regulator@6 { + reg = <6>; + regulator-always-on; + }; + }; +}; + +&cpsw_emac0 { + phy_id = <&davinci_mdio>, <0>; + phy-mode = "mii"; +}; + +&cpsw_emac1 { + phy_id = <&davinci_mdio>, <1>; + phy-mode = "mii"; +}; + +&mac { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&cpsw_default>; + pinctrl-1 = <&cpsw_sleep>; + slaves = <2>; + dual_emac = <1>; + status = "okay"; +}; + +&davinci_mdio { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&davinci_mdio_default>; + pinctrl-1 = <&davinci_mdio_sleep>; + status = "okay"; +}; + +&mmc1 { + status = "okay"; + bus-width = <0x4>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&mmc1_pins_default>; + pinctrl-1 = <&mmc1_pins_sleep>; + cd-gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>; + cd-inverted; +}; + +&dcan0 { + pinctrl-names = "default"; + pinctrl-0 = <&dcan0_default>; + status = "okay"; +}; + +&tscadc { + status = "okay"; + tsc { + ti,wires = <4>; + ti,x-plate-resistance = <200>; + ti,coordinate-readouts = <5>; + ti,wire-config = <0x00 0x11 0x22 0x33>; + }; + + adc { + ti,adc-channels = <0 1 2 3>; + }; +}; diff --git b/arch/arm/boot/dts/am33xx-overlay-edma-fix.dtsi b/arch/arm/boot/dts/am33xx-overlay-edma-fix.dtsi new file mode 100644 index 0000000..88c8d04 --- /dev/null +++ b/arch/arm/boot/dts/am33xx-overlay-edma-fix.dtsi @@ -0,0 +1,25 @@ +/* + * Device Tree Source for AM33xx Overlay EDMA fixes + * + * Copyright (C) 2015 Robert Nelson + * + * 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. + */ + +&spi0 { + status = "okay"; +}; + +&spi1 { + status = "okay"; +}; + +&mcasp0 { + status = "okay"; +}; + +&mcasp1 { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 201bccb..f2a437c 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -45,19 +45,9 @@ device_type = "cpu"; reg = <0>; - /* - * To consider voltage drop between PMIC and SoC, - * tolerance value is reduced to 2% from 4% and - * voltage value is increased as a precaution. - */ - operating-points = < - /* kHz uV */ - 720000 1285000 - 600000 1225000 - 500000 1125000 - 275000 1125000 - >; - voltage-tolerance = <2>; /* 2 percentage */ + operating-points-v2 = <&cpu0_opp_table>; + ti,syscon-efuse = <&scm_conf 0x7fc 0x1fff 0>; + ti,syscon-rev = <&scm_conf 0x600>; clocks = <&dpll_mpu_ck>; clock-names = "cpu"; @@ -66,6 +56,78 @@ }; }; + cpu0_opp_table: opp_table0 { + compatible = "operating-points-v2"; + + /* + * The three following nodes are marked with opp-suspend + * because the can not be enabled simultaneously on a + * single SoC. + */ + opp50@300000000 { + opp-hz = /bits/ 64 <300000000>; + opp-microvolt = <950000 931000 969000>; + opp-supported-hw = <0x06 0x0010>; + opp-suspend; + }; + + opp100@275000000 { + opp-hz = /bits/ 64 <275000000>; + opp-microvolt = <1100000 1078000 1122000>; + opp-supported-hw = <0x01 0x00FF>; + opp-suspend; + }; + + opp100@300000000 { + opp-hz = /bits/ 64 <300000000>; + opp-microvolt = <1100000 1078000 1122000>; + opp-supported-hw = <0x06 0x0020>; + opp-suspend; + }; + + opp100@500000000 { + opp-hz = /bits/ 64 <500000000>; + opp-microvolt = <1100000 1078000 1122000>; + opp-supported-hw = <0x01 0xFFFF>; + }; + + opp100@600000000 { + opp-hz = /bits/ 64 <600000000>; + opp-microvolt = <1100000 1078000 1122000>; + opp-supported-hw = <0x06 0x0040>; + }; + + opp120@600000000 { + opp-hz = /bits/ 64 <600000000>; + opp-microvolt = <1200000 1176000 1224000>; + opp-supported-hw = <0x01 0xFFFF>; + }; + + opp120@720000000 { + opp-hz = /bits/ 64 <720000000>; + opp-microvolt = <1200000 1176000 1224000>; + opp-supported-hw = <0x06 0x0080>; + }; + + oppturbo@720000000 { + opp-hz = /bits/ 64 <720000000>; + opp-microvolt = <1260000 1234800 1285200>; + opp-supported-hw = <0x01 0xFFFF>; + }; + + oppturbo@800000000 { + opp-hz = /bits/ 64 <800000000>; + opp-microvolt = <1260000 1234800 1285200>; + opp-supported-hw = <0x06 0x0100>; + }; + + oppnitro@1000000000 { + opp-hz = /bits/ 64 <1000000000>; + opp-microvolt = <1325000 1298500 1351500>; + opp-supported-hw = <0x04 0x0200>; + }; + }; + pmu { compatible = "arm,cortex-a8-pmu"; interrupts = <3>; @@ -90,7 +152,7 @@ * for the moment, just use a fake OCP bus entry to represent * the whole bus hierarchy. */ - ocp { + ocp: ocp { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; @@ -499,6 +561,17 @@ ti,timer-pwm; }; + pruss: pruss@4a300000 { + compatible = "ti,pruss-v2"; + ti,hwmods = "pruss"; + ti,deassert-hard-reset = "pruss", "pruss"; + reg = <0x4a300000 0x080000>; + ti,pintc-offset = <0x20000>; + interrupt-parent = <&intc>; + status = "disabled"; + interrupts = <20 21 22 23 24 25 26 27>; + }; + rtc: rtc@44e3e000 { compatible = "ti,am3352-rtc", "ti,da830-rtc"; reg = <0x44e3e000 0x1000>; @@ -695,6 +768,15 @@ ti,hwmods = "ehrpwm0"; status = "disabled"; }; + + eqep0: eqep@0x48300180 { + compatible = "ti,am33xx-eqep"; + reg = <0x48300180 0x80>; + interrupt-parent = <&intc>; + interrupts = <79>; + ti,hwmods = "eqep0"; + status = "disabled"; + }; }; epwmss1: epwmss@48302000 { @@ -725,6 +807,15 @@ ti,hwmods = "ehrpwm1"; status = "disabled"; }; + + eqep1: eqep@0x48302180 { + compatible = "ti,am33xx-eqep"; + reg = <0x48302180 0x80>; + interrupt-parent = <&intc>; + interrupts = <88>; + ti,hwmods = "eqep1"; + status = "disabled"; + }; }; epwmss2: epwmss@48304000 { @@ -755,6 +846,15 @@ ti,hwmods = "ehrpwm2"; status = "disabled"; }; + + eqep2: eqep@0x48304180 { + compatible = "ti,am33xx-eqep"; + reg = <0x48304180 0x80>; + interrupt-parent = <&intc>; + interrupts = <89>; + ti,hwmods = "eqep2"; + status = "disabled"; + }; }; mac: ethernet@4a100000 { diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi index e0962f4..9605144 100644 --- a/arch/arm/boot/dts/am4372.dtsi +++ b/arch/arm/boot/dts/am4372.dtsi @@ -44,10 +44,49 @@ clocks = <&dpll_mpu_ck>; clock-names = "cpu"; + operating-points-v2 = <&cpu0_opp_table>; + ti,syscon-efuse = <&scm_conf 0x610 0x3f 0>; + ti,syscon-rev = <&scm_conf 0x600>; + clock-latency = <300000>; /* From omap-cpufreq driver */ }; }; + cpu0_opp_table: opp_table0 { + compatible = "operating-points-v2"; + + opp50@300000000 { + opp-hz = /bits/ 64 <300000000>; + opp-microvolt = <950000 931000 969000>; + opp-supported-hw = <0xFF 0x01>; + opp-suspend; + }; + + opp100@600000000 { + opp-hz = /bits/ 64 <600000000>; + opp-microvolt = <1100000 1078000 1122000>; + opp-supported-hw = <0xFF 0x04>; + }; + + opp120@720000000 { + opp-hz = /bits/ 64 <720000000>; + opp-microvolt = <1200000 1176000 1224000>; + opp-supported-hw = <0xFF 0x08>; + }; + + oppturbo@800000000 { + opp-hz = /bits/ 64 <800000000>; + opp-microvolt = <1260000 1234800 1285200>; + opp-supported-hw = <0xFF 0x10>; + }; + + oppnitro@1000000000 { + opp-hz = /bits/ 64 <1000000000>; + opp-microvolt = <1325000 1298500 1351500>; + opp-supported-hw = <0xFF 0x20>; + }; + }; + gic: interrupt-controller@48241000 { compatible = "arm,cortex-a9-gic"; interrupt-controller; diff --git a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts index 5bcd3aa..84832bf 100644 --- a/arch/arm/boot/dts/am437x-gp-evm.dts +++ b/arch/arm/boot/dts/am437x-gp-evm.dts @@ -975,3 +975,7 @@ clock-names = "ext-clk", "int-clk"; status = "okay"; }; + +&cpu { + cpu0-supply = <&dcdc2>; +}; diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi index 3a8f397..834599e 100644 --- a/arch/arm/boot/dts/dra7.dtsi +++ b/arch/arm/boot/dts/dra7.dtsi @@ -73,6 +73,49 @@ interrupt-parent = <&gic>; }; + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a15"; + reg = <0>; + + operating-points-v2 = <&cpu0_opp_table>; + ti,syscon-efuse = <&scm_wkup 0x20c 0xf80000 19>; + ti,syscon-rev = <&scm_wkup 0x204>; + + clocks = <&dpll_mpu_ck>; + clock-names = "cpu"; + + clock-latency = <300000>; /* From omap-cpufreq driver */ + + /* cooling options */ + cooling-min-level = <0>; + cooling-max-level = <2>; + #cooling-cells = <2>; /* min followed by max */ + }; + }; + + cpu0_opp_table: opp_table0 { + compatible = "operating-points-v2"; + opp-shared; + + opp_nom@1000000000 { + opp-hz = /bits/ 64 <1000000000>; + opp-microvolt = <1060000 850000 1150000>; + opp-supported-hw = <0xFF 0x01>; + opp-suspend; + }; + + opp_od@1176000000 { + opp-hz = /bits/ 64 <1176000000>; + opp-microvolt = <1160000 885000 1160000>; + opp-supported-hw = <0xFF 0x02>; + }; + }; + /* * The soc node represents the soc top level view. It is used for IPs * that are not memory mapped in the MPU view or for the MPU itself. @@ -233,6 +276,11 @@ prm_clockdomains: clockdomains { }; }; + + scm_wkup: scm_conf@c000 { + compatible = "syscon"; + reg = <0xc000 0x1000>; + }; }; axi@0 { @@ -321,6 +369,13 @@ reg = <0x40d00000 0x100>; }; + dra7_iodelay_core: padconf@4844a000 { + compatible = "ti,dra7-iodelay"; + reg = <0x4844a000 0x0d1c>; + #address-cells = <1>; + #size-cells = <0>; + }; + sdma: dma-controller@4a056000 { compatible = "ti,omap4430-sdma"; reg = <0x4a056000 0x1000>; diff --git a/arch/arm/boot/dts/dra72x.dtsi b/arch/arm/boot/dts/dra72x.dtsi index 70a2170..6710760 100644 --- a/arch/arm/boot/dts/dra72x.dtsi +++ b/arch/arm/boot/dts/dra72x.dtsi @@ -12,22 +12,6 @@ / { compatible = "ti,dra722", "ti,dra72", "ti,dra7"; - cpus { - #address-cells = <1>; - #size-cells = <0>; - - cpu0: cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a15"; - reg = <0>; - - /* cooling options */ - cooling-min-level = <0>; - cooling-max-level = <2>; - #cooling-cells = <2>; /* min followed by max */ - }; - }; - pmu { compatible = "arm,cortex-a15-pmu"; interrupt-parent = <&wakeupgen>; diff --git a/arch/arm/boot/dts/dra74x.dtsi b/arch/arm/boot/dts/dra74x.dtsi index 5e06020..8987b3e 100644 --- a/arch/arm/boot/dts/dra74x.dtsi +++ b/arch/arm/boot/dts/dra74x.dtsi @@ -13,34 +13,11 @@ compatible = "ti,dra742", "ti,dra74", "ti,dra7"; cpus { - #address-cells = <1>; - #size-cells = <0>; - - cpu0: cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a15"; - reg = <0>; - - operating-points = < - /* kHz uV */ - 1000000 1060000 - 1176000 1160000 - >; - - clocks = <&dpll_mpu_ck>; - clock-names = "cpu"; - - clock-latency = <300000>; /* From omap-cpufreq driver */ - - /* cooling options */ - cooling-min-level = <0>; - cooling-max-level = <2>; - #cooling-cells = <2>; /* min followed by max */ - }; cpu@1 { device_type = "cpu"; compatible = "arm,cortex-a15"; reg = <1>; + operating-points-v2 = <&cpu0_opp_table>; }; }; diff --git b/arch/arm/boot/dts/exynos5422-artik10-eval.dts b/arch/arm/boot/dts/exynos5422-artik10-eval.dts new file mode 100644 index 0000000..15a9e1b --- /dev/null +++ b/arch/arm/boot/dts/exynos5422-artik10-eval.dts @@ -0,0 +1,262 @@ +/* + * SAMSUNG ARTIK10 board device tree source + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * 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. +*/ + +/dts-v1/; + +#include +#include +#include +#include +#include "exynos5800.dtsi" +#include "exynos5422-cpus.dtsi" +/* #include "exynos5422-cpu-thermal.dtsi" */ + +/ { + model = "Samsung ARTIK10 board based on EXYNOS5422"; + compatible = "samsung,artik10", "samsung,exynos5422", "samsung,exynos5"; + + memory { + reg = <0x40000000 0x80000000>; + }; + + chosen { + linux,stdout-path = &serial_2; + }; + + firmware@02073000 { + compatible = "samsung,secure-firmware"; + reg = <0x02073000 0x1000>; + }; + + fixed-rate-clocks { + oscclk { + compatible = "samsung,exynos5420-oscclk"; + clock-frequency = <24000000>; + }; + }; + + rtc { + status = "okay"; + }; +}; + +&cpu0 { + cpu-supply = <&buck6_reg>; +}; + +&cpu4 { + cpu-supply = <&buck2_reg>; +}; + +&pinctrl_0 { + s2mps11_irq: s2mps11-irq { + samsung,pins = "gpx3-2"; + samsung,pin-pud = <3>; + samsung,pin-drv = <3>; + }; +}; + +&hsi2c_4 { + clock-frequency = <400000>; + status = "okay"; + + s2mps11_pmic@66 { + compatible = "samsung,s2mps11-pmic"; + interrupt-parent = <&gpx3>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; + reg = <0x66>; + + + pinctrl-names = "default"; + pinctrl-0 = <&s2mps11_irq>; + + s2mps11_osc: clocks { + #clock-cells = <1>; + clock-output-names = "s2mps11_ap", + "s2mps11_cp", "s2mps11_bt"; + }; + + regulators { + ldo4_reg: LDO4 { + regulator-name = "vdd_adc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo6_reg: LDO6 { + regulator-name = "vdd_ldo6"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + regulator-always-on; + }; + + ldo7_reg: LDO7 { + regulator-name = "vdd_ldo7"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo13_reg: LDO13 { + regulator-name = "vqmmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + ldo17_reg: LDO17 { + regulator-name = "vdd_ldo17"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + + ldo18_reg: LDO18 { + regulator-name = "vdd_ldo18"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo19_reg: LDO19 { + regulator-name = "vmmc"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo20_reg: LDO20 { + regulator-name = "vdd_ldo20"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo23_reg: LDO23 { + regulator-name = "vdd_mifs"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + ldo24_reg: LDO24 { + regulator-name = "vdd_ldo24"; + regulator-min-microvolt = <2400000>; + regulator-max-microvolt = <2400000>; + }; + + ldo25_reg: LDO25 { + regulator-name = "vdd_ldo25"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo27_reg: LDO27 { + regulator-name = "vdd_g3ds"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1100000>; + regulator-always-on; + }; + + ldo28_reg: LDO28 { + regulator-name = "vdd_ldo28"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + + ldo32_reg: LDO32 { + regulator-name = "vdd_lcd_1v8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + ldo38_reg: LDO38 { + regulator-name = "vdd_ldo38"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-always-on; + }; + + buck1_reg: BUCK1 { + regulator-name = "vdd_mif"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1300000>; + regulator-always-on; + }; + + buck2_reg: BUCK2 { + regulator-name = "vdd_eagle"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + }; + + buck3_reg: BUCK3 { + regulator-name = "vdd_int"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1400000>; + regulator-always-on; + }; + + buck4_reg: BUCK4 { + regulator-name = "vdd_g3d"; + regulator-min-microvolt = <700000>; + regulator-max-microvolt = <1400000>; + regulator-always-on; + }; + + buck6_reg: BUCK6 { + regulator-name = "vdd_kfc"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + }; + + buck10_reg: BUCK10 { + regulator-name = "vdd_cam_isp_1.0v"; + regulator-min-microvolt = <750000>; + regulator-max-microvolt = <1500000>; + regulator-always-on; + }; + }; + }; +}; + + +&mmc_0 { + status = "okay"; + broken-cd; + card-detect-delay = <200>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <0 4>; + samsung,dw-mshc-ddr-timing = <0 2>; + samsung,dw-mshc-hs400-timing = <0 2>; + samsung,read-strobe-delay = <90>; + pinctrl-names = "default"; + pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus1 &sd0_bus4 &sd0_bus8 + &sd0_rclk>; + bus-width = <8>; + cap-mmc-highspeed; + keep-power-in-suspend; + non-removable; +}; + +&mmc_2 { + status = "okay"; + card-detect-delay = <200>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <2 3>; + samsung,dw-mshc-ddr-timing = <1 2>; + pinctrl-names = "default"; + pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_bus1 &sd2_bus4>; + bus-width = <4>; + cap-sd-highspeed; + vmmc-supply = <&ldo28_reg>; +}; diff --git b/arch/arm/boot/dts/imx6dl-sabresd-wl1835.dts b/arch/arm/boot/dts/imx6dl-sabresd-wl1835.dts new file mode 100644 index 0000000..37721c1 --- /dev/null +++ b/arch/arm/boot/dts/imx6dl-sabresd-wl1835.dts @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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 as + * published by the Free Software Foundation. + */ + +/dts-v1/; + +#include "imx6dl.dtsi" +#include "imx6qdl-sabresd-wl1835.dtsi" + +/ { + model = "Freescale i.MX6 DualLite SABRE Smart Device Board"; + compatible = "fsl,imx6dl-sabresd", "fsl,imx6dl"; +}; diff --git b/arch/arm/boot/dts/imx6q-ccimx6sbc.dts b/arch/arm/boot/dts/imx6q-ccimx6sbc.dts new file mode 100644 index 0000000..d249240 --- /dev/null +++ b/arch/arm/boot/dts/imx6q-ccimx6sbc.dts @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2015 Robert Nelson (robertcnelson@gmail.com) + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file 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. + * + * This file 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. + * + * Or, alternatively + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +/dts-v1/; + +#include "imx6q.dtsi" +#include + +/ { + model = "Digi ConnectCore-i.MX6 SBC Board"; + compatible = "digi,connectcore/q", "fsl,imx6q"; + + chosen { + stdout-path = &uart4; + }; + + memory { + reg = <0x10000000 0x40000000>; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + reg_usbh1_reset: regulator@1 { + compatible = "regulator-fixed"; + reg = <1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbh1>; + regulator-name = "usbh1_reset"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio3 10 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + reg_usb_otg_vbus: regulator@2 { + compatible = "regulator-fixed"; + reg = <2>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg>; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio3 22 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + }; +}; + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet>; + phy-mode = "rgmii"; + phy-reset-gpios = <&gpio1 25 GPIO_ACTIVE_LOW>; + status = "okay"; +}; + +&hdmi { + ddc-i2c-bus = <&i2c3>; + status = "okay"; +}; + +&i2c2 { + clock-frequency = <400000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; + status = "okay"; + + pmic@58 { + compatible = "dlg,da9063"; + reg = <0x58>; + interrupt-parent = <&gpio1>; + interrupts = <17 0x8>; /* active-low GPIO0_17 */ + + regulators { + vdd_3v3_reg: bperi { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + ldo3_reg: ldo3 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + ldo4_reg: ldo4 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + ldo6_reg: ldo6 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + ldo7_reg: ldo7 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + ldo8_reg: ldo8 { + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + }; +}; + +&i2c3 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; + status = "okay"; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + imx6qdl-ccimx6sbc { + pinctrl_hog: hoggrp { + fsl,pins = < + /* da9063*/ + MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 + >; + }; + + pinctrl_enet: enetgrp { + fsl,pins = < + MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x100b0 + MX6QDL_PAD_ENET_MDC__ENET_MDC 0x100b0 + MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x100b0 + MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x100b0 + MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x100b0 + MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x100b0 + MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x100b0 + MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x100b0 + MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x100b0 + MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 + MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 + MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 + MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 + MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 + MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 + /* Phy reset */ + MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x000b0 + >; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 + >; + }; + + pinctrl_i2c3: i2c3grp { + fsl,pins = < + MX6QDL_PAD_GPIO_5__I2C3_SCL 0x4001b8b1 + MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 + >; + }; + + pinctrl_uart4: uart4grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 + MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 + >; + }; + + pinctrl_usbh1: usbh1grp { + fsl,pins = < + /* need to force low for hub reset */ + MX6QDL_PAD_EIM_DA10__GPIO3_IO10 0x10b0 + >; + }; + + pinctrl_usbotg: usbotggrp { + fsl,pins = < + MX6QDL_PAD_GPIO_1__USB_OTG_ID 0x17059 + MX6QDL_PAD_EIM_D21__USB_OTG_OC 0x1b0b0 + /* power enable, high active */ + MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x10b0 + >; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059 + >; + }; + + pinctrl_usdhc4: usdhc4grp { + fsl,pins = < + MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 + MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 + MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 + MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 + MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 + MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 + MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059 + MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059 + MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059 + MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059 + >; + }; + }; +}; + +&sata { + status = "okay"; +}; + +&ssi1 { + status = "okay"; +}; + +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4>; + status = "okay"; +}; + +&usbh1 { + vbus-supply = <®_usbh1_reset>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbh1>; + status = "okay"; +}; + +&usbotg { + vbus-supply = <®_usb_otg_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg>; + status = "okay"; +}; + +&usdhc2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc2>; + bus-width = <4>; + broken-cd; /* cd & wp, is not wired up on this board */ + status = "okay"; +}; + +&usdhc4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc4>; + bus-width = <8>; + non-removable; + status = "okay"; +}; diff --git b/arch/arm/boot/dts/imx6q-sabresd-wl1835.dts b/arch/arm/boot/dts/imx6q-sabresd-wl1835.dts new file mode 100644 index 0000000..d88d9e2 --- /dev/null +++ b/arch/arm/boot/dts/imx6q-sabresd-wl1835.dts @@ -0,0 +1,25 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +/dts-v1/; + +#include "imx6q.dtsi" +#include "imx6qdl-sabresd-wl1835.dtsi" + +/ { + model = "Freescale i.MX6 Quad SABRE Smart Device Board"; + compatible = "fsl,imx6q-sabresd", "fsl,imx6q"; +}; + +&sata { + status = "okay"; +}; diff --git b/arch/arm/boot/dts/imx6qdl-sabresd-wl1835.dtsi b/arch/arm/boot/dts/imx6qdl-sabresd-wl1835.dtsi new file mode 100644 index 0000000..862dfbd --- /dev/null +++ b/arch/arm/boot/dts/imx6qdl-sabresd-wl1835.dtsi @@ -0,0 +1,646 @@ +/* Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include + +/ { + chosen { + stdout-path = &uart1; + }; + + memory { + reg = <0x10000000 0x40000000>; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + reg_usb_otg_vbus: regulator@0 { + compatible = "regulator-fixed"; + reg = <0>; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio3 22 0>; + enable-active-high; + vin-supply = <&swbst_reg>; + }; + + reg_usb_h1_vbus: regulator@1 { + compatible = "regulator-fixed"; + reg = <1>; + regulator-name = "usb_h1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio1 29 0>; + enable-active-high; + vin-supply = <&swbst_reg>; + }; + + reg_audio: regulator@2 { + compatible = "regulator-fixed"; + reg = <2>; + regulator-name = "wm8962-supply"; + gpio = <&gpio4 10 0>; + enable-active-high; + }; + + reg_pcie: regulator@3 { + compatible = "regulator-fixed"; + reg = <3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pcie_reg>; + regulator-name = "MPCIE_3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio3 19 0>; + regulator-always-on; + enable-active-high; + }; + + wlan_en_reg: regulator@4 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + /* WLAN_EN GPIO for this board – Bank4, pin7 */ + gpio = <&gpio4 7 0>; + enable-active-high; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio_keys>; + + power { + label = "Power Button"; + gpios = <&gpio3 29 GPIO_ACTIVE_LOW>; + gpio-key,wakeup; + linux,code = ; + }; + + volume-up { + label = "Volume Up"; + gpios = <&gpio1 4 GPIO_ACTIVE_LOW>; + gpio-key,wakeup; + linux,code = ; + }; + + volume-down { + label = "Volume Down"; + gpios = <&gpio1 5 GPIO_ACTIVE_LOW>; + gpio-key,wakeup; + linux,code = ; + }; + }; + + sound { + compatible = "fsl,imx6q-sabresd-wm8962", + "fsl,imx-audio-wm8962"; + model = "wm8962-audio"; + ssi-controller = <&ssi2>; + audio-codec = <&codec>; + audio-routing = + "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "Ext Spk", "SPKOUTL", + "Ext Spk", "SPKOUTR", + "AMIC", "MICBIAS", + "IN3R", "AMIC"; + mux-int-port = <2>; + mux-ext-port = <3>; + }; + + backlight { + compatible = "pwm-backlight"; + pwms = <&pwm1 0 5000000>; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <7>; + status = "okay"; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_gpio_leds>; + +/* red_led gpio gpio1_2 is used for bt_enable + red { + gpios = <&gpio1 2 0>; + default-state = "on"; + }; +*/ + }; + + kim { + compatible = "kim"; + nshutdown_gpio = <2>; + dev_name = "/dev/ttymxc4"; + flow_cntrl = <1>; + baud_rate = <3000000>; + }; + + btwilink { + compatible = "btwilink"; + }; +}; + +&audmux { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_audmux>; + status = "okay"; +}; + +/* +&ecspi1 { + fsl,spi-num-chipselects = <1>; + cs-gpios = <&gpio4 9 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1>; + status = "okay"; + + flash: m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,m25p32"; + spi-max-frequency = <20000000>; + reg = <0>; + }; +}; +*/ + +&fec { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet>; + phy-mode = "rgmii"; + phy-reset-gpios = <&gpio1 25 0>; + status = "okay"; +}; + +&hdmi { + ddc-i2c-bus = <&i2c2>; + status = "okay"; +}; + +&i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; + + codec: wm8962@1a { + compatible = "wlf,wm8962"; + reg = <0x1a>; + clocks = <&clks IMX6QDL_CLK_CKO>; + DCVDD-supply = <®_audio>; + DBVDD-supply = <®_audio>; + AVDD-supply = <®_audio>; + CPVDD-supply = <®_audio>; + MICVDD-supply = <®_audio>; + PLLVDD-supply = <®_audio>; + SPKVDD1-supply = <®_audio>; + SPKVDD2-supply = <®_audio>; + gpio-cfg = < + 0x0000 /* 0:Default */ + 0x0000 /* 1:Default */ + 0x0013 /* 2:FN_DMICCLK */ + 0x0000 /* 3:Default */ + 0x8014 /* 4:FN_DMICCDAT */ + 0x0000 /* 5:Default */ + >; + }; +}; + +&i2c2 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; + status = "okay"; + + pmic: pfuze100@08 { + compatible = "fsl,pfuze100"; + reg = <0x08>; + + regulators { + sw1a_reg: sw1ab { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw1c_reg: sw1c { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw2_reg: sw2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3a_reg: sw3a { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3b_reg: sw3b { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw4_reg: sw4 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + }; + + swbst_reg: swbst { + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5150000>; + }; + + snvs_reg: vsnvs { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + }; + + vref_reg: vrefddr { + regulator-boot-on; + regulator-always-on; + }; + + vgen1_reg: vgen1 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen2_reg: vgen2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen3_reg: vgen3 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + vgen4_reg: vgen4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen5_reg: vgen5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen6_reg: vgen6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + }; +}; + +&i2c3 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c3>; + status = "okay"; + + egalax_ts@04 { + compatible = "eeti,egalax_ts"; + reg = <0x04>; + interrupt-parent = <&gpio6>; + interrupts = <7 2>; + wakeup-gpios = <&gpio6 7 0>; + }; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + imx6qdl-sabresd { + pinctrl_hog: hoggrp { + fsl,pins = < + MX6QDL_PAD_NANDF_D0__GPIO2_IO00 0x1b0b0 + MX6QDL_PAD_NANDF_D1__GPIO2_IO01 0x1b0b0 + MX6QDL_PAD_NANDF_D2__GPIO2_IO02 0x1b0b0 + MX6QDL_PAD_NANDF_D3__GPIO2_IO03 0x1b0b0 + MX6QDL_PAD_GPIO_0__CCM_CLKO1 0x130b0 + MX6QDL_PAD_NANDF_CLE__GPIO6_IO07 0x1b0b0 + MX6QDL_PAD_ENET_TXD1__GPIO1_IO29 0x1b0b0 + MX6QDL_PAD_EIM_D22__GPIO3_IO22 0x1b0b0 + MX6QDL_PAD_ENET_CRS_DV__GPIO1_IO25 0x1b0b0 + MX6QDL_PAD_KEY_ROW0__GPIO4_IO07 0x13059 // reserve two pins wl8 gpio, this is pulled low at reset for WL_EN + MX6QDL_PAD_KEY_COL0__GPIO4_IO06 0x13059 // this is for WL_IRQ which driver will configure as an input with a pull down + >; + }; + + pinctrl_audmux: audmuxgrp { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT7__AUD3_RXD 0x130b0 + MX6QDL_PAD_CSI0_DAT4__AUD3_TXC 0x130b0 + MX6QDL_PAD_CSI0_DAT5__AUD3_TXD 0x110b0 + MX6QDL_PAD_CSI0_DAT6__AUD3_TXFS 0x130b0 + >; + }; + + pinctrl_ecspi1: ecspi1grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL1__ECSPI1_MISO 0x100b1 + MX6QDL_PAD_KEY_ROW0__ECSPI1_MOSI 0x100b1 + MX6QDL_PAD_KEY_COL0__ECSPI1_SCLK 0x100b1 + MX6QDL_PAD_KEY_ROW1__GPIO4_IO09 0x1b0b0 + >; + }; + + pinctrl_enet: enetgrp { + fsl,pins = < + MX6QDL_PAD_ENET_MDIO__ENET_MDIO 0x1b0b0 + MX6QDL_PAD_ENET_MDC__ENET_MDC 0x1b0b0 + MX6QDL_PAD_RGMII_TXC__RGMII_TXC 0x1b0b0 + MX6QDL_PAD_RGMII_TD0__RGMII_TD0 0x1b0b0 + MX6QDL_PAD_RGMII_TD1__RGMII_TD1 0x1b0b0 + MX6QDL_PAD_RGMII_TD2__RGMII_TD2 0x1b0b0 + MX6QDL_PAD_RGMII_TD3__RGMII_TD3 0x1b0b0 + MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b0b0 + MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK 0x1b0b0 + MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 + MX6QDL_PAD_RGMII_RD0__RGMII_RD0 0x1b0b0 + MX6QDL_PAD_RGMII_RD1__RGMII_RD1 0x1b0b0 + MX6QDL_PAD_RGMII_RD2__RGMII_RD2 0x1b0b0 + MX6QDL_PAD_RGMII_RD3__RGMII_RD3 0x1b0b0 + MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0 + MX6QDL_PAD_GPIO_16__ENET_REF_CLK 0x4001b0a8 + >; + }; + + pinctrl_gpio_keys: gpio_keysgrp { + fsl,pins = < + MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x1b0b0 + MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x1b0b0 + MX6QDL_PAD_GPIO_5__GPIO1_IO05 0x1b0b0 + >; + }; + + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT8__I2C1_SDA 0x4001b8b1 + MX6QDL_PAD_CSI0_DAT9__I2C1_SCL 0x4001b8b1 + >; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL3__I2C2_SCL 0x4001b8b1 + MX6QDL_PAD_KEY_ROW3__I2C2_SDA 0x4001b8b1 + >; + }; + + pinctrl_i2c3: i2c3grp { + fsl,pins = < + MX6QDL_PAD_GPIO_3__I2C3_SCL 0x4001b8b1 + MX6QDL_PAD_GPIO_6__I2C3_SDA 0x4001b8b1 + >; + }; + + pinctrl_pcie: pciegrp { + fsl,pins = < + MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x1b0b0 + >; + }; + + pinctrl_pcie_reg: pciereggrp { + fsl,pins = < + MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x1b0b0 + >; + }; + + pinctrl_pwm1: pwm1grp { + fsl,pins = < + MX6QDL_PAD_SD1_DAT3__PWM1_OUT 0x1b0b1 + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 + MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 + >; + }; + + pinctrl_uart5: uart5grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL1__UART5_TX_DATA 0x1b0b1 + MX6QDL_PAD_KEY_ROW1__UART5_RX_DATA 0x1b0b1 + MX6QDL_PAD_KEY_ROW4__UART5_CTS_B 0x1b0b1 + MX6QDL_PAD_KEY_COL4__UART5_RTS_B 0x1b0b1 + >; + }; + + pinctrl_usbotg: usbotggrp { + fsl,pins = < + MX6QDL_PAD_ENET_RX_ER__USB_OTG_ID 0x17059 + >; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + MX6QDL_PAD_SD2_CMD__SD2_CMD 0x17059 + MX6QDL_PAD_SD2_CLK__SD2_CLK 0x10059 + MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059 + MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059 + MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059 + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059 + MX6QDL_PAD_NANDF_D4__SD2_DATA4 0x17059 + MX6QDL_PAD_NANDF_D5__SD2_DATA5 0x17059 + MX6QDL_PAD_NANDF_D6__SD2_DATA6 0x17059 + MX6QDL_PAD_NANDF_D7__SD2_DATA7 0x17059 + >; + }; + + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX6QDL_PAD_SD3_CMD__SD3_CMD 0x17059 + MX6QDL_PAD_SD3_CLK__SD3_CLK 0x10059 + MX6QDL_PAD_SD3_DAT0__SD3_DATA0 0x17059 + MX6QDL_PAD_SD3_DAT1__SD3_DATA1 0x17059 + MX6QDL_PAD_SD3_DAT2__SD3_DATA2 0x17059 + MX6QDL_PAD_SD3_DAT3__SD3_DATA3 0x17059 + MX6QDL_PAD_SD3_DAT4__SD3_DATA4 0x17059 + MX6QDL_PAD_SD3_DAT5__SD3_DATA5 0x17059 + MX6QDL_PAD_SD3_DAT6__SD3_DATA6 0x17059 + MX6QDL_PAD_SD3_DAT7__SD3_DATA7 0x17059 + >; + }; + + pinctrl_usdhc4: usdhc4grp { + fsl,pins = < + MX6QDL_PAD_SD4_CMD__SD4_CMD 0x17059 + MX6QDL_PAD_SD4_CLK__SD4_CLK 0x10059 + MX6QDL_PAD_SD4_DAT0__SD4_DATA0 0x17059 + MX6QDL_PAD_SD4_DAT1__SD4_DATA1 0x17059 + MX6QDL_PAD_SD4_DAT2__SD4_DATA2 0x17059 + MX6QDL_PAD_SD4_DAT3__SD4_DATA3 0x17059 + MX6QDL_PAD_SD4_DAT4__SD4_DATA4 0x17059 + MX6QDL_PAD_SD4_DAT5__SD4_DATA5 0x17059 + MX6QDL_PAD_SD4_DAT6__SD4_DATA6 0x17059 + MX6QDL_PAD_SD4_DAT7__SD4_DATA7 0x17059 + >; + }; + }; + + gpio_leds { + pinctrl_gpio_leds: gpioledsgrp { + fsl,pins = < + MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x1b0b0 + >; + }; + }; +}; + +&ldb { + status = "okay"; + + lvds-channel@1 { + fsl,data-mapping = "spwg"; + fsl,data-width = <18>; + status = "okay"; + + display-timings { + native-mode = <&timing0>; + timing0: hsd100pxn1 { + clock-frequency = <65000000>; + hactive = <1024>; + vactive = <768>; + hback-porch = <220>; + hfront-porch = <40>; + vback-porch = <21>; + vfront-porch = <7>; + hsync-len = <60>; + vsync-len = <10>; + }; + }; + }; +}; + +&pcie { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pcie>; + reset-gpio = <&gpio7 12 0>; + status = "okay"; +}; + +&pwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm1>; + status = "okay"; +}; + +&snvs_poweroff { + status = "okay"; +}; + +&ssi2 { + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; +}; + +&uart5 { + compatible = "fsl,imx21-uart"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart5>; + status = "okay"; + + /* enable rts/cts usage on uart5 */ + fsl,uart-has-rtscts; +}; + +&usbh1 { + vbus-supply = <®_usb_h1_vbus>; + status = "okay"; +}; + +&usbotg { + vbus-supply = <®_usb_otg_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg>; + disable-over-current; + status = "okay"; +}; + +&usdhc2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc2>; + bus-width = <8>; +/* cd-gpios = <&gpio2 2 0>; */ + wp-gpios = <&gpio2 3 0>; + no-1-8-v; + vmmc-supply = <&wlan_en_reg>; + non-removable; /* non-removable is not a variable, the fact it is */ + /* listed is all that is used by driver */ + cap-power-off-card; + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + wlcore: wlcore@0 { + compatible = "ti,wl1835"; + reg = <2>; + interrupt-parent = <&gpio4>; + interrupts = <6 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&usdhc3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3>; + bus-width = <8>; + cd-gpios = <&gpio2 0 0>; + wp-gpios = <&gpio2 1 0>; + status = "okay"; +}; + +&usdhc4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc4>; + bus-width = <8>; + non-removable; + no-1-8-v; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx6qdl-udoo.dtsi b/arch/arm/boot/dts/imx6qdl-udoo.dtsi index 3bee2f9..10df0f2 100644 --- a/arch/arm/boot/dts/imx6qdl-udoo.dtsi +++ b/arch/arm/boot/dts/imx6qdl-udoo.dtsi @@ -9,6 +9,8 @@ * */ +#include + / { aliases { backlight = &backlight; @@ -58,17 +60,6 @@ #address-cells = <1>; #size-cells = <0>; - reg_usb_h1_vbus: regulator@0 { - compatible = "regulator-fixed"; - reg = <0>; - regulator-name = "usb_h1_vbus"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - enable-active-high; - startup-delay-us = <2>; /* USB2415 requires a POR of 1 us minimum */ - gpio = <&gpio7 12 0>; - }; - reg_panel: regulator@1 { compatible = "regulator-fixed"; reg = <1>; @@ -126,10 +117,53 @@ gpios = <&gpio1 15 0>; status = "disabled"; }; + + ov5640_mipi: ov5640_mipi@3c { + compatible = "ovti,ov5640_mipi"; + reg = <0x3c>; + clocks = <&clks IMX6QDL_CLK_CKO>; + clock-names = "csi_mclk"; + pwn-gpios = <&gpio6 4 GPIO_ACTIVE_LOW>; + rst-gpios = <&gpio6 5 GPIO_ACTIVE_HIGH>; + csi_id = <0>; + mclk = <24000000>; + mclk_source = <0>; + }; }; &iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + imx6q-udoo { + pinctrl_hog: hoggrp { + fsl,pins = < + // Internal GPIOs + MX6QDL_PAD_NANDF_D4__GPIO2_IO04 0x80000000 // 5v enable + MX6QDL_PAD_NANDF_CS0__GPIO6_IO11 0x80000000 // Vtt enable + + MX6QDL_PAD_DISP0_DAT5__GPIO4_IO26 0x80000000 // Debug UART (J18) + + MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 // USB hub reset + MX6QDL_PAD_NANDF_CS2__CCM_CLKO2 0x130b0 // USB hub clock + MX6QDL_PAD_EIM_WAIT__GPIO5_IO00 0xb0b1 // USB OTG select + + MX6QDL_PAD_NANDF_D5__GPIO2_IO05 0x80000000 // SD card power + MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x80000000 // SD card detect + + MX6QDL_PAD_GPIO_16__GPIO7_IO11 0xb0b1 // SAM3X OTG vbus_en + MX6QDL_PAD_SD4_DAT7__GPIO2_IO15 0x80000000 // SAM3X usb host + MX6QDL_PAD_GPIO_3__GPIO1_IO03 0x30b1 // Arduino pinout pin 12 + + MX6QDL_PAD_GPIO_2__GPIO1_IO02 0x80000000 // LVDS panel on (CN13) + MX6QDL_PAD_GPIO_4__GPIO1_IO04 0x80000000 // LVDS backlight on (CN13) + + MX6QDL_PAD_CSI0_DAT18__GPIO6_IO04 0x1f071 // CSI camera enable (CN11) + MX6QDL_PAD_CSI0_DAT19__GPIO6_IO05 0x1f071 // CSI camera reset (CN11) + MX6QDL_PAD_CSI0_MCLK__CCM_CLKO1 0x130b0 // CSI master clock (CN11) + >; + }; + pinctrl_enet: enetgrp { fsl,pins = < MX6QDL_PAD_RGMII_RXC__RGMII_RXC 0x1b0b0 @@ -186,6 +220,13 @@ >; }; + pinctrl_uart4: uart4grp { + fsl,pins = < + MX6QDL_PAD_KEY_COL0__UART4_TX_DATA 0x1b0b1 + MX6QDL_PAD_KEY_ROW0__UART4_RX_DATA 0x1b0b1 + >; + }; + pinctrl_usbh: usbhgrp { fsl,pins = < MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 @@ -256,14 +297,28 @@ status = "okay"; }; -&usbh1 { +&uart4 { // iMX6-Arduino internal serial port - ttymxc3 pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usbh>; - vbus-supply = <®_usb_h1_vbus>; - clocks = <&clks IMX6QDL_CLK_CKO>; + pinctrl-0 = <&pinctrl_uart4>; status = "okay"; }; +&usbh1 { + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + hub: usb2415@01 { + compatible = "generic-onboard-device"; + reg = <0x01>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbh>; + clocks = <&clks IMX6QDL_CLK_CKO>; + reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>; + reset-duration-us = <2>; + }; +}; + &usdhc3 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usdhc3>; @@ -285,3 +340,11 @@ ac97-gpios = <&gpio4 19 0 &gpio4 18 0 &gpio2 30 0>; status = "okay"; }; + +&mipi_csi { + status = "okay"; + ipu_id = <0>; + csi_id = <0>; + v_channel = <0>; + lanes = <2>; +}; diff --git a/arch/arm/boot/dts/imx6qdl-wandboard-revb1.dtsi b/arch/arm/boot/dts/imx6qdl-wandboard-revb1.dtsi index ef7fa62..b5d0f58 100644 --- a/arch/arm/boot/dts/imx6qdl-wandboard-revb1.dtsi +++ b/arch/arm/boot/dts/imx6qdl-wandboard-revb1.dtsi @@ -11,6 +11,24 @@ #include "imx6qdl-wandboard.dtsi" +/ { + rfkill { + compatible = "wand,imx6qdl-wandboard-rfkill"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + bluetooth-on = <&gpio3 13 0>; + bluetooth-wake = <&gpio3 14 0>; + bluetooth-host-wake = <&gpio3 15 0>; + + wifi-ref-on = <&gpio2 29 0>; + wifi-rst-n = <&gpio5 2 0>; + wifi-reg-on = <&gpio1 26 0>; + wifi-host-wake = <&gpio1 29 0>; + wifi-wake = <&gpio1 30 0>; + }; +}; + &iomuxc { pinctrl-0 = <&pinctrl_hog>; @@ -37,6 +55,5 @@ &usdhc2 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usdhc2>; - non-removable; status = "okay"; }; diff --git a/arch/arm/boot/dts/imx6qdl-wandboard-revc1.dtsi b/arch/arm/boot/dts/imx6qdl-wandboard-revc1.dtsi index 8d893a7..b2097ef 100644 --- a/arch/arm/boot/dts/imx6qdl-wandboard-revc1.dtsi +++ b/arch/arm/boot/dts/imx6qdl-wandboard-revc1.dtsi @@ -11,6 +11,24 @@ #include "imx6qdl-wandboard.dtsi" +/ { + rfkill { + compatible = "wand,imx6qdl-wandboard-rfkill"; + pinctrl-names = "default"; + pinctrl-0 = <>; + + bluetooth-on = <&gpio5 21 0>; + bluetooth-wake = <&gpio5 30 0>; + bluetooth-host-wake = <&gpio5 20 0>; + + wifi-ref-on = <&gpio5 31 0>; /* Wifi Power Enable */ + wifi-rst-n = <&gpio6 0 0>; /* WIFI_ON reset */ + wifi-reg-on = <&gpio1 26 0>; /* WL_REG_ON */ + wifi-host-wake = <&gpio1 29 0>; /* WL_HOST_WAKE */ + wifi-wake = <&gpio1 30 0>; /* WL_WAKE */ + }; +}; + &iomuxc { pinctrl-0 = <&pinctrl_hog>; diff --git b/arch/arm/boot/dts/imx6sl-evk-wl1835.dts b/arch/arm/boot/dts/imx6sl-evk-wl1835.dts new file mode 100644 index 0000000..e5853f3 --- /dev/null +++ b/arch/arm/boot/dts/imx6sl-evk-wl1835.dts @@ -0,0 +1,633 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, 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 as + * published by the Free Software Foundation. + */ + +/dts-v1/; + +#include +#include +#include "imx6sl.dtsi" + +/ { + model = "Freescale i.MX6 SoloLite EVK Board"; + compatible = "fsl,imx6sl-evk", "fsl,imx6sl"; + + memory { + reg = <0x80000000 0x40000000>; + }; + + backlight { + compatible = "pwm-backlight"; + pwms = <&pwm1 0 5000000>; + brightness-levels = <0 4 8 16 32 64 128 255>; + default-brightness-level = <6>; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_led>; + + user { + label = "debug"; + gpios = <&gpio3 20 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + reg_usb_otg1_vbus: regulator@0 { + compatible = "regulator-fixed"; + reg = <0>; + regulator-name = "usb_otg1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio4 0 0>; + enable-active-high; + vin-supply = <&swbst_reg>; + }; + + reg_usb_otg2_vbus: regulator@1 { + compatible = "regulator-fixed"; + reg = <1>; + regulator-name = "usb_otg2_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + gpio = <&gpio4 2 0>; + enable-active-high; + vin-supply = <&swbst_reg>; + }; + + reg_aud3v: regulator@2 { + compatible = "regulator-fixed"; + reg = <2>; + regulator-name = "wm8962-supply-3v15"; + regulator-min-microvolt = <3150000>; + regulator-max-microvolt = <3150000>; + regulator-boot-on; + }; + + reg_aud4v: regulator@3 { + compatible = "regulator-fixed"; + reg = <3>; + regulator-name = "wm8962-supply-4v2"; + regulator-min-microvolt = <4325000>; + regulator-max-microvolt = <4325000>; + regulator-boot-on; + }; + + reg_lcd_3v3: regulator@4 { + compatible = "regulator-fixed"; + reg = <4>; + regulator-name = "lcd-3v3"; + gpio = <&gpio4 3 0>; + enable-active-high; + }; + + wlan_en_reg: regulator@5 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + /* WLAN_EN GPIO for this board – Bank5, pin13 */ + gpio = <&gpio5 13 0>; + enable-active-high; + }; + }; + + sound { + compatible = "fsl,imx6sl-evk-wm8962", "fsl,imx-audio-wm8962"; + model = "wm8962-audio"; + ssi-controller = <&ssi2>; + audio-codec = <&codec>; + audio-routing = + "Headphone Jack", "HPOUTL", + "Headphone Jack", "HPOUTR", + "Ext Spk", "SPKOUTL", + "Ext Spk", "SPKOUTR", + "AMIC", "MICBIAS", + "IN3R", "AMIC"; + mux-int-port = <2>; + mux-ext-port = <3>; + }; + + kim { + compatible = "kim"; + nshutdown_gpio = <134>; // GPIO5_6 The wl8 driver expects gpio to be an integer, so gpio5_6 is (5-1)*32+6=134 + dev_name = "/dev/ttymxc3"; + flow_cntrl = <1>; + baud_rate = <3000000>; + }; + + btwilink { + compatible = "btwilink"; + }; +}; + +&audmux { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_audmux3>; + status = "okay"; +}; + +&ecspi1 { + fsl,spi-num-chipselects = <1>; + cs-gpios = <&gpio4 11 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_ecspi1>; + status = "okay"; + + flash: m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "st,m25p32"; + spi-max-frequency = <20000000>; + reg = <0>; + }; +}; + +&fec { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&pinctrl_fec>; + pinctrl-1 = <&pinctrl_fec_sleep>; + phy-mode = "rmii"; + status = "okay"; +}; + +&i2c1 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c1>; + status = "okay"; + + pmic: pfuze100@08 { + compatible = "fsl,pfuze100"; + reg = <0x08>; + + regulators { + sw1a_reg: sw1ab { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw1c_reg: sw1c { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw2_reg: sw2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3a_reg: sw3a { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3b_reg: sw3b { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw4_reg: sw4 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + }; + + swbst_reg: swbst { + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5150000>; + }; + + snvs_reg: vsnvs { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + }; + + vref_reg: vrefddr { + regulator-boot-on; + regulator-always-on; + }; + + vgen1_reg: vgen1 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + regulator-always-on; + }; + + vgen2_reg: vgen2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen3_reg: vgen3 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + vgen4_reg: vgen4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen5_reg: vgen5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen6_reg: vgen6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + }; +}; + +&i2c2 { + clock-frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; + status = "okay"; + + codec: wm8962@1a { + compatible = "wlf,wm8962"; + reg = <0x1a>; + clocks = <&clks IMX6SL_CLK_EXTERN_AUDIO>; + DCVDD-supply = <&vgen3_reg>; + DBVDD-supply = <®_aud3v>; + AVDD-supply = <&vgen3_reg>; + CPVDD-supply = <&vgen3_reg>; + MICVDD-supply = <®_aud3v>; + PLLVDD-supply = <&vgen3_reg>; + SPKVDD1-supply = <®_aud4v>; + SPKVDD2-supply = <®_aud4v>; + }; +}; + +&iomuxc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_hog>; + + imx6sl-evk { + pinctrl_hog: hoggrp { + fsl,pins = < + MX6SL_PAD_KEY_ROW7__GPIO4_IO07 0x17059 + MX6SL_PAD_KEY_COL7__GPIO4_IO06 0x17059 + MX6SL_PAD_SD2_DAT7__GPIO5_IO00 0x17059 + MX6SL_PAD_SD2_DAT6__GPIO4_IO29 0x17059 + MX6SL_PAD_REF_CLK_32K__GPIO3_IO22 0x17059 + MX6SL_PAD_KEY_COL4__GPIO4_IO00 0x80000000 + MX6SL_PAD_KEY_COL5__GPIO4_IO02 0x80000000 + MX6SL_PAD_AUD_MCLK__AUDIO_CLK_OUT 0x4130b0 + MX6SL_PAD_SD1_DAT2__GPIO5_IO13 0x13059 // reserve two pins from sd1 for wl8 gpio, this is pulled low at reset for WL_EN + MX6SL_PAD_SD1_DAT1__GPIO5_IO08 0x13059 // this is for wl_irq which driver will configure as an input with a pull down + >; + }; + + pinctrl_audmux3: audmux3grp { + fsl,pins = < + MX6SL_PAD_AUD_RXD__AUD3_RXD 0x4130b0 + MX6SL_PAD_AUD_TXC__AUD3_TXC 0x4130b0 + MX6SL_PAD_AUD_TXD__AUD3_TXD 0x4110b0 + MX6SL_PAD_AUD_TXFS__AUD3_TXFS 0x4130b0 + >; + }; + + pinctrl_ecspi1: ecspi1grp { + fsl,pins = < + MX6SL_PAD_ECSPI1_MISO__ECSPI1_MISO 0x100b1 + MX6SL_PAD_ECSPI1_MOSI__ECSPI1_MOSI 0x100b1 + MX6SL_PAD_ECSPI1_SCLK__ECSPI1_SCLK 0x100b1 + MX6SL_PAD_ECSPI1_SS0__GPIO4_IO11 0x80000000 + >; + }; + + pinctrl_fec: fecgrp { + fsl,pins = < + MX6SL_PAD_FEC_MDC__FEC_MDC 0x1b0b0 + MX6SL_PAD_FEC_MDIO__FEC_MDIO 0x1b0b0 + MX6SL_PAD_FEC_CRS_DV__FEC_RX_DV 0x1b0b0 + MX6SL_PAD_FEC_RXD0__FEC_RX_DATA0 0x1b0b0 + MX6SL_PAD_FEC_RXD1__FEC_RX_DATA1 0x1b0b0 + MX6SL_PAD_FEC_TX_EN__FEC_TX_EN 0x1b0b0 + MX6SL_PAD_FEC_TXD0__FEC_TX_DATA0 0x1b0b0 + MX6SL_PAD_FEC_TXD1__FEC_TX_DATA1 0x1b0b0 + MX6SL_PAD_FEC_REF_CLK__FEC_REF_OUT 0x4001b0a8 + >; + }; + + pinctrl_fec_sleep: fecgrp-sleep { + fsl,pins = < + MX6SL_PAD_FEC_MDC__GPIO4_IO23 0x3080 + MX6SL_PAD_FEC_CRS_DV__GPIO4_IO25 0x3080 + MX6SL_PAD_FEC_RXD0__GPIO4_IO17 0x3080 + MX6SL_PAD_FEC_RXD1__GPIO4_IO18 0x3080 + MX6SL_PAD_FEC_TX_EN__GPIO4_IO22 0x3080 + MX6SL_PAD_FEC_TXD0__GPIO4_IO24 0x3080 + MX6SL_PAD_FEC_TXD1__GPIO4_IO16 0x3080 + MX6SL_PAD_FEC_REF_CLK__GPIO4_IO26 0x3080 + >; + }; + + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX6SL_PAD_I2C1_SCL__I2C1_SCL 0x4001b8b1 + MX6SL_PAD_I2C1_SDA__I2C1_SDA 0x4001b8b1 + >; + }; + + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6SL_PAD_I2C2_SCL__I2C2_SCL 0x4001b8b1 + MX6SL_PAD_I2C2_SDA__I2C2_SDA 0x4001b8b1 + >; + }; + + pinctrl_kpp: kppgrp { + fsl,pins = < + MX6SL_PAD_KEY_ROW0__KEY_ROW0 0x1b010 + MX6SL_PAD_KEY_ROW1__KEY_ROW1 0x1b010 + MX6SL_PAD_KEY_ROW2__KEY_ROW2 0x1b0b0 + MX6SL_PAD_KEY_COL0__KEY_COL0 0x110b0 + MX6SL_PAD_KEY_COL1__KEY_COL1 0x110b0 + MX6SL_PAD_KEY_COL2__KEY_COL2 0x110b0 + >; + }; + + pinctrl_lcd: lcdgrp { + fsl,pins = < + MX6SL_PAD_LCD_DAT0__LCD_DATA00 0x1b0b0 + MX6SL_PAD_LCD_DAT1__LCD_DATA01 0x1b0b0 + MX6SL_PAD_LCD_DAT2__LCD_DATA02 0x1b0b0 + MX6SL_PAD_LCD_DAT3__LCD_DATA03 0x1b0b0 + MX6SL_PAD_LCD_DAT4__LCD_DATA04 0x1b0b0 + MX6SL_PAD_LCD_DAT5__LCD_DATA05 0x1b0b0 + MX6SL_PAD_LCD_DAT6__LCD_DATA06 0x1b0b0 + MX6SL_PAD_LCD_DAT7__LCD_DATA07 0x1b0b0 + MX6SL_PAD_LCD_DAT8__LCD_DATA08 0x1b0b0 + MX6SL_PAD_LCD_DAT9__LCD_DATA09 0x1b0b0 + MX6SL_PAD_LCD_DAT10__LCD_DATA10 0x1b0b0 + MX6SL_PAD_LCD_DAT11__LCD_DATA11 0x1b0b0 + MX6SL_PAD_LCD_DAT12__LCD_DATA12 0x1b0b0 + MX6SL_PAD_LCD_DAT13__LCD_DATA13 0x1b0b0 + MX6SL_PAD_LCD_DAT14__LCD_DATA14 0x1b0b0 + MX6SL_PAD_LCD_DAT15__LCD_DATA15 0x1b0b0 + MX6SL_PAD_LCD_DAT16__LCD_DATA16 0x1b0b0 + MX6SL_PAD_LCD_DAT17__LCD_DATA17 0x1b0b0 + MX6SL_PAD_LCD_DAT18__LCD_DATA18 0x1b0b0 + MX6SL_PAD_LCD_DAT19__LCD_DATA19 0x1b0b0 + MX6SL_PAD_LCD_DAT20__LCD_DATA20 0x1b0b0 + MX6SL_PAD_LCD_DAT21__LCD_DATA21 0x1b0b0 + MX6SL_PAD_LCD_DAT22__LCD_DATA22 0x1b0b0 + MX6SL_PAD_LCD_DAT23__LCD_DATA23 0x1b0b0 + MX6SL_PAD_LCD_CLK__LCD_CLK 0x1b0b0 + MX6SL_PAD_LCD_ENABLE__LCD_ENABLE 0x1b0b0 + MX6SL_PAD_LCD_HSYNC__LCD_HSYNC 0x1b0b0 + MX6SL_PAD_LCD_VSYNC__LCD_VSYNC 0x1b0b0 + >; + }; + + pinctrl_led: ledgrp { + fsl,pins = < + MX6SL_PAD_HSIC_STROBE__GPIO3_IO20 0x17059 + >; + }; + + pinctrl_pwm1: pwmgrp { + fsl,pins = < + MX6SL_PAD_PWM1__PWM1_OUT 0x110b0 + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX6SL_PAD_UART1_RXD__UART1_RX_DATA 0x1b0b1 + MX6SL_PAD_UART1_TXD__UART1_TX_DATA 0x1b0b1 + >; + }; + + pinctrl_uart4: uart4grp { + fsl,pins = < + MX6SL_PAD_SD1_DAT4__UART4_RX_DATA 0x13059 // used for HOST_HCI_RX + MX6SL_PAD_SD1_DAT5__UART4_TX_DATA 0x13059 // used for HOST_HCI_TX + MX6SL_PAD_SD1_DAT7__UART4_CTS_B 0x13059 // used for HOST_HCI_RTS, note reversed to TI nomenclature + MX6SL_PAD_SD1_DAT6__UART4_RTS_B 0x13059 // used for HOST_HCI_CTS + MX6SL_PAD_SD1_DAT3__GPIO5_IO06 0x13059 // used for BT_EN + >; + }; + + pinctrl_usbotg1: usbotg1grp { + fsl,pins = < + MX6SL_PAD_EPDC_PWRCOM__USB_OTG1_ID 0x17059 + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX6SL_PAD_SD1_CMD__SD1_CMD 0x17059 + MX6SL_PAD_SD1_CLK__SD1_CLK 0x10059 + MX6SL_PAD_SD1_DAT0__SD1_DATA0 0x17059 + MX6SL_PAD_SD1_DAT1__SD1_DATA1 0x17059 + MX6SL_PAD_SD1_DAT2__SD1_DATA2 0x17059 + MX6SL_PAD_SD1_DAT3__SD1_DATA3 0x17059 + MX6SL_PAD_SD1_DAT4__SD1_DATA4 0x17059 + MX6SL_PAD_SD1_DAT5__SD1_DATA5 0x17059 + MX6SL_PAD_SD1_DAT6__SD1_DATA6 0x17059 + MX6SL_PAD_SD1_DAT7__SD1_DATA7 0x17059 + >; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + MX6SL_PAD_SD2_CMD__SD2_CMD 0x17059 + MX6SL_PAD_SD2_CLK__SD2_CLK 0x10059 + MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x17059 + MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x17059 + MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x17059 + MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x17059 + >; + }; + + pinctrl_usdhc2_100mhz: usdhc2grp100mhz { + fsl,pins = < + MX6SL_PAD_SD2_CMD__SD2_CMD 0x170b9 + MX6SL_PAD_SD2_CLK__SD2_CLK 0x100b9 + MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x170b9 + MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x170b9 + MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x170b9 + MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x170b9 + >; + }; + + pinctrl_usdhc2_200mhz: usdhc2grp200mhz { + fsl,pins = < + MX6SL_PAD_SD2_CMD__SD2_CMD 0x170f9 + MX6SL_PAD_SD2_CLK__SD2_CLK 0x100f9 + MX6SL_PAD_SD2_DAT0__SD2_DATA0 0x170f9 + MX6SL_PAD_SD2_DAT1__SD2_DATA1 0x170f9 + MX6SL_PAD_SD2_DAT2__SD2_DATA2 0x170f9 + MX6SL_PAD_SD2_DAT3__SD2_DATA3 0x170f9 + >; + }; + + pinctrl_usdhc3: usdhc3grp { + fsl,pins = < + MX6SL_PAD_SD3_CMD__SD3_CMD 0x17059 + MX6SL_PAD_SD3_CLK__SD3_CLK 0x10059 + MX6SL_PAD_SD3_DAT0__SD3_DATA0 0x17059 + MX6SL_PAD_SD3_DAT1__SD3_DATA1 0x17059 + MX6SL_PAD_SD3_DAT2__SD3_DATA2 0x17059 + MX6SL_PAD_SD3_DAT3__SD3_DATA3 0x17059 + >; + }; + }; +}; + +&kpp { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_kpp>; + linux,keymap = < + MATRIX_KEY(0x0, 0x0, KEY_UP) /* ROW0, COL0 */ + MATRIX_KEY(0x0, 0x1, KEY_DOWN) /* ROW0, COL1 */ + MATRIX_KEY(0x0, 0x2, KEY_ENTER) /* ROW0, COL2 */ + MATRIX_KEY(0x1, 0x0, KEY_HOME) /* ROW1, COL0 */ + MATRIX_KEY(0x1, 0x1, KEY_RIGHT) /* ROW1, COL1 */ + MATRIX_KEY(0x1, 0x2, KEY_LEFT) /* ROW1, COL2 */ + MATRIX_KEY(0x2, 0x0, KEY_VOLUMEDOWN) /* ROW2, COL0 */ + MATRIX_KEY(0x2, 0x1, KEY_VOLUMEUP) /* ROW2, COL1 */ + >; + status = "okay"; +}; + +&lcdif { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_lcd>; + lcd-supply = <®_lcd_3v3>; + display = <&display0>; + status = "okay"; + + display0: display0 { + bits-per-pixel = <32>; + bus-width = <24>; + + display-timings { + native-mode = <&timing0>; + timing0: timing0 { + clock-frequency = <33500000>; + hactive = <800>; + vactive = <480>; + hback-porch = <89>; + hfront-porch = <164>; + vback-porch = <23>; + vfront-porch = <10>; + hsync-len = <10>; + vsync-len = <10>; + hsync-active = <0>; + vsync-active = <0>; + de-active = <1>; + pixelclk-active = <0>; + }; + }; + }; +}; + +&pwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm1>; + status = "okay"; +}; + +&snvs_poweroff { + status = "okay"; +}; + +&ssi2 { + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; +}; + +&uart4 { + compatible = "fsl,imx21-uart"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart4>; + status = "okay"; + + /* enable rts/cts usage on uart4 */ + fsl,uart-has-rtscts; +}; + +&usbotg1 { + vbus-supply = <®_usb_otg1_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbotg1>; + disable-over-current; + status = "okay"; +}; + +&usbotg2 { + vbus-supply = <®_usb_otg2_vbus>; + dr_mode = "host"; + disable-over-current; + status = "okay"; +}; + +&usdhc2 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc2>; + pinctrl-1 = <&pinctrl_usdhc2_100mhz>; + pinctrl-2 = <&pinctrl_usdhc2_200mhz>; + cd-gpios = <&gpio5 0 0>; + wp-gpios = <&gpio4 29 0>; + status = "okay"; +}; + +&usdhc3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc3>; + keep-power-in-suspend; + enable-sdio-wakeup; + vmmc-supply = <&wlan_en_reg>; + non-removable; // non-removable is not a variable, the fact it is listed is all that is used by driver + cap-power-off-card; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + wlcore: wlcore@0 { + compatible = "ti,wl1835"; + reg = <2>; + interrupt-parent = <&gpio5>; + interrupts = <8 IRQ_TYPE_EDGE_RISING>; + }; +}; diff --git b/arch/arm/boot/dts/imx6ul-14x14-evk-ism43362-b81-evb.dts b/arch/arm/boot/dts/imx6ul-14x14-evk-ism43362-b81-evb.dts new file mode 100644 index 0000000..ffcbc73 --- /dev/null +++ b/arch/arm/boot/dts/imx6ul-14x14-evk-ism43362-b81-evb.dts @@ -0,0 +1,455 @@ +/* + * Copyright (C) 2015 Freescale Semiconductor, 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 as + * published by the Free Software Foundation. + */ + +/dts-v1/; + +#include "imx6ul.dtsi" + +/ { + model = "Freescale i.MX6 UltraLite 14x14 EVK Board"; + compatible = "fsl,imx6ul-14x14-evk", "fsl,imx6ul"; + + chosen { + stdout-path = &uart1; + }; + + memory { + reg = <0x80000000 0x20000000>; + }; + + regulators { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; + + reg_sd1_vmmc: sd1_regulator { + compatible = "regulator-fixed"; + regulator-name = "VSD_3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio1 9 GPIO_ACTIVE_HIGH>; + enable-active-high; + }; + + wlreg_on: fixedregulator@100 { + compatible = "regulator-fixed"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-name = "wlreg_on"; + gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>; + startup-delay-us = <100>; + enable-active-high; + }; + }; + + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "mx6ul-wm8960"; + simple-audio-card,format = "i2s"; + simple-audio-card,bitclock-master = <&dailink_master>; + simple-audio-card,frame-master = <&dailink_master>; + simple-audio-card,widgets = + "Microphone", "Mic Jack", + "Line", "Line In", + "Line", "Line Out", + "Speaker", "Speaker", + "Headphone", "Headphone Jack"; + simple-audio-card,routing = + "Headphone Jack", "HP_L", + "Headphone Jack", "HP_R", + "Speaker", "SPK_LP", + "Speaker", "SPK_LN", + "Speaker", "SPK_RP", + "Speaker", "SPK_RN", + "LINPUT1", "Mic Jack", + "LINPUT3", "Mic Jack", + "RINPUT1", "Mic Jack", + "RINPUT2", "Mic Jack"; + + simple-audio-card,cpu { + sound-dai = <&sai2>; + }; + + dailink_master: simple-audio-card,codec { + sound-dai = <&codec>; + clocks = <&clks IMX6UL_CLK_SAI2>; + }; + }; +}; + +&clks { + assigned-clocks = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>; + assigned-clock-rates = <786432000>; +}; + +&cpu0 { + arm-supply = <®_arm>; + soc-supply = <®_soc>; +}; + +&i2c2 { + clock_frequency = <100000>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2c2>; + status = "okay"; + + codec: wm8960@1a { + #sound-dai-cells = <0>; + compatible = "wlf,wm8960"; + reg = <0x1a>; + wlf,shared-lrclk; + }; +}; + +&fec1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet1>; + phy-mode = "rmii"; + phy-handle = <ðphy0>; + status = "okay"; +}; + +&fec2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_enet2>; + phy-mode = "rmii"; + phy-handle = <ðphy1>; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + ethphy0: ethernet-phy@2 { + reg = <2>; + }; + + ethphy1: ethernet-phy@1 { + reg = <1>; + }; + }; +}; + +&qspi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_qspi>; + status = "okay"; + + flash0: n25q256a@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "micron,n25q256a"; + spi-max-frequency = <29000000>; + reg = <0>; + }; +}; + +&sai2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sai2>; + assigned-clocks = <&clks IMX6UL_CLK_SAI2_SEL>, + <&clks IMX6UL_CLK_SAI2>; + assigned-clock-parents = <&clks IMX6UL_CLK_PLL4_AUDIO_DIV>; + assigned-clock-rates = <0>, <12288000>; + status = "okay"; +}; + +&snvs_poweroff { + status = "okay"; +}; + +&tsc { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_tsc>; + xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>; + measure-delay-time = <0xffff>; + pre-charge-time = <0xfff>; + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart1>; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_uart2>; + fsl,uart-has-rtscts; + status = "okay"; +}; + +&usbotg1 { + dr_mode = "peripheral"; + status = "okay"; +}; + +&usbotg2 { + dr_mode = "host"; + disable-over-current; + status = "okay"; +}; + +®_sd1_vmmc { + regulator-always-on; +}; + +&usdhc1 { + pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_usdhc1>; + pinctrl-1 = <&pinctrl_usdhc1_100mhz>; + pinctrl-2 = <&pinctrl_usdhc1_200mhz>; + cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>; + no-1-8-v; + keep-power-in-suspend; + wakeup-source; + vmmc-supply = <®_sd1_vmmc>; + status = "okay"; +}; + +&usdhc2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usdhc2>; + no-1-8-v; + keep-power-in-suspend; + wakeup-source; + status = "okay"; +}; + +&iomuxc { + pinctrl-names = "default"; + + pinctrl_csi1: csi1grp { + fsl,pins = < + MX6UL_PAD_CSI_MCLK__CSI_MCLK 0x1b088 + MX6UL_PAD_CSI_PIXCLK__CSI_PIXCLK 0x1b088 + MX6UL_PAD_CSI_VSYNC__CSI_VSYNC 0x1b088 + MX6UL_PAD_CSI_HSYNC__CSI_HSYNC 0x1b088 + MX6UL_PAD_CSI_DATA00__CSI_DATA02 0x1b088 + MX6UL_PAD_CSI_DATA01__CSI_DATA03 0x1b088 + MX6UL_PAD_CSI_DATA02__CSI_DATA04 0x1b088 + MX6UL_PAD_CSI_DATA03__CSI_DATA05 0x1b088 + MX6UL_PAD_CSI_DATA04__CSI_DATA06 0x1b088 + MX6UL_PAD_CSI_DATA05__CSI_DATA07 0x1b088 + MX6UL_PAD_CSI_DATA06__CSI_DATA08 0x1b088 + MX6UL_PAD_CSI_DATA07__CSI_DATA09 0x1b088 + >; + }; + + pinctrl_enet1: enet1grp { + fsl,pins = < + MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0 + MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0 + MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0 + MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0 + MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0 + MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0 + MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0 + MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031 + >; + }; + + pinctrl_enet2: enet2grp { + fsl,pins = < + MX6UL_PAD_GPIO1_IO07__ENET2_MDC 0x1b0b0 + MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0 + MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0 + MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0 + MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0 + MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0 + MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0 + MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0 + MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0 + MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b031 + MX6UL_PAD_SNVS_TAMPER0__GPIO5_IO00 0x17059 + >; + }; + + pinctrl_flexcan1: flexcan1grp{ + fsl,pins = < + MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX 0x1b020 + MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX 0x1b020 + >; + }; + + pinctrl_flexcan2: flexcan2grp{ + fsl,pins = < + MX6UL_PAD_UART2_RTS_B__FLEXCAN2_RX 0x1b020 + MX6UL_PAD_UART2_CTS_B__FLEXCAN2_TX 0x1b020 + >; + }; + + pinctrl_i2c1: i2c1grp { + fsl,pins = < + MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0 + MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0 + >; + }; + + pinctrl_i2c2: i2c2grp { + fsl,pins = < + MX6UL_PAD_UART5_TX_DATA__I2C2_SCL 0x4001b8b0 + MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x4001b8b0 + >; + }; + + pinctrl_lcdif_dat: lcdifdatgrp { + fsl,pins = < + MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x79 + MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x79 + MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x79 + MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x79 + MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x79 + MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x79 + MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x79 + MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x79 + MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x79 + MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x79 + MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x79 + MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x79 + MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x79 + MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x79 + MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x79 + MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x79 + MX6UL_PAD_LCD_DATA16__LCDIF_DATA16 0x79 + MX6UL_PAD_LCD_DATA17__LCDIF_DATA17 0x79 + MX6UL_PAD_LCD_DATA18__LCDIF_DATA18 0x79 + MX6UL_PAD_LCD_DATA19__LCDIF_DATA19 0x79 + MX6UL_PAD_LCD_DATA20__LCDIF_DATA20 0x79 + MX6UL_PAD_LCD_DATA21__LCDIF_DATA21 0x79 + MX6UL_PAD_LCD_DATA22__LCDIF_DATA22 0x79 + MX6UL_PAD_LCD_DATA23__LCDIF_DATA23 0x79 + >; + }; + + pinctrl_lcdif_ctrl: lcdifctrlgrp { + fsl,pins = < + MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x79 + MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x79 + MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x79 + MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x79 + /* used for lcd reset */ + MX6UL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x79 + >; + }; + + pinctrl_qspi: qspigrp { + fsl,pins = < + MX6UL_PAD_NAND_WP_B__QSPI_A_SCLK 0x70a1 + MX6UL_PAD_NAND_READY_B__QSPI_A_DATA00 0x70a1 + MX6UL_PAD_NAND_CE0_B__QSPI_A_DATA01 0x70a1 + MX6UL_PAD_NAND_CE1_B__QSPI_A_DATA02 0x70a1 + MX6UL_PAD_NAND_CLE__QSPI_A_DATA03 0x70a1 + MX6UL_PAD_NAND_DQS__QSPI_A_SS0_B 0x70a1 + >; + }; + + pinctrl_sai2: sai2grp { + fsl,pins = < + MX6UL_PAD_JTAG_TDI__SAI2_TX_BCLK 0x17088 + MX6UL_PAD_JTAG_TDO__SAI2_TX_SYNC 0x17088 + MX6UL_PAD_JTAG_TRST_B__SAI2_TX_DATA 0x11088 + MX6UL_PAD_JTAG_TCK__SAI2_RX_DATA 0x11088 + MX6UL_PAD_JTAG_TMS__SAI2_MCLK 0x17088 + MX6UL_PAD_SNVS_TAMPER4__GPIO5_IO04 0x17059 + >; + }; + + pinctrl_pwm1: pwm1grp { + fsl,pins = < + MX6UL_PAD_GPIO1_IO08__PWM1_OUT 0x110b0 + >; + }; + + pinctrl_sim2: sim2grp { + fsl,pins = < + MX6UL_PAD_CSI_DATA03__SIM2_PORT1_PD 0xb808 + MX6UL_PAD_CSI_DATA04__SIM2_PORT1_CLK 0x31 + MX6UL_PAD_CSI_DATA05__SIM2_PORT1_RST_B 0xb808 + MX6UL_PAD_CSI_DATA06__SIM2_PORT1_SVEN 0xb808 + MX6UL_PAD_CSI_DATA07__SIM2_PORT1_TRXD 0xb809 + MX6UL_PAD_CSI_DATA02__GPIO4_IO23 0x3008 + >; + }; + + pinctrl_tsc: tscgrp { + fsl,pins = < + MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0xb0 + MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0xb0 + MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0xb0 + MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0xb0 + >; + }; + + pinctrl_uart1: uart1grp { + fsl,pins = < + MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1 + MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1 + >; + }; + + pinctrl_uart2: uart2grp { + fsl,pins = < + MX6UL_PAD_UART2_TX_DATA__UART2_DCE_TX 0x1b0b1 + MX6UL_PAD_UART2_RX_DATA__UART2_DCE_RX 0x1b0b1 + MX6UL_PAD_UART3_RX_DATA__UART2_DCE_RTS 0x1b0b1 + MX6UL_PAD_UART3_TX_DATA__UART2_DCE_CTS 0x1b0b1 + >; + }; + + pinctrl_usdhc1: usdhc1grp { + fsl,pins = < + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x17059 + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x10071 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x17059 + MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */ + MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 /* SD1 VSELECT */ + MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059 /* SD1 RESET */ + MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x3029 + >; + }; + + pinctrl_usdhc1_100mhz: usdhc1grp100mhz { + fsl,pins = < + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170b9 + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100b9 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170b9 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170b9 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170b9 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170b9 + MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x3029 + >; + }; + + pinctrl_usdhc1_200mhz: usdhc1grp200mhz { + fsl,pins = < + MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x170f9 + MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x100f9 + MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x170f9 + MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x170f9 + MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x170f9 + MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x170f9 + MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x3029 + >; + }; + + pinctrl_usdhc2: usdhc2grp { + fsl,pins = < + MX6UL_PAD_NAND_RE_B__USDHC2_CLK 0x17059 + MX6UL_PAD_NAND_WE_B__USDHC2_CMD 0x17059 + MX6UL_PAD_NAND_DATA00__USDHC2_DATA0 0x17059 + MX6UL_PAD_NAND_DATA01__USDHC2_DATA1 0x17059 + MX6UL_PAD_NAND_DATA02__USDHC2_DATA2 0x17059 + MX6UL_PAD_NAND_DATA03__USDHC2_DATA3 0x17059 + >; + }; +}; diff --git a/arch/arm/boot/dts/omap3-beagle-xm.dts b/arch/arm/boot/dts/omap3-beagle-xm.dts index 01e1e2d..8a8d72e 100644 --- a/arch/arm/boot/dts/omap3-beagle-xm.dts +++ b/arch/arm/boot/dts/omap3-beagle-xm.dts @@ -146,6 +146,7 @@ }; etb@5401b000 { + status = "disabled"; compatible = "arm,coresight-etb10", "arm,primecell"; reg = <0x5401b000 0x1000>; @@ -160,6 +161,7 @@ }; etm@54010000 { + status = "disabled"; compatible = "arm,coresight-etm3x", "arm,primecell"; reg = <0x54010000 0x1000>; @@ -205,6 +207,25 @@ >; }; + spi3_pins: pinmux_spi3_pins { + pinctrl-single,pins = < + 0x128 (PIN_INPUT | MUX_MODE1) /* sdmmc2_clk.mcspi3_clk gpio_130 */ + 0x12a (PIN_OUTPUT | MUX_MODE1) /* sdmmc2_cmd.mcspi3_simo gpio_131 */ + 0x12c (PIN_INPUT_PULLUP | MUX_MODE1) /* sdmmc2_dat0.mcspi3_somi gpio_132 */ + 0x130 (PIN_OUTPUT | MUX_MODE1) /* sdmmc2_dat2.mcspi3_cs1 gpio_134 */ + 0x132 (PIN_OUTPUT | MUX_MODE1) /* sdmmc2_dat3.mcspi3_cs0 gpio_135 */ + >; + }; + + spi4_pins: pinmux_spi4_pins { + pinctrl-single,pins = < + 0x15c (PIN_INPUT | MUX_MODE1) /* mcbsp1_clkr.mcspi4_clk gpio_156 */ + 0x160 (PIN_OUTPUT | MUX_MODE1) /* mcbsp1_dx.mcspi4_simo gpio_158 */ + 0x162 (PIN_INPUT_PULLUP | MUX_MODE1) /* mcbsp1_dr.mcspi4_somi gpio_159 */ + 0x166 (PIN_OUTPUT | MUX_MODE1) /* mcbsp1_fsx.mcspi4_cs0 gpio_161 */ + >; + }; + hsusb2_pins: pinmux_hsusb2_pins { pinctrl-single,pins = < OMAP3_CORE1_IOPAD(0x21d4, PIN_INPUT_PULLDOWN | MUX_MODE3) /* mcspi1_cs3.hsusb2_data2 */ @@ -279,7 +300,7 @@ }; twl_power: power { - compatible = "ti,twl4030-power-beagleboard-xm", "ti,twl4030-power-idle-osc-off"; + compatible = "ti,twl4030-power-reset"; ti,use_poweroff; }; }; @@ -310,6 +331,36 @@ status = "disabled"; }; +&mcspi3 { + pinctrl-names = "default"; + pinctrl-0 = <&spi3_pins>; + status = "okay"; + + spidev0: spi@0 { + compatible = "spidev"; + reg = <0>; + spi-max-frequency = <48000000>; + }; + + spidev1: spi@1 { + compatible = "spidev"; + reg = <1>; + spi-max-frequency = <48000000>; + }; +}; + +&mcspi4 { + pinctrl-names = "default"; + pinctrl-0 = <&spi4_pins>; + status = "okay"; + + spidev2: spi@0 { + compatible = "spidev"; + reg = <0>; + spi-max-frequency = <48000000>; + }; +}; + &twl_gpio { ti,use-leds; /* pullups: BIT(1) */ diff --git a/arch/arm/boot/dts/omap3-beagle.dts b/arch/arm/boot/dts/omap3-beagle.dts index a4deff0..9a00ca1 100644 --- a/arch/arm/boot/dts/omap3-beagle.dts +++ b/arch/arm/boot/dts/omap3-beagle.dts @@ -141,6 +141,7 @@ }; etb@540000000 { + status = "disabled"; compatible = "arm,coresight-etb10", "arm,primecell"; reg = <0x5401b000 0x1000>; @@ -155,6 +156,7 @@ }; etm@54010000 { + status = "disabled"; compatible = "arm,coresight-etm3x", "arm,primecell"; reg = <0x54010000 0x1000>; @@ -271,9 +273,18 @@ codec { }; }; + + twl_power: power { + compatible = "ti,twl4030-power-reset"; + ti,use_poweroff; + }; }; }; +&i2c2 { + clock-frequency = <400000>; +}; + #include "twl4030.dtsi" #include "twl4030_omap3.dtsi" diff --git a/arch/arm/boot/dts/omap4-panda-a4.dts b/arch/arm/boot/dts/omap4-panda-a4.dts index 78d3631..67ee5b4 100644 --- a/arch/arm/boot/dts/omap4-panda-a4.dts +++ b/arch/arm/boot/dts/omap4-panda-a4.dts @@ -10,6 +10,18 @@ #include "omap443x.dtsi" #include "omap4-panda-common.dtsi" +&emif1 { + cs1-used; + device-handle = <&elpida_ECB240ABACN>; + status = "ok"; +}; + +&emif2 { + cs1-used; + device-handle = <&elpida_ECB240ABACN>; + status = "ok"; +}; + /* Pandaboard Rev A4+ have external pullups on SCL & SDA */ &dss_hdmi_pins { pinctrl-single,pins = < diff --git a/arch/arm/boot/dts/omap4-panda-common.dtsi b/arch/arm/boot/dts/omap4-panda-common.dtsi index df2e356..cf16679 100644 --- a/arch/arm/boot/dts/omap4-panda-common.dtsi +++ b/arch/arm/boot/dts/omap4-panda-common.dtsi @@ -460,16 +460,6 @@ }; }; -&emif1 { - cs1-used; - device-handle = <&elpida_ECB240ABACN>; -}; - -&emif2 { - cs1-used; - device-handle = <&elpida_ECB240ABACN>; -}; - &mcbsp1 { pinctrl-names = "default"; pinctrl-0 = <&mcbsp1_pins>; diff --git b/arch/arm/boot/dts/omap4-panda-es-b3.dts b/arch/arm/boot/dts/omap4-panda-es-b3.dts new file mode 100644 index 0000000..2f1dabc --- /dev/null +++ b/arch/arm/boot/dts/omap4-panda-es-b3.dts @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ +/dts-v1/; + +#include "omap4460.dtsi" +#include "omap4-panda-common.dtsi" + +/ { + model = "TI OMAP4 PandaBoard-ES"; + compatible = "ti,omap4-panda-es", "ti,omap4-panda", "ti,omap4460", "ti,omap4430", "ti,omap4"; +}; + +/* Audio routing is differnet between PandaBoard4430 and PandaBoardES */ +&sound { + ti,model = "PandaBoardES"; + + /* Audio routing */ + ti,audio-routing = + "Headset Stereophone", "HSOL", + "Headset Stereophone", "HSOR", + "Ext Spk", "HFL", + "Ext Spk", "HFR", + "Line Out", "AUXL", + "Line Out", "AUXR", + "AFML", "Line In", + "AFMR", "Line In"; +}; + +/* PandaboardES has external pullups on SCL & SDA */ +&dss_hdmi_pins { + pinctrl-single,pins = < + 0x5a (PIN_INPUT_PULLUP | MUX_MODE0) /* hdmi_cec.hdmi_cec */ + 0x5c (PIN_INPUT | MUX_MODE0) /* hdmi_scl.hdmi_scl */ + 0x5e (PIN_INPUT | MUX_MODE0) /* hdmi_sda.hdmi_sda */ + >; +}; + +&omap4_pmx_core { + led_gpio_pins: gpio_led_pmx { + pinctrl-single,pins = < + 0xb6 (PIN_OUTPUT | MUX_MODE3) /* gpio_110 */ + >; + }; +}; + +&led_wkgpio_pins { + pinctrl-single,pins = < + 0x1c (PIN_OUTPUT | MUX_MODE3) /* gpio_wk8 */ + >; +}; + +&leds { + pinctrl-0 = < + &led_gpio_pins + &led_wkgpio_pins + >; + + heartbeat { + gpios = <&gpio4 14 GPIO_ACTIVE_HIGH>; + }; + mmc { + gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>; + }; +}; + +&gpio1 { + ti,no-reset-on-init; +}; diff --git a/arch/arm/boot/dts/omap4-panda-es.dts b/arch/arm/boot/dts/omap4-panda-es.dts index 119f8e6..5c54ccf 100644 --- a/arch/arm/boot/dts/omap4-panda-es.dts +++ b/arch/arm/boot/dts/omap4-panda-es.dts @@ -15,6 +15,18 @@ compatible = "ti,omap4-panda-es", "ti,omap4-panda", "ti,omap4460", "ti,omap4430", "ti,omap4"; }; +&emif1 { + cs1-used; + device-handle = <&elpida_ECB240ABACN>; + status = "ok"; +}; + +&emif2 { + cs1-used; + device-handle = <&elpida_ECB240ABACN>; + status = "ok"; +}; + /* Audio routing is differnet between PandaBoard4430 and PandaBoardES */ &sound { ti,model = "PandaBoardES"; diff --git a/arch/arm/boot/dts/omap4-panda.dts b/arch/arm/boot/dts/omap4-panda.dts index a0e28b2..3ee41ef 100644 --- a/arch/arm/boot/dts/omap4-panda.dts +++ b/arch/arm/boot/dts/omap4-panda.dts @@ -14,3 +14,15 @@ model = "TI OMAP4 PandaBoard"; compatible = "ti,omap4-panda", "ti,omap4430", "ti,omap4"; }; + +&emif1 { + cs1-used; + device-handle = <&elpida_ECB240ABACN>; + status = "ok"; +}; + +&emif2 { + cs1-used; + device-handle = <&elpida_ECB240ABACN>; + status = "ok"; +}; diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts index aae5132..45df556 100644 --- a/arch/arm/boot/dts/omap4-sdp.dts +++ b/arch/arm/boot/dts/omap4-sdp.dts @@ -501,11 +501,13 @@ &emif1 { cs1-used; device-handle = <&elpida_ECB240ABACN>; + status = "ok"; }; &emif2 { cs1-used; device-handle = <&elpida_ECB240ABACN>; + status = "ok"; }; &keypad { diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi index 3fdc51c..52051ff 100644 --- a/arch/arm/boot/dts/omap4.dtsi +++ b/arch/arm/boot/dts/omap4.dtsi @@ -695,6 +695,7 @@ hw-caps-read-idle-ctrl; hw-caps-ll-interface; hw-caps-temp-alert; + status = "disabled"; }; emif2: emif@4d000000 { @@ -707,6 +708,7 @@ hw-caps-read-idle-ctrl; hw-caps-ll-interface; hw-caps-temp-alert; + status = "disabled"; }; ocp2scp@4a0ad000 { diff --git a/arch/arm/mach-imx/devices/Kconfig b/arch/arm/mach-imx/devices/Kconfig index 3a55298..aa16f7e 100644 --- a/arch/arm/mach-imx/devices/Kconfig +++ b/arch/arm/mach-imx/devices/Kconfig @@ -72,3 +72,9 @@ config IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX config IMX_HAVE_PLATFORM_SPI_IMX bool + +config WAND_RFKILL + tristate "Wandboard RF Kill support" + depends on SOC_IMX6Q + default m + select RFKILL diff --git a/arch/arm/mach-imx/devices/Makefile b/arch/arm/mach-imx/devices/Makefile index e5cf587..eee36d9 100644 --- a/arch/arm/mach-imx/devices/Makefile +++ b/arch/arm/mach-imx/devices/Makefile @@ -26,3 +26,4 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_W1) += platform-mxc_w1.o obj-$(CONFIG_IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX) += platform-sdhci-esdhc-imx.o obj-$(CONFIG_IMX_HAVE_PLATFORM_SPI_IMX) += platform-spi_imx.o obj-$(CONFIG_IMX_HAVE_PLATFORM_MX2_EMMA) += platform-mx2-emma.o +obj-$(CONFIG_WAND_RFKILL) += wand-rfkill.o diff --git b/arch/arm/mach-imx/devices/wand-rfkill.c b/arch/arm/mach-imx/devices/wand-rfkill.c new file mode 100644 index 0000000..da7ef9f --- /dev/null +++ b/arch/arm/mach-imx/devices/wand-rfkill.c @@ -0,0 +1,290 @@ +/* + * arch/arm/mach-imx/devices/wand-rfkill.c + * + * Copyright (C) 2013 Vladimir Ermakov + * + * based on net/rfkill/rfkill-gpio.c + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 + + +struct wand_rfkill_data { + struct rfkill *rfkill_dev; + int shutdown_gpio; + const char *shutdown_name; +}; + +static int wand_rfkill_set_block(void *data, bool blocked) +{ + struct wand_rfkill_data *rfkill = data; + + pr_debug("wandboard-rfkill: set block %d\n", blocked); + + if (blocked) { + if (gpio_is_valid(rfkill->shutdown_gpio)) + gpio_direction_output(rfkill->shutdown_gpio, 0); + } else { + if (gpio_is_valid(rfkill->shutdown_gpio)) + gpio_direction_output(rfkill->shutdown_gpio, 1); + } + + return 0; +} + +static const struct rfkill_ops wand_rfkill_ops = { + .set_block = wand_rfkill_set_block, +}; + +static int wand_rfkill_wifi_probe(struct device *dev, + struct device_node *np, + struct wand_rfkill_data *rfkill) +{ + int ret; + int wl_ref_on, wl_rst_n, wl_reg_on, wl_wake, wl_host_wake; + + wl_ref_on = of_get_named_gpio(np, "wifi-ref-on", 0); + wl_rst_n = of_get_named_gpio(np, "wifi-rst-n", 0); + wl_reg_on = of_get_named_gpio(np, "wifi-reg-on", 0); + wl_wake = of_get_named_gpio(np, "wifi-wake", 0); + wl_host_wake = of_get_named_gpio(np, "wifi-host-wake", 0); + + if (!gpio_is_valid(wl_rst_n) || !gpio_is_valid(wl_ref_on) || + !gpio_is_valid(wl_reg_on) || !gpio_is_valid(wl_wake) || + !gpio_is_valid(wl_host_wake)) { + + dev_err(dev, "incorrect wifi gpios (%d %d %d %d %d)\n", + wl_rst_n, wl_ref_on, wl_reg_on, wl_wake, wl_host_wake); + return -EINVAL; + } + + dev_info(dev, "initialize wifi chip\n"); + + gpio_request(wl_rst_n, "wl_rst_n"); + gpio_direction_output(wl_rst_n, 0); + msleep(11); + gpio_set_value(wl_rst_n, 1); + + gpio_request(wl_ref_on, "wl_ref_on"); + gpio_direction_output(wl_ref_on, 1); + + gpio_request(wl_reg_on, "wl_reg_on"); + gpio_direction_output(wl_reg_on, 1); + + gpio_request(wl_wake, "wl_wake"); + gpio_direction_output(wl_wake, 1); + + gpio_request(wl_host_wake, "wl_host_wake"); + gpio_direction_input(wl_host_wake); + + rfkill->shutdown_name = "wifi_shutdown"; + rfkill->shutdown_gpio = wl_wake; + + rfkill->rfkill_dev = rfkill_alloc("wifi-rfkill", dev, RFKILL_TYPE_WLAN, + &wand_rfkill_ops, rfkill); + if (!rfkill->rfkill_dev) { + ret = -ENOMEM; + goto wifi_fail_free_gpio; + } + + ret = rfkill_register(rfkill->rfkill_dev); + if (ret < 0) + goto wifi_fail_unregister; + + dev_info(dev, "wifi-rfkill registered.\n"); + + return 0; + +wifi_fail_unregister: + rfkill_destroy(rfkill->rfkill_dev); +wifi_fail_free_gpio: + if (gpio_is_valid(wl_rst_n)) gpio_free(wl_rst_n); + if (gpio_is_valid(wl_ref_on)) gpio_free(wl_ref_on); + if (gpio_is_valid(wl_reg_on)) gpio_free(wl_reg_on); + if (gpio_is_valid(wl_wake)) gpio_free(wl_wake); + if (gpio_is_valid(wl_host_wake)) gpio_free(wl_host_wake); + + return ret; +} + +static int wand_rfkill_bt_probe(struct device *dev, + struct device_node *np, + struct wand_rfkill_data *rfkill) +{ + int ret; + int bt_on, bt_wake, bt_host_wake; + + bt_on = of_get_named_gpio(np, "bluetooth-on", 0); + bt_wake = of_get_named_gpio(np, "bluetooth-wake", 0); + bt_host_wake = of_get_named_gpio(np, "bluetooth-host-wake", 0); + + if (!gpio_is_valid(bt_on) || !gpio_is_valid(bt_wake) || + !gpio_is_valid(bt_host_wake)) { + + dev_err(dev, "incorrect bt gpios (%d %d %d)\n", + bt_on, bt_wake, bt_host_wake); + return -EINVAL; + } + + dev_info(dev, "initialize bluetooth chip\n"); + + gpio_request(bt_on, "bt_on"); + gpio_direction_output(bt_on, 0); + msleep(11); + gpio_set_value(bt_on, 1); + + gpio_request(bt_wake, "bt_wake"); + gpio_direction_output(bt_wake, 1); + + gpio_request(bt_host_wake, "bt_host_wake"); + gpio_direction_input(bt_host_wake); + + rfkill->shutdown_name = "bluetooth_shutdown"; + rfkill->shutdown_gpio = bt_wake; + + rfkill->rfkill_dev = rfkill_alloc("bluetooth-rfkill", dev, RFKILL_TYPE_BLUETOOTH, + &wand_rfkill_ops, rfkill); + if (!rfkill->rfkill_dev) { + ret = -ENOMEM; + goto bt_fail_free_gpio; + } + + ret = rfkill_register(rfkill->rfkill_dev); + if (ret < 0) + goto bt_fail_unregister; + + dev_info(dev, "bluetooth-rfkill registered.\n"); + + return 0; + +bt_fail_unregister: + rfkill_destroy(rfkill->rfkill_dev); +bt_fail_free_gpio: + if (gpio_is_valid(bt_on)) gpio_free(bt_on); + if (gpio_is_valid(bt_wake)) gpio_free(bt_wake); + if (gpio_is_valid(bt_host_wake)) gpio_free(bt_host_wake); + + return ret; +} + +static int wand_rfkill_probe(struct platform_device *pdev) +{ + struct wand_rfkill_data *rfkill; + struct pinctrl *pinctrl; + int ret; + + dev_info(&pdev->dev, "Wandboard rfkill initialization\n"); + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "no device tree node\n"); + return -ENODEV; + } + + rfkill = kzalloc(sizeof(*rfkill) * 2, GFP_KERNEL); + if (!rfkill) + return -ENOMEM; + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + int ret = PTR_ERR(pinctrl); + dev_err(&pdev->dev, "failed to get default pinctrl: %d\n", ret); + return ret; + } + + /* setup WiFi */ + ret = wand_rfkill_wifi_probe(&pdev->dev, pdev->dev.of_node, &rfkill[0]); + if (ret < 0) + goto fail_free_rfkill; + + /* setup bluetooth */ + ret = wand_rfkill_bt_probe(&pdev->dev, pdev->dev.of_node, &rfkill[1]); + if (ret < 0) + goto fail_unregister_wifi; + + platform_set_drvdata(pdev, rfkill); + + return 0; + +fail_unregister_wifi: + if (rfkill[1].rfkill_dev) { + rfkill_unregister(rfkill[1].rfkill_dev); + rfkill_destroy(rfkill[1].rfkill_dev); + } + + /* TODO free gpio */ + +fail_free_rfkill: + kfree(rfkill); + + return ret; +} + +static int wand_rfkill_remove(struct platform_device *pdev) +{ + struct wand_rfkill_data *rfkill = platform_get_drvdata(pdev); + + dev_info(&pdev->dev, "Module unloading\n"); + + if (!rfkill) + return 0; + + /* WiFi */ + if (gpio_is_valid(rfkill[0].shutdown_gpio)) + gpio_free(rfkill[0].shutdown_gpio); + + rfkill_unregister(rfkill[0].rfkill_dev); + rfkill_destroy(rfkill[0].rfkill_dev); + + /* Bt */ + if (gpio_is_valid(rfkill[1].shutdown_gpio)) + gpio_free(rfkill[1].shutdown_gpio); + + rfkill_unregister(rfkill[1].rfkill_dev); + rfkill_destroy(rfkill[1].rfkill_dev); + + kfree(rfkill); + + return 0; +} + +static struct of_device_id wand_rfkill_match[] = { + { .compatible = "wand,imx6q-wandboard-rfkill", }, + { .compatible = "wand,imx6dl-wandboard-rfkill", }, + { .compatible = "wand,imx6qdl-wandboard-rfkill", }, + {} +}; + +static struct platform_driver wand_rfkill_driver = { + .driver = { + .name = "wandboard-rfkill", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(wand_rfkill_match), + }, + .probe = wand_rfkill_probe, + .remove = wand_rfkill_remove +}; + +module_platform_driver(wand_rfkill_driver); + +MODULE_AUTHOR("Vladimir Ermakov "); +MODULE_DESCRIPTION("Wandboard rfkill driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-omap2/omap_device.c b/arch/arm/mach-omap2/omap_device.c index f7ff3b9..efcce61 100644 --- a/arch/arm/mach-omap2/omap_device.c +++ b/arch/arm/mach-omap2/omap_device.c @@ -123,8 +123,8 @@ static int omap_device_build_from_dt(struct platform_device *pdev) struct omap_device *od; struct omap_hwmod *oh; struct device_node *node = pdev->dev.of_node; - const char *oh_name; - int oh_cnt, i, ret = 0; + const char *oh_name, *rst_name; + int oh_cnt, dstr_cnt, i, ret = 0; bool device_active = false; oh_cnt = of_property_count_strings(node, "ti,hwmods"); @@ -175,6 +175,26 @@ static int omap_device_build_from_dt(struct platform_device *pdev) omap_device_enable(pdev); pm_runtime_set_active(&pdev->dev); } + dstr_cnt = + of_property_count_strings(node, "ti,deassert-hard-reset"); + if (dstr_cnt > 0) { + for (i = 0; i < dstr_cnt; i += 2) { + of_property_read_string_index( + node, "ti,deassert-hard-reset", i, + &oh_name); + of_property_read_string_index( + node, "ti,deassert-hard-reset", i+1, + &rst_name); + oh = omap_hwmod_lookup(oh_name); + if (!oh) { + dev_warn(&pdev->dev, + "Cannot parse deassert property for '%s'\n", + oh_name); + break; + } + omap_hwmod_deassert_hardreset(oh, rst_name); + } + } odbfd_exit1: kfree(hwmods); @@ -191,12 +211,21 @@ static int _omap_device_notifier_call(struct notifier_block *nb, { struct platform_device *pdev = to_platform_device(dev); struct omap_device *od; - int err; + int i, err; switch (event) { case BUS_NOTIFY_DEL_DEVICE: - if (pdev->archdata.od) - omap_device_delete(pdev->archdata.od); + od = to_omap_device(pdev); + if (!od) + break; + + for (i = 0; i < od->hwmods_cnt; i++) { + /* shutdown hwmods */ + omap_hwmod_shutdown(od->hwmods[i]); + /* we don't remove clocks cause there's no API to do so */ + /* no harm done, since they will not be created next time */ + } + omap_device_delete(od); break; case BUS_NOTIFY_UNBOUND_DRIVER: od = to_omap_device(pdev); @@ -770,6 +799,8 @@ int omap_device_idle(struct platform_device *pdev) struct omap_device *od; od = to_omap_device(pdev); + if (!od) + return 0; if (od->_state != OMAP_DEVICE_STATE_ENABLED) { dev_warn(&pdev->dev, diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index d89b8af..0dea684 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -234,6 +234,17 @@ config ARM_TEGRA124_CPUFREQ help This adds the CPUFreq driver support for Tegra124 SOCs. +config ARM_TI_CPUFREQ + tristate "Texas Instruments CPUFreq support" + depends on ARCH_OMAP2PLUS + help + This driver enables valid OPPs on the running platform based on + values contained within the SoC in use. Enable this in order to + use the cpufreq-dt driver on all Texas Instruments platforms that + provide dt based operating-points-v2 tables with opp-supported-hw + data provided. Required for cpufreq support on AM335x, AM437x, + DRA7x, and AM57x platforms. + config ARM_PXA2xx_CPUFREQ tristate "Intel PXA2xx CPUfreq driver" depends on PXA27x || PXA25x diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 0a9b6a0..5b1b6ec 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o obj-$(CONFIG_ARM_STI_CPUFREQ) += sti-cpufreq.o obj-$(CONFIG_ARM_TEGRA20_CPUFREQ) += tegra20-cpufreq.o obj-$(CONFIG_ARM_TEGRA124_CPUFREQ) += tegra124-cpufreq.o +obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-cpufreq.o obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o diff --git b/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c new file mode 100644 index 0000000..e47b452 --- /dev/null +++ b/drivers/cpufreq/ti-cpufreq.c @@ -0,0 +1,254 @@ +/* + * TI CPUFreq/OPP hw-supported driver + * + * Copyright (C) 2016 Texas Instruments, Inc. + * Dave Gerlach + * + * 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 + +#define REVISION_MASK (0xF << 28) +#define REVISION_SHIFT 28 + +#define DRA7_EFUSE_HAS_OD_MPU_OPP 11 +#define DRA7_EFUSE_HAS_HIGH_MPU_OPP 15 +#define DRA7_EFUSE_HAS_ALL_MPU_OPP 23 + +#define DRA7_EFUSE_NOM_MPU_OPP BIT(0) +#define DRA7_EFUSE_OD_MPU_OPP BIT(1) +#define DRA7_EFUSE_HIGH_MPU_OPP BIT(2) + +#define VERSION_COUNT 2 + +static struct ti_cpufreq_data { + struct device *cpu; + struct regmap *opp_efuse; + struct regmap *revision; +} opp_data; + +static struct ti_cpufreq_soc_data { + unsigned long (*efuse_xlate)(unsigned long efuse); +} *soc_data; + +static unsigned long amx3_efuse_xlate(unsigned long efuse) +{ + /* AM335x and AM437x use "OPP disable" bits, so invert */ + return ~efuse; +} + +static unsigned long dra7_efuse_xlate(unsigned long efuse) +{ + unsigned long calculated_efuse = DRA7_EFUSE_NOM_MPU_OPP; + + /* + * The efuse on dra7 and am57 parts contains a specific + * value indicating the highest available OPP. + */ + + switch (efuse) { + case DRA7_EFUSE_HAS_ALL_MPU_OPP: + case DRA7_EFUSE_HAS_HIGH_MPU_OPP: + calculated_efuse |= DRA7_EFUSE_HIGH_MPU_OPP; + case DRA7_EFUSE_HAS_OD_MPU_OPP: + calculated_efuse |= DRA7_EFUSE_OD_MPU_OPP; + } + + return calculated_efuse; +} + +static struct ti_cpufreq_soc_data amx3_soc_data = { + .efuse_xlate = amx3_efuse_xlate, +}; + +static struct ti_cpufreq_soc_data dra7_soc_data = { + .efuse_xlate = dra7_efuse_xlate, +}; + +/** + * ti_cpufreq_get_efuse() - Parse and return efuse value present on SoC + * @efuse_value: Set to the value parsed from efuse + * + * Returns error code if efuse not read properly. + */ +static int ti_cpufreq_get_efuse(u32 *efuse_value) +{ + struct device *dev = opp_data.cpu; + struct device_node *np = dev->of_node; + unsigned int efuse_offset; + u32 efuse, efuse_mask, efuse_shift; + int ret; + + ret = of_property_read_u32_index(np, "ti,syscon-efuse", + 1, &efuse_offset); + if (ret) { + dev_err(dev, + "No efuse offset provided %s: %d\n", + np->full_name, ret); + return ret; + } + + ret = of_property_read_u32_index(np, "ti,syscon-efuse", 2, + &efuse_mask); + if (ret) + efuse_mask = 0xffffffff; + + ret = of_property_read_u32_index(np, "ti,syscon-efuse", 3, + &efuse_shift); + if (ret) + efuse_shift = 0; + + ret = regmap_read(opp_data.opp_efuse, efuse_offset, &efuse); + if (ret) { + dev_err(dev, + "Failed to read the efuse value from syscon: %d\n", + ret); + return ret; + } + + efuse = (efuse & efuse_mask) >> efuse_shift; + + *efuse_value = soc_data->efuse_xlate(efuse); + + return 0; +} + +/** + * ti_cpufreq_get_rev() - Parse and return rev value present on SoC + * @revision_value: Set to the value parsed from revision register + * + * Returns error code if revision not read properly. + */ +static int ti_cpufreq_get_rev(u32 *revision_value) +{ + struct device *dev = opp_data.cpu; + struct device_node *np = dev->of_node; + unsigned int revision_offset; + u32 revision; + int ret; + + ret = of_property_read_u32_index(np, "ti,syscon-rev", + 1, &revision_offset); + if (ret) { + dev_err(dev, + "No revision offset provided %s [%d]\n", + np->full_name, ret); + return ret; + } + + ret = regmap_read(opp_data.revision, revision_offset, &revision); + if (ret) { + dev_err(dev, + "Failed to read the revision number from syscon: %d\n", + ret); + return ret; + } + + *revision_value = BIT((revision & REVISION_MASK) >> REVISION_SHIFT); + + return 0; +} + +static int ti_cpufreq_setup_syscon_registers(void) +{ + struct device *dev = opp_data.cpu; + struct device_node *np = dev->of_node; + + opp_data.opp_efuse = syscon_regmap_lookup_by_phandle(np, + "ti,syscon-efuse"); + if (IS_ERR(opp_data.opp_efuse)) { + dev_dbg(dev, "\"ti,syscon-efuse\" is missing, cannot use OPPv2 table.\n"); + return PTR_ERR(opp_data.opp_efuse); + } + + opp_data.revision = syscon_regmap_lookup_by_phandle(np, + "ti,syscon-rev"); + if (IS_ERR(opp_data.revision)) { + dev_dbg(dev, "\"ti,syscon-rev\" is missing, cannot use OPPv2 table.\n"); + return PTR_ERR(opp_data.revision); + } + + return 0; +} + +static struct ti_cpufreq_soc_data *ti_cpufreq_get_soc_data(void) +{ + if (of_machine_is_compatible("ti,am33xx") || + of_machine_is_compatible("ti,am4372")) + return &amx3_soc_data; + else if (of_machine_is_compatible("ti,dra7")) + return &dra7_soc_data; + else + return NULL; +} + +static int ti_cpufreq_init(void) +{ + int ret; + u32 version[VERSION_COUNT]; + + soc_data = ti_cpufreq_get_soc_data(); + if (!soc_data) + return -ENODEV; + + opp_data.cpu = get_cpu_device(0); + if (!opp_data.cpu) { + pr_err("%s: Failed to get device for CPU0\n", __func__); + return -ENODEV; + } + + if (!of_get_property(opp_data.cpu->of_node, "operating-points-v2", + NULL)) { + dev_info(opp_data.cpu, "OPP-v2 not supported, cpufreq-dt will attempt to use legacy tables.\n"); + goto register_cpufreq_dt; + } + + ret = ti_cpufreq_setup_syscon_registers(); + if (ret) + goto register_cpufreq_dt; + + /* + * OPPs determine whether or not they are supported based on + * two metrics: + * 0 - SoC Revision + * 1 - eFuse value + */ + ret = ti_cpufreq_get_rev(&version[0]); + if (ret) + return ret; + + ret = ti_cpufreq_get_efuse(&version[1]); + if (ret) + return ret; + + ret = dev_pm_opp_set_supported_hw(opp_data.cpu, version, VERSION_COUNT); + if (ret) { + dev_err(opp_data.cpu, "Failed to set supported hardware\n"); + return ret; + } + +register_cpufreq_dt: + platform_device_register_simple("cpufreq-dt", -1, NULL, 0); + + return 0; +} +module_init(ti_cpufreq_init); + +MODULE_DESCRIPTION("TI CPUFreq/OPP hw-supported driver"); +MODULE_AUTHOR("Dave Gerlach "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 3d89e60..a004269 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -35,6 +35,26 @@ config EXTCON_AXP288 Say Y here to enable support for USB peripheral detection and USB MUX switching by X-Power AXP288 PMIC. +config EXTCON_DT_CON + tristate "Device Tree Overlay based connector" + depends on OF || COMPILE_TEST + depends on OF_OVERLAY + help + Say Y here to enable Device Tree Overlay based connector + +config EXTCON_DT_CON_PROXY + tristate "Device Tree Connector Generic proxy driver" + select EXTCONF_DT_CON + help + Say Y here to enable Device Tree generic proxy connector driver + +config EXTCON_DT_CON_GPIO + tristate "Device Tree Connector GPIO proxy driver" + depends on GPIOLIB && OF_GPIO + select EXTCONF_DT_GPIO + help + Say Y here to enable Device Tree GPIO proxy connector driver + config EXTCON_GPIO tristate "GPIO extcon support" depends on GPIOLIB || COMPILE_TEST diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 2a0e4f4..7db472d 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -6,6 +6,9 @@ obj-$(CONFIG_EXTCON) += extcon.o obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o obj-$(CONFIG_EXTCON_AXP288) += extcon-axp288.o +obj-$(CONFIG_EXTCON_DT_CON) += extcon-dt-con.o +obj-$(CONFIG_EXTCON_DT_CON_GPIO)+= extcon-dt-con-gpio.o +obj-$(CONFIG_EXTCON_DT_CON_PROXY)+= extcon-dt-con-proxy.o obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o obj-$(CONFIG_EXTCON_MAX3355) += extcon-max3355.o diff --git b/drivers/extcon/extcon-dt-con-gpio.c b/drivers/extcon/extcon-dt-con-gpio.c new file mode 100644 index 0000000..1c1bb54 --- /dev/null +++ b/drivers/extcon/extcon-dt-con-gpio.c @@ -0,0 +1,510 @@ +/* + * Copyright (C) 2016 Konsulko Group + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 "extcon-dt-con.h" + +/* format: */ +#define GPIO_CELLS_NUM 3 + +struct dtcon_gpio_func_data { + struct dtcon_function *function; + int gpio_base; + int gpio_cells; +}; + +/* indexed by gpio number */ +struct dtcon_gpio_pin_data { + struct dtcon_pin *dtcp; + struct device_node *chip_np; + struct gpio_chip *chip; /* when requested */ + int hwnum; + char *label; +}; + +struct dtcon_gpio_data { + struct platform_device *pdev; + struct gpio_chip chip; + struct dtcon_proxy *proxy; + struct dtcon_gpio_pin_data *pin_data; +}; +#define to_dtcon_gpio_data(x) container_of((x), struct dtcon_gpio_data, chip) + +/* match when we got the same device node */ +static int dtcon_find_gpiochip(struct gpio_chip *gc, void *data) +{ + return gc->of_node == data; +} + +static struct gpio_chip *dtcon_gpio_get_gpiochip(struct dtcon_gpio_data *dtcg, + unsigned offset) +{ + struct dtcon_gpio_pin_data *pin_data; + struct dtcon_pin *dtcp; + + if (offset >= dtcg->chip.ngpio) + return NULL; + + pin_data = &dtcg->pin_data[offset]; + dtcp = pin_data->dtcp; + + return gpiochip_find(pin_data->chip_np, dtcon_find_gpiochip); +} + +/* methods */ + +static int dtcon_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct dtcon_gpio_data *dtcg = to_dtcon_gpio_data(chip); + struct device *dev = &dtcg->pdev->dev; + struct dtcon_gpio_pin_data *pin_data; + struct gpio_chip *gc; + int err; + + if (offset >= dtcg->chip.ngpio) + return -EINVAL; + + pin_data = &dtcg->pin_data[offset]; + gc = pin_data->chip; + err = gc->request(gc, pin_data->hwnum); + if (IS_ERR_VALUE(err)) { + dev_err(dev, "%s(%u) - %d failed\n", __func__, offset, err); + return err; + } + + dev_dbg(dev, "%s(%u) -> %d\n", __func__, offset, err); + + return err; +} + +static void dtcon_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct dtcon_gpio_data *dtcg = to_dtcon_gpio_data(chip); + struct device *dev = &dtcg->pdev->dev; + struct dtcon_gpio_pin_data *pin_data; + struct gpio_chip *gc; + + if (offset >= dtcg->chip.ngpio) + return; + + pin_data = &dtcg->pin_data[offset]; + gc = pin_data->chip; + + if (gc->free) + gc->free(gc, pin_data->hwnum); + + dev_dbg(dev, "%s(%u)\n", __func__, offset); + +} + +static int dtcon_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct dtcon_gpio_data *dtcg = to_dtcon_gpio_data(chip); + struct device *dev = &dtcg->pdev->dev; + struct dtcon_gpio_pin_data *pin_data; + struct gpio_chip *gc; + int err; + + /* if we're still in probe? return input */ + if (platform_get_drvdata(dtcg->pdev) != dtcg) + return 1; + + BUG_ON(!dtcg); + if (offset >= dtcg->chip.ngpio) + return -EINVAL; + pin_data = &dtcg->pin_data[offset]; + gc = pin_data->chip; + if (!gc->get_direction) + return -EINVAL; + + err = gc->get_direction(gc, pin_data->hwnum); + if (IS_ERR_VALUE(err)) { + dev_err(dev, "%s(%u) - %d failed\n", __func__, offset, err); + return err; + } + + dev_dbg(dev, "%s(%u) -> %d\n", __func__, offset, err); + + return err; +} + +static int dtcon_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct dtcon_gpio_data *dtcg = to_dtcon_gpio_data(chip); + struct device *dev = &dtcg->pdev->dev; + struct dtcon_gpio_pin_data *pin_data; + struct gpio_chip *gc; + int err; + + if (offset >= dtcg->chip.ngpio) + return -EINVAL; + + pin_data = &dtcg->pin_data[offset]; + gc = pin_data->chip; + if (!gc->direction_input) + return -EINVAL; + + err = gc->direction_input(gc, pin_data->hwnum); + if (IS_ERR_VALUE(err)) { + dev_err(dev, "%s(%u) - %d failed\n", __func__, offset, err); + return err; + } + + dev_dbg(dev, "%s(%u) -> %d\n", __func__, offset, err); + + return err; +} + +static int dtcon_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct dtcon_gpio_data *dtcg = to_dtcon_gpio_data(chip); + struct device *dev = &dtcg->pdev->dev; + struct dtcon_gpio_pin_data *pin_data; + struct gpio_chip *gc; + int err; + + if (offset >= dtcg->chip.ngpio) + return -EINVAL; + + pin_data = &dtcg->pin_data[offset]; + gc = pin_data->chip; + if (!gc->get) + return -EINVAL; + + err = gc->get(gc, pin_data->hwnum); + if (IS_ERR_VALUE(err)) { + dev_err(dev, "%s(%u) - %d failed\n", __func__, offset, err); + return err; + } + + dev_dbg(dev, "%s(%u) -> %d\n", __func__, offset, err); + + return err; +} + +static int dtcon_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct dtcon_gpio_data *dtcg = to_dtcon_gpio_data(chip); + struct device *dev = &dtcg->pdev->dev; + struct dtcon_gpio_pin_data *pin_data; + struct gpio_chip *gc; + int err; + + if (offset >= dtcg->chip.ngpio) + return -EINVAL; + + pin_data = &dtcg->pin_data[offset]; + gc = pin_data->chip; + if (!gc->direction_output) + return -EINVAL; + + err = gc->direction_output(gc, pin_data->hwnum, value); + if (IS_ERR_VALUE(err)) { + dev_err(dev, "%s(%u) - %d failed\n", __func__, offset, err); + return err; + } + + dev_dbg(dev, "%s(%u, %d) -> %d\n", __func__, offset, value, err); + + return err; +} + +static int dtcon_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, + unsigned debounce) +{ + struct dtcon_gpio_data *dtcg = to_dtcon_gpio_data(chip); + struct device *dev = &dtcg->pdev->dev; + struct dtcon_gpio_pin_data *pin_data; + struct gpio_chip *gc; + int err; + + if (offset >= dtcg->chip.ngpio) + return -EINVAL; + + pin_data = &dtcg->pin_data[offset]; + gc = pin_data->chip; + if (!gc->set_debounce) + return -EINVAL; + + err = gc->set_debounce(gc, pin_data->hwnum, debounce); + if (IS_ERR_VALUE(err)) { + dev_err(dev, "%s(%u) - %d failed\n", __func__, offset, err); + return err; + } + + dev_dbg(dev, "%s(%u, %d) -> %d\n", __func__, offset, debounce, err); + + return err; +} + +static void dtcon_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct dtcon_gpio_data *dtcg = to_dtcon_gpio_data(chip); + struct device *dev = &dtcg->pdev->dev; + struct dtcon_gpio_pin_data *pin_data; + struct gpio_chip *gc; + int err; + + if (offset >= dtcg->chip.ngpio) + return; + + pin_data = &dtcg->pin_data[offset]; + gc = pin_data->chip; + if (!gc->set) + return; + + gc->set(gc, pin_data->hwnum, value); + + dev_dbg(dev, "%s(%u, %d) -> %d\n", __func__, offset, value, err); +} + +/* gpio function methods */ +static int dtcon_gpio_function_init(struct dtcon_function *dtcf) +{ + struct dtcon_data *dtcd = dtcf->dtcd; + struct device *dev = &dtcd->pdev->dev; + struct dtcon_gpio_func_data *dtcgf = NULL; + int err; + u32 num; + + if (!dtcf->np) { + dev_err(dev, "No gpio function configuration node\n"); + return -EINVAL; + } + + dtcgf = kzalloc(sizeof(*dtcgf), GFP_KERNEL); + if (!dtcgf) { + dev_err(dev, "No memory for function data\n"); + return -ENOMEM; + } + dtcf->data = dtcgf; + + err = of_property_read_u32(dtcf->np, "gpio-base", &num); + if (err) { + dev_err(dev, "No gpio-base in function configuration\n"); + err = -EINVAL; + goto err_out; + } + + dtcgf->gpio_base = num; + + /* format: */ + dtcgf->gpio_cells = GPIO_CELLS_NUM; + + return 0; + +err_out: + kfree(dtcgf); + return err; +} + +static void dtcon_gpio_function_fini(struct dtcon_function *dtcf) +{ + struct dtcon_gpio_func_data *dtcgf = dtcf->data; + + kfree(dtcgf); +} + +static int dtcon_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct dtcon_gpio_data *dtcg; + struct dtcon_data *dtcd; + struct dtcon_function *dtcf; + struct dtcon_gpio_func_data *dtcgf; + struct dtcon_proxy *proxy; + struct dtcon_pin *dtcp; + struct dtcon_gpio_pin_data *pin_data; + struct of_phandle_args args; + const void *pin_regs; + int i, err, count, regsz, index; + + if (!np) { + dev_err(dev, "No OF configuration node\n"); + return -EINVAL; + } + + count = of_property_count_elems_of_size(np, "pin-list", sizeof(u32)); + pin_regs = of_get_property(np, "pin-list", NULL); + if (count <= 0 || !pin_regs) { + dev_err(dev, "Invalid configuration\n"); + return -EINVAL; + } + + dtcg = devm_kzalloc(dev, sizeof(*dtcg), GFP_KERNEL); + if (!dtcg) { + dev_err(dev, "Failed to allocate device data\n"); + return -ENOMEM; + } + dtcg->pdev = pdev; + + proxy = dtcon_proxy_create(pdev, "gpio", dtcon_gpio_function_init); + if (IS_ERR(proxy)) + return PTR_ERR(proxy); + + proxy->data = dtcg; + dtcg->proxy = proxy; + + dtcf = dtcg->proxy->dtcf; + dtcgf = dtcf->data; + dtcd = dtcf->dtcd; + + /* the count must be a multiple of address cell numbers */ + if ((count % dtcd->connector_address_cells) != 0) { + dev_err(dev, "Bad pin-list \n"); + err = -EINVAL; + goto out_destroy_proxy; + } + + count /= dtcd->connector_address_cells; + + dtcg->pin_data = devm_kzalloc(dev, sizeof(*dtcg->pin_data) * count, + GFP_KERNEL); + if (!dtcg->pin_data) { + dev_err(dev, "Failed to allocate pin data\n"); + err = -ENOMEM; + goto out_destroy_proxy; + } + + dtcg->chip.request = dtcon_gpio_request; + dtcg->chip.free = dtcon_gpio_free; + dtcg->chip.get_direction = dtcon_gpio_get_direction; + dtcg->chip.direction_input = dtcon_gpio_direction_input; + dtcg->chip.get = dtcon_gpio_get; + dtcg->chip.direction_output = dtcon_gpio_direction_output; + dtcg->chip.set_debounce = dtcon_gpio_set_debounce; + dtcg->chip.set = dtcon_gpio_set; + dtcg->chip.label = np->name; + dtcg->chip.base = dtcgf->gpio_base; + dtcg->chip.ngpio = count; + dtcg->chip.of_node = of_node_get(np); + + /* request all pins */ + regsz = sizeof(u32) * dtcd->connector_address_cells; + for (i = 0; i < count; i++, pin_regs += regsz) { + + pin_data = &dtcg->pin_data[i]; + + dtcp = dtcon_proxy_pin_request(proxy, pin_regs, regsz, 0); + if (IS_ERR(dtcp)) { + dev_err(dev, "could not request gpio #%d\n", i); + err = PTR_ERR(dtcp); + goto out_release_pin; + } + + pin_data->dtcp = dtcp; + + err = of_parse_phandle_with_fixed_args(dtcp->np, "gpio", 1, 0, &args); + if (err) { + dev_err(dev, "could not #%d parse gpio property\n", i); + goto out_release_pin; + } + pin_data->chip_np = args.np; + pin_data->hwnum = args.args[0]; + + pin_data->chip = dtcon_gpio_get_gpiochip(dtcg, i); + if (!pin_data->chip) { + dev_err(dev, "gpio #%d index #%d not found\n", + i, index); + /* the other device might not come up yet */ + err = -EPROBE_DEFER; + goto out_release_pin; + } + pin_data->label = devm_kasprintf(dev, GFP_KERNEL, + "%s:%d", np->name, i); + if (!pin_data->label) { + dev_err(dev, "gpio #%d index #%d label alloc\n", + i, index); + /* the other device might not come up yet */ + err = -ENOMEM; + goto out_release_pin; + } + + dev_err(dev, "gpio #%d -> %-8s @ %s\n", + i, dtcp->regstr, dtcp->np->name); + } + + err = gpiochip_add_data(&dtcg->chip, dtcg); + if (err) { + dev_err(dev, "Could not register gpio chip %d\n", err); + goto out_release_pin; + } + + /* advance base */ + dtcgf->gpio_base += count; + + platform_set_drvdata(pdev, dtcg); + + return 0; + +out_release_pin: + while (i-- >= 0) { + pin_data = &dtcg->pin_data[i]; + of_node_put(pin_data->chip_np); + dtcon_proxy_pin_release(proxy, pin_data->dtcp); + } + +out_destroy_proxy: + dtcon_proxy_destroy(dtcg->proxy, dtcon_gpio_function_fini); + return err; +} + +static int dtcon_gpio_remove(struct platform_device *pdev) +{ + struct dtcon_gpio_data *dtcg = platform_get_drvdata(pdev); + struct dtcon_proxy *proxy = dtcg->proxy; + struct dtcon_gpio_pin_data *pin_data; + int i; + + for (i = 0; i < dtcg->chip.ngpio; i++) { + pin_data = &dtcg->pin_data[i]; + of_node_put(pin_data->chip_np); + dtcon_proxy_pin_release(proxy, pin_data->dtcp); + } + + gpiochip_remove(&dtcg->chip); + dtcon_proxy_destroy(dtcg->proxy, dtcon_gpio_function_fini); + return 0; +} + +static const struct of_device_id dtcon_gpio_of_match[] = { + { .compatible = "dtcon-gpio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, dtcon_gpio_of_match); + +static struct platform_driver dtcon_gpio = { + .probe = dtcon_gpio_probe, + .remove = dtcon_gpio_remove, + .driver = { + .name = "dtcon-gpio", + .owner = THIS_MODULE, + .of_match_table = dtcon_gpio_of_match, + } +}; + +module_platform_driver(dtcon_gpio); +MODULE_AUTHOR("Pantelis Antoniou "); +MODULE_LICENSE("GPL"); diff --git b/drivers/extcon/extcon-dt-con-proxy.c b/drivers/extcon/extcon-dt-con-proxy.c new file mode 100644 index 0000000..dbbbc0e --- /dev/null +++ b/drivers/extcon/extcon-dt-con-proxy.c @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2016 Konsulko Group + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 "extcon-dt-con.h" + +static const struct of_device_id dtcon_proxy_of_match[] = { + { .compatible = "dtcon-uart", .data = "uart" }, + { .compatible = "dtcon-i2c", .data = "i2c" }, + { .compatible = "dtcon-spi", .data = "spi" }, + {}, +}; +MODULE_DEVICE_TABLE(of, dtcon_proxy_of_match); + +struct dtcon_proxy_data { + struct platform_device *pdev; + struct dtcon_proxy *proxy; + struct of_changeset ocs; + struct device_node *paramsnp; + int param_cells; +}; + +static int dtcon_proxy_parse_params(struct dtcon_proxy_data *dtcpd) +{ + struct device *dev = &dtcpd->pdev->dev; + struct dtcon_proxy *proxy = dtcpd->proxy; + struct dtcon_function *dtcf = proxy->dtcf; + struct device_node *np; + struct dtcon_pin *dtcp; + const void *value; + int err, len; + u32 num; + + dtcpd->paramsnp = of_get_child_by_name(dtcf->np, "params"); + + /* no parameters? no problem */ + if (!dtcpd->paramsnp) + return -EINVAL; + + /* get number of param cells */ + if (of_property_read_u32(dtcpd->paramsnp, "#param-cells", &num)) + num = 2; + + dtcpd->param_cells = num; + + /* iterate over list of parameters */ + err = 0; + for_each_child_of_node(dtcpd->paramsnp, np) { + value = of_get_property(dev->of_node, np->name, &len); + if (!value) { + /* ignore non-required parameter */ + if (!of_property_read_bool(np, "required")) + continue; + /* missing required parameter */ + dev_err(dev, "%s: Missing required parameter %s\n", + dtcf->kind, np->name); +err_out: + of_node_put(np); + err = -EINVAL; + goto out; + } + + /* we have a parameter; instantiate it */ + dev_dbg(dev, "%s: param %s\n", dtcf->kind, + np->name); + + /* connector-pin parameter */ + if (of_property_read_bool(np, "connector-pin")) { + dtcp = dtcon_proxy_pin_request(proxy, value, len, 0); + if (!dtcp) { + dev_err(dev, "%s: failed to request %s's pin\n", + dtcf->kind, np->name); + goto err_out; + } + /* no need to keep dtcp around; it's on proxy's list */ + dev_info(dev, "%s: %s pin is @%s\n", dtcf->kind, + np->name, dtcp->np->name); + + dtcp->param = of_node_get(np); + /* need to iterate over the mux options for matches */ + } + } +out: + return err; +} + +static int dtcon_proxy_grade_single(struct dtcon_proxy_data *dtcpd, + struct device_node *fnp, struct device_node *devnp, + struct device_node *muxnp) +{ + struct device *dev = &dtcpd->pdev->dev; + struct dtcon_pin *dtcp; + struct dtcon_proxy *proxy = dtcpd->proxy; + int ret, paramsz, paramcells, i, count, len, match, score; + const char *param, *gpio_prop; + const __be32 *paramp; + bool avail; + u32 ph; + + if (!devnp) + return 0; + + paramcells = dtcpd->param_cells; + paramsz = paramcells * sizeof(u32); + + score = 0; + list_for_each_entry(dtcp, &proxy->proxy_pin_list, proxy_node) { + + param = dtcp->param->name; + paramp = of_get_property(muxnp, param, &len); + + /* no such parameter? */ + if (!paramp) { + /* GPIO parameters only on disabled devices */ + if (of_device_is_available(devnp)) { + dev_err(dev, "param %s with enabled device\n", param); + score = 0; + break; + } + + ret = of_property_read_string(dtcp->param, + "gpio-property", &gpio_prop); + if (ret) { + dev_err(dev, "no param %s\n", param); + score = 0; + break; + } + dtcp->proxy_gpio = true; + /* gpio matches have a low score */ + score++; + continue; + } + + /* not-there? malformed? bail out */ + if ((len % paramsz)) { + dev_err(dev, "malformed param %s\n", param); + score = 0; + break; + } + + count = len / paramsz; + + match = 0; + for (i = 0; i < count; i++, paramp += paramcells) { + /* get phandle of connector pin */ + ph = be32_to_cpu(paramp[0]); + if (ph != dtcp->np->phandle) + continue; + + avail = of_device_is_available(devnp); + /* when not preeneabled + * -> device must be disabled + * when preenabled + * -> device must be enabled + */ + if (of_property_read_bool(muxnp, "pre-enabled")) + match = avail; + else + match = !avail; + + if (match) { + dtcp->proxy_gpio = false; + /* pinmux changes only on disabled devices */ + if (!avail) { + ph = be32_to_cpu(paramp[1]); + dtcp->match_mux = ph; + } + score += (1 << 16); + break; + } + } + + if (!match) + return 0; + } + + return score; +} + +static int dtcon_proxy_instantiate(struct dtcon_proxy_data *dtcpd) +{ + struct device *dev = &dtcpd->pdev->dev; + struct dtcon_proxy *proxy = dtcpd->proxy; + struct dtcon_function *dtcf = proxy->dtcf; + struct device_node *np, *fnp, *devnp, *muxnp, *best_devnp; + struct dtcon_pin *dtcp; + int err, len, count, score, best_score, arg_len; + const void *value; + const char *gpio_prop; + const void *arg_value; + void *data; + __be32 *muxp; + + err = 0; + + /* iterate over the device targets */ + best_score = 0; + best_devnp = NULL; + for_each_child_of_node(dtcf->np, fnp) { + devnp = of_parse_phandle(fnp, "device", 0); + if (!devnp) + continue; + for_each_child_of_node(fnp, muxnp) { + + score = dtcon_proxy_grade_single(dtcpd, fnp, + devnp, muxnp); + + if (score <= best_score) + continue; + + /* new best score */ + best_score = score; + of_node_put(best_devnp); + best_devnp = of_node_get(devnp); + list_for_each_entry(dtcp, &proxy->proxy_pin_list, + proxy_node) { + + /* if it's a GPIO do not select it */ + if (dtcp->proxy_gpio) + continue; + + of_node_put(dtcp->pinctrl); + dtcp->pinctrl = of_find_node_by_phandle( + dtcp->match_mux); + dtcp->match_mux = 0; + } + } + of_node_put(devnp); + } + + if (best_score == 0) { + dev_info(dev, "no matching proxy config\n"); + return -ENODEV; + } + + devnp = best_devnp; + best_devnp = NULL; + + dev_dbg(dev, "matches device %s\n", devnp->full_name); + + /* need to copy subdevices? */ + if (of_property_read_bool(dtcpd->paramsnp, "copy-subdevices")) { + for_each_available_child_of_node(dev->of_node, np) { + err = of_changeset_node_move(&dtcpd->ocs, np, devnp); + if (err) { + dev_err(dev, "Failed to move nodes\n"); + of_node_put(np); + goto err_out; + } + } + } + + /* changes only allowed to non-activated devices */ + if (!of_device_is_available(devnp)) { + + /* iterate over parameters */ + + /* iterate over list of parameters */ + for_each_child_of_node(dtcpd->paramsnp, np) { + if (!of_property_read_bool(np, "copy")) + continue; + + value = of_get_property(dev->of_node, np->name, &len); + if (!value) { + if (!of_property_read_bool(np, "required")) + continue; + dev_err(dev, "Missing required property %s\n", + np->name); + of_node_put(np); + err = -ENODEV; + goto err_out; + } + + of_changeset_update_property_copy(&dtcpd->ocs, devnp, + np->name, value, len); + + } + + /* generate pinctrl for each pin of the connector */ + if (of_property_read_bool(dtcpd->paramsnp, + "generate-pinctrl")) { + /* count the pinctrl entries we're going to need */ + count = 0; + list_for_each_entry(dtcp, &proxy->proxy_pin_list, + proxy_node) { + if (dtcp->pinctrl) + count++; + } + + if (count > 0) { + muxp = kzalloc(sizeof(u32) * count, GFP_KERNEL); + if (!muxp) { + dev_err(dev, + "Failed to allocate pinmux\n"); + err = -ENOMEM; + goto err_out; + } + + count = 0; + list_for_each_entry(dtcp, + &proxy->proxy_pin_list, + proxy_node) { + if (!dtcp->pinctrl) + continue; + muxp[count++] = cpu_to_be32( + dtcp->pinctrl->phandle); + } + + of_changeset_update_property_string(&dtcpd->ocs, + devnp, "pinctrl-names", + "default"); + of_changeset_update_property_copy(&dtcpd->ocs, + devnp, "pinctrl-0", muxp, + sizeof(u32) * count); + + kfree(muxp); + } + } + + /* fill in gpio properties if required */ + list_for_each_entry(dtcp, &proxy->proxy_pin_list, proxy_node) { + if (!dtcp->proxy_gpio) + continue; + err = of_property_read_string(dtcp->param, + "gpio-property", &gpio_prop); + if (err) { + dev_err(dev, + "Failed to read gpio-property name %s\n", + dtcp->param->name); + goto err_out; + } + value = of_get_property(dtcp->np, "gpio", &len); + if (!value) { + dev_err(dev, + "Failed on gpio name %s - %s\n", + dtcp->param->name, dtcp->np->name); + err = -EINVAL; + goto err_out; + } + arg_value = of_get_property(dtcp->param, + "gpio-args", &arg_len); + if (!arg_value) { + dev_err(dev, + "Failed on gpio-args name %s - %s\n", + dtcp->param->name, dtcp->param->name); + err = -EINVAL; + goto err_out; + } + data = kmalloc(len + arg_len, GFP_KERNEL); + if (!data) { + dev_err(dev, + "Failed to allocate %s - %s\n", + dtcp->param->name, dtcp->param->name); + err = -ENOMEM; + goto err_out; + } + memcpy(data, value, len); + memcpy(data + len, arg_value, arg_len); + of_changeset_add_property_copy(&dtcpd->ocs, devnp, + gpio_prop, data, len + arg_len); + kfree(data); + } + + /* enabled target device and go */ + of_changeset_update_property_string(&dtcpd->ocs, devnp, + "status", "okay"); + } + + of_changeset_apply(&dtcpd->ocs); + + of_node_put(devnp); + of_node_put(muxnp); + + return 0; + +err_out: + of_changeset_destroy(&dtcpd->ocs); + + of_node_put(devnp); + + return err; +} + +static int dtcon_proxy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dtcon_proxy *proxy; + struct dtcon_function *dtcf; + struct dtcon_proxy_data *dtcpd; + const struct of_device_id *of_id; + const char *function; + int err; + + of_id = of_match_device(dtcon_proxy_of_match, dev); + if (!of_id) { + dev_err(dev, "Could not match device\n"); + return -ENODEV; + } + function = of_id->data; + + dtcpd = devm_kzalloc(dev, sizeof(*dtcpd), GFP_KERNEL); + if (!dtcpd) { + dev_err(dev, "Failed to allocate device data\n"); + return -ENOMEM; + } + dtcpd->pdev = pdev; + of_changeset_init(&dtcpd->ocs); + + proxy = dtcon_proxy_create(pdev, function, NULL); + if (IS_ERR(proxy)) + return PTR_ERR(proxy); + + dtcpd->proxy = proxy; + + dtcf = proxy->dtcf; + + platform_set_drvdata(pdev, dtcpd); + + err = dtcon_proxy_parse_params(dtcpd); + if (err) { + dev_err(dev, "Failed to parse params\n"); + return err; + } + + err = dtcon_proxy_instantiate(dtcpd); + if (err) { + dev_err(dev, "Failed to instantiate device\n"); + return err; + } + + dev_info(dev, "OK\n"); + + return 0; +} + +static int dtcon_proxy_remove(struct platform_device *pdev) +{ + struct dtcon_proxy_data *dtcpd = platform_get_drvdata(pdev); + + of_changeset_revert(&dtcpd->ocs); + of_changeset_destroy(&dtcpd->ocs); + dtcon_proxy_destroy(dtcpd->proxy, NULL); + of_node_put(dtcpd->paramsnp); + return 0; +} + +static struct platform_driver dtcon_proxy = { + .probe = dtcon_proxy_probe, + .remove = dtcon_proxy_remove, + .driver = { + .name = "dtcon-proxy", + .owner = THIS_MODULE, + .of_match_table = dtcon_proxy_of_match, + } +}; + +module_platform_driver(dtcon_proxy); +MODULE_AUTHOR("Pantelis Antoniou "); +MODULE_LICENSE("GPL"); diff --git b/drivers/extcon/extcon-dt-con.c b/drivers/extcon/extcon-dt-con.c new file mode 100644 index 0000000..fbc1a35 --- /dev/null +++ b/drivers/extcon/extcon-dt-con.c @@ -0,0 +1,491 @@ +/* + * drivers/extcon/extcon-dt-conn.c + * + * Device Tree Overlay based connector driver. + * + * Copyright (C) 2015 Konsulko Group. + * Author: Pantelis Antoniou + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 "extcon-dt-con.h" + +static const unsigned int dtcon_cable[] = { + EXTCON_NONE, +}; + +static int dtcon_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node, *npn; + struct dtcon_data *dtcd; + struct dtcon_pin *dtcp; + const __be32 *regp; + int regsz, regcells, sz, count, len, i, j, err; + char *s, *e; + u32 num; + + if (!np) { + dev_err(dev, "No device node\n"); + return -ENODEV; + } + + dtcd = devm_kzalloc(dev, sizeof(*dtcd), GFP_KERNEL); + if (!dtcd) { + dev_err(dev, "Failed to allocated data structure\n"); + return -ENOMEM; + } + INIT_LIST_HEAD(&dtcd->pin_list); + INIT_LIST_HEAD(&dtcd->function_list); + + dtcd->pdev = pdev; + dtcd->edev = devm_extcon_dev_allocate(dev, dtcon_cable); + if (IS_ERR(dtcd->edev)) { + dev_err(dev, "Failed to allocate extcon device\n"); + err = PTR_ERR(dtcd->edev); + return err; + } + + err = devm_extcon_dev_register(dev, dtcd->edev); + if (err < 0) { + dev_err(dev, "Failed to register extcon device\n"); + return err; + } + + platform_set_drvdata(pdev, dtcd); + + dtcd->connector = of_get_child_by_name(np, "connector"); + dtcd->functions = of_get_child_by_name(np, "functions"); + dtcd->plugged = of_get_child_by_name(np, "plugged"); + + err = -EINVAL; + + if (!dtcd->connector || !dtcd->functions || !dtcd->plugged) { + dev_err(dev, "Bad OF configuration\n"); + goto out_err; + } + + /* if no property return 1 */ + err = of_property_read_u32(dtcd->connector, "#address-cells", &num); + if (err < 0) + num = 1; + dtcd->connector_address_cells = num; + + /* if no property return 0 */ + err = of_property_read_u32(dtcd->connector, "#size-cells", &num); + if (err < 0) + num = 0; + dtcd->connector_size_cells = num; + if (dtcd->connector_size_cells != 0) { + dev_err(dev, "Only #size-cells = <0>; supported for now\n"); + err = -EINVAL; + goto out_err; + } + + regcells = dtcd->connector_address_cells; + regsz = sizeof(u32) * regcells; + for_each_child_of_node(dtcd->connector, npn) { + regp = of_get_property(npn, "reg", &len); + if (!regp || (len % regsz)) { + dev_err(dev, "Bad connector pin @%s\n", npn->name); + of_node_put(npn); + goto out_err; + } + + dtcp = devm_kzalloc(dev, sizeof(*dtcp), GFP_KERNEL); + if (!dtcp) { + dev_err(dev, "Failed to allocate pin node\n"); + err = -ENOMEM; + of_node_put(npn); + goto out_err; + } + /* keep it and add it to the list */ + dtcp->np = of_node_get(npn); + + /* convert to number of reg ranges */ + count = len / regsz; + /* max reg is ,<4294967295> */ + sz = strlen(",<4294967295>") * count * regcells + 2; + dtcp->regstr = devm_kmalloc(dev, sz, GFP_KERNEL); + if (!dtcp->regstr) { + dev_err(dev, "Failed to allocate reg string\n"); + err = -ENOMEM; + of_node_put(npn); + goto out_err; + } + s = dtcp->regstr; + e = dtcp->regstr + sz; + for (i = 0; i < count; i++, regp += regcells) { + if (i > 0) + *s++ = ','; + *s++ = '<'; + for (j = 0; j < regcells; j++) { + len = scnprintf(s, e - s, "%u", + be32_to_cpu(regp[j])); + s += len; + if (j + 1 < regcells) + *s++ = ' '; + } + *s++ = '>'; + } + *s = '\0'; + + list_add_tail(&dtcp->node, &dtcd->pin_list); + } + + /* populate anything in plugged */ + of_platform_default_populate(dev->of_node, NULL, dev); + + dev_info(dev, "OK\n"); + + return 0; + +out_err: + if (dtcd) { + list_for_each_entry_reverse(dtcp, &dtcd->pin_list, node) + of_node_put(dtcp->np); + of_node_put(dtcd->connector); + of_node_put(dtcd->functions); + of_node_put(dtcd->plugged); + } + + /* no need to manually deallocate, it's all devm */ + return err; +} + +static int dtcon_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dtcon_data *dtcd = platform_get_drvdata(pdev); + struct dtcon_pin *dtcp; + + dev_info(dev, "removing\n"); + + list_for_each_entry_reverse(dtcp, &dtcd->pin_list, node) + of_node_put(dtcp->np); + of_node_put(dtcd->connector); + of_node_put(dtcd->functions); + of_node_put(dtcd->plugged); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int dtcon_resume(struct device *dev) +{ + struct dtcon_data *dtcd; + + dtcd = dev_get_drvdata(dev); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(dtcon_pm_ops, NULL, dtcon_resume); + +static const struct of_device_id dtcon_of_match[] = { + { + .compatible = "extcon,dt-con", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, dtcon_of_match); + +static struct platform_driver dtcon_driver = { + .probe = dtcon_probe, + .remove = dtcon_remove, + .driver = { + .name = "extcon-dt-con", + .pm = &dtcon_pm_ops, + .of_match_table = of_match_ptr(dtcon_of_match), + }, +}; + +module_platform_driver(dtcon_driver); + +/* get the dtcon data from a platform device that's a descendant */ +struct dtcon_data *dtcon_data_from_platform_device(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dtcon_data *dtcd; + struct device *parent; + + /* we need to find the connector which must be a parent */ + for (parent = dev->parent; parent; parent = parent->parent) { + if (parent->of_node && + of_device_is_compatible(parent->of_node, + dtcon_of_match[0].compatible)) { + break; + } + } + + if (!parent) { + dev_err(dev, "Device is not a child of a connector\n"); + return NULL; + } + + /* get pointer to connector data */ + dtcd = platform_get_drvdata(to_platform_device(parent)); + if (!dtcd) { + dev_err(dev, "Missing connector data\n"); + return NULL; + } + + return dtcd; +} +EXPORT_SYMBOL(dtcon_data_from_platform_device); + +struct dtcon_pin *dtcon_pin_lookup(struct dtcon_data *dtcd, + const void *regp, int regsz) +{ + struct dtcon_pin *dtcp = NULL; + const void *value; + int i, len; + + if (!dtcd || !regp) + return NULL; + + /* must be the same size as the address cells */ + if (regsz != dtcd->connector_address_cells * sizeof(u32)) + return NULL; + + /* no size cells supported */ + if (dtcd->connector_size_cells != 0) + return NULL; + + /* find pin with the given reg */ + list_for_each_entry(dtcp, &dtcd->pin_list, node) { + value = of_get_property(dtcp->np, "reg", &len); + + /* check that it exists and is a multiple */ + if (!value || (len % regsz) != 0) + continue; + + /* iterate over all */ + for (i = 0; i < len; i += regsz, value += regsz) { + if (memcmp(value, regp, regsz) == 0) + goto found; + } + } + + return NULL; + +found: + return dtcp; +} +EXPORT_SYMBOL(dtcon_pin_lookup); + +struct dtcon_pin *dtcon_pin_lookup_by_node(struct dtcon_data *dtcd, + struct device_node *np) +{ + struct dtcon_pin *dtcp = NULL; + + if (!dtcd) + return NULL; + + /* release all pins of this proxy */ + list_for_each_entry(dtcp, &dtcd->pin_list, node) { + if (dtcp->np == np) + return dtcp; + } + + return NULL; +} +EXPORT_SYMBOL(dtcon_pin_lookup_by_node); + +struct dtcon_pin *dtcon_pin_lookup_by_phandle(struct dtcon_data *dtcd, + phandle phandle) +{ + struct device_node *np = of_find_node_by_phandle(phandle); + struct dtcon_pin *dtcp; + + if (!np) + return NULL; + + dtcp = dtcon_pin_lookup_by_node(dtcd, np); + of_node_put(np); + return dtcp; +} +EXPORT_SYMBOL(dtcon_pin_lookup_by_phandle); + +struct dtcon_pin *dtcon_proxy_pin_request(struct dtcon_proxy *proxy, + const void *regp, int regsz, unsigned int flags) +{ + struct dtcon_data *dtcd; + struct dtcon_pin *dtcp; + struct device *dev; + + if (!proxy || !regp) + return ERR_PTR(-EINVAL); + + dtcd = proxy->dtcf->dtcd; + dev = &dtcd->pdev->dev; + dtcp = dtcon_pin_lookup(dtcd, regp, regsz); + if (!dtcp) + return ERR_PTR(-ENXIO); + + dtcp->proxy = proxy; + dtcp->data = NULL; + list_add_tail(&dtcp->proxy_node, &proxy->proxy_pin_list); + + return dtcp; +} +EXPORT_SYMBOL(dtcon_proxy_pin_request); + +int dtcon_proxy_pin_release(struct dtcon_proxy *proxy, struct dtcon_pin *dtcp) +{ + if (!proxy || !dtcp || dtcp->proxy != proxy) + return -EINVAL; + + list_del(&dtcp->proxy_node); + dtcp->proxy = NULL; + dtcp->data = NULL; + + /* proxy device data release */ + of_node_put(dtcp->param); + of_node_put(dtcp->pinctrl); + dtcp->param = NULL; + dtcp->pinctrl = NULL; + return 0; +} +EXPORT_SYMBOL(dtcon_proxy_pin_release); + +/* dtcon function methods */ +struct dtcon_function *dtcon_function_lookup(struct dtcon_data *dtcd, + const char *kind) +{ + struct dtcon_function *dtcf; + + /* lookup function by kind */ + list_for_each_entry(dtcf, &dtcd->function_list, node) { + if (strcmp(dtcf->kind, kind) == 0) + return dtcf; + } + + return NULL; +} +EXPORT_SYMBOL(dtcon_function_lookup); + +/* proxy device methods */ +struct dtcon_proxy *dtcon_proxy_create(struct platform_device *pdev, + const char *kind, + int (*func_init)(struct dtcon_function *dtcf)) +{ + struct device *dev = &pdev->dev; + struct dtcon_data *dtcd; + struct dtcon_proxy *proxy; + struct dtcon_function *dtcf; + int err; + + /* get connector data from a parent */ + dtcd = dtcon_data_from_platform_device(pdev); + if (!dtcd) { + err = -ENOMEM; + goto err_out; + } + + proxy = kzalloc(sizeof(*proxy), GFP_KERNEL); + if (!proxy) { + dev_err(dev, "Failed to allocate proxy data\n"); + err = -ENOMEM; + goto err_out; + } + + /* lookup function */ + dtcf = dtcon_function_lookup(dtcd, kind); + if (dtcf == NULL) { + dtcf = kzalloc(sizeof(*dtcf), GFP_KERNEL); + if (!dtcf) { + dev_err(dev, "Failed to allocate proxy data\n"); + err = -ENOMEM; + goto err_free_proxy; + } + dtcf->dtcd = dtcd; + dtcf->kind = kind; + dtcf->np = of_get_child_by_name(dtcd->functions, kind); + INIT_LIST_HEAD(&dtcf->proxy_list); + + if (func_init) { + err = (*func_init)(dtcf); + if (err) { + dev_err(dev, "function init failed\n"); + kfree(dtcf); + goto err_free_proxy; + } + } + list_add_tail(&dtcf->node, &dtcd->function_list); + } + + proxy->pdev = pdev; + proxy->dtcf = dtcf; + + INIT_LIST_HEAD(&proxy->proxy_pin_list); + + list_add_tail(&proxy->node, &dtcf->proxy_list); + + return proxy; + +err_free_proxy: + kfree(proxy); +err_out: + return ERR_PTR(err); +} +EXPORT_SYMBOL(dtcon_proxy_create); + +void dtcon_proxy_destroy(struct dtcon_proxy *proxy, + void (*func_fini)(struct dtcon_function *dtcf)) +{ + struct dtcon_data *dtcd; + struct dtcon_function *dtcf; + struct dtcon_pin *dtcp, *dtcpn; + + if (!proxy || !proxy->dtcf) + return; + + dtcf = proxy->dtcf; + dtcd = dtcf->dtcd; + + list_del(&proxy->node); + + /* last proxy; remove function */ + if (list_empty(&dtcf->proxy_list)) { + list_del(&dtcf->node); + if (func_fini) + (*func_fini)(dtcf); + of_node_put(dtcf->np); + kfree(dtcf); + } + + /* release all pins of this proxy */ + list_for_each_entry_safe_reverse(dtcp, dtcpn, + &proxy->proxy_pin_list, proxy_node) + dtcon_proxy_pin_release(proxy, dtcp); + + kfree(proxy); +} +EXPORT_SYMBOL(dtcon_proxy_destroy); + +MODULE_AUTHOR("Pantelis Antoniou "); +MODULE_DESCRIPTION("DT connector extcon driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:extcon-dt-con"); diff --git b/drivers/extcon/extcon-dt-con.h b/drivers/extcon/extcon-dt-con.h new file mode 100644 index 0000000..9f08f1d --- /dev/null +++ b/drivers/extcon/extcon-dt-con.h @@ -0,0 +1,93 @@ +/* + * dt-con.h + * + * Copyright (c) 2016 Konsulko Group + * + * 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 __LINUX_EXTCON_DT_CON_H +#define __LINUX_EXTCON_DT_CON_H + +#include +#include + +struct dtcon_proxy; + +struct dtcon_pin { + struct list_head node; + struct device_node *np; + char *regstr; /* connector reg prop */ + + /* owner of the pin */ + struct dtcon_proxy *proxy; + struct list_head proxy_node; + void *data; + + /* proxy device data */ + struct device_node *param; + + /* pinctrl config device node */ + bool proxy_gpio; + struct device_node *pinctrl; + phandle match_mux; +}; + +struct dtcon_data { + struct platform_device *pdev; + struct extcon_dev *edev; + + struct device_node *connector; + unsigned int connector_address_cells; + unsigned int connector_size_cells; + + struct device_node *functions; + struct device_node *plugged; + struct list_head pin_list; + + struct list_head function_list; +}; + +struct dtcon_function { + struct dtcon_data *dtcd; + struct list_head node; + const char *kind; + struct device_node *np; + struct list_head proxy_list; + void *data; +}; + +struct dtcon_proxy { + struct platform_device *pdev; + struct dtcon_function *dtcf; + struct list_head node; + + struct list_head proxy_pin_list; + + void *data; +}; + +struct dtcon_data *dtcon_data_from_platform_device( + struct platform_device *pdev); + +struct dtcon_proxy *dtcon_proxy_create(struct platform_device *pdev, + const char *kind, + int (*func_init)(struct dtcon_function *dtcf)); +void dtcon_proxy_destroy(struct dtcon_proxy *proxy, + void (*func_fini)(struct dtcon_function *dtcf)); + +struct dtcon_pin *dtcon_pin_lookup(struct dtcon_data *dtcd, + const void *regp, int regsz); +struct dtcon_pin *dtcon_pin_lookup_by_node(struct dtcon_data *dtcd, + struct device_node *np); +struct dtcon_pin *dtcon_pin_lookup_by_phandle(struct dtcon_data *dtcd, + phandle phandle); +struct dtcon_pin *dtcon_proxy_pin_request(struct dtcon_proxy *proxy, + const void *regp, int regsz, unsigned int flags); +int dtcon_proxy_pin_release(struct dtcon_proxy *proxy, struct dtcon_pin *dtcp); + + +#endif /* __LINUX_EXTCON_DT_CON_H */ diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5d457ff..e9fd289 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -86,6 +86,20 @@ config GPIO_SYSFS Kernel drivers may also request that a particular GPIO be exported to userspace; this can be useful when debugging. +config GPIO_OF_HELPER + bool "GPIO OF helper device (EXPERIMENTAL)" + depends on OF_GPIO + help + Say Y here to add an GPIO OF helper driver + + Allows you specify a GPIO helper based on OF + which allows simple export of GPIO functionality + in user-space. + + Features include, value set/get, direction control, + interrupt/value change poll support, event counting + and others. + config GPIO_GENERIC tristate diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 991598e..19ae808 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_GPIOLIB) += gpiolib-legacy.o obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o +obj-$(CONFIG_GPIO_OF_HELPER) += gpio-of-helper.o # Device drivers. Generally keep list sorted alphabetically obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o diff --git b/drivers/gpio/gpio-of-helper.c b/drivers/gpio/gpio-of-helper.c new file mode 100644 index 0000000..a9f260b --- /dev/null +++ b/drivers/gpio/gpio-of-helper.c @@ -0,0 +1,429 @@ +/* + * GPIO OF based helper + * + * A simple DT based driver to provide access to GPIO functionality + * to user-space via sysfs. + * + * Copyright (C) 2013 Pantelis Antoniou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* fwd decl. */ +struct gpio_of_helper_info; + +enum gpio_type { + GPIO_TYPE_INPUT = 0, + GPIO_TYPE_OUTPUT = 1, +}; + +struct gpio_of_entry { + int id; + struct gpio_of_helper_info *info; + struct device_node *node; + enum gpio_type type; + int gpio; + enum of_gpio_flags gpio_flags; + int irq; + const char *name; + atomic64_t counter; + unsigned int count_flags; +#define COUNT_RISING_EDGE (1 << 0) +#define COUNT_FALLING_EDGE (1 << 1) +}; + +struct gpio_of_helper_info { + struct platform_device *pdev; + struct idr idr; +}; + +static const struct of_device_id gpio_of_helper_of_match[] = { + { + .compatible = "gpio-of-helper", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, gpio_of_helper_of_match); + +static ssize_t gpio_of_helper_show_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_of_helper_info *info = platform_get_drvdata(pdev); + struct gpio_of_entry *entry; + char *p, *e; + int id, n; + + p = buf; + e = p + PAGE_SIZE; + n = 0; + idr_for_each_entry(&info->idr, entry, id) { + switch (entry->type) { + case GPIO_TYPE_INPUT: + n = snprintf(p, e - p, "%2d %-24s %3d %-3s %llu\n", + entry->id, entry->name, entry->gpio, "IN", + (unsigned long long) + atomic64_read(&entry->counter)); + break; + case GPIO_TYPE_OUTPUT: + n = snprintf(p, e - p, "%2d %-24s %3d %-3s\n", + entry->id, entry->name, entry->gpio, "OUT"); + break; + } + p += n; + } + + return p - buf; +} + +static DEVICE_ATTR(status, S_IRUGO, + gpio_of_helper_show_status, NULL); + +static irqreturn_t gpio_of_helper_handler(int irq, void *ptr) +{ + struct gpio_of_entry *entry = ptr; + + /* caution - low speed interfaces only! */ + atomic64_inc(&entry->counter); + + return IRQ_HANDLED; +} + +static struct gpio_of_entry * +gpio_of_entry_create(struct gpio_of_helper_info *info, + struct device_node *node) +{ + struct platform_device *pdev = info->pdev; + struct device *dev = &pdev->dev; + struct gpio_of_entry *entry; + int err, gpio, irq; + unsigned int req_flags, count_flags, irq_flags; + enum gpio_type type; + enum of_gpio_flags gpio_flags; + const char *name; + + /* get the type of the node first */ + if (of_property_read_bool(node, "input")) + type = GPIO_TYPE_INPUT; + else if (of_property_read_bool(node, "output")) + type = GPIO_TYPE_OUTPUT; + else { + dev_err(dev, "Not valid gpio node type\n"); + err = -EINVAL; + goto err_bad_node; + } + + /* get the name */ + err = of_property_read_string(node, "gpio-name", &name); + if (err != 0) { + dev_err(dev, "Failed to get name property\n"); + goto err_bad_node; + } + + err = of_get_named_gpio_flags(node, "gpio", 0, &gpio_flags); + if (IS_ERR_VALUE(err)) { + dev_err(dev, "Failed to get gpio property of '%s'\n", name); + goto err_bad_node; + } + gpio = err; + + req_flags = 0; + count_flags = 0; + + /* set the request flags */ + switch (type) { + case GPIO_TYPE_INPUT: + req_flags = GPIOF_DIR_IN | GPIOF_EXPORT; + if (of_property_read_bool(node, "count-falling-edge")) + count_flags |= COUNT_FALLING_EDGE; + if (of_property_read_bool(node, "count-rising-edge")) + count_flags |= COUNT_RISING_EDGE; + break; + case GPIO_TYPE_OUTPUT: + req_flags = GPIOF_DIR_OUT | GPIOF_EXPORT; + if (of_property_read_bool(node, "init-high")) + req_flags |= GPIOF_OUT_INIT_HIGH; + else if (of_property_read_bool(node, "init-low")) + req_flags |= GPIOF_OUT_INIT_LOW; + break; + } + if (of_property_read_bool(node, "dir-changeable")) + req_flags |= GPIOF_EXPORT_CHANGEABLE; + + /* request the gpio */ + err = devm_gpio_request_one(dev, gpio, req_flags, name); + if (err != 0) { + dev_err(dev, "Failed to request gpio '%s'\n", name); + goto err_bad_node; + } + + irq = -1; + irq_flags = 0; + + /* counter mode requested - need an interrupt */ + if (count_flags != 0) { + irq = gpio_to_irq(gpio); + if (IS_ERR_VALUE(irq)) { + dev_err(dev, "Failed to request gpio '%s'\n", name); + goto err_bad_node; + } + + if (count_flags & COUNT_RISING_EDGE) + irq_flags |= IRQF_TRIGGER_RISING; + if (count_flags & COUNT_FALLING_EDGE) + irq_flags |= IRQF_TRIGGER_FALLING; + } + +// if (!idr_pre_get(&info->idr, GFP_KERNEL)) { +// dev_err(dev, "Failed on idr_pre_get of '%s'\n", name); +// err = -ENOMEM; +// goto err_no_mem; +// } + + idr_preload(GFP_KERNEL); + + entry = devm_kzalloc(dev, sizeof(*entry), GFP_KERNEL); + if (entry == NULL) { + dev_err(dev, "Failed to allocate gpio entry of '%s'\n", name); + err = -ENOMEM; + goto err_no_mem; + } + + entry->id = -1; + entry->info = info; + entry->node = of_node_get(node); /* get node reference */ + entry->type = type; + entry->gpio = gpio; + entry->gpio_flags = gpio_flags; + entry->irq = irq; + entry->name = name; + + /* interrupt enable is last thing done */ + if (irq >= 0) { + atomic64_set(&entry->counter, 0); + entry->count_flags = count_flags; + err = devm_request_irq(dev, irq, gpio_of_helper_handler, + irq_flags, name, entry); + if (err != 0) { + dev_err(dev, "Failed to request irq of '%s'\n", name); + goto err_no_irq; + } + } + + /* all done; insert */ +// err = idr_get_new(&info->idr, entry, &entry->id); +// if (IS_ERR_VALUE(err)) { +// dev_err(dev, "Failed to idr_get_new of '%s'\n", name); +// goto err_fail_idr; +// } + + err = idr_alloc(&info->idr, entry, 0, 0, GFP_NOWAIT); + if (err >= 0) + entry->id = err; + + idr_preload_end(); + + if (err < 0) { + dev_err(dev, "Failed to idr_get_new of '%s'\n", name); + goto err_fail_idr; + } + + dev_info(dev, "Allocated GPIO id=%d\n", entry->id); + + return entry; + +err_fail_idr: + /* nothing to do */ +err_no_irq: + /* release node ref */ + of_node_put(node); + /* nothing else needs to be done, devres handles it */ +err_no_mem: +err_bad_node: + return ERR_PTR(err); +} + +static int gpio_of_entry_destroy(struct gpio_of_entry *entry) +{ + struct gpio_of_helper_info *info = entry->info; + struct platform_device *pdev = info->pdev; + struct device *dev = &pdev->dev; + + dev_info(dev, "Destroying GPIO id=%d\n", entry->id); + + /* remove from the IDR */ + idr_remove(&info->idr, entry->id); + + /* remove node ref */ + of_node_put(entry->node); + + /* free gpio */ + devm_gpio_free(dev, entry->gpio); + + /* gree irq */ + if (entry->irq >= 0) + devm_free_irq(dev, entry->irq, entry); + + /* and free */ + devm_kfree(dev, entry); + + return 0; +} + +static int gpio_of_helper_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_of_helper_info *info; + struct gpio_of_entry *entry; + struct device_node *pnode = pdev->dev.of_node; + struct device_node *cnode; + struct pinctrl *pinctrl; + int err; + + /* we only support OF */ + if (pnode == NULL) { + dev_err(&pdev->dev, "No platform of_node!\n"); + return -ENODEV; + } + + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + /* special handling for probe defer */ + if (PTR_ERR(pinctrl) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_warn(&pdev->dev, + "pins are not configured from the driver\n"); + } + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (info == NULL) { + dev_err(&pdev->dev, "Failed to allocate info\n"); + err = -ENOMEM; + goto err_no_mem; + } + platform_set_drvdata(pdev, info); + info->pdev = pdev; + + idr_init(&info->idr); + + err = device_create_file(dev, &dev_attr_status); + if (err != 0) { + dev_err(dev, "Failed to create status sysfs attribute\n"); + goto err_no_sysfs; + } + + for_each_child_of_node(pnode, cnode) { + + entry = gpio_of_entry_create(info, cnode); + if (IS_ERR_OR_NULL(entry)) { + dev_err(dev, "Failed to create gpio entry\n"); + err = PTR_ERR(entry); + goto err_fail_entry; + } + } + + dev_info(&pdev->dev, "ready\n"); + + return 0; +err_fail_entry: + device_remove_file(&pdev->dev, &dev_attr_status); +err_no_sysfs: +err_no_mem: + return err; +} + +static int gpio_of_helper_remove(struct platform_device *pdev) +{ + struct gpio_of_helper_info *info = platform_get_drvdata(pdev); + struct gpio_of_entry *entry; + int id; + + dev_info(&pdev->dev, "removing\n"); + + device_remove_file(&pdev->dev, &dev_attr_status); + + id = 0; + idr_for_each_entry(&info->idr, entry, id) { + /* destroy each and every one */ + gpio_of_entry_destroy(entry); + } + + return 0; +} + +#ifdef CONFIG_PM +//#ifdef CONFIG_PM_RUNTIME +static int gpio_of_helper_runtime_suspend(struct device *dev) +{ + /* place holder */ + return 0; +} + +static int gpio_of_helper_runtime_resume(struct device *dev) +{ + /* place holder */ + return 0; +} +//#endif /* CONFIG_PM_RUNTIME */ + +static struct dev_pm_ops gpio_of_helper_pm_ops = { + SET_RUNTIME_PM_OPS(gpio_of_helper_runtime_suspend, + gpio_of_helper_runtime_resume, NULL) +}; +#define GPIO_OF_HELPER_PM_OPS (&gpio_of_helper_pm_ops) +#else +#define GPIO_OF_HELPER_PM_OPS NULL +#endif /* CONFIG_PM */ + +struct platform_driver gpio_of_helper_driver = { + .probe = gpio_of_helper_probe, + .remove = gpio_of_helper_remove, + .driver = { + .name = "gpio-of-helper", + .owner = THIS_MODULE, + .pm = GPIO_OF_HELPER_PM_OPS, + .of_match_table = gpio_of_helper_of_match, + }, +}; + +module_platform_driver(gpio_of_helper_driver); + +MODULE_AUTHOR("Pantelis Antoniou "); +MODULE_DESCRIPTION("GPIO OF Helper driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gpio-of-helper"); diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig index 22c7ed6..d19bd80 100644 --- a/drivers/gpu/drm/i2c/Kconfig +++ b/drivers/gpu/drm/i2c/Kconfig @@ -7,6 +7,12 @@ config DRM_I2C_ADV7511 help Support for the Analog Device ADV7511(W) and ADV7513 HDMI encoders. +config DRM_I2C_ADIHDMI + tristate "ADI HDMI encoder" + default m if DRM_TILCDC + help + Support for ADI HDMI encoder. + config DRM_I2C_CH7006 tristate "Chrontel ch7006 TV encoder" default m if DRM_NOUVEAU diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile index 2c72eb5..5a509ae 100644 --- a/drivers/gpu/drm/i2c/Makefile +++ b/drivers/gpu/drm/i2c/Makefile @@ -10,3 +10,6 @@ obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o tda998x-y := tda998x_drv.o obj-$(CONFIG_DRM_I2C_NXP_TDA998X) += tda998x.o + +adihdmi-y := adihdmi_drv.o +obj-$(CONFIG_DRM_I2C_ADIHDMI) += adihdmi.o diff --git b/drivers/gpu/drm/i2c/adihdmi.h b/drivers/gpu/drm/i2c/adihdmi.h new file mode 100644 index 0000000..58d9a2b --- /dev/null +++ b/drivers/gpu/drm/i2c/adihdmi.h @@ -0,0 +1,289 @@ +/* + * Analog Devices ADIHDMI HDMI transmitter driver + * + * Copyright 2012 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#ifndef __DRM_I2C_ADIHDMI_H__ +#define __DRM_I2C_ADIHDMI_H__ + +#include + +#define ADIHDMI_REG_CHIP_REVISION 0x00 +#define ADIHDMI_REG_N0 0x01 +#define ADIHDMI_REG_N1 0x02 +#define ADIHDMI_REG_N2 0x03 +#define ADIHDMI_REG_SPDIF_FREQ 0x04 +#define ADIHDMI_REG_CTS_AUTOMATIC1 0x05 +#define ADIHDMI_REG_CTS_AUTOMATIC2 0x06 +#define ADIHDMI_REG_CTS_MANUAL0 0x07 +#define ADIHDMI_REG_CTS_MANUAL1 0x08 +#define ADIHDMI_REG_CTS_MANUAL2 0x09 +#define ADIHDMI_REG_AUDIO_SOURCE 0x0a +#define ADIHDMI_REG_AUDIO_CONFIG 0x0b +#define ADIHDMI_REG_I2S_CONFIG 0x0c +#define ADIHDMI_REG_I2S_WIDTH 0x0d +#define ADIHDMI_REG_AUDIO_SUB_SRC0 0x0e +#define ADIHDMI_REG_AUDIO_SUB_SRC1 0x0f +#define ADIHDMI_REG_AUDIO_SUB_SRC2 0x10 +#define ADIHDMI_REG_AUDIO_SUB_SRC3 0x11 +#define ADIHDMI_REG_AUDIO_CFG1 0x12 +#define ADIHDMI_REG_AUDIO_CFG2 0x13 +#define ADIHDMI_REG_AUDIO_CFG3 0x14 +#define ADIHDMI_REG_I2C_FREQ_ID_CFG 0x15 +#define ADIHDMI_REG_VIDEO_INPUT_CFG1 0x16 +#define ADIHDMI_REG_CSC_UPPER(x) (0x18 + (x) * 2) +#define ADIHDMI_REG_CSC_LOWER(x) (0x19 + (x) * 2) +#define ADIHDMI_REG_SYNC_DECODER(x) (0x30 + (x)) +#define ADIHDMI_REG_DE_GENERATOR (0x35 + (x)) +#define ADIHDMI_REG_PIXEL_REPETITION 0x3b +#define ADIHDMI_REG_VIC_MANUAL 0x3c +#define ADIHDMI_REG_VIC_SEND 0x3d +#define ADIHDMI_REG_VIC_DETECTED 0x3e +#define ADIHDMI_REG_AUX_VIC_DETECTED 0x3f +#define ADIHDMI_REG_PACKET_ENABLE0 0x40 +#define ADIHDMI_REG_POWER 0x41 +#define ADIHDMI_REG_STATUS 0x42 +#define ADIHDMI_REG_EDID_I2C_ADDR 0x43 +#define ADIHDMI_REG_PACKET_ENABLE1 0x44 +#define ADIHDMI_REG_PACKET_I2C_ADDR 0x45 +#define ADIHDMI_REG_DSD_ENABLE 0x46 +#define ADIHDMI_REG_VIDEO_INPUT_CFG2 0x48 +#define ADIHDMI_REG_INFOFRAME_UPDATE 0x4a +#define ADIHDMI_REG_GC(x) (0x4b + (x)) /* 0x4b - 0x51 */ +#define ADIHDMI_REG_AVI_INFOFRAME_VERSION 0x52 +#define ADIHDMI_REG_AVI_INFOFRAME_LENGTH 0x53 +#define ADIHDMI_REG_AVI_INFOFRAME_CHECKSUM 0x54 +#define ADIHDMI_REG_AVI_INFOFRAME(x) (0x55 + (x)) /* 0x55 - 0x6f */ +#define ADIHDMI_REG_AUDIO_INFOFRAME_VERSION 0x70 +#define ADIHDMI_REG_AUDIO_INFOFRAME_LENGTH 0x71 +#define ADIHDMI_REG_AUDIO_INFOFRAME_CHECKSUM 0x72 +#define ADIHDMI_REG_AUDIO_INFOFRAME(x) (0x73 + (x)) /* 0x73 - 0x7c */ +#define ADIHDMI_REG_INT_ENABLE(x) (0x94 + (x)) +#define ADIHDMI_REG_INT(x) (0x96 + (x)) +#define ADIHDMI_REG_INPUT_CLK_DIV 0x9d +#define ADIHDMI_REG_PLL_STATUS 0x9e +#define ADIHDMI_REG_HDMI_POWER 0xa1 +#define ADIHDMI_REG_HDCP_HDMI_CFG 0xaf +#define ADIHDMI_REG_AN(x) (0xb0 + (x)) /* 0xb0 - 0xb7 */ +#define ADIHDMI_REG_HDCP_STATUS 0xb8 +#define ADIHDMI_REG_BCAPS 0xbe +#define ADIHDMI_REG_BKSV(x) (0xc0 + (x)) /* 0xc0 - 0xc3 */ +#define ADIHDMI_REG_EDID_SEGMENT 0xc4 +#define ADIHDMI_REG_DDC_STATUS 0xc8 +#define ADIHDMI_REG_EDID_READ_CTRL 0xc9 +#define ADIHDMI_REG_BSTATUS(x) (0xca + (x)) /* 0xca - 0xcb */ +#define ADIHDMI_REG_TIMING_GEN_SEQ 0xd0 +#define ADIHDMI_REG_POWER2 0xd6 +#define ADIHDMI_REG_HSYNC_PLACEMENT_MSB 0xfa + +#define ADIHDMI_REG_SYNC_ADJUSTMENT(x) (0xd7 + (x)) /* 0xd7 - 0xdc */ +#define ADIHDMI_REG_TMDS_CLOCK_INV 0xde +#define ADIHDMI_REG_ARC_CTRL 0xdf +#define ADIHDMI_REG_CEC_I2C_ADDR 0xe1 +#define ADIHDMI_REG_CEC_CTRL 0xe2 +#define ADIHDMI_REG_CHIP_ID_HIGH 0xf5 +#define ADIHDMI_REG_CHIP_ID_LOW 0xf6 + +#define ADIHDMI_CSC_ENABLE BIT(7) +#define ADIHDMI_CSC_UPDATE_MODE BIT(5) + +#define ADIHDMI_INT0_HDP BIT(7) +#define ADIHDMI_INT0_VSYNC BIT(5) +#define ADIHDMI_INT0_AUDIO_FIFO_FULL BIT(4) +#define ADIHDMI_INT0_EDID_READY BIT(2) +#define ADIHDMI_INT0_HDCP_AUTHENTICATED BIT(1) + +#define ADIHDMI_INT1_DDC_ERROR BIT(7) +#define ADIHDMI_INT1_BKSV BIT(6) +#define ADIHDMI_INT1_CEC_TX_READY BIT(5) +#define ADIHDMI_INT1_CEC_TX_ARBIT_LOST BIT(4) +#define ADIHDMI_INT1_CEC_TX_RETRY_TIMEOUT BIT(3) +#define ADIHDMI_INT1_CEC_RX_READY3 BIT(2) +#define ADIHDMI_INT1_CEC_RX_READY2 BIT(1) +#define ADIHDMI_INT1_CEC_RX_READY1 BIT(0) + +#define ADIHDMI_ARC_CTRL_POWER_DOWN BIT(0) + +#define ADIHDMI_CEC_CTRL_POWER_DOWN BIT(0) + +#define ADIHDMI_POWER_POWER_DOWN BIT(6) + +#define ADIHDMI_HDMI_CFG_MODE_MASK 0x2 +#define ADIHDMI_HDMI_CFG_MODE_DVI 0x0 +#define ADIHDMI_HDMI_CFG_MODE_HDMI 0x2 + +#define ADIHDMI_AUDIO_SELECT_I2C 0x0 +#define ADIHDMI_AUDIO_SELECT_SPDIF 0x1 +#define ADIHDMI_AUDIO_SELECT_DSD 0x2 +#define ADIHDMI_AUDIO_SELECT_HBR 0x3 +#define ADIHDMI_AUDIO_SELECT_DST 0x4 + +#define ADIHDMI_I2S_SAMPLE_LEN_16 0x2 +#define ADIHDMI_I2S_SAMPLE_LEN_20 0x3 +#define ADIHDMI_I2S_SAMPLE_LEN_18 0x4 +#define ADIHDMI_I2S_SAMPLE_LEN_22 0x5 +#define ADIHDMI_I2S_SAMPLE_LEN_19 0x8 +#define ADIHDMI_I2S_SAMPLE_LEN_23 0x9 +#define ADIHDMI_I2S_SAMPLE_LEN_24 0xb +#define ADIHDMI_I2S_SAMPLE_LEN_17 0xc +#define ADIHDMI_I2S_SAMPLE_LEN_21 0xd + +#define ADIHDMI_SAMPLE_FREQ_44100 0x0 +#define ADIHDMI_SAMPLE_FREQ_48000 0x2 +#define ADIHDMI_SAMPLE_FREQ_32000 0x3 +#define ADIHDMI_SAMPLE_FREQ_88200 0x8 +#define ADIHDMI_SAMPLE_FREQ_96000 0xa +#define ADIHDMI_SAMPLE_FREQ_176400 0xc +#define ADIHDMI_SAMPLE_FREQ_192000 0xe + +#define ADIHDMI_STATUS_POWER_DOWN_POLARITY BIT(7) +#define ADIHDMI_STATUS_HPD BIT(6) +#define ADIHDMI_STATUS_MONITOR_SENSE BIT(5) +#define ADIHDMI_STATUS_I2S_32BIT_MODE BIT(3) + +#define ADIHDMI_PACKET_ENABLE_N_CTS BIT(8+6) +#define ADIHDMI_PACKET_ENABLE_AUDIO_SAMPLE BIT(8+5) +#define ADIHDMI_PACKET_ENABLE_AVI_INFOFRAME BIT(8+4) +#define ADIHDMI_PACKET_ENABLE_AUDIO_INFOFRAME BIT(8+3) +#define ADIHDMI_PACKET_ENABLE_GC BIT(7) +#define ADIHDMI_PACKET_ENABLE_SPD BIT(6) +#define ADIHDMI_PACKET_ENABLE_MPEG BIT(5) +#define ADIHDMI_PACKET_ENABLE_ACP BIT(4) +#define ADIHDMI_PACKET_ENABLE_ISRC BIT(3) +#define ADIHDMI_PACKET_ENABLE_GM BIT(2) +#define ADIHDMI_PACKET_ENABLE_SPARE2 BIT(1) +#define ADIHDMI_PACKET_ENABLE_SPARE1 BIT(0) + +#define ADIHDMI_REG_POWER2_HDP_SRC_MASK 0xc0 +#define ADIHDMI_REG_POWER2_HDP_SRC_BOTH 0x00 +#define ADIHDMI_REG_POWER2_HDP_SRC_HDP 0x40 +#define ADIHDMI_REG_POWER2_HDP_SRC_CEC 0x80 +#define ADIHDMI_REG_POWER2_HDP_SRC_NONE 0xc0 +#define ADIHDMI_REG_POWER2_TDMS_ENABLE BIT(4) +#define ADIHDMI_REG_POWER2_GATE_INPUT_CLK BIT(0) + +#define ADIHDMI_LOW_REFRESH_RATE_NONE 0x0 +#define ADIHDMI_LOW_REFRESH_RATE_24HZ 0x1 +#define ADIHDMI_LOW_REFRESH_RATE_25HZ 0x2 +#define ADIHDMI_LOW_REFRESH_RATE_30HZ 0x3 + +#define ADIHDMI_AUDIO_CFG3_LEN_MASK 0x0f +#define ADIHDMI_I2C_FREQ_ID_CFG_RATE_MASK 0xf0 + +#define ADIHDMI_AUDIO_SOURCE_I2S 0 +#define ADIHDMI_AUDIO_SOURCE_SPDIF 1 + +#define ADIHDMI_I2S_FORMAT_I2S 0 +#define ADIHDMI_I2S_FORMAT_RIGHT_J 1 +#define ADIHDMI_I2S_FORMAT_LEFT_J 2 + +#define ADIHDMI_PACKET(p, x) ((p) * 0x20 + (x)) +#define ADIHDMI_PACKET_SDP(x) ADIHDMI_PACKET(0, x) +#define ADIHDMI_PACKET_MPEG(x) ADIHDMI_PACKET(1, x) +#define ADIHDMI_PACKET_ACP(x) ADIHDMI_PACKET(2, x) +#define ADIHDMI_PACKET_ISRC1(x) ADIHDMI_PACKET(3, x) +#define ADIHDMI_PACKET_ISRC2(x) ADIHDMI_PACKET(4, x) +#define ADIHDMI_PACKET_GM(x) ADIHDMI_PACKET(5, x) +#define ADIHDMI_PACKET_SPARE(x) ADIHDMI_PACKET(6, x) + +enum adihdmi_input_clock { + ADIHDMI_INPUT_CLOCK_1X, + ADIHDMI_INPUT_CLOCK_2X, + ADIHDMI_INPUT_CLOCK_DDR, +}; + +enum adihdmi_input_justification { + ADIHDMI_INPUT_JUSTIFICATION_EVENLY = 0, + ADIHDMI_INPUT_JUSTIFICATION_RIGHT = 1, + ADIHDMI_INPUT_JUSTIFICATION_LEFT = 2, +}; + +enum adihdmi_input_sync_pulse { + ADIHDMI_INPUT_SYNC_PULSE_DE = 0, + ADIHDMI_INPUT_SYNC_PULSE_HSYNC = 1, + ADIHDMI_INPUT_SYNC_PULSE_VSYNC = 2, + ADIHDMI_INPUT_SYNC_PULSE_NONE = 3, +}; + +/** + * enum adihdmi_sync_polarity - Polarity for the input sync signals + * @ADIHDMI_SYNC_POLARITY_PASSTHROUGH: Sync polarity matches that of + * the currently configured mode. + * @ADIHDMI_SYNC_POLARITY_LOW: Sync polarity is low + * @ADIHDMI_SYNC_POLARITY_HIGH: Sync polarity is high + * + * If the polarity is set to either LOW or HIGH the driver will configure the + * ADIHDMI to internally invert the sync signal if required to match the sync + * polarity setting for the currently selected output mode. + * + * If the polarity is set to PASSTHROUGH, the ADIHDMI will route the signal + * unchanged. This is used when the upstream graphics core already generates + * the sync signals with the correct polarity. + */ +enum adihdmi_sync_polarity { + ADIHDMI_SYNC_POLARITY_PASSTHROUGH, + ADIHDMI_SYNC_POLARITY_LOW, + ADIHDMI_SYNC_POLARITY_HIGH, +}; + +/** + * struct adihdmi_link_config - Describes adihdmi hardware configuration + * @input_color_depth: Number of bits per color component (8, 10 or 12) + * @input_colorspace: The input colorspace (RGB, YUV444, YUV422) + * @input_clock: The input video clock style (1x, 2x, DDR) + * @input_style: The input component arrangement variant + * @input_justification: Video input format bit justification + * @clock_delay: Clock delay for the input clock (in ps) + * @embedded_sync: Video input uses BT.656-style embedded sync + * @sync_pulse: Select the sync pulse + * @vsync_polarity: vsync input signal configuration + * @hsync_polarity: hsync input signal configuration + */ +struct adihdmi_link_config { + unsigned int input_color_depth; + enum hdmi_colorspace input_colorspace; + enum adihdmi_input_clock input_clock; + unsigned int input_style; + enum adihdmi_input_justification input_justification; + + int clock_delay; + + bool embedded_sync; + enum adihdmi_input_sync_pulse sync_pulse; + enum adihdmi_sync_polarity vsync_polarity; + enum adihdmi_sync_polarity hsync_polarity; +}; + +/** + * enum adihdmi_csc_scaling - Scaling factor for the ADIHDMI CSC + * @ADIHDMI_CSC_SCALING_1: CSC results are not scaled + * @ADIHDMI_CSC_SCALING_2: CSC results are scaled by a factor of two + * @ADIHDMI_CSC_SCALING_4: CSC results are scalled by a factor of four + */ +enum adihdmi_csc_scaling { + ADIHDMI_CSC_SCALING_1 = 0, + ADIHDMI_CSC_SCALING_2 = 1, + ADIHDMI_CSC_SCALING_4 = 2, +}; + +/** + * struct adihdmi_video_config - Describes adihdmi hardware configuration + * @csc_enable: Whether to enable color space conversion + * @csc_scaling_factor: Color space conversion scaling factor + * @csc_coefficents: Color space conversion coefficents + * @hdmi_mode: Whether to use HDMI or DVI output mode + * @avi_infoframe: HDMI infoframe + */ +struct adihdmi_video_config { + bool csc_enable; + enum adihdmi_csc_scaling csc_scaling_factor; + const uint16_t *csc_coefficents; + + bool hdmi_mode; + struct hdmi_avi_infoframe avi_infoframe; +}; + +#endif /* __DRM_I2C_ADIHDMI_H__ */ diff --git b/drivers/gpu/drm/i2c/adihdmi_drv.c b/drivers/gpu/drm/i2c/adihdmi_drv.c new file mode 100644 index 0000000..6792224 --- /dev/null +++ b/drivers/gpu/drm/i2c/adihdmi_drv.c @@ -0,0 +1,1268 @@ +/* + * Analog Devices ADIHDMI HDMI transmitter driver + * + * Copyright 2012 Analog Devices Inc. + * Copyright 2015 Konsulko Group + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "adihdmi.h" + +#define ADIHDMI_INFOFRAME_PACKETS (0x7900) + +struct adihdmi { + struct i2c_client *i2c_main; + struct i2c_client *i2c_edid; + + struct regmap *regmap; + struct regmap *packet_memory_regmap; + enum drm_connector_status status; + bool powered; + + unsigned int f_tmds; + + unsigned int current_edid_segment; + uint8_t edid_buf[256]; + bool edid_read; + + wait_queue_head_t wq; + struct drm_encoder *encoder; + + bool embedded_sync; + enum adihdmi_sync_polarity vsync_polarity; + enum adihdmi_sync_polarity hsync_polarity; + bool rgb; + + struct edid *edid; + + struct gpio_desc *gpio_pd; +}; + +struct adihdmi2 { + struct adihdmi base; + struct drm_encoder encoder; + struct drm_connector connector; +}; + +/* ADI recommended values for proper operation. */ +static const struct reg_sequence adihdmi_fixed_registers[] = { + { 0x98, 0x03 }, + { 0x9a, 0xe0 }, + { 0x9c, 0x30 }, + { 0x9d, 0x61 }, + { 0xa2, 0xa4 }, + { 0xa3, 0xa4 }, + { 0xe0, 0xd0 }, + { 0xf9, 0x00 }, + { 0x55, 0x02 }, +}; + +/* ----------------------------------------------------------------------------- + * Register access + */ + +static const uint8_t adihdmi_register_defaults[] = { + 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00 */ + 0x00, 0x00, 0x01, 0x0e, 0xbc, 0x18, 0x01, 0x13, + 0x25, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10 */ + 0x46, 0x62, 0x04, 0xa8, 0x00, 0x00, 0x1c, 0x84, + 0x1c, 0xbf, 0x04, 0xa8, 0x1e, 0x70, 0x02, 0x1e, /* 20 */ + 0x00, 0x00, 0x04, 0xa8, 0x08, 0x12, 0x1b, 0xac, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 30 */ + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xb0, + 0x00, 0x50, 0x90, 0x7e, 0x79, 0x70, 0x00, 0x00, /* 40 */ + 0x00, 0xa8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x0d, 0x00, 0x00, 0x00, 0x00, /* 50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 70 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 80 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 90 */ + 0x0b, 0x02, 0x00, 0x18, 0x5a, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x80, 0x08, 0x04, 0x00, 0x00, /* a0 */ + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c0 */ + 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x01, 0x04, + 0x30, 0xff, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, /* d0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, + 0x80, 0x75, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, /* e0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x11, 0x00, /* f0 */ + 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static bool adihdmi_register_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADIHDMI_REG_CHIP_REVISION: + case ADIHDMI_REG_SPDIF_FREQ: + case ADIHDMI_REG_CTS_AUTOMATIC1: + case ADIHDMI_REG_CTS_AUTOMATIC2: + case ADIHDMI_REG_VIC_DETECTED: + case ADIHDMI_REG_VIC_SEND: + case ADIHDMI_REG_AUX_VIC_DETECTED: + case ADIHDMI_REG_STATUS: + case ADIHDMI_REG_GC(1): + case ADIHDMI_REG_INT(0): + case ADIHDMI_REG_INT(1): + case ADIHDMI_REG_PLL_STATUS: + case ADIHDMI_REG_AN(0): + case ADIHDMI_REG_AN(1): + case ADIHDMI_REG_AN(2): + case ADIHDMI_REG_AN(3): + case ADIHDMI_REG_AN(4): + case ADIHDMI_REG_AN(5): + case ADIHDMI_REG_AN(6): + case ADIHDMI_REG_AN(7): + case ADIHDMI_REG_HDCP_STATUS: + case ADIHDMI_REG_BCAPS: + case ADIHDMI_REG_BKSV(0): + case ADIHDMI_REG_BKSV(1): + case ADIHDMI_REG_BKSV(2): + case ADIHDMI_REG_BKSV(3): + case ADIHDMI_REG_BKSV(4): + case ADIHDMI_REG_DDC_STATUS: + case ADIHDMI_REG_BSTATUS(0): + case ADIHDMI_REG_BSTATUS(1): + case ADIHDMI_REG_CHIP_ID_HIGH: + case ADIHDMI_REG_CHIP_ID_LOW: + return true; + } + + return false; +} + +static const struct regmap_config adihdmi_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0xff, + .cache_type = REGCACHE_RBTREE, + .reg_defaults_raw = adihdmi_register_defaults, + .num_reg_defaults_raw = ARRAY_SIZE(adihdmi_register_defaults), + + .volatile_reg = adihdmi_register_volatile, +}; + +/* ----------------------------------------------------------------------------- + * Hardware configuration + */ + + static void adihdmi_audio_setup(struct adihdmi * adihdmi) +{ + /* Select I2S. */ + regmap_write(adihdmi->regmap, ADIHDMI_REG_AUDIO_SOURCE, 0x01); + regmap_write(adihdmi->regmap, ADIHDMI_REG_I2S_CONFIG, 0x84); + + /* Setup clocks for 48KHz. */ + regmap_write(adihdmi->regmap, ADIHDMI_REG_N0, 0x00); + regmap_write(adihdmi->regmap, ADIHDMI_REG_N1, 0x18); + regmap_write(adihdmi->regmap, ADIHDMI_REG_N2, 0x00); + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_I2C_FREQ_ID_CFG, 0xF0, 0x20); + + /* Set audio word length to 24 bits. */ + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_AUDIO_CFG3, 0x0F, 0x0B); + + /* Update audio infoframe. */ + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_INFOFRAME_UPDATE, 0x20, 0x20); + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_AUDIO_INFOFRAME(0), 0x07, 0x01); + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_AUDIO_INFOFRAME(3), 0x1F, 0x00); + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_INFOFRAME_UPDATE, 0x20, 0x00); +} + +static void adihdmi_set_colormap(struct adihdmi *adihdmi, bool enable, + const uint16_t *coeff, + unsigned int scaling_factor) +{ + unsigned int i; + + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_CSC_UPPER(1), + ADIHDMI_CSC_UPDATE_MODE, ADIHDMI_CSC_UPDATE_MODE); + + if (enable) { + for (i = 0; i < 12; ++i) { + regmap_update_bits(adihdmi->regmap, + ADIHDMI_REG_CSC_UPPER(i), + 0x1f, coeff[i] >> 8); + regmap_write(adihdmi->regmap, + ADIHDMI_REG_CSC_LOWER(i), + coeff[i] & 0xff); + } + } + + if (enable) + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_CSC_UPPER(0), + 0xe0, 0x80 | (scaling_factor << 5)); + else + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_CSC_UPPER(0), + 0x80, 0x00); + + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_CSC_UPPER(1), + ADIHDMI_CSC_UPDATE_MODE, 0); +} + +static int adihdmi_packet_enable(struct adihdmi *adihdmi, unsigned int packet) +{ + if (packet & 0xff) + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_PACKET_ENABLE0, + packet, 0xff); + + if (packet & 0xff00) { + packet >>= 8; + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_PACKET_ENABLE1, + packet, 0xff); + } + + return 0; +} + +static int adihdmi_packet_disable(struct adihdmi *adihdmi, unsigned int packet) +{ + if (packet & 0xff) + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_PACKET_ENABLE0, + packet, 0x00); + + if (packet & 0xff00) { + packet >>= 8; + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_PACKET_ENABLE1, + packet, 0x00); + } + + return 0; +} + +/* Coefficients for adihdmi color space conversion */ +static const uint16_t adihdmi_csc_ycbcr_to_rgb[] = { + 0x0734, 0x04ad, 0x0000, 0x1c1b, + 0x1ddc, 0x04ad, 0x1f24, 0x0135, + 0x0000, 0x04ad, 0x087c, 0x1b77, +}; + +static void adihdmi_set_config_csc(struct adihdmi *adihdmi, + struct drm_connector *connector, + bool rgb) +{ + struct adihdmi_video_config config; + bool output_format_422, output_format_ycbcr; + unsigned int mode; + uint8_t infoframe[17]; + + if (adihdmi->edid) + config.hdmi_mode = drm_detect_hdmi_monitor(adihdmi->edid); + else + config.hdmi_mode = false; + + hdmi_avi_infoframe_init(&config.avi_infoframe); + + config.avi_infoframe.scan_mode = HDMI_SCAN_MODE_UNDERSCAN; + + if (rgb) { + config.csc_enable = false; + config.avi_infoframe.colorspace = HDMI_COLORSPACE_RGB; + } else { + config.csc_scaling_factor = ADIHDMI_CSC_SCALING_4; + config.csc_coefficents = adihdmi_csc_ycbcr_to_rgb; + + if ((connector->display_info.color_formats & + DRM_COLOR_FORMAT_YCRCB422) && + config.hdmi_mode) { + config.csc_enable = false; + config.avi_infoframe.colorspace = + HDMI_COLORSPACE_YUV422; + } else { + config.csc_enable = true; + config.avi_infoframe.colorspace = HDMI_COLORSPACE_RGB; + } + } + + if (config.hdmi_mode) { + mode = ADIHDMI_HDMI_CFG_MODE_HDMI; + + switch (config.avi_infoframe.colorspace) { + case HDMI_COLORSPACE_YUV444: + output_format_422 = false; + output_format_ycbcr = true; + break; + case HDMI_COLORSPACE_YUV422: + output_format_422 = true; + output_format_ycbcr = true; + break; + default: + output_format_422 = false; + output_format_ycbcr = false; + break; + } + } else { + mode = ADIHDMI_HDMI_CFG_MODE_DVI; + output_format_422 = false; + output_format_ycbcr = false; + } + + adihdmi_packet_disable(adihdmi, ADIHDMI_INFOFRAME_PACKETS); + + adihdmi_set_colormap(adihdmi, config.csc_enable, + config.csc_coefficents, + config.csc_scaling_factor); + + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_VIDEO_INPUT_CFG1, 0x81, + (output_format_422 << 7) | output_format_ycbcr); + + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_HDCP_HDMI_CFG, + ADIHDMI_HDMI_CFG_MODE_MASK, mode); + + hdmi_avi_infoframe_pack(&config.avi_infoframe, infoframe, + sizeof(infoframe)); + + /* The AVI infoframe id is not configurable */ + regmap_bulk_write(adihdmi->regmap, ADIHDMI_REG_AVI_INFOFRAME_VERSION, + infoframe + 1, sizeof(infoframe) - 1); + + adihdmi_packet_enable(adihdmi, ADIHDMI_INFOFRAME_PACKETS); +} + +static void adihdmi_set_link_config(struct adihdmi *adihdmi, + const struct adihdmi_link_config *config) +{ + /* + * The input style values documented in the datasheet don't match the + * hardware register field values :-( + */ + static const unsigned int input_styles[4] = { 0, 2, 1, 3 }; + + unsigned int clock_delay; + unsigned int color_depth; + unsigned int input_id; + + clock_delay = (config->clock_delay + 1200) / 400; + color_depth = config->input_color_depth == 8 ? 3 + : (config->input_color_depth == 10 ? 1 : 2); + + /* TODO Support input ID 6 */ + if (config->input_colorspace != HDMI_COLORSPACE_YUV422) + input_id = config->input_clock == ADIHDMI_INPUT_CLOCK_DDR + ? 5 : 0; + else if (config->input_clock == ADIHDMI_INPUT_CLOCK_DDR) + input_id = config->embedded_sync ? 8 : 7; + else if (config->input_clock == ADIHDMI_INPUT_CLOCK_2X) + input_id = config->embedded_sync ? 4 : 3; + else + input_id = config->embedded_sync ? 2 : 1; + + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_I2C_FREQ_ID_CFG, 0xf, + input_id); + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_VIDEO_INPUT_CFG1, 0x7e, + (color_depth << 4) | + (input_styles[config->input_style] << 2)); + regmap_write(adihdmi->regmap, ADIHDMI_REG_VIDEO_INPUT_CFG2, + config->input_justification << 3); + regmap_write(adihdmi->regmap, ADIHDMI_REG_TIMING_GEN_SEQ, + config->sync_pulse << 2); + + regmap_write(adihdmi->regmap, 0xba, clock_delay << 5); + + adihdmi->embedded_sync = config->embedded_sync; + adihdmi->hsync_polarity = config->hsync_polarity; + adihdmi->vsync_polarity = config->vsync_polarity; + adihdmi->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB; +} + +static void adihdmi_power_on(struct adihdmi *adihdmi) +{ + adihdmi->current_edid_segment = -1; + + regmap_write(adihdmi->regmap, ADIHDMI_REG_INT(0), + ADIHDMI_INT0_EDID_READY); + regmap_write(adihdmi->regmap, ADIHDMI_REG_INT(1), + ADIHDMI_INT1_DDC_ERROR); + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_POWER, + ADIHDMI_POWER_POWER_DOWN, 0); + + /* + * Per spec it is allowed to pulse the HDP signal to indicate that the + * EDID information has changed. Some monitors do this when they wakeup + * from standby or are enabled. When the HDP goes low the adihdmi is + * reset and the outputs are disabled which might cause the monitor to + * go to standby again. To avoid this we ignore the HDP pin for the + * first few seconds after enabling the output. + */ + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_POWER2, + ADIHDMI_REG_POWER2_HDP_SRC_MASK, + ADIHDMI_REG_POWER2_HDP_SRC_NONE); + + /* + * Most of the registers are reset during power down or when HPD is low. + */ + regcache_sync(adihdmi->regmap); + + adihdmi->powered = true; +} + +static void adihdmi_power_off(struct adihdmi *adihdmi) +{ + /* TODO: setup additional power down modes */ + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_POWER, + ADIHDMI_POWER_POWER_DOWN, + ADIHDMI_POWER_POWER_DOWN); + regcache_mark_dirty(adihdmi->regmap); + + adihdmi->powered = false; +} + +/* ----------------------------------------------------------------------------- + * Interrupt and hotplug detection + */ + +static bool adihdmi_hpd(struct adihdmi *adihdmi) +{ + unsigned int irq0; + int ret; + + ret = regmap_read(adihdmi->regmap, ADIHDMI_REG_INT(0), &irq0); + if (ret < 0) + return false; + + if (irq0 & ADIHDMI_INT0_HDP) { + regmap_write(adihdmi->regmap, ADIHDMI_REG_INT(0), + ADIHDMI_INT0_HDP); + return true; + } + + return false; +} + +static int adihdmi_irq_process(struct adihdmi *adihdmi) +{ + unsigned int irq0, irq1; + int ret; + + ret = regmap_read(adihdmi->regmap, ADIHDMI_REG_INT(0), &irq0); + if (ret < 0) + return ret; + + ret = regmap_read(adihdmi->regmap, ADIHDMI_REG_INT(1), &irq1); + if (ret < 0) + return ret; + + regmap_write(adihdmi->regmap, ADIHDMI_REG_INT(0), irq0); + regmap_write(adihdmi->regmap, ADIHDMI_REG_INT(1), irq1); + + if (irq0 & ADIHDMI_INT0_HDP) + drm_helper_hpd_irq_event(adihdmi->encoder->dev); + + if (irq0 & ADIHDMI_INT0_EDID_READY || irq1 & ADIHDMI_INT1_DDC_ERROR) { + adihdmi->edid_read = true; + + if (adihdmi->i2c_main->irq) + wake_up_all(&adihdmi->wq); + } + + return 0; +} + +static irqreturn_t adihdmi_irq_handler(int irq, void *devid) +{ + struct adihdmi *adihdmi = devid; + int ret; + + ret = adihdmi_irq_process(adihdmi); + return ret < 0 ? IRQ_NONE : IRQ_HANDLED; +} + +/* ----------------------------------------------------------------------------- + * EDID retrieval + */ + +static int adihdmi_wait_for_edid(struct adihdmi *adihdmi, int timeout) +{ + int ret; + + if (adihdmi->i2c_main->irq) { + ret = wait_event_interruptible_timeout(adihdmi->wq, + adihdmi->edid_read, msecs_to_jiffies(timeout)); + } else { + for (; timeout > 0; timeout -= 25) { + ret = adihdmi_irq_process(adihdmi); + if (ret < 0) + break; + + if (adihdmi->edid_read) + break; + + msleep(25); + } + } + + return adihdmi->edid_read ? 0 : -EIO; +} + +static int adihdmi_get_edid_block(void *data, u8 *buf, unsigned int block, + size_t len) +{ + struct adihdmi *adihdmi = data; + struct i2c_msg xfer[2]; + uint8_t offset; + unsigned int i; + int ret; + + if (len > 128) + return -EINVAL; + + if (adihdmi->current_edid_segment != block / 2) { + unsigned int status; + + ret = regmap_read(adihdmi->regmap, ADIHDMI_REG_DDC_STATUS, + &status); + if (ret < 0) + return ret; + + if (status != 2) { + adihdmi->edid_read = false; + regmap_write(adihdmi->regmap, ADIHDMI_REG_EDID_SEGMENT, + block); + ret = adihdmi_wait_for_edid(adihdmi, 200); + if (ret < 0) + return ret; + } + + /* Break this apart, hopefully more I2C controllers will + * support 64 byte transfers than 256 byte transfers + */ + + xfer[0].addr = adihdmi->i2c_edid->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + xfer[0].buf = &offset; + xfer[1].addr = adihdmi->i2c_edid->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 64; + xfer[1].buf = adihdmi->edid_buf; + + offset = 0; + + for (i = 0; i < 4; ++i) { + ret = i2c_transfer(adihdmi->i2c_edid->adapter, xfer, + ARRAY_SIZE(xfer)); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + + xfer[1].buf += 64; + offset += 64; + } + + adihdmi->current_edid_segment = block / 2; + } + + if (block % 2 == 0) + memcpy(buf, adihdmi->edid_buf, len); + else + memcpy(buf, adihdmi->edid_buf + 128, len); + + return 0; +} + +static int adihdmi_mode_valid(struct drm_display_mode *mode) +{ + if (mode->clock > 165000) + return MODE_CLOCK_HIGH; + + return MODE_OK; +} + +/* ----------------------------------------------------------------------------- + * DT and private structure operations + */ + +#define conn_to_adihdmi2(x) \ + container_of(x, struct adihdmi2, connector); + +#define enc_to_adihdmi2(x) \ + container_of(x, struct adihdmi2, encoder); + +#define enc_to_adihdmi(x) \ + (&(container_of(x, struct adihdmi2, encoder)->base)) + +static int adihdmi_parse_dt(struct device_node *np, + struct adihdmi_link_config *config) +{ + memset(config, 0, sizeof(*config)); + + config->input_color_depth = 8; + + config->input_colorspace = HDMI_COLORSPACE_RGB; + //config->input_colorspace = HDMI_COLORSPACE_YUV422; + //config->input_colorspace = HDMI_COLORSPACE_YUV444; + + config->input_clock = ADIHDMI_INPUT_CLOCK_1X; + //config->input_clock = ADIHDMI_INPUT_CLOCK_2X; + //config->input_clock = ADIHDMI_INPUT_CLOCK_DDR; + + if (config->input_colorspace == HDMI_COLORSPACE_YUV422 || + config->input_clock != ADIHDMI_INPUT_CLOCK_1X) { + + config->input_style = 1; + //config->input_justification = ADIHDMI_INPUT_JUSTIFICATION_LEFT; + config->input_justification = ADIHDMI_INPUT_JUSTIFICATION_EVENLY; + //config->input_justification = ADIHDMI_INPUT_JUSTIFICATION_RIGHT; + + } else { + config->input_style = 1; + config->input_justification = ADIHDMI_INPUT_JUSTIFICATION_LEFT; + } + + config->clock_delay = 0; + config->embedded_sync = 0; + + /* Hardcode the sync pulse configurations for now. */ + config->sync_pulse = ADIHDMI_INPUT_SYNC_PULSE_NONE; + config->vsync_polarity = ADIHDMI_SYNC_POLARITY_PASSTHROUGH; + config->hsync_polarity = ADIHDMI_SYNC_POLARITY_PASSTHROUGH; + + return 0; +} + +static const int edid_i2c_addr = 0x7e; +static const int packet_i2c_addr = 0x70; +static const int cec_i2c_addr = 0x78; + +static int adihdmi_create(struct i2c_client *i2c, struct adihdmi *adihdmi) +{ + struct adihdmi_link_config link_config; + struct device *dev = &i2c->dev; + unsigned int val; + int ret; + + adihdmi->powered = false; + adihdmi->status = connector_status_disconnected; + + ret = adihdmi_parse_dt(NULL, &link_config); + if (ret) + { + pr_err("%s - %d - Bad parse\n", __FUNCTION__, __LINE__); + return -EINVAL; + } + + /* + * The power down GPIO is optional. If present, toggle it from active to + * inactive to wake up the encoder. + */ + adihdmi->gpio_pd = devm_gpiod_get_optional(dev, "pd", GPIOD_OUT_HIGH); + if (IS_ERR(adihdmi->gpio_pd)) + { + pr_err("%s - %d - Bad PD GPIO\n", __FUNCTION__, __LINE__); + return PTR_ERR(adihdmi->gpio_pd); + } + + if (adihdmi->gpio_pd) { + mdelay(5); + gpiod_set_value_cansleep(adihdmi->gpio_pd, 0); + } + + adihdmi->regmap = devm_regmap_init_i2c(i2c, &adihdmi_regmap_config); + if (IS_ERR(adihdmi->regmap)) + { + pr_err("%s - %d - Bad reg map init\n", __FUNCTION__, __LINE__); + return PTR_ERR(adihdmi->regmap); + } + + ret = regmap_read(adihdmi->regmap, ADIHDMI_REG_CHIP_REVISION, &val); + if (ret) + { + pr_err("%s - %d - Bad reg map read\n", __FUNCTION__, __LINE__); + return ret; + } + dev_dbg(dev, "Rev. %d\n", val); + + ret = regmap_register_patch(adihdmi->regmap, adihdmi_fixed_registers, + ARRAY_SIZE(adihdmi_fixed_registers)); + if (ret) + { + pr_err("%s - %d - Bad reg map patch\n", __FUNCTION__, __LINE__); + return ret; + } + + regmap_write(adihdmi->regmap, ADIHDMI_REG_EDID_I2C_ADDR, edid_i2c_addr); + regmap_write(adihdmi->regmap, ADIHDMI_REG_PACKET_I2C_ADDR, + packet_i2c_addr); + regmap_write(adihdmi->regmap, ADIHDMI_REG_CEC_I2C_ADDR, cec_i2c_addr); + adihdmi_packet_disable(adihdmi, 0xffff); + + adihdmi->i2c_main = i2c; + adihdmi->i2c_edid = i2c_new_dummy(i2c->adapter, edid_i2c_addr >> 1); + if (!adihdmi->i2c_edid) + { + pr_err("%s - %d - No mem for EDID\n", __FUNCTION__, __LINE__); + return -ENOMEM; + } + + if (i2c->irq) { + init_waitqueue_head(&adihdmi->wq); + + ret = devm_request_threaded_irq(dev, i2c->irq, NULL, + adihdmi_irq_handler, + IRQF_ONESHOT, dev_name(dev), + adihdmi); + if (ret) + { + pr_err("%s - %d - Bad IRQ thread request\n", __FUNCTION__, __LINE__); + goto err_i2c_unregister_device; + } + } + + /* CEC is unused for now */ + regmap_write(adihdmi->regmap, ADIHDMI_REG_CEC_CTRL, + ADIHDMI_CEC_CTRL_POWER_DOWN); + + adihdmi_power_off(adihdmi); + + adihdmi_set_link_config(adihdmi, &link_config); + + adihdmi_audio_setup(adihdmi); + + return 0; + +err_i2c_unregister_device: + i2c_unregister_device(adihdmi->i2c_edid); + + return ret; +} + +static void adihdmi_destroy(struct adihdmi *priv) +{ + i2c_unregister_device(priv->i2c_edid); +} + +/* ----------------------------------------------------------------------------- + * Encoder operations + */ + +static int adihdmi_encoder_get_modes(struct adihdmi *adihdmi, + struct drm_connector *connector) +{ + struct edid *edid; + unsigned int count; + + /* Reading the EDID only works if the device is powered */ + if (!adihdmi->powered) { + regmap_write(adihdmi->regmap, ADIHDMI_REG_INT(0), + ADIHDMI_INT0_EDID_READY); + regmap_write(adihdmi->regmap, ADIHDMI_REG_INT(1), + ADIHDMI_INT1_DDC_ERROR); + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_POWER, + ADIHDMI_POWER_POWER_DOWN, 0); + adihdmi->current_edid_segment = -1; + } + + edid = drm_do_get_edid(connector, adihdmi_get_edid_block, adihdmi); + + if (!adihdmi->powered) + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_POWER, + ADIHDMI_POWER_POWER_DOWN, + ADIHDMI_POWER_POWER_DOWN); + + kfree(adihdmi->edid); + adihdmi->edid = edid; + if (!edid) + { + pr_err("%s - %d - No EDID\n", __FUNCTION__, __LINE__); + return 0; + } + + drm_mode_connector_update_edid_property(connector, edid); + count = drm_add_edid_modes(connector, edid); + + adihdmi_set_config_csc(adihdmi, connector, adihdmi->rgb); + + return count; +} + +static void adihdmi_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct adihdmi2 *priv2 = enc_to_adihdmi2(encoder); + + if (mode == DRM_MODE_DPMS_ON) + adihdmi_power_on(&priv2->base); + else + adihdmi_power_off(&priv2->base); +} + + static enum drm_connector_status +adihdmi_encoder_detect(struct adihdmi *adihdmi, + struct drm_connector *connector) +{ + enum drm_connector_status status; + unsigned int val; + bool hpd; + int ret; + + ret = regmap_read(adihdmi->regmap, ADIHDMI_REG_STATUS, &val); + if (ret < 0) + { + pr_err("%s - %d - Disconnected\n", __FUNCTION__, __LINE__); + return connector_status_disconnected; + } + + if (val & ADIHDMI_STATUS_HPD) + status = connector_status_connected; + else + status = connector_status_disconnected; + + hpd = adihdmi_hpd(adihdmi); + + /* The chip resets itself when the cable is disconnected, so in case + * there is a pending HPD interrupt and the cable is connected there was + * at least one transition from disconnected to connected and the chip + * has to be reinitialized. */ + if (status == connector_status_connected && hpd && adihdmi->powered) { + regcache_mark_dirty(adihdmi->regmap); + adihdmi_power_on(adihdmi); + adihdmi_encoder_get_modes(adihdmi, connector); + if (adihdmi->status == connector_status_connected) + status = connector_status_disconnected; + } else { + /* Renable HDP sensing */ + regmap_update_bits(adihdmi->regmap, ADIHDMI_REG_POWER2, + ADIHDMI_REG_POWER2_HDP_SRC_MASK, + ADIHDMI_REG_POWER2_HDP_SRC_BOTH); + } + + adihdmi->status = status; + return status; +} + +static bool adihdmi_encoder_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static int adihdmi_encoder_mode_valid(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ + return adihdmi_mode_valid(mode); +} + +static void adihdmi_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adj_mode) +{ + unsigned int low_refresh_rate; + unsigned int hsync_polarity = 0; + unsigned int vsync_polarity = 0; + struct adihdmi *adihdmi = enc_to_adihdmi(encoder); + + if (adihdmi->embedded_sync) { + unsigned int hsync_offset, hsync_len; + unsigned int vsync_offset, vsync_len; + + hsync_offset = adj_mode->crtc_hsync_start - + adj_mode->crtc_hdisplay; + vsync_offset = adj_mode->crtc_vsync_start - + adj_mode->crtc_vdisplay; + hsync_len = adj_mode->crtc_hsync_end - + adj_mode->crtc_hsync_start; + vsync_len = adj_mode->crtc_vsync_end - + adj_mode->crtc_vsync_start; + + /* The hardware vsync generator has a off-by-one bug */ + vsync_offset += 1; + + regmap_write(adihdmi->regmap, ADIHDMI_REG_HSYNC_PLACEMENT_MSB, + ((hsync_offset >> 10) & 0x7) << 5); + regmap_write(adihdmi->regmap, ADIHDMI_REG_SYNC_DECODER(0), + (hsync_offset >> 2) & 0xff); + regmap_write(adihdmi->regmap, ADIHDMI_REG_SYNC_DECODER(1), + ((hsync_offset & 0x3) << 6) | + ((hsync_len >> 4) & 0x3f)); + regmap_write(adihdmi->regmap, ADIHDMI_REG_SYNC_DECODER(2), + ((hsync_len & 0xf) << 4) | + ((vsync_offset >> 6) & 0xf)); + regmap_write(adihdmi->regmap, ADIHDMI_REG_SYNC_DECODER(3), + ((vsync_offset & 0x3f) << 2) | + ((vsync_len >> 8) & 0x3)); + regmap_write(adihdmi->regmap, ADIHDMI_REG_SYNC_DECODER(4), + vsync_len & 0xff); + + hsync_polarity = !(adj_mode->flags & DRM_MODE_FLAG_PHSYNC); + vsync_polarity = !(adj_mode->flags & DRM_MODE_FLAG_PVSYNC); + } else { + enum adihdmi_sync_polarity mode_hsync_polarity; + enum adihdmi_sync_polarity mode_vsync_polarity; + + /** + * If the input signal is always low or always high we want to + * invert or let it passthrough depending on the polarity of the + * current mode. + **/ + if (adj_mode->flags & DRM_MODE_FLAG_NHSYNC) + mode_hsync_polarity = ADIHDMI_SYNC_POLARITY_LOW; + else + mode_hsync_polarity = ADIHDMI_SYNC_POLARITY_HIGH; + + if (adj_mode->flags & DRM_MODE_FLAG_NVSYNC) + mode_vsync_polarity = ADIHDMI_SYNC_POLARITY_LOW; + else + mode_vsync_polarity = ADIHDMI_SYNC_POLARITY_HIGH; + + if (adihdmi->hsync_polarity != mode_hsync_polarity && + adihdmi->hsync_polarity != + ADIHDMI_SYNC_POLARITY_PASSTHROUGH) + hsync_polarity = 1; + + if (adihdmi->vsync_polarity != mode_vsync_polarity && + adihdmi->vsync_polarity != + ADIHDMI_SYNC_POLARITY_PASSTHROUGH) + vsync_polarity = 1; + } + + if (mode->vrefresh <= 24000) + low_refresh_rate = ADIHDMI_LOW_REFRESH_RATE_24HZ; + else if (mode->vrefresh <= 25000) + low_refresh_rate = ADIHDMI_LOW_REFRESH_RATE_25HZ; + else if (mode->vrefresh <= 30000) + low_refresh_rate = ADIHDMI_LOW_REFRESH_RATE_30HZ; + else + low_refresh_rate = ADIHDMI_LOW_REFRESH_RATE_NONE; + + regmap_update_bits(adihdmi->regmap, 0xfb, + 0x6, low_refresh_rate << 1); + regmap_update_bits(adihdmi->regmap, 0x17, + 0x60, (vsync_polarity << 6) | (hsync_polarity << 5)); + + /* + * TODO Test first order 4:2:2 to 4:4:4 up conversion method, which is + * supposed to give better results. + */ + + adihdmi->f_tmds = mode->clock; +} + +static void adihdmi_encoder_prepare(struct drm_encoder *encoder) +{ + adihdmi_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); +} + +static void adihdmi_encoder_commit(struct drm_encoder *encoder) +{ + adihdmi_encoder_dpms(encoder, DRM_MODE_DPMS_ON); +} + +static struct drm_encoder_helper_funcs adihdmi_encoder_helper_funcs = { + .dpms = adihdmi_encoder_dpms, + .mode_fixup = adihdmi_encoder_mode_fixup, + .prepare = adihdmi_encoder_prepare, + .commit = adihdmi_encoder_commit, + .mode_set = adihdmi_encoder_mode_set, +}; + +static void adihdmi_encoder_destroy(struct drm_encoder *encoder) +{ + struct adihdmi2 *priv = enc_to_adihdmi2(encoder); + + adihdmi_destroy(&priv->base); + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs adihdmi_encoder_funcs = { + .destroy = adihdmi_encoder_destroy, +}; + +/* ----------------------------------------------------------------------------- + * Slave operations + */ + +static int adihdmi_encoder_slave_create_resources(struct drm_encoder *encoder, struct drm_connector *connector) +{ + pr_debug("%s - %d\n", __FUNCTION__, __LINE__); + return 0; +} + +static void adihdmi_encoder_slave_destroy(struct drm_encoder *encoder) +{ + pr_debug("%s - %d\n", __FUNCTION__, __LINE__); +} + + static enum drm_connector_status +adihdmi_encoder_slave_detect(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + return adihdmi_encoder_detect(enc_to_adihdmi(encoder), + connector); +} + +static int adihdmi_encoder_slave_get_modes(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + return adihdmi_encoder_get_modes(enc_to_adihdmi(encoder), + connector); +} + + +static void adihdmi_encoder_slave_set_config(struct drm_encoder *encoder, void *params) +{ + pr_debug("%s - %d\n", __FUNCTION__, __LINE__); +} + +static int adihdmi_encoder_set_property(struct drm_encoder *encoder, struct drm_connector *connector, struct drm_property *property, uint64_t val) +{ + pr_debug("%s - %d\n", __FUNCTION__, __LINE__); + return 0; +} + +static struct drm_encoder_slave_funcs adihdmi_encoder_slave_funcs = { + .create_resources = adihdmi_encoder_slave_create_resources, + .destroy = adihdmi_encoder_slave_destroy, + .detect = adihdmi_encoder_slave_detect, + .dpms = adihdmi_encoder_dpms, + .get_modes = adihdmi_encoder_slave_get_modes, + .mode_fixup = adihdmi_encoder_mode_fixup, + .mode_set = adihdmi_encoder_mode_set, + .mode_valid = adihdmi_encoder_mode_valid, + .set_config = adihdmi_encoder_slave_set_config, + .set_property = adihdmi_encoder_set_property, +}; + +/* ----------------------------------------------------------------------------- + * Connector operations + */ + +static int adihdmi_connector_get_modes(struct drm_connector *connector) +{ + struct adihdmi2 *priv = conn_to_adihdmi2(connector); + + return adihdmi_encoder_get_modes(&priv->base, connector); +} + +static int adihdmi_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return adihdmi_mode_valid(mode); +} + + static struct drm_encoder * +adihdmi_connector_best_encoder(struct drm_connector *connector) +{ + struct adihdmi2 *priv = conn_to_adihdmi2(connector); + + return &priv->encoder; +} + +static struct drm_connector_helper_funcs adihdmi_connector_helper_funcs = { + .get_modes = adihdmi_connector_get_modes, + .mode_valid = adihdmi_connector_mode_valid, + .best_encoder = adihdmi_connector_best_encoder, +}; + + static enum drm_connector_status +adihdmi_connector_detect(struct drm_connector *connector, bool force) +{ + struct adihdmi2 *priv = conn_to_adihdmi2(connector); + + return adihdmi_encoder_detect(&priv->base, connector); +} + +static void adihdmi_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static struct drm_connector_funcs adihdmi_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = adihdmi_connector_detect, + .destroy = adihdmi_connector_destroy, +}; + +/* ----------------------------------------------------------------------------- + * Component operations + */ + +static int adihdmi_bind(struct device *dev, struct device *master, void *data) +{ + struct i2c_client *client = to_i2c_client(dev); + struct drm_device *drm = data; + struct adihdmi2 *priv; + uint32_t crtcs = 0; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + { + pr_err("%s - %d - No memory for ADIHDMI\n", __FUNCTION__, __LINE__); + return -ENOMEM; + } + + dev_set_drvdata(dev, priv); + + if (dev->of_node) + crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); + + /* If no CRTCs were found, fall back to our old behaviour */ + if (crtcs == 0) { + dev_warn(dev, "Falling back to first CRTC\n"); + crtcs = 1 << 0; + } + + priv->base.encoder = &priv->encoder; + priv->connector.interlace_allowed = 1; + priv->encoder.possible_crtcs = crtcs; + + ret = adihdmi_create(client, &priv->base); + if (ret) + return ret; + + drm_encoder_helper_add(&priv->encoder, &adihdmi_encoder_helper_funcs); + ret = drm_encoder_init(drm, &priv->encoder, &adihdmi_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); + if (ret) + goto err_encoder; + + drm_connector_helper_add(&priv->connector, + &adihdmi_connector_helper_funcs); + ret = drm_connector_init(drm, &priv->connector, + &adihdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + if (ret) + goto err_connector; + + ret = drm_connector_register(&priv->connector); + if (ret) + goto err_sysfs; + + priv->connector.encoder = &priv->encoder; + drm_mode_connector_attach_encoder(&priv->connector, &priv->encoder); + + return 0; + +err_sysfs: + drm_connector_cleanup(&priv->connector); +err_connector: + drm_encoder_cleanup(&priv->encoder); +err_encoder: + adihdmi_destroy(&priv->base); + return ret; + + +} + +static void adihdmi_unbind(struct device *dev, struct device *master, void *data) +{ + struct adihdmi2 *priv = dev_get_drvdata(dev); + + drm_connector_cleanup(&priv->connector); + drm_encoder_cleanup(&priv->encoder); + adihdmi_destroy(&priv->base); +} + +static const struct component_ops adihdmi_ops = +{ + .bind = adihdmi_bind, + .unbind = adihdmi_unbind, +}; + +/* ----------------------------------------------------------------------------- + * Init operations + */ + +static int adihdmi_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + return component_add(&i2c->dev, &adihdmi_ops); +} + +static int adihdmi_remove(struct i2c_client *i2c) +{ + component_del(&i2c->dev, &adihdmi_ops); + + return 0; +} + +static int adihdmi_encoder_init(struct i2c_client *i2c, struct drm_device *dev, + struct drm_encoder_slave *encoder_slave) +{ + + struct adihdmi *adihdmi; + int ret; + + adihdmi = kzalloc(sizeof(*adihdmi), GFP_KERNEL); + if (!adihdmi) + return -ENOMEM; + + adihdmi->encoder = &encoder_slave->base; + + ret = adihdmi_create(i2c, adihdmi); + if (ret) { + kfree(adihdmi); + return ret; + } + + encoder_slave->slave_priv = adihdmi; + encoder_slave->slave_funcs = &adihdmi_encoder_slave_funcs; + + return 0; +} + +static const struct i2c_device_id adihdmi_i2c_ids[] = { + { "adv7511", 0 }, + { "adv7511w", 0 }, + { "adv7513", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adihdmi_i2c_ids); + +static const struct of_device_id adihdmi_of_ids[] = { + { .compatible = "adi,adv7511", }, + { .compatible = "adi,adv7511w", }, + { .compatible = "adi,adv7513", }, + { } +}; +MODULE_DEVICE_TABLE(of, adihdmi_of_ids); + +static struct drm_i2c_encoder_driver adihdmi_driver = { + .i2c_driver = { + .driver = { + .name = "adihdmi", + .of_match_table = adihdmi_of_ids, + }, + .id_table = adihdmi_i2c_ids, + .probe = adihdmi_probe, + .remove = adihdmi_remove, + }, + + .encoder_init = adihdmi_encoder_init, +}; + +static int __init adihdmi_init(void) +{ + return drm_i2c_encoder_register(THIS_MODULE, &adihdmi_driver); +} +module_init(adihdmi_init); + +static void __exit adihdmi_exit(void) +{ + drm_i2c_encoder_unregister(&adihdmi_driver); +} +module_exit(adihdmi_exit); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("ADIHDMI HDMI transmitter driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 79027b1..e894fc7 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -46,6 +46,7 @@ struct tilcdc_crtc { int sync_lost_count; bool frame_intact; + struct work_struct recover_work; }; #define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base) @@ -113,9 +114,47 @@ static void start(struct drm_crtc *crtc) static void stop(struct drm_crtc *crtc) { + struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; + struct tilcdc_drm_private *priv = dev->dev_private; + tilcdc_crtc->frame_done = false; tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); + + /* + * if necessary wait for framedone irq which will still come + * before putting things to sleep.. + */ + if (priv->rev == 2) { + int ret = wait_event_timeout(tilcdc_crtc->frame_done_wq, + tilcdc_crtc->frame_done, + msecs_to_jiffies(50)); + if (ret == 0) + dev_err(dev->dev, "%s: timeout waiting for framedone\n", + __func__); + } +} + +static void tilcdc_crtc_recover_work(struct work_struct *work) +{ + struct tilcdc_crtc *tilcdc_crtc = + container_of(work, struct tilcdc_crtc, recover_work); + struct drm_crtc *crtc = &tilcdc_crtc->base; + struct drm_device *dev = crtc->dev; + + dev_info(crtc->dev->dev, "%s: Reset CRTC", __func__); + + drm_modeset_lock_crtc(crtc, NULL); + + if (tilcdc_crtc->dpms == DRM_MODE_DPMS_OFF) + goto out; + + tilcdc_crtc->frame_done = false; + stop(crtc); + tilcdc_write(dev, LCDC_INT_ENABLE_SET_REG, LCDC_SYNC_LOST); + start(crtc); +out: + drm_modeset_unlock_crtc(crtc); } static void tilcdc_crtc_destroy(struct drm_crtc *crtc) @@ -212,22 +251,7 @@ void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) pm_runtime_get_sync(dev->dev); start(crtc); } else { - tilcdc_crtc->frame_done = false; stop(crtc); - - /* - * if necessary wait for framedone irq which will still come - * before putting things to sleep.. - */ - if (priv->rev == 2) { - int ret = wait_event_timeout( - tilcdc_crtc->frame_done_wq, - tilcdc_crtc->frame_done, - msecs_to_jiffies(50)); - if (ret == 0) - dev_err(dev->dev, "timeout waiting for framedone\n"); - } - pm_runtime_put_sync(dev->dev); if (tilcdc_crtc->next_fb) { @@ -718,30 +742,38 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) tilcdc_crtc->frame_intact = true; } - if (priv->rev == 2) { - if (stat & LCDC_FRAME_DONE) { - tilcdc_crtc->frame_done = true; - wake_up(&tilcdc_crtc->frame_done_wq); - } - tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0); + if (priv->rev == 1) + return IRQ_HANDLED; + /* The rest is for revision 2 only */ + + if (stat & LCDC_FRAME_DONE) { + tilcdc_crtc->frame_done = true; + wake_up(&tilcdc_crtc->frame_done_wq); } + if (stat & LCDC_FIFO_UNDERFLOW) + dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underfow", + __func__, stat); + if (stat & LCDC_SYNC_LOST) { dev_err_ratelimited(dev->dev, "%s(0x%08x): Sync lost", __func__, stat); tilcdc_crtc->frame_intact = false; if (tilcdc_crtc->sync_lost_count++ > SYNC_LOST_COUNT_LIMIT) { dev_err(dev->dev, - "%s(0x%08x): Sync lost flood detected, disabling the interrupt", + "%s(0x%08x): Sync lost flood detected, recovering", __func__, stat); + queue_work(system_wq, &tilcdc_crtc->recover_work); tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, LCDC_SYNC_LOST); + tilcdc_crtc->sync_lost_count = 0; } } - if (stat & LCDC_FIFO_UNDERFLOW) - dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underfow", - __func__, stat); + /* Indicate to LCDC that the interrupt service routine has + * completed, see 13.3.6.1.6 in AM335x TRM. + */ + tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0); return IRQ_HANDLED; } @@ -768,6 +800,7 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) "unref", unref_worker); spin_lock_init(&tilcdc_crtc->irq_lock); + INIT_WORK(&tilcdc_crtc->recover_work, tilcdc_crtc_recover_work); ret = drm_crtc_init(dev, crtc, &tilcdc_crtc_funcs); if (ret < 0) diff --git a/drivers/i2c/muxes/i2c-demux-pinctrl.c b/drivers/i2c/muxes/i2c-demux-pinctrl.c index 215ac87..767c996 100644 --- a/drivers/i2c/muxes/i2c-demux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-demux-pinctrl.c @@ -37,8 +37,6 @@ struct i2c_demux_pinctrl_priv { struct i2c_demux_pinctrl_chan chan[]; }; -static struct property status_okay = { .name = "status", .length = 3, .value = "ok" }; - static int i2c_demux_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct i2c_demux_pinctrl_priv *priv = adap->algo_data; @@ -221,7 +219,8 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev) priv->chan[i].parent_np = adap_np; of_changeset_init(&priv->chan[i].chgset); - of_changeset_update_property(&priv->chan[i].chgset, adap_np, &status_okay); + of_changeset_update_property_string(&priv->chan[i].chgset, + adap_np, "status", "okay"); } priv->num_chan = num_chan; diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 505e921..6743b18 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -46,6 +46,14 @@ config IIO_CONSUMERS_PER_TRIGGER This value controls the maximum number of consumers that a given trigger may handle. Default is 2. +config IIO_SW_DEVICE + tristate "Enable software IIO device support" + select IIO_CONFIGFS + help + Provides IIO core support for software devices. A software + device can be created via configfs or directly by a driver + using the API provided. + config IIO_SW_TRIGGER tristate "Enable software triggers support" select IIO_CONFIGFS diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 20f6490..87e4c43 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -8,6 +8,7 @@ industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o +obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o obj-$(CONFIG_IIO_SW_TRIGGER) += industrialio-sw-trigger.o obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index e4a758c..78f148e 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -17,6 +17,18 @@ config BMA180 To compile this driver as a module, choose M here: the module will be called bma180. +config BMA220 + tristate "Bosch BMA220 3-Axis Accelerometer Driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to add support for the Bosch BMA220 triaxial + acceleration sensor. + + To compile this driver as a module, choose M here: the + module will be called bma220_spi. + config BMC150_ACCEL tristate "Bosch BMC150 Accelerometer Driver" select IIO_BUFFER @@ -136,13 +148,23 @@ config MMA7455_SPI To compile this driver as a module, choose M here: the module will be called mma7455_spi. +config MMA7660 + tristate "Freescale MMA7660FC 3-Axis Accelerometer Driver" + depends on I2C + help + Say yes here to get support for the Freescale MMA7660FC 3-Axis + accelerometer. + + Choosing M will build the driver as a module. If so, the module + will be called mma7660. + config MMA8452 - tristate "Freescale MMA8452Q and similar Accelerometers Driver" + tristate "Freescale / NXP MMA8452Q and similar Accelerometers Driver" depends on I2C select IIO_BUFFER select IIO_TRIGGERED_BUFFER help - Say yes here to build support for the following Freescale 3-axis + Say yes here to build support for the following Freescale / NXP 3-axis accelerometers: MMA8451Q, MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC, FXLS8471Q. @@ -214,7 +236,8 @@ config STK8312 config STK8BA50 tristate "Sensortek STK8BA50 3-Axis Accelerometer Driver" depends on I2C - depends on IIO_TRIGGER + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say yes here to get support for the Sensortek STK8BA50 3-axis accelerometer. diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 71b6794..6cedbec 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -4,6 +4,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_BMA180) += bma180.o +obj-$(CONFIG_BMA220) += bma220_spi.o obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o @@ -15,6 +16,8 @@ obj-$(CONFIG_MMA7455) += mma7455_core.o obj-$(CONFIG_MMA7455_I2C) += mma7455_i2c.o obj-$(CONFIG_MMA7455_SPI) += mma7455_spi.o +obj-$(CONFIG_MMA7660) += mma7660.o + obj-$(CONFIG_MMA8452) += mma8452.o obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index f04b884..e3f88ba 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -654,7 +654,7 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct bma180_data *data = iio_priv(indio_dev); - int64_t time_ns = iio_get_time_ns(); + s64 time_ns = iio_get_time_ns(indio_dev); int bit, ret, i = 0; mutex_lock(&data->mutex); diff --git b/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c new file mode 100644 index 0000000..5099f29 --- /dev/null +++ b/drivers/iio/accel/bma220_spi.c @@ -0,0 +1,338 @@ +/** + * BMA220 Digital triaxial acceleration sensor driver + * + * Copyright (c) 2016, Intel Corporation. + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BMA220_REG_ID 0x00 +#define BMA220_REG_ACCEL_X 0x02 +#define BMA220_REG_ACCEL_Y 0x03 +#define BMA220_REG_ACCEL_Z 0x04 +#define BMA220_REG_RANGE 0x11 +#define BMA220_REG_SUSPEND 0x18 + +#define BMA220_CHIP_ID 0xDD +#define BMA220_READ_MASK 0x80 +#define BMA220_RANGE_MASK 0x03 +#define BMA220_DATA_SHIFT 2 +#define BMA220_SUSPEND_SLEEP 0xFF +#define BMA220_SUSPEND_WAKE 0x00 + +#define BMA220_DEVICE_NAME "bma220" +#define BMA220_SCALE_AVAILABLE "0.623 1.248 2.491 4.983" + +#define BMA220_ACCEL_CHANNEL(index, reg, axis) { \ + .type = IIO_ACCEL, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 6, \ + .storagebits = 8, \ + .shift = BMA220_DATA_SHIFT, \ + .endianness = IIO_CPU, \ + }, \ +} + +enum bma220_axis { + AXIS_X, + AXIS_Y, + AXIS_Z, +}; + +static IIO_CONST_ATTR(in_accel_scale_available, BMA220_SCALE_AVAILABLE); + +static struct attribute *bma220_attributes[] = { + &iio_const_attr_in_accel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group bma220_attribute_group = { + .attrs = bma220_attributes, +}; + +static const int bma220_scale_table[][4] = { + {0, 623000}, {1, 248000}, {2, 491000}, {4, 983000} +}; + +struct bma220_data { + struct spi_device *spi_device; + struct mutex lock; + s8 buffer[16]; /* 3x8-bit channels + 5x8 padding + 8x8 timestamp */ + u8 tx_buf[2] ____cacheline_aligned; +}; + +static const struct iio_chan_spec bma220_channels[] = { + BMA220_ACCEL_CHANNEL(0, BMA220_REG_ACCEL_X, X), + BMA220_ACCEL_CHANNEL(1, BMA220_REG_ACCEL_Y, Y), + BMA220_ACCEL_CHANNEL(2, BMA220_REG_ACCEL_Z, Z), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static inline int bma220_read_reg(struct spi_device *spi, u8 reg) +{ + return spi_w8r8(spi, reg | BMA220_READ_MASK); +} + +static const unsigned long bma220_accel_scan_masks[] = { + BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z), + 0 +}; + +static irqreturn_t bma220_trigger_handler(int irq, void *p) +{ + int ret; + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct bma220_data *data = iio_priv(indio_dev); + struct spi_device *spi = data->spi_device; + + mutex_lock(&data->lock); + data->tx_buf[0] = BMA220_REG_ACCEL_X | BMA220_READ_MASK; + ret = spi_write_then_read(spi, data->tx_buf, 1, data->buffer, + ARRAY_SIZE(bma220_channels) - 1); + if (ret < 0) + goto err; + + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + pf->timestamp); +err: + mutex_unlock(&data->lock); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int bma220_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + u8 range_idx; + struct bma220_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = bma220_read_reg(data->spi_device, chan->address); + if (ret < 0) + return -EINVAL; + *val = sign_extend32(ret >> BMA220_DATA_SHIFT, 5); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = bma220_read_reg(data->spi_device, BMA220_REG_RANGE); + if (ret < 0) + return ret; + range_idx = ret & BMA220_RANGE_MASK; + *val = bma220_scale_table[range_idx][0]; + *val2 = bma220_scale_table[range_idx][1]; + return IIO_VAL_INT_PLUS_MICRO; + } + + return -EINVAL; +} + +static int bma220_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int i; + int ret; + int index = -1; + struct bma220_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + for (i = 0; i < ARRAY_SIZE(bma220_scale_table); i++) + if (val == bma220_scale_table[i][0] && + val2 == bma220_scale_table[i][1]) { + index = i; + break; + } + if (index < 0) + return -EINVAL; + + mutex_lock(&data->lock); + data->tx_buf[0] = BMA220_REG_RANGE; + data->tx_buf[1] = index; + ret = spi_write(data->spi_device, data->tx_buf, + sizeof(data->tx_buf)); + if (ret < 0) + dev_err(&data->spi_device->dev, + "failed to set measurement range\n"); + mutex_unlock(&data->lock); + + return 0; + } + + return -EINVAL; +} + +static const struct iio_info bma220_info = { + .driver_module = THIS_MODULE, + .read_raw = bma220_read_raw, + .write_raw = bma220_write_raw, + .attrs = &bma220_attribute_group, +}; + +static int bma220_init(struct spi_device *spi) +{ + int ret; + + ret = bma220_read_reg(spi, BMA220_REG_ID); + if (ret != BMA220_CHIP_ID) + return -ENODEV; + + /* Make sure the chip is powered on */ + ret = bma220_read_reg(spi, BMA220_REG_SUSPEND); + if (ret < 0) + return ret; + else if (ret == BMA220_SUSPEND_WAKE) + return bma220_read_reg(spi, BMA220_REG_SUSPEND); + + return 0; +} + +static int bma220_deinit(struct spi_device *spi) +{ + int ret; + + /* Make sure the chip is powered off */ + ret = bma220_read_reg(spi, BMA220_REG_SUSPEND); + if (ret < 0) + return ret; + else if (ret == BMA220_SUSPEND_SLEEP) + return bma220_read_reg(spi, BMA220_REG_SUSPEND); + + return 0; +} + +static int bma220_probe(struct spi_device *spi) +{ + int ret; + struct iio_dev *indio_dev; + struct bma220_data *data; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); + if (!indio_dev) { + dev_err(&spi->dev, "iio allocation failed!\n"); + return -ENOMEM; + } + + data = iio_priv(indio_dev); + data->spi_device = spi; + spi_set_drvdata(spi, indio_dev); + mutex_init(&data->lock); + + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &bma220_info; + indio_dev->name = BMA220_DEVICE_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = bma220_channels; + indio_dev->num_channels = ARRAY_SIZE(bma220_channels); + indio_dev->available_scan_masks = bma220_accel_scan_masks; + + ret = bma220_init(data->spi_device); + if (ret < 0) + return ret; + + ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time, + bma220_trigger_handler, NULL); + if (ret < 0) { + dev_err(&spi->dev, "iio triggered buffer setup failed\n"); + goto err_suspend; + } + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&spi->dev, "iio_device_register failed\n"); + iio_triggered_buffer_cleanup(indio_dev); + goto err_suspend; + } + + return 0; + +err_suspend: + return bma220_deinit(spi); +} + +static int bma220_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + + return bma220_deinit(spi); +} + +#ifdef CONFIG_PM_SLEEP +static int bma220_suspend(struct device *dev) +{ + struct bma220_data *data = + iio_priv(spi_get_drvdata(to_spi_device(dev))); + + /* The chip can be suspended/woken up by a simple register read. */ + return bma220_read_reg(data->spi_device, BMA220_REG_SUSPEND); +} + +static int bma220_resume(struct device *dev) +{ + struct bma220_data *data = + iio_priv(spi_get_drvdata(to_spi_device(dev))); + + return bma220_read_reg(data->spi_device, BMA220_REG_SUSPEND); +} + +static SIMPLE_DEV_PM_OPS(bma220_pm_ops, bma220_suspend, bma220_resume); + +#define BMA220_PM_OPS (&bma220_pm_ops) +#else +#define BMA220_PM_OPS NULL +#endif + +static const struct spi_device_id bma220_spi_id[] = { + {"bma220", 0}, + {} +}; + +static const struct acpi_device_id bma220_acpi_id[] = { + {"BMA0220", 0}, + {} +}; + +MODULE_DEVICE_TABLE(spi, bma220_spi_id); + +static struct spi_driver bma220_driver = { + .driver = { + .name = "bma220_spi", + .pm = BMA220_PM_OPS, + .acpi_match_table = ACPI_PTR(bma220_acpi_id), + }, + .probe = bma220_probe, + .remove = bma220_remove, + .id_table = bma220_spi_id, +}; + +module_spi_driver(bma220_driver); + +MODULE_AUTHOR("Tiberiu Breana "); +MODULE_DESCRIPTION("BMA220 acceleration sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 197e693..59b380d 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -67,6 +67,9 @@ #define BMC150_ACCEL_REG_PMU_BW 0x10 #define BMC150_ACCEL_DEF_BW 125 +#define BMC150_ACCEL_REG_RESET 0x14 +#define BMC150_ACCEL_RESET_VAL 0xB6 + #define BMC150_ACCEL_REG_INT_MAP_0 0x19 #define BMC150_ACCEL_INT_MAP_0_BIT_SLOPE BIT(2) @@ -901,7 +904,7 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev, */ if (!irq) { data->old_timestamp = data->timestamp; - data->timestamp = iio_get_time_ns(); + data->timestamp = iio_get_time_ns(indio_dev); } /* @@ -1303,7 +1306,7 @@ static irqreturn_t bmc150_accel_irq_handler(int irq, void *private) int i; data->old_timestamp = data->timestamp; - data->timestamp = iio_get_time_ns(); + data->timestamp = iio_get_time_ns(indio_dev); for (i = 0; i < BMC150_ACCEL_TRIGGERS; i++) { if (data->triggers[i].enabled) { @@ -1497,6 +1500,14 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data) int ret, i; unsigned int val; + /* + * Reset chip to get it in a known good state. A delay of 1.8ms after + * reset is required according to the data sheets of supported chips. + */ + regmap_write(data->regmap, BMC150_ACCEL_REG_RESET, + BMC150_ACCEL_RESET_VAL); + usleep_range(1800, 2500); + ret = regmap_read(data->regmap, BMC150_ACCEL_REG_CHIP_ID, &val); if (ret < 0) { dev_err(dev, "Error: Reading chip id\n"); diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index bfe219a..765a723 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1129,7 +1129,7 @@ static irqreturn_t kxcjk1013_data_rdy_trig_poll(int irq, void *private) struct iio_dev *indio_dev = private; struct kxcjk1013_data *data = iio_priv(indio_dev); - data->timestamp = iio_get_time_ns(); + data->timestamp = iio_get_time_ns(indio_dev); if (data->dready_trigger_on) iio_trigger_poll(data->dready_trig); diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index 3a9f106..9d72d4b 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -160,11 +160,13 @@ static int kxsd9_read_raw(struct iio_dev *indio_dev, if (ret < 0) goto error_ret; *val = ret; + ret = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C)); if (ret < 0) goto error_ret; + *val = 0; *val2 = kxsd9_micro_scales[ret & KXSD9_FS_MASK]; ret = IIO_VAL_INT_PLUS_MICRO; break; diff --git a/drivers/iio/accel/mma7455_core.c b/drivers/iio/accel/mma7455_core.c index c902f54..6551085 100644 --- a/drivers/iio/accel/mma7455_core.c +++ b/drivers/iio/accel/mma7455_core.c @@ -97,7 +97,8 @@ static irqreturn_t mma7455_trigger_handler(int irq, void *p) if (ret) goto done; - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git b/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c new file mode 100644 index 0000000..0acdee5 --- /dev/null +++ b/drivers/iio/accel/mma7660.c @@ -0,0 +1,277 @@ +/** + * Freescale MMA7660FC 3-Axis Accelerometer + * + * Copyright (c) 2016, Intel Corporation. + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * IIO driver for Freescale MMA7660FC; 7-bit I2C address: 0x4c. + */ + +#include +#include +#include +#include +#include + +#define MMA7660_DRIVER_NAME "mma7660" + +#define MMA7660_REG_XOUT 0x00 +#define MMA7660_REG_YOUT 0x01 +#define MMA7660_REG_ZOUT 0x02 +#define MMA7660_REG_OUT_BIT_ALERT BIT(6) + +#define MMA7660_REG_MODE 0x07 +#define MMA7660_REG_MODE_BIT_MODE BIT(0) +#define MMA7660_REG_MODE_BIT_TON BIT(2) + +#define MMA7660_I2C_READ_RETRIES 5 + +/* + * The accelerometer has one measurement range: + * + * -1.5g - +1.5g (6-bit, signed) + * + * scale = (1.5 + 1.5) * 9.81 / (2^6 - 1) = 0.467142857 + */ + +#define MMA7660_SCALE_AVAIL "0.467142857" + +const int mma7660_nscale = 467142857; + +#define MMA7660_CHANNEL(reg, axis) { \ + .type = IIO_ACCEL, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec mma7660_channels[] = { + MMA7660_CHANNEL(MMA7660_REG_XOUT, X), + MMA7660_CHANNEL(MMA7660_REG_YOUT, Y), + MMA7660_CHANNEL(MMA7660_REG_ZOUT, Z), +}; + +enum mma7660_mode { + MMA7660_MODE_STANDBY, + MMA7660_MODE_ACTIVE +}; + +struct mma7660_data { + struct i2c_client *client; + struct mutex lock; + enum mma7660_mode mode; +}; + +static IIO_CONST_ATTR(in_accel_scale_available, MMA7660_SCALE_AVAIL); + +static struct attribute *mma7660_attributes[] = { + &iio_const_attr_in_accel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group mma7660_attribute_group = { + .attrs = mma7660_attributes +}; + +static int mma7660_set_mode(struct mma7660_data *data, + enum mma7660_mode mode) +{ + int ret; + struct i2c_client *client = data->client; + + if (mode == data->mode) + return 0; + + ret = i2c_smbus_read_byte_data(client, MMA7660_REG_MODE); + if (ret < 0) { + dev_err(&client->dev, "failed to read sensor mode\n"); + return ret; + } + + if (mode == MMA7660_MODE_ACTIVE) { + ret &= ~MMA7660_REG_MODE_BIT_TON; + ret |= MMA7660_REG_MODE_BIT_MODE; + } else { + ret &= ~MMA7660_REG_MODE_BIT_TON; + ret &= ~MMA7660_REG_MODE_BIT_MODE; + } + + ret = i2c_smbus_write_byte_data(client, MMA7660_REG_MODE, ret); + if (ret < 0) { + dev_err(&client->dev, "failed to change sensor mode\n"); + return ret; + } + + data->mode = mode; + + return ret; +} + +static int mma7660_read_accel(struct mma7660_data *data, u8 address) +{ + int ret, retries = MMA7660_I2C_READ_RETRIES; + struct i2c_client *client = data->client; + + /* + * Read data. If the Alert bit is set, the register was read at + * the same time as the device was attempting to update the content. + * The solution is to read the register again. Do this only + * MMA7660_I2C_READ_RETRIES times to avoid spending too much time + * in the kernel. + */ + do { + ret = i2c_smbus_read_byte_data(client, address); + if (ret < 0) { + dev_err(&client->dev, "register read failed\n"); + return ret; + } + } while (retries-- > 0 && ret & MMA7660_REG_OUT_BIT_ALERT); + + if (ret & MMA7660_REG_OUT_BIT_ALERT) { + dev_err(&client->dev, "all register read retries failed\n"); + return -ETIMEDOUT; + } + + return ret; +} + +static int mma7660_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mma7660_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->lock); + ret = mma7660_read_accel(data, chan->address); + mutex_unlock(&data->lock); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 5); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = mma7660_nscale; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + + return -EINVAL; +} + +static const struct iio_info mma7660_info = { + .driver_module = THIS_MODULE, + .read_raw = mma7660_read_raw, + .attrs = &mma7660_attribute_group, +}; + +static int mma7660_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct iio_dev *indio_dev; + struct mma7660_data *data; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) { + dev_err(&client->dev, "iio allocation failed!\n"); + return -ENOMEM; + } + + data = iio_priv(indio_dev); + data->client = client; + i2c_set_clientdata(client, indio_dev); + mutex_init(&data->lock); + data->mode = MMA7660_MODE_STANDBY; + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &mma7660_info; + indio_dev->name = MMA7660_DRIVER_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mma7660_channels; + indio_dev->num_channels = ARRAY_SIZE(mma7660_channels); + + ret = mma7660_set_mode(data, MMA7660_MODE_ACTIVE); + if (ret < 0) + return ret; + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "device_register failed\n"); + mma7660_set_mode(data, MMA7660_MODE_STANDBY); + } + + return ret; +} + +static int mma7660_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + + return mma7660_set_mode(iio_priv(indio_dev), MMA7660_MODE_STANDBY); +} + +#ifdef CONFIG_PM_SLEEP +static int mma7660_suspend(struct device *dev) +{ + struct mma7660_data *data; + + data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + return mma7660_set_mode(data, MMA7660_MODE_STANDBY); +} + +static int mma7660_resume(struct device *dev) +{ + struct mma7660_data *data; + + data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + return mma7660_set_mode(data, MMA7660_MODE_ACTIVE); +} + +static SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend, mma7660_resume); + +#define MMA7660_PM_OPS (&mma7660_pm_ops) +#else +#define MMA7660_PM_OPS NULL +#endif + +static const struct i2c_device_id mma7660_i2c_id[] = { + {"mma7660", 0}, + {} +}; + +static const struct acpi_device_id mma7660_acpi_id[] = { + {"MMA7660", 0}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, mma7660_acpi_id); + +static struct i2c_driver mma7660_driver = { + .driver = { + .name = "mma7660", + .pm = MMA7660_PM_OPS, + .acpi_match_table = ACPI_PTR(mma7660_acpi_id), + }, + .probe = mma7660_probe, + .remove = mma7660_remove, + .id_table = mma7660_i2c_id, +}; + +module_i2c_driver(mma7660_driver); + +MODULE_AUTHOR("Constantin Musca "); +MODULE_DESCRIPTION("Freescale MMA7660FC 3-Axis Accelerometer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index e225d3c..d41e1b5 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1,22 +1,22 @@ /* - * mma8452.c - Support for following Freescale 3-axis accelerometers: + * mma8452.c - Support for following Freescale / NXP 3-axis accelerometers: * - * MMA8451Q (14 bit) - * MMA8452Q (12 bit) - * MMA8453Q (10 bit) - * MMA8652FC (12 bit) - * MMA8653FC (10 bit) - * FXLS8471Q (14 bit) + * device name digital output 7-bit I2C slave address (pin selectable) + * --------------------------------------------------------------------- + * MMA8451Q 14 bit 0x1c / 0x1d + * MMA8452Q 12 bit 0x1c / 0x1d + * MMA8453Q 10 bit 0x1c / 0x1d + * MMA8652FC 12 bit 0x1d + * MMA8653FC 10 bit 0x1d + * FXLS8471Q 14 bit 0x1e / 0x1d / 0x1c / 0x1f * - * Copyright 2015 Martin Kepplinger + * Copyright 2015 Martin Kepplinger * Copyright 2014 Peter Meerwald * * This file is subject to the terms and conditions of version 2 of * the GNU General Public License. See the file COPYING in the main * directory of this archive for more details. * - * 7-bit I2C slave address 0x1c/0x1d (pin selectable) - * * TODO: orientation events */ @@ -76,6 +76,8 @@ #define MMA8452_CTRL_DR_DEFAULT 0x4 /* 50 Hz sample frequency */ #define MMA8452_CTRL_REG2 0x2b #define MMA8452_CTRL_REG2_RST BIT(6) +#define MMA8452_CTRL_REG2_MODS_SHIFT 3 +#define MMA8452_CTRL_REG2_MODS_MASK 0x1b #define MMA8452_CTRL_REG4 0x2d #define MMA8452_CTRL_REG5 0x2e #define MMA8452_OFF_X 0x2f @@ -106,7 +108,7 @@ struct mma8452_data { }; /** - * struct mma_chip_info - chip specific data for Freescale's accelerometers + * struct mma_chip_info - chip specific data * @chip_id: WHO_AM_I register's value * @channels: struct iio_chan_spec matching the device's * capabilities @@ -257,20 +259,17 @@ static const int mma8452_samp_freq[8][2] = { {6, 250000}, {1, 560000} }; -/* Datasheet table 35 (step time vs sample frequency) */ -static const int mma8452_transient_time_step_us[8] = { - 1250, - 2500, - 5000, - 10000, - 20000, - 20000, - 20000, - 20000 +/* Datasheet table: step time "Relationship with the ODR" (sample frequency) */ +static const int mma8452_transient_time_step_us[4][8] = { + { 1250, 2500, 5000, 10000, 20000, 20000, 20000, 20000 }, /* normal */ + { 1250, 2500, 5000, 10000, 20000, 80000, 80000, 80000 }, /* l p l n */ + { 1250, 2500, 2500, 2500, 2500, 2500, 2500, 2500 }, /* high res*/ + { 1250, 2500, 5000, 10000, 20000, 80000, 160000, 160000 } /* l p */ }; -/* Datasheet table 18 (normal mode) */ -static const int mma8452_hp_filter_cutoff[8][4][2] = { +/* Datasheet table "High-Pass Filter Cutoff Options" */ +static const int mma8452_hp_filter_cutoff[4][8][4][2] = { + { /* normal */ { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 800 Hz sample */ { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 400 Hz sample */ { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, /* 200 Hz sample */ @@ -279,8 +278,61 @@ static const int mma8452_hp_filter_cutoff[8][4][2] = { { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 12.5 Hz sample */ { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 6.25 Hz sample */ { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} } /* 1.56 Hz sample */ + }, + { /* low noise low power */ + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, + { {4, 0}, {2, 0}, {1, 0}, {0, 500000} }, + { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, + { {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} }, + { {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} }, + { {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} } + }, + { /* high resolution */ + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} } + }, + { /* low power */ + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, + { {4, 0}, {2, 0}, {1, 0}, {0, 500000} }, + { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, + { {1, 0}, {0, 500000}, {0, 250000}, {0, 125000} }, + { {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} }, + { {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} }, + { {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} } + } }; +/* Datasheet table "MODS Oversampling modes averaging values at each ODR" */ +static const u16 mma8452_os_ratio[4][8] = { + /* 800 Hz, 400 Hz, ... , 1.56 Hz */ + { 2, 4, 4, 4, 4, 16, 32, 128 }, /* normal */ + { 2, 4, 4, 4, 4, 4, 8, 32 }, /* low power low noise */ + { 2, 4, 8, 16, 32, 128, 256, 1024 }, /* high resolution */ + { 2, 2, 2, 2, 2, 2, 4, 16 } /* low power */ +}; + +static int mma8452_get_power_mode(struct mma8452_data *data) +{ + int reg; + + reg = i2c_smbus_read_byte_data(data->client, + MMA8452_CTRL_REG2); + if (reg < 0) + return reg; + + return ((reg & MMA8452_CTRL_REG2_MODS_MASK) >> + MMA8452_CTRL_REG2_MODS_SHIFT); +} + static ssize_t mma8452_show_samp_freq_avail(struct device *dev, struct device_attribute *attr, char *buf) @@ -306,10 +358,39 @@ static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct mma8452_data *data = iio_priv(indio_dev); + int i, j; + + i = mma8452_get_odr_index(data); + j = mma8452_get_power_mode(data); + if (j < 0) + return j; + + return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[j][i], + ARRAY_SIZE(mma8452_hp_filter_cutoff[0][0])); +} + +static ssize_t mma8452_show_os_ratio_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mma8452_data *data = iio_priv(indio_dev); int i = mma8452_get_odr_index(data); + int j; + u16 val = 0; + size_t len = 0; - return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[i], - ARRAY_SIZE(mma8452_hp_filter_cutoff[0])); + for (j = 0; j < ARRAY_SIZE(mma8452_os_ratio); j++) { + if (val == mma8452_os_ratio[j][i]) + continue; + + val = mma8452_os_ratio[j][i]; + + len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", val); + } + buf[len - 1] = '\n'; + + return len; } static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail); @@ -317,6 +398,8 @@ static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO, mma8452_show_scale_avail, NULL, 0); static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available, S_IRUGO, mma8452_show_hp_cutoff_avail, NULL, 0); +static IIO_DEVICE_ATTR(in_accel_oversampling_ratio_available, S_IRUGO, + mma8452_show_os_ratio_avail, NULL, 0); static int mma8452_get_samp_freq_index(struct mma8452_data *data, int val, int val2) @@ -335,24 +418,33 @@ static int mma8452_get_scale_index(struct mma8452_data *data, int val, int val2) static int mma8452_get_hp_filter_index(struct mma8452_data *data, int val, int val2) { - int i = mma8452_get_odr_index(data); + int i, j; + + i = mma8452_get_odr_index(data); + j = mma8452_get_power_mode(data); + if (j < 0) + return j; - return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[i], - ARRAY_SIZE(mma8452_hp_filter_cutoff[0]), val, val2); + return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[j][i], + ARRAY_SIZE(mma8452_hp_filter_cutoff[0][0]), val, val2); } static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz) { - int i, ret; + int j, i, ret; ret = i2c_smbus_read_byte_data(data->client, MMA8452_HP_FILTER_CUTOFF); if (ret < 0) return ret; i = mma8452_get_odr_index(data); + j = mma8452_get_power_mode(data); + if (j < 0) + return j; + ret &= MMA8452_HP_FILTER_CUTOFF_SEL_MASK; - *hz = mma8452_hp_filter_cutoff[i][ret][0]; - *uHz = mma8452_hp_filter_cutoff[i][ret][1]; + *hz = mma8452_hp_filter_cutoff[j][i][ret][0]; + *uHz = mma8452_hp_filter_cutoff[j][i][ret][1]; return 0; } @@ -414,6 +506,15 @@ static int mma8452_read_raw(struct iio_dev *indio_dev, } return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = mma8452_get_power_mode(data); + if (ret < 0) + return ret; + + i = mma8452_get_odr_index(data); + + *val = mma8452_os_ratio[ret][i]; + return IIO_VAL_INT; } return -EINVAL; @@ -480,6 +581,21 @@ fail: return ret; } +static int mma8452_set_power_mode(struct mma8452_data *data, u8 mode) +{ + int reg; + + reg = i2c_smbus_read_byte_data(data->client, + MMA8452_CTRL_REG2); + if (reg < 0) + return reg; + + reg &= ~MMA8452_CTRL_REG2_MODS_MASK; + reg |= mode << MMA8452_CTRL_REG2_MODS_SHIFT; + + return mma8452_change_config(data, MMA8452_CTRL_REG2, reg); +} + /* returns >0 if in freefall mode, 0 if not or <0 if an error occurred */ static int mma8452_freefall_mode_enabled(struct mma8452_data *data) { @@ -518,11 +634,7 @@ static int mma8452_set_freefall_mode(struct mma8452_data *data, bool state) val |= MMA8452_FF_MT_CFG_OAE; } - val = mma8452_change_config(data, chip->ev_cfg, val); - if (val) - return val; - - return 0; + return mma8452_change_config(data, chip->ev_cfg, val); } static int mma8452_set_hp_filter_frequency(struct mma8452_data *data, @@ -597,6 +709,14 @@ static int mma8452_write_raw(struct iio_dev *indio_dev, return mma8452_change_config(data, MMA8452_DATA_CFG, data->data_cfg); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = mma8452_get_odr_index(data); + + for (i = 0; i < ARRAY_SIZE(mma8452_os_ratio); i++) { + if (mma8452_os_ratio[i][ret] == val) + return mma8452_set_power_mode(data, i); + } + default: return -EINVAL; } @@ -610,7 +730,7 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev, int *val, int *val2) { struct mma8452_data *data = iio_priv(indio_dev); - int ret, us; + int ret, us, power_mode; switch (info) { case IIO_EV_INFO_VALUE: @@ -629,7 +749,11 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev, if (ret < 0) return ret; - us = ret * mma8452_transient_time_step_us[ + power_mode = mma8452_get_power_mode(data); + if (power_mode < 0) + return power_mode; + + us = ret * mma8452_transient_time_step_us[power_mode][ mma8452_get_odr_index(data)]; *val = us / USEC_PER_SEC; *val2 = us % USEC_PER_SEC; @@ -677,8 +801,12 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev, val); case IIO_EV_INFO_PERIOD: + ret = mma8452_get_power_mode(data); + if (ret < 0) + return ret; + steps = (val * USEC_PER_SEC + val2) / - mma8452_transient_time_step_us[ + mma8452_transient_time_step_us[ret][ mma8452_get_odr_index(data)]; if (steps < 0 || steps > 0xff) @@ -785,7 +913,7 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev, static void mma8452_transient_interrupt(struct iio_dev *indio_dev) { struct mma8452_data *data = iio_priv(indio_dev); - s64 ts = iio_get_time_ns(); + s64 ts = iio_get_time_ns(indio_dev); int src; src = i2c_smbus_read_byte_data(data->client, data->chip_info->ev_src); @@ -865,7 +993,7 @@ static irqreturn_t mma8452_trigger_handler(int irq, void *p) goto done; iio_push_to_buffers_with_timestamp(indio_dev, buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -978,7 +1106,8 @@ static struct attribute_group mma8452_event_attribute_group = { BIT(IIO_CHAN_INFO_CALIBBIAS), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .scan_index = idx, \ .scan_type = { \ .sign = 's', \ @@ -998,7 +1127,8 @@ static struct attribute_group mma8452_event_attribute_group = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_CALIBBIAS), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ - BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .scan_index = idx, \ .scan_type = { \ .sign = 's', \ @@ -1171,6 +1301,7 @@ static struct attribute *mma8452_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_accel_scale_available.dev_attr.attr, &iio_dev_attr_in_accel_filter_high_pass_3db_frequency_available.dev_attr.attr, + &iio_dev_attr_in_accel_oversampling_ratio_available.dev_attr.attr, NULL }; @@ -1444,8 +1575,8 @@ static int mma8452_probe(struct i2c_client *client, goto buffer_cleanup; ret = mma8452_set_freefall_mode(data, false); - if (ret) - return ret; + if (ret < 0) + goto buffer_cleanup; return 0; @@ -1558,5 +1689,5 @@ static struct i2c_driver mma8452_driver = { module_i2c_driver(mma8452_driver); MODULE_AUTHOR("Peter Meerwald "); -MODULE_DESCRIPTION("Freescale MMA8452 accelerometer driver"); +MODULE_DESCRIPTION("Freescale / NXP MMA8452 accelerometer driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index d899a4d..bf27044 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -391,7 +391,7 @@ static irqreturn_t mma9551_event_handler(int irq, void *private) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_INCLI, 0, (mma_axis + 1), IIO_EV_TYPE_ROC, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); out: mutex_unlock(&data->mutex); diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index bb05f3e..36bf197 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -1001,7 +1001,7 @@ static irqreturn_t mma9553_irq_handler(int irq, void *private) struct iio_dev *indio_dev = private; struct mma9553_data *data = iio_priv(indio_dev); - data->timestamp = iio_get_time_ns(); + data->timestamp = iio_get_time_ns(indio_dev); /* * Since we only configure the interrupt pin when an * event is enabled, we are sure we have at least diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 57f83a6..f8dfdb6 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -29,6 +29,7 @@ #define LSM330_ACCEL_DEV_NAME "lsm330_accel" #define LSM303AGR_ACCEL_DEV_NAME "lsm303agr_accel" #define LIS2DH12_ACCEL_DEV_NAME "lis2dh12_accel" +#define LIS3L02DQ_ACCEL_DEV_NAME "lis3l02dq" /** * struct st_sensors_platform_data - default accel platform data diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 4d95bfc..da3fb06 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -215,6 +215,22 @@ #define ST_ACCEL_6_IHL_IRQ_MASK 0x80 #define ST_ACCEL_6_MULTIREAD_BIT true +/* CUSTOM VALUES FOR SENSOR 7 */ +#define ST_ACCEL_7_ODR_ADDR 0x20 +#define ST_ACCEL_7_ODR_MASK 0x30 +#define ST_ACCEL_7_ODR_AVL_280HZ_VAL 0x00 +#define ST_ACCEL_7_ODR_AVL_560HZ_VAL 0x01 +#define ST_ACCEL_7_ODR_AVL_1120HZ_VAL 0x02 +#define ST_ACCEL_7_ODR_AVL_4480HZ_VAL 0x03 +#define ST_ACCEL_7_PW_ADDR 0x20 +#define ST_ACCEL_7_PW_MASK 0xc0 +#define ST_ACCEL_7_FS_AVL_2_GAIN IIO_G_TO_M_S_2(488) +#define ST_ACCEL_7_BDU_ADDR 0x21 +#define ST_ACCEL_7_BDU_MASK 0x40 +#define ST_ACCEL_7_DRDY_IRQ_ADDR 0x21 +#define ST_ACCEL_7_DRDY_IRQ_INT1_MASK 0x04 +#define ST_ACCEL_7_MULTIREAD_BIT false + static const struct iio_chan_spec st_accel_8bit_channels[] = { ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), @@ -662,6 +678,54 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .multi_read_bit = ST_ACCEL_6_MULTIREAD_BIT, .bootime = 2, }, + { + /* No WAI register present */ + .sensors_supported = { + [0] = LIS3L02DQ_ACCEL_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr = ST_ACCEL_7_ODR_ADDR, + .mask = ST_ACCEL_7_ODR_MASK, + .odr_avl = { + { 280, ST_ACCEL_7_ODR_AVL_280HZ_VAL, }, + { 560, ST_ACCEL_7_ODR_AVL_560HZ_VAL, }, + { 1120, ST_ACCEL_7_ODR_AVL_1120HZ_VAL, }, + { 4480, ST_ACCEL_7_ODR_AVL_4480HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_ACCEL_7_PW_ADDR, + .mask = ST_ACCEL_7_PW_MASK, + .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .gain = ST_ACCEL_7_FS_AVL_2_GAIN, + }, + }, + }, + /* + * The part has a BDU bit but if set the data is never + * updated so don't set it. + */ + .bdu = { + }, + .drdy_irq = { + .addr = ST_ACCEL_7_DRDY_IRQ_ADDR, + .mask_int1 = ST_ACCEL_7_DRDY_IRQ_INT1_MASK, + .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + }, + .multi_read_bit = ST_ACCEL_7_MULTIREAD_BIT, + .bootime = 2, + }, }; static int st_accel_read_raw(struct iio_dev *indio_dev, @@ -758,13 +822,15 @@ int st_accel_common_probe(struct iio_dev *indio_dev) indio_dev->info = &accel_info; mutex_init(&adata->tb.buf_lock); - st_sensors_power_enable(indio_dev); + err = st_sensors_power_enable(indio_dev); + if (err) + return err; err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_accel_sensors_settings), st_accel_sensors_settings); if (err < 0) - return err; + goto st_accel_power_off; adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS; adata->multiread_bit = adata->sensor_settings->multi_read_bit; @@ -781,11 +847,11 @@ int st_accel_common_probe(struct iio_dev *indio_dev) err = st_sensors_init_sensor(indio_dev, adata->dev->platform_data); if (err < 0) - return err; + goto st_accel_power_off; err = st_accel_allocate_ring(indio_dev); if (err < 0) - return err; + goto st_accel_power_off; if (irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -808,6 +874,8 @@ st_accel_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_accel_probe_trigger_error: st_accel_deallocate_ring(indio_dev); +st_accel_power_off: + st_sensors_power_disable(indio_dev); return err; } diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 7333ee9..e9d427a 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -80,6 +80,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,h3lis331dl-accel", .data = H3LIS331DL_DRIVER_NAME, }, + { + .compatible = "st,lis3l02dq", + .data = LIS3L02DQ_ACCEL_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -130,6 +134,7 @@ static const struct i2c_device_id st_accel_id_table[] = { { LSM330_ACCEL_DEV_NAME }, { LSM303AGR_ACCEL_DEV_NAME }, { LIS2DH12_ACCEL_DEV_NAME }, + { LIS3L02DQ_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(i2c, st_accel_id_table); diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index fcd5847..efd4394 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -59,6 +59,7 @@ static const struct spi_device_id st_accel_id_table[] = { { LSM330_ACCEL_DEV_NAME }, { LSM303AGR_ACCEL_DEV_NAME }, { LIS2DH12_ACCEL_DEV_NAME }, + { LIS3L02DQ_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 25378c5..7675772 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -153,6 +153,18 @@ config AXP288_ADC To compile this driver as a module, choose M here: the module will be called axp288_adc. +config BCM_IPROC_ADC + tristate "Broadcom IPROC ADC driver" + depends on ARCH_BCM_IPROC || COMPILE_TEST + depends on MFD_SYSCON + default ARCH_BCM_CYGNUS + help + Say Y here if you want to add support for the Broadcom static + ADC driver. + + Broadcom iProc ADC driver. Broadcom iProc ADC controller has 8 + channels. The driver allows the user to read voltage values. + config BERLIN2_ADC tristate "Marvell Berlin2 ADC driver" depends on ARCH_BERLIN @@ -377,6 +389,7 @@ config QCOM_SPMI_VADC config ROCKCHIP_SARADC tristate "Rockchip SARADC driver" depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST) + depends on RESET_CONTROLLER help Say yes here to build support for the SARADC found in SoCs from Rockchip. diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 38638d4..0ba0d50 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_AD799X) += ad799x.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o obj-$(CONFIG_AXP288_ADC) += axp288_adc.o +obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index 2123f0a..c0f6a98 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -154,12 +154,11 @@ static int ad7266_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - if (iio_buffer_enabled(indio_dev)) - return -EBUSY; - - ret = ad7266_read_single(st, val, chan->address); + ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; + ret = ad7266_read_single(st, val, chan->address); + iio_device_release_direct_mode(indio_dev); *val = (*val >> 2) & 0xfff; if (chan->scan_type.sign == 's') @@ -441,6 +440,7 @@ static int ad7266_probe(struct spi_device *spi) st->spi = spi; indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &ad7266_info; diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c index c0eabf1..1d90b02 100644 --- a/drivers/iio/adc/ad7291.c +++ b/drivers/iio/adc/ad7291.c @@ -115,7 +115,7 @@ static irqreturn_t ad7291_event_handler(int irq, void *private) u16 t_status, v_status; u16 command; int i; - s64 timestamp = iio_get_time_ns(); + s64 timestamp = iio_get_time_ns(indio_dev); if (ad7291_i2c_read(chip, AD7291_T_ALERT_STATUS, &t_status)) return IRQ_HANDLED; @@ -505,6 +505,7 @@ static int ad7291_probe(struct i2c_client *client, indio_dev->num_channels = ARRAY_SIZE(ad7291_channels); indio_dev->dev.parent = &client->dev; + indio_dev->dev.of_node = client->dev.of_node; indio_dev->info = &ad7291_info; indio_dev->modes = INDIO_DIRECT_MODE; diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index 62bb8f7..10ec8fc 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -163,7 +163,7 @@ static irqreturn_t ad7298_trigger_handler(int irq, void *p) goto done; iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -315,6 +315,7 @@ static int ad7298_probe(struct spi_device *spi) indio_dev->name = spi_get_device_id(spi)->name; indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = ad7298_channels; indio_dev->num_channels = ARRAY_SIZE(ad7298_channels); diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index be85c2a..b7ecf9a 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -70,7 +70,7 @@ static irqreturn_t ad7476_trigger_handler(int irq, void *p) goto done; iio_push_to_buffers_with_timestamp(indio_dev, st->data, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -106,12 +106,11 @@ static int ad7476_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) - ret = -EBUSY; - else - ret = ad7476_scan_direct(st); - mutex_unlock(&indio_dev->mlock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = ad7476_scan_direct(st); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; @@ -228,6 +227,7 @@ static int ad7476_probe(struct spi_device *spi) /* Establish that the iio_dev is a child of the spi device */ indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = st->chip_info->channel; diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index cf172d5..1817ebf 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -272,30 +272,22 @@ static ssize_t ad7791_write_frequency(struct device *dev, struct ad7791_state *st = iio_priv(indio_dev); int i, ret; - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { - mutex_unlock(&indio_dev->mlock); - return -EBUSY; - } - mutex_unlock(&indio_dev->mlock); - - ret = -EINVAL; - - for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) { - if (sysfs_streq(ad7791_sample_freq_avail[i], buf)) { - - mutex_lock(&indio_dev->mlock); - st->filter &= ~AD7791_FILTER_RATE_MASK; - st->filter |= i; - ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, - sizeof(st->filter), st->filter); - mutex_unlock(&indio_dev->mlock); - ret = 0; + for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) + if (sysfs_streq(ad7791_sample_freq_avail[i], buf)) break; - } - } + if (i == ARRAY_SIZE(ad7791_sample_freq_avail)) + return -EINVAL; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + st->filter &= ~AD7791_FILTER_RATE_MASK; + st->filter |= i; + ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, sizeof(st->filter), + st->filter); + iio_device_release_direct_mode(indio_dev); - return ret ? ret : len; + return len; } static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, @@ -383,6 +375,7 @@ static int ad7791_probe(struct spi_device *spi) spi_set_drvdata(spi, indio_dev); indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = st->info->channels; diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 7b07bb6..847789b 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -369,13 +369,6 @@ static ssize_t ad7793_write_frequency(struct device *dev, long lval; int i, ret; - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { - mutex_unlock(&indio_dev->mlock); - return -EBUSY; - } - mutex_unlock(&indio_dev->mlock); - ret = kstrtol(buf, 10, &lval); if (ret) return ret; @@ -383,20 +376,21 @@ static ssize_t ad7793_write_frequency(struct device *dev, if (lval == 0) return -EINVAL; - ret = -EINVAL; - for (i = 0; i < 16; i++) - if (lval == st->chip_info->sample_freq_avail[i]) { - mutex_lock(&indio_dev->mlock); - st->mode &= ~AD7793_MODE_RATE(-1); - st->mode |= AD7793_MODE_RATE(i); - ad_sd_write_reg(&st->sd, AD7793_REG_MODE, - sizeof(st->mode), st->mode); - mutex_unlock(&indio_dev->mlock); - ret = 0; - } + if (lval == st->chip_info->sample_freq_avail[i]) + break; + if (i == 16) + return -EINVAL; - return ret ? ret : len; + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + st->mode &= ~AD7793_MODE_RATE(-1); + st->mode |= AD7793_MODE_RATE(i); + ad_sd_write_reg(&st->sd, AD7793_REG_MODE, sizeof(st->mode), st->mode); + iio_device_release_direct_mode(indio_dev); + + return len; } static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, @@ -790,6 +784,7 @@ static int ad7793_probe(struct spi_device *spi) spi_set_drvdata(spi, indio_dev); indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = st->chip_info->channels; diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 2d3c397..7a483bf 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -122,7 +122,7 @@ static irqreturn_t ad7887_trigger_handler(int irq, void *p) goto done; iio_push_to_buffers_with_timestamp(indio_dev, st->data, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -156,12 +156,11 @@ static int ad7887_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) - ret = -EBUSY; - else - ret = ad7887_scan_direct(st, chan->address); - mutex_unlock(&indio_dev->mlock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = ad7887_scan_direct(st, chan->address); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; @@ -265,6 +264,7 @@ static int ad7887_probe(struct spi_device *spi) /* Estabilish that the iio_dev is a child of the spi device */ indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->info = &ad7887_info; indio_dev->modes = INDIO_DIRECT_MODE; diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index 45e29cc..77a675e 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -181,7 +181,7 @@ static irqreturn_t ad7923_trigger_handler(int irq, void *p) goto done; iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -233,12 +233,11 @@ static int ad7923_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) - ret = -EBUSY; - else - ret = ad7923_scan_direct(st, chan->address); - mutex_unlock(&indio_dev->mlock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = ad7923_scan_direct(st, chan->address); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; @@ -289,6 +288,7 @@ static int ad7923_probe(struct spi_device *spi) indio_dev->name = spi_get_device_id(spi)->name; indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = info->channels; indio_dev->num_channels = info->num_channels; diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index a3f5254..9704090 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -212,7 +212,7 @@ static irqreturn_t ad799x_trigger_handler(int irq, void *p) goto out; iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); @@ -282,12 +282,11 @@ static int ad799x_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) - ret = -EBUSY; - else - ret = ad799x_scan_direct(st, chan->scan_index); - mutex_unlock(&indio_dev->mlock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = ad799x_scan_direct(st, chan->scan_index); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; @@ -395,11 +394,9 @@ static int ad799x_write_event_config(struct iio_dev *indio_dev, struct ad799x_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { - ret = -EBUSY; - goto done; - } + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; if (state) st->config |= BIT(chan->scan_index) << AD799X_CHANNEL_SHIFT; @@ -412,10 +409,7 @@ static int ad799x_write_event_config(struct iio_dev *indio_dev, st->config &= ~AD7998_ALERT_EN; ret = ad799x_write_config(st, st->config); - -done: - mutex_unlock(&indio_dev->mlock); - + iio_device_release_direct_mode(indio_dev); return ret; } @@ -508,7 +502,7 @@ static irqreturn_t ad799x_event_handler(int irq, void *private) (i >> 1), IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } done: @@ -533,6 +527,7 @@ static struct attribute_group ad799x_event_attrs_group = { static const struct iio_info ad7991_info = { .read_raw = &ad799x_read_raw, .driver_module = THIS_MODULE, + .update_scan_mode = ad799x_update_scan_mode, }; static const struct iio_info ad7993_4_7_8_noirq_info = { @@ -812,6 +807,7 @@ static int ad799x_probe(struct i2c_client *client, st->client = client; indio_dev->dev.parent = &client->dev; + indio_dev->dev.of_node = client->dev.of_node; indio_dev->name = id->name; indio_dev->info = st->chip_config->info; diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 52430ba..0438c68 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -381,8 +381,8 @@ static irqreturn_t at91_adc_rl_interrupt(int irq, void *private) st->ts_bufferedmeasure = false; input_report_key(st->ts_input, BTN_TOUCH, 0); input_sync(st->ts_input); - } else if (status & AT91_ADC_EOC(3)) { - /* Conversion finished */ + } else if (status & AT91_ADC_EOC(3) && st->ts_input) { + /* Conversion finished and we've a touchscreen */ if (st->ts_bufferedmeasure) { /* * Last measurement is always discarded, since it can diff --git b/drivers/iio/adc/bcm_iproc_adc.c b/drivers/iio/adc/bcm_iproc_adc.c new file mode 100644 index 0000000..21d38c8 --- /dev/null +++ b/drivers/iio/adc/bcm_iproc_adc.c @@ -0,0 +1,644 @@ +/* + * Copyright 2016 Broadcom + * + * 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 (the "GPL"). + * + * 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 version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Below Register's are common to IPROC ADC and Touchscreen IP */ +#define IPROC_REGCTL1 0x00 +#define IPROC_REGCTL2 0x04 +#define IPROC_INTERRUPT_THRES 0x08 +#define IPROC_INTERRUPT_MASK 0x0c +#define IPROC_INTERRUPT_STATUS 0x10 +#define IPROC_ANALOG_CONTROL 0x1c +#define IPROC_CONTROLLER_STATUS 0x14 +#define IPROC_AUX_DATA 0x20 +#define IPROC_SOFT_BYPASS_CONTROL 0x38 +#define IPROC_SOFT_BYPASS_DATA 0x3C + +/* IPROC ADC Channel register offsets */ +#define IPROC_ADC_CHANNEL_REGCTL1 0x800 +#define IPROC_ADC_CHANNEL_REGCTL2 0x804 +#define IPROC_ADC_CHANNEL_STATUS 0x808 +#define IPROC_ADC_CHANNEL_INTERRUPT_STATUS 0x80c +#define IPROC_ADC_CHANNEL_INTERRUPT_MASK 0x810 +#define IPROC_ADC_CHANNEL_DATA 0x814 +#define IPROC_ADC_CHANNEL_OFFSET 0x20 + +/* Bit definitions for IPROC_REGCTL2 */ +#define IPROC_ADC_AUXIN_SCAN_ENA BIT(0) +#define IPROC_ADC_PWR_LDO BIT(5) +#define IPROC_ADC_PWR_ADC BIT(4) +#define IPROC_ADC_PWR_BG BIT(3) +#define IPROC_ADC_CONTROLLER_EN BIT(17) + +/* Bit definitions for IPROC_INTERRUPT_MASK and IPROC_INTERRUPT_STATUS */ +#define IPROC_ADC_AUXDATA_RDY_INTR BIT(3) +#define IPROC_ADC_INTR 9 +#define IPROC_ADC_INTR_MASK (0xFF << IPROC_ADC_INTR) + +/* Bit definitions for IPROC_ANALOG_CONTROL */ +#define IPROC_ADC_CHANNEL_SEL 11 +#define IPROC_ADC_CHANNEL_SEL_MASK (0x7 << IPROC_ADC_CHANNEL_SEL) + +/* Bit definitions for IPROC_ADC_CHANNEL_REGCTL1 */ +#define IPROC_ADC_CHANNEL_ROUNDS 0x2 +#define IPROC_ADC_CHANNEL_ROUNDS_MASK (0x3F << IPROC_ADC_CHANNEL_ROUNDS) +#define IPROC_ADC_CHANNEL_MODE 0x1 +#define IPROC_ADC_CHANNEL_MODE_MASK (0x1 << IPROC_ADC_CHANNEL_MODE) +#define IPROC_ADC_CHANNEL_MODE_TDM 0x1 +#define IPROC_ADC_CHANNEL_MODE_SNAPSHOT 0x0 +#define IPROC_ADC_CHANNEL_ENABLE 0x0 +#define IPROC_ADC_CHANNEL_ENABLE_MASK 0x1 + +/* Bit definitions for IPROC_ADC_CHANNEL_REGCTL2 */ +#define IPROC_ADC_CHANNEL_WATERMARK 0x0 +#define IPROC_ADC_CHANNEL_WATERMARK_MASK \ + (0x3F << IPROC_ADC_CHANNEL_WATERMARK) + +#define IPROC_ADC_WATER_MARK_LEVEL 0x1 + +/* Bit definitions for IPROC_ADC_CHANNEL_STATUS */ +#define IPROC_ADC_CHANNEL_DATA_LOST 0x0 +#define IPROC_ADC_CHANNEL_DATA_LOST_MASK \ + (0x0 << IPROC_ADC_CHANNEL_DATA_LOST) +#define IPROC_ADC_CHANNEL_VALID_ENTERIES 0x1 +#define IPROC_ADC_CHANNEL_VALID_ENTERIES_MASK \ + (0xFF << IPROC_ADC_CHANNEL_VALID_ENTERIES) +#define IPROC_ADC_CHANNEL_TOTAL_ENTERIES 0x9 +#define IPROC_ADC_CHANNEL_TOTAL_ENTERIES_MASK \ + (0xFF << IPROC_ADC_CHANNEL_TOTAL_ENTERIES) + +/* Bit definitions for IPROC_ADC_CHANNEL_INTERRUPT_MASK */ +#define IPROC_ADC_CHANNEL_WTRMRK_INTR 0x0 +#define IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK \ + (0x1 << IPROC_ADC_CHANNEL_WTRMRK_INTR) +#define IPROC_ADC_CHANNEL_FULL_INTR 0x1 +#define IPROC_ADC_CHANNEL_FULL_INTR_MASK \ + (0x1 << IPROC_ADC_IPROC_ADC_CHANNEL_FULL_INTR) +#define IPROC_ADC_CHANNEL_EMPTY_INTR 0x2 +#define IPROC_ADC_CHANNEL_EMPTY_INTR_MASK \ + (0x1 << IPROC_ADC_CHANNEL_EMPTY_INTR) + +#define IPROC_ADC_WATER_MARK_INTR_ENABLE 0x1 + +/* Number of time to retry a set of the interrupt mask reg */ +#define IPROC_ADC_INTMASK_RETRY_ATTEMPTS 10 + +#define IPROC_ADC_READ_TIMEOUT (HZ*2) + +#define iproc_adc_dbg_reg(dev, priv, reg) \ +do { \ + u32 val; \ + regmap_read(priv->regmap, reg, &val); \ + dev_dbg(dev, "%20s= 0x%08x\n", #reg, val); \ +} while (0) + +struct iproc_adc_priv { + struct regmap *regmap; + struct clk *adc_clk; + struct mutex mutex; + int irqno; + int chan_val; + int chan_id; + struct completion completion; +}; + +static void iproc_adc_reg_dump(struct iio_dev *indio_dev) +{ + struct device *dev = &indio_dev->dev; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + iproc_adc_dbg_reg(dev, adc_priv, IPROC_REGCTL1); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_REGCTL2); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_INTERRUPT_THRES); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_INTERRUPT_MASK); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_INTERRUPT_STATUS); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_CONTROLLER_STATUS); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_ANALOG_CONTROL); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_AUX_DATA); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_SOFT_BYPASS_CONTROL); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_SOFT_BYPASS_DATA); +} + +static irqreturn_t iproc_adc_interrupt_handler(int irq, void *data) +{ + u32 channel_intr_status; + u32 intr_status; + u32 intr_mask; + struct iio_dev *indio_dev = data; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + /* + * This interrupt is shared with the touchscreen driver. + * Make sure this interrupt is intended for us. + * Handle only ADC channel specific interrupts. + */ + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_STATUS, &intr_status); + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &intr_mask); + intr_status = intr_status & intr_mask; + channel_intr_status = (intr_status & IPROC_ADC_INTR_MASK) >> + IPROC_ADC_INTR; + if (channel_intr_status) + return IRQ_WAKE_THREAD; + + return IRQ_NONE; +} + +static irqreturn_t iproc_adc_interrupt_thread(int irq, void *data) +{ + irqreturn_t retval = IRQ_NONE; + struct iproc_adc_priv *adc_priv; + struct iio_dev *indio_dev = data; + unsigned int valid_entries; + u32 intr_status; + u32 intr_channels; + u32 channel_status; + u32 ch_intr_status; + + adc_priv = iio_priv(indio_dev); + + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_STATUS, &intr_status); + dev_dbg(&indio_dev->dev, "iproc_adc_interrupt_thread(),INTRPT_STS:%x\n", + intr_status); + + intr_channels = (intr_status & IPROC_ADC_INTR_MASK) >> IPROC_ADC_INTR; + if (intr_channels) { + regmap_read(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_STATUS + + IPROC_ADC_CHANNEL_OFFSET * adc_priv->chan_id, + &ch_intr_status); + + if (ch_intr_status & IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK) { + regmap_read(adc_priv->regmap, + IPROC_ADC_CHANNEL_STATUS + + IPROC_ADC_CHANNEL_OFFSET * + adc_priv->chan_id, + &channel_status); + + valid_entries = ((channel_status & + IPROC_ADC_CHANNEL_VALID_ENTERIES_MASK) >> + IPROC_ADC_CHANNEL_VALID_ENTERIES); + if (valid_entries >= 1) { + regmap_read(adc_priv->regmap, + IPROC_ADC_CHANNEL_DATA + + IPROC_ADC_CHANNEL_OFFSET * + adc_priv->chan_id, + &adc_priv->chan_val); + complete(&adc_priv->completion); + } else { + dev_err(&indio_dev->dev, + "No data rcvd on channel %d\n", + adc_priv->chan_id); + } + regmap_write(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_MASK + + IPROC_ADC_CHANNEL_OFFSET * + adc_priv->chan_id, + (ch_intr_status & + ~(IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK))); + } + regmap_write(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_STATUS + + IPROC_ADC_CHANNEL_OFFSET * adc_priv->chan_id, + ch_intr_status); + regmap_write(adc_priv->regmap, IPROC_INTERRUPT_STATUS, + intr_channels); + retval = IRQ_HANDLED; + } + + return retval; +} + +static int iproc_adc_do_read(struct iio_dev *indio_dev, + int channel, + u16 *p_adc_data) +{ + int read_len = 0; + u32 val; + u32 mask; + u32 val_check; + int failed_cnt = 0; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + mutex_lock(&adc_priv->mutex); + + /* + * After a read is complete the ADC interrupts will be disabled so + * we can assume this section of code is safe from interrupts. + */ + adc_priv->chan_val = -1; + adc_priv->chan_id = channel; + + reinit_completion(&adc_priv->completion); + /* Clear any pending interrupt */ + regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_STATUS, + IPROC_ADC_INTR_MASK | IPROC_ADC_AUXDATA_RDY_INTR, + ((0x0 << channel) << IPROC_ADC_INTR) | + IPROC_ADC_AUXDATA_RDY_INTR); + + /* Configure channel for snapshot mode and enable */ + val = (BIT(IPROC_ADC_CHANNEL_ROUNDS) | + (IPROC_ADC_CHANNEL_MODE_SNAPSHOT << IPROC_ADC_CHANNEL_MODE) | + (0x1 << IPROC_ADC_CHANNEL_ENABLE)); + + mask = IPROC_ADC_CHANNEL_ROUNDS_MASK | IPROC_ADC_CHANNEL_MODE_MASK | + IPROC_ADC_CHANNEL_ENABLE_MASK; + regmap_update_bits(adc_priv->regmap, (IPROC_ADC_CHANNEL_REGCTL1 + + IPROC_ADC_CHANNEL_OFFSET * channel), + mask, val); + + /* Set the Watermark for a channel */ + regmap_update_bits(adc_priv->regmap, (IPROC_ADC_CHANNEL_REGCTL2 + + IPROC_ADC_CHANNEL_OFFSET * channel), + IPROC_ADC_CHANNEL_WATERMARK_MASK, + 0x1); + + /* Enable water mark interrupt */ + regmap_update_bits(adc_priv->regmap, (IPROC_ADC_CHANNEL_INTERRUPT_MASK + + IPROC_ADC_CHANNEL_OFFSET * + channel), + IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK, + IPROC_ADC_WATER_MARK_INTR_ENABLE); + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val); + + /* Enable ADC interrupt for a channel */ + val |= (BIT(channel) << IPROC_ADC_INTR); + regmap_write(adc_priv->regmap, IPROC_INTERRUPT_MASK, val); + + /* + * There seems to be a very rare issue where writing to this register + * does not take effect. To work around the issue we will try multiple + * writes. In total we will spend about 10*10 = 100 us attempting this. + * Testing has shown that this may loop a few time, but we have never + * hit the full count. + */ + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check); + while (val_check != val) { + failed_cnt++; + + if (failed_cnt > IPROC_ADC_INTMASK_RETRY_ATTEMPTS) + break; + + udelay(10); + regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_MASK, + IPROC_ADC_INTR_MASK, + ((0x1 << channel) << + IPROC_ADC_INTR)); + + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check); + } + + if (failed_cnt) { + dev_dbg(&indio_dev->dev, + "IntMask failed (%d times)", failed_cnt); + if (failed_cnt > IPROC_ADC_INTMASK_RETRY_ATTEMPTS) { + dev_err(&indio_dev->dev, + "IntMask set failed. Read will likely fail."); + read_len = -EIO; + goto adc_err; + }; + } + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check); + + if (wait_for_completion_timeout(&adc_priv->completion, + IPROC_ADC_READ_TIMEOUT) > 0) { + + /* Only the lower 16 bits are relevant */ + *p_adc_data = adc_priv->chan_val & 0xFFFF; + read_len = sizeof(*p_adc_data); + + } else { + /* + * We never got the interrupt, something went wrong. + * Perhaps the interrupt may still be coming, we do not want + * that now. Lets disable the ADC interrupt, and clear the + * status to put it back in to normal state. + */ + read_len = -ETIMEDOUT; + goto adc_err; + } + mutex_unlock(&adc_priv->mutex); + + return read_len; + +adc_err: + regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_MASK, + IPROC_ADC_INTR_MASK, + ((0x0 << channel) << IPROC_ADC_INTR)); + + regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_STATUS, + IPROC_ADC_INTR_MASK, + ((0x0 << channel) << IPROC_ADC_INTR)); + + dev_err(&indio_dev->dev, "Timed out waiting for ADC data!\n"); + iproc_adc_reg_dump(indio_dev); + mutex_unlock(&adc_priv->mutex); + + return read_len; +} + +static int iproc_adc_enable(struct iio_dev *indio_dev) +{ + u32 val; + u32 channel_id; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + int ret; + + /* Set i_amux = 3b'000, select channel 0 */ + ret = regmap_update_bits(adc_priv->regmap, IPROC_ANALOG_CONTROL, + IPROC_ADC_CHANNEL_SEL_MASK, 0); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write IPROC_ANALOG_CONTROL %d\n", ret); + return ret; + } + adc_priv->chan_val = -1; + + /* + * PWR up LDO, ADC, and Band Gap (0 to enable) + * Also enable ADC controller (set high) + */ + ret = regmap_read(adc_priv->regmap, IPROC_REGCTL2, &val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to read IPROC_REGCTL2 %d\n", ret); + return ret; + } + + val &= ~(IPROC_ADC_PWR_LDO | IPROC_ADC_PWR_ADC | IPROC_ADC_PWR_BG); + + ret = regmap_write(adc_priv->regmap, IPROC_REGCTL2, val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write IPROC_REGCTL2 %d\n", ret); + return ret; + } + + ret = regmap_read(adc_priv->regmap, IPROC_REGCTL2, &val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to read IPROC_REGCTL2 %d\n", ret); + return ret; + } + + val |= IPROC_ADC_CONTROLLER_EN; + ret = regmap_write(adc_priv->regmap, IPROC_REGCTL2, val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write IPROC_REGCTL2 %d\n", ret); + return ret; + } + + for (channel_id = 0; channel_id < indio_dev->num_channels; + channel_id++) { + ret = regmap_write(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_MASK + + IPROC_ADC_CHANNEL_OFFSET * channel_id, 0); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write ADC_CHANNEL_INTERRUPT_MASK %d\n", + ret); + return ret; + } + + ret = regmap_write(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_STATUS + + IPROC_ADC_CHANNEL_OFFSET * channel_id, 0); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write ADC_CHANNEL_INTERRUPT_STATUS %d\n", + ret); + return ret; + } + } + + return 0; +} + +static void iproc_adc_disable(struct iio_dev *indio_dev) +{ + u32 val; + int ret; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + ret = regmap_read(adc_priv->regmap, IPROC_REGCTL2, &val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to read IPROC_REGCTL2 %d\n", ret); + return; + } + + val &= ~IPROC_ADC_CONTROLLER_EN; + ret = regmap_write(adc_priv->regmap, IPROC_REGCTL2, val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write IPROC_REGCTL2 %d\n", ret); + return; + } +} + +static int iproc_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + u16 adc_data; + int err; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = iproc_adc_do_read(indio_dev, chan->channel, &adc_data); + if (err < 0) + return err; + *val = adc_data; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = 1800; + *val2 = 10; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct iio_info iproc_adc_iio_info = { + .read_raw = &iproc_adc_read_raw, + .driver_module = THIS_MODULE, +}; + +#define IPROC_ADC_CHANNEL(_index, _id) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _id, \ +} + +static const struct iio_chan_spec iproc_adc_iio_channels[] = { + IPROC_ADC_CHANNEL(0, "adc0"), + IPROC_ADC_CHANNEL(1, "adc1"), + IPROC_ADC_CHANNEL(2, "adc2"), + IPROC_ADC_CHANNEL(3, "adc3"), + IPROC_ADC_CHANNEL(4, "adc4"), + IPROC_ADC_CHANNEL(5, "adc5"), + IPROC_ADC_CHANNEL(6, "adc6"), + IPROC_ADC_CHANNEL(7, "adc7"), +}; + +static int iproc_adc_probe(struct platform_device *pdev) +{ + struct iproc_adc_priv *adc_priv; + struct iio_dev *indio_dev = NULL; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, + sizeof(*adc_priv)); + if (!indio_dev) { + dev_err(&pdev->dev, "failed to allocate iio device\n"); + return -ENOMEM; + } + + adc_priv = iio_priv(indio_dev); + platform_set_drvdata(pdev, indio_dev); + + mutex_init(&adc_priv->mutex); + + init_completion(&adc_priv->completion); + + adc_priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "adc-syscon"); + if (IS_ERR(adc_priv->regmap)) { + dev_err(&pdev->dev, "failed to get handle for tsc syscon\n"); + ret = PTR_ERR(adc_priv->regmap); + return ret; + } + + adc_priv->adc_clk = devm_clk_get(&pdev->dev, "tsc_clk"); + if (IS_ERR(adc_priv->adc_clk)) { + dev_err(&pdev->dev, + "failed getting clock tsc_clk\n"); + ret = PTR_ERR(adc_priv->adc_clk); + return ret; + } + + adc_priv->irqno = platform_get_irq(pdev, 0); + if (adc_priv->irqno <= 0) { + dev_err(&pdev->dev, "platform_get_irq failed\n"); + ret = -ENODEV; + return ret; + } + + ret = regmap_update_bits(adc_priv->regmap, IPROC_REGCTL2, + IPROC_ADC_AUXIN_SCAN_ENA, 0); + if (ret) { + dev_err(&pdev->dev, "failed to write IPROC_REGCTL2 %d\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(&pdev->dev, adc_priv->irqno, + iproc_adc_interrupt_thread, + iproc_adc_interrupt_handler, + IRQF_SHARED, "iproc-adc", indio_dev); + if (ret) { + dev_err(&pdev->dev, "request_irq error %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(adc_priv->adc_clk); + if (ret) { + dev_err(&pdev->dev, + "clk_prepare_enable failed %d\n", ret); + return ret; + } + + ret = iproc_adc_enable(indio_dev); + if (ret) { + dev_err(&pdev->dev, "failed to enable adc %d\n", ret); + goto err_adc_enable; + } + + indio_dev->name = "iproc-static-adc"; + indio_dev->dev.parent = &pdev->dev; + indio_dev->dev.of_node = pdev->dev.of_node; + indio_dev->info = &iproc_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = iproc_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(iproc_adc_iio_channels); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "iio_device_register failed:err %d\n", ret); + goto err_clk; + } + + return 0; + +err_clk: + iproc_adc_disable(indio_dev); +err_adc_enable: + clk_disable_unprepare(adc_priv->adc_clk); + + return ret; +} + +static int iproc_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iproc_adc_disable(indio_dev); + clk_disable_unprepare(adc_priv->adc_clk); + + return 0; +} + +static const struct of_device_id iproc_adc_of_match[] = { + {.compatible = "brcm,iproc-static-adc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, iproc_adc_of_match); + +static struct platform_driver iproc_adc_driver = { + .probe = iproc_adc_probe, + .remove = iproc_adc_remove, + .driver = { + .name = "iproc-static-adc", + .of_match_table = of_match_ptr(iproc_adc_of_match), + }, +}; +module_platform_driver(iproc_adc_driver); + +MODULE_DESCRIPTION("Broadcom iProc ADC controller driver"); +MODULE_AUTHOR("Raveendra Padasalagi "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c index 8254f52..91636c0 100644 --- a/drivers/iio/adc/cc10001_adc.c +++ b/drivers/iio/adc/cc10001_adc.c @@ -186,7 +186,7 @@ done: if (!sample_invalid) iio_push_to_buffers_with_timestamp(indio_dev, data, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c index c73c6c6..678e8c7 100644 --- a/drivers/iio/adc/hi8435.c +++ b/drivers/iio/adc/hi8435.c @@ -400,7 +400,7 @@ static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val) iio_push_event(idev, IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, IIO_EV_TYPE_THRESH, dir), - iio_get_time_ns()); + iio_get_time_ns(idev)); } } @@ -455,6 +455,7 @@ static int hi8435_probe(struct spi_device *spi) mutex_init(&priv->lock); idev->dev.parent = &spi->dev; + idev->dev.of_node = spi->dev.of_node; idev->name = spi_get_device_id(spi)->name; idev->modes = INDIO_DIRECT_MODE; idev->info = &hi8435_info; diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 502f2fb..955f3fd 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -465,7 +465,7 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) s64 time_a, time_b; unsigned int alert; - time_a = iio_get_time_ns(); + time_a = iio_get_time_ns(indio_dev); /* * Because the timer thread and the chip conversion clock @@ -504,7 +504,7 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) data[i++] = val; } - time_b = iio_get_time_ns(); + time_b = iio_get_time_ns(indio_dev); iio_push_to_buffers_with_timestamp(indio_dev, (unsigned int *)data, time_a); @@ -554,7 +554,7 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev) dev_dbg(&indio_dev->dev, "Async readout mode: %d\n", chip->allow_async_readout); - chip->prev_ns = iio_get_time_ns(); + chip->prev_ns = iio_get_time_ns(indio_dev); chip->task = kthread_run(ina2xx_capture_thread, (void *)indio_dev, "%s:%d-%uus", indio_dev->name, indio_dev->id, @@ -691,6 +691,7 @@ static int ina2xx_probe(struct i2c_client *client, indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; indio_dev->dev.parent = &client->dev; + indio_dev->dev.of_node = client->dev.of_node; indio_dev->channels = ina2xx_channels; indio_dev->num_channels = ARRAY_SIZE(ina2xx_channels); indio_dev->name = id->name; diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 41d495c..712fbd2 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -426,6 +426,7 @@ static int max1027_probe(struct spi_device *spi) indio_dev->name = spi_get_device_id(spi)->name; indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; indio_dev->info = &max1027_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = st->info->channels; diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 998dc3c..841a13c 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -788,7 +790,7 @@ static irqreturn_t max1363_event_handler(int irq, void *private) { struct iio_dev *indio_dev = private; struct max1363_state *st = iio_priv(indio_dev); - s64 timestamp = iio_get_time_ns(); + s64 timestamp = iio_get_time_ns(indio_dev); unsigned long mask, loc; u8 rx; u8 tx[2] = { st->setupbyte, @@ -1506,7 +1508,8 @@ static irqreturn_t max1363_trigger_handler(int irq, void *p) if (b_sent < 0) goto done_free; - iio_push_to_buffers_with_timestamp(indio_dev, rxbuf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, rxbuf, + iio_get_time_ns(indio_dev)); done_free: kfree(rxbuf); @@ -1516,6 +1519,56 @@ done: return IRQ_HANDLED; } +#ifdef CONFIG_OF + +#define MAX1363_COMPATIBLE(of_compatible, cfg) { \ + .compatible = of_compatible, \ + .data = &max1363_chip_info_tbl[cfg], \ +} + +static const struct of_device_id max1363_of_match[] = { + MAX1363_COMPATIBLE("maxim,max1361", max1361), + MAX1363_COMPATIBLE("maxim,max1362", max1362), + MAX1363_COMPATIBLE("maxim,max1363", max1363), + MAX1363_COMPATIBLE("maxim,max1364", max1364), + MAX1363_COMPATIBLE("maxim,max1036", max1036), + MAX1363_COMPATIBLE("maxim,max1037", max1037), + MAX1363_COMPATIBLE("maxim,max1038", max1038), + MAX1363_COMPATIBLE("maxim,max1039", max1039), + MAX1363_COMPATIBLE("maxim,max1136", max1136), + MAX1363_COMPATIBLE("maxim,max1137", max1137), + MAX1363_COMPATIBLE("maxim,max1138", max1138), + MAX1363_COMPATIBLE("maxim,max1139", max1139), + MAX1363_COMPATIBLE("maxim,max1236", max1236), + MAX1363_COMPATIBLE("maxim,max1237", max1237), + MAX1363_COMPATIBLE("maxim,max1238", max1238), + MAX1363_COMPATIBLE("maxim,max1239", max1239), + MAX1363_COMPATIBLE("maxim,max11600", max11600), + MAX1363_COMPATIBLE("maxim,max11601", max11601), + MAX1363_COMPATIBLE("maxim,max11602", max11602), + MAX1363_COMPATIBLE("maxim,max11603", max11603), + MAX1363_COMPATIBLE("maxim,max11604", max11604), + MAX1363_COMPATIBLE("maxim,max11605", max11605), + MAX1363_COMPATIBLE("maxim,max11606", max11606), + MAX1363_COMPATIBLE("maxim,max11607", max11607), + MAX1363_COMPATIBLE("maxim,max11608", max11608), + MAX1363_COMPATIBLE("maxim,max11609", max11609), + MAX1363_COMPATIBLE("maxim,max11610", max11610), + MAX1363_COMPATIBLE("maxim,max11611", max11611), + MAX1363_COMPATIBLE("maxim,max11612", max11612), + MAX1363_COMPATIBLE("maxim,max11613", max11613), + MAX1363_COMPATIBLE("maxim,max11614", max11614), + MAX1363_COMPATIBLE("maxim,max11615", max11615), + MAX1363_COMPATIBLE("maxim,max11616", max11616), + MAX1363_COMPATIBLE("maxim,max11617", max11617), + MAX1363_COMPATIBLE("maxim,max11644", max11644), + MAX1363_COMPATIBLE("maxim,max11645", max11645), + MAX1363_COMPATIBLE("maxim,max11646", max11646), + MAX1363_COMPATIBLE("maxim,max11647", max11647), + { /* sentinel */ } +}; +#endif + static int max1363_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1523,6 +1576,7 @@ static int max1363_probe(struct i2c_client *client, struct max1363_state *st; struct iio_dev *indio_dev; struct regulator *vref; + const struct of_device_id *match; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(struct max1363_state)); @@ -1549,7 +1603,12 @@ static int max1363_probe(struct i2c_client *client, /* this is only used for device removal purposes */ i2c_set_clientdata(client, indio_dev); - st->chip_info = &max1363_chip_info_tbl[id->driver_data]; + match = of_match_device(of_match_ptr(max1363_of_match), + &client->dev); + if (match) + st->chip_info = of_device_get_match_data(&client->dev); + else + st->chip_info = &max1363_chip_info_tbl[id->driver_data]; st->client = client; st->vref_uv = st->chip_info->int_vref_mv * 1000; @@ -1587,6 +1646,7 @@ static int max1363_probe(struct i2c_client *client, /* Establish that the iio_dev is a child of the i2c device */ indio_dev->dev.parent = &client->dev; + indio_dev->dev.of_node = client->dev.of_node; indio_dev->name = id->name; indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; @@ -1692,6 +1752,7 @@ MODULE_DEVICE_TABLE(i2c, max1363_id); static struct i2c_driver max1363_driver = { .driver = { .name = "max1363", + .of_match_table = of_match_ptr(max1363_of_match), }, .probe = max1363_probe, .remove = max1363_remove, diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c index a850ca7..634717a 100644 --- a/drivers/iio/adc/mcp320x.c +++ b/drivers/iio/adc/mcp320x.c @@ -308,6 +308,7 @@ static int mcp320x_probe(struct spi_device *spi) adc->spi = spi; indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &mcp320x_info; diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c index d1172dc..254135e 100644 --- a/drivers/iio/adc/mcp3422.c +++ b/drivers/iio/adc/mcp3422.c @@ -352,6 +352,7 @@ static int mcp3422_probe(struct i2c_client *client, mutex_init(&adc->lock); indio_dev->dev.parent = &client->dev; + indio_dev->dev.of_node = client->dev.of_node; indio_dev->name = dev_name(&client->dev); indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &mcp3422_info; diff --git a/drivers/iio/adc/mxs-lradc.c b/drivers/iio/adc/mxs-lradc.c index ad26da1..b84d37c 100644 --- a/drivers/iio/adc/mxs-lradc.c +++ b/drivers/iio/adc/mxs-lradc.c @@ -373,13 +373,6 @@ static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc) return LRADC_CTRL0_MX28_PLATE_MASK; } -static u32 mxs_lradc_irq_en_mask(struct mxs_lradc *lradc) -{ - if (lradc->soc == IMX23_LRADC) - return LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK; - return LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK; -} - static u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc) { if (lradc->soc == IMX23_LRADC) @@ -1120,18 +1113,16 @@ static int mxs_lradc_ts_register(struct mxs_lradc *lradc) { struct input_dev *input; struct device *dev = lradc->dev; - int ret; if (!lradc->use_touchscreen) return 0; - input = input_allocate_device(); + input = devm_input_allocate_device(dev); if (!input) return -ENOMEM; input->name = DRIVER_NAME; input->id.bustype = BUS_HOST; - input->dev.parent = dev; input->open = mxs_lradc_ts_open; input->close = mxs_lradc_ts_close; @@ -1146,20 +1137,8 @@ static int mxs_lradc_ts_register(struct mxs_lradc *lradc) lradc->ts_input = input; input_set_drvdata(input, lradc); - ret = input_register_device(input); - if (ret) - input_free_device(lradc->ts_input); - - return ret; -} - -static void mxs_lradc_ts_unregister(struct mxs_lradc *lradc) -{ - if (!lradc->use_touchscreen) - return; - mxs_lradc_disable_ts(lradc); - input_unregister_device(lradc->ts_input); + return input_register_device(input); } /* @@ -1510,7 +1489,9 @@ static void mxs_lradc_hw_stop(struct mxs_lradc *lradc) { int i; - mxs_lradc_reg_clear(lradc, mxs_lradc_irq_en_mask(lradc), LRADC_CTRL1); + mxs_lradc_reg_clear(lradc, + lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, + LRADC_CTRL1); for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++) mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(i)); @@ -1721,13 +1702,11 @@ static int mxs_lradc_probe(struct platform_device *pdev) ret = iio_device_register(iio); if (ret) { dev_err(dev, "Failed to register IIO device\n"); - goto err_ts; + return ret; } return 0; -err_ts: - mxs_lradc_ts_unregister(lradc); err_ts_register: mxs_lradc_hw_stop(lradc); err_dev: @@ -1745,7 +1724,6 @@ static int mxs_lradc_remove(struct platform_device *pdev) struct mxs_lradc *lradc = iio_priv(iio); iio_device_unregister(iio); - mxs_lradc_ts_unregister(lradc); mxs_lradc_hw_stop(lradc); mxs_lradc_trigger_remove(iio); iio_triggered_buffer_cleanup(iio); diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c index e525aa6..db9b829 100644 --- a/drivers/iio/adc/nau7802.c +++ b/drivers/iio/adc/nau7802.c @@ -79,10 +79,29 @@ static const struct iio_chan_spec nau7802_chan_array[] = { static const u16 nau7802_sample_freq_avail[] = {10, 20, 40, 80, 10, 10, 10, 320}; +static ssize_t nau7802_show_scales(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nau7802_state *st = iio_priv(dev_to_iio_dev(dev)); + int i, len = 0; + + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%09d ", + st->scale_avail[i]); + + buf[len-1] = '\n'; + + return len; +} + static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 40 80 320"); +static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO, nau7802_show_scales, + NULL, 0); + static struct attribute *nau7802_attributes[] = { &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, NULL }; @@ -414,6 +433,7 @@ static int nau7802_probe(struct i2c_client *client, i2c_set_clientdata(client, indio_dev); indio_dev->dev.parent = &client->dev; + indio_dev->dev.of_node = client->dev.of_node; indio_dev->name = dev_name(&client->dev); indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &nau7802_info; diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index f9ad6c2..85d7012 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include @@ -53,6 +55,7 @@ struct rockchip_saradc { struct clk *clk; struct completion completion; struct regulator *vref; + struct reset_control *reset; const struct rockchip_saradc_data *data; u16 last_val; }; @@ -190,6 +193,16 @@ static const struct of_device_id rockchip_saradc_match[] = { }; MODULE_DEVICE_TABLE(of, rockchip_saradc_match); +/** + * Reset SARADC Controller. + */ +static void rockchip_saradc_reset_controller(struct reset_control *reset) +{ + reset_control_assert(reset); + usleep_range(10, 20); + reset_control_deassert(reset); +} + static int rockchip_saradc_probe(struct platform_device *pdev) { struct rockchip_saradc *info = NULL; @@ -218,6 +231,20 @@ static int rockchip_saradc_probe(struct platform_device *pdev) if (IS_ERR(info->regs)) return PTR_ERR(info->regs); + /* + * The reset should be an optional property, as it should work + * with old devicetrees as well + */ + info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb"); + if (IS_ERR(info->reset)) { + ret = PTR_ERR(info->reset); + if (ret != -ENOENT) + return ret; + + dev_dbg(&pdev->dev, "no reset control found\n"); + info->reset = NULL; + } + init_completion(&info->completion); irq = platform_get_irq(pdev, 0); @@ -252,6 +279,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev) return PTR_ERR(info->vref); } + if (info->reset) + rockchip_saradc_reset_controller(info->reset); + /* * Use a default value for the converter clock. * This may become user-configurable in the future. diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index 9fd032d..319172c 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -138,7 +139,8 @@ static irqreturn_t adc081c_trigger_handler(int irq, void *p) if (ret < 0) goto out; buf[0] = ret; - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; @@ -149,12 +151,24 @@ static int adc081c_probe(struct i2c_client *client, { struct iio_dev *iio; struct adc081c *adc; - struct adcxx1c_model *model = &adcxx1c_models[id->driver_data]; + struct adcxx1c_model *model; int err; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -EOPNOTSUPP; + if (ACPI_COMPANION(&client->dev)) { + const struct acpi_device_id *ad_id; + + ad_id = acpi_match_device(client->dev.driver->acpi_match_table, + &client->dev); + if (!ad_id) + return -ENODEV; + model = &adcxx1c_models[ad_id->driver_data]; + } else { + model = &adcxx1c_models[id->driver_data]; + } + iio = devm_iio_device_alloc(&client->dev, sizeof(*adc)); if (!iio) return -ENOMEM; @@ -172,6 +186,7 @@ static int adc081c_probe(struct i2c_client *client, return err; iio->dev.parent = &client->dev; + iio->dev.of_node = client->dev.of_node; iio->name = dev_name(&client->dev); iio->modes = INDIO_DIRECT_MODE; iio->info = &adc081c_info; @@ -231,10 +246,21 @@ static const struct of_device_id adc081c_of_match[] = { MODULE_DEVICE_TABLE(of, adc081c_of_match); #endif +#ifdef CONFIG_ACPI +static const struct acpi_device_id adc081c_acpi_match[] = { + { "ADC081C", ADC081C }, + { "ADC101C", ADC101C }, + { "ADC121C", ADC121C }, + { } +}; +MODULE_DEVICE_TABLE(acpi, adc081c_acpi_match); +#endif + static struct i2c_driver adc081c_driver = { .driver = { .name = "adc081c", .of_match_table = of_match_ptr(adc081c_of_match), + .acpi_match_table = ACPI_PTR(adc081c_acpi_match), }, .probe = adc081c_probe, .remove = adc081c_remove, diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c index 0afeac0..f4ba23e 100644 --- a/drivers/iio/adc/ti-adc0832.c +++ b/drivers/iio/adc/ti-adc0832.c @@ -194,6 +194,7 @@ static int adc0832_probe(struct spi_device *spi) indio_dev->name = spi_get_device_id(spi)->name; indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; indio_dev->info = &adc0832_info; indio_dev->modes = INDIO_DIRECT_MODE; diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index bc58867..89dfbd3 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -150,6 +150,7 @@ static int adc128_probe(struct spi_device *spi) spi_set_drvdata(spi, indio_dev); indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &adc128_info; diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 73cbf0b..066abaf 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -55,6 +55,11 @@ #define ADS1015_DEFAULT_DATA_RATE 4 #define ADS1015_DEFAULT_CHAN 0 +enum { + ADS1015, + ADS1115, +}; + enum ads1015_channels { ADS1015_AIN0_AIN1 = 0, ADS1015_AIN0_AIN3, @@ -71,6 +76,10 @@ static const unsigned int ads1015_data_rate[] = { 128, 250, 490, 920, 1600, 2400, 3300, 3300 }; +static const unsigned int ads1115_data_rate[] = { + 8, 16, 32, 64, 128, 250, 475, 860 +}; + static const struct { int scale; int uscale; @@ -101,6 +110,7 @@ static const struct { .shift = 4, \ .endianness = IIO_CPU, \ }, \ + .datasheet_name = "AIN"#_chan, \ } #define ADS1015_V_DIFF_CHAN(_chan, _chan2, _addr) { \ @@ -121,6 +131,45 @@ static const struct { .shift = 4, \ .endianness = IIO_CPU, \ }, \ + .datasheet_name = "AIN"#_chan"-AIN"#_chan2, \ +} + +#define ADS1115_V_CHAN(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ + .datasheet_name = "AIN"#_chan, \ +} + +#define ADS1115_V_DIFF_CHAN(_chan, _chan2, _addr) { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .channel2 = _chan2, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ + .datasheet_name = "AIN"#_chan"-AIN"#_chan2, \ } struct ads1015_data { @@ -131,6 +180,8 @@ struct ads1015_data { */ struct mutex lock; struct ads1015_channel_data channel_data[ADS1015_CHANNELS]; + + unsigned int *data_rate; }; static bool ads1015_is_writeable_reg(struct device *dev, unsigned int reg) @@ -157,6 +208,18 @@ static const struct iio_chan_spec ads1015_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP), }; +static const struct iio_chan_spec ads1115_channels[] = { + ADS1115_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1), + ADS1115_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3), + ADS1115_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3), + ADS1115_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3), + ADS1115_V_CHAN(0, ADS1015_AIN0), + ADS1115_V_CHAN(1, ADS1015_AIN1), + ADS1115_V_CHAN(2, ADS1015_AIN2), + ADS1115_V_CHAN(3, ADS1015_AIN3), + IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP), +}; + static int ads1015_set_power_state(struct ads1015_data *data, bool on) { int ret; @@ -196,7 +259,7 @@ int ads1015_get_adc_result(struct ads1015_data *data, int chan, int *val) return ret; if (change) { - conv_time = DIV_ROUND_UP(USEC_PER_SEC, ads1015_data_rate[dr]); + conv_time = DIV_ROUND_UP(USEC_PER_SEC, data->data_rate[dr]); usleep_range(conv_time, conv_time + 1); } @@ -225,7 +288,8 @@ static irqreturn_t ads1015_trigger_handler(int irq, void *p) buf[0] = res; mutex_unlock(&data->lock); - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); err: iio_trigger_notify_done(indio_dev->trig); @@ -263,7 +327,7 @@ static int ads1015_set_data_rate(struct ads1015_data *data, int chan, int rate) int i, ret, rindex = -1; for (i = 0; i < ARRAY_SIZE(ads1015_data_rate); i++) - if (ads1015_data_rate[i] == rate) { + if (data->data_rate[i] == rate) { rindex = i; break; } @@ -291,7 +355,9 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, mutex_lock(&indio_dev->mlock); mutex_lock(&data->lock); switch (mask) { - case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_RAW: { + int shift = chan->scan_type.shift; + if (iio_buffer_enabled(indio_dev)) { ret = -EBUSY; break; @@ -307,8 +373,7 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, break; } - /* 12 bit res, D0 is bit 4 in conversion register */ - *val = sign_extend32(*val >> 4, 11); + *val = sign_extend32(*val >> shift, 15 - shift); ret = ads1015_set_power_state(data, false); if (ret < 0) @@ -316,6 +381,7 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, ret = IIO_VAL_INT; break; + } case IIO_CHAN_INFO_SCALE: idx = data->channel_data[chan->address].pga; *val = ads1015_scale[idx].scale; @@ -324,7 +390,7 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, break; case IIO_CHAN_INFO_SAMP_FREQ: idx = data->channel_data[chan->address].data_rate; - *val = ads1015_data_rate[idx]; + *val = data->data_rate[idx]; ret = IIO_VAL_INT; break; default: @@ -380,12 +446,15 @@ static const struct iio_buffer_setup_ops ads1015_buffer_setup_ops = { }; static IIO_CONST_ATTR(scale_available, "3 2 1 0.5 0.25 0.125"); -static IIO_CONST_ATTR(sampling_frequency_available, - "128 250 490 920 1600 2400 3300"); + +static IIO_CONST_ATTR_NAMED(ads1015_sampling_frequency_available, + sampling_frequency_available, "128 250 490 920 1600 2400 3300"); +static IIO_CONST_ATTR_NAMED(ads1115_sampling_frequency_available, + sampling_frequency_available, "8 16 32 64 128 250 475 860"); static struct attribute *ads1015_attributes[] = { &iio_const_attr_scale_available.dev_attr.attr, - &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_const_attr_ads1015_sampling_frequency_available.dev_attr.attr, NULL, }; @@ -393,17 +462,35 @@ static const struct attribute_group ads1015_attribute_group = { .attrs = ads1015_attributes, }; -static const struct iio_info ads1015_info = { +static struct attribute *ads1115_attributes[] = { + &iio_const_attr_scale_available.dev_attr.attr, + &iio_const_attr_ads1115_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ads1115_attribute_group = { + .attrs = ads1115_attributes, +}; + +static struct iio_info ads1015_info = { .driver_module = THIS_MODULE, .read_raw = ads1015_read_raw, .write_raw = ads1015_write_raw, - .attrs = &ads1015_attribute_group, + .attrs = &ads1015_attribute_group, +}; + +static struct iio_info ads1115_info = { + .driver_module = THIS_MODULE, + .read_raw = ads1015_read_raw, + .write_raw = ads1015_write_raw, + .attrs = &ads1115_attribute_group, }; #ifdef CONFIG_OF static int ads1015_get_channels_config_of(struct i2c_client *client) { - struct ads1015_data *data = i2c_get_clientdata(client); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ads1015_data *data = iio_priv(indio_dev); struct device_node *node; if (!client->dev.of_node || @@ -500,12 +587,25 @@ static int ads1015_probe(struct i2c_client *client, mutex_init(&data->lock); indio_dev->dev.parent = &client->dev; - indio_dev->info = &ads1015_info; + indio_dev->dev.of_node = client->dev.of_node; indio_dev->name = ADS1015_DRV_NAME; - indio_dev->channels = ads1015_channels; - indio_dev->num_channels = ARRAY_SIZE(ads1015_channels); indio_dev->modes = INDIO_DIRECT_MODE; + switch (id->driver_data) { + case ADS1015: + indio_dev->channels = ads1015_channels; + indio_dev->num_channels = ARRAY_SIZE(ads1015_channels); + indio_dev->info = &ads1015_info; + data->data_rate = (unsigned int *) &ads1015_data_rate; + break; + case ADS1115: + indio_dev->channels = ads1115_channels; + indio_dev->num_channels = ARRAY_SIZE(ads1115_channels); + indio_dev->info = &ads1115_info; + data->data_rate = (unsigned int *) &ads1115_data_rate; + break; + } + /* we need to keep this ABI the same as used by hwmon ADS1015 driver */ ads1015_get_channels_config(client); @@ -590,7 +690,8 @@ static const struct dev_pm_ops ads1015_pm_ops = { }; static const struct i2c_device_id ads1015_id[] = { - {"ads1015", 0}, + {"ads1015", ADS1015}, + {"ads1115", ADS1115}, {} }; MODULE_DEVICE_TABLE(i2c, ads1015_id); diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c index 03e9070..c400439 100644 --- a/drivers/iio/adc/ti-ads8688.c +++ b/drivers/iio/adc/ti-ads8688.c @@ -421,6 +421,7 @@ static int ads8688_probe(struct spi_device *spi) indio_dev->name = spi_get_device_id(spi)->name; indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index c1e0553..c3cfacc 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -32,6 +32,7 @@ struct tiadc_device { struct ti_tscadc_dev *mfd_tscadc; + struct mutex fifo1_lock; /* to protect fifo access */ int channels; u8 channel_line[8]; u8 channel_step[8]; @@ -326,8 +327,7 @@ static int tiadc_channel_init(struct iio_dev *indio_dev, int channels) int i; indio_dev->num_channels = channels; - chan_array = kcalloc(channels, - sizeof(struct iio_chan_spec), GFP_KERNEL); + chan_array = kcalloc(channels, sizeof(*chan_array), GFP_KERNEL); if (chan_array == NULL) return -ENOMEM; @@ -360,6 +360,7 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct tiadc_device *adc_dev = iio_priv(indio_dev); + int ret = IIO_VAL_INT; int i, map_val; unsigned int fifo1count, read, stepid; bool found = false; @@ -373,13 +374,14 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, if (!step_en) return -EINVAL; + mutex_lock(&adc_dev->fifo1_lock); fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); while (fifo1count--) tiadc_readl(adc_dev, REG_FIFO1); am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en); - timeout = jiffies + usecs_to_jiffies + timeout = jiffies + msecs_to_jiffies (IDLE_TIMEOUT * adc_dev->channels); /* Wait for Fifo threshold interrupt */ while (1) { @@ -389,7 +391,8 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, if (time_after(jiffies, timeout)) { am335x_tsc_se_adc_done(adc_dev->mfd_tscadc); - return -EAGAIN; + ret = -EAGAIN; + goto err_unlock; } } map_val = adc_dev->channel_step[chan->scan_index]; @@ -415,8 +418,11 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, am335x_tsc_se_adc_done(adc_dev->mfd_tscadc); if (found == false) - return -EBUSY; - return IIO_VAL_INT; + ret = -EBUSY; + +err_unlock: + mutex_unlock(&adc_dev->fifo1_lock); + return ret; } static const struct iio_info tiadc_info = { @@ -467,8 +473,7 @@ static int tiadc_probe(struct platform_device *pdev) return -EINVAL; } - indio_dev = devm_iio_device_alloc(&pdev->dev, - sizeof(struct tiadc_device)); + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*indio_dev)); if (indio_dev == NULL) { dev_err(&pdev->dev, "failed to allocate iio device\n"); return -ENOMEM; @@ -485,6 +490,7 @@ static int tiadc_probe(struct platform_device *pdev) tiadc_step_config(indio_dev); tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD); + mutex_init(&adc_dev->fifo1_lock); err = tiadc_channel_init(indio_dev, adc_dev->channels); if (err < 0) @@ -531,8 +537,7 @@ static int tiadc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int tiadc_suspend(struct device *dev) +static int __maybe_unused tiadc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct tiadc_device *adc_dev = iio_priv(indio_dev); @@ -550,7 +555,7 @@ static int tiadc_suspend(struct device *dev) return 0; } -static int tiadc_resume(struct device *dev) +static int __maybe_unused tiadc_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct tiadc_device *adc_dev = iio_priv(indio_dev); @@ -567,14 +572,7 @@ static int tiadc_resume(struct device *dev) return 0; } -static const struct dev_pm_ops tiadc_pm_ops = { - .suspend = tiadc_suspend, - .resume = tiadc_resume, -}; -#define TIADC_PM_OPS (&tiadc_pm_ops) -#else -#define TIADC_PM_OPS NULL -#endif +static SIMPLE_DEV_PM_OPS(tiadc_pm_ops, tiadc_suspend, tiadc_resume); static const struct of_device_id ti_adc_dt_ids[] = { { .compatible = "ti,am3359-adc", }, @@ -585,7 +583,7 @@ MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); static struct platform_driver tiadc_driver = { .driver = { .name = "TI-am335x-adc", - .pm = TIADC_PM_OPS, + .pm = &tiadc_pm_ops, .of_match_table = ti_adc_dt_ids, }, .probe = tiadc_probe, diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 653bf13..228a003 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -594,7 +594,8 @@ static irqreturn_t vf610_adc_isr(int irq, void *dev_id) if (iio_buffer_enabled(indio_dev)) { info->buffer[0] = info->value; iio_push_to_buffers_with_timestamp(indio_dev, - info->buffer, iio_get_time_ns()); + info->buffer, + iio_get_time_ns(indio_dev)); iio_trigger_notify_done(indio_dev->trig); } else complete(&info->completion); diff --git a/drivers/iio/adc/xilinx-xadc-events.c b/drivers/iio/adc/xilinx-xadc-events.c index edcf3aa..6d5c2a6 100644 --- a/drivers/iio/adc/xilinx-xadc-events.c +++ b/drivers/iio/adc/xilinx-xadc-events.c @@ -46,7 +46,7 @@ static void xadc_handle_event(struct iio_dev *indio_dev, unsigned int event) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } else { /* * For other channels we don't know whether it is a upper or @@ -56,7 +56,7 @@ static void xadc_handle_event(struct iio_dev *indio_dev, unsigned int event) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } } diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c index 212cbed..dd99d27 100644 --- a/drivers/iio/buffer/industrialio-buffer-dma.c +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -305,7 +305,7 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer) queue->fileio.active_block = NULL; spin_lock_irq(&queue->list_lock); - for (i = 0; i < 2; i++) { + for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { block = queue->fileio.blocks[i]; /* If we can't re-use it free it */ @@ -323,7 +323,7 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer) INIT_LIST_HEAD(&queue->incoming); - for (i = 0; i < 2; i++) { + for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { if (queue->fileio.blocks[i]) { block = queue->fileio.blocks[i]; if (block->state == IIO_BLOCK_STATE_DEAD) { diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index f73290f..4bcc025 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -5,15 +5,17 @@ menu "Chemical Sensors" config ATLAS_PH_SENSOR - tristate "Atlas Scientific OEM pH-SM sensor" + tristate "Atlas Scientific OEM SM sensors" depends on I2C select REGMAP_I2C select IIO_BUFFER select IIO_TRIGGERED_BUFFER select IRQ_WORK help - Say Y here to build I2C interface support for the Atlas - Scientific OEM pH-SM sensor. + Say Y here to build I2C interface support for the following + Atlas Scientific OEM SM sensors: + * pH SM sensor + * EC SM sensor To compile this driver as module, choose M here: the module will be called atlas-ph-sensor. diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c index 62b37cd..407f141 100644 --- a/drivers/iio/chemical/atlas-ph-sensor.c +++ b/drivers/iio/chemical/atlas-ph-sensor.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -43,29 +44,50 @@ #define ATLAS_REG_PWR_CONTROL 0x06 -#define ATLAS_REG_CALIB_STATUS 0x0d -#define ATLAS_REG_CALIB_STATUS_MASK 0x07 -#define ATLAS_REG_CALIB_STATUS_LOW BIT(0) -#define ATLAS_REG_CALIB_STATUS_MID BIT(1) -#define ATLAS_REG_CALIB_STATUS_HIGH BIT(2) +#define ATLAS_REG_PH_CALIB_STATUS 0x0d +#define ATLAS_REG_PH_CALIB_STATUS_MASK 0x07 +#define ATLAS_REG_PH_CALIB_STATUS_LOW BIT(0) +#define ATLAS_REG_PH_CALIB_STATUS_MID BIT(1) +#define ATLAS_REG_PH_CALIB_STATUS_HIGH BIT(2) -#define ATLAS_REG_TEMP_DATA 0x0e +#define ATLAS_REG_EC_CALIB_STATUS 0x0f +#define ATLAS_REG_EC_CALIB_STATUS_MASK 0x0f +#define ATLAS_REG_EC_CALIB_STATUS_DRY BIT(0) +#define ATLAS_REG_EC_CALIB_STATUS_SINGLE BIT(1) +#define ATLAS_REG_EC_CALIB_STATUS_LOW BIT(2) +#define ATLAS_REG_EC_CALIB_STATUS_HIGH BIT(3) + +#define ATLAS_REG_PH_TEMP_DATA 0x0e #define ATLAS_REG_PH_DATA 0x16 +#define ATLAS_REG_EC_PROBE 0x08 +#define ATLAS_REG_EC_TEMP_DATA 0x10 +#define ATLAS_REG_EC_DATA 0x18 +#define ATLAS_REG_TDS_DATA 0x1c +#define ATLAS_REG_PSS_DATA 0x20 + #define ATLAS_PH_INT_TIME_IN_US 450000 +#define ATLAS_EC_INT_TIME_IN_US 650000 + +enum { + ATLAS_PH_SM, + ATLAS_EC_SM, +}; struct atlas_data { struct i2c_client *client; struct iio_trigger *trig; + struct atlas_device *chip; struct regmap *regmap; struct irq_work work; - __be32 buffer[4]; /* 32-bit pH data + 32-bit pad + 64-bit timestamp */ + __be32 buffer[6]; /* 96-bit data + 32-bit pad + 64-bit timestamp */ }; static const struct regmap_range atlas_volatile_ranges[] = { regmap_reg_range(ATLAS_REG_INT_CONTROL, ATLAS_REG_INT_CONTROL), regmap_reg_range(ATLAS_REG_PH_DATA, ATLAS_REG_PH_DATA + 4), + regmap_reg_range(ATLAS_REG_EC_DATA, ATLAS_REG_PSS_DATA + 4), }; static const struct regmap_access_table atlas_volatile_table = { @@ -80,13 +102,14 @@ static const struct regmap_config atlas_regmap_config = { .val_bits = 8, .volatile_table = &atlas_volatile_table, - .max_register = ATLAS_REG_PH_DATA + 4, + .max_register = ATLAS_REG_PSS_DATA + 4, .cache_type = REGCACHE_RBTREE, }; -static const struct iio_chan_spec atlas_channels[] = { +static const struct iio_chan_spec atlas_ph_channels[] = { { .type = IIO_PH, + .address = ATLAS_REG_PH_DATA, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), .scan_index = 0, @@ -100,7 +123,7 @@ static const struct iio_chan_spec atlas_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(1), { .type = IIO_TEMP, - .address = ATLAS_REG_TEMP_DATA, + .address = ATLAS_REG_PH_TEMP_DATA, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), .output = 1, @@ -108,6 +131,142 @@ static const struct iio_chan_spec atlas_channels[] = { }, }; +#define ATLAS_EC_CHANNEL(_idx, _addr) \ + {\ + .type = IIO_CONCENTRATION, \ + .indexed = 1, \ + .channel = _idx, \ + .address = _addr, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = _idx + 1, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 32, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec atlas_ec_channels[] = { + { + .type = IIO_ELECTRICALCONDUCTIVITY, + .address = ATLAS_REG_EC_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + ATLAS_EC_CHANNEL(0, ATLAS_REG_TDS_DATA), + ATLAS_EC_CHANNEL(1, ATLAS_REG_PSS_DATA), + IIO_CHAN_SOFT_TIMESTAMP(3), + { + .type = IIO_TEMP, + .address = ATLAS_REG_EC_TEMP_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .output = 1, + .scan_index = -1 + }, +}; + +static int atlas_check_ph_calibration(struct atlas_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + unsigned int val; + + ret = regmap_read(data->regmap, ATLAS_REG_PH_CALIB_STATUS, &val); + if (ret) + return ret; + + if (!(val & ATLAS_REG_PH_CALIB_STATUS_MASK)) { + dev_warn(dev, "device has not been calibrated\n"); + return 0; + } + + if (!(val & ATLAS_REG_PH_CALIB_STATUS_LOW)) + dev_warn(dev, "device missing low point calibration\n"); + + if (!(val & ATLAS_REG_PH_CALIB_STATUS_MID)) + dev_warn(dev, "device missing mid point calibration\n"); + + if (!(val & ATLAS_REG_PH_CALIB_STATUS_HIGH)) + dev_warn(dev, "device missing high point calibration\n"); + + return 0; +} + +static int atlas_check_ec_calibration(struct atlas_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + unsigned int val; + + ret = regmap_bulk_read(data->regmap, ATLAS_REG_EC_PROBE, &val, 2); + if (ret) + return ret; + + dev_info(dev, "probe set to K = %d.%.2d", be16_to_cpu(val) / 100, + be16_to_cpu(val) % 100); + + ret = regmap_read(data->regmap, ATLAS_REG_EC_CALIB_STATUS, &val); + if (ret) + return ret; + + if (!(val & ATLAS_REG_EC_CALIB_STATUS_MASK)) { + dev_warn(dev, "device has not been calibrated\n"); + return 0; + } + + if (!(val & ATLAS_REG_EC_CALIB_STATUS_DRY)) + dev_warn(dev, "device missing dry point calibration\n"); + + if (val & ATLAS_REG_EC_CALIB_STATUS_SINGLE) { + dev_warn(dev, "device using single point calibration\n"); + } else { + if (!(val & ATLAS_REG_EC_CALIB_STATUS_LOW)) + dev_warn(dev, "device missing low point calibration\n"); + + if (!(val & ATLAS_REG_EC_CALIB_STATUS_HIGH)) + dev_warn(dev, "device missing high point calibration\n"); + } + + return 0; +} + +struct atlas_device { + const struct iio_chan_spec *channels; + int num_channels; + int data_reg; + + int (*calibration)(struct atlas_data *data); + int delay; +}; + +static struct atlas_device atlas_devices[] = { + [ATLAS_PH_SM] = { + .channels = atlas_ph_channels, + .num_channels = 3, + .data_reg = ATLAS_REG_PH_DATA, + .calibration = &atlas_check_ph_calibration, + .delay = ATLAS_PH_INT_TIME_IN_US, + }, + [ATLAS_EC_SM] = { + .channels = atlas_ec_channels, + .num_channels = 5, + .data_reg = ATLAS_REG_EC_DATA, + .calibration = &atlas_check_ec_calibration, + .delay = ATLAS_EC_INT_TIME_IN_US, + }, + +}; + static int atlas_set_powermode(struct atlas_data *data, int on) { return regmap_write(data->regmap, ATLAS_REG_PWR_CONTROL, on); @@ -178,12 +337,13 @@ static irqreturn_t atlas_trigger_handler(int irq, void *private) struct atlas_data *data = iio_priv(indio_dev); int ret; - ret = regmap_bulk_read(data->regmap, ATLAS_REG_PH_DATA, - (u8 *) &data->buffer, sizeof(data->buffer[0])); + ret = regmap_bulk_read(data->regmap, data->chip->data_reg, + (u8 *) &data->buffer, + sizeof(__be32) * (data->chip->num_channels - 2)); if (!ret) iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); iio_trigger_notify_done(indio_dev->trig); @@ -200,7 +360,7 @@ static irqreturn_t atlas_interrupt_handler(int irq, void *private) return IRQ_HANDLED; } -static int atlas_read_ph_measurement(struct atlas_data *data, __be32 *val) +static int atlas_read_measurement(struct atlas_data *data, int reg, __be32 *val) { struct device *dev = &data->client->dev; int suspended = pm_runtime_suspended(dev); @@ -213,11 +373,9 @@ static int atlas_read_ph_measurement(struct atlas_data *data, __be32 *val) } if (suspended) - usleep_range(ATLAS_PH_INT_TIME_IN_US, - ATLAS_PH_INT_TIME_IN_US + 100000); + usleep_range(data->chip->delay, data->chip->delay + 100000); - ret = regmap_bulk_read(data->regmap, ATLAS_REG_PH_DATA, - (u8 *) val, sizeof(*val)); + ret = regmap_bulk_read(data->regmap, reg, (u8 *) val, sizeof(*val)); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); @@ -242,12 +400,15 @@ static int atlas_read_raw(struct iio_dev *indio_dev, (u8 *) ®, sizeof(reg)); break; case IIO_PH: + case IIO_CONCENTRATION: + case IIO_ELECTRICALCONDUCTIVITY: mutex_lock(&indio_dev->mlock); if (iio_buffer_enabled(indio_dev)) ret = -EBUSY; else - ret = atlas_read_ph_measurement(data, ®); + ret = atlas_read_measurement(data, + chan->address, ®); mutex_unlock(&indio_dev->mlock); break; @@ -271,6 +432,14 @@ static int atlas_read_raw(struct iio_dev *indio_dev, *val = 1; /* 0.001 */ *val2 = 1000; break; + case IIO_ELECTRICALCONDUCTIVITY: + *val = 1; /* 0.00001 */ + *val2 = 100000; + break; + case IIO_CONCENTRATION: + *val = 0; /* 0.000000001 */ + *val2 = 1000; + return IIO_VAL_INT_PLUS_NANO; default: return -EINVAL; } @@ -303,37 +472,26 @@ static const struct iio_info atlas_info = { .write_raw = atlas_write_raw, }; -static int atlas_check_calibration(struct atlas_data *data) -{ - struct device *dev = &data->client->dev; - int ret; - unsigned int val; - - ret = regmap_read(data->regmap, ATLAS_REG_CALIB_STATUS, &val); - if (ret) - return ret; - - if (!(val & ATLAS_REG_CALIB_STATUS_MASK)) { - dev_warn(dev, "device has not been calibrated\n"); - return 0; - } - - if (!(val & ATLAS_REG_CALIB_STATUS_LOW)) - dev_warn(dev, "device missing low point calibration\n"); - - if (!(val & ATLAS_REG_CALIB_STATUS_MID)) - dev_warn(dev, "device missing mid point calibration\n"); - - if (!(val & ATLAS_REG_CALIB_STATUS_HIGH)) - dev_warn(dev, "device missing high point calibration\n"); +static const struct i2c_device_id atlas_id[] = { + { "atlas-ph-sm", ATLAS_PH_SM}, + { "atlas-ec-sm", ATLAS_EC_SM}, + {} +}; +MODULE_DEVICE_TABLE(i2c, atlas_id); - return 0; +static const struct of_device_id atlas_dt_ids[] = { + { .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, }, + { .compatible = "atlas,ec-sm", .data = (void *)ATLAS_EC_SM, }, + { } }; +MODULE_DEVICE_TABLE(of, atlas_dt_ids); static int atlas_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct atlas_data *data; + struct atlas_device *chip; + const struct of_device_id *of_id; struct iio_trigger *trig; struct iio_dev *indio_dev; int ret; @@ -342,10 +500,16 @@ static int atlas_probe(struct i2c_client *client, if (!indio_dev) return -ENOMEM; + of_id = of_match_device(atlas_dt_ids, &client->dev); + if (!of_id) + chip = &atlas_devices[id->driver_data]; + else + chip = &atlas_devices[(unsigned long)of_id->data]; + indio_dev->info = &atlas_info; indio_dev->name = ATLAS_DRV_NAME; - indio_dev->channels = atlas_channels; - indio_dev->num_channels = ARRAY_SIZE(atlas_channels); + indio_dev->channels = chip->channels; + indio_dev->num_channels = chip->num_channels; indio_dev->modes = INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE; indio_dev->dev.parent = &client->dev; @@ -358,6 +522,7 @@ static int atlas_probe(struct i2c_client *client, data = iio_priv(indio_dev); data->client = client; data->trig = trig; + data->chip = chip; trig->dev.parent = indio_dev->dev.parent; trig->ops = &atlas_interrupt_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); @@ -379,7 +544,7 @@ static int atlas_probe(struct i2c_client *client, return -EINVAL; } - ret = atlas_check_calibration(data); + ret = chip->calibration(data); if (ret) return ret; @@ -480,18 +645,6 @@ static const struct dev_pm_ops atlas_pm_ops = { atlas_runtime_resume, NULL) }; -static const struct i2c_device_id atlas_id[] = { - { "atlas-ph-sm", 0 }, - {} -}; -MODULE_DEVICE_TABLE(i2c, atlas_id); - -static const struct of_device_id atlas_dt_ids[] = { - { .compatible = "atlas,ph-sm" }, - { } -}; -MODULE_DEVICE_TABLE(of, atlas_dt_ids); - static struct i2c_driver atlas_driver = { .driver = { .name = ATLAS_DRV_NAME, diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index e81f434..dc33c1d 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -56,8 +56,8 @@ static struct { {HID_USAGE_SENSOR_ALS, 0, 1, 0}, {HID_USAGE_SENSOR_ALS, HID_USAGE_SENSOR_UNITS_LUX, 1, 0}, - {HID_USAGE_SENSOR_PRESSURE, 0, 100000, 0}, - {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 1, 0}, + {HID_USAGE_SENSOR_PRESSURE, 0, 100, 0}, + {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 0, 1000}, }; static int pow_10(unsigned power) diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c index f1693db..d06e728 100644 --- a/drivers/iio/common/st_sensors/st_sensors_buffer.c +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c @@ -22,34 +22,32 @@ #include -int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf) +static int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf) { - int i, len; - int total = 0; + int i; struct st_sensor_data *sdata = iio_priv(indio_dev); unsigned int num_data_channels = sdata->num_data_channels; - for (i = 0; i < num_data_channels; i++) { - unsigned int bytes_to_read; - - if (test_bit(i, indio_dev->active_scan_mask)) { - bytes_to_read = indio_dev->channels[i].scan_type.storagebits >> 3; - len = sdata->tf->read_multiple_byte(&sdata->tb, - sdata->dev, indio_dev->channels[i].address, - bytes_to_read, - buf + total, sdata->multiread_bit); - - if (len < bytes_to_read) - return -EIO; - - /* Advance the buffer pointer */ - total += len; - } + for_each_set_bit(i, indio_dev->active_scan_mask, num_data_channels) { + const struct iio_chan_spec *channel = &indio_dev->channels[i]; + unsigned int bytes_to_read = channel->scan_type.realbits >> 3; + unsigned int storage_bytes = + channel->scan_type.storagebits >> 3; + + buf = PTR_ALIGN(buf, storage_bytes); + if (sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev, + channel->address, + bytes_to_read, buf, + sdata->multiread_bit) < + bytes_to_read) + return -EIO; + + /* Advance the buffer pointer */ + buf += storage_bytes; } - return total; + return 0; } -EXPORT_SYMBOL(st_sensors_get_buffer_element); irqreturn_t st_sensors_trigger_handler(int irq, void *p) { @@ -59,11 +57,16 @@ irqreturn_t st_sensors_trigger_handler(int irq, void *p) struct st_sensor_data *sdata = iio_priv(indio_dev); s64 timestamp; - /* If we do timetamping here, do it before reading the values */ + /* + * If we do timetamping here, do it before reading the values, because + * once we've read the values, new interrupts can occur (when using + * the hardware trigger) and the hw_timestamp may get updated. + * By storing it in a local variable first, we are safe. + */ if (sdata->hw_irq_trigger) timestamp = sdata->hw_timestamp; else - timestamp = iio_get_time_ns(); + timestamp = iio_get_time_ns(indio_dev); len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data); if (len < 0) diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 9e59c90..2d5282e 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -228,7 +228,7 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable) } EXPORT_SYMBOL(st_sensors_set_axis_enable); -void st_sensors_power_enable(struct iio_dev *indio_dev) +int st_sensors_power_enable(struct iio_dev *indio_dev) { struct st_sensor_data *pdata = iio_priv(indio_dev); int err; @@ -237,18 +237,37 @@ void st_sensors_power_enable(struct iio_dev *indio_dev) pdata->vdd = devm_regulator_get_optional(indio_dev->dev.parent, "vdd"); if (!IS_ERR(pdata->vdd)) { err = regulator_enable(pdata->vdd); - if (err != 0) + if (err != 0) { dev_warn(&indio_dev->dev, "Failed to enable specified Vdd supply\n"); + return err; + } + } else { + err = PTR_ERR(pdata->vdd); + if (err != -ENODEV) + return err; } pdata->vdd_io = devm_regulator_get_optional(indio_dev->dev.parent, "vddio"); if (!IS_ERR(pdata->vdd_io)) { err = regulator_enable(pdata->vdd_io); - if (err != 0) + if (err != 0) { dev_warn(&indio_dev->dev, "Failed to enable specified Vdd_IO supply\n"); + goto st_sensors_disable_vdd; + } + } else { + err = PTR_ERR(pdata->vdd_io); + if (err != -ENODEV) + goto st_sensors_disable_vdd; } + + return 0; + +st_sensors_disable_vdd: + if (!IS_ERR_OR_NULL(pdata->vdd)) + regulator_disable(pdata->vdd); + return err; } EXPORT_SYMBOL(st_sensors_power_enable); @@ -256,10 +275,10 @@ void st_sensors_power_disable(struct iio_dev *indio_dev) { struct st_sensor_data *pdata = iio_priv(indio_dev); - if (!IS_ERR(pdata->vdd)) + if (!IS_ERR_OR_NULL(pdata->vdd)) regulator_disable(pdata->vdd); - if (!IS_ERR(pdata->vdd_io)) + if (!IS_ERR_OR_NULL(pdata->vdd_io)) regulator_disable(pdata->vdd_io); } EXPORT_SYMBOL(st_sensors_power_disable); @@ -471,7 +490,7 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev, int err; u8 *outdata; struct st_sensor_data *sdata = iio_priv(indio_dev); - unsigned int byte_for_channel = ch->scan_type.storagebits >> 3; + unsigned int byte_for_channel = ch->scan_type.realbits >> 3; outdata = kmalloc(byte_for_channel, GFP_KERNEL); if (!outdata) @@ -531,7 +550,7 @@ int st_sensors_check_device_support(struct iio_dev *indio_dev, int num_sensors_list, const struct st_sensor_settings *sensor_settings) { - int i, n, err; + int i, n, err = 0; u8 wai; struct st_sensor_data *sdata = iio_priv(indio_dev); @@ -551,17 +570,21 @@ int st_sensors_check_device_support(struct iio_dev *indio_dev, return -ENODEV; } - err = sdata->tf->read_byte(&sdata->tb, sdata->dev, - sensor_settings[i].wai_addr, &wai); - if (err < 0) { - dev_err(&indio_dev->dev, "failed to read Who-Am-I register.\n"); - return err; - } + if (sensor_settings[i].wai_addr) { + err = sdata->tf->read_byte(&sdata->tb, sdata->dev, + sensor_settings[i].wai_addr, &wai); + if (err < 0) { + dev_err(&indio_dev->dev, + "failed to read Who-Am-I register.\n"); + return err; + } - if (sensor_settings[i].wai != wai) { - dev_err(&indio_dev->dev, "%s: WhoAmI mismatch (0x%x).\n", - indio_dev->name, wai); - return -EINVAL; + if (sensor_settings[i].wai != wai) { + dev_err(&indio_dev->dev, + "%s: WhoAmI mismatch (0x%x).\n", + indio_dev->name, wai); + return -EINVAL; + } } sdata->sensor_settings = diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c index 98cfee2..b43aa36 100644 --- a/drivers/iio/common/st_sensors/st_sensors_i2c.c +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c @@ -48,8 +48,8 @@ static int st_sensors_i2c_read_multiple_byte( if (multiread_bit) reg_addr |= ST_SENSORS_I2C_MULTIREAD; - return i2c_smbus_read_i2c_block_data(to_i2c_client(dev), - reg_addr, len, data); + return i2c_smbus_read_i2c_block_data_or_emulated(to_i2c_client(dev), + reg_addr, len, data); } static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb, diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 296e4ff..e66f12e 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -18,6 +18,50 @@ #include "st_sensors_core.h" /** + * st_sensors_new_samples_available() - check if more samples came in + * returns: + * 0 - no new samples available + * 1 - new samples available + * negative - error or unknown + */ +static int st_sensors_new_samples_available(struct iio_dev *indio_dev, + struct st_sensor_data *sdata) +{ + u8 status; + int ret; + + /* How would I know if I can't check it? */ + if (!sdata->sensor_settings->drdy_irq.addr_stat_drdy) + return -EINVAL; + + /* No scan mask, no interrupt */ + if (!indio_dev->active_scan_mask) + return 0; + + ret = sdata->tf->read_byte(&sdata->tb, sdata->dev, + sdata->sensor_settings->drdy_irq.addr_stat_drdy, + &status); + if (ret < 0) { + dev_err(sdata->dev, + "error checking samples available\n"); + return ret; + } + /* + * the lower bits of .active_scan_mask[0] is directly mapped + * to the channels on the sensor: either bit 0 for + * one-dimensional sensors, or e.g. x,y,z for accelerometers, + * gyroscopes or magnetometers. No sensor use more than 3 + * channels, so cut the other status bits here. + */ + status &= 0x07; + + if (status & (u8)indio_dev->active_scan_mask[0]) + return 1; + + return 0; +} + +/** * st_sensors_irq_handler() - top half of the IRQ-based triggers * @irq: irq number * @p: private handler data @@ -29,7 +73,7 @@ irqreturn_t st_sensors_irq_handler(int irq, void *p) struct st_sensor_data *sdata = iio_priv(indio_dev); /* Get the time stamp as close in time as possible */ - sdata->hw_timestamp = iio_get_time_ns(); + sdata->hw_timestamp = iio_get_time_ns(indio_dev); return IRQ_WAKE_THREAD; } @@ -43,44 +87,43 @@ irqreturn_t st_sensors_irq_thread(int irq, void *p) struct iio_trigger *trig = p; struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); struct st_sensor_data *sdata = iio_priv(indio_dev); - int ret; /* * If this trigger is backed by a hardware interrupt and we have a - * status register, check if this IRQ came from us + * status register, check if this IRQ came from us. Notice that + * we will process also if st_sensors_new_samples_available() + * returns negative: if we can't check status, then poll + * unconditionally. */ - if (sdata->sensor_settings->drdy_irq.addr_stat_drdy) { - u8 status; - - ret = sdata->tf->read_byte(&sdata->tb, sdata->dev, - sdata->sensor_settings->drdy_irq.addr_stat_drdy, - &status); - if (ret < 0) { - dev_err(sdata->dev, "could not read channel status\n"); - goto out_poll; - } - /* - * the lower bits of .active_scan_mask[0] is directly mapped - * to the channels on the sensor: either bit 0 for - * one-dimensional sensors, or e.g. x,y,z for accelerometers, - * gyroscopes or magnetometers. No sensor use more than 3 - * channels, so cut the other status bits here. - */ - status &= 0x07; + if (sdata->hw_irq_trigger && + st_sensors_new_samples_available(indio_dev, sdata)) { + iio_trigger_poll_chained(p); + } else { + dev_dbg(sdata->dev, "spurious IRQ\n"); + return IRQ_NONE; + } - /* - * If this was not caused by any channels on this sensor, - * return IRQ_NONE - */ - if (!indio_dev->active_scan_mask) - return IRQ_NONE; - if (!(status & (u8)indio_dev->active_scan_mask[0])) - return IRQ_NONE; + /* + * If we have proper level IRQs the handler will be re-entered if + * the line is still active, so return here and come back in through + * the top half if need be. + */ + if (!sdata->edge_irq) + return IRQ_HANDLED; + + /* + * If we are using egde IRQs, new samples arrived while processing + * the IRQ and those may be missed unless we pick them here, so poll + * again. If the sensor delivery frequency is very high, this thread + * turns into a polled loop handler. + */ + while (sdata->hw_irq_trigger && + st_sensors_new_samples_available(indio_dev, sdata)) { + dev_dbg(sdata->dev, "more samples came in during polling\n"); + sdata->hw_timestamp = iio_get_time_ns(indio_dev); + iio_trigger_poll_chained(p); } -out_poll: - /* It's our IRQ: proceed to handle the register polling */ - iio_trigger_poll_chained(p); return IRQ_HANDLED; } @@ -107,13 +150,18 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, * If the IRQ is triggered on falling edge, we need to mark the * interrupt as active low, if the hardware supports this. */ - if (irq_trig == IRQF_TRIGGER_FALLING) { + switch(irq_trig) { + case IRQF_TRIGGER_FALLING: + case IRQF_TRIGGER_LOW: if (!sdata->sensor_settings->drdy_irq.addr_ihl) { dev_err(&indio_dev->dev, - "falling edge specified for IRQ but hardware " - "only support rising edge, will request " - "rising edge\n"); - irq_trig = IRQF_TRIGGER_RISING; + "falling/low specified for IRQ " + "but hardware only support rising/high: " + "will request rising/high\n"); + if (irq_trig == IRQF_TRIGGER_FALLING) + irq_trig = IRQF_TRIGGER_RISING; + if (irq_trig == IRQF_TRIGGER_LOW) + irq_trig = IRQF_TRIGGER_HIGH; } else { /* Set up INT active low i.e. falling edge */ err = st_sensors_write_data_with_mask(indio_dev, @@ -122,20 +170,39 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, if (err < 0) goto iio_trigger_free; dev_info(&indio_dev->dev, - "interrupts on the falling edge\n"); + "interrupts on the falling edge or " + "active low level\n"); } - } else if (irq_trig == IRQF_TRIGGER_RISING) { + break; + case IRQF_TRIGGER_RISING: dev_info(&indio_dev->dev, "interrupts on the rising edge\n"); - - } else { + break; + case IRQF_TRIGGER_HIGH: + dev_info(&indio_dev->dev, + "interrupts active high level\n"); + break; + default: + /* This is the most preferred mode, if possible */ dev_err(&indio_dev->dev, - "unsupported IRQ trigger specified (%lx), only " - "rising and falling edges supported, enforce " + "unsupported IRQ trigger specified (%lx), enforce " "rising edge\n", irq_trig); irq_trig = IRQF_TRIGGER_RISING; } + /* Tell the interrupt handler that we're dealing with edges */ + if (irq_trig == IRQF_TRIGGER_FALLING || + irq_trig == IRQF_TRIGGER_RISING) + sdata->edge_irq = true; + else + /* + * If we're not using edges (i.e. level interrupts) we + * just mask off the IRQ, handle one interrupt, then + * if the line is still low, we return to the + * interrupt handler top half again and start over. + */ + irq_trig |= IRQF_ONESHOT; + /* * If the interrupt pin is Open Drain, by definition this * means that the interrupt line may be shared with other @@ -148,9 +215,6 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, sdata->sensor_settings->drdy_irq.addr_stat_drdy) irq_trig |= IRQF_SHARED; - /* Let's create an interrupt thread masking the hard IRQ here */ - irq_trig |= IRQF_ONESHOT; - err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev), st_sensors_irq_handler, st_sensors_irq_thread, diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index f7c71da..ca81447 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -248,11 +248,12 @@ config MCP4922 config STX104 tristate "Apex Embedded Systems STX104 DAC driver" depends on X86 && ISA_BUS_API + select GPIOLIB help - Say yes here to build support for the 2-channel DAC on the Apex - Embedded Systems STX104 integrated analog PC/104 card. The base port - addresses for the devices may be configured via the "base" module - parameter array. + Say yes here to build support for the 2-channel DAC and GPIO on the + Apex Embedded Systems STX104 integrated analog PC/104 card. The base + port addresses for the devices may be configured via the base array + module parameter. config VF610_DAC tristate "Vybrid vf610 DAC driver" diff --git a/drivers/iio/dac/ad5421.c b/drivers/iio/dac/ad5421.c index 968712b..559061a 100644 --- a/drivers/iio/dac/ad5421.c +++ b/drivers/iio/dac/ad5421.c @@ -242,7 +242,7 @@ static irqreturn_t ad5421_fault_handler(int irq, void *data) 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } if (events & AD5421_FAULT_UNDER_CURRENT) { @@ -251,7 +251,7 @@ static irqreturn_t ad5421_fault_handler(int irq, void *data) 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } if (events & AD5421_FAULT_TEMP_OVER_140) { @@ -260,7 +260,7 @@ static irqreturn_t ad5421_fault_handler(int irq, void *data) 0, IIO_EV_TYPE_MAG, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } old_fault = fault; diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 4e4c20d..788b3d6 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -223,7 +223,7 @@ static irqreturn_t ad5504_event_handler(int irq, void *private) 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns((struct iio_dev *)private)); return IRQ_HANDLED; } diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index bfb350a..0fde593 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -109,6 +110,51 @@ enum ad5755_type { ID_AD5737, }; +#ifdef CONFIG_OF +static const int ad5755_dcdc_freq_table[][2] = { + { 250000, AD5755_DC_DC_FREQ_250kHZ }, + { 410000, AD5755_DC_DC_FREQ_410kHZ }, + { 650000, AD5755_DC_DC_FREQ_650kHZ } +}; + +static const int ad5755_dcdc_maxv_table[][2] = { + { 23000000, AD5755_DC_DC_MAXV_23V }, + { 24500000, AD5755_DC_DC_MAXV_24V5 }, + { 27000000, AD5755_DC_DC_MAXV_27V }, + { 29500000, AD5755_DC_DC_MAXV_29V5 }, +}; + +static const int ad5755_slew_rate_table[][2] = { + { 64000, AD5755_SLEW_RATE_64k }, + { 32000, AD5755_SLEW_RATE_32k }, + { 16000, AD5755_SLEW_RATE_16k }, + { 8000, AD5755_SLEW_RATE_8k }, + { 4000, AD5755_SLEW_RATE_4k }, + { 2000, AD5755_SLEW_RATE_2k }, + { 1000, AD5755_SLEW_RATE_1k }, + { 500, AD5755_SLEW_RATE_500 }, + { 250, AD5755_SLEW_RATE_250 }, + { 125, AD5755_SLEW_RATE_125 }, + { 64, AD5755_SLEW_RATE_64 }, + { 32, AD5755_SLEW_RATE_32 }, + { 16, AD5755_SLEW_RATE_16 }, + { 8, AD5755_SLEW_RATE_8 }, + { 4, AD5755_SLEW_RATE_4 }, + { 0, AD5755_SLEW_RATE_0_5 }, +}; + +static const int ad5755_slew_step_table[][2] = { + { 256, AD5755_SLEW_STEP_SIZE_256 }, + { 128, AD5755_SLEW_STEP_SIZE_128 }, + { 64, AD5755_SLEW_STEP_SIZE_64 }, + { 32, AD5755_SLEW_STEP_SIZE_32 }, + { 16, AD5755_SLEW_STEP_SIZE_16 }, + { 4, AD5755_SLEW_STEP_SIZE_4 }, + { 2, AD5755_SLEW_STEP_SIZE_2 }, + { 1, AD5755_SLEW_STEP_SIZE_1 }, +}; +#endif + static int ad5755_write_unlocked(struct iio_dev *indio_dev, unsigned int reg, unsigned int val) { @@ -556,6 +602,129 @@ static const struct ad5755_platform_data ad5755_default_pdata = { }, }; +#ifdef CONFIG_OF +static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct device_node *pp; + struct ad5755_platform_data *pdata; + unsigned int tmp; + unsigned int tmparray[3]; + int devnr, i; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + pdata->ext_dc_dc_compenstation_resistor = + of_property_read_bool(np, "adi,ext-dc-dc-compenstation-resistor"); + + if (!of_property_read_u32(np, "adi,dc-dc-phase", &tmp)) + pdata->dc_dc_phase = tmp; + else + pdata->dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE; + + pdata->dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ; + if (!of_property_read_u32(np, "adi,dc-dc-freq-hz", &tmp)) { + for (i = 0; i < ARRAY_SIZE(ad5755_dcdc_freq_table); i++) { + if (tmp == ad5755_dcdc_freq_table[i][0]) { + pdata->dc_dc_freq = ad5755_dcdc_freq_table[i][1]; + break; + } + } + + if (i == ARRAY_SIZE(ad5755_dcdc_freq_table)) { + dev_err(dev, + "adi,dc-dc-freq out of range selecting 410kHz"); + } + } + + pdata->dc_dc_maxv = AD5755_DC_DC_MAXV_23V; + if (!of_property_read_u32(np, "adi,dc-dc-max-microvolt", &tmp)) { + for (i = 0; i < ARRAY_SIZE(ad5755_dcdc_maxv_table); i++) { + if (tmp == ad5755_dcdc_maxv_table[i][0]) { + pdata->dc_dc_maxv = ad5755_dcdc_maxv_table[i][1]; + break; + } + } + if (i == ARRAY_SIZE(ad5755_dcdc_maxv_table)) { + dev_err(dev, + "adi,dc-dc-maxv out of range selecting 23V"); + } + } + + devnr = 0; + for_each_child_of_node(np, pp) { + if (devnr > AD5755_NUM_CHANNELS) { + dev_err(dev, + "There is to many channels defined in DT\n"); + goto error_out; + } + + if (!of_property_read_u32(pp, "adi,mode", &tmp)) + pdata->dac[devnr].mode = tmp; + else + pdata->dac[devnr].mode = AD5755_MODE_CURRENT_4mA_20mA; + + pdata->dac[devnr].ext_current_sense_resistor = + of_property_read_bool(pp, "adi,ext-current-sense-resistor"); + + pdata->dac[devnr].enable_voltage_overrange = + of_property_read_bool(pp, "adi,enable-voltage-overrange"); + + if (!of_property_read_u32_array(pp, "adi,slew", tmparray, 3)) { + pdata->dac[devnr].slew.enable = tmparray[0]; + + pdata->dac[devnr].slew.rate = AD5755_SLEW_RATE_64k; + for (i = 0; i < ARRAY_SIZE(ad5755_slew_rate_table); i++) { + if (tmparray[1] == ad5755_slew_rate_table[i][0]) { + pdata->dac[devnr].slew.rate = + ad5755_slew_rate_table[i][1]; + break; + } + } + if (i == ARRAY_SIZE(ad5755_slew_rate_table)) { + dev_err(dev, + "channel %d slew rate out of range selecting 64kHz", + devnr); + } + + pdata->dac[devnr].slew.step_size = AD5755_SLEW_STEP_SIZE_1; + for (i = 0; i < ARRAY_SIZE(ad5755_slew_step_table); i++) { + if (tmparray[2] == ad5755_slew_step_table[i][0]) { + pdata->dac[devnr].slew.step_size = + ad5755_slew_step_table[i][1]; + break; + } + } + if (i == ARRAY_SIZE(ad5755_slew_step_table)) { + dev_err(dev, + "channel %d slew step size out of range selecting 1 LSB", + devnr); + } + } else { + pdata->dac[devnr].slew.enable = false; + pdata->dac[devnr].slew.rate = AD5755_SLEW_RATE_64k; + pdata->dac[devnr].slew.step_size = + AD5755_SLEW_STEP_SIZE_1; + } + devnr++; + } + + return pdata; + + error_out: + devm_kfree(dev, pdata); + return NULL; +} +#else +static +struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) +{ + return NULL; +} +#endif + static int ad5755_probe(struct spi_device *spi) { enum ad5755_type type = spi_get_device_id(spi)->driver_data; @@ -583,8 +752,15 @@ static int ad5755_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->num_channels = AD5755_NUM_CHANNELS; - if (!pdata) + if (spi->dev.of_node) + pdata = ad5755_parse_dt(&spi->dev); + else + pdata = spi->dev.platform_data; + + if (!pdata) { + dev_warn(&spi->dev, "no platform data? using default\n"); pdata = &ad5755_default_pdata; + } ret = ad5755_init_channels(indio_dev, pdata); if (ret) @@ -607,6 +783,16 @@ static const struct spi_device_id ad5755_id[] = { }; MODULE_DEVICE_TABLE(spi, ad5755_id); +static const struct of_device_id ad5755_of_match[] = { + { .compatible = "adi,ad5755" }, + { .compatible = "adi,ad5755-1" }, + { .compatible = "adi,ad5757" }, + { .compatible = "adi,ad5735" }, + { .compatible = "adi,ad5737" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad5755_of_match); + static struct spi_driver ad5755_driver = { .driver = { .name = "ad5755", diff --git a/drivers/iio/dac/stx104.c b/drivers/iio/dac/stx104.c index 2794122..bebbd00 100644 --- a/drivers/iio/dac/stx104.c +++ b/drivers/iio/dac/stx104.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ #include #include #include +#include #define STX104_NUM_CHAN 2 @@ -49,6 +51,30 @@ struct stx104_iio { unsigned base; }; +/** + * struct stx104_gpio - GPIO device private data structure + * @chip: instance of the gpio_chip + * @lock: synchronization lock to prevent I/O race conditions + * @base: base port address of the GPIO device + * @out_state: output bits state + */ +struct stx104_gpio { + struct gpio_chip chip; + spinlock_t lock; + unsigned int base; + unsigned int out_state; +}; + +/** + * struct stx104_dev - STX104 device private data structure + * @indio_dev: IIO device + * @chip: instance of the gpio_chip + */ +struct stx104_dev { + struct iio_dev *indio_dev; + struct gpio_chip *chip; +}; + static int stx104_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { @@ -88,15 +114,87 @@ static const struct iio_chan_spec stx104_channels[STX104_NUM_CHAN] = { STX104_CHAN(1) }; +static int stx104_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + /* GPIO 0-3 are input only, while the rest are output only */ + if (offset < 4) + return 1; + + return 0; +} + +static int stx104_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + if (offset >= 4) + return -EINVAL; + + return 0; +} + +static int stx104_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + if (offset < 4) + return -EINVAL; + + chip->set(chip, offset, value); + return 0; +} + +static int stx104_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); + + if (offset >= 4) + return -EINVAL; + + return !!(inb(stx104gpio->base) & BIT(offset)); +} + +static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); + const unsigned int mask = BIT(offset) >> 4; + unsigned long flags; + + if (offset < 4) + return; + + spin_lock_irqsave(&stx104gpio->lock, flags); + + if (value) + stx104gpio->out_state |= mask; + else + stx104gpio->out_state &= ~mask; + + outb(stx104gpio->out_state, stx104gpio->base); + + spin_unlock_irqrestore(&stx104gpio->lock, flags); +} + static int stx104_probe(struct device *dev, unsigned int id) { struct iio_dev *indio_dev; struct stx104_iio *priv; + struct stx104_gpio *stx104gpio; + struct stx104_dev *stx104dev; + int err; indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); if (!indio_dev) return -ENOMEM; + stx104gpio = devm_kzalloc(dev, sizeof(*stx104gpio), GFP_KERNEL); + if (!stx104gpio) + return -ENOMEM; + + stx104dev = devm_kzalloc(dev, sizeof(*stx104dev), GFP_KERNEL); + if (!stx104dev) + return -ENOMEM; + if (!devm_request_region(dev, base[id], STX104_EXTENT, dev_name(dev))) { dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", @@ -117,14 +215,57 @@ static int stx104_probe(struct device *dev, unsigned int id) outw(0, base[id] + 4); outw(0, base[id] + 6); - return devm_iio_device_register(dev, indio_dev); + stx104gpio->chip.label = dev_name(dev); + stx104gpio->chip.parent = dev; + stx104gpio->chip.owner = THIS_MODULE; + stx104gpio->chip.base = -1; + stx104gpio->chip.ngpio = 8; + stx104gpio->chip.get_direction = stx104_gpio_get_direction; + stx104gpio->chip.direction_input = stx104_gpio_direction_input; + stx104gpio->chip.direction_output = stx104_gpio_direction_output; + stx104gpio->chip.get = stx104_gpio_get; + stx104gpio->chip.set = stx104_gpio_set; + stx104gpio->base = base[id] + 3; + stx104gpio->out_state = 0x0; + + spin_lock_init(&stx104gpio->lock); + + stx104dev->indio_dev = indio_dev; + stx104dev->chip = &stx104gpio->chip; + dev_set_drvdata(dev, stx104dev); + + err = gpiochip_add_data(&stx104gpio->chip, stx104gpio); + if (err) { + dev_err(dev, "GPIO registering failed (%d)\n", err); + return err; + } + + err = iio_device_register(indio_dev); + if (err) { + dev_err(dev, "IIO device registering failed (%d)\n", err); + gpiochip_remove(&stx104gpio->chip); + return err; + } + + return 0; +} + +static int stx104_remove(struct device *dev, unsigned int id) +{ + struct stx104_dev *const stx104dev = dev_get_drvdata(dev); + + iio_device_unregister(stx104dev->indio_dev); + gpiochip_remove(stx104dev->chip); + + return 0; } static struct isa_driver stx104_driver = { .probe = stx104_probe, .driver = { .name = "stx104" - } + }, + .remove = stx104_remove }; module_isa_driver(stx104_driver, num_stx104); diff --git a/drivers/iio/dummy/Kconfig b/drivers/iio/dummy/Kconfig index 71805ce..aa5824d 100644 --- a/drivers/iio/dummy/Kconfig +++ b/drivers/iio/dummy/Kconfig @@ -10,6 +10,7 @@ config IIO_DUMMY_EVGEN config IIO_SIMPLE_DUMMY tristate "An example driver with no hardware requirements" + depends on IIO_SW_DEVICE help Driver intended mainly as documentation for how to write a driver. May also be useful for testing userspace code diff --git a/drivers/iio/dummy/iio_simple_dummy.c b/drivers/iio/dummy/iio_simple_dummy.c index 43fe4ba..ad3410e 100644 --- a/drivers/iio/dummy/iio_simple_dummy.c +++ b/drivers/iio/dummy/iio_simple_dummy.c @@ -17,26 +17,18 @@ #include #include #include +#include #include #include #include #include +#include #include "iio_simple_dummy.h" -/* - * A few elements needed to fake a bus for this driver - * Note instances parameter controls how many of these - * dummy devices are registered. - */ -static unsigned instances = 1; -module_param(instances, uint, 0); - -/* Pointer array used to fake bus elements */ -static struct iio_dev **iio_dummy_devs; - -/* Fake a name for the part number, usually obtained from the id table */ -static const char *iio_dummy_part_number = "iio_dummy_part_no"; +static struct config_item_type iio_dummy_type = { + .ct_owner = THIS_MODULE, +}; /** * struct iio_dummy_accel_calibscale - realworld to register mapping @@ -572,12 +564,18 @@ static int iio_dummy_init_device(struct iio_dev *indio_dev) * const struct i2c_device_id *id) * SPI: iio_dummy_probe(struct spi_device *spi) */ -static int iio_dummy_probe(int index) +static struct iio_sw_device *iio_dummy_probe(const char *name) { int ret; struct iio_dev *indio_dev; struct iio_dummy_state *st; + struct iio_sw_device *swd; + swd = kzalloc(sizeof(*swd), GFP_KERNEL); + if (!swd) { + ret = -ENOMEM; + goto error_kzalloc; + } /* * Allocate an IIO device. * @@ -608,7 +606,7 @@ static int iio_dummy_probe(int index) * i2c_set_clientdata(client, indio_dev); * spi_set_drvdata(spi, indio_dev); */ - iio_dummy_devs[index] = indio_dev; + swd->device = indio_dev; /* * Set the device name. @@ -619,7 +617,7 @@ static int iio_dummy_probe(int index) * indio_dev->name = id->name; * indio_dev->name = spi_get_device_id(spi)->name; */ - indio_dev->name = iio_dummy_part_number; + indio_dev->name = kstrdup(name, GFP_KERNEL); /* Provide description of available channels */ indio_dev->channels = iio_dummy_channels; @@ -646,7 +644,9 @@ static int iio_dummy_probe(int index) if (ret < 0) goto error_unconfigure_buffer; - return 0; + iio_swd_group_init_type_name(swd, name, &iio_dummy_type); + + return swd; error_unconfigure_buffer: iio_simple_dummy_unconfigure_buffer(indio_dev); error_unregister_events: @@ -654,16 +654,18 @@ error_unregister_events: error_free_device: iio_device_free(indio_dev); error_ret: - return ret; + kfree(swd); +error_kzalloc: + return ERR_PTR(ret); } /** * iio_dummy_remove() - device instance removal function - * @index: device index. + * @swd: pointer to software IIO device abstraction * * Parameters follow those of iio_dummy_probe for buses. */ -static void iio_dummy_remove(int index) +static int iio_dummy_remove(struct iio_sw_device *swd) { /* * Get a pointer to the device instance iio_dev structure @@ -671,7 +673,7 @@ static void iio_dummy_remove(int index) * struct iio_dev *indio_dev = i2c_get_clientdata(client); * struct iio_dev *indio_dev = spi_get_drvdata(spi); */ - struct iio_dev *indio_dev = iio_dummy_devs[index]; + struct iio_dev *indio_dev = swd->device; /* Unregister the device */ iio_device_unregister(indio_dev); @@ -684,11 +686,13 @@ static void iio_dummy_remove(int index) iio_simple_dummy_events_unregister(indio_dev); /* Free all structures */ + kfree(indio_dev->name); iio_device_free(indio_dev); -} + return 0; +} /** - * iio_dummy_init() - device driver registration + * module_iio_sw_device_driver() - device driver registration * * Varies depending on bus type of the device. As there is no device * here, call probe directly. For information on device registration @@ -697,50 +701,18 @@ static void iio_dummy_remove(int index) * spi: * Documentation/spi/spi-summary */ -static __init int iio_dummy_init(void) -{ - int i, ret; - - if (instances > 10) { - instances = 1; - return -EINVAL; - } - - /* Fake a bus */ - iio_dummy_devs = kcalloc(instances, sizeof(*iio_dummy_devs), - GFP_KERNEL); - /* Here we have no actual device so call probe */ - for (i = 0; i < instances; i++) { - ret = iio_dummy_probe(i); - if (ret < 0) - goto error_remove_devs; - } - return 0; - -error_remove_devs: - while (i--) - iio_dummy_remove(i); - - kfree(iio_dummy_devs); - return ret; -} -module_init(iio_dummy_init); +static const struct iio_sw_device_ops iio_dummy_device_ops = { + .probe = iio_dummy_probe, + .remove = iio_dummy_remove, +}; -/** - * iio_dummy_exit() - device driver removal - * - * Varies depending on bus type of the device. - * As there is no device here, call remove directly. - */ -static __exit void iio_dummy_exit(void) -{ - int i; +static struct iio_sw_device_type iio_dummy_device = { + .name = "dummy", + .owner = THIS_MODULE, + .ops = &iio_dummy_device_ops, +}; - for (i = 0; i < instances; i++) - iio_dummy_remove(i); - kfree(iio_dummy_devs); -} -module_exit(iio_dummy_exit); +module_iio_sw_device_driver(iio_dummy_device); MODULE_AUTHOR("Jonathan Cameron "); MODULE_DESCRIPTION("IIO dummy driver"); diff --git a/drivers/iio/dummy/iio_simple_dummy_buffer.c b/drivers/iio/dummy/iio_simple_dummy_buffer.c index cf44a6f..b383892 100644 --- a/drivers/iio/dummy/iio_simple_dummy_buffer.c +++ b/drivers/iio/dummy/iio_simple_dummy_buffer.c @@ -85,7 +85,8 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) } } - iio_push_to_buffers_with_timestamp(indio_dev, data, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, data, + iio_get_time_ns(indio_dev)); kfree(data); diff --git a/drivers/iio/dummy/iio_simple_dummy_events.c b/drivers/iio/dummy/iio_simple_dummy_events.c index 6eb600f..ed63ffd 100644 --- a/drivers/iio/dummy/iio_simple_dummy_events.c +++ b/drivers/iio/dummy/iio_simple_dummy_events.c @@ -158,7 +158,7 @@ static irqreturn_t iio_simple_dummy_get_timestamp(int irq, void *private) struct iio_dev *indio_dev = private; struct iio_dummy_state *st = iio_priv(indio_dev); - st->event_timestamp = iio_get_time_ns(); + st->event_timestamp = iio_get_time_ns(indio_dev); return IRQ_WAKE_THREAD; } diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index 7ccc044..f7fcfa8 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -50,6 +50,7 @@ #define BMG160_REG_PMU_BW 0x10 #define BMG160_NO_FILTER 0 #define BMG160_DEF_BW 100 +#define BMG160_REG_PMU_BW_RES BIT(7) #define BMG160_REG_INT_MAP_0 0x17 #define BMG160_INT_MAP_0_BIT_ANY BIT(1) @@ -100,7 +101,6 @@ struct bmg160_data { struct iio_trigger *motion_trig; struct mutex mutex; s16 buffer[8]; - u8 bw_bits; u32 dps_range; int ev_enable_state; int slope_thres; @@ -117,13 +117,16 @@ enum bmg160_axis { }; static const struct { - int val; + int odr; + int filter; int bw_bits; -} bmg160_samp_freq_table[] = { {100, 0x07}, - {200, 0x06}, - {400, 0x03}, - {1000, 0x02}, - {2000, 0x01} }; +} bmg160_samp_freq_table[] = { {100, 32, 0x07}, + {200, 64, 0x06}, + {100, 12, 0x05}, + {200, 23, 0x04}, + {400, 47, 0x03}, + {1000, 116, 0x02}, + {2000, 230, 0x01} }; static const struct { int scale; @@ -153,7 +156,7 @@ static int bmg160_convert_freq_to_bit(int val) int i; for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { - if (bmg160_samp_freq_table[i].val == val) + if (bmg160_samp_freq_table[i].odr == val) return bmg160_samp_freq_table[i].bw_bits; } @@ -176,7 +179,53 @@ static int bmg160_set_bw(struct bmg160_data *data, int val) return ret; } - data->bw_bits = bw_bits; + return 0; +} + +static int bmg160_get_filter(struct bmg160_data *data, int *val) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret; + int i; + unsigned int bw_bits; + + ret = regmap_read(data->regmap, BMG160_REG_PMU_BW, &bw_bits); + if (ret < 0) { + dev_err(dev, "Error reading reg_pmu_bw\n"); + return ret; + } + + /* Ignore the readonly reserved bit. */ + bw_bits &= ~BMG160_REG_PMU_BW_RES; + + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { + if (bmg160_samp_freq_table[i].bw_bits == bw_bits) + break; + } + + *val = bmg160_samp_freq_table[i].filter; + + return ret ? ret : IIO_VAL_INT; +} + + +static int bmg160_set_filter(struct bmg160_data *data, int val) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { + if (bmg160_samp_freq_table[i].filter == val) + break; + } + + ret = regmap_write(data->regmap, BMG160_REG_PMU_BW, + bmg160_samp_freq_table[i].bw_bits); + if (ret < 0) { + dev_err(dev, "Error writing reg_pmu_bw\n"); + return ret; + } return 0; } @@ -386,11 +435,23 @@ static int bmg160_setup_new_data_interrupt(struct bmg160_data *data, static int bmg160_get_bw(struct bmg160_data *data, int *val) { + struct device *dev = regmap_get_device(data->regmap); int i; + unsigned int bw_bits; + int ret; + + ret = regmap_read(data->regmap, BMG160_REG_PMU_BW, &bw_bits); + if (ret < 0) { + dev_err(dev, "Error reading reg_pmu_bw\n"); + return ret; + } + + /* Ignore the readonly reserved bit. */ + bw_bits &= ~BMG160_REG_PMU_BW_RES; for (i = 0; i < ARRAY_SIZE(bmg160_samp_freq_table); ++i) { - if (bmg160_samp_freq_table[i].bw_bits == data->bw_bits) { - *val = bmg160_samp_freq_table[i].val; + if (bmg160_samp_freq_table[i].bw_bits == bw_bits) { + *val = bmg160_samp_freq_table[i].odr; return IIO_VAL_INT; } } @@ -507,6 +568,8 @@ static int bmg160_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; } else return -EINVAL; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return bmg160_get_filter(data, val); case IIO_CHAN_INFO_SCALE: *val = 0; switch (chan->type) { @@ -571,6 +634,26 @@ static int bmg160_write_raw(struct iio_dev *indio_dev, ret = bmg160_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + if (val2) + return -EINVAL; + + mutex_lock(&data->mutex); + ret = bmg160_set_power_state(data, true); + if (ret < 0) { + bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + } + ret = bmg160_set_filter(data, val); + if (ret < 0) { + bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; + } + ret = bmg160_set_power_state(data, false); + mutex_unlock(&data->mutex); + return ret; case IIO_CHAN_INFO_SCALE: if (val) return -EINVAL; @@ -728,7 +811,8 @@ static const struct iio_event_spec bmg160_event = { .channel2 = IIO_MOD_##_axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ .scan_index = AXIS_##_axis, \ .scan_type = { \ .sign = 's', \ @@ -885,25 +969,25 @@ static irqreturn_t bmg160_event_handler(int irq, void *private) if (val & BMG160_ANY_MOTION_BIT_X) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, - 0, - IIO_MOD_X, - IIO_EV_TYPE_ROC, - dir), - iio_get_time_ns()); + 0, + IIO_MOD_X, + IIO_EV_TYPE_ROC, + dir), + iio_get_time_ns(indio_dev)); if (val & BMG160_ANY_MOTION_BIT_Y) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, - 0, - IIO_MOD_Y, - IIO_EV_TYPE_ROC, - dir), - iio_get_time_ns()); + 0, + IIO_MOD_Y, + IIO_EV_TYPE_ROC, + dir), + iio_get_time_ns(indio_dev)); if (val & BMG160_ANY_MOTION_BIT_Z) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, - 0, - IIO_MOD_Z, - IIO_EV_TYPE_ROC, - dir), - iio_get_time_ns()); + 0, + IIO_MOD_Z, + IIO_EV_TYPE_ROC, + dir), + iio_get_time_ns(indio_dev)); ack_intr_status: if (!data->dready_trigger_on) { diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index a801295..aea034d 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -426,13 +426,15 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) indio_dev->info = &gyro_info; mutex_init(&gdata->tb.buf_lock); - st_sensors_power_enable(indio_dev); + err = st_sensors_power_enable(indio_dev); + if (err) + return err; err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_gyro_sensors_settings), st_gyro_sensors_settings); if (err < 0) - return err; + goto st_gyro_power_off; gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS; gdata->multiread_bit = gdata->sensor_settings->multi_read_bit; @@ -446,11 +448,11 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) err = st_sensors_init_sensor(indio_dev, (struct st_sensors_platform_data *)&gyro_pdata); if (err < 0) - return err; + goto st_gyro_power_off; err = st_gyro_allocate_ring(indio_dev); if (err < 0) - return err; + goto st_gyro_power_off; if (irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -473,6 +475,8 @@ st_gyro_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_gyro_probe_trigger_error: st_gyro_deallocate_ring(indio_dev); +st_gyro_power_off: + st_sensors_power_disable(indio_dev); return err; } diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 88e43f8..9a08146 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -1,7 +1,7 @@ /* * AFE4403 Heart Rate Monitors and Low-Cost Pulse Oximeters * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ * Andrew F. Davis * * This program is free software; you can redistribute it and/or modify @@ -39,127 +39,90 @@ #define AFE4403_TIAGAIN 0x20 #define AFE4403_TIA_AMB_GAIN 0x21 -/* AFE4403 GAIN register fields */ -#define AFE4403_TIAGAIN_RES_MASK GENMASK(2, 0) -#define AFE4403_TIAGAIN_RES_SHIFT 0 -#define AFE4403_TIAGAIN_CAP_MASK GENMASK(7, 3) -#define AFE4403_TIAGAIN_CAP_SHIFT 3 - -/* AFE4403 LEDCNTRL register fields */ -#define AFE440X_LEDCNTRL_LED1_MASK GENMASK(15, 8) -#define AFE440X_LEDCNTRL_LED1_SHIFT 8 -#define AFE440X_LEDCNTRL_LED2_MASK GENMASK(7, 0) -#define AFE440X_LEDCNTRL_LED2_SHIFT 0 -#define AFE440X_LEDCNTRL_LED_RANGE_MASK GENMASK(17, 16) -#define AFE440X_LEDCNTRL_LED_RANGE_SHIFT 16 - -/* AFE4403 CONTROL2 register fields */ -#define AFE440X_CONTROL2_PWR_DWN_TX BIT(2) -#define AFE440X_CONTROL2_EN_SLOW_DIAG BIT(8) -#define AFE440X_CONTROL2_DIAG_OUT_TRI BIT(10) -#define AFE440X_CONTROL2_TX_BRDG_MOD BIT(11) -#define AFE440X_CONTROL2_TX_REF_MASK GENMASK(18, 17) -#define AFE440X_CONTROL2_TX_REF_SHIFT 17 - -/* AFE4404 NULL fields */ -#define NULL_MASK 0 -#define NULL_SHIFT 0 - -/* AFE4403 LEDCNTRL values */ -#define AFE440X_LEDCNTRL_RANGE_TX_HALF 0x1 -#define AFE440X_LEDCNTRL_RANGE_TX_FULL 0x2 -#define AFE440X_LEDCNTRL_RANGE_TX_OFF 0x3 - -/* AFE4403 CONTROL2 values */ -#define AFE440X_CONTROL2_TX_REF_025 0x0 -#define AFE440X_CONTROL2_TX_REF_050 0x1 -#define AFE440X_CONTROL2_TX_REF_100 0x2 -#define AFE440X_CONTROL2_TX_REF_075 0x3 - -/* AFE4403 CONTROL3 values */ -#define AFE440X_CONTROL3_CLK_DIV_2 0x0 -#define AFE440X_CONTROL3_CLK_DIV_4 0x2 -#define AFE440X_CONTROL3_CLK_DIV_6 0x3 -#define AFE440X_CONTROL3_CLK_DIV_8 0x4 -#define AFE440X_CONTROL3_CLK_DIV_12 0x5 -#define AFE440X_CONTROL3_CLK_DIV_1 0x7 - -/* AFE4403 TIAGAIN_CAP values */ -#define AFE4403_TIAGAIN_CAP_5_P 0x0 -#define AFE4403_TIAGAIN_CAP_10_P 0x1 -#define AFE4403_TIAGAIN_CAP_20_P 0x2 -#define AFE4403_TIAGAIN_CAP_30_P 0x3 -#define AFE4403_TIAGAIN_CAP_55_P 0x8 -#define AFE4403_TIAGAIN_CAP_155_P 0x10 - -/* AFE4403 TIAGAIN_RES values */ -#define AFE4403_TIAGAIN_RES_500_K 0x0 -#define AFE4403_TIAGAIN_RES_250_K 0x1 -#define AFE4403_TIAGAIN_RES_100_K 0x2 -#define AFE4403_TIAGAIN_RES_50_K 0x3 -#define AFE4403_TIAGAIN_RES_25_K 0x4 -#define AFE4403_TIAGAIN_RES_10_K 0x5 -#define AFE4403_TIAGAIN_RES_1_M 0x6 -#define AFE4403_TIAGAIN_RES_NONE 0x7 +enum afe4403_fields { + /* Gains */ + F_RF_LED1, F_CF_LED1, + F_RF_LED, F_CF_LED, + + /* LED Current */ + F_ILED1, F_ILED2, + + /* sentinel */ + F_MAX_FIELDS +}; + +static const struct reg_field afe4403_reg_fields[] = { + /* Gains */ + [F_RF_LED1] = REG_FIELD(AFE4403_TIAGAIN, 0, 2), + [F_CF_LED1] = REG_FIELD(AFE4403_TIAGAIN, 3, 7), + [F_RF_LED] = REG_FIELD(AFE4403_TIA_AMB_GAIN, 0, 2), + [F_CF_LED] = REG_FIELD(AFE4403_TIA_AMB_GAIN, 3, 7), + /* LED Current */ + [F_ILED1] = REG_FIELD(AFE440X_LEDCNTRL, 0, 7), + [F_ILED2] = REG_FIELD(AFE440X_LEDCNTRL, 8, 15), +}; /** - * struct afe4403_data - * @dev - Device structure - * @spi - SPI device handle - * @regmap - Register map of the device - * @regulator - Pointer to the regulator for the IC - * @trig - IIO trigger for this device - * @irq - ADC_RDY line interrupt number + * struct afe4403_data - AFE4403 device instance data + * @dev: Device structure + * @spi: SPI device handle + * @regmap: Register map of the device + * @fields: Register fields of the device + * @regulator: Pointer to the regulator for the IC + * @trig: IIO trigger for this device + * @irq: ADC_RDY line interrupt number */ struct afe4403_data { struct device *dev; struct spi_device *spi; struct regmap *regmap; + struct regmap_field *fields[F_MAX_FIELDS]; struct regulator *regulator; struct iio_trigger *trig; int irq; }; enum afe4403_chan_id { + LED2 = 1, + ALED2, LED1, ALED1, - LED2, - ALED2, - LED1_ALED1, LED2_ALED2, - ILED1, - ILED2, + LED1_ALED1, }; -static const struct afe440x_reg_info afe4403_reg_info[] = { - [LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, 0, NULL), - [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, 0, NULL), - [LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, 0, NULL), - [ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, 0, NULL), - [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), - [LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL), - [ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE440X_LEDCNTRL_LED1), - [ILED2] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE440X_LEDCNTRL_LED2), +static const unsigned int afe4403_channel_values[] = { + [LED2] = AFE440X_LED2VAL, + [ALED2] = AFE440X_ALED2VAL, + [LED1] = AFE440X_LED1VAL, + [ALED1] = AFE440X_ALED1VAL, + [LED2_ALED2] = AFE440X_LED2_ALED2VAL, + [LED1_ALED1] = AFE440X_LED1_ALED1VAL, +}; + +static const unsigned int afe4403_channel_leds[] = { + [LED2] = F_ILED2, + [LED1] = F_ILED1, }; static const struct iio_chan_spec afe4403_channels[] = { /* ADC values */ - AFE440X_INTENSITY_CHAN(LED1, "led1", 0), - AFE440X_INTENSITY_CHAN(ALED1, "led1_ambient", 0), - AFE440X_INTENSITY_CHAN(LED2, "led2", 0), - AFE440X_INTENSITY_CHAN(ALED2, "led2_ambient", 0), - AFE440X_INTENSITY_CHAN(LED1_ALED1, "led1-led1_ambient", 0), - AFE440X_INTENSITY_CHAN(LED2_ALED2, "led2-led2_ambient", 0), + AFE440X_INTENSITY_CHAN(LED2, 0), + AFE440X_INTENSITY_CHAN(ALED2, 0), + AFE440X_INTENSITY_CHAN(LED1, 0), + AFE440X_INTENSITY_CHAN(ALED1, 0), + AFE440X_INTENSITY_CHAN(LED2_ALED2, 0), + AFE440X_INTENSITY_CHAN(LED1_ALED1, 0), /* LED current */ - AFE440X_CURRENT_CHAN(ILED1, "led1"), - AFE440X_CURRENT_CHAN(ILED2, "led2"), + AFE440X_CURRENT_CHAN(LED2), + AFE440X_CURRENT_CHAN(LED1), }; static const struct afe440x_val_table afe4403_res_table[] = { { 500000 }, { 250000 }, { 100000 }, { 50000 }, { 25000 }, { 10000 }, { 1000000 }, { 0 }, }; -AFE440X_TABLE_ATTR(tia_resistance_available, afe4403_res_table); +AFE440X_TABLE_ATTR(in_intensity_resistance_available, afe4403_res_table); static const struct afe440x_val_table afe4403_cap_table[] = { { 0, 5000 }, { 0, 10000 }, { 0, 20000 }, { 0, 25000 }, @@ -171,7 +134,7 @@ static const struct afe440x_val_table afe4403_cap_table[] = { { 0, 205000 }, { 0, 210000 }, { 0, 220000 }, { 0, 225000 }, { 0, 230000 }, { 0, 235000 }, { 0, 245000 }, { 0, 250000 }, }; -AFE440X_TABLE_ATTR(tia_capacitance_available, afe4403_cap_table); +AFE440X_TABLE_ATTR(in_intensity_capacitance_available, afe4403_cap_table); static ssize_t afe440x_show_register(struct device *dev, struct device_attribute *attr, @@ -180,38 +143,21 @@ static ssize_t afe440x_show_register(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct afe4403_data *afe = iio_priv(indio_dev); struct afe440x_attr *afe440x_attr = to_afe440x_attr(attr); - unsigned int reg_val, type; + unsigned int reg_val; int vals[2]; - int ret, val_len; + int ret; - ret = regmap_read(afe->regmap, afe440x_attr->reg, ®_val); + ret = regmap_field_read(afe->fields[afe440x_attr->field], ®_val); if (ret) return ret; - reg_val &= afe440x_attr->mask; - reg_val >>= afe440x_attr->shift; - - switch (afe440x_attr->type) { - case SIMPLE: - type = IIO_VAL_INT; - val_len = 1; - vals[0] = reg_val; - break; - case RESISTANCE: - case CAPACITANCE: - type = IIO_VAL_INT_PLUS_MICRO; - val_len = 2; - if (reg_val < afe440x_attr->table_size) { - vals[0] = afe440x_attr->val_table[reg_val].integer; - vals[1] = afe440x_attr->val_table[reg_val].fract; - break; - } - return -EINVAL; - default: + if (reg_val >= afe440x_attr->table_size) return -EINVAL; - } - return iio_format_value(buf, type, val_len, vals); + vals[0] = afe440x_attr->val_table[reg_val].integer; + vals[1] = afe440x_attr->val_table[reg_val].fract; + + return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, vals); } static ssize_t afe440x_store_register(struct device *dev, @@ -227,48 +173,43 @@ static ssize_t afe440x_store_register(struct device *dev, if (ret) return ret; - switch (afe440x_attr->type) { - case SIMPLE: - val = integer; - break; - case RESISTANCE: - case CAPACITANCE: - for (val = 0; val < afe440x_attr->table_size; val++) - if (afe440x_attr->val_table[val].integer == integer && - afe440x_attr->val_table[val].fract == fract) - break; - if (val == afe440x_attr->table_size) - return -EINVAL; - break; - default: + for (val = 0; val < afe440x_attr->table_size; val++) + if (afe440x_attr->val_table[val].integer == integer && + afe440x_attr->val_table[val].fract == fract) + break; + if (val == afe440x_attr->table_size) return -EINVAL; - } - ret = regmap_update_bits(afe->regmap, afe440x_attr->reg, - afe440x_attr->mask, - (val << afe440x_attr->shift)); + ret = regmap_field_write(afe->fields[afe440x_attr->field], val); if (ret) return ret; return count; } -static AFE440X_ATTR(tia_separate_en, AFE4403_TIAGAIN, AFE440X_TIAGAIN_ENSEPGAIN, SIMPLE, NULL, 0); +static AFE440X_ATTR(in_intensity1_resistance, F_RF_LED, afe4403_res_table); +static AFE440X_ATTR(in_intensity1_capacitance, F_CF_LED, afe4403_cap_table); + +static AFE440X_ATTR(in_intensity2_resistance, F_RF_LED, afe4403_res_table); +static AFE440X_ATTR(in_intensity2_capacitance, F_CF_LED, afe4403_cap_table); -static AFE440X_ATTR(tia_resistance1, AFE4403_TIAGAIN, AFE4403_TIAGAIN_RES, RESISTANCE, afe4403_res_table, ARRAY_SIZE(afe4403_res_table)); -static AFE440X_ATTR(tia_capacitance1, AFE4403_TIAGAIN, AFE4403_TIAGAIN_CAP, CAPACITANCE, afe4403_cap_table, ARRAY_SIZE(afe4403_cap_table)); +static AFE440X_ATTR(in_intensity3_resistance, F_RF_LED1, afe4403_res_table); +static AFE440X_ATTR(in_intensity3_capacitance, F_CF_LED1, afe4403_cap_table); -static AFE440X_ATTR(tia_resistance2, AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES, RESISTANCE, afe4403_res_table, ARRAY_SIZE(afe4403_res_table)); -static AFE440X_ATTR(tia_capacitance2, AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES, CAPACITANCE, afe4403_cap_table, ARRAY_SIZE(afe4403_cap_table)); +static AFE440X_ATTR(in_intensity4_resistance, F_RF_LED1, afe4403_res_table); +static AFE440X_ATTR(in_intensity4_capacitance, F_CF_LED1, afe4403_cap_table); static struct attribute *afe440x_attributes[] = { - &afe440x_attr_tia_separate_en.dev_attr.attr, - &afe440x_attr_tia_resistance1.dev_attr.attr, - &afe440x_attr_tia_capacitance1.dev_attr.attr, - &afe440x_attr_tia_resistance2.dev_attr.attr, - &afe440x_attr_tia_capacitance2.dev_attr.attr, - &dev_attr_tia_resistance_available.attr, - &dev_attr_tia_capacitance_available.attr, + &dev_attr_in_intensity_resistance_available.attr, + &dev_attr_in_intensity_capacitance_available.attr, + &afe440x_attr_in_intensity1_resistance.dev_attr.attr, + &afe440x_attr_in_intensity1_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity2_resistance.dev_attr.attr, + &afe440x_attr_in_intensity2_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity3_resistance.dev_attr.attr, + &afe440x_attr_in_intensity3_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity4_resistance.dev_attr.attr, + &afe440x_attr_in_intensity4_capacitance.dev_attr.attr, NULL }; @@ -309,35 +250,26 @@ static int afe4403_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct afe4403_data *afe = iio_priv(indio_dev); - const struct afe440x_reg_info reg_info = afe4403_reg_info[chan->address]; + unsigned int reg = afe4403_channel_values[chan->address]; + unsigned int field = afe4403_channel_leds[chan->address]; int ret; switch (chan->type) { case IIO_INTENSITY: switch (mask) { case IIO_CHAN_INFO_RAW: - ret = afe4403_read(afe, reg_info.reg, val); - if (ret) - return ret; - return IIO_VAL_INT; - case IIO_CHAN_INFO_OFFSET: - ret = regmap_read(afe->regmap, reg_info.offreg, - val); + ret = afe4403_read(afe, reg, val); if (ret) return ret; - *val &= reg_info.mask; - *val >>= reg_info.shift; return IIO_VAL_INT; } break; case IIO_CURRENT: switch (mask) { case IIO_CHAN_INFO_RAW: - ret = regmap_read(afe->regmap, reg_info.reg, val); + ret = regmap_field_read(afe->fields[field], val); if (ret) return ret; - *val &= reg_info.mask; - *val >>= reg_info.shift; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 0; @@ -357,25 +289,13 @@ static int afe4403_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct afe4403_data *afe = iio_priv(indio_dev); - const struct afe440x_reg_info reg_info = afe4403_reg_info[chan->address]; + unsigned int field = afe4403_channel_leds[chan->address]; switch (chan->type) { - case IIO_INTENSITY: - switch (mask) { - case IIO_CHAN_INFO_OFFSET: - return regmap_update_bits(afe->regmap, - reg_info.offreg, - reg_info.mask, - (val << reg_info.shift)); - } - break; case IIO_CURRENT: switch (mask) { case IIO_CHAN_INFO_RAW: - return regmap_update_bits(afe->regmap, - reg_info.reg, - reg_info.mask, - (val << reg_info.shift)); + return regmap_field_write(afe->fields[field], val); } break; default: @@ -410,7 +330,7 @@ static irqreturn_t afe4403_trigger_handler(int irq, void *private) for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { ret = spi_write_then_read(afe->spi, - &afe4403_reg_info[bit].reg, 1, + &afe4403_channel_values[bit], 1, rx, 3); if (ret) goto err; @@ -472,12 +392,8 @@ static const struct iio_trigger_ops afe4403_trigger_ops = { static const struct reg_sequence afe4403_reg_sequences[] = { AFE4403_TIMING_PAIRS, - { AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN | 0x000007}, - { AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES_1_M }, - { AFE440X_LEDCNTRL, (0x14 << AFE440X_LEDCNTRL_LED1_SHIFT) | - (0x14 << AFE440X_LEDCNTRL_LED2_SHIFT) }, - { AFE440X_CONTROL2, AFE440X_CONTROL2_TX_REF_050 << - AFE440X_CONTROL2_TX_REF_SHIFT }, + { AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN }, + { AFE4403_TIAGAIN, AFE440X_TIAGAIN_ENSEPGAIN }, }; static const struct regmap_range afe4403_yes_ranges[] = { @@ -498,13 +414,11 @@ static const struct regmap_config afe4403_regmap_config = { .volatile_table = &afe4403_volatile_table, }; -#ifdef CONFIG_OF static const struct of_device_id afe4403_of_match[] = { { .compatible = "ti,afe4403", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, afe4403_of_match); -#endif static int __maybe_unused afe4403_suspend(struct device *dev) { @@ -553,7 +467,7 @@ static int afe4403_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct afe4403_data *afe; - int ret; + int i, ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*afe)); if (!indio_dev) @@ -572,6 +486,15 @@ static int afe4403_probe(struct spi_device *spi) return PTR_ERR(afe->regmap); } + for (i = 0; i < F_MAX_FIELDS; i++) { + afe->fields[i] = devm_regmap_field_alloc(afe->dev, afe->regmap, + afe4403_reg_fields[i]); + if (IS_ERR(afe->fields[i])) { + dev_err(afe->dev, "Unable to allocate regmap fields\n"); + return PTR_ERR(afe->fields[i]); + } + } + afe->regulator = devm_regulator_get(afe->dev, "tx_sup"); if (IS_ERR(afe->regulator)) { dev_err(afe->dev, "Unable to get regulator\n"); @@ -694,7 +617,7 @@ MODULE_DEVICE_TABLE(spi, afe4403_ids); static struct spi_driver afe4403_spi_driver = { .driver = { .name = AFE4403_DRIVER_NAME, - .of_match_table = of_match_ptr(afe4403_of_match), + .of_match_table = afe4403_of_match, .pm = &afe4403_pm_ops, }, .probe = afe4403_probe, @@ -704,5 +627,5 @@ static struct spi_driver afe4403_spi_driver = { module_spi_driver(afe4403_spi_driver); MODULE_AUTHOR("Andrew F. Davis "); -MODULE_DESCRIPTION("TI AFE4403 Heart Rate and Pulse Oximeter"); +MODULE_DESCRIPTION("TI AFE4403 Heart Rate Monitor and Pulse Oximeter AFE"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 5096a46..4526640 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -1,7 +1,7 @@ /* * AFE4404 Heart Rate Monitors and Low-Cost Pulse Oximeters * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ * Andrew F. Davis * * This program is free software; you can redistribute it and/or modify @@ -48,118 +48,102 @@ #define AFE4404_AVG_LED2_ALED2VAL 0x3f #define AFE4404_AVG_LED1_ALED1VAL 0x40 -/* AFE4404 GAIN register fields */ -#define AFE4404_TIA_GAIN_RES_MASK GENMASK(2, 0) -#define AFE4404_TIA_GAIN_RES_SHIFT 0 -#define AFE4404_TIA_GAIN_CAP_MASK GENMASK(5, 3) -#define AFE4404_TIA_GAIN_CAP_SHIFT 3 +/* AFE4404 CONTROL2 register fields */ +#define AFE440X_CONTROL2_OSC_ENABLE BIT(9) -/* AFE4404 LEDCNTRL register fields */ -#define AFE4404_LEDCNTRL_ILED1_MASK GENMASK(5, 0) -#define AFE4404_LEDCNTRL_ILED1_SHIFT 0 -#define AFE4404_LEDCNTRL_ILED2_MASK GENMASK(11, 6) -#define AFE4404_LEDCNTRL_ILED2_SHIFT 6 -#define AFE4404_LEDCNTRL_ILED3_MASK GENMASK(17, 12) -#define AFE4404_LEDCNTRL_ILED3_SHIFT 12 +enum afe4404_fields { + /* Gains */ + F_TIA_GAIN_SEP, F_TIA_CF_SEP, + F_TIA_GAIN, TIA_CF, -/* AFE4404 CONTROL2 register fields */ -#define AFE440X_CONTROL2_ILED_2X_MASK BIT(17) -#define AFE440X_CONTROL2_ILED_2X_SHIFT 17 - -/* AFE4404 CONTROL3 register fields */ -#define AFE440X_CONTROL3_OSC_ENABLE BIT(9) - -/* AFE4404 OFFDAC register current fields */ -#define AFE4404_OFFDAC_CURR_LED1_MASK GENMASK(9, 5) -#define AFE4404_OFFDAC_CURR_LED1_SHIFT 5 -#define AFE4404_OFFDAC_CURR_LED2_MASK GENMASK(19, 15) -#define AFE4404_OFFDAC_CURR_LED2_SHIFT 15 -#define AFE4404_OFFDAC_CURR_LED3_MASK GENMASK(4, 0) -#define AFE4404_OFFDAC_CURR_LED3_SHIFT 0 -#define AFE4404_OFFDAC_CURR_ALED1_MASK GENMASK(14, 10) -#define AFE4404_OFFDAC_CURR_ALED1_SHIFT 10 -#define AFE4404_OFFDAC_CURR_ALED2_MASK GENMASK(4, 0) -#define AFE4404_OFFDAC_CURR_ALED2_SHIFT 0 - -/* AFE4404 NULL fields */ -#define NULL_MASK 0 -#define NULL_SHIFT 0 - -/* AFE4404 TIA_GAIN_CAP values */ -#define AFE4404_TIA_GAIN_CAP_5_P 0x0 -#define AFE4404_TIA_GAIN_CAP_2_5_P 0x1 -#define AFE4404_TIA_GAIN_CAP_10_P 0x2 -#define AFE4404_TIA_GAIN_CAP_7_5_P 0x3 -#define AFE4404_TIA_GAIN_CAP_20_P 0x4 -#define AFE4404_TIA_GAIN_CAP_17_5_P 0x5 -#define AFE4404_TIA_GAIN_CAP_25_P 0x6 -#define AFE4404_TIA_GAIN_CAP_22_5_P 0x7 - -/* AFE4404 TIA_GAIN_RES values */ -#define AFE4404_TIA_GAIN_RES_500_K 0x0 -#define AFE4404_TIA_GAIN_RES_250_K 0x1 -#define AFE4404_TIA_GAIN_RES_100_K 0x2 -#define AFE4404_TIA_GAIN_RES_50_K 0x3 -#define AFE4404_TIA_GAIN_RES_25_K 0x4 -#define AFE4404_TIA_GAIN_RES_10_K 0x5 -#define AFE4404_TIA_GAIN_RES_1_M 0x6 -#define AFE4404_TIA_GAIN_RES_2_M 0x7 + /* LED Current */ + F_ILED1, F_ILED2, F_ILED3, + + /* Offset DAC */ + F_OFFDAC_AMB2, F_OFFDAC_LED1, F_OFFDAC_AMB1, F_OFFDAC_LED2, + + /* sentinel */ + F_MAX_FIELDS +}; + +static const struct reg_field afe4404_reg_fields[] = { + /* Gains */ + [F_TIA_GAIN_SEP] = REG_FIELD(AFE4404_TIA_GAIN_SEP, 0, 2), + [F_TIA_CF_SEP] = REG_FIELD(AFE4404_TIA_GAIN_SEP, 3, 5), + [F_TIA_GAIN] = REG_FIELD(AFE4404_TIA_GAIN, 0, 2), + [TIA_CF] = REG_FIELD(AFE4404_TIA_GAIN, 3, 5), + /* LED Current */ + [F_ILED1] = REG_FIELD(AFE440X_LEDCNTRL, 0, 5), + [F_ILED2] = REG_FIELD(AFE440X_LEDCNTRL, 6, 11), + [F_ILED3] = REG_FIELD(AFE440X_LEDCNTRL, 12, 17), + /* Offset DAC */ + [F_OFFDAC_AMB2] = REG_FIELD(AFE4404_OFFDAC, 0, 4), + [F_OFFDAC_LED1] = REG_FIELD(AFE4404_OFFDAC, 5, 9), + [F_OFFDAC_AMB1] = REG_FIELD(AFE4404_OFFDAC, 10, 14), + [F_OFFDAC_LED2] = REG_FIELD(AFE4404_OFFDAC, 15, 19), +}; /** - * struct afe4404_data - * @dev - Device structure - * @regmap - Register map of the device - * @regulator - Pointer to the regulator for the IC - * @trig - IIO trigger for this device - * @irq - ADC_RDY line interrupt number + * struct afe4404_data - AFE4404 device instance data + * @dev: Device structure + * @regmap: Register map of the device + * @fields: Register fields of the device + * @regulator: Pointer to the regulator for the IC + * @trig: IIO trigger for this device + * @irq: ADC_RDY line interrupt number */ struct afe4404_data { struct device *dev; struct regmap *regmap; + struct regmap_field *fields[F_MAX_FIELDS]; struct regulator *regulator; struct iio_trigger *trig; int irq; }; enum afe4404_chan_id { + LED2 = 1, + ALED2, LED1, ALED1, - LED2, - ALED2, - LED3, - LED1_ALED1, LED2_ALED2, - ILED1, - ILED2, - ILED3, + LED1_ALED1, +}; + +static const unsigned int afe4404_channel_values[] = { + [LED2] = AFE440X_LED2VAL, + [ALED2] = AFE440X_ALED2VAL, + [LED1] = AFE440X_LED1VAL, + [ALED1] = AFE440X_ALED1VAL, + [LED2_ALED2] = AFE440X_LED2_ALED2VAL, + [LED1_ALED1] = AFE440X_LED1_ALED1VAL, }; -static const struct afe440x_reg_info afe4404_reg_info[] = { - [LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED1), - [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED1), - [LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED2), - [ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED2), - [LED3] = AFE440X_REG_INFO(AFE440X_ALED2VAL, 0, NULL), - [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), - [LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL), - [ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED1), - [ILED2] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED2), - [ILED3] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED3), +static const unsigned int afe4404_channel_leds[] = { + [LED2] = F_ILED2, + [ALED2] = F_ILED3, + [LED1] = F_ILED1, +}; + +static const unsigned int afe4404_channel_offdacs[] = { + [LED2] = F_OFFDAC_LED2, + [ALED2] = F_OFFDAC_AMB2, + [LED1] = F_OFFDAC_LED1, + [ALED1] = F_OFFDAC_AMB1, }; static const struct iio_chan_spec afe4404_channels[] = { /* ADC values */ - AFE440X_INTENSITY_CHAN(LED1, "led1", BIT(IIO_CHAN_INFO_OFFSET)), - AFE440X_INTENSITY_CHAN(ALED1, "led1_ambient", BIT(IIO_CHAN_INFO_OFFSET)), - AFE440X_INTENSITY_CHAN(LED2, "led2", BIT(IIO_CHAN_INFO_OFFSET)), - AFE440X_INTENSITY_CHAN(ALED2, "led2_ambient", BIT(IIO_CHAN_INFO_OFFSET)), - AFE440X_INTENSITY_CHAN(LED3, "led3", BIT(IIO_CHAN_INFO_OFFSET)), - AFE440X_INTENSITY_CHAN(LED1_ALED1, "led1-led1_ambient", 0), - AFE440X_INTENSITY_CHAN(LED2_ALED2, "led2-led2_ambient", 0), + AFE440X_INTENSITY_CHAN(LED2, BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(ALED2, BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(LED1, BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(ALED1, BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(LED2_ALED2, 0), + AFE440X_INTENSITY_CHAN(LED1_ALED1, 0), /* LED current */ - AFE440X_CURRENT_CHAN(ILED1, "led1"), - AFE440X_CURRENT_CHAN(ILED2, "led2"), - AFE440X_CURRENT_CHAN(ILED3, "led3"), + AFE440X_CURRENT_CHAN(LED2), + AFE440X_CURRENT_CHAN(ALED2), + AFE440X_CURRENT_CHAN(LED1), }; static const struct afe440x_val_table afe4404_res_table[] = { @@ -172,7 +156,7 @@ static const struct afe440x_val_table afe4404_res_table[] = { { .integer = 1000000, .fract = 0 }, { .integer = 2000000, .fract = 0 }, }; -AFE440X_TABLE_ATTR(tia_resistance_available, afe4404_res_table); +AFE440X_TABLE_ATTR(in_intensity_resistance_available, afe4404_res_table); static const struct afe440x_val_table afe4404_cap_table[] = { { .integer = 0, .fract = 5000 }, @@ -184,7 +168,7 @@ static const struct afe440x_val_table afe4404_cap_table[] = { { .integer = 0, .fract = 25000 }, { .integer = 0, .fract = 22500 }, }; -AFE440X_TABLE_ATTR(tia_capacitance_available, afe4404_cap_table); +AFE440X_TABLE_ATTR(in_intensity_capacitance_available, afe4404_cap_table); static ssize_t afe440x_show_register(struct device *dev, struct device_attribute *attr, @@ -193,38 +177,21 @@ static ssize_t afe440x_show_register(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct afe4404_data *afe = iio_priv(indio_dev); struct afe440x_attr *afe440x_attr = to_afe440x_attr(attr); - unsigned int reg_val, type; + unsigned int reg_val; int vals[2]; - int ret, val_len; + int ret; - ret = regmap_read(afe->regmap, afe440x_attr->reg, ®_val); + ret = regmap_field_read(afe->fields[afe440x_attr->field], ®_val); if (ret) return ret; - reg_val &= afe440x_attr->mask; - reg_val >>= afe440x_attr->shift; - - switch (afe440x_attr->type) { - case SIMPLE: - type = IIO_VAL_INT; - val_len = 1; - vals[0] = reg_val; - break; - case RESISTANCE: - case CAPACITANCE: - type = IIO_VAL_INT_PLUS_MICRO; - val_len = 2; - if (reg_val < afe440x_attr->table_size) { - vals[0] = afe440x_attr->val_table[reg_val].integer; - vals[1] = afe440x_attr->val_table[reg_val].fract; - break; - } - return -EINVAL; - default: + if (reg_val >= afe440x_attr->table_size) return -EINVAL; - } - return iio_format_value(buf, type, val_len, vals); + vals[0] = afe440x_attr->val_table[reg_val].integer; + vals[1] = afe440x_attr->val_table[reg_val].fract; + + return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, vals); } static ssize_t afe440x_store_register(struct device *dev, @@ -240,48 +207,43 @@ static ssize_t afe440x_store_register(struct device *dev, if (ret) return ret; - switch (afe440x_attr->type) { - case SIMPLE: - val = integer; - break; - case RESISTANCE: - case CAPACITANCE: - for (val = 0; val < afe440x_attr->table_size; val++) - if (afe440x_attr->val_table[val].integer == integer && - afe440x_attr->val_table[val].fract == fract) - break; - if (val == afe440x_attr->table_size) - return -EINVAL; - break; - default: + for (val = 0; val < afe440x_attr->table_size; val++) + if (afe440x_attr->val_table[val].integer == integer && + afe440x_attr->val_table[val].fract == fract) + break; + if (val == afe440x_attr->table_size) return -EINVAL; - } - ret = regmap_update_bits(afe->regmap, afe440x_attr->reg, - afe440x_attr->mask, - (val << afe440x_attr->shift)); + ret = regmap_field_write(afe->fields[afe440x_attr->field], val); if (ret) return ret; return count; } -static AFE440X_ATTR(tia_separate_en, AFE4404_TIA_GAIN_SEP, AFE440X_TIAGAIN_ENSEPGAIN, SIMPLE, NULL, 0); +static AFE440X_ATTR(in_intensity1_resistance, F_TIA_GAIN_SEP, afe4404_res_table); +static AFE440X_ATTR(in_intensity1_capacitance, F_TIA_CF_SEP, afe4404_cap_table); + +static AFE440X_ATTR(in_intensity2_resistance, F_TIA_GAIN_SEP, afe4404_res_table); +static AFE440X_ATTR(in_intensity2_capacitance, F_TIA_CF_SEP, afe4404_cap_table); -static AFE440X_ATTR(tia_resistance1, AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_RES, RESISTANCE, afe4404_res_table, ARRAY_SIZE(afe4404_res_table)); -static AFE440X_ATTR(tia_capacitance1, AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_CAP, CAPACITANCE, afe4404_cap_table, ARRAY_SIZE(afe4404_cap_table)); +static AFE440X_ATTR(in_intensity3_resistance, F_TIA_GAIN, afe4404_res_table); +static AFE440X_ATTR(in_intensity3_capacitance, TIA_CF, afe4404_cap_table); -static AFE440X_ATTR(tia_resistance2, AFE4404_TIA_GAIN_SEP, AFE4404_TIA_GAIN_RES, RESISTANCE, afe4404_res_table, ARRAY_SIZE(afe4404_res_table)); -static AFE440X_ATTR(tia_capacitance2, AFE4404_TIA_GAIN_SEP, AFE4404_TIA_GAIN_CAP, CAPACITANCE, afe4404_cap_table, ARRAY_SIZE(afe4404_cap_table)); +static AFE440X_ATTR(in_intensity4_resistance, F_TIA_GAIN, afe4404_res_table); +static AFE440X_ATTR(in_intensity4_capacitance, TIA_CF, afe4404_cap_table); static struct attribute *afe440x_attributes[] = { - &afe440x_attr_tia_separate_en.dev_attr.attr, - &afe440x_attr_tia_resistance1.dev_attr.attr, - &afe440x_attr_tia_capacitance1.dev_attr.attr, - &afe440x_attr_tia_resistance2.dev_attr.attr, - &afe440x_attr_tia_capacitance2.dev_attr.attr, - &dev_attr_tia_resistance_available.attr, - &dev_attr_tia_capacitance_available.attr, + &dev_attr_in_intensity_resistance_available.attr, + &dev_attr_in_intensity_capacitance_available.attr, + &afe440x_attr_in_intensity1_resistance.dev_attr.attr, + &afe440x_attr_in_intensity1_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity2_resistance.dev_attr.attr, + &afe440x_attr_in_intensity2_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity3_resistance.dev_attr.attr, + &afe440x_attr_in_intensity3_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity4_resistance.dev_attr.attr, + &afe440x_attr_in_intensity4_capacitance.dev_attr.attr, NULL }; @@ -294,35 +256,32 @@ static int afe4404_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct afe4404_data *afe = iio_priv(indio_dev); - const struct afe440x_reg_info reg_info = afe4404_reg_info[chan->address]; + unsigned int value_reg = afe4404_channel_values[chan->address]; + unsigned int led_field = afe4404_channel_leds[chan->address]; + unsigned int offdac_field = afe4404_channel_offdacs[chan->address]; int ret; switch (chan->type) { case IIO_INTENSITY: switch (mask) { case IIO_CHAN_INFO_RAW: - ret = regmap_read(afe->regmap, reg_info.reg, val); + ret = regmap_read(afe->regmap, value_reg, val); if (ret) return ret; return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: - ret = regmap_read(afe->regmap, reg_info.offreg, - val); + ret = regmap_field_read(afe->fields[offdac_field], val); if (ret) return ret; - *val &= reg_info.mask; - *val >>= reg_info.shift; return IIO_VAL_INT; } break; case IIO_CURRENT: switch (mask) { case IIO_CHAN_INFO_RAW: - ret = regmap_read(afe->regmap, reg_info.reg, val); + ret = regmap_field_read(afe->fields[led_field], val); if (ret) return ret; - *val &= reg_info.mask; - *val >>= reg_info.shift; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 0; @@ -342,25 +301,20 @@ static int afe4404_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct afe4404_data *afe = iio_priv(indio_dev); - const struct afe440x_reg_info reg_info = afe4404_reg_info[chan->address]; + unsigned int led_field = afe4404_channel_leds[chan->address]; + unsigned int offdac_field = afe4404_channel_offdacs[chan->address]; switch (chan->type) { case IIO_INTENSITY: switch (mask) { case IIO_CHAN_INFO_OFFSET: - return regmap_update_bits(afe->regmap, - reg_info.offreg, - reg_info.mask, - (val << reg_info.shift)); + return regmap_field_write(afe->fields[offdac_field], val); } break; case IIO_CURRENT: switch (mask) { case IIO_CHAN_INFO_RAW: - return regmap_update_bits(afe->regmap, - reg_info.reg, - reg_info.mask, - (val << reg_info.shift)); + return regmap_field_write(afe->fields[led_field], val); } break; default: @@ -387,7 +341,7 @@ static irqreturn_t afe4404_trigger_handler(int irq, void *private) for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { - ret = regmap_read(afe->regmap, afe4404_reg_info[bit].reg, + ret = regmap_read(afe->regmap, afe4404_channel_values[bit], &buffer[i++]); if (ret) goto err; @@ -443,11 +397,8 @@ static const struct iio_trigger_ops afe4404_trigger_ops = { static const struct reg_sequence afe4404_reg_sequences[] = { AFE4404_TIMING_PAIRS, { AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN }, - { AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_RES_50_K }, - { AFE440X_LEDCNTRL, (0xf << AFE4404_LEDCNTRL_ILED1_SHIFT) | - (0x3 << AFE4404_LEDCNTRL_ILED2_SHIFT) | - (0x3 << AFE4404_LEDCNTRL_ILED3_SHIFT) }, - { AFE440X_CONTROL2, AFE440X_CONTROL3_OSC_ENABLE }, + { AFE4404_TIA_GAIN_SEP, AFE440X_TIAGAIN_ENSEPGAIN }, + { AFE440X_CONTROL2, AFE440X_CONTROL2_OSC_ENABLE }, }; static const struct regmap_range afe4404_yes_ranges[] = { @@ -469,13 +420,11 @@ static const struct regmap_config afe4404_regmap_config = { .volatile_table = &afe4404_volatile_table, }; -#ifdef CONFIG_OF static const struct of_device_id afe4404_of_match[] = { { .compatible = "ti,afe4404", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, afe4404_of_match); -#endif static int __maybe_unused afe4404_suspend(struct device *dev) { @@ -525,7 +474,7 @@ static int afe4404_probe(struct i2c_client *client, { struct iio_dev *indio_dev; struct afe4404_data *afe; - int ret; + int i, ret; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*afe)); if (!indio_dev) @@ -543,6 +492,15 @@ static int afe4404_probe(struct i2c_client *client, return PTR_ERR(afe->regmap); } + for (i = 0; i < F_MAX_FIELDS; i++) { + afe->fields[i] = devm_regmap_field_alloc(afe->dev, afe->regmap, + afe4404_reg_fields[i]); + if (IS_ERR(afe->fields[i])) { + dev_err(afe->dev, "Unable to allocate regmap fields\n"); + return PTR_ERR(afe->fields[i]); + } + } + afe->regulator = devm_regulator_get(afe->dev, "tx_sup"); if (IS_ERR(afe->regulator)) { dev_err(afe->dev, "Unable to get regulator\n"); @@ -665,7 +623,7 @@ MODULE_DEVICE_TABLE(i2c, afe4404_ids); static struct i2c_driver afe4404_i2c_driver = { .driver = { .name = AFE4404_DRIVER_NAME, - .of_match_table = of_match_ptr(afe4404_of_match), + .of_match_table = afe4404_of_match, .pm = &afe4404_pm_ops, }, .probe = afe4404_probe, @@ -675,5 +633,5 @@ static struct i2c_driver afe4404_i2c_driver = { module_i2c_driver(afe4404_i2c_driver); MODULE_AUTHOR("Andrew F. Davis "); -MODULE_DESCRIPTION("TI AFE4404 Heart Rate and Pulse Oximeter"); +MODULE_DESCRIPTION("TI AFE4404 Heart Rate Monitor and Pulse Oximeter AFE"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/health/afe440x.h b/drivers/iio/health/afe440x.h index c671ab7..1a0f247 100644 --- a/drivers/iio/health/afe440x.h +++ b/drivers/iio/health/afe440x.h @@ -71,8 +71,7 @@ #define AFE440X_CONTROL1_TIMEREN BIT(8) /* TIAGAIN register fields */ -#define AFE440X_TIAGAIN_ENSEPGAIN_MASK BIT(15) -#define AFE440X_TIAGAIN_ENSEPGAIN_SHIFT 15 +#define AFE440X_TIAGAIN_ENSEPGAIN BIT(15) /* CONTROL2 register fields */ #define AFE440X_CONTROL2_PDN_AFE BIT(0) @@ -89,22 +88,7 @@ #define AFE440X_CONTROL0_WRITE 0x0 #define AFE440X_CONTROL0_READ 0x1 -struct afe440x_reg_info { - unsigned int reg; - unsigned int offreg; - unsigned int shift; - unsigned int mask; -}; - -#define AFE440X_REG_INFO(_reg, _offreg, _sm) \ - { \ - .reg = _reg, \ - .offreg = _offreg, \ - .shift = _sm ## _SHIFT, \ - .mask = _sm ## _MASK, \ - } - -#define AFE440X_INTENSITY_CHAN(_index, _name, _mask) \ +#define AFE440X_INTENSITY_CHAN(_index, _mask) \ { \ .type = IIO_INTENSITY, \ .channel = _index, \ @@ -116,29 +100,23 @@ struct afe440x_reg_info { .storagebits = 32, \ .endianness = IIO_CPU, \ }, \ - .extend_name = _name, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ _mask, \ + .indexed = true, \ } -#define AFE440X_CURRENT_CHAN(_index, _name) \ +#define AFE440X_CURRENT_CHAN(_index) \ { \ .type = IIO_CURRENT, \ .channel = _index, \ .address = _index, \ - .scan_index = _index, \ - .extend_name = _name, \ + .scan_index = -1, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = true, \ .output = true, \ } -enum afe440x_reg_type { - SIMPLE, - RESISTANCE, - CAPACITANCE, -}; - struct afe440x_val_table { int integer; int fract; @@ -164,10 +142,7 @@ static DEVICE_ATTR_RO(_name) struct afe440x_attr { struct device_attribute dev_attr; - unsigned int reg; - unsigned int shift; - unsigned int mask; - enum afe440x_reg_type type; + unsigned int field; const struct afe440x_val_table *val_table; unsigned int table_size; }; @@ -175,17 +150,14 @@ struct afe440x_attr { #define to_afe440x_attr(_dev_attr) \ container_of(_dev_attr, struct afe440x_attr, dev_attr) -#define AFE440X_ATTR(_name, _reg, _field, _type, _table, _size) \ +#define AFE440X_ATTR(_name, _field, _table) \ struct afe440x_attr afe440x_attr_##_name = { \ .dev_attr = __ATTR(_name, (S_IRUGO | S_IWUSR), \ afe440x_show_register, \ afe440x_store_register), \ - .reg = _reg, \ - .shift = _field ## _SHIFT, \ - .mask = _field ## _MASK, \ - .type = _type, \ + .field = _field, \ .val_table = _table, \ - .table_size = _size, \ + .table_size = ARRAY_SIZE(_table), \ } #endif /* _AFE440X_H */ diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig index 738a86d..d041243 100644 --- a/drivers/iio/humidity/Kconfig +++ b/drivers/iio/humidity/Kconfig @@ -6,6 +6,8 @@ menu "Humidity sensors" config AM2315 tristate "Aosong AM2315 relative humidity and temperature sensor" depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help If you say yes here you get support for the Aosong AM2315 relative humidity and ambient temperature sensor. diff --git a/drivers/iio/humidity/am2315.c b/drivers/iio/humidity/am2315.c index 1153591..ff96b6d 100644 --- a/drivers/iio/humidity/am2315.c +++ b/drivers/iio/humidity/am2315.c @@ -244,7 +244,7 @@ static int am2315_probe(struct i2c_client *client, indio_dev->channels = am2315_channels; indio_dev->num_channels = ARRAY_SIZE(am2315_channels); - ret = iio_triggered_buffer_setup(indio_dev, NULL, + ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time, am2315_trigger_handler, NULL); if (ret < 0) { dev_err(&client->dev, "iio triggered buffer setup failed\n"); @@ -276,6 +276,7 @@ static const struct i2c_device_id am2315_i2c_id[] = { {"am2315", 0}, {} }; +MODULE_DEVICE_TABLE(i2c, am2315_i2c_id); static const struct acpi_device_id am2315_acpi_id[] = { {"AOS2315", 0}, diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c index a03832a..e0c9c70 100644 --- a/drivers/iio/humidity/hdc100x.c +++ b/drivers/iio/humidity/hdc100x.c @@ -142,7 +142,7 @@ static int hdc100x_get_measurement(struct hdc100x_data *data, struct i2c_client *client = data->client; int delay = data->adc_int_us[chan->address]; int ret; - int val; + __be16 val; /* start measurement */ ret = i2c_smbus_write_byte(client, chan->address); @@ -154,26 +154,13 @@ static int hdc100x_get_measurement(struct hdc100x_data *data, /* wait for integration time to pass */ usleep_range(delay, delay + 1000); - /* - * i2c_smbus_read_word_data cannot() be used here due to the command - * value not being understood and causes NAKs preventing any reading - * from being accessed. - */ - ret = i2c_smbus_read_byte(client); + /* read measurement */ + ret = i2c_master_recv(data->client, (char *)&val, sizeof(val)); if (ret < 0) { - dev_err(&client->dev, "cannot read high byte measurement"); + dev_err(&client->dev, "cannot read sensor data\n"); return ret; } - val = ret << 8; - - ret = i2c_smbus_read_byte(client); - if (ret < 0) { - dev_err(&client->dev, "cannot read low byte measurement"); - return ret; - } - val |= ret; - - return val; + return be16_to_cpu(val); } static int hdc100x_get_heater_status(struct hdc100x_data *data) @@ -272,8 +259,8 @@ static int hdc100x_probe(struct i2c_client *client, struct iio_dev *indio_dev; struct hdc100x_data *data; - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE)) + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C)) return -EOPNOTSUPP; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); diff --git a/drivers/iio/humidity/htu21.c b/drivers/iio/humidity/htu21.c index 11cbc38..0fbbd8c 100644 --- a/drivers/iio/humidity/htu21.c +++ b/drivers/iio/humidity/htu21.c @@ -236,6 +236,7 @@ static const struct i2c_device_id htu21_id[] = { {"ms8607-humidity", MS8607}, {} }; +MODULE_DEVICE_TABLE(i2c, htu21_id); static struct i2c_driver htu21_driver = { .probe = htu21_probe, diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 3598835..4c45488 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -79,4 +79,7 @@ void iio_device_unregister_eventset(struct iio_dev *indio_dev); void iio_device_wakeup_eventset(struct iio_dev *indio_dev); int iio_event_getfd(struct iio_dev *indio_dev); +struct iio_event_interface; +bool iio_event_enabled(const struct iio_event_interface *ev_int); + #endif diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index b8a290e..e0251b8 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "bmi160.h" @@ -410,7 +411,8 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p) buf[j++] = sample; } - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; @@ -466,10 +468,36 @@ static int bmi160_write_raw(struct iio_dev *indio_dev, return 0; } +static +IIO_CONST_ATTR(in_accel_sampling_frequency_available, + "0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600"); +static +IIO_CONST_ATTR(in_anglvel_sampling_frequency_available, + "25 50 100 200 400 800 1600 3200"); +static +IIO_CONST_ATTR(in_accel_scale_available, + "0.000598 0.001197 0.002394 0.004788"); +static +IIO_CONST_ATTR(in_anglvel_scale_available, + "0.001065 0.000532 0.000266 0.000133 0.000066"); + +static struct attribute *bmi160_attrs[] = { + &iio_const_attr_in_accel_sampling_frequency_available.dev_attr.attr, + &iio_const_attr_in_anglvel_sampling_frequency_available.dev_attr.attr, + &iio_const_attr_in_accel_scale_available.dev_attr.attr, + &iio_const_attr_in_anglvel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group bmi160_attrs_group = { + .attrs = bmi160_attrs, +}; + static const struct iio_info bmi160_info = { .driver_module = THIS_MODULE, .read_raw = bmi160_read_raw, .write_raw = bmi160_write_raw, + .attrs = &bmi160_attrs_group, }; static const char *bmi160_match_acpi_device(struct device *dev) diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index f756fee..5483b2e 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -13,8 +13,8 @@ config INV_MPU6050_I2C select INV_MPU6050_IIO select REGMAP_I2C help - This driver supports the Invensense MPU6050/6500/9150 motion tracking - devices over I2C. + This driver supports the Invensense MPU6050/6500/9150 and ICM20608 + motion tracking devices over I2C. This driver can be built as a module. The module will be called inv-mpu6050-i2c. @@ -24,7 +24,7 @@ config INV_MPU6050_SPI select INV_MPU6050_IIO select REGMAP_SPI help - This driver supports the Invensense MPU6000/6500/9150 motion tracking - devices over SPI. + This driver supports the Invensense MPU6050/6500/9150 and ICM20608 + motion tracking devices over SPI. This driver can be built as a module. The module will be called inv-mpu6050-spi. diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index ee40dae..b9fcbf1 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -113,6 +113,12 @@ static const struct inv_mpu6050_hw hw_info[] = { .reg = ®_set_6050, .config = &chip_config_6050, }, + { + .whoami = INV_ICM20608_WHOAMI_VALUE, + .name = "ICM20608", + .reg = ®_set_6500, + .config = &chip_config_6050, + }, }; int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index e1fd7fa..19580d1 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -170,6 +170,7 @@ static const struct i2c_device_id inv_mpu_id[] = { {"mpu6050", INV_MPU6050}, {"mpu6500", INV_MPU6500}, {"mpu9150", INV_MPU9150}, + {"icm20608", INV_ICM20608}, {} }; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 3bf8544..f0e8c5d 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -70,6 +70,7 @@ enum inv_devices { INV_MPU6500, INV_MPU6000, INV_MPU9150, + INV_ICM20608, INV_NUM_PARTS }; @@ -225,6 +226,7 @@ struct inv_mpu6050_state { #define INV_MPU6050_WHOAMI_VALUE 0x68 #define INV_MPU6500_WHOAMI_VALUE 0x70 #define INV_MPU9150_WHOAMI_VALUE 0x68 +#define INV_ICM20608_WHOAMI_VALUE 0xAF /* scan element definition */ enum inv_mpu6050_scan { diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index d070062..3a9f3ea 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -107,7 +107,7 @@ irqreturn_t inv_mpu6050_irq_handler(int irq, void *p) struct inv_mpu6050_state *st = iio_priv(indio_dev); s64 timestamp; - timestamp = iio_get_time_ns(); + timestamp = iio_get_time_ns(indio_dev); kfifo_in_spinlocked(&st->timestamps, ×tamp, 1, &st->time_stamp_lock); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 190a4a5..6e6476d 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -82,6 +82,7 @@ static const struct spi_device_id inv_mpu_id[] = { {"mpu6000", INV_MPU6000}, {"mpu6500", INV_MPU6500}, {"mpu9150", INV_MPU9150}, + {"icm20608", INV_ICM20608}, {} }; diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 49bf9c5..158aaf4 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -110,7 +110,7 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, DEFINE_WAIT_FUNC(wait, woken_wake_function); size_t datum_size; size_t to_wait; - int ret; + int ret = 0; if (!indio_dev->info) return -ENODEV; @@ -153,7 +153,7 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, ret = rb->access->read_first_n(rb, n, buf); if (ret == 0 && (filp->f_flags & O_NONBLOCK)) ret = -EAGAIN; - } while (ret == 0); + } while (ret == 0); remove_wait_queue(&rb->pollq, &wait); return ret; diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index e6319a9..d2b8899 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -80,6 +80,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_RESISTANCE] = "resistance", [IIO_PH] = "ph", [IIO_UVINDEX] = "uvindex", + [IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity", }; static const char * const iio_modifier_names[] = { @@ -177,6 +178,86 @@ ssize_t iio_read_const_attr(struct device *dev, } EXPORT_SYMBOL(iio_read_const_attr); +static int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id) +{ + int ret; + const struct iio_event_interface *ev_int = indio_dev->event_interface; + + ret = mutex_lock_interruptible(&indio_dev->mlock); + if (ret) + return ret; + if ((ev_int && iio_event_enabled(ev_int)) || + iio_buffer_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + return -EBUSY; + } + indio_dev->clock_id = clock_id; + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +/** + * iio_get_time_ns() - utility function to get a time stamp for events etc + * @indio_dev: device + */ +s64 iio_get_time_ns(const struct iio_dev *indio_dev) +{ + struct timespec tp; + + switch (iio_device_get_clock(indio_dev)) { + case CLOCK_REALTIME: + ktime_get_real_ts(&tp); + break; + case CLOCK_MONOTONIC: + ktime_get_ts(&tp); + break; + case CLOCK_MONOTONIC_RAW: + getrawmonotonic(&tp); + break; + case CLOCK_REALTIME_COARSE: + tp = current_kernel_time(); + break; + case CLOCK_MONOTONIC_COARSE: + tp = get_monotonic_coarse(); + break; + case CLOCK_BOOTTIME: + get_monotonic_boottime(&tp); + break; + case CLOCK_TAI: + timekeeping_clocktai(&tp); + break; + default: + BUG(); + } + + return timespec_to_ns(&tp); +} +EXPORT_SYMBOL(iio_get_time_ns); + +/** + * iio_get_time_res() - utility function to get time stamp clock resolution in + * nano seconds. + * @indio_dev: device + */ +unsigned int iio_get_time_res(const struct iio_dev *indio_dev) +{ + switch (iio_device_get_clock(indio_dev)) { + case CLOCK_REALTIME: + case CLOCK_MONOTONIC: + case CLOCK_MONOTONIC_RAW: + case CLOCK_BOOTTIME: + case CLOCK_TAI: + return hrtimer_resolution; + case CLOCK_REALTIME_COARSE: + case CLOCK_MONOTONIC_COARSE: + return LOW_RES_NSEC; + default: + BUG(); + } +} +EXPORT_SYMBOL(iio_get_time_res); + static int __init iio_init(void) { int ret; @@ -532,9 +613,8 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals) return sprintf(buf, "%d.%09u\n", vals[0], vals[1]); case IIO_VAL_FRACTIONAL: tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]); - vals[1] = do_div(tmp, 1000000000LL); - vals[0] = tmp; - return sprintf(buf, "%d.%09u\n", vals[0], vals[1]); + vals[0] = (int)div_s64_rem(tmp, 1000000000, &vals[1]); + return sprintf(buf, "%d.%09u\n", vals[0], abs(vals[1])); case IIO_VAL_FRACTIONAL_LOG2: tmp = (s64)vals[0] * 1000000000LL >> vals[1]; vals[1] = do_div(tmp, 1000000000LL); @@ -989,11 +1069,91 @@ static ssize_t iio_show_dev_name(struct device *dev, static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL); +static ssize_t iio_show_timestamp_clock(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + const struct iio_dev *indio_dev = dev_to_iio_dev(dev); + const clockid_t clk = iio_device_get_clock(indio_dev); + const char *name; + ssize_t sz; + + switch (clk) { + case CLOCK_REALTIME: + name = "realtime\n"; + sz = sizeof("realtime\n"); + break; + case CLOCK_MONOTONIC: + name = "monotonic\n"; + sz = sizeof("monotonic\n"); + break; + case CLOCK_MONOTONIC_RAW: + name = "monotonic_raw\n"; + sz = sizeof("monotonic_raw\n"); + break; + case CLOCK_REALTIME_COARSE: + name = "realtime_coarse\n"; + sz = sizeof("realtime_coarse\n"); + break; + case CLOCK_MONOTONIC_COARSE: + name = "monotonic_coarse\n"; + sz = sizeof("monotonic_coarse\n"); + break; + case CLOCK_BOOTTIME: + name = "boottime\n"; + sz = sizeof("boottime\n"); + break; + case CLOCK_TAI: + name = "tai\n"; + sz = sizeof("tai\n"); + break; + default: + BUG(); + } + + memcpy(buf, name, sz); + return sz; +} + +static ssize_t iio_store_timestamp_clock(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + clockid_t clk; + int ret; + + if (sysfs_streq(buf, "realtime")) + clk = CLOCK_REALTIME; + else if (sysfs_streq(buf, "monotonic")) + clk = CLOCK_MONOTONIC; + else if (sysfs_streq(buf, "monotonic_raw")) + clk = CLOCK_MONOTONIC_RAW; + else if (sysfs_streq(buf, "realtime_coarse")) + clk = CLOCK_REALTIME_COARSE; + else if (sysfs_streq(buf, "monotonic_coarse")) + clk = CLOCK_MONOTONIC_COARSE; + else if (sysfs_streq(buf, "boottime")) + clk = CLOCK_BOOTTIME; + else if (sysfs_streq(buf, "tai")) + clk = CLOCK_TAI; + else + return -EINVAL; + + ret = iio_device_set_clock(dev_to_iio_dev(dev), clk); + if (ret) + return ret; + + return len; +} + +static DEVICE_ATTR(current_timestamp_clock, S_IRUGO | S_IWUSR, + iio_show_timestamp_clock, iio_store_timestamp_clock); + static int iio_device_register_sysfs(struct iio_dev *indio_dev) { int i, ret = 0, attrcount, attrn, attrcount_orig = 0; struct iio_dev_attr *p; - struct attribute **attr; + struct attribute **attr, *clk = NULL; /* First count elements in any existing group */ if (indio_dev->info->attrs) { @@ -1008,16 +1168,25 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev) */ if (indio_dev->channels) for (i = 0; i < indio_dev->num_channels; i++) { - ret = iio_device_add_channel_sysfs(indio_dev, - &indio_dev - ->channels[i]); + const struct iio_chan_spec *chan = + &indio_dev->channels[i]; + + if (chan->type == IIO_TIMESTAMP) + clk = &dev_attr_current_timestamp_clock.attr; + + ret = iio_device_add_channel_sysfs(indio_dev, chan); if (ret < 0) goto error_clear_attrs; attrcount += ret; } + if (indio_dev->event_interface) + clk = &dev_attr_current_timestamp_clock.attr; + if (indio_dev->name) attrcount++; + if (clk) + attrcount++; indio_dev->chan_attr_group.attrs = kcalloc(attrcount + 1, sizeof(indio_dev->chan_attr_group.attrs[0]), @@ -1038,6 +1207,8 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev) indio_dev->chan_attr_group.attrs[attrn++] = &p->dev_attr.attr; if (indio_dev->name) indio_dev->chan_attr_group.attrs[attrn++] = &dev_attr_name.attr; + if (clk) + indio_dev->chan_attr_group.attrs[attrn++] = clk; indio_dev->groups[indio_dev->groupcounter++] = &indio_dev->chan_attr_group; diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index cae332b..0ebfc92 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -44,6 +44,11 @@ struct iio_event_interface { struct mutex read_lock; }; +bool iio_event_enabled(const struct iio_event_interface *ev_int) +{ + return !!test_bit(IIO_BUSY_BIT_POS, &ev_int->flags); +} + /** * iio_push_event() - try to add event to the list for userspace reading * @indio_dev: IIO device structure @@ -60,7 +65,7 @@ int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp) int copied; /* Does anyone care? */ - if (test_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) { + if (iio_event_enabled(ev_int)) { ev.id = ev_code; ev.timestamp = timestamp; @@ -180,8 +185,14 @@ int iio_event_getfd(struct iio_dev *indio_dev) if (ev_int == NULL) return -ENODEV; - if (test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) - return -EBUSY; + fd = mutex_lock_interruptible(&indio_dev->mlock); + if (fd) + return fd; + + if (test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) { + fd = -EBUSY; + goto unlock; + } iio_device_get(indio_dev); @@ -194,6 +205,8 @@ int iio_event_getfd(struct iio_dev *indio_dev) kfifo_reset_out(&ev_int->det_events); } +unlock: + mutex_unlock(&indio_dev->mlock); return fd; } diff --git b/drivers/iio/industrialio-sw-device.c b/drivers/iio/industrialio-sw-device.c new file mode 100644 index 0000000..81b49cf --- /dev/null +++ b/drivers/iio/industrialio-sw-device.c @@ -0,0 +1,182 @@ +/* + * The Industrial I/O core, software IIO devices functions + * + * Copyright (c) 2016 Intel Corporation + * + * 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 + +static struct config_group *iio_devices_group; +static struct config_item_type iio_device_type_group_type; + +static struct config_item_type iio_devices_group_type = { + .ct_owner = THIS_MODULE, +}; + +static LIST_HEAD(iio_device_types_list); +static DEFINE_MUTEX(iio_device_types_lock); + +static +struct iio_sw_device_type *__iio_find_sw_device_type(const char *name, + unsigned len) +{ + struct iio_sw_device_type *d = NULL, *iter; + + list_for_each_entry(iter, &iio_device_types_list, list) + if (!strcmp(iter->name, name)) { + d = iter; + break; + } + + return d; +} + +int iio_register_sw_device_type(struct iio_sw_device_type *d) +{ + struct iio_sw_device_type *iter; + int ret = 0; + + mutex_lock(&iio_device_types_lock); + iter = __iio_find_sw_device_type(d->name, strlen(d->name)); + if (iter) + ret = -EBUSY; + else + list_add_tail(&d->list, &iio_device_types_list); + mutex_unlock(&iio_device_types_lock); + + if (ret) + return ret; + + d->group = configfs_register_default_group(iio_devices_group, d->name, + &iio_device_type_group_type); + if (IS_ERR(d->group)) + ret = PTR_ERR(d->group); + + return ret; +} +EXPORT_SYMBOL(iio_register_sw_device_type); + +void iio_unregister_sw_device_type(struct iio_sw_device_type *dt) +{ + struct iio_sw_device_type *iter; + + mutex_lock(&iio_device_types_lock); + iter = __iio_find_sw_device_type(dt->name, strlen(dt->name)); + if (iter) + list_del(&dt->list); + mutex_unlock(&iio_device_types_lock); + + configfs_unregister_default_group(dt->group); +} +EXPORT_SYMBOL(iio_unregister_sw_device_type); + +static +struct iio_sw_device_type *iio_get_sw_device_type(const char *name) +{ + struct iio_sw_device_type *dt; + + mutex_lock(&iio_device_types_lock); + dt = __iio_find_sw_device_type(name, strlen(name)); + if (dt && !try_module_get(dt->owner)) + dt = NULL; + mutex_unlock(&iio_device_types_lock); + + return dt; +} + +struct iio_sw_device *iio_sw_device_create(const char *type, const char *name) +{ + struct iio_sw_device *d; + struct iio_sw_device_type *dt; + + dt = iio_get_sw_device_type(type); + if (!dt) { + pr_err("Invalid device type: %s\n", type); + return ERR_PTR(-EINVAL); + } + d = dt->ops->probe(name); + if (IS_ERR(d)) + goto out_module_put; + + d->device_type = dt; + + return d; +out_module_put: + module_put(dt->owner); + return d; +} +EXPORT_SYMBOL(iio_sw_device_create); + +void iio_sw_device_destroy(struct iio_sw_device *d) +{ + struct iio_sw_device_type *dt = d->device_type; + + dt->ops->remove(d); + module_put(dt->owner); +} +EXPORT_SYMBOL(iio_sw_device_destroy); + +static struct config_group *device_make_group(struct config_group *group, + const char *name) +{ + struct iio_sw_device *d; + + d = iio_sw_device_create(group->cg_item.ci_name, name); + if (IS_ERR(d)) + return ERR_CAST(d); + + config_item_set_name(&d->group.cg_item, "%s", name); + + return &d->group; +} + +static void device_drop_group(struct config_group *group, + struct config_item *item) +{ + struct iio_sw_device *d = to_iio_sw_device(item); + + iio_sw_device_destroy(d); + config_item_put(item); +} + +static struct configfs_group_operations device_ops = { + .make_group = &device_make_group, + .drop_item = &device_drop_group, +}; + +static struct config_item_type iio_device_type_group_type = { + .ct_group_ops = &device_ops, + .ct_owner = THIS_MODULE, +}; + +static int __init iio_sw_device_init(void) +{ + iio_devices_group = + configfs_register_default_group(&iio_configfs_subsys.su_group, + "devices", + &iio_devices_group_type); + return PTR_ERR_OR_ZERO(iio_devices_group); +} +module_init(iio_sw_device_init); + +static void __exit iio_sw_device_exit(void) +{ + configfs_unregister_default_group(iio_devices_group); +} +module_exit(iio_sw_device_exit); + +MODULE_AUTHOR("Daniel Baluta "); +MODULE_DESCRIPTION("Industrial I/O software devices support"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 0c52dfe..7ad82fd 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -64,10 +64,16 @@ static struct attribute *iio_trig_dev_attrs[] = { }; ATTRIBUTE_GROUPS(iio_trig_dev); +static struct iio_trigger *__iio_trigger_find_by_name(const char *name); + int iio_trigger_register(struct iio_trigger *trig_info) { int ret; + /* trig_info->ops is required for the module member */ + if (!trig_info->ops) + return -EINVAL; + trig_info->id = ida_simple_get(&iio_trigger_ida, 0, 0, GFP_KERNEL); if (trig_info->id < 0) return trig_info->id; @@ -82,11 +88,19 @@ int iio_trigger_register(struct iio_trigger *trig_info) /* Add to list of available triggers held by the IIO core */ mutex_lock(&iio_trigger_list_lock); + if (__iio_trigger_find_by_name(trig_info->name)) { + pr_err("Duplicate trigger name '%s'\n", trig_info->name); + ret = -EEXIST; + goto error_device_del; + } list_add_tail(&trig_info->list, &iio_trigger_list); mutex_unlock(&iio_trigger_list_lock); return 0; +error_device_del: + mutex_unlock(&iio_trigger_list_lock); + device_del(&trig_info->dev); error_unregister_id: ida_simple_remove(&iio_trigger_ida, trig_info->id); return ret; @@ -105,6 +119,18 @@ void iio_trigger_unregister(struct iio_trigger *trig_info) } EXPORT_SYMBOL(iio_trigger_unregister); +/* Search for trigger by name, assuming iio_trigger_list_lock held */ +static struct iio_trigger *__iio_trigger_find_by_name(const char *name) +{ + struct iio_trigger *iter; + + list_for_each_entry(iter, &iio_trigger_list, list) + if (!strcmp(iter->name, name)) + return iter; + + return NULL; +} + static struct iio_trigger *iio_trigger_find_by_name(const char *name, size_t len) { @@ -164,8 +190,7 @@ EXPORT_SYMBOL(iio_trigger_poll_chained); void iio_trigger_notify_done(struct iio_trigger *trig) { - if (atomic_dec_and_test(&trig->use_count) && trig->ops && - trig->ops->try_reenable) + if (atomic_dec_and_test(&trig->use_count) && trig->ops->try_reenable) if (trig->ops->try_reenable(trig)) /* Missed an interrupt so launch new poll now */ iio_trigger_poll(trig); @@ -224,7 +249,7 @@ static int iio_trigger_attach_poll_func(struct iio_trigger *trig, goto out_put_irq; /* Enable trigger in driver */ - if (trig->ops && trig->ops->set_trigger_state && notinuse) { + if (trig->ops->set_trigger_state && notinuse) { ret = trig->ops->set_trigger_state(trig, true); if (ret < 0) goto out_free_irq; @@ -249,7 +274,7 @@ static int iio_trigger_detach_poll_func(struct iio_trigger *trig, = (bitmap_weight(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER) == 1); - if (trig->ops && trig->ops->set_trigger_state && no_other_users) { + if (trig->ops->set_trigger_state && no_other_users) { ret = trig->ops->set_trigger_state(trig, false); if (ret) return ret; @@ -264,7 +289,7 @@ static int iio_trigger_detach_poll_func(struct iio_trigger *trig, irqreturn_t iio_pollfunc_store_time(int irq, void *p) { struct iio_poll_func *pf = p; - pf->timestamp = iio_get_time_ns(); + pf->timestamp = iio_get_time_ns(pf->indio_dev); return IRQ_WAKE_THREAD; } EXPORT_SYMBOL(iio_pollfunc_store_time); @@ -371,7 +396,7 @@ static ssize_t iio_trigger_write_current(struct device *dev, return ret; } - if (trig && trig->ops && trig->ops->validate_device) { + if (trig && trig->ops->validate_device) { ret = trig->ops->validate_device(trig, indio_dev); if (ret) return ret; diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 7c566f5..3574945 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -76,7 +76,6 @@ config BH1750 config BH1780 tristate "ROHM BH1780 ambient light sensor" depends on I2C - depends on !SENSORS_BH1780 help Say Y here to build support for the ROHM BH1780GLI ambient light sensor. @@ -238,6 +237,8 @@ config MAX44000 tristate "MAX44000 Ambient and Infrared Proximity Sensor" depends on I2C select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say Y here if you want to build support for Maxim Integrated's MAX44000 ambient and infrared proximity sensor device. diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index 53201d9..f0b47c5 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -118,7 +118,7 @@ static void acpi_als_notify(struct acpi_device *device, u32 event) struct iio_dev *indio_dev = acpi_driver_data(device); struct acpi_als *als = iio_priv(indio_dev); s32 *buffer = als->evt_buffer; - s64 time_ns = iio_get_time_ns(); + s64 time_ns = iio_get_time_ns(indio_dev); s32 val; int ret; diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index 09ad5f1..0113fc8 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -118,7 +118,7 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct adjd_s311_data *data = iio_priv(indio_dev); - s64 time_ns = iio_get_time_ns(); + s64 time_ns = iio_get_time_ns(indio_dev); int i, j = 0; int ret = adjd_s311_req_data(indio_dev); diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c index e1b9fa5..649b26f 100644 --- a/drivers/iio/light/apds9300.c +++ b/drivers/iio/light/apds9300.c @@ -396,7 +396,7 @@ static irqreturn_t apds9300_interrupt_handler(int irq, void *private) IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(dev_info)); apds9300_clear_intr(data); diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index 651d57b..a4304ed 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -807,7 +807,7 @@ static irqreturn_t apds9960_interrupt_handler(int irq, void *private) IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); regmap_write(data->regmap, APDS9960_REG_CICLEAR, 1); } @@ -816,7 +816,7 @@ static irqreturn_t apds9960_interrupt_handler(int irq, void *private) IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); regmap_write(data->regmap, APDS9960_REG_PICLEAR, 1); } diff --git a/drivers/iio/light/cm36651.c b/drivers/iio/light/cm36651.c index c8d7b5e..9d66e89 100644 --- a/drivers/iio/light/cm36651.c +++ b/drivers/iio/light/cm36651.c @@ -268,7 +268,7 @@ static irqreturn_t cm36651_irq_handler(int irq, void *data) CM36651_CMD_READ_RAW_PROXIMITY, IIO_EV_TYPE_THRESH, ev_dir); - iio_push_event(indio_dev, ev_code, iio_get_time_ns()); + iio_push_event(indio_dev, ev_code, iio_get_time_ns(indio_dev)); return IRQ_HANDLED; } diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 6d41086..6ada914 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -851,7 +851,7 @@ static irqreturn_t gp2ap020a00f_prox_sensing_handler(int irq, void *data) GP2AP020A00F_SCAN_MODE_PROXIMITY, IIO_EV_TYPE_ROC, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } else { iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE( @@ -859,7 +859,7 @@ static irqreturn_t gp2ap020a00f_prox_sensing_handler(int irq, void *data) GP2AP020A00F_SCAN_MODE_PROXIMITY, IIO_EV_TYPE_ROC, IIO_EV_DIR_FALLING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } } @@ -925,7 +925,7 @@ static irqreturn_t gp2ap020a00f_thresh_event_handler(int irq, void *data) IIO_MOD_LIGHT_CLEAR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } if (test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &priv->flags)) { @@ -939,7 +939,7 @@ static irqreturn_t gp2ap020a00f_thresh_event_handler(int irq, void *data) IIO_MOD_LIGHT_CLEAR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } } @@ -1287,22 +1287,14 @@ static int gp2ap020a00f_read_raw(struct iio_dev *indio_dev, struct gp2ap020a00f_data *data = iio_priv(indio_dev); int err = -EINVAL; - mutex_lock(&data->lock); - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (iio_buffer_enabled(indio_dev)) { - err = -EBUSY; - goto error_unlock; - } + if (mask == IIO_CHAN_INFO_RAW) { + err = iio_device_claim_direct_mode(indio_dev); + if (err) + return err; err = gp2ap020a00f_read_channel(data, chan, val); - break; + iio_device_release_direct_mode(indio_dev); } - -error_unlock: - mutex_unlock(&data->lock); - return err < 0 ? err : IIO_VAL_INT; } diff --git a/drivers/iio/light/isl29125.c b/drivers/iio/light/isl29125.c index e2945a2..1d2c0c8 100644 --- a/drivers/iio/light/isl29125.c +++ b/drivers/iio/light/isl29125.c @@ -44,13 +44,15 @@ #define ISL29125_MODE_B 0x3 #define ISL29125_MODE_RGB 0x5 +#define ISL29125_SENSING_RANGE_0 5722 /* 375 lux full range */ +#define ISL29125_SENSING_RANGE_1 152590 /* 10k lux full range */ + #define ISL29125_MODE_RANGE BIT(3) #define ISL29125_STATUS_CONV BIT(1) struct isl29125_data { struct i2c_client *client; - struct mutex lock; u8 conf1; u16 buffer[8]; /* 3x 16-bit, padding, 8 bytes timestamp */ }; @@ -128,11 +130,11 @@ static int isl29125_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - if (iio_buffer_enabled(indio_dev)) - return -EBUSY; - mutex_lock(&data->lock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; ret = isl29125_read_data(data, chan->scan_index); - mutex_unlock(&data->lock); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; *val = ret; @@ -140,9 +142,9 @@ static int isl29125_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: *val = 0; if (data->conf1 & ISL29125_MODE_RANGE) - *val2 = 152590; /* 10k lux full range */ + *val2 = ISL29125_SENSING_RANGE_1; /*10k lux full range*/ else - *val2 = 5722; /* 375 lux full range */ + *val2 = ISL29125_SENSING_RANGE_0; /*375 lux full range*/ return IIO_VAL_INT_PLUS_MICRO; } return -EINVAL; @@ -158,9 +160,9 @@ static int isl29125_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: if (val != 0) return -EINVAL; - if (val2 == 152590) + if (val2 == ISL29125_SENSING_RANGE_1) data->conf1 |= ISL29125_MODE_RANGE; - else if (val2 == 5722) + else if (val2 == ISL29125_SENSING_RANGE_0) data->conf1 &= ~ISL29125_MODE_RANGE; else return -EINVAL; @@ -189,7 +191,7 @@ static irqreturn_t isl29125_trigger_handler(int irq, void *p) } iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -259,7 +261,6 @@ static int isl29125_probe(struct i2c_client *client, data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); data->client = client; - mutex_init(&data->lock); indio_dev->dev.parent = &client->dev; indio_dev->info = &isl29125_info; diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c index 99a6281..e8a8931 100644 --- a/drivers/iio/light/jsa1212.c +++ b/drivers/iio/light/jsa1212.c @@ -325,9 +325,6 @@ static int jsa1212_probe(struct i2c_client *client, struct regmap *regmap; int ret; - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EOPNOTSUPP; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index e56937c..f409c20 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -267,7 +267,7 @@ static irqreturn_t lm3533_als_isr(int irq, void *dev_id) 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); out: return IRQ_HANDLED; } diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 6bf89d8..3afc53a 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -1256,7 +1256,8 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p) buf[j++] = psdata & LTR501_PS_DATA_MASK; } - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -1282,14 +1283,14 @@ static irqreturn_t ltr501_interrupt_handler(int irq, void *private) IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); if (status & LTR501_STATUS_PS_INTR) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); return IRQ_HANDLED; } diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c index f17cb2e..6511b20 100644 --- a/drivers/iio/light/max44000.c +++ b/drivers/iio/light/max44000.c @@ -511,7 +511,8 @@ static irqreturn_t max44000_trigger_handler(int irq, void *p) } mutex_unlock(&data->lock); - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index b776c8e..78c9b3a 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -713,13 +713,13 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(iio)); if (ret & OPT3001_CONFIGURATION_FL) iio_push_event(iio, IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - iio_get_time_ns()); + iio_get_time_ns(iio)); } else if (ret & OPT3001_CONFIGURATION_CRF) { ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT); if (ret < 0) { diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index 9e847f8..45cf8b0 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -528,7 +528,7 @@ static irqreturn_t stk3310_irq_handler(int irq, void *private) struct iio_dev *indio_dev = private; struct stk3310_data *data = iio_priv(indio_dev); - data->timestamp = iio_get_time_ns(); + data->timestamp = iio_get_time_ns(indio_dev); return IRQ_WAKE_THREAD; } diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c index f90f8c5..a795afb 100644 --- a/drivers/iio/light/tcs3414.c +++ b/drivers/iio/light/tcs3414.c @@ -53,7 +53,6 @@ struct tcs3414_data { struct i2c_client *client; - struct mutex lock; u8 control; u8 gain; u8 timing; @@ -134,16 +133,16 @@ static int tcs3414_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - if (iio_buffer_enabled(indio_dev)) - return -EBUSY; - mutex_lock(&data->lock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; ret = tcs3414_req_data(data); if (ret < 0) { - mutex_unlock(&data->lock); + iio_device_release_direct_mode(indio_dev); return ret; } ret = i2c_smbus_read_word_data(data->client, chan->address); - mutex_unlock(&data->lock); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; *val = ret; @@ -217,7 +216,7 @@ static irqreturn_t tcs3414_trigger_handler(int irq, void *p) } iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -288,7 +287,6 @@ static int tcs3414_probe(struct i2c_client *client, data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); data->client = client; - mutex_init(&data->lock); indio_dev->dev.parent = &client->dev; indio_dev->info = &tcs3414_info; diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 1b530bf..3aa71e3 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -52,7 +52,6 @@ struct tcs3472_data { struct i2c_client *client; - struct mutex lock; u8 enable; u8 control; u8 atime; @@ -117,17 +116,16 @@ static int tcs3472_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - if (iio_buffer_enabled(indio_dev)) - return -EBUSY; - - mutex_lock(&data->lock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; ret = tcs3472_req_data(data); if (ret < 0) { - mutex_unlock(&data->lock); + iio_device_release_direct_mode(indio_dev); return ret; } ret = i2c_smbus_read_word_data(data->client, chan->address); - mutex_unlock(&data->lock); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; *val = ret; @@ -204,7 +202,7 @@ static irqreturn_t tcs3472_trigger_handler(int irq, void *p) } iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -263,7 +261,6 @@ static int tcs3472_probe(struct i2c_client *client, data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); data->client = client; - mutex_init(&data->lock); indio_dev->dev.parent = &client->dev; indio_dev->info = &tcs3472_info; diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index 57b108c..04598ae 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -630,7 +630,7 @@ static irqreturn_t tsl2563_event_handler(int irq, void *private) 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(dev_info)); /* clear the interrupt and push the event */ i2c_smbus_write_byte(chip->client, TSL2563_CMD | TSL2563_CLEARINT); diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c index 45bc2f7..20c40f7 100644 --- a/drivers/iio/light/us5182d.c +++ b/drivers/iio/light/us5182d.c @@ -833,7 +833,7 @@ static irqreturn_t us5182d_irq_thread_handler(int irq, void *private) dir = ret & US5182D_CFG0_PROX ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1, IIO_EV_TYPE_THRESH, dir); - iio_push_event(indio_dev, ev, iio_get_time_ns()); + iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev)); ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret & ~US5182D_CFG0_PX_IRQ); diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 84e6559..1f842ab 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -44,6 +44,7 @@ config BMC150_MAGN_I2C This driver is only implementing magnetometer part, which has its own address and register map. + This driver also supports I2C Bosch BMC156 and BMM150 chips. To compile this driver as a module, choose M here: the module will be called bmc150_magn_i2c. @@ -60,6 +61,7 @@ config BMC150_MAGN_SPI This driver is only implementing magnetometer part, which has its own address and register map. + This driver also supports SPI Bosch BMC156 and BMM150 chips. To compile this driver as a module, choose M here: the module will be called bmc150_magn_spi. diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 609a2c4..af8606c 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -379,37 +380,40 @@ struct ak8975_data { u8 cntl_cache; struct iio_mount_matrix orientation; struct regulator *vdd; + struct regulator *vid; }; /* Enable attached power regulator if any. */ -static int ak8975_power_on(struct i2c_client *client) +static int ak8975_power_on(const struct ak8975_data *data) { - const struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct ak8975_data *data = iio_priv(indio_dev); int ret; - data->vdd = devm_regulator_get(&client->dev, "vdd"); - if (IS_ERR_OR_NULL(data->vdd)) { - ret = PTR_ERR(data->vdd); - if (ret == -ENODEV) - ret = 0; - } else { - ret = regulator_enable(data->vdd); + ret = regulator_enable(data->vdd); + if (ret) { + dev_warn(&data->client->dev, + "Failed to enable specified Vdd supply\n"); + return ret; } - - if (ret) - dev_err(&client->dev, "failed to enable Vdd supply: %d\n", ret); - return ret; + ret = regulator_enable(data->vid); + if (ret) { + dev_warn(&data->client->dev, + "Failed to enable specified Vid supply\n"); + return ret; + } + /* + * According to the datasheet the power supply rise time i 200us + * and the minimum wait time before mode setting is 100us, in + * total 300 us. Add some margin and say minimum 500us here. + */ + usleep_range(500, 1000); + return 0; } /* Disable attached power regulator if any. */ -static void ak8975_power_off(const struct i2c_client *client) +static void ak8975_power_off(const struct ak8975_data *data) { - const struct iio_dev *indio_dev = i2c_get_clientdata(client); - const struct ak8975_data *data = iio_priv(indio_dev); - - if (!IS_ERR_OR_NULL(data->vdd)) - regulator_disable(data->vdd); + regulator_disable(data->vid); + regulator_disable(data->vdd); } /* @@ -430,8 +434,8 @@ static int ak8975_who_i_am(struct i2c_client *client, * AK8975 | DEVICE_ID | NA * AK8963 | DEVICE_ID | NA */ - ret = i2c_smbus_read_i2c_block_data(client, AK09912_REG_WIA1, - 2, wia_val); + ret = i2c_smbus_read_i2c_block_data_or_emulated( + client, AK09912_REG_WIA1, 2, wia_val); if (ret < 0) { dev_err(&client->dev, "Error reading WIA\n"); return ret; @@ -543,9 +547,9 @@ static int ak8975_setup(struct i2c_client *client) } /* Get asa data and store in the device data. */ - ret = i2c_smbus_read_i2c_block_data(client, - data->def->ctrl_regs[ASA_BASE], - 3, data->asa); + ret = i2c_smbus_read_i2c_block_data_or_emulated( + client, data->def->ctrl_regs[ASA_BASE], + 3, data->asa); if (ret < 0) { dev_err(&client->dev, "Not able to read asa data\n"); return ret; @@ -686,22 +690,31 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) struct ak8975_data *data = iio_priv(indio_dev); const struct i2c_client *client = data->client; const struct ak_def *def = data->def; + u16 buff; int ret; + pm_runtime_get_sync(&data->client->dev); + mutex_lock(&data->lock); ret = ak8975_start_read_axis(data, client); if (ret) goto exit; - ret = i2c_smbus_read_word_data(client, def->data_regs[index]); + ret = i2c_smbus_read_i2c_block_data_or_emulated( + client, def->data_regs[index], + sizeof(buff), (u8*)&buff); if (ret < 0) goto exit; mutex_unlock(&data->lock); - /* Clamp to valid range. */ - *val = clamp_t(s16, ret, -def->range, def->range); + pm_runtime_mark_last_busy(&data->client->dev); + pm_runtime_put_autosuspend(&data->client->dev); + + /* Swap bytes and convert to valid range. */ + buff = le16_to_cpu(buff); + *val = clamp_t(s16, buff, -def->range, def->range); return IIO_VAL_INT; exit: @@ -825,7 +838,8 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev) buff[1] = clamp_t(s16, le16_to_cpu(buff[1]), -def->range, def->range); buff[2] = clamp_t(s16, le16_to_cpu(buff[2]), -def->range, def->range); - iio_push_to_buffers_with_timestamp(indio_dev, buff, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buff, + iio_get_time_ns(indio_dev)); return; unlock: @@ -919,7 +933,15 @@ static int ak8975_probe(struct i2c_client *client, data->def = &ak_def_array[chipset]; - err = ak8975_power_on(client); + /* Fetch the regulators */ + data->vdd = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(data->vdd)) + return PTR_ERR(data->vdd); + data->vid = devm_regulator_get(&client->dev, "vid"); + if (IS_ERR(data->vid)) + return PTR_ERR(data->vid); + + err = ak8975_power_on(data); if (err) return err; @@ -959,26 +981,93 @@ static int ak8975_probe(struct i2c_client *client, goto cleanup_buffer; } + /* Enable runtime PM */ + pm_runtime_get_noresume(&client->dev); + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + /* + * The device comes online in 500us, so add two orders of magnitude + * of delay before autosuspending: 50 ms. + */ + pm_runtime_set_autosuspend_delay(&client->dev, 50); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_put(&client->dev); + return 0; cleanup_buffer: iio_triggered_buffer_cleanup(indio_dev); power_off: - ak8975_power_off(client); + ak8975_power_off(data); return err; } static int ak8975_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ak8975_data *data = iio_priv(indio_dev); + pm_runtime_get_sync(&client->dev); + pm_runtime_put_noidle(&client->dev); + pm_runtime_disable(&client->dev); iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); - ak8975_power_off(client); + ak8975_set_mode(data, POWER_DOWN); + ak8975_power_off(data); return 0; } +#ifdef CONFIG_PM +static int ak8975_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ak8975_data *data = iio_priv(indio_dev); + int ret; + + /* Set the device in power down if it wasn't already */ + ret = ak8975_set_mode(data, POWER_DOWN); + if (ret < 0) { + dev_err(&client->dev, "Error in setting power-down mode\n"); + return ret; + } + /* Next cut the regulators */ + ak8975_power_off(data); + + return 0; +} + +static int ak8975_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ak8975_data *data = iio_priv(indio_dev); + int ret; + + /* Take up the regulators */ + ak8975_power_on(data); + /* + * We come up in powered down mode, the reading routines will + * put us in the mode to read values later. + */ + ret = ak8975_set_mode(data, POWER_DOWN); + if (ret < 0) { + dev_err(&client->dev, "Error in setting power-down mode\n"); + return ret; + } + + return 0; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops ak8975_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(ak8975_runtime_suspend, + ak8975_runtime_resume, NULL) +}; + static const struct i2c_device_id ak8975_id[] = { {"ak8975", AK8975}, {"ak8963", AK8963}, @@ -1006,6 +1095,7 @@ MODULE_DEVICE_TABLE(of, ak8975_of_match); static struct i2c_driver ak8975_driver = { .driver = { .name = "ak8975", + .pm = &ak8975_dev_pm_ops, .of_match_table = of_match_ptr(ak8975_of_match), .acpi_match_table = ACPI_PTR(ak_acpi_match), }, diff --git a/drivers/iio/magnetometer/bmc150_magn_i2c.c b/drivers/iio/magnetometer/bmc150_magn_i2c.c index eddc7f0..ee05722 100644 --- a/drivers/iio/magnetometer/bmc150_magn_i2c.c +++ b/drivers/iio/magnetometer/bmc150_magn_i2c.c @@ -2,6 +2,7 @@ * 3-axis magnetometer driver supporting following I2C Bosch-Sensortec chips: * - BMC150 * - BMC156 + * - BMM150 * * Copyright (c) 2016, Intel Corporation. * @@ -49,6 +50,7 @@ static int bmc150_magn_i2c_remove(struct i2c_client *client) static const struct acpi_device_id bmc150_magn_acpi_match[] = { {"BMC150B", 0}, {"BMC156B", 0}, + {"BMM150B", 0}, {}, }; MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match); @@ -56,6 +58,7 @@ MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match); static const struct i2c_device_id bmc150_magn_i2c_id[] = { {"bmc150_magn", 0}, {"bmc156_magn", 0}, + {"bmm150_magn", 0}, {} }; MODULE_DEVICE_TABLE(i2c, bmc150_magn_i2c_id); diff --git a/drivers/iio/magnetometer/bmc150_magn_spi.c b/drivers/iio/magnetometer/bmc150_magn_spi.c index c4c738a..7d4152d 100644 --- a/drivers/iio/magnetometer/bmc150_magn_spi.c +++ b/drivers/iio/magnetometer/bmc150_magn_spi.c @@ -2,6 +2,7 @@ * 3-axis magnetometer driver support following SPI Bosch-Sensortec chips: * - BMC150 * - BMC156 + * - BMM150 * * Copyright (c) 2016, Intel Corporation. * @@ -41,6 +42,7 @@ static int bmc150_magn_spi_remove(struct spi_device *spi) static const struct spi_device_id bmc150_magn_spi_id[] = { {"bmc150_magn", 0}, {"bmc156_magn", 0}, + {"bmm150_magn", 0}, {} }; MODULE_DEVICE_TABLE(spi, bmc150_magn_spi_id); @@ -48,6 +50,7 @@ MODULE_DEVICE_TABLE(spi, bmc150_magn_spi_id); static const struct acpi_device_id bmc150_magn_acpi_match[] = { {"BMC150B", 0}, {"BMC156B", 0}, + {"BMM150B", 0}, {}, }; MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match); diff --git a/drivers/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c index 77882b4..ba3e2a3 100644 --- a/drivers/iio/magnetometer/hmc5843_core.c +++ b/drivers/iio/magnetometer/hmc5843_core.c @@ -451,7 +451,7 @@ static irqreturn_t hmc5843_trigger_handler(int irq, void *p) goto done; iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index 261d517..f2be4a0 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -261,7 +261,7 @@ static irqreturn_t mag3110_trigger_handler(int irq, void *p) } iio_push_to_buffers_with_timestamp(indio_dev, buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 8250fc3..3e1f06b 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -589,13 +589,15 @@ int st_magn_common_probe(struct iio_dev *indio_dev) indio_dev->info = &magn_info; mutex_init(&mdata->tb.buf_lock); - st_sensors_power_enable(indio_dev); + err = st_sensors_power_enable(indio_dev); + if (err) + return err; err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_magn_sensors_settings), st_magn_sensors_settings); if (err < 0) - return err; + goto st_magn_power_off; mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS; mdata->multiread_bit = mdata->sensor_settings->multi_read_bit; @@ -608,11 +610,11 @@ int st_magn_common_probe(struct iio_dev *indio_dev) err = st_sensors_init_sensor(indio_dev, NULL); if (err < 0) - return err; + goto st_magn_power_off; err = st_magn_allocate_ring(indio_dev); if (err < 0) - return err; + goto st_magn_power_off; if (irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -635,6 +637,8 @@ st_magn_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_magn_probe_trigger_error: st_magn_deallocate_ring(indio_dev); +st_magn_power_off: + st_sensors_power_disable(indio_dev); return err; } diff --git a/drivers/iio/potentiometer/Kconfig b/drivers/iio/potentiometer/Kconfig index 6acb238..2e9da1c 100644 --- a/drivers/iio/potentiometer/Kconfig +++ b/drivers/iio/potentiometer/Kconfig @@ -10,11 +10,22 @@ config DS1803 depends on I2C help Say yes here to build support for the Maxim Integrated DS1803 - digital potentiomenter chip. + digital potentiometer chip. To compile this driver as a module, choose M here: the module will be called ds1803. +config MAX5487 + tristate "Maxim MAX5487/MAX5488/MAX5489 Digital Potentiometer driver" + depends on SPI + help + Say yes here to build support for the Maxim + MAX5487, MAX5488, MAX5489 digital potentiometer + chips. + + To compile this driver as a module, choose M here: the + module will be called max5487. + config MCP4131 tristate "Microchip MCP413X/414X/415X/416X/423X/424X/425X/426X Digital Potentiometer driver" depends on SPI @@ -28,7 +39,7 @@ config MCP4131 MCP4241, MCP4242, MCP4251, MCP4252, MCP4261, MCP4262, - digital potentiomenter chips. + digital potentiometer chips. To compile this driver as a module, choose M here: the module will be called mcp4131. @@ -38,9 +49,11 @@ config MCP4531 depends on I2C help Say yes here to build support for the Microchip - MCP4531, MCP4532, MCP4551, MCP4552, - MCP4631, MCP4632, MCP4651, MCP4652 - digital potentiomenter chips. + MCP4531, MCP4532, MCP4541, MCP4542, + MCP4551, MCP4552, MCP4561, MCP4562, + MCP4631, MCP4632, MCP4641, MCP4642, + MCP4651, MCP4652, MCP4661, MCP4662 + digital potentiometer chips. To compile this driver as a module, choose M here: the module will be called mcp4531. diff --git a/drivers/iio/potentiometer/Makefile b/drivers/iio/potentiometer/Makefile index 6007faa..8adb58f 100644 --- a/drivers/iio/potentiometer/Makefile +++ b/drivers/iio/potentiometer/Makefile @@ -4,6 +4,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_DS1803) += ds1803.o +obj-$(CONFIG_MAX5487) += max5487.o obj-$(CONFIG_MCP4131) += mcp4131.o obj-$(CONFIG_MCP4531) += mcp4531.o obj-$(CONFIG_TPL0102) += tpl0102.o diff --git b/drivers/iio/potentiometer/max5487.c b/drivers/iio/potentiometer/max5487.c new file mode 100644 index 0000000..6c50939 --- /dev/null +++ b/drivers/iio/potentiometer/max5487.c @@ -0,0 +1,161 @@ +/* + * max5487.c - Support for MAX5487, MAX5488, MAX5489 digital potentiometers + * + * Copyright (C) 2016 Cristina-Gabriela Moraru + * + * 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 MAX5487_WRITE_WIPER_A (0x01 << 8) +#define MAX5487_WRITE_WIPER_B (0x02 << 8) + +/* copy both wiper regs to NV regs */ +#define MAX5487_COPY_AB_TO_NV (0x23 << 8) +/* copy both NV regs to wiper regs */ +#define MAX5487_COPY_NV_TO_AB (0x33 << 8) + +#define MAX5487_MAX_POS 255 + +struct max5487_data { + struct spi_device *spi; + int kohms; +}; + +#define MAX5487_CHANNEL(ch, addr) { \ + .type = IIO_RESISTANCE, \ + .indexed = 1, \ + .output = 1, \ + .channel = ch, \ + .address = addr, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec max5487_channels[] = { + MAX5487_CHANNEL(0, MAX5487_WRITE_WIPER_A), + MAX5487_CHANNEL(1, MAX5487_WRITE_WIPER_B), +}; + +static int max5487_write_cmd(struct spi_device *spi, u16 cmd) +{ + return spi_write(spi, (const void *) &cmd, sizeof(u16)); +} + +static int max5487_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max5487_data *data = iio_priv(indio_dev); + + if (mask != IIO_CHAN_INFO_SCALE) + return -EINVAL; + + *val = 1000 * data->kohms; + *val2 = MAX5487_MAX_POS; + + return IIO_VAL_FRACTIONAL; +} + +static int max5487_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct max5487_data *data = iio_priv(indio_dev); + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + if (val < 0 || val > MAX5487_MAX_POS) + return -EINVAL; + + return max5487_write_cmd(data->spi, chan->address | val); +} + +static const struct iio_info max5487_info = { + .read_raw = max5487_read_raw, + .write_raw = max5487_write_raw, + .driver_module = THIS_MODULE, +}; + +static int max5487_spi_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct max5487_data *data; + const struct spi_device_id *id = spi_get_device_id(spi); + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + dev_set_drvdata(&spi->dev, indio_dev); + data = iio_priv(indio_dev); + + data->spi = spi; + data->kohms = id->driver_data; + + indio_dev->info = &max5487_info; + indio_dev->name = id->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max5487_channels; + indio_dev->num_channels = ARRAY_SIZE(max5487_channels); + + /* restore both wiper regs from NV regs */ + ret = max5487_write_cmd(data->spi, MAX5487_COPY_NV_TO_AB); + if (ret < 0) + return ret; + + return iio_device_register(indio_dev); +} + +static int max5487_spi_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev); + + iio_device_unregister(indio_dev); + + /* save both wiper regs to NV regs */ + return max5487_write_cmd(spi, MAX5487_COPY_AB_TO_NV); +} + +static const struct spi_device_id max5487_id[] = { + { "MAX5487", 10 }, + { "MAX5488", 50 }, + { "MAX5489", 100 }, + { } +}; +MODULE_DEVICE_TABLE(spi, max5487_id); + +static const struct acpi_device_id max5487_acpi_match[] = { + { "MAX5487", 10 }, + { "MAX5488", 50 }, + { "MAX5489", 100 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, max5487_acpi_match); + +static struct spi_driver max5487_driver = { + .driver = { + .name = "max5487", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(max5487_acpi_match), + }, + .id_table = max5487_id, + .probe = max5487_spi_probe, + .remove = max5487_spi_remove +}; +module_spi_driver(max5487_driver); + +MODULE_AUTHOR("Cristina-Gabriela Moraru "); +MODULE_DESCRIPTION("max5487 SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/potentiometer/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c index 3b72e1a..13b6ae2 100644 --- a/drivers/iio/potentiometer/mcp4531.c +++ b/drivers/iio/potentiometer/mcp4531.c @@ -8,12 +8,20 @@ * DEVID #Wipers #Positions Resistor Opts (kOhm) i2c address * mcp4531 1 129 5, 10, 50, 100 010111x * mcp4532 1 129 5, 10, 50, 100 01011xx + * mcp4541 1 129 5, 10, 50, 100 010111x + * mcp4542 1 129 5, 10, 50, 100 01011xx * mcp4551 1 257 5, 10, 50, 100 010111x * mcp4552 1 257 5, 10, 50, 100 01011xx + * mcp4561 1 257 5, 10, 50, 100 010111x + * mcp4562 1 257 5, 10, 50, 100 01011xx * mcp4631 2 129 5, 10, 50, 100 0101xxx * mcp4632 2 129 5, 10, 50, 100 01011xx + * mcp4641 2 129 5, 10, 50, 100 0101xxx + * mcp4642 2 129 5, 10, 50, 100 01011xx * mcp4651 2 257 5, 10, 50, 100 0101xxx * mcp4652 2 257 5, 10, 50, 100 01011xx + * mcp4661 2 257 5, 10, 50, 100 0101xxx + * mcp4662 2 257 5, 10, 50, 100 01011xx * * 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 @@ -23,6 +31,8 @@ #include #include #include +#include +#include #include @@ -37,18 +47,34 @@ enum mcp4531_type { MCP453x_103, MCP453x_503, MCP453x_104, + MCP454x_502, + MCP454x_103, + MCP454x_503, + MCP454x_104, MCP455x_502, MCP455x_103, MCP455x_503, MCP455x_104, + MCP456x_502, + MCP456x_103, + MCP456x_503, + MCP456x_104, MCP463x_502, MCP463x_103, MCP463x_503, MCP463x_104, + MCP464x_502, + MCP464x_103, + MCP464x_503, + MCP464x_104, MCP465x_502, MCP465x_103, MCP465x_503, MCP465x_104, + MCP466x_502, + MCP466x_103, + MCP466x_503, + MCP466x_104, }; static const struct mcp4531_cfg mcp4531_cfg[] = { @@ -56,18 +82,34 @@ static const struct mcp4531_cfg mcp4531_cfg[] = { [MCP453x_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, }, [MCP453x_503] = { .wipers = 1, .max_pos = 128, .kohms = 50, }, [MCP453x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, }, + [MCP454x_502] = { .wipers = 1, .max_pos = 128, .kohms = 5, }, + [MCP454x_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, }, + [MCP454x_503] = { .wipers = 1, .max_pos = 128, .kohms = 50, }, + [MCP454x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, }, [MCP455x_502] = { .wipers = 1, .max_pos = 256, .kohms = 5, }, [MCP455x_103] = { .wipers = 1, .max_pos = 256, .kohms = 10, }, [MCP455x_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, }, [MCP455x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, }, + [MCP456x_502] = { .wipers = 1, .max_pos = 256, .kohms = 5, }, + [MCP456x_103] = { .wipers = 1, .max_pos = 256, .kohms = 10, }, + [MCP456x_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, }, + [MCP456x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, }, [MCP463x_502] = { .wipers = 2, .max_pos = 128, .kohms = 5, }, [MCP463x_103] = { .wipers = 2, .max_pos = 128, .kohms = 10, }, [MCP463x_503] = { .wipers = 2, .max_pos = 128, .kohms = 50, }, [MCP463x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, }, + [MCP464x_502] = { .wipers = 2, .max_pos = 128, .kohms = 5, }, + [MCP464x_103] = { .wipers = 2, .max_pos = 128, .kohms = 10, }, + [MCP464x_503] = { .wipers = 2, .max_pos = 128, .kohms = 50, }, + [MCP464x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, }, [MCP465x_502] = { .wipers = 2, .max_pos = 256, .kohms = 5, }, [MCP465x_103] = { .wipers = 2, .max_pos = 256, .kohms = 10, }, [MCP465x_503] = { .wipers = 2, .max_pos = 256, .kohms = 50, }, [MCP465x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, }, + [MCP466x_502] = { .wipers = 2, .max_pos = 256, .kohms = 5, }, + [MCP466x_103] = { .wipers = 2, .max_pos = 256, .kohms = 10, }, + [MCP466x_503] = { .wipers = 2, .max_pos = 256, .kohms = 50, }, + [MCP466x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, }, }; #define MCP4531_WRITE (0 << 2) @@ -148,12 +190,89 @@ static const struct iio_info mcp4531_info = { .driver_module = THIS_MODULE, }; +#ifdef CONFIG_OF + +#define MCP4531_COMPATIBLE(of_compatible, cfg) { \ + .compatible = of_compatible, \ + .data = &mcp4531_cfg[cfg], \ +} + +static const struct of_device_id mcp4531_of_match[] = { + MCP4531_COMPATIBLE("microchip,mcp4531-502", MCP453x_502), + MCP4531_COMPATIBLE("microchip,mcp4531-103", MCP453x_103), + MCP4531_COMPATIBLE("microchip,mcp4531-503", MCP453x_503), + MCP4531_COMPATIBLE("microchip,mcp4531-104", MCP453x_104), + MCP4531_COMPATIBLE("microchip,mcp4532-502", MCP453x_502), + MCP4531_COMPATIBLE("microchip,mcp4532-103", MCP453x_103), + MCP4531_COMPATIBLE("microchip,mcp4532-503", MCP453x_503), + MCP4531_COMPATIBLE("microchip,mcp4532-104", MCP453x_104), + MCP4531_COMPATIBLE("microchip,mcp4541-502", MCP454x_502), + MCP4531_COMPATIBLE("microchip,mcp4541-103", MCP454x_103), + MCP4531_COMPATIBLE("microchip,mcp4541-503", MCP454x_503), + MCP4531_COMPATIBLE("microchip,mcp4541-104", MCP454x_104), + MCP4531_COMPATIBLE("microchip,mcp4542-502", MCP454x_502), + MCP4531_COMPATIBLE("microchip,mcp4542-103", MCP454x_103), + MCP4531_COMPATIBLE("microchip,mcp4542-503", MCP454x_503), + MCP4531_COMPATIBLE("microchip,mcp4542-104", MCP454x_104), + MCP4531_COMPATIBLE("microchip,mcp4551-502", MCP455x_502), + MCP4531_COMPATIBLE("microchip,mcp4551-103", MCP455x_103), + MCP4531_COMPATIBLE("microchip,mcp4551-503", MCP455x_503), + MCP4531_COMPATIBLE("microchip,mcp4551-104", MCP455x_104), + MCP4531_COMPATIBLE("microchip,mcp4552-502", MCP455x_502), + MCP4531_COMPATIBLE("microchip,mcp4552-103", MCP455x_103), + MCP4531_COMPATIBLE("microchip,mcp4552-503", MCP455x_503), + MCP4531_COMPATIBLE("microchip,mcp4552-104", MCP455x_104), + MCP4531_COMPATIBLE("microchip,mcp4561-502", MCP456x_502), + MCP4531_COMPATIBLE("microchip,mcp4561-103", MCP456x_103), + MCP4531_COMPATIBLE("microchip,mcp4561-503", MCP456x_503), + MCP4531_COMPATIBLE("microchip,mcp4561-104", MCP456x_104), + MCP4531_COMPATIBLE("microchip,mcp4562-502", MCP456x_502), + MCP4531_COMPATIBLE("microchip,mcp4562-103", MCP456x_103), + MCP4531_COMPATIBLE("microchip,mcp4562-503", MCP456x_503), + MCP4531_COMPATIBLE("microchip,mcp4562-104", MCP456x_104), + MCP4531_COMPATIBLE("microchip,mcp4631-502", MCP463x_502), + MCP4531_COMPATIBLE("microchip,mcp4631-103", MCP463x_103), + MCP4531_COMPATIBLE("microchip,mcp4631-503", MCP463x_503), + MCP4531_COMPATIBLE("microchip,mcp4631-104", MCP463x_104), + MCP4531_COMPATIBLE("microchip,mcp4632-502", MCP463x_502), + MCP4531_COMPATIBLE("microchip,mcp4632-103", MCP463x_103), + MCP4531_COMPATIBLE("microchip,mcp4632-503", MCP463x_503), + MCP4531_COMPATIBLE("microchip,mcp4632-104", MCP463x_104), + MCP4531_COMPATIBLE("microchip,mcp4641-502", MCP464x_502), + MCP4531_COMPATIBLE("microchip,mcp4641-103", MCP464x_103), + MCP4531_COMPATIBLE("microchip,mcp4641-503", MCP464x_503), + MCP4531_COMPATIBLE("microchip,mcp4641-104", MCP464x_104), + MCP4531_COMPATIBLE("microchip,mcp4642-502", MCP464x_502), + MCP4531_COMPATIBLE("microchip,mcp4642-103", MCP464x_103), + MCP4531_COMPATIBLE("microchip,mcp4642-503", MCP464x_503), + MCP4531_COMPATIBLE("microchip,mcp4642-104", MCP464x_104), + MCP4531_COMPATIBLE("microchip,mcp4651-502", MCP465x_502), + MCP4531_COMPATIBLE("microchip,mcp4651-103", MCP465x_103), + MCP4531_COMPATIBLE("microchip,mcp4651-503", MCP465x_503), + MCP4531_COMPATIBLE("microchip,mcp4651-104", MCP465x_104), + MCP4531_COMPATIBLE("microchip,mcp4652-502", MCP465x_502), + MCP4531_COMPATIBLE("microchip,mcp4652-103", MCP465x_103), + MCP4531_COMPATIBLE("microchip,mcp4652-503", MCP465x_503), + MCP4531_COMPATIBLE("microchip,mcp4652-104", MCP465x_104), + MCP4531_COMPATIBLE("microchip,mcp4661-502", MCP466x_502), + MCP4531_COMPATIBLE("microchip,mcp4661-103", MCP466x_103), + MCP4531_COMPATIBLE("microchip,mcp4661-503", MCP466x_503), + MCP4531_COMPATIBLE("microchip,mcp4661-104", MCP466x_104), + MCP4531_COMPATIBLE("microchip,mcp4662-502", MCP466x_502), + MCP4531_COMPATIBLE("microchip,mcp4662-103", MCP466x_103), + MCP4531_COMPATIBLE("microchip,mcp4662-503", MCP466x_503), + MCP4531_COMPATIBLE("microchip,mcp4662-104", MCP466x_104), + { /* sentinel */ } +}; +#endif + static int mcp4531_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; struct mcp4531_data *data; struct iio_dev *indio_dev; + const struct of_device_id *match; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { @@ -167,7 +286,12 @@ static int mcp4531_probe(struct i2c_client *client, data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); data->client = client; - data->cfg = &mcp4531_cfg[id->driver_data]; + + match = of_match_device(of_match_ptr(mcp4531_of_match), dev); + if (match) + data->cfg = of_device_get_match_data(dev); + else + data->cfg = &mcp4531_cfg[id->driver_data]; indio_dev->dev.parent = dev; indio_dev->info = &mcp4531_info; @@ -187,6 +311,14 @@ static const struct i2c_device_id mcp4531_id[] = { { "mcp4532-103", MCP453x_103 }, { "mcp4532-503", MCP453x_503 }, { "mcp4532-104", MCP453x_104 }, + { "mcp4541-502", MCP454x_502 }, + { "mcp4541-103", MCP454x_103 }, + { "mcp4541-503", MCP454x_503 }, + { "mcp4541-104", MCP454x_104 }, + { "mcp4542-502", MCP454x_502 }, + { "mcp4542-103", MCP454x_103 }, + { "mcp4542-503", MCP454x_503 }, + { "mcp4542-104", MCP454x_104 }, { "mcp4551-502", MCP455x_502 }, { "mcp4551-103", MCP455x_103 }, { "mcp4551-503", MCP455x_503 }, @@ -195,6 +327,14 @@ static const struct i2c_device_id mcp4531_id[] = { { "mcp4552-103", MCP455x_103 }, { "mcp4552-503", MCP455x_503 }, { "mcp4552-104", MCP455x_104 }, + { "mcp4561-502", MCP456x_502 }, + { "mcp4561-103", MCP456x_103 }, + { "mcp4561-503", MCP456x_503 }, + { "mcp4561-104", MCP456x_104 }, + { "mcp4562-502", MCP456x_502 }, + { "mcp4562-103", MCP456x_103 }, + { "mcp4562-503", MCP456x_503 }, + { "mcp4562-104", MCP456x_104 }, { "mcp4631-502", MCP463x_502 }, { "mcp4631-103", MCP463x_103 }, { "mcp4631-503", MCP463x_503 }, @@ -203,6 +343,14 @@ static const struct i2c_device_id mcp4531_id[] = { { "mcp4632-103", MCP463x_103 }, { "mcp4632-503", MCP463x_503 }, { "mcp4632-104", MCP463x_104 }, + { "mcp4641-502", MCP464x_502 }, + { "mcp4641-103", MCP464x_103 }, + { "mcp4641-503", MCP464x_503 }, + { "mcp4641-104", MCP464x_104 }, + { "mcp4642-502", MCP464x_502 }, + { "mcp4642-103", MCP464x_103 }, + { "mcp4642-503", MCP464x_503 }, + { "mcp4642-104", MCP464x_104 }, { "mcp4651-502", MCP465x_502 }, { "mcp4651-103", MCP465x_103 }, { "mcp4651-503", MCP465x_503 }, @@ -211,6 +359,14 @@ static const struct i2c_device_id mcp4531_id[] = { { "mcp4652-103", MCP465x_103 }, { "mcp4652-503", MCP465x_503 }, { "mcp4652-104", MCP465x_104 }, + { "mcp4661-502", MCP466x_502 }, + { "mcp4661-103", MCP466x_103 }, + { "mcp4661-503", MCP466x_503 }, + { "mcp4661-104", MCP466x_104 }, + { "mcp4662-502", MCP466x_502 }, + { "mcp4662-103", MCP466x_103 }, + { "mcp4662-503", MCP466x_503 }, + { "mcp4662-104", MCP466x_104 }, {} }; MODULE_DEVICE_TABLE(i2c, mcp4531_id); @@ -218,6 +374,7 @@ MODULE_DEVICE_TABLE(i2c, mcp4531_id); static struct i2c_driver mcp4531_driver = { .driver = { .name = "mcp4531", + .of_match_table = of_match_ptr(mcp4531_of_match), }, .probe = mcp4531_probe, .id_table = mcp4531_id, diff --git a/drivers/iio/potentiometer/tpl0102.c b/drivers/iio/potentiometer/tpl0102.c index 5c304d4..7b6b545 100644 --- a/drivers/iio/potentiometer/tpl0102.c +++ b/drivers/iio/potentiometer/tpl0102.c @@ -116,10 +116,6 @@ static int tpl0102_probe(struct i2c_client *client, struct tpl0102_data *data; struct iio_dev *indio_dev; - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WORD_DATA)) - return -EOPNOTSUPP; - indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index cda9f12..d130cdc 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -6,16 +6,33 @@ menu "Pressure sensors" config BMP280 - tristate "Bosch Sensortec BMP180 and BMP280 pressure sensor driver" - depends on I2C + tristate "Bosch Sensortec BMP180/BMP280 pressure sensor I2C driver" + depends on (I2C || SPI_MASTER) depends on !(BMP085_I2C=y || BMP085_I2C=m) - select REGMAP_I2C + depends on !(BMP085_SPI=y || BMP085_SPI=m) + select REGMAP + select BMP280_I2C if (I2C) + select BMP280_SPI if (SPI_MASTER) help Say yes here to build support for Bosch Sensortec BMP180 and BMP280 - pressure and temperature sensors. + pressure and temperature sensors. Also supports the BE280 with + an additional humidity sensor channel. - To compile this driver as a module, choose M here: the module - will be called bmp280. + To compile this driver as a module, choose M here: the core module + will be called bmp280 and you will also get bmp280-i2c for I2C + and/or bmp280-spi for SPI support. + +config BMP280_I2C + tristate + depends on BMP280 + depends on I2C + select REGMAP_I2C + +config BMP280_SPI + tristate + depends on BMP280 + depends on SPI_MASTER + select REGMAP config HID_SENSOR_PRESS depends on HID_SENSOR_HUB @@ -130,7 +147,7 @@ config IIO_ST_PRESS select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) help Say yes here to build support for STMicroelectronics pressure - sensors: LPS001WP, LPS25H, LPS331AP. + sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB. This driver can also be built as a module. If so, these modules will be created: diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index 17d6e7a..7f395be 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -4,6 +4,9 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_BMP280) += bmp280.o +bmp280-objs := bmp280-core.o bmp280-regmap.o +obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o +obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o obj-$(CONFIG_HP03) += hp03.o obj-$(CONFIG_MPL115) += mpl115.o diff --git b/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c new file mode 100644 index 0000000..e5a533c --- /dev/null +++ b/drivers/iio/pressure/bmp280-core.c @@ -0,0 +1,1119 @@ +/* + * Copyright (c) 2010 Christoph Mair + * Copyright (c) 2012 Bosch Sensortec GmbH + * Copyright (c) 2012 Unixphere AB + * Copyright (c) 2014 Intel Corporation + * Copyright (c) 2016 Linus Walleij + * + * Driver for Bosch Sensortec BMP180 and BMP280 digital pressure sensor. + * + * 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. + * + * Datasheet: + * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP180-DS000-121.pdf + * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP280-DS001-12.pdf + * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280_DS001-11.pdf + */ + +#define pr_fmt(fmt) "bmp280: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For irq_get_irq_data() */ +#include +#include +#include + +#include "bmp280.h" + +/* + * These enums are used for indexing into the array of calibration + * coefficients for BMP180. + */ +enum { AC1, AC2, AC3, AC4, AC5, AC6, B1, B2, MB, MC, MD }; + +struct bmp180_calib { + s16 AC1; + s16 AC2; + s16 AC3; + u16 AC4; + u16 AC5; + u16 AC6; + s16 B1; + s16 B2; + s16 MB; + s16 MC; + s16 MD; +}; + +struct bmp280_data { + struct device *dev; + struct mutex lock; + struct regmap *regmap; + struct completion done; + bool use_eoc; + const struct bmp280_chip_info *chip_info; + struct bmp180_calib calib; + struct regulator *vddd; + struct regulator *vdda; + unsigned int start_up_time; /* in milliseconds */ + + /* log of base 2 of oversampling rate */ + u8 oversampling_press; + u8 oversampling_temp; + u8 oversampling_humid; + + /* + * Carryover value from temperature conversion, used in pressure + * calculation. + */ + s32 t_fine; +}; + +struct bmp280_chip_info { + const int *oversampling_temp_avail; + int num_oversampling_temp_avail; + + const int *oversampling_press_avail; + int num_oversampling_press_avail; + + const int *oversampling_humid_avail; + int num_oversampling_humid_avail; + + int (*chip_config)(struct bmp280_data *); + int (*read_temp)(struct bmp280_data *, int *); + int (*read_press)(struct bmp280_data *, int *, int *); + int (*read_humid)(struct bmp280_data *, int *, int *); +}; + +/* + * These enums are used for indexing into the array of compensation + * parameters for BMP280. + */ +enum { T1, T2, T3 }; +enum { P1, P2, P3, P4, P5, P6, P7, P8, P9 }; + +static const struct iio_chan_spec bmp280_channels[] = { + { + .type = IIO_PRESSURE, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + }, + { + .type = IIO_HUMIDITYRELATIVE, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + }, +}; + +/* + * Returns humidity in percent, resolution is 0.01 percent. Output value of + * "47445" represents 47445/1024 = 46.333 %RH. + * + * Taken from BME280 datasheet, Section 4.2.3, "Compensation formula". + */ + +static u32 bmp280_compensate_humidity(struct bmp280_data *data, + s32 adc_humidity) +{ + struct device *dev = data->dev; + unsigned int H1, H3, tmp; + int H2, H4, H5, H6, ret, var; + + ret = regmap_read(data->regmap, BMP280_REG_COMP_H1, &H1); + if (ret < 0) { + dev_err(dev, "failed to read H1 comp value\n"); + return ret; + } + + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H2, &tmp, 2); + if (ret < 0) { + dev_err(dev, "failed to read H2 comp value\n"); + return ret; + } + H2 = sign_extend32(le16_to_cpu(tmp), 15); + + ret = regmap_read(data->regmap, BMP280_REG_COMP_H3, &H3); + if (ret < 0) { + dev_err(dev, "failed to read H3 comp value\n"); + return ret; + } + + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H4, &tmp, 2); + if (ret < 0) { + dev_err(dev, "failed to read H4 comp value\n"); + return ret; + } + H4 = sign_extend32(((be16_to_cpu(tmp) >> 4) & 0xff0) | + (be16_to_cpu(tmp) & 0xf), 11); + + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H5, &tmp, 2); + if (ret < 0) { + dev_err(dev, "failed to read H5 comp value\n"); + return ret; + } + H5 = sign_extend32(((le16_to_cpu(tmp) >> 4) & 0xfff), 11); + + ret = regmap_read(data->regmap, BMP280_REG_COMP_H6, &tmp); + if (ret < 0) { + dev_err(dev, "failed to read H6 comp value\n"); + return ret; + } + H6 = sign_extend32(tmp, 7); + + var = ((s32)data->t_fine) - 76800; + var = ((((adc_humidity << 14) - (H4 << 20) - (H5 * var)) + 16384) >> 15) + * (((((((var * H6) >> 10) * (((var * H3) >> 11) + 32768)) >> 10) + + 2097152) * H2 + 8192) >> 14); + var -= ((((var >> 15) * (var >> 15)) >> 7) * H1) >> 4; + + return var >> 12; +}; + +/* + * Returns temperature in DegC, resolution is 0.01 DegC. Output value of + * "5123" equals 51.23 DegC. t_fine carries fine temperature as global + * value. + * + * Taken from datasheet, Section 3.11.3, "Compensation formula". + */ +static s32 bmp280_compensate_temp(struct bmp280_data *data, + s32 adc_temp) +{ + int ret; + s32 var1, var2; + __le16 buf[BMP280_COMP_TEMP_REG_COUNT / 2]; + + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_TEMP_START, + buf, BMP280_COMP_TEMP_REG_COUNT); + if (ret < 0) { + dev_err(data->dev, + "failed to read temperature calibration parameters\n"); + return ret; + } + + /* + * The double casts are necessary because le16_to_cpu returns an + * unsigned 16-bit value. Casting that value directly to a + * signed 32-bit will not do proper sign extension. + * + * Conversely, T1 and P1 are unsigned values, so they can be + * cast straight to the larger type. + */ + var1 = (((adc_temp >> 3) - ((s32)le16_to_cpu(buf[T1]) << 1)) * + ((s32)(s16)le16_to_cpu(buf[T2]))) >> 11; + var2 = (((((adc_temp >> 4) - ((s32)le16_to_cpu(buf[T1]))) * + ((adc_temp >> 4) - ((s32)le16_to_cpu(buf[T1])))) >> 12) * + ((s32)(s16)le16_to_cpu(buf[T3]))) >> 14; + data->t_fine = var1 + var2; + + return (data->t_fine * 5 + 128) >> 8; +} + +/* + * Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 + * integer bits and 8 fractional bits). Output value of "24674867" + * represents 24674867/256 = 96386.2 Pa = 963.862 hPa + * + * Taken from datasheet, Section 3.11.3, "Compensation formula". + */ +static u32 bmp280_compensate_press(struct bmp280_data *data, + s32 adc_press) +{ + int ret; + s64 var1, var2, p; + __le16 buf[BMP280_COMP_PRESS_REG_COUNT / 2]; + + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_PRESS_START, + buf, BMP280_COMP_PRESS_REG_COUNT); + if (ret < 0) { + dev_err(data->dev, + "failed to read pressure calibration parameters\n"); + return ret; + } + + var1 = ((s64)data->t_fine) - 128000; + var2 = var1 * var1 * (s64)(s16)le16_to_cpu(buf[P6]); + var2 += (var1 * (s64)(s16)le16_to_cpu(buf[P5])) << 17; + var2 += ((s64)(s16)le16_to_cpu(buf[P4])) << 35; + var1 = ((var1 * var1 * (s64)(s16)le16_to_cpu(buf[P3])) >> 8) + + ((var1 * (s64)(s16)le16_to_cpu(buf[P2])) << 12); + var1 = ((((s64)1) << 47) + var1) * ((s64)le16_to_cpu(buf[P1])) >> 33; + + if (var1 == 0) + return 0; + + p = ((((s64)1048576 - adc_press) << 31) - var2) * 3125; + p = div64_s64(p, var1); + var1 = (((s64)(s16)le16_to_cpu(buf[P9])) * (p >> 13) * (p >> 13)) >> 25; + var2 = (((s64)(s16)le16_to_cpu(buf[P8])) * p) >> 19; + p = ((p + var1 + var2) >> 8) + (((s64)(s16)le16_to_cpu(buf[P7])) << 4); + + return (u32)p; +} + +static int bmp280_read_temp(struct bmp280_data *data, + int *val) +{ + int ret; + __be32 tmp = 0; + s32 adc_temp, comp_temp; + + ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB, + (u8 *) &tmp, 3); + if (ret < 0) { + dev_err(data->dev, "failed to read temperature\n"); + return ret; + } + + adc_temp = be32_to_cpu(tmp) >> 12; + comp_temp = bmp280_compensate_temp(data, adc_temp); + + /* + * val might be NULL if we're called by the read_press routine, + * who only cares about the carry over t_fine value. + */ + if (val) { + *val = comp_temp * 10; + return IIO_VAL_INT; + } + + return 0; +} + +static int bmp280_read_press(struct bmp280_data *data, + int *val, int *val2) +{ + int ret; + __be32 tmp = 0; + s32 adc_press; + u32 comp_press; + + /* Read and compensate temperature so we get a reading of t_fine. */ + ret = bmp280_read_temp(data, NULL); + if (ret < 0) + return ret; + + ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB, + (u8 *) &tmp, 3); + if (ret < 0) { + dev_err(data->dev, "failed to read pressure\n"); + return ret; + } + + adc_press = be32_to_cpu(tmp) >> 12; + comp_press = bmp280_compensate_press(data, adc_press); + + *val = comp_press; + *val2 = 256000; + + return IIO_VAL_FRACTIONAL; +} + +static int bmp280_read_humid(struct bmp280_data *data, int *val, int *val2) +{ + int ret; + __be16 tmp = 0; + s32 adc_humidity; + u32 comp_humidity; + + /* Read and compensate temperature so we get a reading of t_fine. */ + ret = bmp280_read_temp(data, NULL); + if (ret < 0) + return ret; + + ret = regmap_bulk_read(data->regmap, BMP280_REG_HUMIDITY_MSB, + (u8 *) &tmp, 2); + if (ret < 0) { + dev_err(data->dev, "failed to read humidity\n"); + return ret; + } + + adc_humidity = be16_to_cpu(tmp); + comp_humidity = bmp280_compensate_humidity(data, adc_humidity); + + *val = comp_humidity; + *val2 = 1024; + + return IIO_VAL_FRACTIONAL; +} + +static int bmp280_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + struct bmp280_data *data = iio_priv(indio_dev); + + pm_runtime_get_sync(data->dev); + mutex_lock(&data->lock); + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_HUMIDITYRELATIVE: + ret = data->chip_info->read_humid(data, val, val2); + break; + case IIO_PRESSURE: + ret = data->chip_info->read_press(data, val, val2); + break; + case IIO_TEMP: + ret = data->chip_info->read_temp(data, val); + break; + default: + ret = -EINVAL; + break; + } + break; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + switch (chan->type) { + case IIO_HUMIDITYRELATIVE: + *val = 1 << data->oversampling_humid; + ret = IIO_VAL_INT; + break; + case IIO_PRESSURE: + *val = 1 << data->oversampling_press; + ret = IIO_VAL_INT; + break; + case IIO_TEMP: + *val = 1 << data->oversampling_temp; + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&data->lock); + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + + return ret; +} + +static int bmp280_write_oversampling_ratio_humid(struct bmp280_data *data, + int val) +{ + int i; + const int *avail = data->chip_info->oversampling_humid_avail; + const int n = data->chip_info->num_oversampling_humid_avail; + + for (i = 0; i < n; i++) { + if (avail[i] == val) { + data->oversampling_humid = ilog2(val); + + return data->chip_info->chip_config(data); + } + } + return -EINVAL; +} + +static int bmp280_write_oversampling_ratio_temp(struct bmp280_data *data, + int val) +{ + int i; + const int *avail = data->chip_info->oversampling_temp_avail; + const int n = data->chip_info->num_oversampling_temp_avail; + + for (i = 0; i < n; i++) { + if (avail[i] == val) { + data->oversampling_temp = ilog2(val); + + return data->chip_info->chip_config(data); + } + } + return -EINVAL; +} + +static int bmp280_write_oversampling_ratio_press(struct bmp280_data *data, + int val) +{ + int i; + const int *avail = data->chip_info->oversampling_press_avail; + const int n = data->chip_info->num_oversampling_press_avail; + + for (i = 0; i < n; i++) { + if (avail[i] == val) { + data->oversampling_press = ilog2(val); + + return data->chip_info->chip_config(data); + } + } + return -EINVAL; +} + +static int bmp280_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret = 0; + struct bmp280_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + pm_runtime_get_sync(data->dev); + mutex_lock(&data->lock); + switch (chan->type) { + case IIO_HUMIDITYRELATIVE: + ret = bmp280_write_oversampling_ratio_humid(data, val); + break; + case IIO_PRESSURE: + ret = bmp280_write_oversampling_ratio_press(data, val); + break; + case IIO_TEMP: + ret = bmp280_write_oversampling_ratio_temp(data, val); + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&data->lock); + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + break; + default: + return -EINVAL; + } + + return ret; +} + +static ssize_t bmp280_show_avail(char *buf, const int *vals, const int n) +{ + size_t len = 0; + int i; + + for (i = 0; i < n; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", vals[i]); + + buf[len - 1] = '\n'; + + return len; +} + +static ssize_t bmp280_show_temp_oversampling_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bmp280_data *data = iio_priv(dev_to_iio_dev(dev)); + + return bmp280_show_avail(buf, data->chip_info->oversampling_temp_avail, + data->chip_info->num_oversampling_temp_avail); +} + +static ssize_t bmp280_show_press_oversampling_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bmp280_data *data = iio_priv(dev_to_iio_dev(dev)); + + return bmp280_show_avail(buf, data->chip_info->oversampling_press_avail, + data->chip_info->num_oversampling_press_avail); +} + +static IIO_DEVICE_ATTR(in_temp_oversampling_ratio_available, + S_IRUGO, bmp280_show_temp_oversampling_avail, NULL, 0); + +static IIO_DEVICE_ATTR(in_pressure_oversampling_ratio_available, + S_IRUGO, bmp280_show_press_oversampling_avail, NULL, 0); + +static struct attribute *bmp280_attributes[] = { + &iio_dev_attr_in_temp_oversampling_ratio_available.dev_attr.attr, + &iio_dev_attr_in_pressure_oversampling_ratio_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group bmp280_attrs_group = { + .attrs = bmp280_attributes, +}; + +static const struct iio_info bmp280_info = { + .driver_module = THIS_MODULE, + .read_raw = &bmp280_read_raw, + .write_raw = &bmp280_write_raw, + .attrs = &bmp280_attrs_group, +}; + +static int bmp280_chip_config(struct bmp280_data *data) +{ + int ret; + u8 osrs = BMP280_OSRS_TEMP_X(data->oversampling_temp + 1) | + BMP280_OSRS_PRESS_X(data->oversampling_press + 1); + + ret = regmap_update_bits(data->regmap, BMP280_REG_CTRL_MEAS, + BMP280_OSRS_TEMP_MASK | + BMP280_OSRS_PRESS_MASK | + BMP280_MODE_MASK, + osrs | BMP280_MODE_NORMAL); + if (ret < 0) { + dev_err(data->dev, + "failed to write ctrl_meas register\n"); + return ret; + } + + ret = regmap_update_bits(data->regmap, BMP280_REG_CONFIG, + BMP280_FILTER_MASK, + BMP280_FILTER_4X); + if (ret < 0) { + dev_err(data->dev, + "failed to write config register\n"); + return ret; + } + + return ret; +} + +static const int bmp280_oversampling_avail[] = { 1, 2, 4, 8, 16 }; + +static const struct bmp280_chip_info bmp280_chip_info = { + .oversampling_temp_avail = bmp280_oversampling_avail, + .num_oversampling_temp_avail = ARRAY_SIZE(bmp280_oversampling_avail), + + .oversampling_press_avail = bmp280_oversampling_avail, + .num_oversampling_press_avail = ARRAY_SIZE(bmp280_oversampling_avail), + + .chip_config = bmp280_chip_config, + .read_temp = bmp280_read_temp, + .read_press = bmp280_read_press, +}; + +static int bme280_chip_config(struct bmp280_data *data) +{ + int ret = bmp280_chip_config(data); + u8 osrs = BMP280_OSRS_HUMIDITIY_X(data->oversampling_humid + 1); + + if (ret < 0) + return ret; + + return regmap_update_bits(data->regmap, BMP280_REG_CTRL_HUMIDITY, + BMP280_OSRS_HUMIDITY_MASK, osrs); +} + +static const struct bmp280_chip_info bme280_chip_info = { + .oversampling_temp_avail = bmp280_oversampling_avail, + .num_oversampling_temp_avail = ARRAY_SIZE(bmp280_oversampling_avail), + + .oversampling_press_avail = bmp280_oversampling_avail, + .num_oversampling_press_avail = ARRAY_SIZE(bmp280_oversampling_avail), + + .oversampling_humid_avail = bmp280_oversampling_avail, + .num_oversampling_humid_avail = ARRAY_SIZE(bmp280_oversampling_avail), + + .chip_config = bme280_chip_config, + .read_temp = bmp280_read_temp, + .read_press = bmp280_read_press, + .read_humid = bmp280_read_humid, +}; + +static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas) +{ + int ret; + const int conversion_time_max[] = { 4500, 7500, 13500, 25500 }; + unsigned int delay_us; + unsigned int ctrl; + + if (data->use_eoc) + init_completion(&data->done); + + ret = regmap_write(data->regmap, BMP280_REG_CTRL_MEAS, ctrl_meas); + if (ret) + return ret; + + if (data->use_eoc) { + /* + * If we have a completion interrupt, use it, wait up to + * 100ms. The longest conversion time listed is 76.5 ms for + * advanced resolution mode. + */ + ret = wait_for_completion_timeout(&data->done, + 1 + msecs_to_jiffies(100)); + if (!ret) + dev_err(data->dev, "timeout waiting for completion\n"); + } else { + if (ctrl_meas == BMP180_MEAS_TEMP) + delay_us = 4500; + else + delay_us = + conversion_time_max[data->oversampling_press]; + + usleep_range(delay_us, delay_us + 1000); + } + + ret = regmap_read(data->regmap, BMP280_REG_CTRL_MEAS, &ctrl); + if (ret) + return ret; + + /* The value of this bit reset to "0" after conversion is complete */ + if (ctrl & BMP180_MEAS_SCO) + return -EIO; + + return 0; +} + +static int bmp180_read_adc_temp(struct bmp280_data *data, int *val) +{ + int ret; + __be16 tmp = 0; + + ret = bmp180_measure(data, BMP180_MEAS_TEMP); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB, (u8 *)&tmp, 2); + if (ret) + return ret; + + *val = be16_to_cpu(tmp); + + return 0; +} + +static int bmp180_read_calib(struct bmp280_data *data, + struct bmp180_calib *calib) +{ + int ret; + int i; + __be16 buf[BMP180_REG_CALIB_COUNT / 2]; + + ret = regmap_bulk_read(data->regmap, BMP180_REG_CALIB_START, buf, + sizeof(buf)); + + if (ret < 0) + return ret; + + /* None of the words has the value 0 or 0xFFFF */ + for (i = 0; i < ARRAY_SIZE(buf); i++) { + if (buf[i] == cpu_to_be16(0) || buf[i] == cpu_to_be16(0xffff)) + return -EIO; + } + + /* Toss the calibration data into the entropy pool */ + add_device_randomness(buf, sizeof(buf)); + + calib->AC1 = be16_to_cpu(buf[AC1]); + calib->AC2 = be16_to_cpu(buf[AC2]); + calib->AC3 = be16_to_cpu(buf[AC3]); + calib->AC4 = be16_to_cpu(buf[AC4]); + calib->AC5 = be16_to_cpu(buf[AC5]); + calib->AC6 = be16_to_cpu(buf[AC6]); + calib->B1 = be16_to_cpu(buf[B1]); + calib->B2 = be16_to_cpu(buf[B2]); + calib->MB = be16_to_cpu(buf[MB]); + calib->MC = be16_to_cpu(buf[MC]); + calib->MD = be16_to_cpu(buf[MD]); + + return 0; +} + +/* + * Returns temperature in DegC, resolution is 0.1 DegC. + * t_fine carries fine temperature as global value. + * + * Taken from datasheet, Section 3.5, "Calculating pressure and temperature". + */ +static s32 bmp180_compensate_temp(struct bmp280_data *data, s32 adc_temp) +{ + s32 x1, x2; + struct bmp180_calib *calib = &data->calib; + + x1 = ((adc_temp - calib->AC6) * calib->AC5) >> 15; + x2 = (calib->MC << 11) / (x1 + calib->MD); + data->t_fine = x1 + x2; + + return (data->t_fine + 8) >> 4; +} + +static int bmp180_read_temp(struct bmp280_data *data, int *val) +{ + int ret; + s32 adc_temp, comp_temp; + + ret = bmp180_read_adc_temp(data, &adc_temp); + if (ret) + return ret; + + comp_temp = bmp180_compensate_temp(data, adc_temp); + + /* + * val might be NULL if we're called by the read_press routine, + * who only cares about the carry over t_fine value. + */ + if (val) { + *val = comp_temp * 100; + return IIO_VAL_INT; + } + + return 0; +} + +static int bmp180_read_adc_press(struct bmp280_data *data, int *val) +{ + int ret; + __be32 tmp = 0; + u8 oss = data->oversampling_press; + + ret = bmp180_measure(data, BMP180_MEAS_PRESS_X(oss)); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB, (u8 *)&tmp, 3); + if (ret) + return ret; + + *val = (be32_to_cpu(tmp) >> 8) >> (8 - oss); + + return 0; +} + +/* + * Returns pressure in Pa, resolution is 1 Pa. + * + * Taken from datasheet, Section 3.5, "Calculating pressure and temperature". + */ +static u32 bmp180_compensate_press(struct bmp280_data *data, s32 adc_press) +{ + s32 x1, x2, x3, p; + s32 b3, b6; + u32 b4, b7; + s32 oss = data->oversampling_press; + struct bmp180_calib *calib = &data->calib; + + b6 = data->t_fine - 4000; + x1 = (calib->B2 * (b6 * b6 >> 12)) >> 11; + x2 = calib->AC2 * b6 >> 11; + x3 = x1 + x2; + b3 = ((((s32)calib->AC1 * 4 + x3) << oss) + 2) / 4; + x1 = calib->AC3 * b6 >> 13; + x2 = (calib->B1 * ((b6 * b6) >> 12)) >> 16; + x3 = (x1 + x2 + 2) >> 2; + b4 = calib->AC4 * (u32)(x3 + 32768) >> 15; + b7 = ((u32)adc_press - b3) * (50000 >> oss); + if (b7 < 0x80000000) + p = (b7 * 2) / b4; + else + p = (b7 / b4) * 2; + + x1 = (p >> 8) * (p >> 8); + x1 = (x1 * 3038) >> 16; + x2 = (-7357 * p) >> 16; + + return p + ((x1 + x2 + 3791) >> 4); +} + +static int bmp180_read_press(struct bmp280_data *data, + int *val, int *val2) +{ + int ret; + s32 adc_press; + u32 comp_press; + + /* Read and compensate temperature so we get a reading of t_fine. */ + ret = bmp180_read_temp(data, NULL); + if (ret) + return ret; + + ret = bmp180_read_adc_press(data, &adc_press); + if (ret) + return ret; + + comp_press = bmp180_compensate_press(data, adc_press); + + *val = comp_press; + *val2 = 1000; + + return IIO_VAL_FRACTIONAL; +} + +static int bmp180_chip_config(struct bmp280_data *data) +{ + return 0; +} + +static const int bmp180_oversampling_temp_avail[] = { 1 }; +static const int bmp180_oversampling_press_avail[] = { 1, 2, 4, 8 }; + +static const struct bmp280_chip_info bmp180_chip_info = { + .oversampling_temp_avail = bmp180_oversampling_temp_avail, + .num_oversampling_temp_avail = + ARRAY_SIZE(bmp180_oversampling_temp_avail), + + .oversampling_press_avail = bmp180_oversampling_press_avail, + .num_oversampling_press_avail = + ARRAY_SIZE(bmp180_oversampling_press_avail), + + .chip_config = bmp180_chip_config, + .read_temp = bmp180_read_temp, + .read_press = bmp180_read_press, +}; + +static irqreturn_t bmp085_eoc_irq(int irq, void *d) +{ + struct bmp280_data *data = d; + + complete(&data->done); + + return IRQ_HANDLED; +} + +static int bmp085_fetch_eoc_irq(struct device *dev, + const char *name, + int irq, + struct bmp280_data *data) +{ + unsigned long irq_trig; + int ret; + + irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq)); + if (irq_trig != IRQF_TRIGGER_RISING) { + dev_err(dev, "non-rising trigger given for EOC interrupt, " + "trying to enforce it\n"); + irq_trig = IRQF_TRIGGER_RISING; + } + ret = devm_request_threaded_irq(dev, + irq, + bmp085_eoc_irq, + NULL, + irq_trig, + name, + data); + if (ret) { + /* Bail out without IRQ but keep the driver in place */ + dev_err(dev, "unable to request DRDY IRQ\n"); + return 0; + } + + data->use_eoc = true; + return 0; +} + +int bmp280_common_probe(struct device *dev, + struct regmap *regmap, + unsigned int chip, + const char *name, + int irq) +{ + int ret; + struct iio_dev *indio_dev; + struct bmp280_data *data; + unsigned int chip_id; + struct gpio_desc *gpiod; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + mutex_init(&data->lock); + data->dev = dev; + + indio_dev->dev.parent = dev; + indio_dev->name = name; + indio_dev->channels = bmp280_channels; + indio_dev->info = &bmp280_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + switch (chip) { + case BMP180_CHIP_ID: + indio_dev->num_channels = 2; + data->chip_info = &bmp180_chip_info; + data->oversampling_press = ilog2(8); + data->oversampling_temp = ilog2(1); + data->start_up_time = 10; + break; + case BMP280_CHIP_ID: + indio_dev->num_channels = 2; + data->chip_info = &bmp280_chip_info; + data->oversampling_press = ilog2(16); + data->oversampling_temp = ilog2(2); + data->start_up_time = 2; + break; + case BME280_CHIP_ID: + indio_dev->num_channels = 3; + data->chip_info = &bme280_chip_info; + data->oversampling_press = ilog2(16); + data->oversampling_humid = ilog2(16); + data->oversampling_temp = ilog2(2); + data->start_up_time = 2; + break; + default: + return -EINVAL; + } + + /* Bring up regulators */ + data->vddd = devm_regulator_get(dev, "vddd"); + if (IS_ERR(data->vddd)) { + dev_err(dev, "failed to get VDDD regulator\n"); + return PTR_ERR(data->vddd); + } + ret = regulator_enable(data->vddd); + if (ret) { + dev_err(dev, "failed to enable VDDD regulator\n"); + return ret; + } + data->vdda = devm_regulator_get(dev, "vdda"); + if (IS_ERR(data->vdda)) { + dev_err(dev, "failed to get VDDA regulator\n"); + ret = PTR_ERR(data->vdda); + goto out_disable_vddd; + } + ret = regulator_enable(data->vdda); + if (ret) { + dev_err(dev, "failed to enable VDDA regulator\n"); + goto out_disable_vddd; + } + /* Wait to make sure we started up properly */ + mdelay(data->start_up_time); + + /* Bring chip out of reset if there is an assigned GPIO line */ + gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + /* Deassert the signal */ + if (!IS_ERR(gpiod)) { + dev_info(dev, "release reset\n"); + gpiod_set_value(gpiod, 0); + } + + data->regmap = regmap; + ret = regmap_read(regmap, BMP280_REG_ID, &chip_id); + if (ret < 0) + goto out_disable_vdda; + if (chip_id != chip) { + dev_err(dev, "bad chip id: expected %x got %x\n", + chip, chip_id); + ret = -EINVAL; + goto out_disable_vdda; + } + + ret = data->chip_info->chip_config(data); + if (ret < 0) + goto out_disable_vdda; + + dev_set_drvdata(dev, indio_dev); + + /* + * The BMP085 and BMP180 has calibration in an E2PROM, read it out + * at probe time. It will not change. + */ + if (chip_id == BMP180_CHIP_ID) { + ret = bmp180_read_calib(data, &data->calib); + if (ret < 0) { + dev_err(data->dev, + "failed to read calibration coefficients\n"); + goto out_disable_vdda; + } + } + + /* + * Attempt to grab an optional EOC IRQ - only the BMP085 has this + * however as it happens, the BMP085 shares the chip ID of BMP180 + * so we look for an IRQ if we have that. + */ + if (irq > 0 || (chip_id == BMP180_CHIP_ID)) { + ret = bmp085_fetch_eoc_irq(dev, name, irq, data); + if (ret) + goto out_disable_vdda; + } + + /* Enable runtime PM */ + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + /* + * Set autosuspend to two orders of magnitude larger than the + * start-up time. + */ + pm_runtime_set_autosuspend_delay(dev, data->start_up_time *100); + pm_runtime_use_autosuspend(dev); + pm_runtime_put(dev); + + ret = iio_device_register(indio_dev); + if (ret) + goto out_runtime_pm_disable; + + + return 0; + +out_runtime_pm_disable: + pm_runtime_get_sync(data->dev); + pm_runtime_put_noidle(data->dev); + pm_runtime_disable(data->dev); +out_disable_vdda: + regulator_disable(data->vdda); +out_disable_vddd: + regulator_disable(data->vddd); + return ret; +} +EXPORT_SYMBOL(bmp280_common_probe); + +int bmp280_common_remove(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmp280_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + pm_runtime_get_sync(data->dev); + pm_runtime_put_noidle(data->dev); + pm_runtime_disable(data->dev); + regulator_disable(data->vdda); + regulator_disable(data->vddd); + return 0; +} +EXPORT_SYMBOL(bmp280_common_remove); + +#ifdef CONFIG_PM +static int bmp280_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmp280_data *data = iio_priv(indio_dev); + int ret; + + ret = regulator_disable(data->vdda); + if (ret) + return ret; + return regulator_disable(data->vddd); +} + +static int bmp280_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmp280_data *data = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(data->vddd); + if (ret) + return ret; + ret = regulator_enable(data->vdda); + if (ret) + return ret; + msleep(data->start_up_time); + return data->chip_info->chip_config(data); +} +#endif /* CONFIG_PM */ + +const struct dev_pm_ops bmp280_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(bmp280_runtime_suspend, + bmp280_runtime_resume, NULL) +}; +EXPORT_SYMBOL(bmp280_dev_pm_ops); + +MODULE_AUTHOR("Vlad Dogaru "); +MODULE_DESCRIPTION("Driver for Bosch Sensortec BMP180/BMP280 pressure and temperature sensor"); +MODULE_LICENSE("GPL v2"); diff --git b/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c new file mode 100644 index 0000000..03742b1 --- /dev/null +++ b/drivers/iio/pressure/bmp280-i2c.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include + +#include "bmp280.h" + +static int bmp280_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + const struct regmap_config *regmap_config; + + switch (id->driver_data) { + case BMP180_CHIP_ID: + regmap_config = &bmp180_regmap_config; + break; + case BMP280_CHIP_ID: + case BME280_CHIP_ID: + regmap_config = &bmp280_regmap_config; + break; + default: + return -EINVAL; + } + + regmap = devm_regmap_init_i2c(client, regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "failed to allocate register map\n"); + return PTR_ERR(regmap); + } + + return bmp280_common_probe(&client->dev, + regmap, + id->driver_data, + id->name, + client->irq); +} + +static int bmp280_i2c_remove(struct i2c_client *client) +{ + return bmp280_common_remove(&client->dev); +} + +static const struct acpi_device_id bmp280_acpi_i2c_match[] = { + {"BMP0280", BMP280_CHIP_ID }, + {"BMP0180", BMP180_CHIP_ID }, + {"BMP0085", BMP180_CHIP_ID }, + {"BME0280", BME280_CHIP_ID }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, bmp280_acpi_i2c_match); + +#ifdef CONFIG_OF +static const struct of_device_id bmp280_of_i2c_match[] = { + { .compatible = "bosch,bme280", .data = (void *)BME280_CHIP_ID }, + { .compatible = "bosch,bmp280", .data = (void *)BMP280_CHIP_ID }, + { .compatible = "bosch,bmp180", .data = (void *)BMP180_CHIP_ID }, + { .compatible = "bosch,bmp085", .data = (void *)BMP180_CHIP_ID }, + { }, +}; +MODULE_DEVICE_TABLE(of, bmp280_of_i2c_match); +#else +#define bmp280_of_i2c_match NULL +#endif + +static const struct i2c_device_id bmp280_i2c_id[] = { + {"bmp280", BMP280_CHIP_ID }, + {"bmp180", BMP180_CHIP_ID }, + {"bmp085", BMP180_CHIP_ID }, + {"bme280", BME280_CHIP_ID }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, bmp280_i2c_id); + +static struct i2c_driver bmp280_i2c_driver = { + .driver = { + .name = "bmp280", + .acpi_match_table = ACPI_PTR(bmp280_acpi_i2c_match), + .of_match_table = of_match_ptr(bmp280_of_i2c_match), + .pm = &bmp280_dev_pm_ops, + }, + .probe = bmp280_i2c_probe, + .remove = bmp280_i2c_remove, + .id_table = bmp280_i2c_id, +}; +module_i2c_driver(bmp280_i2c_driver); + +MODULE_AUTHOR("Vlad Dogaru "); +MODULE_DESCRIPTION("Driver for Bosch Sensortec BMP180/BMP280 pressure and temperature sensor"); +MODULE_LICENSE("GPL v2"); diff --git b/drivers/iio/pressure/bmp280-regmap.c b/drivers/iio/pressure/bmp280-regmap.c new file mode 100644 index 0000000..6807113 --- /dev/null +++ b/drivers/iio/pressure/bmp280-regmap.c @@ -0,0 +1,84 @@ +#include +#include +#include + +#include "bmp280.h" + +static bool bmp180_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMP280_REG_CTRL_MEAS: + case BMP280_REG_RESET: + return true; + default: + return false; + }; +} + +static bool bmp180_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMP180_REG_OUT_XLSB: + case BMP180_REG_OUT_LSB: + case BMP180_REG_OUT_MSB: + case BMP280_REG_CTRL_MEAS: + return true; + default: + return false; + } +} + +const struct regmap_config bmp180_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BMP180_REG_OUT_XLSB, + .cache_type = REGCACHE_RBTREE, + + .writeable_reg = bmp180_is_writeable_reg, + .volatile_reg = bmp180_is_volatile_reg, +}; +EXPORT_SYMBOL(bmp180_regmap_config); + +static bool bmp280_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMP280_REG_CONFIG: + case BMP280_REG_CTRL_HUMIDITY: + case BMP280_REG_CTRL_MEAS: + case BMP280_REG_RESET: + return true; + default: + return false; + }; +} + +static bool bmp280_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BMP280_REG_HUMIDITY_LSB: + case BMP280_REG_HUMIDITY_MSB: + case BMP280_REG_TEMP_XLSB: + case BMP280_REG_TEMP_LSB: + case BMP280_REG_TEMP_MSB: + case BMP280_REG_PRESS_XLSB: + case BMP280_REG_PRESS_LSB: + case BMP280_REG_PRESS_MSB: + case BMP280_REG_STATUS: + return true; + default: + return false; + } +} + +const struct regmap_config bmp280_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BMP280_REG_HUMIDITY_LSB, + .cache_type = REGCACHE_RBTREE, + + .writeable_reg = bmp280_is_writeable_reg, + .volatile_reg = bmp280_is_volatile_reg, +}; +EXPORT_SYMBOL(bmp280_regmap_config); diff --git b/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c new file mode 100644 index 0000000..17bc955 --- /dev/null +++ b/drivers/iio/pressure/bmp280-spi.c @@ -0,0 +1,125 @@ +/* + * SPI interface for the BMP280 driver + * + * Inspired by the older BMP085 driver drivers/misc/bmp085-spi.c + */ +#include +#include +#include +#include + +#include "bmp280.h" + +static int bmp280_regmap_spi_write(void *context, const void *data, + size_t count) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + u8 buf[2]; + + memcpy(buf, data, 2); + /* + * The SPI register address (= full register address without bit 7) and + * the write command (bit7 = RW = '0') + */ + buf[0] &= ~0x80; + + return spi_write_then_read(spi, buf, 2, NULL, 0); +} + +static int bmp280_regmap_spi_read(void *context, const void *reg, + size_t reg_size, void *val, size_t val_size) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + + return spi_write_then_read(spi, reg, reg_size, val, val_size); +} + +static struct regmap_bus bmp280_regmap_bus = { + .write = bmp280_regmap_spi_write, + .read = bmp280_regmap_spi_read, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static int bmp280_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap *regmap; + const struct regmap_config *regmap_config; + int ret; + + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret < 0) { + dev_err(&spi->dev, "spi_setup failed!\n"); + return ret; + } + + switch (id->driver_data) { + case BMP180_CHIP_ID: + regmap_config = &bmp180_regmap_config; + break; + case BMP280_CHIP_ID: + case BME280_CHIP_ID: + regmap_config = &bmp280_regmap_config; + break; + default: + return -EINVAL; + } + + regmap = devm_regmap_init(&spi->dev, + &bmp280_regmap_bus, + &spi->dev, + regmap_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "failed to allocate register map\n"); + return PTR_ERR(regmap); + } + + return bmp280_common_probe(&spi->dev, + regmap, + id->driver_data, + id->name, + spi->irq); +} + +static int bmp280_spi_remove(struct spi_device *spi) +{ + return bmp280_common_remove(&spi->dev); +} + +static const struct of_device_id bmp280_of_spi_match[] = { + { .compatible = "bosch,bmp085", }, + { .compatible = "bosch,bmp180", }, + { .compatible = "bosch,bmp181", }, + { .compatible = "bosch,bmp280", }, + { .compatible = "bosch,bme280", }, + { }, +}; +MODULE_DEVICE_TABLE(of, bmp280_of_spi_match); + +static const struct spi_device_id bmp280_spi_id[] = { + { "bmp180", BMP180_CHIP_ID }, + { "bmp181", BMP180_CHIP_ID }, + { "bmp280", BMP280_CHIP_ID }, + { "bme280", BME280_CHIP_ID }, + { } +}; +MODULE_DEVICE_TABLE(spi, bmp280_spi_id); + +static struct spi_driver bmp280_spi_driver = { + .driver = { + .name = "bmp280", + .of_match_table = bmp280_of_spi_match, + .pm = &bmp280_dev_pm_ops, + }, + .id_table = bmp280_spi_id, + .probe = bmp280_spi_probe, + .remove = bmp280_spi_remove, +}; +module_spi_driver(bmp280_spi_driver); + +MODULE_DESCRIPTION("BMP280 SPI bus driver"); +MODULE_LICENSE("GPL"); diff --git b/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h new file mode 100644 index 0000000..2c770e1 --- /dev/null +++ b/drivers/iio/pressure/bmp280.h @@ -0,0 +1,112 @@ +#include +#include +#include + +/* BMP280 specific registers */ +#define BMP280_REG_HUMIDITY_LSB 0xFE +#define BMP280_REG_HUMIDITY_MSB 0xFD +#define BMP280_REG_TEMP_XLSB 0xFC +#define BMP280_REG_TEMP_LSB 0xFB +#define BMP280_REG_TEMP_MSB 0xFA +#define BMP280_REG_PRESS_XLSB 0xF9 +#define BMP280_REG_PRESS_LSB 0xF8 +#define BMP280_REG_PRESS_MSB 0xF7 + +#define BMP280_REG_CONFIG 0xF5 +#define BMP280_REG_CTRL_MEAS 0xF4 +#define BMP280_REG_STATUS 0xF3 +#define BMP280_REG_CTRL_HUMIDITY 0xF2 + +/* Due to non linear mapping, and data sizes we can't do a bulk read */ +#define BMP280_REG_COMP_H1 0xA1 +#define BMP280_REG_COMP_H2 0xE1 +#define BMP280_REG_COMP_H3 0xE3 +#define BMP280_REG_COMP_H4 0xE4 +#define BMP280_REG_COMP_H5 0xE5 +#define BMP280_REG_COMP_H6 0xE7 + +#define BMP280_REG_COMP_TEMP_START 0x88 +#define BMP280_COMP_TEMP_REG_COUNT 6 + +#define BMP280_REG_COMP_PRESS_START 0x8E +#define BMP280_COMP_PRESS_REG_COUNT 18 + +#define BMP280_FILTER_MASK (BIT(4) | BIT(3) | BIT(2)) +#define BMP280_FILTER_OFF 0 +#define BMP280_FILTER_2X BIT(2) +#define BMP280_FILTER_4X BIT(3) +#define BMP280_FILTER_8X (BIT(3) | BIT(2)) +#define BMP280_FILTER_16X BIT(4) + +#define BMP280_OSRS_HUMIDITY_MASK (BIT(2) | BIT(1) | BIT(0)) +#define BMP280_OSRS_HUMIDITIY_X(osrs_h) ((osrs_h) << 0) +#define BMP280_OSRS_HUMIDITY_SKIP 0 +#define BMP280_OSRS_HUMIDITY_1X BMP280_OSRS_HUMIDITIY_X(1) +#define BMP280_OSRS_HUMIDITY_2X BMP280_OSRS_HUMIDITIY_X(2) +#define BMP280_OSRS_HUMIDITY_4X BMP280_OSRS_HUMIDITIY_X(3) +#define BMP280_OSRS_HUMIDITY_8X BMP280_OSRS_HUMIDITIY_X(4) +#define BMP280_OSRS_HUMIDITY_16X BMP280_OSRS_HUMIDITIY_X(5) + +#define BMP280_OSRS_TEMP_MASK (BIT(7) | BIT(6) | BIT(5)) +#define BMP280_OSRS_TEMP_SKIP 0 +#define BMP280_OSRS_TEMP_X(osrs_t) ((osrs_t) << 5) +#define BMP280_OSRS_TEMP_1X BMP280_OSRS_TEMP_X(1) +#define BMP280_OSRS_TEMP_2X BMP280_OSRS_TEMP_X(2) +#define BMP280_OSRS_TEMP_4X BMP280_OSRS_TEMP_X(3) +#define BMP280_OSRS_TEMP_8X BMP280_OSRS_TEMP_X(4) +#define BMP280_OSRS_TEMP_16X BMP280_OSRS_TEMP_X(5) + +#define BMP280_OSRS_PRESS_MASK (BIT(4) | BIT(3) | BIT(2)) +#define BMP280_OSRS_PRESS_SKIP 0 +#define BMP280_OSRS_PRESS_X(osrs_p) ((osrs_p) << 2) +#define BMP280_OSRS_PRESS_1X BMP280_OSRS_PRESS_X(1) +#define BMP280_OSRS_PRESS_2X BMP280_OSRS_PRESS_X(2) +#define BMP280_OSRS_PRESS_4X BMP280_OSRS_PRESS_X(3) +#define BMP280_OSRS_PRESS_8X BMP280_OSRS_PRESS_X(4) +#define BMP280_OSRS_PRESS_16X BMP280_OSRS_PRESS_X(5) + +#define BMP280_MODE_MASK (BIT(1) | BIT(0)) +#define BMP280_MODE_SLEEP 0 +#define BMP280_MODE_FORCED BIT(0) +#define BMP280_MODE_NORMAL (BIT(1) | BIT(0)) + +/* BMP180 specific registers */ +#define BMP180_REG_OUT_XLSB 0xF8 +#define BMP180_REG_OUT_LSB 0xF7 +#define BMP180_REG_OUT_MSB 0xF6 + +#define BMP180_REG_CALIB_START 0xAA +#define BMP180_REG_CALIB_COUNT 22 + +#define BMP180_MEAS_SCO BIT(5) +#define BMP180_MEAS_TEMP (0x0E | BMP180_MEAS_SCO) +#define BMP180_MEAS_PRESS_X(oss) ((oss) << 6 | 0x14 | BMP180_MEAS_SCO) +#define BMP180_MEAS_PRESS_1X BMP180_MEAS_PRESS_X(0) +#define BMP180_MEAS_PRESS_2X BMP180_MEAS_PRESS_X(1) +#define BMP180_MEAS_PRESS_4X BMP180_MEAS_PRESS_X(2) +#define BMP180_MEAS_PRESS_8X BMP180_MEAS_PRESS_X(3) + +/* BMP180 and BMP280 common registers */ +#define BMP280_REG_CTRL_MEAS 0xF4 +#define BMP280_REG_RESET 0xE0 +#define BMP280_REG_ID 0xD0 + +#define BMP180_CHIP_ID 0x55 +#define BMP280_CHIP_ID 0x58 +#define BME280_CHIP_ID 0x60 +#define BMP280_SOFT_RESET_VAL 0xB6 + +/* Regmap configurations */ +extern const struct regmap_config bmp180_regmap_config; +extern const struct regmap_config bmp280_regmap_config; + +/* Probe called from different transports */ +int bmp280_common_probe(struct device *dev, + struct regmap *regmap, + unsigned int chip, + const char *name, + int irq); +int bmp280_common_remove(struct device *dev); + +/* PM ops */ +extern const struct dev_pm_ops bmp280_dev_pm_ops; diff --git a/drivers/iio/pressure/hp206c.c b/drivers/iio/pressure/hp206c.c index 90f2b6e..12f769e 100644 --- a/drivers/iio/pressure/hp206c.c +++ b/drivers/iio/pressure/hp206c.c @@ -401,6 +401,7 @@ static const struct i2c_device_id hp206c_id[] = { {"hp206c"}, {} }; +MODULE_DEVICE_TABLE(i2c, hp206c_id); #ifdef CONFIG_ACPI static const struct acpi_device_id hp206c_acpi_match[] = { diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c index 01b2e0b..6392d7b 100644 --- a/drivers/iio/pressure/mpl3115.c +++ b/drivers/iio/pressure/mpl3115.c @@ -171,7 +171,7 @@ static irqreturn_t mpl3115_trigger_handler(int irq, void *p) mutex_unlock(&data->lock); iio_push_to_buffers_with_timestamp(indio_dev, buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index 76578b0..feb41f8 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -224,7 +224,8 @@ static irqreturn_t ms5611_trigger_handler(int irq, void *p) if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); err: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/pressure/ms5637.c b/drivers/iio/pressure/ms5637.c index e68052c..953ffbc 100644 --- a/drivers/iio/pressure/ms5637.c +++ b/drivers/iio/pressure/ms5637.c @@ -1,6 +1,6 @@ /* - * ms5637.c - Support for Measurement-Specialties ms5637 and ms8607 - * pressure & temperature sensor + * ms5637.c - Support for Measurement-Specialties MS5637, MS5805 + * MS5837 and MS8607 pressure & temperature sensor * * Copyright (c) 2015 Measurement-Specialties * @@ -11,6 +11,10 @@ * Datasheet: * http://www.meas-spec.com/downloads/MS5637-02BA03.pdf * Datasheet: + * http://www.meas-spec.com/downloads/MS5805-02BA01.pdf + * Datasheet: + * http://www.meas-spec.com/downloads/MS5837-30BA.pdf + * Datasheet: * http://www.meas-spec.com/downloads/MS8607-02BA01.pdf */ @@ -170,9 +174,12 @@ static int ms5637_probe(struct i2c_client *client, static const struct i2c_device_id ms5637_id[] = { {"ms5637", 0}, - {"ms8607-temppressure", 1}, + {"ms5805", 0}, + {"ms5837", 0}, + {"ms8607-temppressure", 0}, {} }; +MODULE_DEVICE_TABLE(i2c, ms5637_id); static struct i2c_driver ms5637_driver = { .probe = ms5637_probe, diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h index f5f4149..903a21e 100644 --- a/drivers/iio/pressure/st_pressure.h +++ b/drivers/iio/pressure/st_pressure.h @@ -17,6 +17,7 @@ #define LPS001WP_PRESS_DEV_NAME "lps001wp" #define LPS25H_PRESS_DEV_NAME "lps25h" #define LPS331AP_PRESS_DEV_NAME "lps331ap" +#define LPS22HB_PRESS_DEV_NAME "lps22hb" /** * struct st_sensors_platform_data - default press platform data diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 92a118c..55df9a7 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -28,6 +28,72 @@ #include #include "st_pressure.h" +/* + * About determining pressure scaling factors + * ------------------------------------------ + * + * Datasheets specify typical pressure sensitivity so that pressure is computed + * according to the following equation : + * pressure[mBar] = raw / sensitivity + * where : + * raw the 24 bits long raw sampled pressure + * sensitivity a scaling factor specified by the datasheet in LSB/mBar + * + * IIO ABI expects pressure to be expressed as kPascal, hence pressure should be + * computed according to : + * pressure[kPascal] = pressure[mBar] / 10 + * = raw / (sensitivity * 10) (1) + * + * Finally, st_press_read_raw() returns pressure scaling factor as an + * IIO_VAL_INT_PLUS_NANO with a zero integral part and "gain" as decimal part. + * Therefore, from (1), "gain" becomes : + * gain = 10^9 / (sensitivity * 10) + * = 10^8 / sensitivity + * + * About determining temperature scaling factors and offsets + * --------------------------------------------------------- + * + * Datasheets specify typical temperature sensitivity and offset so that + * temperature is computed according to the following equation : + * temp[Celsius] = offset[Celsius] + (raw / sensitivity) + * where : + * raw the 16 bits long raw sampled temperature + * offset a constant specified by the datasheet in degree Celsius + * (sometimes zero) + * sensitivity a scaling factor specified by the datasheet in LSB/Celsius + * + * IIO ABI expects temperature to be expressed as milli degree Celsius such as + * user space should compute temperature according to : + * temp[mCelsius] = temp[Celsius] * 10^3 + * = (offset[Celsius] + (raw / sensitivity)) * 10^3 + * = ((offset[Celsius] * sensitivity) + raw) * + * (10^3 / sensitivity) (2) + * + * IIO ABI expects user space to apply offset and scaling factors to raw samples + * according to : + * temp[mCelsius] = (OFFSET + raw) * SCALE + * where : + * OFFSET an arbitrary constant exposed by device + * SCALE an arbitrary scaling factor exposed by device + * + * Matching OFFSET and SCALE with members of (2) gives : + * OFFSET = offset[Celsius] * sensitivity (3) + * SCALE = 10^3 / sensitivity (4) + * + * st_press_read_raw() returns temperature scaling factor as an + * IIO_VAL_FRACTIONAL with a 10^3 numerator and "gain2" as denominator. + * Therefore, from (3), "gain2" becomes : + * gain2 = sensitivity + * + * When declared within channel, i.e. for a non zero specified offset, + * st_press_read_raw() will return the latter as an IIO_VAL_FRACTIONAL such as : + * numerator = OFFSET * 10^3 + * denominator = 10^3 + * giving from (4): + * numerator = offset[Celsius] * 10^3 * sensitivity + * = offset[mCelsius] * gain2 + */ + #define MCELSIUS_PER_CELSIUS 1000 /* Default pressure sensitivity */ @@ -39,8 +105,6 @@ #define ST_PRESS_LSB_PER_CELSIUS 480UL #define ST_PRESS_MILLI_CELSIUS_OFFSET 42500UL -#define ST_PRESS_NUMBER_DATA_CHANNELS 1 - /* FULLSCALE */ #define ST_PRESS_FS_AVL_1100MB 1100 #define ST_PRESS_FS_AVL_1260MB 1260 @@ -48,7 +112,11 @@ #define ST_PRESS_1_OUT_XL_ADDR 0x28 #define ST_TEMP_1_OUT_L_ADDR 0x2b -/* CUSTOM VALUES FOR LPS331AP SENSOR */ +/* + * CUSTOM VALUES FOR LPS331AP SENSOR + * See LPS331AP datasheet: + * http://www2.st.com/resource/en/datasheet/lps331ap.pdf + */ #define ST_PRESS_LPS331AP_WAI_EXP 0xbb #define ST_PRESS_LPS331AP_ODR_ADDR 0x20 #define ST_PRESS_LPS331AP_ODR_MASK 0x70 @@ -71,7 +139,9 @@ #define ST_PRESS_LPS331AP_OD_IRQ_MASK 0x40 #define ST_PRESS_LPS331AP_MULTIREAD_BIT true -/* CUSTOM VALUES FOR LPS001WP SENSOR */ +/* + * CUSTOM VALUES FOR THE OBSOLETE LPS001WP SENSOR + */ /* LPS001WP pressure resolution */ #define ST_PRESS_LPS001WP_LSB_PER_MBAR 16UL @@ -94,7 +164,11 @@ #define ST_PRESS_LPS001WP_OUT_L_ADDR 0x28 #define ST_TEMP_LPS001WP_OUT_L_ADDR 0x2a -/* CUSTOM VALUES FOR LPS25H SENSOR */ +/* + * CUSTOM VALUES FOR LPS25H SENSOR + * See LPS25H datasheet: + * http://www2.st.com/resource/en/datasheet/lps25h.pdf + */ #define ST_PRESS_LPS25H_WAI_EXP 0xbd #define ST_PRESS_LPS25H_ODR_ADDR 0x20 #define ST_PRESS_LPS25H_ODR_MASK 0x70 @@ -117,27 +191,54 @@ #define ST_PRESS_LPS25H_OUT_XL_ADDR 0x28 #define ST_TEMP_LPS25H_OUT_L_ADDR 0x2b +/* + * CUSTOM VALUES FOR LPS22HB SENSOR + * See LPS22HB datasheet: + * http://www2.st.com/resource/en/datasheet/lps22hb.pdf + */ + +/* LPS22HB temperature sensitivity */ +#define ST_PRESS_LPS22HB_LSB_PER_CELSIUS 100UL + +#define ST_PRESS_LPS22HB_WAI_EXP 0xb1 +#define ST_PRESS_LPS22HB_ODR_ADDR 0x10 +#define ST_PRESS_LPS22HB_ODR_MASK 0x70 +#define ST_PRESS_LPS22HB_ODR_AVL_1HZ_VAL 0x01 +#define ST_PRESS_LPS22HB_ODR_AVL_10HZ_VAL 0x02 +#define ST_PRESS_LPS22HB_ODR_AVL_25HZ_VAL 0x03 +#define ST_PRESS_LPS22HB_ODR_AVL_50HZ_VAL 0x04 +#define ST_PRESS_LPS22HB_ODR_AVL_75HZ_VAL 0x05 +#define ST_PRESS_LPS22HB_PW_ADDR 0x10 +#define ST_PRESS_LPS22HB_PW_MASK 0x70 +#define ST_PRESS_LPS22HB_BDU_ADDR 0x10 +#define ST_PRESS_LPS22HB_BDU_MASK 0x02 +#define ST_PRESS_LPS22HB_DRDY_IRQ_ADDR 0x12 +#define ST_PRESS_LPS22HB_DRDY_IRQ_INT1_MASK 0x04 +#define ST_PRESS_LPS22HB_DRDY_IRQ_INT2_MASK 0x08 +#define ST_PRESS_LPS22HB_IHL_IRQ_ADDR 0x12 +#define ST_PRESS_LPS22HB_IHL_IRQ_MASK 0x80 +#define ST_PRESS_LPS22HB_OD_IRQ_ADDR 0x12 +#define ST_PRESS_LPS22HB_OD_IRQ_MASK 0x40 +#define ST_PRESS_LPS22HB_MULTIREAD_BIT true + static const struct iio_chan_spec st_press_1_channels[] = { { .type = IIO_PRESSURE, - .channel2 = IIO_NO_MOD, .address = ST_PRESS_1_OUT_XL_ADDR, - .scan_index = ST_SENSORS_SCAN_X, + .scan_index = 0, .scan_type = { .sign = 'u', .realbits = 24, - .storagebits = 24, + .storagebits = 32, .endianness = IIO_LE, }, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), - .modified = 0, }, { .type = IIO_TEMP, - .channel2 = IIO_NO_MOD, .address = ST_TEMP_1_OUT_L_ADDR, - .scan_index = -1, + .scan_index = 1, .scan_type = { .sign = 'u', .realbits = 16, @@ -148,17 +249,15 @@ static const struct iio_chan_spec st_press_1_channels[] = { BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), - .modified = 0, }, - IIO_CHAN_SOFT_TIMESTAMP(1) + IIO_CHAN_SOFT_TIMESTAMP(2) }; static const struct iio_chan_spec st_press_lps001wp_channels[] = { { .type = IIO_PRESSURE, - .channel2 = IIO_NO_MOD, .address = ST_PRESS_LPS001WP_OUT_L_ADDR, - .scan_index = ST_SENSORS_SCAN_X, + .scan_index = 0, .scan_type = { .sign = 'u', .realbits = 16, @@ -168,13 +267,11 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), - .modified = 0, }, { .type = IIO_TEMP, - .channel2 = IIO_NO_MOD, .address = ST_TEMP_LPS001WP_OUT_L_ADDR, - .scan_index = -1, + .scan_index = 1, .scan_type = { .sign = 'u', .realbits = 16, @@ -184,9 +281,42 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), - .modified = 0, }, - IIO_CHAN_SOFT_TIMESTAMP(1) + IIO_CHAN_SOFT_TIMESTAMP(2) +}; + +static const struct iio_chan_spec st_press_lps22hb_channels[] = { + { + .type = IIO_PRESSURE, + .address = ST_PRESS_1_OUT_XL_ADDR, + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_LE, + }, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + }, + { + .type = IIO_TEMP, + .address = ST_TEMP_1_OUT_L_ADDR, + .scan_index = 1, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_LE, + }, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + }, + IIO_CHAN_SOFT_TIMESTAMP(2) }; static const struct st_sensor_settings st_press_sensors_settings[] = { @@ -346,6 +476,59 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT, .bootime = 2, }, + { + .wai = ST_PRESS_LPS22HB_WAI_EXP, + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, + .sensors_supported = { + [0] = LPS22HB_PRESS_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_press_lps22hb_channels, + .num_ch = ARRAY_SIZE(st_press_lps22hb_channels), + .odr = { + .addr = ST_PRESS_LPS22HB_ODR_ADDR, + .mask = ST_PRESS_LPS22HB_ODR_MASK, + .odr_avl = { + { 1, ST_PRESS_LPS22HB_ODR_AVL_1HZ_VAL, }, + { 10, ST_PRESS_LPS22HB_ODR_AVL_10HZ_VAL, }, + { 25, ST_PRESS_LPS22HB_ODR_AVL_25HZ_VAL, }, + { 50, ST_PRESS_LPS22HB_ODR_AVL_50HZ_VAL, }, + { 75, ST_PRESS_LPS22HB_ODR_AVL_75HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_PRESS_LPS22HB_PW_ADDR, + .mask = ST_PRESS_LPS22HB_PW_MASK, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .fs = { + .fs_avl = { + /* + * Pressure and temperature sensitivity values + * as defined in table 3 of LPS22HB datasheet. + */ + [0] = { + .num = ST_PRESS_FS_AVL_1260MB, + .gain = ST_PRESS_KPASCAL_NANO_SCALE, + .gain2 = ST_PRESS_LPS22HB_LSB_PER_CELSIUS, + }, + }, + }, + .bdu = { + .addr = ST_PRESS_LPS22HB_BDU_ADDR, + .mask = ST_PRESS_LPS22HB_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_PRESS_LPS22HB_DRDY_IRQ_ADDR, + .mask_int1 = ST_PRESS_LPS22HB_DRDY_IRQ_INT1_MASK, + .mask_int2 = ST_PRESS_LPS22HB_DRDY_IRQ_INT2_MASK, + .addr_ihl = ST_PRESS_LPS22HB_IHL_IRQ_ADDR, + .mask_ihl = ST_PRESS_LPS22HB_IHL_IRQ_MASK, + .addr_od = ST_PRESS_LPS22HB_OD_IRQ_ADDR, + .mask_od = ST_PRESS_LPS22HB_OD_IRQ_MASK, + .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + }, + .multi_read_bit = ST_PRESS_LPS22HB_MULTIREAD_BIT, + }, }; static int st_press_write_raw(struct iio_dev *indio_dev, @@ -462,23 +645,30 @@ int st_press_common_probe(struct iio_dev *indio_dev) indio_dev->info = &press_info; mutex_init(&press_data->tb.buf_lock); - st_sensors_power_enable(indio_dev); + err = st_sensors_power_enable(indio_dev); + if (err) + return err; err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_press_sensors_settings), st_press_sensors_settings); if (err < 0) - return err; - - press_data->num_data_channels = ST_PRESS_NUMBER_DATA_CHANNELS; + goto st_press_power_off; + + /* + * Skip timestamping channel while declaring available channels to + * common st_sensor layer. Look at st_sensors_get_buffer_element() to + * see how timestamps are explicitly pushed as last samples block + * element. + */ + press_data->num_data_channels = press_data->sensor_settings->num_ch - 1; press_data->multiread_bit = press_data->sensor_settings->multi_read_bit; indio_dev->channels = press_data->sensor_settings->ch; indio_dev->num_channels = press_data->sensor_settings->num_ch; - if (press_data->sensor_settings->fs.addr != 0) - press_data->current_fullscale = - (struct st_sensor_fullscale_avl *) - &press_data->sensor_settings->fs.fs_avl[0]; + press_data->current_fullscale = + (struct st_sensor_fullscale_avl *) + &press_data->sensor_settings->fs.fs_avl[0]; press_data->odr = press_data->sensor_settings->odr.odr_avl[0].hz; @@ -490,11 +680,11 @@ int st_press_common_probe(struct iio_dev *indio_dev) err = st_sensors_init_sensor(indio_dev, press_data->dev->platform_data); if (err < 0) - return err; + goto st_press_power_off; err = st_press_allocate_ring(indio_dev); if (err < 0) - return err; + goto st_press_power_off; if (irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -517,6 +707,8 @@ st_press_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_press_probe_trigger_error: st_press_deallocate_ring(indio_dev); +st_press_power_off: + st_sensors_power_disable(indio_dev); return err; } diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 8fcf976..ed18701 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -32,6 +32,10 @@ static const struct of_device_id st_press_of_match[] = { .compatible = "st,lps331ap-press", .data = LPS331AP_PRESS_DEV_NAME, }, + { + .compatible = "st,lps22hb-press", + .data = LPS22HB_PRESS_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_press_of_match); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index 40c0692..5505080 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -50,6 +50,7 @@ static const struct spi_device_id st_press_id_table[] = { { LPS001WP_PRESS_DEV_NAME }, { LPS25H_PRESS_DEV_NAME }, { LPS331AP_PRESS_DEV_NAME }, + { LPS22HB_PRESS_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_press_id_table); diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index e2f926c..5656deb 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -231,10 +231,16 @@ static void as3935_event_work(struct work_struct *work) { struct as3935_state *st; int val; + int ret; st = container_of(work, struct as3935_state, work.work); - as3935_read(st, AS3935_INT, &val); + ret = as3935_read(st, AS3935_INT, &val); + if (ret) { + dev_warn(&st->spi->dev, "read error\n"); + return; + } + val &= AS3935_INT_MASK; switch (val) { @@ -242,7 +248,7 @@ static void as3935_event_work(struct work_struct *work) iio_trigger_poll(st->trig); break; case AS3935_NOISE_INT: - dev_warn(&st->spi->dev, "noise level is too high"); + dev_warn(&st->spi->dev, "noise level is too high\n"); break; } } @@ -346,7 +352,6 @@ static int as3935_probe(struct spi_device *spi) st = iio_priv(indio_dev); st->spi = spi; - st->tune_cap = 0; spi_set_drvdata(spi, indio_dev); mutex_init(&st->lock); @@ -392,7 +397,7 @@ static int as3935_probe(struct spi_device *spi) return ret; } - ret = iio_triggered_buffer_setup(indio_dev, NULL, + ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time, &as3935_trigger_handler, NULL); if (ret) { @@ -468,4 +473,3 @@ module_spi_driver(as3935_driver); MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("AS3935 lightning sensor"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:as3935"); diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index 4f50238..3141c3c 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -203,22 +203,19 @@ static int lidar_read_raw(struct iio_dev *indio_dev, struct lidar_data *data = iio_priv(indio_dev); int ret = -EINVAL; - mutex_lock(&indio_dev->mlock); - - if (iio_buffer_enabled(indio_dev) && mask == IIO_CHAN_INFO_RAW) { - ret = -EBUSY; - goto error_busy; - } - switch (mask) { case IIO_CHAN_INFO_RAW: { u16 reg; + if (iio_device_claim_direct_mode(indio_dev)) + return -EBUSY; + ret = lidar_get_measurement(data, ®); if (!ret) { *val = reg; ret = IIO_VAL_INT; } + iio_device_release_direct_mode(indio_dev); break; } case IIO_CHAN_INFO_SCALE: @@ -228,9 +225,6 @@ static int lidar_read_raw(struct iio_dev *indio_dev, break; } -error_busy: - mutex_unlock(&indio_dev->mlock); - return ret; } @@ -244,7 +238,7 @@ static irqreturn_t lidar_trigger_handler(int irq, void *private) ret = lidar_get_measurement(data, data->buffer); if (!ret) { iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } else if (ret != -EINVAL) { dev_err(&data->client->dev, "cannot read LIDAR measurement"); } diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 66cd09a..1d74b3a 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -492,7 +492,7 @@ static void sx9500_push_events(struct iio_dev *indio_dev) dir = new_prox ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING; ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan, IIO_EV_TYPE_THRESH, dir); - iio_push_event(indio_dev, ev, iio_get_time_ns()); + iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev)); data->prox_stat[chan] = new_prox; } } @@ -669,7 +669,7 @@ static irqreturn_t sx9500_trigger_handler(int irq, void *private) } iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); out: mutex_unlock(&data->mutex); diff --git a/drivers/iio/temperature/tsys02d.c b/drivers/iio/temperature/tsys02d.c index ab6fe8f..c0a19a0 100644 --- a/drivers/iio/temperature/tsys02d.c +++ b/drivers/iio/temperature/tsys02d.c @@ -174,6 +174,7 @@ static const struct i2c_device_id tsys02d_id[] = { {"tsys02d", 0}, {} }; +MODULE_DEVICE_TABLE(i2c, tsys02d_id); static struct i2c_driver tsys02d_driver = { .probe = tsys02d_probe, diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig index 519e677..809b2e7 100644 --- a/drivers/iio/trigger/Kconfig +++ b/drivers/iio/trigger/Kconfig @@ -24,6 +24,18 @@ config IIO_INTERRUPT_TRIGGER To compile this driver as a module, choose M here: the module will be called iio-trig-interrupt. +config IIO_TIGHTLOOP_TRIGGER + tristate "A kthread based hammering loop trigger" + depends on IIO_SW_TRIGGER + help + An experimental trigger, used to allow sensors to be sampled as fast + as possible under the limitations of whatever else is going on. + Uses a tight loop in a kthread. Will only work with lower half only + trigger consumers. + + To compile this driver as a module, choose M here: the + module will be called iio-trig-loop. + config IIO_SYSFS_TRIGGER tristate "SYSFS trigger" depends on SYSFS diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile index fe06eb5..aab4dc2 100644 --- a/drivers/iio/trigger/Makefile +++ b/drivers/iio/trigger/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o +obj-$(CONFIG_IIO_TIGHTLOOP_TRIGGER) += iio-trig-loop.o diff --git b/drivers/iio/trigger/iio-trig-loop.c b/drivers/iio/trigger/iio-trig-loop.c new file mode 100644 index 0000000..dc6be28 --- /dev/null +++ b/drivers/iio/trigger/iio-trig-loop.c @@ -0,0 +1,143 @@ +/* + * Copyright 2016 Jonathan Cameron + * + * Licensed under the GPL-2. + * + * Based on a mashup of the hrtimer trigger and continuous sampling proposal of + * Gregor Boirie + * + * Note this is still rather experimental and may eat babies. + * + * Todo + * * Protect against connection of devices that 'need' the top half + * handler. + * * Work out how to run top half handlers in this context if it is + * safe to do so (timestamp grabbing for example) + * + * Tested against a max1363. Used about 33% cpu for the thread and 20% + * for generic_buffer piping to /dev/null. Watermark set at 64 on a 128 + * element kfifo buffer. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct iio_loop_info { + struct iio_sw_trigger swt; + struct task_struct *task; +}; + +static struct config_item_type iio_loop_type = { + .ct_owner = THIS_MODULE, +}; + +static int iio_loop_thread(void *data) +{ + struct iio_trigger *trig = data; + + set_freezable(); + + do { + iio_trigger_poll_chained(trig); + } while (likely(!kthread_freezable_should_stop(NULL))); + + return 0; +} + +static int iio_loop_trigger_set_state(struct iio_trigger *trig, bool state) +{ + struct iio_loop_info *loop_trig = iio_trigger_get_drvdata(trig); + + if (state) { + loop_trig->task = kthread_run(iio_loop_thread, + trig, trig->name); + if (unlikely(IS_ERR(loop_trig->task))) { + dev_err(&trig->dev, + "failed to create trigger loop thread\n"); + return PTR_ERR(loop_trig->task); + } + } else { + kthread_stop(loop_trig->task); + } + + return 0; +} + +static const struct iio_trigger_ops iio_loop_trigger_ops = { + .set_trigger_state = iio_loop_trigger_set_state, + .owner = THIS_MODULE, +}; + +static struct iio_sw_trigger *iio_trig_loop_probe(const char *name) +{ + struct iio_loop_info *trig_info; + int ret; + + trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); + if (!trig_info) + return ERR_PTR(-ENOMEM); + + trig_info->swt.trigger = iio_trigger_alloc("%s", name); + if (!trig_info->swt.trigger) { + ret = -ENOMEM; + goto err_free_trig_info; + } + + iio_trigger_set_drvdata(trig_info->swt.trigger, trig_info); + trig_info->swt.trigger->ops = &iio_loop_trigger_ops; + + ret = iio_trigger_register(trig_info->swt.trigger); + if (ret) + goto err_free_trigger; + + iio_swt_group_init_type_name(&trig_info->swt, name, &iio_loop_type); + + return &trig_info->swt; + +err_free_trigger: + iio_trigger_free(trig_info->swt.trigger); +err_free_trig_info: + kfree(trig_info); + + return ERR_PTR(ret); +} + +static int iio_trig_loop_remove(struct iio_sw_trigger *swt) +{ + struct iio_loop_info *trig_info; + + trig_info = iio_trigger_get_drvdata(swt->trigger); + + iio_trigger_unregister(swt->trigger); + iio_trigger_free(swt->trigger); + kfree(trig_info); + + return 0; +} + +static const struct iio_sw_trigger_ops iio_trig_loop_ops = { + .probe = iio_trig_loop_probe, + .remove = iio_trig_loop_remove, +}; + +static struct iio_sw_trigger_type iio_trig_loop = { + .name = "loop", + .owner = THIS_MODULE, + .ops = &iio_trig_loop_ops, +}; + +module_iio_sw_trigger_driver(iio_trig_loop); + +MODULE_AUTHOR("Jonathan Cameron "); +MODULE_DESCRIPTION("Loop based trigger for the iio subsystem"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:iio-trig-loop"); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 8ecdc38..2fb1f43 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -632,7 +632,7 @@ config TOUCHSCREEN_EDT_FT5X06 config TOUCHSCREEN_MIGOR tristate "Renesas MIGO-R touchscreen" - depends on SH_MIGOR && I2C + depends on (SH_MIGOR || COMPILE_TEST) && I2C help Say Y here to enable MIGO-R touchscreen support. @@ -1046,6 +1046,44 @@ config TOUCHSCREEN_PCAP To compile this driver as a module, choose M here: the module will be called pcap_ts. +config TOUCHSCREEN_RM_TS + tristate "Raydium I2C Touchscreen" + depends on I2C + depends on GPIOLIB || COMPILE_TEST + help + Say Y here if you have Raydium series I2C touchscreen, + such as RM32380, connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called raydium_i2c_ts. + +config TOUCHSCREEN_SILEAD + tristate "Silead I2C touchscreen" + depends on I2C + help + Say Y here if you have the Silead touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called silead. + +config TOUCHSCREEN_SIS_I2C + tristate "SiS 9200 family I2C touchscreen" + depends on I2C + select CRC_ITU_T + depends on GPIOLIB || COMPILE_TEST + help + This enables support for SiS 9200 family over I2C based touchscreens. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sis_i2c. + config TOUCHSCREEN_ST1232 tristate "Sitronix ST1232 touchscreen controllers" depends on I2C @@ -1094,6 +1132,19 @@ config TOUCHSCREEN_SUR40 To compile this driver as a module, choose M here: the module will be called sur40. +config TOUCHSCREEN_SURFACE3_SPI + tristate "Ntrig/Microsoft Surface 3 SPI touchscreen" + depends on SPI + depends on GPIOLIB || COMPILE_TEST + help + Say Y here if you have the Ntrig/Microsoft SPI touchscreen + controller chip as found on the Surface 3 in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called surface3_spi. + config TOUCHSCREEN_SX8654 tristate "Semtech SX8654 touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index f42975e..b4373d6 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -62,11 +62,15 @@ obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o +obj-$(CONFIG_TOUCHSCREEN_RM_TS) += raydium_i2c_ts.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o +obj-$(CONFIG_TOUCHSCREEN_SILEAD) += silead.o +obj-$(CONFIG_TOUCHSCREEN_SIS_I2C) += sis_i2c.o obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o obj-$(CONFIG_TOUCHSCREEN_SUN4I) += sun4i-ts.o obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o +obj-$(CONFIG_TOUCHSCREEN_SURFACE3_SPI) += surface3_spi.o obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index e4bf110..e16a446 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -595,7 +595,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, } else { input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); - touchscreen_parse_properties(input_dev, false); + touchscreen_parse_properties(input_dev, false, NULL); if (!input_abs_get_max(input_dev, ABS_PRESSURE)) { dev_err(dev, "Touchscreen pressure is not specified\n"); return ERR_PTR(-EINVAL); diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index a61b215..1ce3ecb 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -1473,7 +1473,6 @@ static int ads7846_remove(struct spi_device *spi) ads784x_hwmon_unregister(spi, ts); - regulator_disable(ts->reg); regulator_put(ts->reg); if (!ts->get_pendown_state) { diff --git a/drivers/input/touchscreen/chipone_icn8318.c b/drivers/input/touchscreen/chipone_icn8318.c index 22a6fea..0bf1406 100644 --- a/drivers/input/touchscreen/chipone_icn8318.c +++ b/drivers/input/touchscreen/chipone_icn8318.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -52,11 +53,7 @@ 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; + struct touchscreen_properties prop; }; static int icn8318_read_touch_data(struct i2c_client *client, @@ -91,7 +88,7 @@ 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; + int i, ret; ret = icn8318_read_touch_data(data->client, &touch_data); if (ret < 0) { @@ -124,22 +121,9 @@ static irqreturn_t icn8318_irq(int irq, void *dev_id) 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); - } + touchscreen_report_pos(data->input, &data->prop, + be16_to_cpu(touch->x), + be16_to_cpu(touch->y), true); } input_mt_sync_frame(data->input); @@ -200,10 +184,8 @@ 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) { @@ -223,19 +205,6 @@ static int icn8318_probe(struct i2c_client *client, 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; @@ -246,16 +215,14 @@ static int icn8318_probe(struct i2c_client *client, 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); + input_set_capability(input, EV_ABS, ABS_MT_POSITION_X); + input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y); + + touchscreen_parse_properties(input, true, &data->prop); + if (!input_abs_get_max(input, ABS_MT_POSITION_X) || + !input_abs_get_max(input, ABS_MT_POSITION_Y)) { + dev_err(dev, "Error touchscreen-size-x and/or -y missing\n"); + return -EINVAL; } error = input_mt_init_slots(input, ICN8318_MAX_TOUCHES, diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 91cda8f..79381cc 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -657,7 +657,7 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X); input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y); - touchscreen_parse_properties(input_dev, true); + touchscreen_parse_properties(input_dev, true, NULL); error = input_mt_init_slots(input_dev, CY_MAX_ID, 0); if (error) { diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 23fbe38..703e295 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -86,6 +86,7 @@ struct edt_reg_addr { struct edt_ft5x06_ts_data { struct i2c_client *client; struct input_dev *input; + struct touchscreen_properties prop; u16 num_x; u16 num_y; @@ -246,8 +247,8 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) if (!down) continue; - input_report_abs(tsdata->input, ABS_MT_POSITION_X, x); - input_report_abs(tsdata->input, ABS_MT_POSITION_Y, y); + touchscreen_report_pos(tsdata->input, &tsdata->prop, x, y, + true); } input_mt_report_pointer_emulation(tsdata->input, true); @@ -972,7 +973,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, input_set_abs_params(input, ABS_MT_POSITION_Y, 0, tsdata->num_y * 64 - 1, 0, 0); - touchscreen_parse_properties(input, true); + touchscreen_parse_properties(input, true, &tsdata->prop); error = input_mt_init_slots(input, tsdata->max_support_points, INPUT_MT_DIRECT); diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index ddf694b..fe4848b 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -169,7 +169,7 @@ static ssize_t ili210x_calibrate(struct device *dev, return count; } -static DEVICE_ATTR(calibrate, 0644, NULL, ili210x_calibrate); +static DEVICE_ATTR(calibrate, S_IWUSR, NULL, ili210x_calibrate); static struct attribute *ili210x_attributes[] = { &dev_attr_calibrate.attr, diff --git a/drivers/input/touchscreen/migor_ts.c b/drivers/input/touchscreen/migor_ts.c index c038db9..02fb119 100644 --- a/drivers/input/touchscreen/migor_ts.c +++ b/drivers/input/touchscreen/migor_ts.c @@ -202,7 +202,7 @@ static int migor_ts_remove(struct i2c_client *client) return 0; } -static int migor_ts_suspend(struct device *dev) +static int __maybe_unused migor_ts_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct migor_ts_priv *priv = i2c_get_clientdata(client); @@ -213,7 +213,7 @@ static int migor_ts_suspend(struct device *dev) return 0; } -static int migor_ts_resume(struct device *dev) +static int __maybe_unused migor_ts_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct migor_ts_priv *priv = i2c_get_clientdata(client); @@ -230,7 +230,7 @@ static const struct i2c_device_id migor_ts_id[] = { { "migor_ts", 0 }, { } }; -MODULE_DEVICE_TABLE(i2c, migor_ts); +MODULE_DEVICE_TABLE(i2c, migor_ts_id); static struct i2c_driver migor_ts_driver = { .driver = { diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c index bb6f2fe..8d7f9c8 100644 --- a/drivers/input/touchscreen/of_touchscreen.c +++ b/drivers/input/touchscreen/of_touchscreen.c @@ -55,12 +55,16 @@ static void touchscreen_set_params(struct input_dev *dev, * @input: input device that should be parsed * @multitouch: specifies whether parsed properties should be applied to * single-touch or multi-touch axes + * @prop: pointer to a struct touchscreen_properties into which to store + * axis swap and invert info for use with touchscreen_report_x_y(); + * or %NULL * * This function parses common DT properties for touchscreens and setups the * input device accordingly. The function keeps previously set up default * values if no value is specified via DT. */ -void touchscreen_parse_properties(struct input_dev *input, bool multitouch) +void touchscreen_parse_properties(struct input_dev *input, bool multitouch, + struct touchscreen_properties *prop) { struct device *dev = input->dev.parent; unsigned int axis; @@ -104,5 +108,80 @@ void touchscreen_parse_properties(struct input_dev *input, bool multitouch) &fuzz); if (data_present) touchscreen_set_params(input, axis, maximum, fuzz); + + if (!prop) + return; + + axis = multitouch ? ABS_MT_POSITION_X : ABS_X; + + prop->max_x = input_abs_get_max(input, axis); + prop->max_y = input_abs_get_max(input, axis + 1); + prop->invert_x = + device_property_read_bool(dev, "touchscreen-inverted-x"); + prop->invert_y = + device_property_read_bool(dev, "touchscreen-inverted-y"); + prop->swap_x_y = + device_property_read_bool(dev, "touchscreen-swapped-x-y"); + + if (prop->swap_x_y) + swap(input->absinfo[axis], input->absinfo[axis + 1]); } EXPORT_SYMBOL(touchscreen_parse_properties); + +static void +touchscreen_apply_prop_to_x_y(const struct touchscreen_properties *prop, + unsigned int *x, unsigned int *y) +{ + if (prop->invert_x) + *x = prop->max_x - *x; + + if (prop->invert_y) + *y = prop->max_y - *y; + + if (prop->swap_x_y) + swap(*x, *y); +} + +/** + * touchscreen_set_mt_pos - Set input_mt_pos coordinates + * @pos: input_mt_pos to set coordinates of + * @prop: pointer to a struct touchscreen_properties + * @x: X coordinate to store in pos + * @y: Y coordinate to store in pos + * + * Adjust the passed in x and y values applying any axis inversion and + * swapping requested in the passed in touchscreen_properties and store + * the result in a struct input_mt_pos. + */ +void touchscreen_set_mt_pos(struct input_mt_pos *pos, + const struct touchscreen_properties *prop, + unsigned int x, unsigned int y) +{ + touchscreen_apply_prop_to_x_y(prop, &x, &y); + pos->x = x; + pos->y = y; +} +EXPORT_SYMBOL(touchscreen_set_mt_pos); + +/** + * touchscreen_report_pos - Report touchscreen coordinates + * @input: input_device to report coordinates for + * @prop: pointer to a struct touchscreen_properties + * @x: X coordinate to report + * @y: Y coordinate to report + * @multitouch: Report coordinates on single-touch or multi-touch axes + * + * Adjust the passed in x and y values applying any axis inversion and + * swapping requested in the passed in touchscreen_properties and then + * report the resulting coordinates on the input_dev's x and y axis. + */ +void touchscreen_report_pos(struct input_dev *input, + const struct touchscreen_properties *prop, + unsigned int x, unsigned int y, + bool multitouch) +{ + touchscreen_apply_prop_to_x_y(prop, &x, &y); + input_report_abs(input, multitouch ? ABS_MT_POSITION_X : ABS_X, x); + input_report_abs(input, multitouch ? ABS_MT_POSITION_Y : ABS_Y, y); +} +EXPORT_SYMBOL(touchscreen_report_pos); diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 09523a3..d159e14 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -27,9 +27,9 @@ #include #include #include -/*#include */ #include #include +#include #define PIXCIR_MAX_SLOTS 5 /* Max fingers supported by driver */ @@ -41,19 +41,15 @@ struct pixcir_i2c_ts_data { struct gpio_desc *gpio_enable; struct gpio_desc *gpio_wake; const struct pixcir_i2c_chip_data *chip; + struct touchscreen_properties prop; int max_fingers; /* Max fingers supported in this instance */ bool running; }; -struct pixcir_touch { - int x; - int y; - int id; -}; - struct pixcir_report_data { int num_touches; - struct pixcir_touch touches[PIXCIR_MAX_SLOTS]; + struct input_mt_pos pos[PIXCIR_MAX_SLOTS]; + int ids[PIXCIR_MAX_SLOTS]; }; static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata, @@ -98,11 +94,11 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata, bufptr = &rdbuf[2]; for (i = 0; i < touch; i++) { - report->touches[i].x = (bufptr[1] << 8) | bufptr[0]; - report->touches[i].y = (bufptr[3] << 8) | bufptr[2]; - + touchscreen_set_mt_pos(&report->pos[i], &tsdata->prop, + get_unaligned_le16(bufptr), + get_unaligned_le16(bufptr + 2)); if (chip->has_hw_ids) { - report->touches[i].id = bufptr[4]; + report->ids[i] = bufptr[4]; bufptr = bufptr + 5; } else { bufptr = bufptr + 4; @@ -113,9 +109,7 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata, static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts, struct pixcir_report_data *report) { - struct input_mt_pos pos[PIXCIR_MAX_SLOTS]; int slots[PIXCIR_MAX_SLOTS]; - struct pixcir_touch *touch; int n, i, slot; struct device *dev = &ts->client->dev; const struct pixcir_i2c_chip_data *chip = ts->chip; @@ -124,24 +118,16 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts, if (n > PIXCIR_MAX_SLOTS) n = PIXCIR_MAX_SLOTS; - if (!ts->chip->has_hw_ids) { - for (i = 0; i < n; i++) { - touch = &report->touches[i]; - pos[i].x = touch->x; - pos[i].y = touch->y; - } - - input_mt_assign_slots(ts->input, slots, pos, n, 0); - } + if (!ts->chip->has_hw_ids) + input_mt_assign_slots(ts->input, slots, report->pos, n, 0); for (i = 0; i < n; i++) { - touch = &report->touches[i]; - if (chip->has_hw_ids) { - slot = input_mt_get_slot_by_key(ts->input, touch->id); + slot = input_mt_get_slot_by_key(ts->input, + report->ids[i]); if (slot < 0) { dev_dbg(dev, "no free slot for id 0x%x\n", - touch->id); + report->ids[i]); continue; } } else { @@ -149,14 +135,15 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts, } input_mt_slot(ts->input, slot); - input_mt_report_slot_state(ts->input, - MT_TOOL_FINGER, true); + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true); - input_event(ts->input, EV_ABS, ABS_MT_POSITION_X, touch->x); - input_event(ts->input, EV_ABS, ABS_MT_POSITION_Y, touch->y); + input_report_abs(ts->input, ABS_MT_POSITION_X, + report->pos[i].x); + input_report_abs(ts->input, ABS_MT_POSITION_Y, + report->pos[i].y); dev_dbg(dev, "%d: slot %d, x %d, y %d\n", - i, slot, touch->x, touch->y); + i, slot, report->pos[i].x, report->pos[i].y); } input_mt_sync_frame(ts->input); @@ -515,7 +502,7 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, } else { input_set_capability(input, EV_ABS, ABS_MT_POSITION_X); input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y); - touchscreen_parse_properties(input, true); + touchscreen_parse_properties(input, true, &tsdata->prop); if (!input_abs_get_max(input, ABS_MT_POSITION_X) || !input_abs_get_max(input, ABS_MT_POSITION_Y)) { dev_err(dev, "Touchscreen size is not specified\n"); diff --git b/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c new file mode 100644 index 0000000..a99fb5c --- /dev/null +++ b/drivers/input/touchscreen/raydium_i2c_ts.c @@ -0,0 +1,1238 @@ +/* + * Raydium touchscreen I2C driver. + * + * Copyright (C) 2012-2014, Raydium Semiconductor Corporation. + * + * 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. + * + * Raydium reserves the right to make changes without further notice + * to the materials described herein. Raydium does not assume any + * liability arising out of the application described herein. + * + * Contact Raydium Semiconductor Corporation at www.rad-ic.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Slave I2C mode */ +#define RM_BOOT_BLDR 0x02 +#define RM_BOOT_MAIN 0x03 + +/* I2C bootoloader commands */ +#define RM_CMD_BOOT_PAGE_WRT 0x0B /* send bl page write */ +#define RM_CMD_BOOT_WRT 0x11 /* send bl write */ +#define RM_CMD_BOOT_ACK 0x22 /* send ack*/ +#define RM_CMD_BOOT_CHK 0x33 /* send data check */ +#define RM_CMD_BOOT_READ 0x44 /* send wait bl data ready*/ + +#define RM_BOOT_RDY 0xFF /* bl data ready */ + +/* I2C main commands */ +#define RM_CMD_QUERY_BANK 0x2B +#define RM_CMD_DATA_BANK 0x4D +#define RM_CMD_ENTER_SLEEP 0x4E +#define RM_CMD_BANK_SWITCH 0xAA + +#define RM_RESET_MSG_ADDR 0x40000004 + +#define RM_MAX_READ_SIZE 56 +#define RM_PACKET_CRC_SIZE 2 + +/* Touch relative info */ +#define RM_MAX_RETRIES 3 +#define RM_MAX_TOUCH_NUM 10 +#define RM_BOOT_DELAY_MS 100 + +/* Offsets in contact data */ +#define RM_CONTACT_STATE_POS 0 +#define RM_CONTACT_X_POS 1 +#define RM_CONTACT_Y_POS 3 +#define RM_CONTACT_PRESSURE_POS 5 +#define RM_CONTACT_WIDTH_X_POS 6 +#define RM_CONTACT_WIDTH_Y_POS 7 + +/* Bootloader relative info */ +#define RM_BL_WRT_CMD_SIZE 3 /* bl flash wrt cmd size */ +#define RM_BL_WRT_PKG_SIZE 32 /* bl wrt pkg size */ +#define RM_BL_WRT_LEN (RM_BL_WRT_PKG_SIZE + RM_BL_WRT_CMD_SIZE) +#define RM_FW_PAGE_SIZE 128 +#define RM_MAX_FW_RETRIES 30 +#define RM_MAX_FW_SIZE 0xD000 + +#define RM_POWERON_DELAY_USEC 500 +#define RM_RESET_DELAY_MSEC 50 + +enum raydium_bl_cmd { + BL_HEADER = 0, + BL_PAGE_STR, + BL_PKG_IDX, + BL_DATA_STR, +}; + +enum raydium_bl_ack { + RAYDIUM_ACK_NULL = 0, + RAYDIUM_WAIT_READY, + RAYDIUM_PATH_READY, +}; + +enum raydium_boot_mode { + RAYDIUM_TS_MAIN = 0, + RAYDIUM_TS_BLDR, +}; + +/* Response to RM_CMD_DATA_BANK request */ +struct raydium_data_info { + __le32 data_bank_addr; + u8 pkg_size; + u8 tp_info_size; +}; + +struct raydium_info { + __le32 hw_ver; /*device version */ + u8 main_ver; + u8 sub_ver; + __le16 ft_ver; /* test version */ + u8 x_num; + u8 y_num; + __le16 x_max; + __le16 y_max; + u8 x_res; /* units/mm */ + u8 y_res; /* units/mm */ +}; + +/* struct raydium_data - represents state of Raydium touchscreen device */ +struct raydium_data { + struct i2c_client *client; + struct input_dev *input; + + struct regulator *avdd; + struct regulator *vccio; + struct gpio_desc *reset_gpio; + + struct raydium_info info; + + struct mutex sysfs_mutex; + + u8 *report_data; + + u32 data_bank_addr; + u8 report_size; + u8 contact_size; + u8 pkg_size; + + enum raydium_boot_mode boot_mode; + + bool wake_irq_enabled; +}; + +static int raydium_i2c_send(struct i2c_client *client, + u8 addr, const void *data, size_t len) +{ + u8 *buf; + int tries = 0; + int ret; + + buf = kmalloc(len + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = addr; + memcpy(buf + 1, data, len); + + do { + ret = i2c_master_send(client, buf, len + 1); + if (likely(ret == len + 1)) + break; + + msleep(20); + } while (++tries < RM_MAX_RETRIES); + + kfree(buf); + + if (unlikely(ret != len + 1)) { + if (ret >= 0) + ret = -EIO; + dev_err(&client->dev, "%s failed: %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static int raydium_i2c_read(struct i2c_client *client, + u8 addr, void *data, size_t len) +{ + struct i2c_msg xfer[] = { + { + .addr = client->addr, + .len = 1, + .buf = &addr, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = data, + } + }; + int ret; + + ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer)); + if (unlikely(ret != ARRAY_SIZE(xfer))) + return ret < 0 ? ret : -EIO; + + return 0; +} + +static int raydium_i2c_read_message(struct i2c_client *client, + u32 addr, void *data, size_t len) +{ + __be32 be_addr; + size_t xfer_len; + int error; + + while (len) { + xfer_len = min_t(size_t, len, RM_MAX_READ_SIZE); + + be_addr = cpu_to_be32(addr); + + error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH, + &be_addr, sizeof(be_addr)); + if (!error) + error = raydium_i2c_read(client, addr & 0xff, + data, xfer_len); + if (error) + return error; + + len -= xfer_len; + data += xfer_len; + addr += xfer_len; + } + + return 0; +} + +static int raydium_i2c_send_message(struct i2c_client *client, + u32 addr, const void *data, size_t len) +{ + __be32 be_addr = cpu_to_be32(addr); + int error; + + error = raydium_i2c_send(client, RM_CMD_BANK_SWITCH, + &be_addr, sizeof(be_addr)); + if (!error) + error = raydium_i2c_send(client, addr & 0xff, data, len); + + return error; +} + +static int raydium_i2c_sw_reset(struct i2c_client *client) +{ + const u8 soft_rst_cmd = 0x01; + int error; + + error = raydium_i2c_send_message(client, RM_RESET_MSG_ADDR, + &soft_rst_cmd, sizeof(soft_rst_cmd)); + if (error) { + dev_err(&client->dev, "software reset failed: %d\n", error); + return error; + } + + msleep(RM_RESET_DELAY_MSEC); + + return 0; +} + +static int raydium_i2c_query_ts_info(struct raydium_data *ts) +{ + struct i2c_client *client = ts->client; + struct raydium_data_info data_info; + __le32 query_bank_addr; + + int error, retry_cnt; + + for (retry_cnt = 0; retry_cnt < RM_MAX_RETRIES; retry_cnt++) { + error = raydium_i2c_read(client, RM_CMD_DATA_BANK, + &data_info, sizeof(data_info)); + if (error) + continue; + + /* + * Warn user if we already allocated memory for reports and + * then the size changed (due to firmware update?) and keep + * old size instead. + */ + if (ts->report_data && ts->pkg_size != data_info.pkg_size) { + dev_warn(&client->dev, + "report size changes, was: %d, new: %d\n", + ts->pkg_size, data_info.pkg_size); + } else { + ts->pkg_size = data_info.pkg_size; + ts->report_size = ts->pkg_size - RM_PACKET_CRC_SIZE; + } + + ts->contact_size = data_info.tp_info_size; + ts->data_bank_addr = le32_to_cpu(data_info.data_bank_addr); + + dev_dbg(&client->dev, + "data_bank_addr: %#08x, report_size: %d, contact_size: %d\n", + ts->data_bank_addr, ts->report_size, ts->contact_size); + + error = raydium_i2c_read(client, RM_CMD_QUERY_BANK, + &query_bank_addr, + sizeof(query_bank_addr)); + if (error) + continue; + + error = raydium_i2c_read_message(client, + le32_to_cpu(query_bank_addr), + &ts->info, sizeof(ts->info)); + if (error) + continue; + + return 0; + } + + dev_err(&client->dev, "failed to query device parameters: %d\n", error); + return error; +} + +static int raydium_i2c_check_fw_status(struct raydium_data *ts) +{ + struct i2c_client *client = ts->client; + static const u8 bl_ack = 0x62; + static const u8 main_ack = 0x66; + u8 buf[4]; + int error; + + error = raydium_i2c_read(client, RM_CMD_BOOT_READ, buf, sizeof(buf)); + if (!error) { + if (buf[0] == bl_ack) + ts->boot_mode = RAYDIUM_TS_BLDR; + else if (buf[0] == main_ack) + ts->boot_mode = RAYDIUM_TS_MAIN; + return 0; + } + + return error; +} + +static int raydium_i2c_initialize(struct raydium_data *ts) +{ + struct i2c_client *client = ts->client; + int error, retry_cnt; + + for (retry_cnt = 0; retry_cnt < RM_MAX_RETRIES; retry_cnt++) { + /* Wait for Hello packet */ + msleep(RM_BOOT_DELAY_MS); + + error = raydium_i2c_check_fw_status(ts); + if (error) { + dev_err(&client->dev, + "failed to read 'hello' packet: %d\n", error); + continue; + } + + if (ts->boot_mode == RAYDIUM_TS_BLDR || + ts->boot_mode == RAYDIUM_TS_MAIN) { + break; + } + } + + if (error) + ts->boot_mode = RAYDIUM_TS_BLDR; + + if (ts->boot_mode == RAYDIUM_TS_BLDR) { + ts->info.hw_ver = cpu_to_le32(0xffffffffUL); + ts->info.main_ver = 0xff; + ts->info.sub_ver = 0xff; + } else { + raydium_i2c_query_ts_info(ts); + } + + return error; +} + +static int raydium_i2c_bl_chk_state(struct i2c_client *client, + enum raydium_bl_ack state) +{ + static const u8 ack_ok[] = { 0xFF, 0x39, 0x30, 0x30, 0x54 }; + u8 rbuf[sizeof(ack_ok)]; + u8 retry; + int error; + + for (retry = 0; retry < RM_MAX_FW_RETRIES; retry++) { + switch (state) { + case RAYDIUM_ACK_NULL: + return 0; + + case RAYDIUM_WAIT_READY: + error = raydium_i2c_read(client, RM_CMD_BOOT_CHK, + &rbuf[0], 1); + if (!error && rbuf[0] == RM_BOOT_RDY) + return 0; + + break; + + case RAYDIUM_PATH_READY: + error = raydium_i2c_read(client, RM_CMD_BOOT_CHK, + rbuf, sizeof(rbuf)); + if (!error && !memcmp(rbuf, ack_ok, sizeof(ack_ok))) + return 0; + + break; + + default: + dev_err(&client->dev, "%s: invalid target state %d\n", + __func__, state); + return -EINVAL; + } + + msleep(20); + } + + return -ETIMEDOUT; +} + +static int raydium_i2c_write_object(struct i2c_client *client, + const void *data, size_t len, + enum raydium_bl_ack state) +{ + int error; + + error = raydium_i2c_send(client, RM_CMD_BOOT_WRT, data, len); + if (error) { + dev_err(&client->dev, "WRT obj command failed: %d\n", + error); + return error; + } + + error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, NULL, 0); + if (error) { + dev_err(&client->dev, "Ack obj command failed: %d\n", error); + return error; + } + + error = raydium_i2c_bl_chk_state(client, state); + if (error) { + dev_err(&client->dev, "BL check state failed: %d\n", error); + return error; + } + return 0; +} + +static bool raydium_i2c_boot_trigger(struct i2c_client *client) +{ + static const u8 cmd[7][6] = { + { 0x08, 0x0C, 0x09, 0x00, 0x50, 0xD7 }, + { 0x08, 0x04, 0x09, 0x00, 0x50, 0xA5 }, + { 0x08, 0x04, 0x09, 0x00, 0x50, 0x00 }, + { 0x08, 0x04, 0x09, 0x00, 0x50, 0xA5 }, + { 0x08, 0x0C, 0x09, 0x00, 0x50, 0x00 }, + { 0x06, 0x01, 0x00, 0x00, 0x00, 0x00 }, + { 0x02, 0xA2, 0x00, 0x00, 0x00, 0x00 }, + }; + int i; + int error; + + for (i = 0; i < 7; i++) { + error = raydium_i2c_write_object(client, cmd[i], sizeof(cmd[i]), + RAYDIUM_WAIT_READY); + if (error) { + dev_err(&client->dev, + "boot trigger failed at step %d: %d\n", + i, error); + return error; + } + } + + return 0; +} + +static bool raydium_i2c_fw_trigger(struct i2c_client *client) +{ + static const u8 cmd[5][11] = { + { 0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0xD7, 0, 0, 0 }, + { 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0 }, + { 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0x00, 0, 0, 0 }, + { 0, 0x09, 0x71, 0x04, 0x09, 0x00, 0x50, 0xA5, 0, 0, 0 }, + { 0, 0x09, 0x71, 0x0C, 0x09, 0x00, 0x50, 0x00, 0, 0, 0 }, + }; + int i; + int error; + + for (i = 0; i < 5; i++) { + error = raydium_i2c_write_object(client, cmd[i], sizeof(cmd[i]), + RAYDIUM_ACK_NULL); + if (error) { + dev_err(&client->dev, + "fw trigger failed at step %d: %d\n", + i, error); + return error; + } + } + + return 0; +} + +static int raydium_i2c_check_path(struct i2c_client *client) +{ + static const u8 cmd[] = { 0x09, 0x00, 0x09, 0x00, 0x50, 0x10, 0x00 }; + int error; + + error = raydium_i2c_write_object(client, cmd, sizeof(cmd), + RAYDIUM_PATH_READY); + if (error) { + dev_err(&client->dev, "check path command failed: %d\n", error); + return error; + } + + return 0; +} + +static int raydium_i2c_enter_bl(struct i2c_client *client) +{ + static const u8 cal_cmd[] = { 0x00, 0x01, 0x52 }; + int error; + + error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd), + RAYDIUM_ACK_NULL); + if (error) { + dev_err(&client->dev, "enter bl command failed: %d\n", error); + return error; + } + + msleep(RM_BOOT_DELAY_MS); + return 0; +} + +static int raydium_i2c_leave_bl(struct i2c_client *client) +{ + static const u8 leave_cmd[] = { 0x05, 0x00 }; + int error; + + error = raydium_i2c_write_object(client, leave_cmd, sizeof(leave_cmd), + RAYDIUM_ACK_NULL); + if (error) { + dev_err(&client->dev, "leave bl command failed: %d\n", error); + return error; + } + + msleep(RM_BOOT_DELAY_MS); + return 0; +} + +static int raydium_i2c_write_checksum(struct i2c_client *client, + size_t length, u16 checksum) +{ + u8 checksum_cmd[] = { 0x00, 0x05, 0x6D, 0x00, 0x00, 0x00, 0x00 }; + int error; + + put_unaligned_le16(length, &checksum_cmd[3]); + put_unaligned_le16(checksum, &checksum_cmd[5]); + + error = raydium_i2c_write_object(client, + checksum_cmd, sizeof(checksum_cmd), + RAYDIUM_ACK_NULL); + if (error) { + dev_err(&client->dev, "failed to write checksum: %d\n", + error); + return error; + } + + return 0; +} + +static int raydium_i2c_disable_watch_dog(struct i2c_client *client) +{ + static const u8 cmd[] = { 0x0A, 0xAA }; + int error; + + error = raydium_i2c_write_object(client, cmd, sizeof(cmd), + RAYDIUM_WAIT_READY); + if (error) { + dev_err(&client->dev, "disable watchdog command failed: %d\n", + error); + return error; + } + + return 0; +} + +static int raydium_i2c_fw_write_page(struct i2c_client *client, + u16 page_idx, const void *data, size_t len) +{ + u8 buf[RM_BL_WRT_LEN]; + size_t xfer_len; + int error; + int i; + + BUILD_BUG_ON((RM_FW_PAGE_SIZE % RM_BL_WRT_PKG_SIZE) != 0); + + for (i = 0; i < RM_FW_PAGE_SIZE / RM_BL_WRT_PKG_SIZE; i++) { + buf[BL_HEADER] = RM_CMD_BOOT_PAGE_WRT; + buf[BL_PAGE_STR] = page_idx ? 0xff : 0; + buf[BL_PKG_IDX] = i + 1; + + xfer_len = min_t(size_t, len, RM_BL_WRT_PKG_SIZE); + memcpy(&buf[BL_DATA_STR], data, xfer_len); + if (len < RM_BL_WRT_PKG_SIZE) + memset(&buf[BL_DATA_STR + xfer_len], 0xff, + RM_BL_WRT_PKG_SIZE - xfer_len); + + error = raydium_i2c_write_object(client, buf, RM_BL_WRT_LEN, + RAYDIUM_WAIT_READY); + if (error) { + dev_err(&client->dev, + "page write command failed for page %d, chunk %d: %d\n", + page_idx, i, error); + return error; + } + + data += xfer_len; + len -= xfer_len; + } + + return error; +} + +static u16 raydium_calc_chksum(const u8 *buf, u16 len) +{ + u16 checksum = 0; + u16 i; + + for (i = 0; i < len; i++) + checksum += buf[i]; + + return checksum; +} + +static int raydium_i2c_do_update_firmware(struct raydium_data *ts, + const struct firmware *fw) +{ + struct i2c_client *client = ts->client; + const void *data; + size_t data_len; + size_t len; + int page_nr; + int i; + int error; + u16 fw_checksum; + + if (fw->size == 0 || fw->size > RM_MAX_FW_SIZE) { + dev_err(&client->dev, "Invalid firmware length\n"); + return -EINVAL; + } + + error = raydium_i2c_check_fw_status(ts); + if (error) { + dev_err(&client->dev, "Unable to access IC %d\n", error); + return error; + } + + if (ts->boot_mode == RAYDIUM_TS_MAIN) { + for (i = 0; i < RM_MAX_RETRIES; i++) { + error = raydium_i2c_enter_bl(client); + if (!error) { + error = raydium_i2c_check_fw_status(ts); + if (error) { + dev_err(&client->dev, + "unable to access IC: %d\n", + error); + return error; + } + + if (ts->boot_mode == RAYDIUM_TS_BLDR) + break; + } + } + + if (ts->boot_mode == RAYDIUM_TS_MAIN) { + dev_err(&client->dev, + "failied to jump to boot loader: %d\n", + error); + return -EIO; + } + } + + error = raydium_i2c_disable_watch_dog(client); + if (error) + return error; + + error = raydium_i2c_check_path(client); + if (error) + return error; + + error = raydium_i2c_boot_trigger(client); + if (error) { + dev_err(&client->dev, "send boot trigger fail: %d\n", error); + return error; + } + + msleep(RM_BOOT_DELAY_MS); + + data = fw->data; + data_len = fw->size; + page_nr = 0; + + while (data_len) { + len = min_t(size_t, data_len, RM_FW_PAGE_SIZE); + + error = raydium_i2c_fw_write_page(client, page_nr++, data, len); + if (error) + return error; + + msleep(20); + + data += len; + data_len -= len; + } + + error = raydium_i2c_leave_bl(client); + if (error) { + dev_err(&client->dev, + "failed to leave boot loader: %d\n", error); + return error; + } + + dev_dbg(&client->dev, "left boot loader mode\n"); + msleep(RM_BOOT_DELAY_MS); + + error = raydium_i2c_check_fw_status(ts); + if (error) { + dev_err(&client->dev, + "failed to check fw status after write: %d\n", + error); + return error; + } + + if (ts->boot_mode != RAYDIUM_TS_MAIN) { + dev_err(&client->dev, + "failed to switch to main fw after writing firmware: %d\n", + error); + return -EINVAL; + } + + error = raydium_i2c_fw_trigger(client); + if (error) { + dev_err(&client->dev, "failed to trigger fw: %d\n", error); + return error; + } + + fw_checksum = raydium_calc_chksum(fw->data, fw->size); + + error = raydium_i2c_write_checksum(client, fw->size, fw_checksum); + if (error) + return error; + + return 0; +} + +static int raydium_i2c_fw_update(struct raydium_data *ts) +{ + struct i2c_client *client = ts->client; + const struct firmware *fw = NULL; + const char *fw_file = "raydium.fw"; + int error; + + error = request_firmware(&fw, fw_file, &client->dev); + if (error) { + dev_err(&client->dev, "Unable to open firmware %s\n", fw_file); + return error; + } + + disable_irq(client->irq); + + error = raydium_i2c_do_update_firmware(ts, fw); + if (error) { + dev_err(&client->dev, "firmware update failed: %d\n", error); + ts->boot_mode = RAYDIUM_TS_BLDR; + goto out_enable_irq; + } + + error = raydium_i2c_initialize(ts); + if (error) { + dev_err(&client->dev, + "failed to initialize device after firmware update: %d\n", + error); + ts->boot_mode = RAYDIUM_TS_BLDR; + goto out_enable_irq; + } + + ts->boot_mode = RAYDIUM_TS_MAIN; + +out_enable_irq: + enable_irq(client->irq); + msleep(100); + + release_firmware(fw); + + return error; +} + +static void raydium_mt_event(struct raydium_data *ts) +{ + int i; + + for (i = 0; i < ts->report_size / ts->contact_size; i++) { + u8 *contact = &ts->report_data[ts->contact_size * i]; + bool state = contact[RM_CONTACT_STATE_POS]; + u8 wx, wy; + + input_mt_slot(ts->input, i); + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, state); + + if (!state) + continue; + + input_report_abs(ts->input, ABS_MT_POSITION_X, + get_unaligned_le16(&contact[RM_CONTACT_X_POS])); + input_report_abs(ts->input, ABS_MT_POSITION_Y, + get_unaligned_le16(&contact[RM_CONTACT_Y_POS])); + input_report_abs(ts->input, ABS_MT_PRESSURE, + contact[RM_CONTACT_PRESSURE_POS]); + + wx = contact[RM_CONTACT_WIDTH_X_POS]; + wy = contact[RM_CONTACT_WIDTH_Y_POS]; + + input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, max(wx, wy)); + input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, min(wx, wy)); + } + + input_mt_sync_frame(ts->input); + input_sync(ts->input); +} + +static irqreturn_t raydium_i2c_irq(int irq, void *_dev) +{ + struct raydium_data *ts = _dev; + int error; + u16 fw_crc; + u16 calc_crc; + + if (ts->boot_mode != RAYDIUM_TS_MAIN) + goto out; + + error = raydium_i2c_read_message(ts->client, ts->data_bank_addr, + ts->report_data, ts->pkg_size); + if (error) + goto out; + + fw_crc = get_unaligned_le16(&ts->report_data[ts->report_size]); + calc_crc = raydium_calc_chksum(ts->report_data, ts->report_size); + if (unlikely(fw_crc != calc_crc)) { + dev_warn(&ts->client->dev, + "%s: invalid packet crc %#04x vs %#04x\n", + __func__, calc_crc, fw_crc); + goto out; + } + + raydium_mt_event(ts); + +out: + return IRQ_HANDLED; +} + +static ssize_t raydium_i2c_fw_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct raydium_data *ts = i2c_get_clientdata(client); + + return sprintf(buf, "%d.%d\n", ts->info.main_ver, ts->info.sub_ver); +} + +static ssize_t raydium_i2c_hw_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct raydium_data *ts = i2c_get_clientdata(client); + + return sprintf(buf, "%#04x\n", le32_to_cpu(ts->info.hw_ver)); +} + +static ssize_t raydium_i2c_boot_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct raydium_data *ts = i2c_get_clientdata(client); + + return sprintf(buf, "%s\n", + ts->boot_mode == RAYDIUM_TS_MAIN ? + "Normal" : "Recovery"); +} + +static ssize_t raydium_i2c_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct raydium_data *ts = i2c_get_clientdata(client); + int error; + + error = mutex_lock_interruptible(&ts->sysfs_mutex); + if (error) + return error; + + error = raydium_i2c_fw_update(ts); + + mutex_unlock(&ts->sysfs_mutex); + + return error ?: count; +} + +static ssize_t raydium_i2c_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct raydium_data *ts = i2c_get_clientdata(client); + static const u8 cal_cmd[] = { 0x00, 0x01, 0x9E }; + int error; + + error = mutex_lock_interruptible(&ts->sysfs_mutex); + if (error) + return error; + + error = raydium_i2c_write_object(client, cal_cmd, sizeof(cal_cmd), + RAYDIUM_WAIT_READY); + if (error) + dev_err(&client->dev, "calibrate command failed: %d\n", error); + + mutex_unlock(&ts->sysfs_mutex); + return error ?: count; +} + +static DEVICE_ATTR(fw_version, S_IRUGO, raydium_i2c_fw_ver_show, NULL); +static DEVICE_ATTR(hw_version, S_IRUGO, raydium_i2c_hw_ver_show, NULL); +static DEVICE_ATTR(boot_mode, S_IRUGO, raydium_i2c_boot_mode_show, NULL); +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, raydium_i2c_update_fw_store); +static DEVICE_ATTR(calibrate, S_IWUSR, NULL, raydium_i2c_calibrate_store); + +static struct attribute *raydium_i2c_attributes[] = { + &dev_attr_update_fw.attr, + &dev_attr_boot_mode.attr, + &dev_attr_fw_version.attr, + &dev_attr_hw_version.attr, + &dev_attr_calibrate.attr, + NULL +}; + +static struct attribute_group raydium_i2c_attribute_group = { + .attrs = raydium_i2c_attributes, +}; + +static void raydium_i2c_remove_sysfs_group(void *_data) +{ + struct raydium_data *ts = _data; + + sysfs_remove_group(&ts->client->dev.kobj, &raydium_i2c_attribute_group); +} + +static int raydium_i2c_power_on(struct raydium_data *ts) +{ + int error; + + if (!ts->reset_gpio) + return 0; + + gpiod_set_value_cansleep(ts->reset_gpio, 1); + + error = regulator_enable(ts->avdd); + if (error) { + dev_err(&ts->client->dev, + "failed to enable avdd regulator: %d\n", error); + goto release_reset_gpio; + } + + error = regulator_enable(ts->vccio); + if (error) { + regulator_disable(ts->avdd); + dev_err(&ts->client->dev, + "failed to enable vccio regulator: %d\n", error); + goto release_reset_gpio; + } + + udelay(RM_POWERON_DELAY_USEC); + +release_reset_gpio: + gpiod_set_value_cansleep(ts->reset_gpio, 0); + + if (error) + return error; + + msleep(RM_RESET_DELAY_MSEC); + + return 0; +} + +static void raydium_i2c_power_off(void *_data) +{ + struct raydium_data *ts = _data; + + if (ts->reset_gpio) { + gpiod_set_value_cansleep(ts->reset_gpio, 1); + regulator_disable(ts->vccio); + regulator_disable(ts->avdd); + } +} + +static int raydium_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + union i2c_smbus_data dummy; + struct raydium_data *ts; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, + "i2c check functionality error (need I2C_FUNC_I2C)\n"); + return -ENXIO; + } + + ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + mutex_init(&ts->sysfs_mutex); + + ts->client = client; + i2c_set_clientdata(client, ts); + + ts->avdd = devm_regulator_get(&client->dev, "avdd"); + if (IS_ERR(ts->avdd)) { + error = PTR_ERR(ts->avdd); + if (error != -EPROBE_DEFER) + dev_err(&client->dev, + "Failed to get 'avdd' regulator: %d\n", error); + return error; + } + + ts->vccio = devm_regulator_get(&client->dev, "vccio"); + if (IS_ERR(ts->vccio)) { + error = PTR_ERR(ts->vccio); + if (error != -EPROBE_DEFER) + dev_err(&client->dev, + "Failed to get 'vccio' regulator: %d\n", error); + return error; + } + + ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(ts->reset_gpio)) { + error = PTR_ERR(ts->reset_gpio); + if (error != -EPROBE_DEFER) + dev_err(&client->dev, + "failed to get reset gpio: %d\n", error); + return error; + } + + error = raydium_i2c_power_on(ts); + if (error) + return error; + + error = devm_add_action(&client->dev, raydium_i2c_power_off, ts); + if (error) { + dev_err(&client->dev, + "failed to install power off action: %d\n", error); + raydium_i2c_power_off(ts); + return error; + } + + /* Make sure there is something at this address */ + if (i2c_smbus_xfer(client->adapter, client->addr, 0, + I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) { + dev_err(&client->dev, "nothing at this address\n"); + return -ENXIO; + } + + error = raydium_i2c_initialize(ts); + if (error) { + dev_err(&client->dev, "failed to initialize: %d\n", error); + return error; + } + + ts->report_data = devm_kmalloc(&client->dev, + ts->pkg_size, GFP_KERNEL); + if (!ts->report_data) + return -ENOMEM; + + ts->input = devm_input_allocate_device(&client->dev); + if (!ts->input) { + dev_err(&client->dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + ts->input->name = "Raydium Touchscreen"; + ts->input->id.bustype = BUS_I2C; + + input_set_drvdata(ts->input, ts); + + input_set_abs_params(ts->input, ABS_MT_POSITION_X, + 0, le16_to_cpu(ts->info.x_max), 0, 0); + input_set_abs_params(ts->input, ABS_MT_POSITION_Y, + 0, le16_to_cpu(ts->info.y_max), 0, 0); + input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->info.x_res); + input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->info.y_res); + + input_set_abs_params(ts->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ts->input, ABS_MT_PRESSURE, 0, 255, 0, 0); + + error = input_mt_init_slots(ts->input, RM_MAX_TOUCH_NUM, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) { + dev_err(&client->dev, + "failed to initialize MT slots: %d\n", error); + return error; + } + + error = input_register_device(ts->input); + if (error) { + dev_err(&client->dev, + "unable to register input device: %d\n", error); + return error; + } + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, raydium_i2c_irq, + IRQF_ONESHOT, client->name, ts); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + return error; + } + + error = sysfs_create_group(&client->dev.kobj, + &raydium_i2c_attribute_group); + if (error) { + dev_err(&client->dev, "failed to create sysfs attributes: %d\n", + error); + return error; + } + + error = devm_add_action(&client->dev, + raydium_i2c_remove_sysfs_group, ts); + if (error) { + raydium_i2c_remove_sysfs_group(ts); + dev_err(&client->dev, + "Failed to add sysfs cleanup action: %d\n", error); + return error; + } + + return 0; +} + +static void __maybe_unused raydium_enter_sleep(struct i2c_client *client) +{ + static const u8 sleep_cmd[] = { 0x5A, 0xff, 0x00, 0x0f }; + int error; + + error = raydium_i2c_send(client, RM_CMD_ENTER_SLEEP, + sleep_cmd, sizeof(sleep_cmd)); + if (error) + dev_err(&client->dev, + "sleep command failed: %d\n", error); +} + +static int __maybe_unused raydium_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct raydium_data *ts = i2c_get_clientdata(client); + + /* Sleep is not available in BLDR recovery mode */ + if (ts->boot_mode != RAYDIUM_TS_MAIN) + return -EBUSY; + + disable_irq(client->irq); + + if (device_may_wakeup(dev)) { + raydium_enter_sleep(client); + + ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0); + } else { + raydium_i2c_power_off(ts); + } + + return 0; +} + +static int __maybe_unused raydium_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct raydium_data *ts = i2c_get_clientdata(client); + + if (device_may_wakeup(dev)) { + if (ts->wake_irq_enabled) + disable_irq_wake(client->irq); + raydium_i2c_sw_reset(client); + } else { + raydium_i2c_power_on(ts); + raydium_i2c_initialize(ts); + } + + enable_irq(client->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops, + raydium_i2c_suspend, raydium_i2c_resume); + +static const struct i2c_device_id raydium_i2c_id[] = { + { "raydium_i2c" , 0 }, + { "rm32380", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, raydium_i2c_id); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id raydium_acpi_id[] = { + { "RAYD0001", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, raydium_acpi_id); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id raydium_of_match[] = { + { .compatible = "raydium,rm32380", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, raydium_of_match); +#endif + +static struct i2c_driver raydium_i2c_driver = { + .probe = raydium_i2c_probe, + .id_table = raydium_i2c_id, + .driver = { + .name = "raydium_ts", + .pm = &raydium_i2c_pm_ops, + .acpi_match_table = ACPI_PTR(raydium_acpi_id), + .of_match_table = of_match_ptr(raydium_of_match), + }, +}; +module_i2c_driver(raydium_i2c_driver); + +MODULE_AUTHOR("Raydium"); +MODULE_DESCRIPTION("Raydium I2c Touchscreen driver"); +MODULE_LICENSE("GPL v2"); diff --git b/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c new file mode 100644 index 0000000..b2744a6 --- /dev/null +++ b/drivers/input/touchscreen/silead.c @@ -0,0 +1,565 @@ +/* ------------------------------------------------------------------------- + * Copyright (C) 2014-2015, Intel Corporation + * + * Derived from: + * gslX68X.c + * Copyright (C) 2010-2015, Shanghai Sileadinc Co.Ltd + * + * 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 + +#define SILEAD_TS_NAME "silead_ts" + +#define SILEAD_REG_RESET 0xE0 +#define SILEAD_REG_DATA 0x80 +#define SILEAD_REG_TOUCH_NR 0x80 +#define SILEAD_REG_POWER 0xBC +#define SILEAD_REG_CLOCK 0xE4 +#define SILEAD_REG_STATUS 0xB0 +#define SILEAD_REG_ID 0xFC +#define SILEAD_REG_MEM_CHECK 0xB0 + +#define SILEAD_STATUS_OK 0x5A5A5A5A +#define SILEAD_TS_DATA_LEN 44 +#define SILEAD_CLOCK 0x04 + +#define SILEAD_CMD_RESET 0x88 +#define SILEAD_CMD_START 0x00 + +#define SILEAD_POINT_DATA_LEN 0x04 +#define SILEAD_POINT_Y_OFF 0x00 +#define SILEAD_POINT_Y_MSB_OFF 0x01 +#define SILEAD_POINT_X_OFF 0x02 +#define SILEAD_POINT_X_MSB_OFF 0x03 +#define SILEAD_TOUCH_ID_MASK 0xF0 + +#define SILEAD_CMD_SLEEP_MIN 10000 +#define SILEAD_CMD_SLEEP_MAX 20000 +#define SILEAD_POWER_SLEEP 20 +#define SILEAD_STARTUP_SLEEP 30 + +#define SILEAD_MAX_FINGERS 10 + +enum silead_ts_power { + SILEAD_POWER_ON = 1, + SILEAD_POWER_OFF = 0 +}; + +struct silead_ts_data { + struct i2c_client *client; + struct gpio_desc *gpio_power; + struct input_dev *input; + char fw_name[64]; + struct touchscreen_properties prop; + u32 max_fingers; + u32 chip_id; + struct input_mt_pos pos[SILEAD_MAX_FINGERS]; + int slots[SILEAD_MAX_FINGERS]; + int id[SILEAD_MAX_FINGERS]; +}; + +struct silead_fw_data { + u32 offset; + u32 val; +}; + +static int silead_ts_request_input_dev(struct silead_ts_data *data) +{ + struct device *dev = &data->client->dev; + int error; + + data->input = devm_input_allocate_device(dev); + if (!data->input) { + dev_err(dev, + "Failed to allocate input device\n"); + return -ENOMEM; + } + + input_set_abs_params(data->input, ABS_MT_POSITION_X, 0, 4095, 0, 0); + input_set_abs_params(data->input, ABS_MT_POSITION_Y, 0, 4095, 0, 0); + touchscreen_parse_properties(data->input, true, &data->prop); + + input_mt_init_slots(data->input, data->max_fingers, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED | + INPUT_MT_TRACK); + + data->input->name = SILEAD_TS_NAME; + data->input->phys = "input/ts"; + data->input->id.bustype = BUS_I2C; + + error = input_register_device(data->input); + if (error) { + dev_err(dev, "Failed to register input device: %d\n", error); + return error; + } + + return 0; +} + +static void silead_ts_set_power(struct i2c_client *client, + enum silead_ts_power state) +{ + struct silead_ts_data *data = i2c_get_clientdata(client); + + if (data->gpio_power) { + gpiod_set_value_cansleep(data->gpio_power, state); + msleep(SILEAD_POWER_SLEEP); + } +} + +static void silead_ts_read_data(struct i2c_client *client) +{ + struct silead_ts_data *data = i2c_get_clientdata(client); + struct input_dev *input = data->input; + struct device *dev = &client->dev; + u8 *bufp, buf[SILEAD_TS_DATA_LEN]; + int touch_nr, error, i; + + error = i2c_smbus_read_i2c_block_data(client, SILEAD_REG_DATA, + SILEAD_TS_DATA_LEN, buf); + if (error < 0) { + dev_err(dev, "Data read error %d\n", error); + return; + } + + touch_nr = buf[0]; + if (touch_nr > data->max_fingers) { + dev_warn(dev, "More touches reported then supported %d > %d\n", + touch_nr, data->max_fingers); + touch_nr = data->max_fingers; + } + + bufp = buf + SILEAD_POINT_DATA_LEN; + for (i = 0; i < touch_nr; i++, bufp += SILEAD_POINT_DATA_LEN) { + /* Bits 4-7 are the touch id */ + data->id[i] = (bufp[SILEAD_POINT_X_MSB_OFF] & + SILEAD_TOUCH_ID_MASK) >> 4; + touchscreen_set_mt_pos(&data->pos[i], &data->prop, + get_unaligned_le16(&bufp[SILEAD_POINT_X_OFF]) & 0xfff, + get_unaligned_le16(&bufp[SILEAD_POINT_Y_OFF]) & 0xfff); + } + + input_mt_assign_slots(input, data->slots, data->pos, touch_nr, 0); + + for (i = 0; i < touch_nr; i++) { + input_mt_slot(input, data->slots[i]); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + input_report_abs(input, ABS_MT_POSITION_X, data->pos[i].x); + input_report_abs(input, ABS_MT_POSITION_Y, data->pos[i].y); + + dev_dbg(dev, "x=%d y=%d hw_id=%d sw_id=%d\n", data->pos[i].x, + data->pos[i].y, data->id[i], data->slots[i]); + } + + input_mt_sync_frame(input); + input_sync(input); +} + +static int silead_ts_init(struct i2c_client *client) +{ + struct silead_ts_data *data = i2c_get_clientdata(client); + int error; + + error = i2c_smbus_write_byte_data(client, SILEAD_REG_RESET, + SILEAD_CMD_RESET); + if (error) + goto i2c_write_err; + usleep_range(SILEAD_CMD_SLEEP_MIN, SILEAD_CMD_SLEEP_MAX); + + error = i2c_smbus_write_byte_data(client, SILEAD_REG_TOUCH_NR, + data->max_fingers); + if (error) + goto i2c_write_err; + usleep_range(SILEAD_CMD_SLEEP_MIN, SILEAD_CMD_SLEEP_MAX); + + error = i2c_smbus_write_byte_data(client, SILEAD_REG_CLOCK, + SILEAD_CLOCK); + if (error) + goto i2c_write_err; + usleep_range(SILEAD_CMD_SLEEP_MIN, SILEAD_CMD_SLEEP_MAX); + + error = i2c_smbus_write_byte_data(client, SILEAD_REG_RESET, + SILEAD_CMD_START); + if (error) + goto i2c_write_err; + usleep_range(SILEAD_CMD_SLEEP_MIN, SILEAD_CMD_SLEEP_MAX); + + return 0; + +i2c_write_err: + dev_err(&client->dev, "Registers clear error %d\n", error); + return error; +} + +static int silead_ts_reset(struct i2c_client *client) +{ + int error; + + error = i2c_smbus_write_byte_data(client, SILEAD_REG_RESET, + SILEAD_CMD_RESET); + if (error) + goto i2c_write_err; + usleep_range(SILEAD_CMD_SLEEP_MIN, SILEAD_CMD_SLEEP_MAX); + + error = i2c_smbus_write_byte_data(client, SILEAD_REG_CLOCK, + SILEAD_CLOCK); + if (error) + goto i2c_write_err; + usleep_range(SILEAD_CMD_SLEEP_MIN, SILEAD_CMD_SLEEP_MAX); + + error = i2c_smbus_write_byte_data(client, SILEAD_REG_POWER, + SILEAD_CMD_START); + if (error) + goto i2c_write_err; + usleep_range(SILEAD_CMD_SLEEP_MIN, SILEAD_CMD_SLEEP_MAX); + + return 0; + +i2c_write_err: + dev_err(&client->dev, "Chip reset error %d\n", error); + return error; +} + +static int silead_ts_startup(struct i2c_client *client) +{ + int error; + + error = i2c_smbus_write_byte_data(client, SILEAD_REG_RESET, 0x00); + if (error) { + dev_err(&client->dev, "Startup error %d\n", error); + return error; + } + + msleep(SILEAD_STARTUP_SLEEP); + + return 0; +} + +static int silead_ts_load_fw(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct silead_ts_data *data = i2c_get_clientdata(client); + unsigned int fw_size, i; + const struct firmware *fw; + struct silead_fw_data *fw_data; + int error; + + dev_dbg(dev, "Firmware file name: %s", data->fw_name); + + error = request_firmware(&fw, data->fw_name, dev); + if (error) { + dev_err(dev, "Firmware request error %d\n", error); + return error; + } + + fw_size = fw->size / sizeof(*fw_data); + fw_data = (struct silead_fw_data *)fw->data; + + for (i = 0; i < fw_size; i++) { + error = i2c_smbus_write_i2c_block_data(client, + fw_data[i].offset, + 4, + (u8 *)&fw_data[i].val); + if (error) { + dev_err(dev, "Firmware load error %d\n", error); + break; + } + } + + release_firmware(fw); + return error ?: 0; +} + +static u32 silead_ts_get_status(struct i2c_client *client) +{ + int error; + __le32 status; + + error = i2c_smbus_read_i2c_block_data(client, SILEAD_REG_STATUS, + sizeof(status), (u8 *)&status); + if (error < 0) { + dev_err(&client->dev, "Status read error %d\n", error); + return error; + } + + return le32_to_cpu(status); +} + +static int silead_ts_get_id(struct i2c_client *client) +{ + struct silead_ts_data *data = i2c_get_clientdata(client); + __le32 chip_id; + int error; + + error = i2c_smbus_read_i2c_block_data(client, SILEAD_REG_ID, + sizeof(chip_id), (u8 *)&chip_id); + if (error < 0) { + dev_err(&client->dev, "Chip ID read error %d\n", error); + return error; + } + + data->chip_id = le32_to_cpu(chip_id); + dev_info(&client->dev, "Silead chip ID: 0x%8X", data->chip_id); + + return 0; +} + +static int silead_ts_setup(struct i2c_client *client) +{ + int error; + u32 status; + + silead_ts_set_power(client, SILEAD_POWER_OFF); + silead_ts_set_power(client, SILEAD_POWER_ON); + + error = silead_ts_get_id(client); + if (error) + return error; + + error = silead_ts_init(client); + if (error) + return error; + + error = silead_ts_reset(client); + if (error) + return error; + + error = silead_ts_load_fw(client); + if (error) + return error; + + error = silead_ts_startup(client); + if (error) + return error; + + status = silead_ts_get_status(client); + if (status != SILEAD_STATUS_OK) { + dev_err(&client->dev, + "Initialization error, status: 0x%X\n", status); + return -ENODEV; + } + + return 0; +} + +static irqreturn_t silead_ts_threaded_irq_handler(int irq, void *id) +{ + struct silead_ts_data *data = id; + struct i2c_client *client = data->client; + + silead_ts_read_data(client); + + return IRQ_HANDLED; +} + +static void silead_ts_read_props(struct i2c_client *client) +{ + struct silead_ts_data *data = i2c_get_clientdata(client); + struct device *dev = &client->dev; + const char *str; + int error; + + error = device_property_read_u32(dev, "silead,max-fingers", + &data->max_fingers); + if (error) { + dev_dbg(dev, "Max fingers read error %d\n", error); + data->max_fingers = 5; /* Most devices handle up-to 5 fingers */ + } + + error = device_property_read_string(dev, "touchscreen-fw-name", &str); + if (!error) + snprintf(data->fw_name, sizeof(data->fw_name), "%s", str); + else + dev_dbg(dev, "Firmware file name read error. Using default."); +} + +#ifdef CONFIG_ACPI +static int silead_ts_set_default_fw_name(struct silead_ts_data *data, + const struct i2c_device_id *id) +{ + const struct acpi_device_id *acpi_id; + struct device *dev = &data->client->dev; + int i; + + if (ACPI_HANDLE(dev)) { + acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!acpi_id) + return -ENODEV; + + snprintf(data->fw_name, sizeof(data->fw_name), "%s.fw", + acpi_id->id); + + for (i = 0; i < strlen(data->fw_name); i++) + data->fw_name[i] = tolower(data->fw_name[i]); + } else { + snprintf(data->fw_name, sizeof(data->fw_name), "%s.fw", + id->name); + } + + return 0; +} +#else +static int silead_ts_set_default_fw_name(struct silead_ts_data *data, + const struct i2c_device_id *id) +{ + snprintf(data->fw_name, sizeof(data->fw_name), "%s.fw", id->name); + return 0; +} +#endif + +static int silead_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct silead_ts_data *data; + struct device *dev = &client->dev; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_I2C | + I2C_FUNC_SMBUS_READ_I2C_BLOCK | + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) { + dev_err(dev, "I2C functionality check failed\n"); + return -ENXIO; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->client = client; + + error = silead_ts_set_default_fw_name(data, id); + if (error) + return error; + + silead_ts_read_props(client); + + /* We must have the IRQ provided by DT or ACPI subsytem */ + if (client->irq <= 0) + return -ENODEV; + + /* Power GPIO pin */ + data->gpio_power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(data->gpio_power)) { + if (PTR_ERR(data->gpio_power) != -EPROBE_DEFER) + dev_err(dev, "Shutdown GPIO request failed\n"); + return PTR_ERR(data->gpio_power); + } + + error = silead_ts_setup(client); + if (error) + return error; + + error = silead_ts_request_input_dev(data); + if (error) + return error; + + error = devm_request_threaded_irq(dev, client->irq, + NULL, silead_ts_threaded_irq_handler, + IRQF_ONESHOT, client->name, data); + if (error) { + if (error != -EPROBE_DEFER) + dev_err(dev, "IRQ request failed %d\n", error); + return error; + } + + return 0; +} + +static int __maybe_unused silead_ts_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + silead_ts_set_power(client, SILEAD_POWER_OFF); + return 0; +} + +static int __maybe_unused silead_ts_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int error, status; + + silead_ts_set_power(client, SILEAD_POWER_ON); + + error = silead_ts_reset(client); + if (error) + return error; + + error = silead_ts_startup(client); + if (error) + return error; + + status = silead_ts_get_status(client); + if (status != SILEAD_STATUS_OK) { + dev_err(dev, "Resume error, status: 0x%02x\n", status); + return -ENODEV; + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(silead_ts_pm, silead_ts_suspend, silead_ts_resume); + +static const struct i2c_device_id silead_ts_id[] = { + { "gsl1680", 0 }, + { "gsl1688", 0 }, + { "gsl3670", 0 }, + { "gsl3675", 0 }, + { "gsl3692", 0 }, + { "mssl1680", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, silead_ts_id); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id silead_ts_acpi_match[] = { + { "GSL1680", 0 }, + { "GSL1688", 0 }, + { "GSL3670", 0 }, + { "GSL3675", 0 }, + { "GSL3692", 0 }, + { "MSSL1680", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, silead_ts_acpi_match); +#endif + +static struct i2c_driver silead_ts_driver = { + .probe = silead_ts_probe, + .id_table = silead_ts_id, + .driver = { + .name = SILEAD_TS_NAME, + .acpi_match_table = ACPI_PTR(silead_ts_acpi_match), + .pm = &silead_ts_pm, + }, +}; +module_i2c_driver(silead_ts_driver); + +MODULE_AUTHOR("Robert Dolca "); +MODULE_DESCRIPTION("Silead I2C touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git b/drivers/input/touchscreen/sis_i2c.c b/drivers/input/touchscreen/sis_i2c.c new file mode 100644 index 0000000..8d93f8c --- /dev/null +++ b/drivers/input/touchscreen/sis_i2c.c @@ -0,0 +1,413 @@ +/* + * Touch Screen driver for SiS 9200 family I2C Touch panels + * + * Copyright (C) 2015 SiS, Inc. + * Copyright (C) 2016 Nextfour Group + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 + +#define SIS_I2C_NAME "sis_i2c_ts" + +/* + * The I2C packet format: + * le16 byte count + * u8 Report ID + * + * u8 Number of contacts + * le16 Scan Time (optional) + * le16 CRC + * + * One touch point information consists of 6+ bytes, the order is: + * u8 contact state + * u8 finger id + * le16 x axis + * le16 y axis + * u8 contact width (optional) + * u8 contact height (optional) + * u8 pressure (optional) + * + * Maximum amount of data transmitted in one shot is 64 bytes, if controller + * needs to report more contacts than fit in one packet it will send true + * number of contacts in first packet and 0 as number of contacts in second + * packet. + */ + +#define SIS_MAX_PACKET_SIZE 64 + +#define SIS_PKT_LEN_OFFSET 0 +#define SIS_PKT_REPORT_OFFSET 2 /* Report ID/type */ +#define SIS_PKT_CONTACT_OFFSET 3 /* First contact */ + +#define SIS_SCAN_TIME_LEN 2 + +/* Supported report types */ +#define SIS_ALL_IN_ONE_PACKAGE 0x10 +#define SIS_PKT_IS_TOUCH(x) (((x) & 0x0f) == 0x01) +#define SIS_PKT_IS_HIDI2C(x) (((x) & 0x0f) == 0x06) + +/* Contact properties within report */ +#define SIS_PKT_HAS_AREA(x) ((x) & BIT(4)) +#define SIS_PKT_HAS_PRESSURE(x) ((x) & BIT(5)) +#define SIS_PKT_HAS_SCANTIME(x) ((x) & BIT(6)) + +/* Contact size */ +#define SIS_BASE_LEN_PER_CONTACT 6 +#define SIS_AREA_LEN_PER_CONTACT 2 +#define SIS_PRESSURE_LEN_PER_CONTACT 1 + +/* Offsets within contact data */ +#define SIS_CONTACT_STATUS_OFFSET 0 +#define SIS_CONTACT_ID_OFFSET 1 /* Contact ID */ +#define SIS_CONTACT_X_OFFSET 2 +#define SIS_CONTACT_Y_OFFSET 4 +#define SIS_CONTACT_WIDTH_OFFSET 6 +#define SIS_CONTACT_HEIGHT_OFFSET 7 +#define SIS_CONTACT_PRESSURE_OFFSET(id) (SIS_PKT_HAS_AREA(id) ? 8 : 6) + +/* Individual contact state */ +#define SIS_STATUS_UP 0x0 +#define SIS_STATUS_DOWN 0x3 + +/* Touchscreen parameters */ +#define SIS_MAX_FINGERS 10 +#define SIS_MAX_X 4095 +#define SIS_MAX_Y 4095 +#define SIS_MAX_PRESSURE 255 + +/* Resolution diagonal */ +#define SIS_AREA_LENGTH_LONGER 5792 +/*((SIS_MAX_X^2) + (SIS_MAX_Y^2))^0.5*/ +#define SIS_AREA_LENGTH_SHORT 5792 +#define SIS_AREA_UNIT (5792 / 32) + +struct sis_ts_data { + struct i2c_client *client; + struct input_dev *input; + + struct gpio_desc *attn_gpio; + struct gpio_desc *reset_gpio; + + u8 packet[SIS_MAX_PACKET_SIZE]; +}; + +static int sis_read_packet(struct i2c_client *client, u8 *buf, + unsigned int *num_contacts, + unsigned int *contact_size) +{ + int count_idx; + int ret; + u16 len; + u16 crc, pkg_crc; + u8 report_id; + + ret = i2c_master_recv(client, buf, SIS_MAX_PACKET_SIZE); + if (ret <= 0) + return -EIO; + + len = get_unaligned_le16(&buf[SIS_PKT_LEN_OFFSET]); + if (len > SIS_MAX_PACKET_SIZE) { + dev_err(&client->dev, + "%s: invalid packet length (%d vs %d)\n", + __func__, len, SIS_MAX_PACKET_SIZE); + return -E2BIG; + } + + if (len < 10) + return -EINVAL; + + report_id = buf[SIS_PKT_REPORT_OFFSET]; + count_idx = len - 1; + *contact_size = SIS_BASE_LEN_PER_CONTACT; + + if (report_id != SIS_ALL_IN_ONE_PACKAGE) { + if (SIS_PKT_IS_TOUCH(report_id)) { + /* + * Calculate CRC ignoring packet length + * in the beginning and CRC transmitted + * at the end of the packet. + */ + crc = crc_itu_t(0, buf + 2, len - 2 - 2); + pkg_crc = get_unaligned_le16(&buf[len - 2]); + + if (crc != pkg_crc) { + dev_err(&client->dev, + "%s: CRC Error (%d vs %d)\n", + __func__, crc, pkg_crc); + return -EINVAL; + } + + count_idx -= 2; + + } else if (!SIS_PKT_IS_HIDI2C(report_id)) { + dev_err(&client->dev, + "%s: invalid packet ID %#02x\n", + __func__, report_id); + return -EINVAL; + } + + if (SIS_PKT_HAS_SCANTIME(report_id)) + count_idx -= SIS_SCAN_TIME_LEN; + + if (SIS_PKT_HAS_AREA(report_id)) + *contact_size += SIS_AREA_LEN_PER_CONTACT; + if (SIS_PKT_HAS_PRESSURE(report_id)) + *contact_size += SIS_PRESSURE_LEN_PER_CONTACT; + } + + *num_contacts = buf[count_idx]; + return 0; +} + +static int sis_ts_report_contact(struct sis_ts_data *ts, const u8 *data, u8 id) +{ + struct input_dev *input = ts->input; + int slot; + u8 status = data[SIS_CONTACT_STATUS_OFFSET]; + u8 pressure; + u8 height, width; + u16 x, y; + + if (status != SIS_STATUS_DOWN && status != SIS_STATUS_UP) { + dev_err(&ts->client->dev, "Unexpected touch status: %#02x\n", + data[SIS_CONTACT_STATUS_OFFSET]); + return -EINVAL; + } + + slot = input_mt_get_slot_by_key(input, data[SIS_CONTACT_ID_OFFSET]); + if (slot < 0) + return -ENOENT; + + input_mt_slot(input, slot); + input_mt_report_slot_state(input, MT_TOOL_FINGER, + status == SIS_STATUS_DOWN); + + if (status == SIS_STATUS_DOWN) { + pressure = height = width = 1; + if (id != SIS_ALL_IN_ONE_PACKAGE) { + if (SIS_PKT_HAS_AREA(id)) { + width = data[SIS_CONTACT_WIDTH_OFFSET]; + height = data[SIS_CONTACT_HEIGHT_OFFSET]; + } + + if (SIS_PKT_HAS_PRESSURE(id)) + pressure = + data[SIS_CONTACT_PRESSURE_OFFSET(id)]; + } + + x = get_unaligned_le16(&data[SIS_CONTACT_X_OFFSET]); + y = get_unaligned_le16(&data[SIS_CONTACT_Y_OFFSET]); + + input_report_abs(input, ABS_MT_TOUCH_MAJOR, + width * SIS_AREA_UNIT); + input_report_abs(input, ABS_MT_TOUCH_MINOR, + height * SIS_AREA_UNIT); + input_report_abs(input, ABS_MT_PRESSURE, pressure); + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + } + + return 0; +} + +static void sis_ts_handle_packet(struct sis_ts_data *ts) +{ + const u8 *contact; + unsigned int num_to_report = 0; + unsigned int num_contacts; + unsigned int num_reported; + unsigned int contact_size; + int error; + u8 report_id; + + do { + error = sis_read_packet(ts->client, ts->packet, + &num_contacts, &contact_size); + if (error) + break; + + if (num_to_report == 0) { + num_to_report = num_contacts; + } else if (num_contacts != 0) { + dev_err(&ts->client->dev, + "%s: nonzero (%d) point count in tail packet\n", + __func__, num_contacts); + break; + } + + report_id = ts->packet[SIS_PKT_REPORT_OFFSET]; + contact = &ts->packet[SIS_PKT_CONTACT_OFFSET]; + num_reported = 0; + + while (num_to_report > 0) { + error = sis_ts_report_contact(ts, contact, report_id); + if (error) + break; + + contact += contact_size; + num_to_report--; + num_reported++; + + if (report_id != SIS_ALL_IN_ONE_PACKAGE && + num_reported >= 5) { + /* + * The remainder of contacts is sent + * in the 2nd packet. + */ + break; + } + } + } while (num_to_report > 0); + + input_mt_sync_frame(ts->input); + input_sync(ts->input); +} + +static irqreturn_t sis_ts_irq_handler(int irq, void *dev_id) +{ + struct sis_ts_data *ts = dev_id; + + do { + sis_ts_handle_packet(ts); + } while (ts->attn_gpio && gpiod_get_value_cansleep(ts->attn_gpio)); + + return IRQ_HANDLED; +} + +static void sis_ts_reset(struct sis_ts_data *ts) +{ + if (ts->reset_gpio) { + /* Get out of reset */ + usleep_range(1000, 2000); + gpiod_set_value(ts->reset_gpio, 1); + usleep_range(1000, 2000); + gpiod_set_value(ts->reset_gpio, 0); + msleep(100); + } +} + +static int sis_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sis_ts_data *ts; + struct input_dev *input; + int error; + + ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + ts->client = client; + i2c_set_clientdata(client, ts); + + ts->attn_gpio = devm_gpiod_get_optional(&client->dev, + "attn", GPIOD_IN); + if (IS_ERR(ts->attn_gpio)) { + error = PTR_ERR(ts->attn_gpio); + if (error != -EPROBE_DEFER) + dev_err(&client->dev, + "Failed to get attention GPIO: %d\n", error); + return error; + } + + ts->reset_gpio = devm_gpiod_get_optional(&client->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(ts->reset_gpio)) { + error = PTR_ERR(ts->reset_gpio); + if (error != -EPROBE_DEFER) + dev_err(&client->dev, + "Failed to get reset GPIO: %d\n", error); + return error; + } + + sis_ts_reset(ts); + + ts->input = input = devm_input_allocate_device(&client->dev); + if (!input) { + dev_err(&client->dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + input->name = "SiS Touchscreen"; + input->id.bustype = BUS_I2C; + + input_set_abs_params(input, ABS_MT_POSITION_X, 0, SIS_MAX_X, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, SIS_MAX_Y, 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, SIS_MAX_PRESSURE, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, + 0, SIS_AREA_LENGTH_LONGER, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, + 0, SIS_AREA_LENGTH_SHORT, 0, 0); + + error = input_mt_init_slots(input, SIS_MAX_FINGERS, INPUT_MT_DIRECT); + if (error) { + dev_err(&client->dev, + "Failed to initialize MT slots: %d\n", error); + return error; + } + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, sis_ts_irq_handler, + IRQF_ONESHOT, + client->name, ts); + if (error) { + dev_err(&client->dev, "Failed to request IRQ: %d\n", error); + return error; + } + + error = input_register_device(ts->input); + if (error) { + dev_err(&client->dev, + "Failed to register input device: %d\n", error); + return error; + } + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id sis_ts_dt_ids[] = { + { .compatible = "sis,9200-ts" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sis_ts_dt_ids); +#endif + +static const struct i2c_device_id sis_ts_id[] = { + { SIS_I2C_NAME, 0 }, + { "9200-ts", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, sis_ts_id); + +static struct i2c_driver sis_ts_driver = { + .driver = { + .name = SIS_I2C_NAME, + .of_match_table = of_match_ptr(sis_ts_dt_ids), + }, + .probe = sis_ts_probe, + .id_table = sis_ts_id, +}; +module_i2c_driver(sis_ts_driver); + +MODULE_DESCRIPTION("SiS 9200 Family Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Mika Penttilä "); diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index b7e8c11..4ea4757 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -151,7 +151,6 @@ struct sur40_state { struct mutex lock; struct vb2_queue queue; - struct vb2_alloc_ctx *alloc_ctx; struct list_head buf_list; spinlock_t qlock; int sequence; @@ -580,19 +579,13 @@ static int sur40_probe(struct usb_interface *interface, sur40->queue = sur40_queue; sur40->queue.drv_priv = sur40; sur40->queue.lock = &sur40->lock; + sur40->queue.dev = sur40->dev; /* initialize the queue */ error = vb2_queue_init(&sur40->queue); if (error) goto err_unreg_v4l2; - sur40->alloc_ctx = vb2_dma_sg_init_ctx(sur40->dev); - if (IS_ERR(sur40->alloc_ctx)) { - dev_err(sur40->dev, "Can't allocate buffer context"); - error = PTR_ERR(sur40->alloc_ctx); - goto err_unreg_v4l2; - } - sur40->vdev = sur40_video_device; sur40->vdev.v4l2_dev = &sur40->v4l2; sur40->vdev.lock = &sur40->lock; @@ -633,7 +626,6 @@ static void sur40_disconnect(struct usb_interface *interface) video_unregister_device(&sur40->vdev); v4l2_device_unregister(&sur40->v4l2); - vb2_dma_sg_cleanup_ctx(sur40->alloc_ctx); input_unregister_polled_device(sur40->input); input_free_polled_device(sur40->input); @@ -653,13 +645,10 @@ static void sur40_disconnect(struct usb_interface *interface) */ static int sur40_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) + unsigned int sizes[], struct device *alloc_devs[]) { - struct sur40_state *sur40 = vb2_get_drv_priv(q); - if (q->num_buffers + *nbuffers < 3) *nbuffers = 3 - q->num_buffers; - alloc_ctxs[0] = sur40->alloc_ctx; if (*nplanes) return sizes[0] < sur40_video_format.sizeimage ? -EINVAL : 0; @@ -794,7 +783,6 @@ static int sur40_vidioc_enum_fmt(struct file *file, void *priv, { if (f->index != 0) return -EINVAL; - strlcpy(f->description, "8-bit greyscale", sizeof(f->description)); f->pixelformat = V4L2_PIX_FMT_GREY; f->flags = 0; return 0; diff --git b/drivers/input/touchscreen/surface3_spi.c b/drivers/input/touchscreen/surface3_spi.c new file mode 100644 index 0000000..e12fb9b --- /dev/null +++ b/drivers/input/touchscreen/surface3_spi.c @@ -0,0 +1,427 @@ +/* + * Driver for Ntrig/Microsoft Touchscreens over SPI + * + * Copyright (c) 2016 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; version 2 of the License. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SURFACE3_PACKET_SIZE 264 + +#define SURFACE3_REPORT_TOUCH 0xd2 +#define SURFACE3_REPORT_PEN 0x16 + +struct surface3_ts_data { + struct spi_device *spi; + struct gpio_desc *gpiod_rst[2]; + struct input_dev *input_dev; + struct input_dev *pen_input_dev; + int pen_tool; + + u8 rd_buf[SURFACE3_PACKET_SIZE] ____cacheline_aligned; +}; + +struct surface3_ts_data_finger { + u8 status; + __le16 tracking_id; + __le16 x; + __le16 cx; + __le16 y; + __le16 cy; + __le16 width; + __le16 height; + u32 padding; +} __packed; + +struct surface3_ts_data_pen { + u8 status; + __le16 x; + __le16 y; + __le16 pressure; + u8 padding; +} __packed; + +static int surface3_spi_read(struct surface3_ts_data *ts_data) +{ + struct spi_device *spi = ts_data->spi; + + memset(ts_data->rd_buf, 0, sizeof(ts_data->rd_buf)); + return spi_read(spi, ts_data->rd_buf, sizeof(ts_data->rd_buf)); +} + +static void surface3_spi_report_touch(struct surface3_ts_data *ts_data, + struct surface3_ts_data_finger *finger) +{ + int st = finger->status & 0x01; + int slot; + + slot = input_mt_get_slot_by_key(ts_data->input_dev, + get_unaligned_le16(&finger->tracking_id)); + if (slot < 0) + return; + + input_mt_slot(ts_data->input_dev, slot); + input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, st); + if (st) { + input_report_abs(ts_data->input_dev, + ABS_MT_POSITION_X, + get_unaligned_le16(&finger->x)); + input_report_abs(ts_data->input_dev, + ABS_MT_POSITION_Y, + get_unaligned_le16(&finger->y)); + input_report_abs(ts_data->input_dev, + ABS_MT_WIDTH_MAJOR, + get_unaligned_le16(&finger->width)); + input_report_abs(ts_data->input_dev, + ABS_MT_WIDTH_MINOR, + get_unaligned_le16(&finger->height)); + } +} + +static void surface3_spi_process_touch(struct surface3_ts_data *ts_data, u8 *data) +{ + u16 timestamp; + unsigned int i; + timestamp = get_unaligned_le16(&data[15]); + + for (i = 0; i < 13; i++) { + struct surface3_ts_data_finger *finger; + + finger = (struct surface3_ts_data_finger *)&data[17 + + i * sizeof(struct surface3_ts_data_finger)]; + + /* + * When bit 5 of status is 1, it marks the end of the report: + * - touch present: 0xe7 + * - touch released: 0xe4 + * - nothing valuable: 0xff + */ + if (finger->status & 0x10) + break; + + surface3_spi_report_touch(ts_data, finger); + } + + input_mt_sync_frame(ts_data->input_dev); + input_sync(ts_data->input_dev); +} + +static void surface3_spi_report_pen(struct surface3_ts_data *ts_data, + struct surface3_ts_data_pen *pen) +{ + struct input_dev *dev = ts_data->pen_input_dev; + int st = pen->status; + int prox = st & 0x01; + int rubber = st & 0x18; + int tool = (prox && rubber) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + + /* fake proximity out to switch tools */ + if (ts_data->pen_tool != tool) { + input_report_key(dev, ts_data->pen_tool, 0); + input_sync(dev); + ts_data->pen_tool = tool; + } + + input_report_key(dev, BTN_TOUCH, st & 0x12); + + input_report_key(dev, ts_data->pen_tool, prox); + + if (st) { + input_report_key(dev, + BTN_STYLUS, + st & 0x04); + + input_report_abs(dev, + ABS_X, + get_unaligned_le16(&pen->x)); + input_report_abs(dev, + ABS_Y, + get_unaligned_le16(&pen->y)); + input_report_abs(dev, + ABS_PRESSURE, + get_unaligned_le16(&pen->pressure)); + } +} + +static void surface3_spi_process_pen(struct surface3_ts_data *ts_data, u8 *data) +{ + struct surface3_ts_data_pen *pen; + + pen = (struct surface3_ts_data_pen *)&data[15]; + + surface3_spi_report_pen(ts_data, pen); + input_sync(ts_data->pen_input_dev); +} + +static void surface3_spi_process(struct surface3_ts_data *ts_data) +{ + const char header[] = { + 0xff, 0xff, 0xff, 0xff, 0xa5, 0x5a, 0xe7, 0x7e, 0x01 + }; + u8 *data = ts_data->rd_buf; + + if (memcmp(header, data, sizeof(header))) + dev_err(&ts_data->spi->dev, + "%s header error: %*ph, ignoring...\n", + __func__, (int)sizeof(header), data); + + switch (data[9]) { + case SURFACE3_REPORT_TOUCH: + surface3_spi_process_touch(ts_data, data); + break; + case SURFACE3_REPORT_PEN: + surface3_spi_process_pen(ts_data, data); + break; + default: + dev_err(&ts_data->spi->dev, + "%s unknown packet type: %x, ignoring...\n", + __func__, data[9]); + break; + } +} + +static irqreturn_t surface3_spi_irq_handler(int irq, void *dev_id) +{ + struct surface3_ts_data *data = dev_id; + + if (surface3_spi_read(data)) + return IRQ_HANDLED; + + dev_dbg(&data->spi->dev, "%s received -> %*ph\n", + __func__, SURFACE3_PACKET_SIZE, data->rd_buf); + surface3_spi_process(data); + + return IRQ_HANDLED; +} + +static void surface3_spi_power(struct surface3_ts_data *data, bool on) +{ + gpiod_set_value(data->gpiod_rst[0], on); + gpiod_set_value(data->gpiod_rst[1], on); + /* let the device settle a little */ + msleep(20); +} + +/** + * surface3_spi_get_gpio_config - Get GPIO config from ACPI/DT + * + * @ts: surface3_spi_ts_data pointer + */ +static int surface3_spi_get_gpio_config(struct surface3_ts_data *data) +{ + int error; + struct device *dev; + struct gpio_desc *gpiod; + int i; + + dev = &data->spi->dev; + + /* Get the reset lines GPIO pin number */ + for (i = 0; i < 2; i++) { + gpiod = devm_gpiod_get_index(dev, NULL, i, GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) { + error = PTR_ERR(gpiod); + if (error != -EPROBE_DEFER) + dev_err(dev, + "Failed to get power GPIO %d: %d\n", + i, + error); + return error; + } + + data->gpiod_rst[i] = gpiod; + } + + return 0; +} + +static int surface3_spi_create_touch_input(struct surface3_ts_data *data) +{ + struct input_dev *input; + int error; + + input = devm_input_allocate_device(&data->spi->dev); + if (!input) + return -ENOMEM; + + data->input_dev = input; + + input_set_abs_params(input, ABS_MT_POSITION_X, 0, 9600, 0, 0); + input_abs_set_res(input, ABS_MT_POSITION_X, 40); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 7200, 0, 0); + input_abs_set_res(input, ABS_MT_POSITION_Y, 48); + input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 1024, 0, 0); + input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 1024, 0, 0); + input_mt_init_slots(input, 10, INPUT_MT_DIRECT); + + input->name = "Surface3 SPI Capacitive TouchScreen"; + input->phys = "input/ts"; + input->id.bustype = BUS_SPI; + input->id.vendor = 0x045e; /* Microsoft */ + input->id.product = 0x0001; + input->id.version = 0x0000; + + error = input_register_device(input); + if (error) { + dev_err(&data->spi->dev, + "Failed to register input device: %d", error); + return error; + } + + return 0; +} + +static int surface3_spi_create_pen_input(struct surface3_ts_data *data) +{ + struct input_dev *input; + int error; + + input = devm_input_allocate_device(&data->spi->dev); + if (!input) + return -ENOMEM; + + data->pen_input_dev = input; + data->pen_tool = BTN_TOOL_PEN; + + __set_bit(INPUT_PROP_DIRECT, input->propbit); + __set_bit(INPUT_PROP_POINTER, input->propbit); + input_set_abs_params(input, ABS_X, 0, 9600, 0, 0); + input_abs_set_res(input, ABS_X, 40); + input_set_abs_params(input, ABS_Y, 0, 7200, 0, 0); + input_abs_set_res(input, ABS_Y, 48); + input_set_abs_params(input, ABS_PRESSURE, 0, 1024, 0, 0); + input_set_capability(input, EV_KEY, BTN_TOUCH); + input_set_capability(input, EV_KEY, BTN_STYLUS); + input_set_capability(input, EV_KEY, BTN_TOOL_PEN); + input_set_capability(input, EV_KEY, BTN_TOOL_RUBBER); + + input->name = "Surface3 SPI Pen Input"; + input->phys = "input/ts"; + input->id.bustype = BUS_SPI; + input->id.vendor = 0x045e; /* Microsoft */ + input->id.product = 0x0002; + input->id.version = 0x0000; + + error = input_register_device(input); + if (error) { + dev_err(&data->spi->dev, + "Failed to register input device: %d", error); + return error; + } + + return 0; +} + +static int surface3_spi_probe(struct spi_device *spi) +{ + struct surface3_ts_data *data; + int error; + + /* Set up SPI*/ + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + error = spi_setup(spi); + if (error) + return error; + + data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->spi = spi; + spi_set_drvdata(spi, data); + + error = surface3_spi_get_gpio_config(data); + if (error) + return error; + + surface3_spi_power(data, true); + surface3_spi_power(data, false); + surface3_spi_power(data, true); + + error = surface3_spi_create_touch_input(data); + if (error) + return error; + + error = surface3_spi_create_pen_input(data); + if (error) + return error; + + error = devm_request_threaded_irq(&spi->dev, spi->irq, + NULL, surface3_spi_irq_handler, + IRQF_ONESHOT, + "Surface3-irq", data); + if (error) + return error; + + return 0; +} + +static int __maybe_unused surface3_spi_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct surface3_ts_data *data = spi_get_drvdata(spi); + + disable_irq(data->spi->irq); + + surface3_spi_power(data, false); + + return 0; +} + +static int __maybe_unused surface3_spi_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct surface3_ts_data *data = spi_get_drvdata(spi); + + surface3_spi_power(data, true); + + enable_irq(data->spi->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(surface3_spi_pm_ops, + surface3_spi_suspend, + surface3_spi_resume); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id surface3_spi_acpi_match[] = { + { "MSHW0037", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, surface3_spi_acpi_match); +#endif + +static struct spi_driver surface3_spi_driver = { + .driver = { + .name = "Surface3-spi", + .acpi_match_table = ACPI_PTR(surface3_spi_acpi_match), + .pm = &surface3_spi_pm_ops, + }, + .probe = surface3_spi_probe, +}; + +module_spi_driver(surface3_spi_driver); + +MODULE_AUTHOR("Benjamin Tissoires "); +MODULE_DESCRIPTION("Surface 3 SPI touchscreen driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 8b3f15c..7953381 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -406,7 +406,7 @@ static int titsc_probe(struct platform_device *pdev) int err; /* Allocate memory for device */ - ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL); + ts_dev = kzalloc(sizeof(*ts_dev), GFP_KERNEL); input_dev = input_allocate_device(); if (!ts_dev || !input_dev) { dev_err(&pdev->dev, "failed to allocate memory.\n"); diff --git a/drivers/input/touchscreen/tsc200x-core.c b/drivers/input/touchscreen/tsc200x-core.c index dfa7f1c..b7059ed 100644 --- a/drivers/input/touchscreen/tsc200x-core.c +++ b/drivers/input/touchscreen/tsc200x-core.c @@ -568,7 +568,7 @@ int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id, input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0); if (np) - touchscreen_parse_properties(input_dev, false); + touchscreen_parse_properties(input_dev, false, NULL); input_dev->open = tsc200x_open; input_dev->close = tsc200x_close; diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index b6fc4bd..85e9572 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -518,13 +518,21 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename, w8001->pktlen = W8001_PKTLEN_TOUCH2FG; __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); - input_mt_init_slots(dev, 2, 0); + error = input_mt_init_slots(dev, 2, 0); + if (error) { + dev_err(&w8001->serio->dev, + "failed to initialize MT slots: %d\n", error); + return error; + } + input_set_abs_params(dev, ABS_MT_POSITION_X, 0, touch.x, 0, 0); input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, touch.y, 0, 0); input_set_abs_params(dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0); + input_abs_set_res(dev, ABS_MT_POSITION_X, touch.panel_res); + input_abs_set_res(dev, ABS_MT_POSITION_Y, touch.panel_res); strlcat(basename, " 2FG", basename_sz); if (w8001->max_pen_x && w8001->max_pen_y) diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index 049a6fc..403f795 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -24,8 +24,12 @@ #include #include #include +#include #include #include +#include +#include +#include #include #include @@ -162,6 +166,82 @@ static const struct of_device_id tps65217_of_match[] = { }; MODULE_DEVICE_TABLE(of, tps65217_of_match); +static irqreturn_t tps65217_irq(int irq, void *irq_data) +{ + struct tps65217 *tps = irq_data; + unsigned int int_reg = 0, status_reg = 0; + + tps65217_reg_read(tps, TPS65217_REG_INT, &int_reg); + tps65217_reg_read(tps, TPS65217_REG_STATUS, &status_reg); + if (status_reg) + dev_dbg(tps->dev, "status now: 0x%X\n", status_reg); + + if (!int_reg) + return IRQ_NONE; + + if (int_reg & TPS65217_INT_PBI) { + /* Handle push button */ + dev_dbg(tps->dev, "power button status change\n"); + input_report_key(tps->pwr_but, KEY_POWER, + status_reg & TPS65217_STATUS_PB); + input_sync(tps->pwr_but); + } + if (int_reg & TPS65217_INT_ACI) { + /* Handle AC power status change */ + dev_dbg(tps->dev, "AC power status change\n"); + /* Press KEY_POWER when AC not present */ + input_report_key(tps->pwr_but, KEY_POWER, + ~status_reg & TPS65217_STATUS_ACPWR); + input_sync(tps->pwr_but); + } + if (int_reg & TPS65217_INT_USBI) { + /* Handle USB power status change */ + dev_dbg(tps->dev, "USB power status change\n"); + } + + return IRQ_HANDLED; +} + +static int tps65217_probe_pwr_but(struct tps65217 *tps) +{ + int ret; + unsigned int int_reg; + + tps->pwr_but = devm_input_allocate_device(tps->dev); + if (!tps->pwr_but) { + dev_err(tps->dev, + "Failed to allocated pwr_but input device\n"); + return -ENOMEM; + } + + tps->pwr_but->evbit[0] = BIT_MASK(EV_KEY); + tps->pwr_but->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); + tps->pwr_but->name = "tps65217_pwr_but"; + ret = input_register_device(tps->pwr_but); + if (ret) { + /* NOTE: devm managed device */ + dev_err(tps->dev, "Failed to register button device\n"); + return ret; + } + ret = devm_request_threaded_irq(tps->dev, + tps->irq, NULL, tps65217_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "tps65217", tps); + if (ret != 0) { + dev_err(tps->dev, "Failed to request IRQ %d\n", tps->irq); + return ret; + } + + /* enable the power button interrupt */ + ret = tps65217_reg_read(tps, TPS65217_REG_INT, &int_reg); + if (ret < 0) { + dev_err(tps->dev, "Failed to read INT reg\n"); + return ret; + } + int_reg &= ~TPS65217_INT_PBM; + tps65217_reg_write(tps, TPS65217_REG_INT, int_reg, TPS65217_PROTECT_NONE); + return 0; +} + static int tps65217_probe(struct i2c_client *client, const struct i2c_device_id *ids) { @@ -169,10 +249,13 @@ static int tps65217_probe(struct i2c_client *client, unsigned int version; unsigned long chip_id = ids->driver_data; const struct of_device_id *match; + struct device_node *node; bool status_off = false; + int irq = -1, irq_gpio = -1; int ret; - if (client->dev.of_node) { + node = client->dev.of_node; + if (node) { match = of_match_device(tps65217_of_match, &client->dev); if (!match) { dev_err(&client->dev, @@ -180,8 +263,31 @@ static int tps65217_probe(struct i2c_client *client, return -EINVAL; } chip_id = (unsigned long)match->data; - status_off = of_property_read_bool(client->dev.of_node, + status_off = of_property_read_bool(node, "ti,pmic-shutdown-controller"); + + /* at first try to get irq via OF method */ + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) { + irq = -1; + irq_gpio = of_get_named_gpio(node, "irq-gpio", 0); + if (irq_gpio >= 0) { + /* valid gpio; convert to irq */ + ret = devm_gpio_request_one(&client->dev, + irq_gpio, GPIOF_DIR_IN, + "tps65217-gpio-irq"); + if (ret != 0) + dev_warn(&client->dev, "Failed to " + "request gpio #%d\n", irq_gpio); + irq = gpio_to_irq(irq_gpio); + if (irq <= 0) { + dev_warn(&client->dev, "Failed to " + "convert gpio #%d to irq\n", + irq_gpio); + irq = -1; + } + } + } } if (!chip_id) { @@ -205,6 +311,18 @@ static int tps65217_probe(struct i2c_client *client, return ret; } + tps->irq = irq; + tps->irq_gpio = irq_gpio; + + /* we got an irq, request it */ + if (tps->irq >= 0) { + ret = tps65217_probe_pwr_but(tps); + if (ret < 0) { + dev_err(tps->dev, "Failed to probe pwr_but\n"); + return ret; + } + } + ret = devm_mfd_add_devices(tps->dev, -1, tps65217s, ARRAY_SIZE(tps65217s), NULL, 0, NULL); if (ret < 0) { diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index a216b46..f93a3eb 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -804,6 +804,36 @@ config PANEL_BOOT_MESSAGE An empty message will only clear the display at driver init time. Any other printf()-formatted message is valid with newline and escape codes. +config BONE_CAPEMGR + tristate "Beaglebone cape manager" + depends on ARCH_OMAP2PLUS && OF + select EEPROM + select OF_OVERLAY + help + Say Y here to include support for automatic loading of + beaglebone capes. Select M to build as a module which + will be named bone_capemgr. + +config DEV_OVERLAYMGR + tristate "Device overlay manager" + depends on OF + select OF_OVERLAY + default n + help + Say Y here to include support for the automagical dev + overlay manager. + +config TIEQEP + tristate "EQEP Hardware quadrature encoder controller" + depends on SOC_AM33XX + select PWM_TIPWMSS + help + Driver support for the EQEP quadrature encoder controller AM33XX + TI SOC + + To compile this driver as a module, choose M here: the module + will be called tieqep. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" @@ -813,7 +843,9 @@ source "drivers/misc/altera-stapl/Kconfig" source "drivers/misc/mei/Kconfig" source "drivers/misc/vmw_vmci/Kconfig" source "drivers/misc/mic/Kconfig" +source "drivers/misc/cape_bone_argus/Kconfig" source "drivers/misc/genwqe/Kconfig" +source "drivers/misc/cape/Kconfig" source "drivers/misc/echo/Kconfig" source "drivers/misc/cxl/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index b2fb6db..90b8900 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -50,10 +50,15 @@ obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o +obj-$(CONFIG_TIEQEP) += tieqep.o obj-$(CONFIG_SRAM) += sram.o obj-y += mic/ +obj-y += cape_bone_argus/ obj-$(CONFIG_GENWQE) += genwqe/ +obj-y += cape/ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_PANEL) += panel.o +obj-$(CONFIG_BONE_CAPEMGR) += bone_capemgr.o +obj-$(CONFIG_DEV_OVERLAYMGR) += devovmgr.o diff --git b/drivers/misc/bone_capemgr.c b/drivers/misc/bone_capemgr.c new file mode 100644 index 0000000..f82623d --- /dev/null +++ b/drivers/misc/bone_capemgr.c @@ -0,0 +1,1885 @@ +/* + * TI Beaglebone cape manager + * + * Copyright (C) 2012 Texas Instruments Inc. + * Copyright (C) 2012-2015 Konsulko Group. + * Author: Pantelis Antoniou + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* disabled capes */ +static char *disable_partno; +module_param(disable_partno, charp, 0444); +MODULE_PARM_DESC(disable_partno, + "Comma delimited list of PART-NUMBER[:REV] of disabled capes"); + +/* enable capes */ +static char *enable_partno; +module_param(enable_partno, charp, 0444); +MODULE_PARM_DESC(enable_partno, + "Comma delimited list of PART-NUMBER[:REV] of enabled capes"); + +/* delay to scan on boot until rootfs appears */ +static int boot_scan_period = 1000; +module_param(boot_scan_period, int, 0444); +MODULE_PARM_DESC(boot_scan_period, + "boot scan period until rootfs firmware is available"); + +struct capemgr_info; + +struct slot_ee_attribute { + struct device_attribute devattr; + unsigned int field; + struct bone_cape_slot *slot; /* this is filled when instantiated */ +}; +#define to_slot_ee_attribute(x) \ + container_of((x), struct slot_ee_attribute, devattr) + +struct bbrd_ee_attribute { + struct device_attribute devattr; + unsigned int field; +}; +#define to_bbrd_ee_attribute(x) \ + container_of((x), struct bbrd_ee_attribute, devattr) + +struct bone_cape_slot { + struct list_head node; + struct capemgr_info *info; + int slotno; + struct nvmem_cell *nvmem_cell; + + char text_id[256]; + char signature[256]; + /* quick access */ + char board_name[32+1]; + char version[4+1]; + char manufacturer[16+1]; + char part_number[16+1]; + + /* attribute group */ + char *ee_attr_name; + int ee_attrs_count; + struct slot_ee_attribute *ee_attrs; + struct attribute **ee_attrs_tab; + struct attribute_group attrgroup; + + /* state flags */ + unsigned int probed : 1; + unsigned int probe_failed : 1; + unsigned int override : 1; + unsigned int loading : 1; + unsigned int loaded : 1; + unsigned int retry_loading : 1; + unsigned int disabled : 1; + + char *dtbo; + const struct firmware *fw; + struct device_node *overlay; + int overlay_id; + + /* loader thread */ + struct task_struct *loader_thread; + + /* load priority */ + int priority; +}; + +struct bone_baseboard { + + /* from the matched boardmap node */ + char *compatible_name; + + /* filled in by reading the eeprom */ + char signature[256]; + char text_id[64+1]; + + /* quick access */ + char board_name[8+1]; + char revision[4+1]; + char serial_number[12+1]; + + /* access to the eeprom */ + struct nvmem_cell *nvmem_cell; +}; + +struct capemgr_info { + struct platform_device *pdev; + + atomic_t next_slot_nr; + struct list_head slot_list; + struct mutex slots_list_mutex; + + /* baseboard EEPROM data */ + struct bone_baseboard baseboard; + + /* wait queue for keeping the priorities straight */ + wait_queue_head_t load_wq; +}; + +static int bone_slot_fill_override(struct bone_cape_slot *slot, + const char *part_number, const char *version); +static struct bone_cape_slot *capemgr_add_slot( + struct capemgr_info *info, const char *slot_name, + const char *part_number, const char *version, int prio); +static int capemgr_remove_slot_no_lock(struct bone_cape_slot *slot); +static int capemgr_remove_slot(struct bone_cape_slot *slot); +static int capemgr_load_slot(struct bone_cape_slot *slot); +static int capemgr_unload_slot(struct bone_cape_slot *slot); + +/* baseboard EEPROM field definition */ +#define BBRD_EE_FIELD_HEADER 0 +#define BBRD_EE_FIELD_BOARD_NAME 1 +#define BBRD_EE_FIELD_REVISION 2 +#define BBRD_EE_FIELD_SERIAL_NUMBER 3 +#define BBRD_EE_FIELD_CONFIG_OPTION 4 +#define BBRD_EE_FILED_RSVD1 5 +#define BBRD_EE_FILED_RSVD2 6 +#define BBRD_EE_FILED_RSVD3 7 + +/* cape EEPROM field definitions */ +#define CAPE_EE_FIELD_HEADER 0 +#define CAPE_EE_FIELD_EEPROM_REV 1 +#define CAPE_EE_FIELD_BOARD_NAME 2 +#define CAPE_EE_FIELD_VERSION 3 +#define CAPE_EE_FIELD_MANUFACTURER 4 +#define CAPE_EE_FIELD_PART_NUMBER 5 +#define CAPE_EE_FIELD_NUMBER_OF_PINS 6 +#define CAPE_EE_FIELD_SERIAL_NUMBER 7 +#define CAPE_EE_FIELD_PIN_USAGE 8 +#define CAPE_EE_FIELD_VDD_3V3EXP 9 +#define CAPE_EE_FIELD_VDD_5V 10 +#define CAPE_EE_FIELD_SYS_5V 11 +#define CAPE_EE_FIELD_DC_SUPPLIED 12 +#define CAPE_EE_FIELD_FIELDS_NR 13 + +#define EE_FIELD_MAKE_HEADER(p) \ + ({ \ + const u8 *_p = (p); \ + (((u32)_p[0] << 24) | ((u32)_p[1] << 16) | \ + ((u32)_p[2] << 8) | (u32)_p[3]); \ + }) + +#define EE_FIELD_HEADER_VALID 0xaa5533ee + +struct ee_field { + const char *name; + int start; + int size; + unsigned int ascii : 1; + unsigned int strip_trailing_dots : 1; + const char *override; +}; + +/* baseboard EEPROM definitions */ +static const struct ee_field bbrd_sig_fields[] = { + [BBRD_EE_FIELD_HEADER] = { + .name = "header", + .start = 0, + .size = 4, + .ascii = 0, + .override = "\xaa\x55\x33\xee", /* AA 55 33 EE */ + }, + [BBRD_EE_FIELD_BOARD_NAME] = { + .name = "board-name", + .start = 4, + .size = 8, + .ascii = 1, + .strip_trailing_dots = 1, + .override = "Board Name", + }, + [BBRD_EE_FIELD_REVISION] = { + .name = "revision", + .start = 12, + .size = 4, + .ascii = 1, + .override = "00A0", + }, + [BBRD_EE_FIELD_SERIAL_NUMBER] = { + .name = "serial-number", + .start = 16, + .size = 12, + .ascii = 1, + .override = "0000000000", + }, + [BBRD_EE_FIELD_CONFIG_OPTION] = { + .name = "config-option", + .start = 28, + .size = 32, + }, +}; + +/* cape EEPROM definitions */ +static const struct ee_field cape_sig_fields[] = { + [CAPE_EE_FIELD_HEADER] = { + .name = "header", + .start = 0, + .size = 4, + .ascii = 0, + .override = "\xaa\x55\x33\xee", /* AA 55 33 EE */ + }, + [CAPE_EE_FIELD_EEPROM_REV] = { + .name = "eeprom-format-revision", + .start = 4, + .size = 2, + .ascii = 1, + .override = "A0", + }, + [CAPE_EE_FIELD_BOARD_NAME] = { + .name = "board-name", + .start = 6, + .size = 32, + .ascii = 1, + .strip_trailing_dots = 1, + .override = "Override Board Name", + }, + [CAPE_EE_FIELD_VERSION] = { + .name = "version", + .start = 38, + .size = 4, + .ascii = 1, + .override = "00A0", + }, + [CAPE_EE_FIELD_MANUFACTURER] = { + .name = "manufacturer", + .start = 42, + .size = 16, + .ascii = 1, + .strip_trailing_dots = 1, + .override = "Override Manuf", + }, + [CAPE_EE_FIELD_PART_NUMBER] = { + .name = "part-number", + .start = 58, + .size = 16, + .ascii = 1, + .strip_trailing_dots = 1, + .override = "Override Part#", + }, + [CAPE_EE_FIELD_NUMBER_OF_PINS] = { + .name = "number-of-pins", + .start = 74, + .size = 2, + .ascii = 0, + .override = NULL, + }, + [CAPE_EE_FIELD_SERIAL_NUMBER] = { + .name = "serial-number", + .start = 76, + .size = 12, + .ascii = 1, + .override = "0000000000", + }, + [CAPE_EE_FIELD_PIN_USAGE] = { + .name = "pin-usage", + .start = 88, + .size = 140, + .ascii = 0, + .override = NULL, + }, + [CAPE_EE_FIELD_VDD_3V3EXP] = { + .name = "vdd-3v3exp", + .start = 228, + .size = 2, + .ascii = 0, + .override = NULL, + }, + [CAPE_EE_FIELD_VDD_5V] = { + .name = "vdd-5v", + .start = 230, + .size = 2, + .ascii = 0, + .override = NULL, + }, + [CAPE_EE_FIELD_SYS_5V] = { + .name = "sys-5v", + .start = 232, + .size = 2, + .ascii = 0, + .override = NULL, + }, + [CAPE_EE_FIELD_DC_SUPPLIED] = { + .name = "dc-supplied", + .start = 234, + .size = 2, + .ascii = 0, + .override = NULL, + }, +}; + +static char *ee_field_get(const struct ee_field *sig_field, + const void *data, int field, char *buf, int bufsz) +{ + int len; + + /* enough space? */ + if (bufsz < sig_field->size + sig_field->ascii) + return NULL; + + memcpy(buf, (char *)data + sig_field->start, sig_field->size); + + /* terminate ascii field */ + if (sig_field->ascii) + buf[sig_field->size] = '\0'; + + if (sig_field->strip_trailing_dots) { + len = strlen(buf); + while (len > 1 && buf[len - 1] == '.') + buf[--len] = '\0'; + } + + return buf; +} + +char *bbrd_ee_field_get(const void *data, + int field, char *buf, int bufsz) +{ + if ((unsigned int)field >= ARRAY_SIZE(bbrd_sig_fields)) + return NULL; + + return ee_field_get(&bbrd_sig_fields[field], data, field, buf, bufsz); +} + +char *cape_ee_field_get(const void *data, + int field, char *buf, int bufsz) +{ + if ((unsigned int)field >= ARRAY_SIZE(cape_sig_fields)) + return NULL; + + return ee_field_get(&cape_sig_fields[field], data, field, buf, bufsz); +} + +#ifdef CONFIG_OF +static const struct of_device_id capemgr_of_match[] = { + { + .compatible = "ti,bone-capemgr", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, capemgr_of_match); + +#endif + +static int bone_baseboard_scan(struct bone_baseboard *bbrd) +{ + struct capemgr_info *info = container_of(bbrd, + struct capemgr_info, baseboard); + const u8 *p; + int ret; + size_t len; + + p = nvmem_cell_read(bbrd->nvmem_cell, &len); + if (IS_ERR(p)) { + ret = PTR_ERR(p); + dev_err(&info->pdev->dev, + "Cannot read cell (ret=%d)\n", ret); + return ret; + } + if (len < sizeof(bbrd->signature)) { + dev_info(&info->pdev->dev, + "Short read %d (should be >= %d bytes)\n", + len, sizeof(bbrd->signature)); + return -EINVAL; + } + memcpy(bbrd->signature, p, sizeof(bbrd->signature)); + + p = bbrd->signature; + if (EE_FIELD_MAKE_HEADER(p) != EE_FIELD_HEADER_VALID) { + dev_err(&info->pdev->dev, "Invalid board signature '%08x'\n", + EE_FIELD_MAKE_HEADER(p)); + return -ENODEV; + } + + bbrd_ee_field_get(bbrd->signature, + BBRD_EE_FIELD_BOARD_NAME, + bbrd->board_name, sizeof(bbrd->board_name)); + bbrd_ee_field_get(bbrd->signature, + BBRD_EE_FIELD_REVISION, + bbrd->revision, sizeof(bbrd->revision)); + bbrd_ee_field_get(bbrd->signature, + BBRD_EE_FIELD_SERIAL_NUMBER, + bbrd->serial_number, sizeof(bbrd->serial_number)); + + /* board_name,version,manufacturer,part_number */ + snprintf(bbrd->text_id, sizeof(bbrd->text_id) - 1, + "%s,%s,%s", bbrd->board_name, bbrd->revision, + bbrd->serial_number); + + /* terminate always */ + bbrd->text_id[sizeof(bbrd->text_id) - 1] = '\0'; + + return 0; +} + +static int bone_slot_scan(struct bone_cape_slot *slot) +{ + struct capemgr_info *info = slot->info; + const u8 *p; + int r; + ssize_t len; + + /* need to read EEPROM? */ + if (slot->probed) + goto slot_fail_check; + + slot->probed = 1; + + if (!slot->override) { + + p = nvmem_cell_read(slot->nvmem_cell, &len); + if (IS_ERR(p)) { + r = PTR_ERR(p); + slot->probe_failed = 1; + + /* timeout is normal when no cape is present */ + if (r != -ETIMEDOUT) + dev_err(&info->pdev->dev, + "Cannot read cell (ret=%d)\n", r); + return r; + } + if (len < sizeof(slot->signature)) { + dev_info(&info->pdev->dev, + "Short read %d (should be >= %d bytes)\n", + len, sizeof(slot->signature)); + return -EINVAL; + } + memcpy(slot->signature, p, sizeof(slot->signature)); + + } else + dev_info(&info->pdev->dev, + "Using override eeprom data at slot %d\n", + slot->slotno); + + p = slot->signature; + if (EE_FIELD_MAKE_HEADER(p) != EE_FIELD_HEADER_VALID) { + dev_err(&info->pdev->dev, + "Invalid signature '%08x' at slot %d\n", + EE_FIELD_MAKE_HEADER(p), slot->slotno); + slot->probe_failed = 1; + return -ENODEV; + } + + cape_ee_field_get(slot->signature, + CAPE_EE_FIELD_BOARD_NAME, + slot->board_name, sizeof(slot->board_name)); + cape_ee_field_get(slot->signature, + CAPE_EE_FIELD_VERSION, + slot->version, sizeof(slot->version)); + cape_ee_field_get(slot->signature, + CAPE_EE_FIELD_MANUFACTURER, + slot->manufacturer, sizeof(slot->manufacturer)); + cape_ee_field_get(slot->signature, + CAPE_EE_FIELD_PART_NUMBER, + slot->part_number, sizeof(slot->part_number)); + + /* board_name,version,manufacturer,part_number */ + snprintf(slot->text_id, sizeof(slot->text_id) - 1, + "%s,%s,%s,%s", slot->board_name, slot->version, + slot->manufacturer, slot->part_number); + + /* terminate always */ + slot->text_id[sizeof(slot->text_id) - 1] = '\0'; + +slot_fail_check: + /* slot has failed and we don't support hotpluging */ + if (slot->probe_failed) + return -ENODEV; + + return 0; +} + +/* return 0 if not matched,, 1 if matched */ +static int bone_match_cape(const char *match, + const char *part_number, const char *version) +{ + char *tmp_part_number, *tmp_version; + char *buf, *s, *e, *sn; + int found; + + if (match == NULL || part_number == NULL) + return 0; + + /* copy the argument to work on it */ + buf = kstrdup(match, GFP_KERNEL); + + /* no memory, too bad... */ + if (buf == NULL) + return 0; + + found = 0; + s = buf; + e = s + strlen(s); + while (s < e) { + /* find comma separator */ + sn = strchr(s, ','); + if (sn != NULL) + *sn++ = '\0'; + else + sn = e; + tmp_part_number = s; + tmp_version = strchr(tmp_part_number, ':'); + if (tmp_version != NULL) + *tmp_version++ = '\0'; + s = sn; + + /* the part names must match */ + if (strcmp(tmp_part_number, part_number) != 0) + continue; + + /* if there's no version, match any */ + if (version == NULL || tmp_version == NULL || + strcmp(version, tmp_version) == 0) { + found = 1; + break; + } + } + + kfree(buf); + + return found; +} + +/* helper method */ +static int of_multi_prop_cmp(const struct property *prop, const char *value) +{ + const char *cp; + int cplen, vlen, l; + + /* check if it's directly compatible */ + cp = prop->value; + cplen = prop->length; + vlen = strlen(value); + + while (cplen > 0) { + /* compatible? */ + if (of_compat_cmp(cp, value, vlen) == 0) + return 0; + l = strlen(cp) + 1; + cp += l; + cplen -= l; + } + return -1; +} + +static ssize_t slot_ee_attr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct slot_ee_attribute *ee_attr = to_slot_ee_attribute(attr); + struct bone_cape_slot *slot = ee_attr->slot; + const struct ee_field *sig_field; + int i, len; + char *p, *s; + u16 val; + + /* add newline for ascii fields */ + sig_field = &cape_sig_fields[ee_attr->field]; + + len = sig_field->size + sig_field->ascii; + p = kmalloc(len, GFP_KERNEL); + if (p == NULL) + return -ENOMEM; + + s = cape_ee_field_get(slot->signature, ee_attr->field, p, len); + if (s == NULL) + return -EINVAL; + + /* add newline for ascii fields and return */ + if (sig_field->ascii) { + len = sprintf(buf, "%s\n", s); + goto out; + } + + /* case by case handling */ + switch (ee_attr->field) { + case CAPE_EE_FIELD_HEADER: + len = sprintf(buf, "%02x %02x %02x %02x\n", + s[0], s[1], s[2], s[3]); + break; + + /* 2 bytes */ + case CAPE_EE_FIELD_NUMBER_OF_PINS: + case CAPE_EE_FIELD_VDD_3V3EXP: + case CAPE_EE_FIELD_VDD_5V: + case CAPE_EE_FIELD_SYS_5V: + case CAPE_EE_FIELD_DC_SUPPLIED: + /* the bone is LE */ + val = s[0] & (s[1] << 8); + len = sprintf(buf, "%u\n", (unsigned int)val & 0xffff); + break; + + case CAPE_EE_FIELD_PIN_USAGE: + + len = 0; + for (i = 0; i < sig_field->size / 2; i++) { + /* the bone is LE */ + val = s[0] & (s[1] << 8); + sprintf(buf, "%04x\n", val); + buf += 5; + len += 5; + s += 2; + } + + break; + + default: + *buf = '\0'; + len = 0; + break; + } + +out: + kfree(p); + + return len; +} + +#define SLOT_EE_ATTR(_name, _field) \ + { \ + .devattr = __ATTR(_name, S_IRUGO, slot_ee_attr_show, NULL), \ + .field = CAPE_EE_FIELD_##_field, \ + .slot = NULL, \ + } + +static const struct slot_ee_attribute slot_ee_attrs[] = { + SLOT_EE_ATTR(header, HEADER), + SLOT_EE_ATTR(eeprom-format-revision, EEPROM_REV), + SLOT_EE_ATTR(board-name, BOARD_NAME), + SLOT_EE_ATTR(version, VERSION), + SLOT_EE_ATTR(manufacturer, MANUFACTURER), + SLOT_EE_ATTR(part-number, PART_NUMBER), + SLOT_EE_ATTR(number-of-pins, NUMBER_OF_PINS), + SLOT_EE_ATTR(serial-number, SERIAL_NUMBER), + SLOT_EE_ATTR(pin-usage, PIN_USAGE), + SLOT_EE_ATTR(vdd-3v3exp, VDD_3V3EXP), + SLOT_EE_ATTR(vdd-5v, VDD_5V), + SLOT_EE_ATTR(sys-5v, SYS_5V), + SLOT_EE_ATTR(dc-supplied, DC_SUPPLIED), +}; + +static int bone_cape_slot_sysfs_register(struct bone_cape_slot *slot) +{ + struct capemgr_info *info = slot->info; + struct device *dev = &info->pdev->dev; + struct slot_ee_attribute *ee_attr; + struct attribute_group *attrgroup; + int i, err, sz; + + slot->ee_attr_name = kasprintf(GFP_KERNEL, "slot-%d", slot->slotno); + if (slot->ee_attr_name == NULL) { + dev_err(dev, "slot #%d: Failed to allocate ee_attr_name\n", + slot->slotno); + err = -ENOMEM; + goto err_fail_no_ee_attr_name; + } + + slot->ee_attrs_count = ARRAY_SIZE(slot_ee_attrs); + + sz = slot->ee_attrs_count * sizeof(*slot->ee_attrs); + slot->ee_attrs = kmalloc(sz, GFP_KERNEL); + if (slot->ee_attrs == NULL) { + dev_err(dev, "slot #%d: Failed to allocate ee_attrs\n", + slot->slotno); + err = -ENOMEM; + goto err_fail_no_ee_attrs; + } + + attrgroup = &slot->attrgroup; + memset(attrgroup, 0, sizeof(*attrgroup)); + attrgroup->name = slot->ee_attr_name; + + sz = sizeof(*slot->ee_attrs_tab) * (slot->ee_attrs_count + 1); + attrgroup->attrs = kmalloc(sz, GFP_KERNEL); + if (attrgroup->attrs == NULL) { + dev_err(dev, "slot #%d: Failed to allocate ee_attrs_tab\n", + slot->slotno); + err = -ENOMEM; + goto err_fail_no_ee_attrs_tab; + } + /* copy everything over */ + memcpy(slot->ee_attrs, slot_ee_attrs, sizeof(slot_ee_attrs)); + + /* bind this attr to the slot */ + for (i = 0; i < slot->ee_attrs_count; i++) { + ee_attr = &slot->ee_attrs[i]; + ee_attr->slot = slot; + attrgroup->attrs[i] = &ee_attr->devattr.attr; + } + attrgroup->attrs[i] = NULL; + + /* make lockdep happy */ + for (i = 0; i < slot->ee_attrs_count; i++) { + ee_attr = &slot->ee_attrs[i]; + sysfs_attr_init(&ee_attr->devattr.attr); + } + + err = sysfs_create_group(&dev->kobj, attrgroup); + if (err != 0) { + dev_err(dev, "slot #%d: Failed to allocate ee_attrs_tab\n", + slot->slotno); + err = -ENOMEM; + goto err_fail_no_ee_attrs_group; + } + + return 0; + +err_fail_no_ee_attrs_group: + kfree(slot->ee_attrs_tab); +err_fail_no_ee_attrs_tab: + kfree(slot->ee_attrs); +err_fail_no_ee_attrs: + kfree(slot->ee_attr_name); +err_fail_no_ee_attr_name: + return err; +} + +static void bone_cape_slot_sysfs_unregister(struct bone_cape_slot *slot) +{ + struct capemgr_info *info = slot->info; + struct device *dev = &info->pdev->dev; + + sysfs_remove_group(&dev->kobj, &slot->attrgroup); + kfree(slot->ee_attrs_tab); + kfree(slot->ee_attrs); + kfree(slot->ee_attr_name); +} + +static ssize_t bbrd_ee_attr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bbrd_ee_attribute *ee_attr = to_bbrd_ee_attribute(attr); + struct platform_device *pdev = to_platform_device(dev); + struct capemgr_info *info = platform_get_drvdata(pdev); + struct bone_baseboard *bbrd = &info->baseboard; + const struct ee_field *sig_field; + u16 val; + int i, len; + char *p, *s; + + /* add newline for ascii fields */ + sig_field = &bbrd_sig_fields[ee_attr->field]; + + len = sig_field->size + sig_field->ascii; + p = kmalloc(len, GFP_KERNEL); + if (p == NULL) + return -ENOMEM; + + s = bbrd_ee_field_get(bbrd->signature, ee_attr->field, p, len); + if (s == NULL) + return -EINVAL; + + /* add newline for ascii fields and return */ + if (sig_field->ascii) { + len = sprintf(buf, "%s\n", s); + goto out; + } + + /* case by case handling */ + switch (ee_attr->field) { + case BBRD_EE_FIELD_HEADER: + len = sprintf(buf, "%02x %02x %02x %02x\n", + s[0], s[1], s[2], s[3]); + break; + + case BBRD_EE_FIELD_CONFIG_OPTION: + len = 0; + for (i = 0; i < sig_field->size / 2; i++) { + /* the bone is LE */ + val = s[0] & (s[1] << 8); + sprintf(buf, "%04x\n", val); + buf += 5; + len += 5; + s += 2; + } + break; + + default: + *buf = '\0'; + len = 0; + break; + } + +out: + kfree(p); + + return len; +} + +#define BBRD_EE_ATTR(_name, _field) \ + { \ + .devattr = __ATTR(_name, 0440, bbrd_ee_attr_show, NULL), \ + .field = BBRD_EE_FIELD_##_field, \ + } + +static struct bbrd_ee_attribute bbrd_ee_attrs[] = { + BBRD_EE_ATTR(header, HEADER), + BBRD_EE_ATTR(board-name, BOARD_NAME), + BBRD_EE_ATTR(revision, REVISION), + BBRD_EE_ATTR(serial-number, SERIAL_NUMBER), + BBRD_EE_ATTR(config-option, CONFIG_OPTION), +}; + +static struct attribute *bbrd_attrs_flat[] = { + &bbrd_ee_attrs[BBRD_EE_FIELD_HEADER].devattr.attr, + &bbrd_ee_attrs[BBRD_EE_FIELD_BOARD_NAME].devattr.attr, + &bbrd_ee_attrs[BBRD_EE_FIELD_REVISION].devattr.attr, + &bbrd_ee_attrs[BBRD_EE_FIELD_SERIAL_NUMBER].devattr.attr, + &bbrd_ee_attrs[BBRD_EE_FIELD_CONFIG_OPTION].devattr.attr, + NULL, +}; + +static const struct attribute_group bbrd_attr_group = { + .name = "baseboard", + .attrs = bbrd_attrs_flat, +}; + +static ssize_t slots_show(struct device *dev, struct device_attribute *attr, + char *buf); +static ssize_t slots_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); + +static DEVICE_ATTR(slots, 0644, slots_show, slots_store); + +static struct attribute *root_attrs_flat[] = { + &dev_attr_slots.attr, + NULL, +}; + +static const struct attribute_group root_attr_group = { + .attrs = root_attrs_flat, +}; + +static const struct attribute_group *attr_groups[] = { + &root_attr_group, + &bbrd_attr_group, + NULL, +}; + +static ssize_t slots_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct capemgr_info *info = platform_get_drvdata(pdev); + struct bone_cape_slot *slot; + ssize_t len, sz; + + mutex_lock(&info->slots_list_mutex); + sz = 0; + list_for_each_entry(slot, &info->slot_list, node) { + + len = sprintf(buf, "%2d: %c%c%c%c%c%c %3d %s\n", + slot->slotno, + slot->probed ? 'P' : '-', + slot->probe_failed ? 'F' : '-', + slot->override ? 'O' : '-', + slot->loading ? 'l' : '-', + slot->loaded ? 'L' : '-', + slot->disabled ? 'D' : '-', + slot->overlay_id, slot->text_id); + + buf += len; + sz += len; + } + mutex_unlock(&info->slots_list_mutex); + + return sz; +} + +static ssize_t slots_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct capemgr_info *info = platform_get_drvdata(pdev); + struct bone_cape_slot *slot; + struct device_node *pnode, *node; + char *s, *part_number, *version; + int ret; + int slotno; + + /* check for remove slot */ + if (strlen(buf) > 0 && buf[0] == '-') { + ret = kstrtoint(buf + 1, 10, &slotno); + if (ret != 0) + return ret; + + /* now load each (take lock to be sure */ + mutex_lock(&info->slots_list_mutex); + list_for_each_entry(slot, &info->slot_list, node) { + if (slotno == slot->slotno) + goto found; + } + + mutex_unlock(&info->slots_list_mutex); + return -ENODEV; +found: + /* the hardware slots just get unloaded */ + if (!slot->override) { + ret = capemgr_unload_slot(slot); + if (ret == 0) + dev_info(&pdev->dev, + "Unloaded slot #%d\n", slotno); + else + dev_err(&pdev->dev, + "Failed to unload slot #%d\n", slotno); + } else { + ret = capemgr_remove_slot_no_lock(slot); + if (ret == 0) + dev_info(&pdev->dev, + "Removed slot #%d\n", slotno); + else + dev_err(&pdev->dev, + "Failed to remove slot #%d\n", slotno); + } + mutex_unlock(&info->slots_list_mutex); + + return ret == 0 ? strlen(buf) : ret; + } + + part_number = kstrdup(buf, GFP_KERNEL); + if (part_number == NULL) + return -ENOMEM; + + /* remove trailing spaces dots and newlines */ + s = part_number + strlen(part_number); + while (s > part_number && + (isspace(s[-1]) || s[-1] == '\n' || s[-1] == '.')) + *--s = '\0'; + + version = strchr(part_number, ':'); + if (version != NULL) + *version++ = '\0'; + + dev_info(&pdev->dev, "part_number '%s', version '%s'\n", + part_number, version ? version : "N/A"); + + pnode = pdev->dev.of_node; + node = NULL; + slot = NULL; + ret = 0; + + /* no specific slot found, try immediate */ + slot = capemgr_add_slot(info, NULL, part_number, version, 0); + + if (IS_ERR_OR_NULL(slot)) { + dev_err(&pdev->dev, "Failed to add slot #%d\n", + atomic_read(&info->next_slot_nr) - 1); + ret = slot ? PTR_ERR(slot) : -ENODEV; + slot = NULL; + goto err_fail; + } + + kfree(part_number); + + ret = capemgr_load_slot(slot); + if (ret != 0) + capemgr_remove_slot(slot); + + return ret == 0 ? strlen(buf) : ret; +err_fail: + of_node_put(node); + kfree(part_number); + return ret; +} + +/* verify the overlay */ +static int capemgr_verify_overlay(struct bone_cape_slot *slot) +{ + struct capemgr_info *info = slot->info; + struct device *dev = &info->pdev->dev; + struct bone_baseboard *bbrd = &info->baseboard; + struct device_node *node = slot->overlay; + struct property *prop; + struct bone_cape_slot *slotn; + int err, counta, countb, i, j; + const char *ra, *rb; + + /* validate */ + if (node == NULL) { + dev_err(dev, "slot #%d: No overlay for '%s'\n", + slot->slotno, slot->part_number); + return -EINVAL; + } + + /* check if the slot is compatible with the board */ + prop = of_find_property(node, "compatible", NULL); + + /* no compatible property? */ + if (prop == NULL) { + dev_err(dev, "slot #%d: No compatible property for '%s'\n", + slot->slotno, slot->part_number); + return -EINVAL; + } + + /* verify that the cape is baseboard compatible */ + if (of_multi_prop_cmp(prop, bbrd->compatible_name) != 0) { + dev_err(dev, "slot #%d: Incompatible with baseboard for '%s'\n", + slot->slotno, slot->part_number); + return -EINVAL; + } + + /* count the strings */ + counta = of_property_count_strings(node, "exclusive-use"); + /* no valid property, or no resources; no matter, it's OK */ + if (counta <= 0) + return 0; + + /* and now check if there's a resource conflict */ + err = 0; + mutex_lock(&info->slots_list_mutex); + for (i = 0; i < counta; i++) { + + ra = NULL; + err = of_property_read_string_index(node, "exclusive-use", + i, &ra); + if (err != 0) { + dev_err(dev, "slot #%d: Could not read string #%d\n", + slot->slotno, i); + break; + } + + list_for_each_entry(slotn, &info->slot_list, node) { + + /* don't check against self */ + if (slot == slotn) + continue; + + /* only check against loaded or loading slots */ + if (!slotn->loaded && !slotn->loading) + continue; + + countb = of_property_count_strings(slotn->overlay, + "exclusive-use"); + /* no valid property, or resources; it's OK */ + if (countb <= 0) + continue; + + + for (j = 0; j < countb; j++) { + + /* count the resources */ + rb = NULL; + err = of_property_read_string_index( + slotn->overlay, "exclusive-use", + j, &rb); + if (err != 0) { + /* error, but we don't care */ + err = 0; + break; + } + + /* ignore case; just in case ;) */ + if (strcasecmp(ra, rb) == 0) { + + /* resource conflict */ + err = -EEXIST; + dev_err(dev, + "slot #%d: %s conflict %s (#%d:%s)\n", + slot->slotno, + slot->part_number, ra, + slotn->slotno, + slotn->part_number); + goto out; + } + } + } + } +out: + mutex_unlock(&info->slots_list_mutex); + + return err; +} + +static int capemgr_load_slot(struct bone_cape_slot *slot) +{ + struct capemgr_info *info = slot->info; + struct device *dev = &info->pdev->dev; + const char *dtbo; + int err; + + if (slot->probe_failed) { + dev_err(dev, "slot #%d: probe failed for '%s'\n", + slot->slotno, slot->part_number); + return -ENODEV; + } + + if (slot->loaded) { + dev_err(dev, "slot #%d: already loaded for '%s'\n", + slot->slotno, slot->part_number); + return -EAGAIN; + } + + /* make sure we don't leak this on repeated calls */ + kfree(slot->dtbo); + slot->dtbo = NULL; + + dev_dbg(dev, "slot #%d: Requesting part number/version based '%s-%s.dtbo\n", + slot->slotno, slot->part_number, slot->version); + + /* request the part number + .dtbo*/ + slot->dtbo = kasprintf(GFP_KERNEL, "%s-%s.dtbo", + slot->part_number, slot->version); + if (slot->dtbo == NULL) { + dev_err(dev, "slot #%d: Failed to get dtbo '%s'\n", + slot->slotno, dtbo); + return -ENOMEM; + } + + dev_dbg(dev, "slot #%d: Requesting firmware '%s' for board-name '%s', version '%s'%s\n", + slot->slotno, + slot->dtbo, slot->board_name, slot->version, + system_state == SYSTEM_BOOTING ? " - booting" : ""); + + err = request_firmware_direct(&slot->fw, slot->dtbo, dev); + if (err != 0) { + dev_dbg(dev, "failed to load firmware '%s'\n", slot->dtbo); + goto err_fail_no_fw; + } + + dev_dbg(dev, "slot #%d: dtbo '%s' loaded; converting to live tree\n", + slot->slotno, slot->dtbo); + + of_fdt_unflatten_tree((unsigned long *)slot->fw->data, NULL, + &slot->overlay); + if (slot->overlay == NULL) { + dev_err(dev, "slot #%d: Failed to unflatten\n", + slot->slotno); + err = -EINVAL; + goto err_fail; + } + + /* mark it as detached */ + of_node_set_flag(slot->overlay, OF_DETACHED); + + /* perform resolution */ + err = of_resolve_phandles(slot->overlay); + if (err != 0) { + dev_err(dev, "slot #%d: Failed to resolve tree\n", + slot->slotno); + goto err_fail; + } + + err = capemgr_verify_overlay(slot); + if (err != 0) { + dev_err(dev, "slot #%d: Failed verification\n", + slot->slotno); + goto err_fail; + } + + err = of_overlay_create(slot->overlay); + if (err < 0) { + dev_err(dev, "slot #%d: Failed to create overlay\n", + slot->slotno); + goto err_fail; + } + slot->overlay_id = err; + + slot->loading = 0; + slot->loaded = 1; + + dev_info(dev, "slot #%d: dtbo '%s' loaded; overlay id #%d\n", + slot->slotno, slot->dtbo, slot->overlay_id); + + return 0; + +err_fail: + + /* TODO: free the overlay, we can't right now cause + * the unflatten method does not track it */ + slot->overlay = NULL; + + release_firmware(slot->fw); + slot->fw = NULL; + +err_fail_no_fw: + slot->loading = 0; + return err; +} + +static int capemgr_unload_slot(struct bone_cape_slot *slot) +{ + if (!slot->loaded || slot->overlay_id == -1) + return -EINVAL; + + of_overlay_destroy(slot->overlay_id); + slot->overlay_id = -1; + + slot->loaded = 0; + + return 0; + +} + +/* slots_list_mutex must be taken */ +static int capemgr_remove_slot_no_lock(struct bone_cape_slot *slot) +{ + struct capemgr_info *info = slot->info; + struct device *dev = &info->pdev->dev; + int ret; + + if (slot == NULL) + return 0; + + if (slot->loaded && slot->overlay_id >= 0) { + /* unload just in case */ + ret = capemgr_unload_slot(slot); + if (ret != 0) { + dev_err(dev, "Unable to unload slot #%d\n", + slot->slotno); + return ret; + } + } + + /* if probed OK, remove the sysfs nodes */ + if (slot->probed && !slot->probe_failed) + bone_cape_slot_sysfs_unregister(slot); + + /* remove it from the list */ + list_del(&slot->node); + + if (slot->nvmem_cell) + nvmem_cell_put(slot->nvmem_cell); + devm_kfree(dev, slot); + return 0; +} + +static int capemgr_remove_slot(struct bone_cape_slot *slot) +{ + struct capemgr_info *info = slot->info; + int ret; + + mutex_lock(&info->slots_list_mutex); + ret = capemgr_remove_slot_no_lock(slot); + mutex_unlock(&info->slots_list_mutex); + + return ret; +} + +static int bone_slot_fill_override(struct bone_cape_slot *slot, + const char *part_number, const char *version) +{ + const struct ee_field *sig_field; + int i, len, has_part_number; + char *p; + + slot->probe_failed = 0; + slot->probed = 0; + + /* zero out signature */ + memset(slot->signature, 0, + sizeof(slot->signature)); + + /* first, fill in all with override defaults */ + for (i = 0; i < ARRAY_SIZE(cape_sig_fields); i++) { + + sig_field = &cape_sig_fields[i]; + + /* point to the entry */ + p = slot->signature + sig_field->start; + + if (sig_field->override) + memcpy(p, sig_field->override, + sig_field->size); + else + memset(p, 0, sig_field->size); + } + + /* if a part_number is supplied use it */ + len = part_number ? strlen(part_number) : 0; + if (len > 0) { + sig_field = &cape_sig_fields[CAPE_EE_FIELD_PART_NUMBER]; + + /* point to the entry */ + p = slot->signature + sig_field->start; + + /* copy and zero out any remainder */ + if (len > sig_field->size) + len = sig_field->size; + memcpy(p, part_number, len); + if (len < sig_field->size) + memset(p + len, 0, sig_field->size - len); + + has_part_number = 1; + } + + /* if a version is supplied use it */ + len = version ? strlen(version) : 0; + if (len > 0) { + sig_field = &cape_sig_fields[CAPE_EE_FIELD_VERSION]; + + /* point to the entry */ + p = slot->signature + sig_field->start; + + /* copy and zero out any remainder */ + if (len > sig_field->size) + len = sig_field->size; + memcpy(p, version, len); + if (len < sig_field->size) + memset(p + len, 0, sig_field->size - len); + } + + /* we must have a part number */ + if (!has_part_number) + return -EINVAL; + + slot->override = 1; + + return 0; +} + +static struct bone_cape_slot * +capemgr_add_slot(struct capemgr_info *info, const char *slot_name, + const char *part_number, const char *version, int prio) +{ + struct bone_cape_slot *slot; + struct device *dev = &info->pdev->dev; + int slotno; + int ret; + + slotno = atomic_inc_return(&info->next_slot_nr) - 1; + + slot = devm_kzalloc(dev, sizeof(*slot), GFP_KERNEL); + if (slot == NULL) + return ERR_PTR(-ENOMEM); + + slot->info = info; + slot->slotno = slotno; + slot->priority = prio; + slot->overlay_id = -1; + + if (slot_name) { + slot->nvmem_cell = nvmem_cell_get(dev, slot_name); + if (IS_ERR(slot->nvmem_cell)) { + ret = PTR_ERR(slot->nvmem_cell); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get slot eeprom cell\n"); + slot->nvmem_cell = NULL; + goto err_out; + } + } else { + dev_info(dev, "slot #%d: override\n", slotno); + + /* fill in everything with defaults first */ + ret = bone_slot_fill_override(slot, part_number, version); + if (ret != 0) { + dev_err(dev, "slot #%d: override failed\n", slotno); + goto err_out; + } + } + + ret = bone_slot_scan(slot); + if (ret != 0) { + + if (!slot->probe_failed) { + dev_err(dev, "slot #%d: scan failed\n", + slotno); + goto err_out; + } + + dev_err(dev, "slot #%d: No cape found\n", slotno); + /* but all is fine */ + } else { + + dev_info(dev, "slot #%d: '%s'\n", + slotno, slot->text_id); + + ret = bone_cape_slot_sysfs_register(slot); + if (ret != 0) { + dev_err(dev, "slot #%d: sysfs register failed\n", + slotno); + goto err_out; + } + + } + + /* add to the slot list */ + mutex_lock(&info->slots_list_mutex); + list_add_tail(&slot->node, &info->slot_list); + mutex_unlock(&info->slots_list_mutex); + + return slot; + +err_out: + if (slot->nvmem_cell) + nvmem_cell_put(slot->nvmem_cell); + devm_kfree(dev, slot); + return ERR_PTR(ret); +} + +/* return 1 if it makes sense to retry loading */ +static int retry_loading_condition(struct bone_cape_slot *slot) +{ + struct capemgr_info *info = slot->info; + struct device *dev = &info->pdev->dev; + struct bone_cape_slot *slotn; + int ret; + + dev_dbg(dev, "loader: retry_loading slot-%d %s:%s (prio %d)\n", + slot->slotno, slot->part_number, slot->version, + slot->priority); + + mutex_lock(&info->slots_list_mutex); + ret = 0; + list_for_each_entry(slotn, &info->slot_list, node) { + /* if same slot or not loading skip */ + if (!slotn->loading || slotn->retry_loading) + continue; + /* at least one cape is still loading (without retrying) */ + ret = 1; + } + mutex_unlock(&info->slots_list_mutex); + return ret; +} + +/* return 1 if this slot is clear to try to load now */ +static int clear_to_load_condition(struct bone_cape_slot *slot) +{ + struct capemgr_info *info = slot->info; + int my_prio = slot->priority; + struct device *dev = &info->pdev->dev; + int ret; + + dev_dbg(dev, "loader: check slot-%d %s:%s (prio %d)\n", slot->slotno, + slot->part_number, slot->version, slot->priority); + + mutex_lock(&info->slots_list_mutex); + ret = 1; + list_for_each_entry(slot, &info->slot_list, node) { + /* if any slot is loading with lowest priority */ + if (!slot->loading) + continue; + if (slot->priority < my_prio) { + ret = 0; + break; + } + } + mutex_unlock(&info->slots_list_mutex); + return ret; +} + +static int capemgr_loader(void *data) +{ + struct bone_cape_slot *slot = data; + struct capemgr_info *info = slot->info; + struct device *dev = &info->pdev->dev; + int ret, done, other_loading, booting; + + done = 0; + + slot->retry_loading = 0; + + dev_dbg(dev, "loader: before slot-%d %s:%s (prio %d)\n", slot->slotno, + slot->part_number, slot->version, slot->priority); + + /* + * We have a basic priority based arbitration system + * Slots have priorities, so the lower priority ones + * should start loading first. So each time we end up + * here. + */ + ret = wait_event_interruptible(info->load_wq, + clear_to_load_condition(slot)); + if (ret < 0) { + dev_warn(dev, "loader, Signal pending\n"); + return ret; + } + + dev_dbg(dev, "loader: after slot-%d %s:%s (prio %d)\n", slot->slotno, + slot->part_number, slot->version, slot->priority); + + /* using the return value */ + ret = capemgr_load_slot(slot); + + /* wake up all just in case */ + wake_up_interruptible_all(&info->load_wq); + + if (ret == 0) + goto done; + + dev_dbg(dev, "loader: retrying slot-%d %s:%s (prio %d)\n", slot->slotno, + slot->part_number, slot->version, slot->priority); + + /* first attempt has failed; now try each time there's any change */ + slot->retry_loading = 1; + + for (;;) { + booting = (system_state == SYSTEM_BOOTING); + other_loading = retry_loading_condition(slot); + if (!booting && !other_loading) + break; + + /* simple wait for someone to kick us */ + if (other_loading) { + DEFINE_WAIT(__wait); + + prepare_to_wait(&info->load_wq, &__wait, + TASK_INTERRUPTIBLE); + finish_wait(&info->load_wq, &__wait); + } else { + /* always delay when booting */ + msleep(boot_scan_period); + } + + if (signal_pending(current)) { + dev_warn(dev, "loader, Signal pending\n"); + ret = -ERESTARTSYS; + goto done; + } + + /* using the return value */ + ret = capemgr_load_slot(slot); + if (ret == 0) + goto done; + + /* wake up all just in case */ + wake_up_interruptible_all(&info->load_wq); + } + +done: + slot->loading = 0; + slot->retry_loading = 0; + + if (ret == 0) { + dev_dbg(dev, "loader: done slot-%d %s:%s (prio %d)\n", + slot->slotno, slot->part_number, slot->version, + slot->priority); + } else { + dev_err(dev, "loader: failed to load slot-%d %s:%s (prio %d)\n", + slot->slotno, slot->part_number, slot->version, + slot->priority); + + /* if it's a override slot remove it */ + if (slot->override) + capemgr_remove_slot(slot); + } + + return ret; +} + +static int +capemgr_probe(struct platform_device *pdev) +{ + struct capemgr_info *info; + struct bone_baseboard *bbrd; + struct bone_cape_slot *slot; + struct device_node *pnode = pdev->dev.of_node; + struct device_node *baseboardmaps_node; + struct device_node *node; + const char *part_number; + const char *version; + const char *board_name; + const char *compatible_name; + char slot_name[16]; + u32 slots_nr; + int i, ret, len, prio; + long val; + char *wbuf, *s, *p, *e; + + /* we don't use platform_data at all; we require OF */ + if (pnode == NULL) + return -ENOTSUPP; + + info = devm_kzalloc(&pdev->dev, + sizeof(struct capemgr_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pdev = pdev; + platform_set_drvdata(pdev, info); + + atomic_set(&info->next_slot_nr, 0); + INIT_LIST_HEAD(&info->slot_list); + mutex_init(&info->slots_list_mutex); + + init_waitqueue_head(&info->load_wq); + + baseboardmaps_node = NULL; + + /* find the baseboard */ + bbrd = &info->baseboard; + + baseboardmaps_node = of_get_child_by_name(pnode, "baseboardmaps"); + if (baseboardmaps_node == NULL) { + dev_err(&pdev->dev, "Failed to get baseboardmaps node"); + ret = -ENODEV; + goto err_exit; + } + + bbrd->nvmem_cell = nvmem_cell_get(&pdev->dev, "baseboard"); + if (IS_ERR(bbrd->nvmem_cell)) { + ret = PTR_ERR(bbrd->nvmem_cell); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to get baseboard eeprom cell\n"); + bbrd->nvmem_cell = NULL; + goto err_exit; + } + + ret = bone_baseboard_scan(bbrd); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to scan baseboard eeprom\n"); + goto err_exit; + } + + dev_info(&pdev->dev, "Baseboard: '%s'\n", bbrd->text_id); + + board_name = NULL; + compatible_name = NULL; + for_each_child_of_node(baseboardmaps_node, node) { + /* there must be board-name */ + if (of_property_read_string(node, "board-name", + &board_name) != 0 || + of_property_read_string(node, "compatible-name", + &compatible_name) != 0) + continue; + + if (strcmp(bbrd->board_name, board_name) == 0) + break; + } + of_node_put(baseboardmaps_node); + baseboardmaps_node = NULL; + + if (node == NULL) { + dev_err(&pdev->dev, "Failed to find compatible map for %s\n", + bbrd->board_name); + ret = -ENODEV; + goto err_exit; + } + bbrd->compatible_name = kstrdup(compatible_name, GFP_KERNEL); + if (bbrd->compatible_name == NULL) { + ret = -ENOMEM; + goto err_exit; + } + of_node_put(node); + + /* get slot number */ + ret = of_property_read_u32(pnode, "#slots", &slots_nr); + if (ret != 0) + slots_nr = 0; + + dev_info(&pdev->dev, "compatible-baseboard=%s - #slots=%d\n", + bbrd->compatible_name, slots_nr); + + for (i = 0; i < slots_nr; i++) { + snprintf(slot_name, sizeof(slot_name), "slot%d", i); + slot = capemgr_add_slot(info, slot_name, NULL, NULL, 0); + if (IS_ERR(slot)) { + dev_err(&pdev->dev, "Failed to add slot #%d\n", + atomic_read(&info->next_slot_nr)); + ret = PTR_ERR(slot); + goto err_exit; + } + } + + /* iterate over enable_partno (if there) */ + if (enable_partno && strlen(enable_partno) > 0) { + + /* allocate a temporary buffer */ + wbuf = devm_kzalloc(&pdev->dev, PAGE_SIZE, GFP_KERNEL); + if (wbuf == NULL) { + ret = -ENOMEM; + goto err_exit; + } + + /* add any enable_partno capes */ + s = enable_partno; + while (*s) { + /* form is PART[:REV[:PRIO]],PART.. */ + p = strchr(s, ','); + if (p == NULL) + e = s + strlen(s); + else + e = p; + + /* copy to temp buffer */ + len = e - s; + if (len >= PAGE_SIZE - 1) + len = PAGE_SIZE - 1; + memcpy(wbuf, s, len); + wbuf[len] = '\0'; + + /* move to the next */ + s = *e ? e + 1 : e; + + part_number = wbuf; + + /* default version is NULL & prio is 0 */ + version = NULL; + prio = 0; + + /* now split the rev & prio part */ + p = strchr(wbuf, ':'); + if (p != NULL) { + *p++ = '\0'; + if (*p != ':') + version = p; + p = strchr(p, ':'); + if (p != NULL) { + *p++ = '\0'; + ret = kstrtol(p, 10, &val); + if (ret == 0) + prio = val; + } + } + + dev_info(&pdev->dev, + "enabled_partno PARTNO '%s' VER '%s' PR '%d'\n", + part_number, + version ? version : "N/A", prio); + + /* only immediate slots are allowed here */ + slot = capemgr_add_slot(info, NULL, + part_number, version, prio); + + /* we continue even in case of an error */ + if (IS_ERR_OR_NULL(slot)) { + dev_warn(&pdev->dev, "Failed to add slot #%d\n", + atomic_read(&info->next_slot_nr) - 1); + } + } + + devm_kfree(&pdev->dev, wbuf); + } + + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (IS_ERR_VALUE(ret)) { + dev_err(&pdev->dev, "Failed to pm_runtime_get_sync()\n"); + goto err_exit; + } + + pm_runtime_put(&pdev->dev); + + /* it is safe to create the attribute groups */ + ret = sysfs_create_groups(&pdev->dev.kobj, attr_groups); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to create sysfs attributes\n"); + goto err_exit; + } + /* automatically cleared by driver core now */ + pdev->dev.groups = attr_groups; + + /* now load each (take lock to be sure */ + mutex_lock(&info->slots_list_mutex); + + list_for_each_entry(slot, &info->slot_list, node) { + + /* if matches the disabled ones skip */ + if (bone_match_cape(disable_partno, slot->part_number, + slot->version)) { + dev_info(&pdev->dev, + "Skipping loading of disabled cape with part# %s\n", + slot->part_number); + slot->disabled = 1; + continue; + } + + if (!slot->probe_failed && !slot->loaded) + slot->loading = 1; + } + + /* now start the loader thread(s) (all at once) */ + list_for_each_entry(slot, &info->slot_list, node) { + + if (!slot->loading) + continue; + + slot->loader_thread = kthread_run(capemgr_loader, + slot, "capemgr-loader-%d", + slot->slotno); + if (IS_ERR(slot->loader_thread)) { + dev_warn(&pdev->dev, "slot #%d: Failed to start loader\n", + slot->slotno); + slot->loader_thread = NULL; + } + } + mutex_unlock(&info->slots_list_mutex); + + dev_info(&pdev->dev, "initialized OK.\n"); + + return 0; + +err_exit: + if (bbrd->nvmem_cell) + nvmem_cell_put(bbrd->nvmem_cell); + of_node_put(baseboardmaps_node); + platform_set_drvdata(pdev, NULL); + devm_kfree(&pdev->dev, info); + + return ret; +} + +static int capemgr_remove(struct platform_device *pdev) +{ + struct capemgr_info *info = platform_get_drvdata(pdev); + struct bone_baseboard *bbrd = &info->baseboard; + struct bone_cape_slot *slot, *slotn; + int ret; + + mutex_lock(&info->slots_list_mutex); + list_for_each_entry_safe(slot, slotn, &info->slot_list, node) + capemgr_remove_slot_no_lock(slot); + mutex_unlock(&info->slots_list_mutex); + + platform_set_drvdata(pdev, NULL); + + ret = pm_runtime_get_sync(&pdev->dev); + if (IS_ERR_VALUE(ret)) + return ret; + + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + if (bbrd->nvmem_cell) + nvmem_cell_put(bbrd->nvmem_cell); + devm_kfree(&pdev->dev, info); + + return 0; +} + +static struct platform_driver capemgr_driver = { + .probe = capemgr_probe, + .remove = capemgr_remove, + .driver = { + .name = "bone_capemgr", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(capemgr_of_match), + }, +}; + +module_platform_driver(capemgr_driver); + +MODULE_AUTHOR("Pantelis Antoniou"); +MODULE_DESCRIPTION("Beaglebone cape manager"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bone_capemgr"); diff --git b/drivers/misc/cape/Kconfig b/drivers/misc/cape/Kconfig new file mode 100644 index 0000000..a2ef85e --- /dev/null +++ b/drivers/misc/cape/Kconfig @@ -0,0 +1,5 @@ +# +# Capes +# + +source "drivers/misc/cape/beaglebone/Kconfig" diff --git b/drivers/misc/cape/Makefile b/drivers/misc/cape/Makefile new file mode 100644 index 0000000..7c4eb96 --- /dev/null +++ b/drivers/misc/cape/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for cape like devices +# + +obj-y += beaglebone/ diff --git b/drivers/misc/cape/beaglebone/Kconfig b/drivers/misc/cape/beaglebone/Kconfig new file mode 100644 index 0000000..eeb6782 --- /dev/null +++ b/drivers/misc/cape/beaglebone/Kconfig @@ -0,0 +1,10 @@ +# +# Beaglebone capes +# + +config BEAGLEBONE_PINMUX_HELPER + tristate "Beaglebone Pinmux Helper" + depends on ARCH_OMAP2PLUS && OF + default n + help + Say Y here to include support for the pinmux helper diff --git b/drivers/misc/cape/beaglebone/Makefile b/drivers/misc/cape/beaglebone/Makefile new file mode 100644 index 0000000..7f4617a --- /dev/null +++ b/drivers/misc/cape/beaglebone/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for beaglebone capes +# + +obj-$(CONFIG_BEAGLEBONE_PINMUX_HELPER) += bone-pinmux-helper.o diff --git b/drivers/misc/cape/beaglebone/bone-pinmux-helper.c b/drivers/misc/cape/beaglebone/bone-pinmux-helper.c new file mode 100644 index 0000000..d81363a --- /dev/null +++ b/drivers/misc/cape/beaglebone/bone-pinmux-helper.c @@ -0,0 +1,242 @@ +/* + * Pinmux helper driver + * + * Copyright (C) 2013 Pantelis Antoniou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct of_device_id bone_pinmux_helper_of_match[] = { + { + .compatible = "bone-pinmux-helper", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, bone_pinmux_helper_of_match); + +struct pinmux_helper_data { + struct pinctrl *pinctrl; + char *selected_state_name; +}; + +static ssize_t pinmux_helper_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pinmux_helper_data *data = platform_get_drvdata(pdev); + const char *name; + + name = data->selected_state_name; + if (name == NULL || strlen(name) == 0) + name = "none"; + return sprintf(buf, "%s\n", name); +} + +static ssize_t pinmux_helper_store_state(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct pinmux_helper_data *data = platform_get_drvdata(pdev); + struct pinctrl_state *state; + char *state_name; + char *s; + int err; + + /* duplicate (as a null terminated string) */ + state_name = kmalloc(count + 1, GFP_KERNEL); + if (state_name == NULL) + return -ENOMEM; + memcpy(state_name, buf, count); + state_name[count] = '\0'; + + /* and chop off newline */ + s = strchr(state_name, '\n'); + if (s != NULL) + *s = '\0'; + + /* try to select default state at first (if it exists) */ + state = pinctrl_lookup_state(data->pinctrl, state_name); + if (!IS_ERR(state)) { + err = pinctrl_select_state(data->pinctrl, state); + if (err != 0) + dev_err(dev, "Failed to select state %s\n", + state_name); + } else { + dev_err(dev, "Failed to find state %s\n", state_name); + err = PTR_RET(state); + } + + if (err == 0) { + kfree(data->selected_state_name); + data->selected_state_name = state_name; + } + + return err ? err : count; +} + +static DEVICE_ATTR(state, S_IWUSR | S_IRUGO, + pinmux_helper_show_state, pinmux_helper_store_state); + +static struct attribute *pinmux_helper_attributes[] = { + &dev_attr_state.attr, + NULL +}; + +static const struct attribute_group pinmux_helper_attr_group = { + .attrs = pinmux_helper_attributes, +}; + +static int bone_pinmux_helper_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pinmux_helper_data *data; + struct pinctrl_state *state; + char *state_name; + const char *mode_name; + int mode_len; + int err; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (data == NULL) { + dev_err(dev, "Failed to allocate data\n"); + err = -ENOMEM; + goto err_no_mem; + } + + state_name = kmalloc(strlen(PINCTRL_STATE_DEFAULT) + 1, + GFP_KERNEL); + if (state_name == NULL) { + dev_err(dev, "Failed to allocate state name\n"); + err = -ENOMEM; + goto err_no_state_mem; + } + data->selected_state_name = state_name; + strcpy(data->selected_state_name, PINCTRL_STATE_DEFAULT); + + platform_set_drvdata(pdev, data); + + data->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(data->pinctrl)) { + dev_err(dev, "Failed to get pinctrl\n"); + err = PTR_RET(data->pinctrl); + goto err_no_pinctrl; + } + + /* See if an initial mode is specified in the device tree */ + mode_name = of_get_property(dev->of_node, "mode", &mode_len); + + err = -1; + if (mode_name != NULL ) { + state_name = kmalloc(mode_len + 1, GFP_KERNEL); + if (state_name == NULL) { + dev_err(dev, "Failed to allocate state name\n"); + err = -ENOMEM; + goto err_no_mode_mem; + } + strncpy(state_name, mode_name, mode_len); + + /* try to select requested mode */ + state = pinctrl_lookup_state(data->pinctrl, state_name); + if (!IS_ERR(state)) { + err = pinctrl_select_state(data->pinctrl, state); + if (err != 0) { + dev_warn(dev, "Unable to select requested mode %s\n", state_name); + kfree(state_name); + } else { + kfree(data->selected_state_name); + data->selected_state_name = state_name; + dev_notice(dev, "Set initial pinmux mode to %s\n", state_name); + } + } + } + + /* try to select default state if mode_name failed */ + if ( err != 0) { + state = pinctrl_lookup_state(data->pinctrl, + data->selected_state_name); + if (!IS_ERR(state)) { + err = pinctrl_select_state(data->pinctrl, state); + if (err != 0) { + dev_err(dev, "Failed to select default state\n"); + goto err_no_state; + } + } else { + data->selected_state_name = '\0'; + } + } + + /* Register sysfs hooks */ + err = sysfs_create_group(&dev->kobj, &pinmux_helper_attr_group); + if (err) { + dev_err(dev, "Failed to create sysfs group\n"); + goto err_no_sysfs; + } + + return 0; + +err_no_sysfs: +err_no_state: +err_no_mode_mem: + devm_pinctrl_put(data->pinctrl); +err_no_pinctrl: + devm_kfree(dev, data->selected_state_name); +err_no_state_mem: + devm_kfree(dev, data); +err_no_mem: + return err; +} + +static int bone_pinmux_helper_remove(struct platform_device *pdev) +{ + struct pinmux_helper_data *data = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + sysfs_remove_group(&dev->kobj, &pinmux_helper_attr_group); + kfree(data->selected_state_name); + devm_pinctrl_put(data->pinctrl); + devm_kfree(dev, data); + + return 0; +} + +struct platform_driver bone_pinmux_helper_driver = { + .probe = bone_pinmux_helper_probe, + .remove = bone_pinmux_helper_remove, + .driver = { + .name = "bone-pinmux-helper", + .owner = THIS_MODULE, + .of_match_table = bone_pinmux_helper_of_match, + }, +}; + +module_platform_driver(bone_pinmux_helper_driver); + +MODULE_AUTHOR("Pantelis Antoniou"); +MODULE_DESCRIPTION("Beaglebone pinmux helper driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bone-pinmux-helper"); diff --git b/drivers/misc/cape_bone_argus/Kconfig b/drivers/misc/cape_bone_argus/Kconfig new file mode 100644 index 0000000..1b39661 --- /dev/null +++ b/drivers/misc/cape_bone_argus/Kconfig @@ -0,0 +1,7 @@ +comment "Argus cape driver for beaglebone black" + +config CAPE_BONE_ARGUS + tristate "Argus Cape Driver" + default M + help + Argus Cape Driver diff --git b/drivers/misc/cape_bone_argus/Makefile b/drivers/misc/cape_bone_argus/Makefile new file mode 100644 index 0000000..5482562 --- /dev/null +++ b/drivers/misc/cape_bone_argus/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for Argus cape +# + +obj-$(CONFIG_CAPE_BONE_ARGUS) += cape_bone_argus.o diff --git b/drivers/misc/cape_bone_argus/cape_bone_argus.c b/drivers/misc/cape_bone_argus/cape_bone_argus.c new file mode 100644 index 0000000..c434218 --- /dev/null +++ b/drivers/misc/cape_bone_argus/cape_bone_argus.c @@ -0,0 +1,415 @@ +/* -*- linux-c -*- */ + +/* Linux Kernel Module for Breakaway Systems UPS control. + * + * PUBLIC DOMAIN + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Module to sync file systems leaving them mounted read-only, + * then signal the UPS that it is safe to remove + * power, and finally halt the processor. + * Also to allow kicking the watchdog from user mode. + */ +#undef DEBUG_ARGUS + +#define N_GPIOS 9 /* Total number of GPIOS used */ + +#define REQ_GPIO_IDX 0 /* Indices got GPIOS */ +#define ACK_GPIO_IDX 1 +#define WDG_GPIO_IDX 2 +#define LED1_GREEN_IDX 3 +#define LED1_RED_IDX 4 +#define LED2_GREEN_IDX 5 +#define LED2_RED_IDX 6 +#define GEN_OUT1_IDX 7 +#define GEN_OUT2_IDX 8 + +static struct argus_ups_info { /* As there is only one UPS device we can make this static */ + struct fasync_struct *async_queue; /* asynchronous readers */ + struct platform_device *pdev; + struct pwm_device *pwm_dev; + struct gpio gpios[N_GPIOS]; +} info = {NULL, NULL, NULL, /* Some fields filled in by device tree, probe, etc. */ + { + {-1, GPIOF_IN, "Powerdown request"}, + {-1, GPIOF_OUT_INIT_LOW, "Powerdown acknowledge" }, + {-1, GPIOF_OUT_INIT_LOW, "Watchdog"}, + {-1, GPIOF_OUT_INIT_LOW, "LED 1 Green"}, + {-1, GPIOF_OUT_INIT_LOW, "LED 1 Red"}, + {-1, GPIOF_OUT_INIT_LOW, "LED 2 Green"}, + {-1, GPIOF_OUT_INIT_LOW, "LED 2 Red"}, + {-1, GPIOF_OUT_INIT_LOW, "General Output #1"}, + {-1, GPIOF_OUT_INIT_LOW, "General Output #2"} + }, +}; + + +static const struct of_device_id argus_ups_of_ids[] = { + { .compatible = "argus-ups" }, + { } +}; + +static int argus_ups_major; /* Major device number */ + +static struct class *argus_ups_class; /* /sys/class */ + +dev_t argus_ups_dev; /* Device number */ + +static struct cdev *argus_ups_cdev; /* Character device details */ + +static void argus_ups_function(struct work_struct *ignored); /* Work function */ + +static DECLARE_DELAYED_WORK(argus_ups_work, argus_ups_function); /* Kernel workqueue glue */ + +static struct workqueue_struct *argus_ups_workqueue; /* Kernel workqueue */ + +static int debug = 0; +module_param(debug, int, S_IRUGO); +MODULE_PARM_DESC(debug, "Debug flag"); + +static int shutdown = 1; +module_param(shutdown, int, S_IRUGO); +MODULE_PARM_DESC(shutdown, "Shutdown flag"); + +#ifdef DEBUG_ARGUS +static char* fs_type_names[] = {"vfat", "ext4"}; /* File system names that may need syncing. */ +#endif + +/* Just kick watchdog */ + +static ssize_t argus_ups_write(struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + int i; + if (debug >= 3) { + printk("Writing to watchdog - count:%d\n", count); + } + for (i = 0; i < count; i++) { + gpio_set_value(info.gpios[WDG_GPIO_IDX].gpio, 1); /* Set it */ + msleep(10); /* Wait */ + gpio_set_value(info.gpios[WDG_GPIO_IDX].gpio, 0); /* End clearing it */ + msleep(10); + } + return count; /* Always returns what we sent, regardsless */ +} + +static long argus_ups_ioctl(struct file *file, + unsigned int ioctl, + unsigned long param) +{ + if (debug >= 4) { + printk(KERN_ERR "ioctl: %d, param: %ld\n", ioctl, param); + } + switch(ioctl) { + case 10001: { + debug = param; + printk("Debug set to %d\n", debug); + break; + } + case 10002: { + unsigned char value = param & 0x0F; + unsigned char mask = (param >> 4) & 0x0F; + int i; /* Loop iterator */ + if (mask == 0) { + printk(KERN_ERR "Pointless mask of zero!\n"); + } + for (i = 0; i < 4; i++) { /* For all four LEDS */ + if (mask & (1 << i)) { /* Only masked values */ + if (value & (1 << i)) { /* On - so gpio is hi */ + if (debug >= 4) { + printk("Setting %d hi, ", + info.gpios[LED1_GREEN_IDX + i].gpio); + } + gpio_set_value(info.gpios[LED1_GREEN_IDX + i].gpio, 1); + } + else { /* Off - so gpio is lo */ + if (debug >= 4) { + printk("Setting %d lo, ", + info.gpios[LED1_GREEN_IDX + i].gpio); + } + gpio_set_value(info.gpios[LED1_GREEN_IDX + i].gpio, 0); + } + } + } + if (debug >= 4) { + printk("\n"); + } + break; + } + case 10003: { + gpio_set_value(info.gpios[GEN_OUT1_IDX].gpio, param & 1); + break; + } + case 10004: { + gpio_set_value(info.gpios[GEN_OUT2_IDX].gpio, param & 1); + break; + } + default: + { + printk(KERN_ERR "Invalid ioctl %d\n", ioctl); + return -1; + } + } + return 0; +} + +static int argus_ups_fasync(int fd, struct file *filp, int mode) +{ + printk(KERN_ERR "In argus_ups_fasync() fd:%d, filp:%p, mode:%d\n", fd, filp, mode); + return fasync_helper(fd, filp, mode, &info.async_queue); +} + +static struct file_operations argus_ups_fops = { /* Only file operation is to kick watchdog via a write */ + .owner = THIS_MODULE, + .llseek = NULL, + .read = NULL, + .unlocked_ioctl = argus_ups_ioctl, + .write = argus_ups_write, + .open = NULL, + .release = NULL, + .fasync = argus_ups_fasync, +}; + +#ifdef DEBUG_ARGUS +static void remount_sb(struct super_block *sb) +{ + int flags = MS_RDONLY; + int result = sb->s_op->remount_fs(sb, &flags, ""); + if (debug) { + printk("Processing superblock %p\n", sb); + printk("Remount operation returned %d\n", result); + } +} +#endif + +static void argus_ups_function(struct work_struct *ignored) +{ + static int testdata = 0; /* Data for test */ + int i; /* Iterator */ + testdata++; + if (!gpio_get_value(info.gpios[REQ_GPIO_IDX].gpio)) { + queue_delayed_work(argus_ups_workqueue, &argus_ups_work, HZ/100); /* Re-queue in 10mS*/ + return; + } + printk(KERN_ERR "Request received\n"); + if (debug) { + printk("Shutdown request received from UPS\n"); + } + if (!shutdown) { + printk("Shutdown request ignored\n"); + return; + } + + if (debug) { + printk("Sending async kill SIGIO to %p\n", info.async_queue); + } + if (info.async_queue) { /* Try and tell usermode to halt system */ + kill_fasync(&info.async_queue, SIGIO, POLL_IN); + } + gpio_set_value(info.gpios[LED1_GREEN_IDX].gpio, 0); /* Turn off green LED1 */ + for (i = 0; i < 300; i++) { /* Toggle acknowledge at 10 Hz for 15 seconds */ + if (debug >= 2) { + printk("Waiting for first shutdown request:%d\n", i); + } + gpio_set_value(info.gpios[ACK_GPIO_IDX].gpio, i & 1); /* Toggle acknowledge */ + gpio_set_value(info.gpios[LED1_RED_IDX].gpio, i & 1); /* and LED1 red */ + msleep(50); /* Wait in 50ms increments */ + } + + { + char *argv[] = { "/sbin/halt", NULL }; + static char *envp[] = { + "HOME=/", + "TERM=linux", + "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin", NULL }; + + call_usermodehelper( argv[0], argv, envp, UMH_WAIT_PROC ); + } + for (i = 0; i < 300; i++) { /* Toggle acknowledge at 10 Hz for 15 more seconds */ + if (debug >= 2) { + printk("Waiting for second shutdown request:%d\n", i); + } + gpio_set_value(info.gpios[ACK_GPIO_IDX].gpio, i & 1); /* Toggle acknowledge */ + gpio_set_value(info.gpios[LED1_RED_IDX].gpio, i & 1); /* and LED1 red */ + msleep(50); /* Wait in 50ms increments */ + } + printk(KERN_ERR "Usermode failed to halt system\n"); + kernel_halt(); /* Last resort - may give some oopss */ +} + + +static int argus_ups_probe(struct platform_device *pdev) /* Entry point */ +{ + struct pinctrl *pinctrl; + struct device_node *pnode = pdev->dev.of_node; + int i; + int ret; + printk("Init UPS module - debug=%d, shutdown=%d\n", + debug, shutdown); + platform_set_drvdata(pdev, &info); + info.pdev = pdev; + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) { + dev_warn(&pdev->dev, + "pins are not configured from the driver\n"); + return -1; + } + ret = of_property_read_u32(pnode, "debug", &debug); + if (ret != 0) { + dev_err(&pdev->dev, "Unable to read debug parameter\n"); + } + else { + printk("Debug parameter read from DT:%d\n", debug); + } + + ret = of_property_read_u32(pnode, "shutdown", &shutdown); + if (ret != 0) { + dev_err(&pdev->dev, "Unable to read shutdown parameter\n"); + } + else { + printk("Shutdown parameter read from DT:%d\n", shutdown); + } + + ret = of_gpio_count(pnode); + + if (ret != N_GPIOS) { + printk(KERN_ERR "Wrong number of gpios"); + return -1; + } + + for (i = 0; i < of_gpio_count(pnode); i++) { + ret = of_get_gpio_flags(pnode, i, NULL); + if (debug) { + printk("GPIO#%d:%d\n", i, ret); + } + if (IS_ERR_VALUE(ret)) { + dev_err(&pdev->dev, "unable to get GPIO %d\n", i); + goto err_no_gpio; + } + info.gpios[i].gpio = ret; + } + + + ret = alloc_chrdev_region(&argus_ups_dev, 0, 2, "argus_ups"); + argus_ups_major = MAJOR(argus_ups_dev); + if (ret) { + printk(KERN_ERR "Error %d adding argus_ups\n", ret); + return -1; + } + if (debug) { + printk("argus_ups major: %d\n", argus_ups_major); + } + argus_ups_cdev = cdev_alloc(); /* Make this a character device */ + argus_ups_cdev->ops = &argus_ups_fops; /* File operations */ + argus_ups_cdev->owner = THIS_MODULE; /* Top level device */ + ret = cdev_add(argus_ups_cdev, argus_ups_dev, 1); /* Add it to the kernel */ + if (ret) { + printk(KERN_ERR "cdev_add returned %d\n", ret); + unregister_chrdev_region(0, 1); + return -1; + } + ret = gpio_request_array(info.gpios, N_GPIOS); + if (ret) { + printk(KERN_ERR "Error %d requesting GPIOs\n", ret); + unregister_chrdev_region(0, 1); + return -1; + } + + argus_ups_class = class_create(THIS_MODULE, "argus_ups"); /* /sys/class entry for udev */ + if (IS_ERR(argus_ups_class)) { + printk(KERN_ERR "Error creating argus_ups_class\n"); + unregister_chrdev_region(0, 1); + return -1; + } + device_create(argus_ups_class, NULL, MKDEV(argus_ups_major, 0), NULL, "argus_ups"); + argus_ups_workqueue = create_singlethread_workqueue("argus_ups"); + INIT_DELAYED_WORK(&argus_ups_work, argus_ups_function); + queue_delayed_work(argus_ups_workqueue, &argus_ups_work, 0); /* Start work immediately */ + + return 0; +err_no_gpio: + return ret; + +} + + +static void argus_ups_cleanup(void) +{ + printk("Module cleanup called\n"); + while (cancel_delayed_work(&argus_ups_work) == 0) { + flush_workqueue(argus_ups_workqueue); /* Make sure all work is completed */ + } + destroy_workqueue(argus_ups_workqueue); + gpio_free_array(info.gpios, N_GPIOS); + device_destroy(argus_ups_class, argus_ups_dev); + class_destroy(argus_ups_class); + unregister_chrdev_region(argus_ups_dev, 1); + cdev_del(argus_ups_cdev); +} + + + +static int argus_ups_remove(struct platform_device *pdev) +{ + printk("In argus_ups_remove()\n"); + argus_ups_cleanup(); + printk("After cleanup\n"); + return 0; +} + +#define ARGUS_UPS_PM_OPS NULL + +struct platform_driver argus_ups_driver = { + .probe = argus_ups_probe, + .remove = argus_ups_remove, + .driver = { + .name = "argus-ups", + .owner = THIS_MODULE, + .pm = ARGUS_UPS_PM_OPS, + .of_match_table = argus_ups_of_ids, + }, +}; + + +static int __init argus_ups_init(void) +{ + return platform_driver_probe(&argus_ups_driver, + argus_ups_probe); +} + +static void __exit argus_ups_exit(void) +{ + platform_driver_unregister(&argus_ups_driver); + printk("After driver unregister\n"); +} + +module_init(argus_ups_init); +module_exit(argus_ups_exit); + +/* + * Get rid of taint message. + */ +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Lambert"); /* Who wrote this module? */ +MODULE_DESCRIPTION("Argus UPS control"); /* What does this module do */ +MODULE_ALIAS("platform:argus-ups"); +MODULE_DEVICE_TABLE(of, argus_ups_of_ids); diff --git b/drivers/misc/devovmgr.c b/drivers/misc/devovmgr.c new file mode 100644 index 0000000..d5c8d1d --- /dev/null +++ b/drivers/misc/devovmgr.c @@ -0,0 +1,1306 @@ +/* + * Device overlay manager + * + * Copyright (C) 2015 Konsulko Group + * Pantelis Antoniou + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum dovmgr_type { + ITEM_PCI, + ITEM_USB +}; + +struct dovmgr_item; + +struct dovmgr_dev_item { + struct dovmgr_item *item; + struct list_head node; + struct device *dev; + const struct firmware *fw; + struct device_node *overlay; + int overlay_id; + struct work_struct work; +}; + +struct dovmgr_item { + struct config_item item; + char *path; + bool enable; + char *overlay_name; + struct mutex dev_item_mutex; + struct list_head dev_item_list; + enum dovmgr_type type; + union { + struct pci_device_id pci; + struct usb_device_id usb; + }; +}; + +struct config_group dovmgr_pci_group; +struct config_group dovmgr_usb_group; + +static inline struct dovmgr_item *to_dovmgr_item(struct config_item *cfsitem) +{ + if (!cfsitem) + return NULL; + + return container_of(cfsitem, struct dovmgr_item, item); +} + +static int dovmgr_notifier_action(struct config_group *group, + unsigned long action, struct device *dev, + int (*do_match)(struct dovmgr_item *item, struct device *dev), + int (*do_action)(struct dovmgr_item *item, unsigned long action, + struct device *dev)) +{ + struct config_item *cfsitem; + struct dovmgr_item *item; + int ret; + + /* only handle device notifiers */ + if (action != BUS_NOTIFY_ADD_DEVICE && + action != BUS_NOTIFY_DEL_DEVICE && + action != BUS_NOTIFY_REMOVED_DEVICE) + return 0; + + ret = 0; + list_for_each_entry(cfsitem, &group->cg_children, ci_entry) { + item = to_dovmgr_item(cfsitem); + if (!item->enable || !(*do_match)(item, dev)) + continue; + + ret = (*do_action)(item, action, dev); + if (ret != 0) + break; + } + return ret; +} + +#if IS_ENABLED(CONFIG_PCI) +/* copy of drivers/pci/pci.h */ +static inline const struct pci_device_id * +pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev) +{ + if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) && + (id->device == PCI_ANY_ID || id->device == dev->device) && + (id->subvendor == PCI_ANY_ID || + id->subvendor == dev->subsystem_vendor) && + (id->subdevice == PCI_ANY_ID || + id->subdevice == dev->subsystem_device) && + !((id->class ^ dev->class) & id->class_mask)) + return id; + return NULL; +} + +static int dovmgr_pci_item_match(struct dovmgr_item *item, struct device *dev) +{ + struct pci_dev *pdev; + + BUG_ON(item->type != ITEM_PCI); + pdev = to_pci_dev(dev); + + return pci_match_one_device(&item->pci, pdev) != NULL; +} +#endif + +#if IS_ENABLED(CONFIG_USB) +/* in drivers/usb/core/driver.c */ +extern int usb_match_device(struct usb_device *dev, + const struct usb_device_id *id); + +static int dovmgr_usb_item_match(struct dovmgr_item *item, struct device *dev) +{ + struct usb_device *udev; + + BUG_ON(item->type != ITEM_USB); + udev = to_usb_device(dev); + + return usb_match_device(udev, &item->usb); +} +#endif + +static struct dovmgr_dev_item *dovmgr_lookup_dev_item(struct dovmgr_item *item, + struct device *dev) +{ + struct dovmgr_dev_item *ditem; + + list_for_each_entry(ditem, &item->dev_item_list, node) + if (ditem->dev == dev) + return ditem; + return NULL; +} + +static void dovmgr_item_work_func(struct work_struct *work) +{ + struct dovmgr_dev_item *ditem = container_of(work, + struct dovmgr_dev_item, work); + struct dovmgr_item *item = ditem->item; + struct device *dev; + struct device_node *np; + int err; + + mutex_lock(&item->dev_item_mutex); + + dev = ditem->dev; + np = dev->of_node; + if (!dev || !np || !item->overlay_name || ditem->overlay_id >= 0) + goto out_unlock; + + pr_info("%s: %s %s\n", __func__, + kobject_name(&dev->kobj), of_node_full_name(np)); + + err = request_firmware_direct(&ditem->fw, item->overlay_name, dev); + if (err != 0) { + pr_err("%s: %s failed to load firmware '%s'\n", __func__, + kobject_name(&dev->kobj), item->overlay_name); + goto out_unlock; + } + + of_fdt_unflatten_tree((void *)ditem->fw->data, &ditem->overlay); + if (ditem->overlay == NULL) { + pr_err("%s: %s failed to load firmware '%s'\n", __func__, + kobject_name(&dev->kobj), item->overlay_name); + goto out_release_fw; + } + + /* mark it as detached */ + of_node_set_flag(ditem->overlay, OF_DETACHED); + + /* perform resolution */ + err = of_resolve_phandles(ditem->overlay); + if (err != 0) { + pr_err("%s: %s failed to resolve tree\n", __func__, + kobject_name(&dev->kobj)); + goto out_release_overlay; + } + + err = of_overlay_create_target_root(ditem->overlay, np); + if (err < 0) { + pr_err("%s: %s failed to create overlay\n", __func__, + kobject_name(&dev->kobj)); + goto out_release_overlay; + } + ditem->overlay_id = err; + +out_unlock: + mutex_unlock(&item->dev_item_mutex); + return; + +out_release_overlay: + /* TODO: free the overlay, we can't right now cause + * the unflatten method does not track it */ + ditem->overlay = NULL; +out_release_fw: + release_firmware(ditem->fw); + ditem->fw = NULL; + goto out_unlock; +} + +/* dev item list mutex lock must be held */ +static int dovmgr_add_dev_item(struct dovmgr_item *item, struct device *dev) +{ + struct dovmgr_dev_item *ditem; + + /* first make sure there's no duplicate */ + if (dovmgr_lookup_dev_item(item, dev)) + return -EEXIST; + + /* add the device item */ + ditem = kzalloc(sizeof(*ditem), GFP_KERNEL); + if (!ditem) + return -ENOMEM; + ditem->overlay_id = -1; + ditem->dev = get_device(dev); + INIT_WORK(&ditem->work, dovmgr_item_work_func); + ditem->item = item; + + list_add_tail(&ditem->node, &item->dev_item_list); + + pr_info("%s: added device %s from item's %s list\n", __func__, + kobject_name(&dev->kobj), + config_item_name(&item->item)); + + /* now schedule the overlay application */ + if (item->overlay_name) + schedule_work(&ditem->work); + + return 0; +} + +static int dovmgr_remove_dev_item(struct dovmgr_item *item, struct device *dev) +{ + struct dovmgr_dev_item *ditem; + + /* find it */ + ditem = dovmgr_lookup_dev_item(item, dev); + if (!ditem) + return -ENODEV; + + if (work_pending(&ditem->work)) + cancel_work_sync(&ditem->work); + + if (ditem->overlay_id >= 0) { + of_overlay_destroy(ditem->overlay_id); + ditem->overlay_id = -1; + + } + + if (ditem->overlay) { + /* TODO: free the overlay, we can't right now cause + * the unflatten method does not track it */ + ditem->overlay = NULL; + } + + if (ditem->fw) { + /* TODO release_firmware(ditem->fw); */ + release_firmware(ditem->fw); + ditem->fw = NULL; + } + + put_device(ditem->dev); + list_del(&ditem->node); + + kfree(ditem); + + pr_info("%s: removed device %s from item's %s list\n", __func__, + kobject_name(&dev->kobj), + config_item_name(&item->item)); + + return 0; +} + +static int dovmgr_item_notify(struct dovmgr_item *item, + unsigned long action, struct device *dev) +{ + int ret; + + ret = 0; + mutex_lock(&item->dev_item_mutex); + + switch (action) { + case BUS_NOTIFY_ADD_DEVICE: + pr_info("%s: BUS_NOTIFY_ADD_DEVICE for %s\n", __func__, + kobject_name(&dev->kobj)); + + ret = dovmgr_add_dev_item(item, dev); + if (ret != 0) + goto out_unlock; + + break; + + case BUS_NOTIFY_DEL_DEVICE: + pr_info("%s: BUS_NOTIFY_DEL_DEVICE for %s\n", __func__, + kobject_name(&dev->kobj)); + break; + + case BUS_NOTIFY_REMOVED_DEVICE: + pr_info("%s: BUS_NOTIFY_REMOVE_DEVICE for %s\n", __func__, + kobject_name(&dev->kobj)); + + ret = dovmgr_remove_dev_item(item, dev); + if (ret != 0) + goto out_unlock; + + break; + } + +out_unlock: + mutex_unlock(&item->dev_item_mutex); + + return ret; +} + +#if IS_ENABLED(CONFIG_PCI) +static int dovmgr_pci_add_iterator(struct device *dev, void *data) +{ + struct dovmgr_item *item = data; + + /* do add match */ + if (!item->enable || !dovmgr_pci_item_match(item, dev)) + return 0; + + pr_info("%s: dev=%s\n", __func__, kobject_name(&dev->kobj)); + + return dovmgr_item_notify(item, BUS_NOTIFY_ADD_DEVICE, dev); +} + +static int dovmgr_pci_removed_iterator(struct device *dev, void *data) +{ + struct dovmgr_item *item = data; + + /* do add match */ + if (item->enable || !dovmgr_pci_item_match(item, dev)) + return 0; + + pr_info("%s: dev=%s\n", __func__, kobject_name(&dev->kobj)); + + return dovmgr_item_notify(item, BUS_NOTIFY_REMOVED_DEVICE, dev); +} +#endif + +#if IS_ENABLED(CONFIG_USB) +static int dovmgr_usb_add_iterator(struct device *dev, void *data) +{ + struct dovmgr_item *item = data; + + /* do add match */ + if (item->enable || !dovmgr_usb_item_match(item, dev)) + return 0; + + pr_info("%s: dev=%s\n", __func__, kobject_name(&dev->kobj)); + + return dovmgr_item_notify(item, BUS_NOTIFY_ADD_DEVICE, dev); +} + +static int dovmgr_usb_removed_iterator(struct device *dev, void *data) +{ + struct dovmgr_item *item = data; + + /* do add match */ + if (!item->enable || !dovmgr_usb_item_match(item, dev)) + return 0; + + pr_info("%s: dev=%s\n", __func__, kobject_name(&dev->kobj)); + + return dovmgr_item_notify(item, BUS_NOTIFY_REMOVED_DEVICE, dev); +} +#endif + +static int dovmgr_item_set_enable(struct dovmgr_item *item, bool new_enable) +{ + int ret; + + if (new_enable == item->enable) + return 0; + + item->enable = new_enable; + switch (item->type) { +#if IS_ENABLED(CONFIG_PCI) + case ITEM_PCI: + ret = bus_for_each_dev(&pci_bus_type, NULL, item, + new_enable ? dovmgr_pci_add_iterator : + dovmgr_pci_removed_iterator); + if (ret != 0) + return ret; + break; +#endif +#if IS_ENABLED(CONFIG_USB) + case ITEM_USB: + ret = bus_for_each_dev(&usb_bus_type, NULL, item, + new_enable ? dovmgr_usb_add_iterator : + dovmgr_usb_removed_iterator); + if (ret != 0) + return ret; + break; +#endif + default: + break; + } + return 0; +} + + +static ssize_t dovmgr_item_str_show(struct dovmgr_item *item, + char *page, char **strp) +{ + return snprintf(page, PAGE_SIZE, "%s\n", + *strp ? *strp : ""); +} + +static ssize_t dovmgr_item_str_store(struct dovmgr_item *item, + const char *page, size_t count, char **strp) +{ + const char *s; + int len; + + /* copy to path buffer (and make sure it's always zero terminated */ + len = strnlen(page, PAGE_SIZE); + if (len >= PAGE_SIZE) + return -EINVAL; + s = page + len; + while (len > 0 && *--s == '\n') + len--; + if (len == 0) + return -EINVAL; + + if (*strp) + kfree(*strp); + *strp = kmalloc(len + 1, GFP_KERNEL); + if (!*strp) + return -ENOMEM; + memcpy(*strp, page, len); + (*strp)[len + 1] = '\0'; + + return count; +} + +static ssize_t dovmgr_item_path_show(struct config_item *citem, char *page) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + return dovmgr_item_str_show(item, page, &item->path); +} + +static ssize_t dovmgr_item_path_store(struct config_item *citem, + const char *page, size_t count) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + return dovmgr_item_str_store(item, page, count, &item->path); +} + +static ssize_t dovmgr_item_enable_show(struct config_item *citem, char *page) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + return snprintf(page, PAGE_SIZE, "%u\n", !!item->enable); +} + +static ssize_t dovmgr_item_enable_store(struct config_item *citem, + const char *page, size_t count) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + int ret; + unsigned int val; + + ret = kstrtouint(page, 0, &val); + if (ret != 0) + return ret; + + ret = dovmgr_item_set_enable(item, !!val); + if (ret != 0) + return ret; + + return count; +} + +static ssize_t dovmgr_item_overlay_show(struct config_item *citem, char *page) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + ssize_t ret; + + mutex_lock(&item->dev_item_mutex); + ret = snprintf(page, PAGE_SIZE, "%s\n", item->overlay_name ? + item->overlay_name : ""); + mutex_unlock(&item->dev_item_mutex); + return ret; +}; + + +static ssize_t dovmgr_item_overlay_store(struct config_item *citem, + const char *page, size_t count) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + ssize_t ret; + + mutex_lock(&item->dev_item_mutex); + kfree(item->overlay_name); + item->overlay_name = kstrndup(page, PAGE_SIZE, GFP_KERNEL); + if (!item->overlay_name) + ret = -ENOMEM; + else + ret = count; + mutex_unlock(&item->dev_item_mutex); + return ret; +} + +static ssize_t dovmgr_item_status_show(struct config_item *citem, char *page) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + struct dovmgr_dev_item *ditem; + char *p, *e; + int len; + + p = page; + e = page + PAGE_SIZE; + + mutex_lock(&item->dev_item_mutex); + list_for_each_entry(ditem, &item->dev_item_list, node) { + len = snprintf(p, e - p, "%s:%s:%d\n", + kobject_name(&ditem->dev->kobj), + of_node_full_name(ditem->dev->of_node), + ditem->overlay_id); + p += len; + if (p >= e - 1) + break; + } + mutex_unlock(&item->dev_item_mutex); + + return p - page; +} + +CONFIGFS_ATTR(dovmgr_item_, path); +CONFIGFS_ATTR_RO(dovmgr_item_, status); +CONFIGFS_ATTR(dovmgr_item_, enable); +CONFIGFS_ATTR(dovmgr_item_, overlay); + +#if IS_ENABLED(CONFIG_PCI) +static ssize_t dovmgr_item_pci_device_show(struct config_item *citem, + char *page) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + return snprintf(page, PAGE_SIZE, "0x%04x\n", item->pci.device); +} + +static ssize_t dovmgr_item_pci_device_store(struct config_item *citem, + const char *page, size_t count) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + int ret; + unsigned int val; + + ret = kstrtouint(page, 0, &val); + if (ret != 0) + return ret; + item->pci.device = val; + return count; +} + +static ssize_t dovmgr_item_pci_vendor_show(struct config_item *citem, + char *page) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + return snprintf(page, PAGE_SIZE, "0x%04x\n", item->pci.vendor); +} + +static ssize_t dovmgr_item_pci_vendor_store(struct config_item *citem, + const char *page, size_t count) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + int ret; + unsigned int val; + + /* cannot modify when item is enabled */ + if (item->enable) + return -EBUSY; + + ret = kstrtouint(page, 0, &val); + if (ret != 0) + return ret; + item->pci.vendor = val; + return count; +} + +static ssize_t dovmgr_item_pci_subdevice_show(struct config_item *citem, + char *page) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + return snprintf(page, PAGE_SIZE, "0x%08x\n", item->pci.subdevice); +} + +static ssize_t dovmgr_item_pci_subdevice_store(struct config_item *citem, + const char *page, size_t count) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + int ret; + unsigned int val; + + /* cannot modify when item is enabled */ + if (item->enable) + return -EBUSY; + + ret = kstrtouint(page, 0, &val); + if (ret != 0) + return ret; + item->pci.subdevice = val; + return count; +} + +static ssize_t dovmgr_item_pci_subvendor_show(struct config_item *citem, + char *page) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + return snprintf(page, PAGE_SIZE, "0x%08x\n", item->pci.subvendor); +} + +static ssize_t dovmgr_item_pci_subvendor_store(struct config_item *citem, + const char *page, size_t count) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + int ret; + unsigned int val; + + /* cannot modify when item is enabled */ + if (item->enable) + return -EBUSY; + + ret = kstrtouint(page, 0, &val); + if (ret != 0) + return ret; + item->pci.subvendor = val; + return count; +} + +static ssize_t dovmgr_item_pci_class_show(struct config_item *citem, char *page) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + return snprintf(page, PAGE_SIZE, "0x%04x\n", item->pci.class); +} + +static ssize_t dovmgr_item_pci_class_store(struct config_item *citem, + const char *page, size_t count) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + int ret; + unsigned int val; + + /* cannot modify when item is enabled */ + if (item->enable) + return -EBUSY; + + ret = kstrtouint(page, 0, &val); + if (ret != 0) + return ret; + item->pci.class = val; + return count; +} + +static ssize_t dovmgr_item_pci_class_mask_show(struct config_item *citem, + char *page) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + return snprintf(page, PAGE_SIZE, "0x%04x\n", item->pci.class_mask); +} + +static ssize_t dovmgr_item_pci_class_mask_store(struct config_item *citem, + const char *page, size_t count) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + int ret; + unsigned int val; + + /* cannot modify when item is enabled */ + if (item->enable) + return -EBUSY; + + ret = kstrtouint(page, 0, &val); + if (ret != 0) + return ret; + item->pci.class_mask = val; + return count; +} + +CONFIGFS_ATTR(dovmgr_item_pci_, device); +CONFIGFS_ATTR(dovmgr_item_pci_, vendor); +CONFIGFS_ATTR(dovmgr_item_pci_, subdevice); +CONFIGFS_ATTR(dovmgr_item_pci_, subvendor); +CONFIGFS_ATTR(dovmgr_item_pci_, class); +CONFIGFS_ATTR(dovmgr_item_pci_, class_mask); +#endif + +#if IS_ENABLED(CONFIG_USB) +static ssize_t dovmgr_item_usb_idProduct_show(struct config_item *citem, + char *page) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + return snprintf(page, PAGE_SIZE, "0x%04x\n", + item->usb.idProduct); +} + +static ssize_t dovmgr_item_usb_idProduct_store(struct config_item *citem, + const char *page, size_t count) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + int ret; + unsigned int val; + + /* cannot modify when item is enabled */ + if (item->enable) + return -EBUSY; + + ret = kstrtouint(page, 0, &val); + if (ret != 0) + return ret; + item->usb.idProduct = val; + return count; +} + +static ssize_t dovmgr_item_usb_idVendor_show(struct config_item *citem, + char *page) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + return snprintf(page, PAGE_SIZE, "0x%04x\n", + item->usb.idVendor); +} + +static ssize_t dovmgr_item_usb_idVendor_store(struct config_item *citem, + const char *page, size_t count) +{ + struct dovmgr_item *item = to_dovmgr_item(citem); + int ret; + unsigned int val; + + /* cannot modify when item is enabled */ + if (item->enable) + return -EBUSY; + + ret = kstrtouint(page, 0, &val); + if (ret != 0) + return ret; + item->usb.idVendor = val; + return count; +} + +CONFIGFS_ATTR(dovmgr_item_usb_, idProduct); +CONFIGFS_ATTR(dovmgr_item_usb_, idVendor); +#endif + +#if IS_ENABLED(CONFIG_PCI) +static struct configfs_attribute *dovmgr_pci_attrs[] = { + &dovmgr_item_attr_path, + &dovmgr_item_attr_status, + &dovmgr_item_attr_enable, + &dovmgr_item_attr_overlay, + &dovmgr_item_pci_attr_device, + &dovmgr_item_pci_attr_vendor, + &dovmgr_item_pci_attr_subdevice, + &dovmgr_item_pci_attr_subvendor, + &dovmgr_item_pci_attr_class, + &dovmgr_item_pci_attr_class_mask, + NULL, +}; +#endif + +#if IS_ENABLED(CONFIG_USB) +static struct configfs_attribute *dovmgr_usb_attrs[] = { + &dovmgr_item_attr_path, + &dovmgr_item_attr_enable, + &dovmgr_item_attr_status, + &dovmgr_item_attr_overlay, + &dovmgr_item_usb_attr_idVendor, + &dovmgr_item_usb_attr_idProduct, + NULL, +}; +#endif + +static void dovmgr_release(struct config_item *cfsitem) +{ + struct dovmgr_item *item = to_dovmgr_item(cfsitem); + + /* disable item (this removes the overlay and all) */ + dovmgr_item_set_enable(item, false); + + kfree(item->path); + kfree(item); +} + +static struct configfs_item_operations dovmgr_item_ops = { + .release = dovmgr_release, +}; + +#if IS_ENABLED(CONFIG_PCI) +static struct config_item_type dovmgr_pci_item_type = { + .ct_item_ops = &dovmgr_item_ops, + .ct_attrs = dovmgr_pci_attrs, + .ct_owner = THIS_MODULE, +}; +#endif + +#if IS_ENABLED(CONFIG_USB) +static struct config_item_type dovmgr_usb_item_type = { + .ct_item_ops = &dovmgr_item_ops, + .ct_attrs = dovmgr_usb_attrs, + .ct_owner = THIS_MODULE, +}; +#endif + +static struct config_item *dovmgr_group_make_item( + struct config_group *group, const char *name, + enum dovmgr_type type) +{ + struct dovmgr_item *item; + struct config_item_type *item_type; + + switch (type) { +#if IS_ENABLED(CONFIG_PCI) + case ITEM_PCI: + item_type = &dovmgr_pci_item_type; + break; +#endif +#if IS_ENABLED(CONFIG_USB) + case ITEM_USB: + item_type = &dovmgr_usb_item_type; + break; +#endif + default: + return ERR_PTR(-EINVAL); + }; + + item = kzalloc(sizeof(*item), GFP_KERNEL); + if (!item) + return ERR_PTR(-ENOMEM); + + item->type = type; + item->enable = false; + mutex_init(&item->dev_item_mutex); + INIT_LIST_HEAD(&item->dev_item_list); + + switch (type) { +#if IS_ENABLED(CONFIG_PCI) + case ITEM_PCI: + /* default for matching device/vendor */ + item->pci.vendor = PCI_ANY_ID; + item->pci.device = PCI_ANY_ID; + item->pci.subvendor = PCI_ANY_ID; + item->pci.subdevice = PCI_ANY_ID; + item->pci.class = 0; + item->pci.class_mask = 0; + break; +#endif +#if IS_ENABLED(CONFIG_USB) + case ITEM_USB: + /* default */ + item->usb.match_flags = USB_DEVICE_ID_MATCH_DEVICE; + break; +#endif + default: + return ERR_PTR(-EINVAL); + }; + + config_item_init_type_name(&item->item, name, item_type); + return &item->item; +} + +#if IS_ENABLED(CONFIG_PCI) +static struct config_item *dovmgr_group_pci_make_item( + struct config_group *group, const char *name) +{ + return dovmgr_group_make_item(group, name, ITEM_PCI); +} +#endif + +#if IS_ENABLED(CONFIG_USB) +static struct config_item *dovmgr_group_usb_make_item( + struct config_group *group, const char *name) +{ + return dovmgr_group_make_item(group, name, ITEM_USB); +} +#endif + +static void dovmgr_group_drop_item(struct config_group *group, + struct config_item *cfsitem) +{ + struct dovmgr_item *item = to_dovmgr_item(cfsitem); + + switch (item->type) { +#if IS_ENABLED(CONFIG_PCI) + case ITEM_PCI: + break; +#endif +#if IS_ENABLED(CONFIG_USB) + case ITEM_USB: + break; +#endif + default: + break; + } + config_item_put(&item->item); +} + +#if IS_ENABLED(CONFIG_PCI) +static struct configfs_group_operations dovmgr_pci_group_ops = { + .make_item = dovmgr_group_pci_make_item, + .drop_item = dovmgr_group_drop_item, +}; + +static struct config_item_type dovmgr_pci_type = { + .ct_group_ops = &dovmgr_pci_group_ops, + .ct_owner = THIS_MODULE, +}; +#endif + +#if IS_ENABLED(CONFIG_USB) +static struct configfs_group_operations dovmgr_usb_group_ops = { + .make_item = dovmgr_group_usb_make_item, + .drop_item = dovmgr_group_drop_item, +}; + +static struct config_item_type dovmgr_usb_type = { + .ct_group_ops = &dovmgr_usb_group_ops, + .ct_owner = THIS_MODULE, +}; +#endif + +static struct configfs_group_operations dovmgr_ops = { + /* empty - we don't allow anything to be created */ +}; + +static struct config_item_type dovmgr_type = { + .ct_group_ops = &dovmgr_ops, + .ct_owner = THIS_MODULE, +}; + +struct config_group *dovmgr_def_groups[] = { +#if IS_ENABLED(CONFIG_PCI) + &dovmgr_pci_group, +#endif +#if IS_ENABLED(CONFIG_USB) + &dovmgr_usb_group, +#endif + NULL +}; + +static struct configfs_subsystem dovmgr_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "dovmgr", + .ci_type = &dovmgr_type, + }, + .default_groups = dovmgr_def_groups, + }, + .su_mutex = __MUTEX_INITIALIZER(dovmgr_subsys.su_mutex), +}; + +#if IS_ENABLED(CONFIG_PCI) +static int pci_dev_instantiate(struct pci_dev *pdev) +{ + struct device *dev = &pdev->dev; + struct device *bus_dev; + struct of_changeset cset; + struct device_node *np, *npb; + int ret; + + npb = NULL; + + /* al