diff --git a/.gitignore b/.gitignore index c2ed4ec..0544d50 100644 --- a/.gitignore +++ b/.gitignore @@ -114,3 +114,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/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/net/allwinner,sun8i-emac.txt b/Documentation/devicetree/bindings/net/allwinner,sun8i-emac.txt new file mode 100644 index 0000000..4bf4e53 --- /dev/null +++ b/Documentation/devicetree/bindings/net/allwinner,sun8i-emac.txt @@ -0,0 +1,65 @@ +* Allwinner sun8i EMAC ethernet controller + +Required properties: +- compatible: "allwinner,sun8i-a83t-emac", "allwinner,sun8i-h3-emac", + or "allwinner,sun50i-a64-emac" +- reg: address and length of the register sets for the device. +- reg-names: should be "emac" and "syscon", matching the register sets +- interrupts: interrupt for the device +- clocks: A phandle to the reference clock for this device +- clock-names: should be "ahb" +- resets: A phandle to the reset control for this device +- reset-names: should be "ahb" +- phy-mode: See ethernet.txt +- phy or phy-handle: See ethernet.txt +- #address-cells: shall be 1 +- #size-cells: shall be 0 + +"allwinner,sun8i-h3-emac" also requires: +- clocks: an extra phandle to the reference clock for the EPHY +- clock-names: an extra "ephy" entry matching the clocks property +- resets: an extra phandle to the reset control for the EPHY +- resets-names: an extra "ephy" entry matching the resets property + +See ethernet.txt in the same directory for generic bindings for ethernet +controllers. + +The device node referenced by "phy" or "phy-handle" should be a child node +of this node. See phy.txt for the generic PHY bindings. + +Optional properties: +- phy-supply: phandle to a regulator if the PHY needs one +- phy-io-supply: phandle to a regulator if the PHY needs a another one for I/O. + This is sometimes found with RGMII PHYs, which use a second + regulator for the lower I/O voltage. +- allwinner,tx-delay: The setting of the TX clock delay chain +- allwinner,rx-delay: The setting of the RX clock delay chain + +The TX/RX clock delay chain settings are board specific. + +Optional properties for "allwinner,sun8i-h3-emac": +- allwinner,use-internal-phy: Use the H3 SoC's internal E(thernet) PHY +- allwinner,leds-active-low: EPHY LEDs are active low + +When the internal PHY is requested, the implementation shall configure the +internal PHY to use the address specified in the child PHY node. + +Example: + +emac: ethernet@01c0b000 { + compatible = "allwinner,sun8i-h3-emac"; + reg = <0x01c0b000 0x104>, <0x01c00030 0x4>; + reg-names = "emac", "syscon"; + interrupts = ; + clocks = <&bus_gates 17>, <&bus_gates 128>; + clock-names = "ahb", "ephy"; + resets = <&ahb_rst 17>, <&ahb_rst 66>; + reset-names = "ahb", "ephy"; + phy = <&phy1>; + allwinner,use-internal-phy; + allwinner,leds-active-low; + + phy1: ethernet-phy@1 { + reg = <1>; + }; +}; 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/power/pwrseq/pwrseq-generic.txt b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt new file mode 100644 index 0000000..ebf0d47 --- /dev/null +++ b/Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt @@ -0,0 +1,48 @@ +The generic power sequence library + +Some hard-wired devices (eg USB/MMC) need to do power sequence before +the device can be enumerated on the bus, the typical power sequence +like: enable USB PHY clock, toggle reset pin, etc. But current +Linux device driver lacks of such code to do it, it may cause some +hard-wired devices works abnormal or can't be recognized by +controller at all. The power sequence will be done before this device +can be found at the bus. + +The power sequence properties is under the device node. + +Optional properties: +- clocks: the input clocks for device. +- reset-gpios: Should specify the GPIO for reset. +- reset-duration-us: the duration in microsecond for assert reset signal. + +Below is the example of USB power sequence properties on USB device +nodes which have two level USB hubs. + +&usbotg1 { + vbus-supply = <®_usb_otg1_vbus>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usb_otg1_id>; + status = "okay"; + + #address-cells = <1>; + #size-cells = <0>; + genesys: hub@1 { + compatible = "usb5e3,608"; + reg = <1>; + + clocks = <&clks IMX6SX_CLK_CKO>; + reset-gpios = <&gpio4 5 GPIO_ACTIVE_LOW>; /* hub reset pin */ + reset-duration-us = <10>; + + #address-cells = <1>; + #size-cells = <0>; + asix: ethernet@1 { + compatible = "usbb95,1708"; + reg = <1>; + + clocks = <&clks IMX6SX_CLK_IPG>; + reset-gpios = <&gpio4 6 GPIO_ACTIVE_LOW>; /* ethernet_rst */ + reset-duration-us = <15>; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/usb/usb-device.txt b/Documentation/devicetree/bindings/usb/usb-device.txt index 1c35e7b..3661dd2 100644 --- a/Documentation/devicetree/bindings/usb/usb-device.txt +++ b/Documentation/devicetree/bindings/usb/usb-device.txt @@ -13,6 +13,10 @@ Required properties: - reg: the port number which this device is connecting to, the range is 1-31. +Optional properties: +power sequence properties, see +Documentation/devicetree/bindings/power/pwrseq/pwrseq-generic.txt for detail + Example: &usb1 { @@ -21,8 +25,12 @@ Example: #address-cells = <1>; #size-cells = <0>; - hub: genesys@1 { + genesys: hub@1 { compatible = "usb5e3,608"; reg = <1>; + + clocks = <&clks IMX6SX_CLK_CKO>; + reset-gpios = <&gpio4 5 GPIO_ACTIVE_LOW>; /* hub reset pin */ + 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 46726d4..dd5b442 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. @@ -2794,6 +2795,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 babaf82..7cde109 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -582,6 +582,12 @@ S: Maintained F: Documentation/i2c/busses/i2c-ali1563 F: drivers/i2c/busses/i2c-ali1563.c +ALLWINNER SUN8I-EMAC ETHERNET DRIVER +M: Corentin Labbe +L: netdev@vger.kernel.org +S: Maintained +F: drivers/net/ethernet/allwinner/sun8i-emac.c + ALLWINNER SECURITY SYSTEM M: Corentin Labbe L: linux-crypto@vger.kernel.org @@ -2364,6 +2370,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 M: Luis de Bethencourt M: Salah Triki @@ -9353,6 +9367,15 @@ F: include/linux/pm_* F: include/linux/powercap.h F: drivers/powercap/ +POWER SEQUENCE LIBRARY +M: Peter Chen +T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git +L: linux-pm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/power/pwrseq/ +F: drivers/power/pwrseq/ +F: include/linux/power/pwrseq.h/ + POWER SUPPLY CLASS/SUBSYSTEM and DRIVERS M: Sebastian Reichel M: Dmitry Eremin-Solenikov diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 61f6ccc..1144422 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 bdc1d5a..b619d3c 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 faacd52..0027b73 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_AT91RM9200) += \ at91rm9200ek.dtb \ @@ -148,6 +154,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 \ @@ -339,6 +346,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 \ @@ -356,6 +364,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 \ @@ -380,6 +389,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 \ @@ -398,6 +408,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 \ @@ -407,6 +418,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 \ @@ -533,6 +545,26 @@ dtb-$(CONFIG_SOC_AM33XX) += \ am335x-base0033.dtb \ am335x-bone.dtb \ am335x-boneblack.dtb \ + am335x-arduino-tre.dtb \ + am335x-sancloud-bbe.dtb \ + am335x-boneblue.dtb \ + am335x-boneblack-wireless-emmc-overlay.dtb \ + am335x-boneblack-wireless.dtb \ + am335x-bonegreen-wireless.dtb \ + am335x-boneblack-audio.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-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 \ @@ -552,6 +584,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..cfb17df --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-common-no-capemgr.dtsi @@ -0,0 +1,348 @@ +/* + * 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 */ + }; + + chosen { + stdout-path = &uart0; + }; + + 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 { + slaves = <1>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&cpsw_default>; + pinctrl-1 = <&cpsw_sleep>; + 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-pins.dtsi b/arch/arm/boot/dts/am335x-bone-common-universal-pins.dtsi new file mode 100644 index 0000000..e4d4971 --- /dev/null +++ b/arch/arm/boot/dts/am335x-bone-common-universal-pins.dtsi @@ -0,0 +1,941 @@ +/* + * 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_33_qep_pin: pinmux_P8_33_qep_pin { + pinctrl-single,pins = <0x0d4 0x22>; }; /* Mode 2, Pull-Down, RxActive */ + + /* 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_35_qep_pin: pinmux_P8_35_qep_pin { + pinctrl-single,pins = <0x0d0 0x22>; }; /* Mode 2, Pull-Down, RxActive */ + + /* 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 */ +}; 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 e247c15..56e2120 100644 --- a/arch/arm/boot/dts/am335x-bone-common.dtsi +++ b/arch/arm/boot/dts/am335x-bone-common.dtsi @@ -29,14 +29,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"; @@ -66,9 +66,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 */ @@ -99,15 +96,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 */ @@ -127,6 +120,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) @@ -312,6 +307,9 @@ */ ti,pmic-shutdown-controller; + interrupt-parent = <&intc>; + interrupts = <7>; /* NNMI */ + regulators { dcdc1_reg: regulator@0 { regulator-name = "vdds_dpr"; @@ -393,3 +391,32 @@ &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"; + }; + }; + }; +}; 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..603ef0a --- /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 = < + AM33XX_IOPAD(0x9b4, 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..8b6256d 100644 --- a/arch/arm/boot/dts/am335x-bone.dts +++ b/arch/arm/boot/dts/am335x-bone.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"; 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..af92fcf --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-hdmi-overlay.dts @@ -0,0 +1,106 @@ +/* + * 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..af92fcf --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-nhdmi-overlay.dts @@ -0,0 +1,106 @@ +/* + * 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-wireless-emmc-overlay.dts b/arch/arm/boot/dts/am335x-boneblack-wireless-emmc-overlay.dts new file mode 100644 index 0000000..68b01eb --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-wireless-emmc-overlay.dts @@ -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. + */ +/dts-v1/; + +#include "am33xx.dtsi" +#include "am335x-bone-common.dtsi" +/* #include "am33xx-overlay-edma-fix.dtsi" */ +#include "am335x-boneblack-wl1835.dtsi" +/* #include "am335x-bone-jtag.dtsi" */ + +/ { + model = "TI AM335x BeagleBone Black Wireless"; + 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"; +}; + +&mac { + status = "disabled"; +}; + +&mmc3 { + status = "okay"; +}; diff --git b/arch/arm/boot/dts/am335x-boneblack-wireless.dts b/arch/arm/boot/dts/am335x-boneblack-wireless.dts new file mode 100644 index 0000000..42da263 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-wireless.dts @@ -0,0 +1,103 @@ +/* + * 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 "am335x-boneblack-wl1835.dtsi" +/* #include "am335x-bone-jtag.dtsi" */ + +/ { + model = "TI AM335x BeagleBone Black Wireless"; + 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"; +}; + +&mac { + status = "disabled"; +}; + +&mmc3 { + status = "okay"; +}; + +&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-wl1835.dtsi b/arch/arm/boot/dts/am335x-boneblack-wl1835.dtsi new file mode 100644 index 0000000..ec6c0e4 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblack-wl1835.dtsi @@ -0,0 +1,143 @@ + +#include + +/ { + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us= <70000>; + + /* WL_EN */ + gpio = <&gpio3 9 0>; + enable-active-high; + }; + + leds { + pinctrl-names = "default"; + pinctrl-0 = <&wl18xx_pins>; + compatible = "gpio-leds"; + + wl18xx_bt_en { + label = "wl18xx_bt_en"; + gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; + + btwilink { + compatible = "btwilink"; + }; +}; + +&am33xx_pinmux { + wl18xx_pins: pinmux_wl18xx_pins { + pinctrl-single,pins = < + 0x128 (PIN_OUTPUT_PULLUP | MUX_MODE7) /* (K17) gmii1_txd0.gpio0[28] - BT_EN */ + >; + }; + + wlbtbuf_pin: pinmux_wlbtbuf_pin { + pinctrl-single,pins = < + 0x130 ( PIN_OUTPUT_PULLUP | MUX_MODE7 ) /* (L18) gmii1_rxclk.gpio3[10] - LS_BUF_EN */ + >; + }; + + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + 0x13c ( PIN_INPUT_PULLUP | MUX_MODE6 ) /* (L15) gmii1_rxd1.mmc2_clk */ + 0x114 ( PIN_INPUT_PULLUP | MUX_MODE6 ) /* (J16) gmii1_txen.mmc2_cmd */ + 0x118 ( PIN_INPUT_PULLUP | MUX_MODE5 ) /* (J17) gmii1_rxdv.mmc2_dat0 */ + 0x11c ( PIN_INPUT_PULLUP | MUX_MODE5 ) /* (J18) gmii1_txd3.mmc2_dat1 */ + 0x120 ( PIN_INPUT_PULLUP | MUX_MODE5 ) /* (K15) gmii1_txd2.mmc2_dat2 */ + 0x108 ( PIN_INPUT_PULLUP | MUX_MODE5 ) /* (H16) gmii1_col.mmc2_dat3 */ + >; + }; + + mmc3_pins_sleep: pinmux_mmc3_pins_sleep { + pinctrl-single,pins = < + 0x13c ( PIN_INPUT_PULLDOWN | MUX_MODE6 ) /* (L15) gmii1_rxd1.mmc2_clk */ + 0x114 ( PIN_INPUT_PULLDOWN | MUX_MODE6 ) /* (J16) gmii1_txen.mmc2_cmd */ + 0x118 ( PIN_INPUT_PULLDOWN | MUX_MODE5 ) /* (J17) gmii1_rxdv.mmc2_dat0 */ + 0x11c ( PIN_INPUT_PULLDOWN | MUX_MODE5 ) /* (J18) gmii1_txd3.mmc2_dat1 */ + 0x120 ( PIN_INPUT_PULLDOWN | MUX_MODE5 ) /* (K15) gmii1_txd2.mmc2_dat2 */ + 0x108 ( PIN_INPUT_PULLDOWN | MUX_MODE5 ) /* (H16) gmii1_col.mmc2_dat3 */ + >; + }; + + /* wl18xx card enable/irq GPIOs. */ + wlan_pins: pinmux_wlan_pins { + pinctrl-single,pins = < + 0x12c (PIN_OUTPUT_PULLDOWN | MUX_MODE7 ) /* (K18) gmii1_txclk.gpio3[9] - WL_EN */ + 0x144 (PIN_INPUT_PULLDOWN | MUX_MODE7 ) /* (H18) rmii1_refclk.gpio0[29] - WL_IRQ */ + >; + }; + + /* wl18xx card enable/irq GPIOs. */ + wlan_pins_sleep: pinmux_wlan_pins_sleep { + pinctrl-single,pins = < + 0x12c (PIN_OUTPUT_PULLDOWN | MUX_MODE7 ) /* (K18) gmii1_txclk.gpio3[9] - WL_EN */ + 0x144 (PIN_INPUT_PULLDOWN | MUX_MODE7 ) /* (H18) rmii1_refclk.gpio0[29] - WL_IRQ */ + >; + }; + + 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 */ + >; + }; +}; + +&mmc3 { + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&mmc3_pins &wlan_pins &wlbtbuf_pin>; + pinctrl-1 = <&mmc3_pins_sleep &wlan_pins_sleep &wlbtbuf_pin>; + 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 = <29 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&uart3 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&uart3_pins_default>; + pinctrl-1 = <&uart3_pins_sleep>; + status = "okay"; +}; + +&gpio3 { + ls_buf_en { + gpio-hog; + gpios = <10 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "LS_BUF_EN"; + }; +}; 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 ca72167..525a25a 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"; @@ -101,7 +103,3 @@ }; }; }; - -&rtc { - system-power-controller; -}; diff --git b/arch/arm/boot/dts/am335x-boneblue-wl1835.dtsi b/arch/arm/boot/dts/am335x-boneblue-wl1835.dtsi new file mode 100644 index 0000000..dd16d82 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblue-wl1835.dtsi @@ -0,0 +1,142 @@ +#include + +/ { + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us= <70000>; + + /* WL_EN */ + gpio = <&gpio3 9 0>; + enable-active-high; + }; + + leds { + pinctrl-names = "default"; + pinctrl-0 = <&wl18xx_pins>; + compatible = "gpio-leds"; + + wl18xx_bt_en { + label = "wl18xx_bt_en"; + gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + }; + + btwilink { + compatible = "btwilink"; + }; +}; + +&am33xx_pinmux { + wl18xx_pins: pinmux_wl18xx_pins { + pinctrl-single,pins = < + 0x128 (PIN_OUTPUT_PULLUP | MUX_MODE7) /* (K17) gmii1_txd0.gpio0[28] - BT_EN */ + >; + }; + + wlbtbuf_pin: pinmux_wlbtbuf_pin { + pinctrl-single,pins = < + 0x130 ( PIN_OUTPUT_PULLUP | MUX_MODE7 ) /* (L18) gmii1_rxclk.gpio3[10] - LS_BUF_EN */ + >; + }; + + mmc3_pins: pinmux_mmc3_pins { + pinctrl-single,pins = < + 0x13c ( PIN_INPUT_PULLUP | MUX_MODE6 ) /* (L15) gmii1_rxd1.mmc2_clk */ + 0x114 ( PIN_INPUT_PULLUP | MUX_MODE6 ) /* (J16) gmii1_txen.mmc2_cmd */ + 0x118 ( PIN_INPUT_PULLUP | MUX_MODE5 ) /* (J17) gmii1_rxdv.mmc2_dat0 */ + 0x11c ( PIN_INPUT_PULLUP | MUX_MODE5 ) /* (J18) gmii1_txd3.mmc2_dat1 */ + 0x120 ( PIN_INPUT_PULLUP | MUX_MODE5 ) /* (K15) gmii1_txd2.mmc2_dat2 */ + 0x108 ( PIN_INPUT_PULLUP | MUX_MODE5 ) /* (H16) gmii1_col.mmc2_dat3 */ + >; + }; + + mmc3_pins_sleep: pinmux_mmc3_pins_sleep { + pinctrl-single,pins = < + 0x13c ( PIN_INPUT_PULLDOWN | MUX_MODE6 ) /* (L15) gmii1_rxd1.mmc2_clk */ + 0x114 ( PIN_INPUT_PULLDOWN | MUX_MODE6 ) /* (J16) gmii1_txen.mmc2_cmd */ + 0x118 ( PIN_INPUT_PULLDOWN | MUX_MODE5 ) /* (J17) gmii1_rxdv.mmc2_dat0 */ + 0x11c ( PIN_INPUT_PULLDOWN | MUX_MODE5 ) /* (J18) gmii1_txd3.mmc2_dat1 */ + 0x120 ( PIN_INPUT_PULLDOWN | MUX_MODE5 ) /* (K15) gmii1_txd2.mmc2_dat2 */ + 0x108 ( PIN_INPUT_PULLDOWN | MUX_MODE5 ) /* (H16) gmii1_col.mmc2_dat3 */ + >; + }; + + /* wl18xx card enable/irq GPIOs. */ + wlan_pins: pinmux_wlan_pins { + pinctrl-single,pins = < + 0x12c (PIN_OUTPUT_PULLDOWN | MUX_MODE7 ) /* (K18) gmii1_txclk.gpio3[9] - WL_EN */ + 0x124 (PIN_INPUT_PULLUP | MUX_MODE7 ) /* (K16) gmii1_txd1.gpio0[21] - WL_IRQ */ + >; + }; + + /* wl18xx card enable/irq GPIOs. */ + wlan_pins_sleep: pinmux_wlan_pins_sleep { + pinctrl-single,pins = < + 0x12c (PIN_OUTPUT_PULLDOWN | MUX_MODE7 ) /* (K18) gmii1_txclk.gpio3[9] - WL_EN */ + 0x124 (PIN_INPUT_PULLUP | MUX_MODE7 ) /* (K16) gmii1_txd1.gpio0[21] - WL_IRQ */ + >; + }; + + 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 */ + >; + }; +}; + +&mmc3 { + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + bus-width = <4>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&mmc3_pins &wlan_pins &wlbtbuf_pin>; + pinctrl-1 = <&mmc3_pins_sleep &wlan_pins_sleep &wlbtbuf_pin>; + 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 = <21 IRQ_TYPE_EDGE_RISING>; + }; +}; + +&uart3 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&uart3_pins_default>; + pinctrl-1 = <&uart3_pins_sleep>; + status = "okay"; +}; + +&gpio3 { + ls_buf_en { + gpio-hog; + gpios = <10 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "LS_BUF_EN"; + }; +}; diff --git b/arch/arm/boot/dts/am335x-boneblue.dts b/arch/arm/boot/dts/am335x-boneblue.dts new file mode 100644 index 0000000..8a853d6 --- /dev/null +++ b/arch/arm/boot/dts/am335x-boneblue.dts @@ -0,0 +1,316 @@ +/* + * 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" +#include "am335x-bone-common-universal-pins.dtsi" +#include "am335x-boneblue-wl1835.dtsi" +/* #include "am335x-bone-jtag.dtsi" */ + +#define BLUE_IO(x, y) AM33XX_IOPAD((x)*4+0x800, (y)) + +/ { + model = "TI AM335x BeagleBone Blue"; + compatible = "ti,am335x-bone-blue", "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"; +}; + +&mac { + status = "disabled"; +}; + +/**************************************** +* Pin Muxing +****************************************/ +&am33xx_pinmux { + A15_spi_pin: pinmux_A15_spi_pin { + pinctrl-single,pins = < + BLUE_IO(108, PIN_INPUT_PULLUP | MUX_MODE4) /* spi1_cs1 */ + >; + }; + + mux_helper_pins: pins { + pinctrl-single,pins = < + /* PRU encoder input */ + 0x03c 0x36 /* P8_15,PRU0_r31_15,MODE6 */ + 0x038 0x36 /* P8_16,PRU0_r31_16,MODE6 */ + + /* PRU Servo output */ + 0x0e0 0x25 /*pru1_pru_r30_8, MODE5*/ + 0x0e8 0x25 /*pru1_pru_r30_10, MODE5 */ + 0x0e4 0x25 /*pr1_pru1_pru_r30_9, MODE5 */ + 0x0ec 0x25 /*pru1_pru_r30_11, MODE5 */ + 0x0b8 0x25 /*pru1_pru_r30_6, MODE5 */ + 0x0bc 0x25 /*pru1_pru_r30_7, MODE5 */ + 0x0b0 0x25 /*pru1_pru_r30_4, MODE5 */ + 0x0b4 0x25 /*pru1_pru_r30_5, MODE5 */ + + /* GPIO Inputs */ + 0x09c 0x37 /*P8.9 Pause BUTTON, input pullup*/ + 0x098 0x37 /*P8.10 MODE BUTTON input pullup*/ + 0x1AC 0x37 /*P9.25 MPU-9150 INTERRUPT IN*/ + + /* GPIO Outputs + 0x090 0x0F /*P8.7 - R7*/ + 0x094 0x0F /*P8.8*/ + 0x028 0x0F /*P8.14*/ + 0x02C 0x0F /*P8.17*/ + 0x08c 0x0F /*P8.18*/ + 0x07c 0x0F /*P8.26*/ + /* 0x0?? 0x0F /*P8.32 - T5 - BATT_LED3 */ + 0x0cc 0x0F /*P8.34*/ + 0x0C8 0x0F /*P8.36*/ + 0x0a8 0x0F /*P8.43*/ + 0x0ac 0x0F /*P8.44*/ + 0x0a0 0x0F /*P8.45*/ + 0x0a4 0x0F /*P8.46*/ + 0x078 0x0F /*P9.12*/ + 0x074 0x0F /*P9.13*/ + 0x040 0x0F /*P9.15*/ + 0x044 0x0F /*P9.23*/ + 0x1b4 0x0F /*P9.41*/ + >; + }; + + /****************************************/ + /* DSM2 UART4 pinmux */ + /* only pin that swaps between GPIO&UART*/ + /****************************************/ + P9_11_default_pin: pinmux_P9_11_default_pin { + pinctrl-single,pins = <0x070 0x36>; }; /* Mode 6 uart */ + 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, uart */ +}; + +&ocp { + /**************************************** + * activates the pinmux helper list of pin modes + ****************************************/ + test_helper: helper { + compatible = "bone-pinmux-helper"; + pinctrl-names = "default"; + pinctrl-0 = <&mux_helper_pins>; + + status = "okay"; + }; + + /**************************************** + * configurable DSM2 pin swaps between GPIO & UART4 at runtime + ****************************************/ + 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>; + }; +}; + + +/**************************************** + PWMSS +****************************************/ +&epwmss0 { + status = "okay"; +}; + +&epwmss1 { + status = "okay"; +}; + +&epwmss2 { + status = "okay"; +}; + +&ehrpwm0 { + status = "okay"; +}; + +&ehrpwm1 { + pinctrl-names = "default"; + pinctrl-0 = <&P8_19_pwm_pin &P8_13_pwm_pin>; + status = "okay"; +}; + +&ehrpwm2 { + pinctrl-names = "default"; + pinctrl-0 = <&P9_14_pwm_pin &P9_16_pwm_pin>; + status = "okay"; +}; + + +//&eqep0 { +// pinctrl-names = "default"; +// pinctrl-0 = <&P9_92_qep_pin &P9_27_qep_pin>; +// +// count_mode = <0>; /* 0 - Quadrature mode, normal 90 phase offset cha & chb. 1 - Direction mode. cha input = clock, chb input = direction */ +// swap_inputs = <0>; /* Are channel A and channel B swapped? (0 - no, 1 - yes) */ +// invert_qa = <1>; /* Should we invert the channel A input? */ +// invert_qb = <1>; /* Should we invert the channel B input? I invert these because my encoder outputs drive transistors that pull down the pins */ +// invert_qi = <0>; /* Should we invert the index input? */ +// invert_qs = <0>; /* Should we invert the strobe input? */ +// +// status = "okay"; +//}; + +//&eqep1 { +// pinctrl-names = "default"; +// pinctrl-0 = <&P8_33_qep_pin &P8_35_qep_pin>; +// +// count_mode = <0>; /* 0 - Quadrature mode, normal 90 phase offset cha & chb. 1 - Direction mode. cha input = clock, chb input = direction */ +// swap_inputs = <0>; /* Are channel A and channel B swapped? (0 - no, 1 - yes) */ +// invert_qa = <1>; /* Should we invert the channel A input? */ +// invert_qb = <1>; /* Should we invert the channel B input? I invert these because my encoder outputs drive transistors that pull down the pins */ +// invert_qi = <0>; /* Should we invert the index input? */ +// invert_qs = <0>; /* Should we invert the strobe input? */ +// +// status = "okay"; +//}; + +//&eqep2 { +// pinctrl-names = "default"; +// pinctrl-0 = <&P8_12_qep_pin &P8_11_qep_pin>; +// +// count_mode = <0>; /* 0 - Quadrature mode, normal 90 phase offset cha & chb. 1 - Direction mode. cha input = clock, chb input = direction */ +// swap_inputs = <0>; /* Are channel A and channel B swapped? (0 - no, 1 - yes) */ +// invert_qa = <1>; /* Should we invert the channel A input? */ +// invert_qb = <1>; /* Should we invert the channel B input? I invert these because my encoder outputs drive transistors that pull down the pins */ +// invert_qi = <0>; /* Should we invert the index input? */ +// invert_qs = <0>; /* Should we invert the strobe input? */ +// +// status = "okay"; +//}; + + +/**************************************** + UART + + UART 1, 2, 5 are used on the cape. + The Beaglebone Blue uses others so all are enabled here. +****************************************/ + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = < + &P9_24_uart_pin /* uart1_txd */ + &P9_26_uart_pin /* uart1_rxd */ + >; + status = "okay"; +}; + +&uart2 { + pinctrl-names = "default"; + pinctrl-0 = < + &P9_21_uart_pin /* uart2_txd */ + &P9_22_uart_pin /* uart2_rxd */ + >; + status = "okay"; +}; + +&uart4 { + status = "okay"; +}; + +&uart5 { + pinctrl-names = "default"; + pinctrl-0 = < + &P8_37_uart_pin /* uart5_txd */ + &P8_38_uart_pin /* uart5_rxd */ + >; + status = "okay"; +}; + + +/**************************************** + PRU +****************************************/ +&pruss { + status = "okay"; +}; + + +/**************************************** + I2C +****************************************/ +&i2c1 { /* i2c1 is numbered correctly */ + pinctrl-names = "default"; + pinctrl-0 = <&P9_17_i2c_pin &P9_18_i2c_pin>; + status = "okay"; + + /* this is the configuration part */ + clock-frequency = <400000>; + + #address-cells = <1>; + #size-cells = <0>; + + /* add any i2c devices on the bus here */ +}; + +&i2c2 { /* i2c2 is numbered correctly */ + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; + + status = "okay"; + clock-frequency = <400000>; + + #address-cells = <1>; + #size-cells = <0>; + + /* add any i2c devices on the bus here */ +}; + +/**************************************** + SPI +****************************************/ +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&P9_31_spi_pin /* spi1_sclk */ + &P9_29_spi_pin /* spi1_d0 */ + &P9_30_spi_pin /* spi1_d1 */ + &P9_28_gpio_pu_pin /* gpio3_17 - cs0 */ + &A15_spi_pin /* spi1_cs1 */ + >; + + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + spidev@1 { + spi-max-frequency = <24000000>; + reg = <0>; + compatible = "linux,spidev"; + }; +}; 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..f37f39e --- /dev/null +++ b/arch/arm/boot/dts/am335x-bonegreen-wireless.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 "am335x-bonegreen-wl1835.dtsi" +/* #include "am335x-bone-jtag.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..0d4798d --- /dev/null +++ b/arch/arm/boot/dts/am335x-bonegreen-wl1835.dtsi @@ -0,0 +1,147 @@ +#include + +/ { + wlan_en_reg: fixedregulator@2 { + compatible = "regulator-fixed"; + regulator-name = "wlan-en-regulator"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + startup-delay-us= <70000>; + + /* WL_EN */ + gpio = <&gpio0 26 0>; + enable-active-high; + }; + + btwilink { + compatible = "btwilink"; + }; + + wilink8_pcm: wilink8_pcm { + compatible = "ti,wilink8_bt"; + status = "okay"; + }; + + sound{ + compatible = "ti,wilink8-bt-audio"; + ti,model = "WILINK8_BT"; + ti,audio-codec = <&wilink8_pcm>; + ti,mcasp-controller = <&mcasp0>; + ti,codec-clock-rate = <24000000>; + }; +}; + +&am33xx_pinmux { + bt_pins: pinmux_bt_pins { + pinctrl-single,pins = < + 0x78 (PIN_OUTPUT | 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_PULLDOWN | 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_PULLDOWN | MUX_MODE7) /* gpmc_ad10.gpio0_26 WL_EN*/ + 0x2C (PIN_INPUT_PULLDOWN | 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 */ + >; + }; +}; + +&mmc3 { + dmas = <&edma_xbar 12 0 1 + &edma_xbar 13 0 2>; + dma-names = "tx", "rx"; + status = "okay"; + vmmc-supply = <&wlan_en_reg>; + 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_EDGE_RISING>; + }; +}; + +&uart3 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&uart3_pins_default &bt_pins>; + pinctrl-1 = <&uart3_pins_sleep &bt_pins>; + status = "okay"; +}; + +/* BT_AUD_OUT from wl1835 has to be pulled low when WL_EN is activated. */ +/* in case it isn't, wilink8 ends up in one of the test modes that */ +/* intruces various issues (elp wkaeup timeouts etc.) */ +/* On the BBGW this pin is routed through the level shifter (U21) that */ +/* introduces a pullup on the line and wilink8 ends up in a bad state. */ +/* use a gpio hog to force this pin low. An alternative may be adding */ +/* an external pulldown on U21 pin 4. */ + +&gpio3 { + bt_aud_in { + gpio-hog; + gpios = <16 GPIO_ACTIVE_HIGH>; + output-low; + line-name = "MCASP0_AXR0"; + }; +}; diff --git a/arch/arm/boot/dts/am335x-bonegreen.dts b/arch/arm/boot/dts/am335x-bonegreen.dts index dce3c86..fe86cca 100644 --- a/arch/arm/boot/dts/am335x-bonegreen.dts +++ b/arch/arm/boot/dts/am335x-bonegreen.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 Green"; @@ -32,22 +34,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 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 5599772..5424472 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -152,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>; @@ -561,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>; diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi index d9bfb94..08726e9 100644 --- a/arch/arm/boot/dts/dra7.dtsi +++ b/arch/arm/boot/dts/dra7.dtsi @@ -416,6 +416,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 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 a/arch/arm/boot/dts/imx6q-evi.dts b/arch/arm/boot/dts/imx6q-evi.dts index 4fa5601..49c6f61 100644 --- a/arch/arm/boot/dts/imx6q-evi.dts +++ b/arch/arm/boot/dts/imx6q-evi.dts @@ -54,18 +54,6 @@ reg = <0x10000000 0x40000000>; }; - reg_usbh1_vbus: regulator-usbhubreset { - compatible = "regulator-fixed"; - regulator-name = "usbh1_vbus"; - regulator-min-microvolt = <5000000>; - regulator-max-microvolt = <5000000>; - enable-active-high; - startup-delay-us = <2>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usbh1_hubreset>; - gpio = <&gpio7 12 GPIO_ACTIVE_HIGH>; - }; - reg_usb_otg_vbus: regulator-usbotgvbus { compatible = "regulator-fixed"; regulator-name = "usb_otg_vbus"; @@ -204,12 +192,18 @@ }; &usbh1 { - vbus-supply = <®_usbh1_vbus>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usbh1>; dr_mode = "host"; disable-over-current; status = "okay"; + + usb2415host: hub@1 { + compatible = "usb424,2513"; + reg = <1>; + reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>; + reset-duration-us = <3000>; + }; }; &usbotg { @@ -467,11 +461,6 @@ MX6QDL_PAD_GPIO_3__USB_H1_OC 0x1b0b0 /* usbh1_b OC */ MX6QDL_PAD_GPIO_0__GPIO1_IO00 0x1b0b0 - >; - }; - - pinctrl_usbh1_hubreset: usbh1hubresetgrp { - fsl,pins = < MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x1b0b0 >; }; 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..87fe31f 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>; @@ -188,7 +179,7 @@ pinctrl_usbh: usbhgrp { fsl,pins = < - MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x80000000 + MX6QDL_PAD_GPIO_17__GPIO7_IO12 0x1b0b0 MX6QDL_PAD_NANDF_CS2__CCM_CLKO2 0x130b0 >; }; @@ -259,9 +250,16 @@ &usbh1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usbh>; - vbus-supply = <®_usb_h1_vbus>; - clocks = <&clks IMX6QDL_CLK_CKO>; status = "okay"; + + usb2415: hub@1 { + compatible = "usb424,2514"; + reg = <1>; + + clocks = <&clks IMX6QDL_CLK_CKO>; + reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>; + reset-duration-us = <3000>; + }; }; &usdhc3 { 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 a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi index be2e747..b946245 100644 --- a/arch/arm/boot/dts/imx6qdl.dtsi +++ b/arch/arm/boot/dts/imx6qdl.dtsi @@ -935,6 +935,8 @@ usbh1: usb@02184200 { compatible = "fsl,imx6q-usb", "fsl,imx27-usb"; + #address-cells = <1>; + #size-cells = <0>; reg = <0x02184200 0x200>; interrupts = <0 40 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clks IMX6QDL_CLK_USBOH3>; @@ -949,6 +951,8 @@ usbh2: usb@02184400 { compatible = "fsl,imx6q-usb", "fsl,imx27-usb"; + #address-cells = <1>; + #size-cells = <0>; reg = <0x02184400 0x200>; interrupts = <0 41 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clks IMX6QDL_CLK_USBOH3>; @@ -962,6 +966,8 @@ usbh3: usb@02184600 { compatible = "fsl,imx6q-usb", "fsl,imx27-usb"; + #address-cells = <1>; + #size-cells = <0>; reg = <0x02184600 0x200>; interrupts = <0 42 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clks IMX6QDL_CLK_USBOH3>; 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 8ffde06..f18a3e7 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 a19d907..71f4390 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 f8f1395..031ca35 100644 --- a/arch/arm/boot/dts/omap4-panda-common.dtsi +++ b/arch/arm/boot/dts/omap4-panda-common.dtsi @@ -461,16 +461,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 10d73a7..0faaf07 100644 --- a/arch/arm/boot/dts/omap4-sdp.dts +++ b/arch/arm/boot/dts/omap4-sdp.dts @@ -502,11 +502,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/boot/dts/sun8i-h3-orangepi-one.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts index 0adf932..8df5c74 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-one.dts @@ -94,6 +94,17 @@ status = "okay"; }; +&emac { + phy = <&phy1>; + phy-mode = "mii"; + allwinner,use-internal-phy; + allwinner,leds-active-low; + status = "okay"; + phy1: ethernet-phy@1 { + reg = <1>; + }; +}; + &mmc0 { pinctrl-names = "default"; pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin>; diff --git a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts index daf50b9..b8340f7 100644 --- a/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts +++ b/arch/arm/boot/dts/sun8i-h3-orangepi-pc.dts @@ -165,3 +165,14 @@ /* USB VBUS is always on */ status = "okay"; }; + +&emac { + phy = <&phy1>; + phy-mode = "mii"; + allwinner,use-internal-phy; + allwinner,leds-active-low; + status = "okay"; + phy1: ethernet-phy@1 { + reg = <1>; + }; +}; diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi index fdf9fdb..93c88f3 100644 --- a/arch/arm/boot/dts/sun8i-h3.dtsi +++ b/arch/arm/boot/dts/sun8i-h3.dtsi @@ -50,6 +50,10 @@ / { interrupt-parent = <&gic>; + aliases { + ethernet0 = &emac; + }; + cpus { #address-cells = <1>; #size-cells = <0>; @@ -441,6 +445,20 @@ status = "disabled"; }; + emac: ethernet@1c30000 { + compatible = "allwinner,sun8i-h3-emac"; + reg = <0x01c30000 0x104>, <0x01c00030 0x4>; + reg-names = "emac", "syscon"; + interrupts = ; + resets = <&ccu RST_BUS_EMAC>, <&ccu RST_BUS_EPHY>; + reset-names = "ahb", "ephy"; + clocks = <&ccu CLK_BUS_EMAC>, <&ccu CLK_BUS_EPHY>; + clock-names = "ahb", "ephy"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + gic: interrupt-controller@01c81000 { compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic"; reg = <0x01c81000 0x1000>, diff --git a/arch/arm/mach-imx/devices/Kconfig b/arch/arm/mach-imx/devices/Kconfig index 6ffe572..bb7bd88 100644 --- a/arch/arm/mach-imx/devices/Kconfig +++ b/arch/arm/mach-imx/devices/Kconfig @@ -68,3 +68,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 e920dd8..f5eff35 100644 --- a/arch/arm/mach-omap2/omap_device.c +++ b/arch/arm/mach-omap2/omap_device.c @@ -138,8 +138,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"); @@ -190,6 +190,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); @@ -206,12 +226,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_REMOVED_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); @@ -785,6 +814,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/gpio/Kconfig b/drivers/gpio/Kconfig index 24caedb..47de741 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 2a035ed..aed419d 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 4d341db..808a6fb 100644 --- a/drivers/gpu/drm/i2c/Kconfig +++ b/drivers/gpu/drm/i2c/Kconfig @@ -1,6 +1,12 @@ menu "I2C encoder or helper chips" depends on DRM && DRM_KMS_HELPER && I2C +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 43aa33b..62a4eea 100644 --- a/drivers/gpu/drm/i2c/Makefile +++ b/drivers/gpu/drm/i2c/Makefile @@ -8,3 +8,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/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 78f148e..2b791fe 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -52,6 +52,27 @@ config BMC150_ACCEL_SPI tristate select REGMAP_SPI +config DMARD06 + tristate "Domintech DMARD06 Digital Accelerometer Driver" + depends on OF || COMPILE_TEST + depends on I2C + help + Say yes here to build support for the Domintech low-g tri-axial + digital accelerometers: DMARD05, DMARD06, DMARD07. + + To compile this driver as a module, choose M here: the + module will be called dmard06. + +config DMARD09 + tristate "Domintech DMARD09 3-axis Accelerometer Driver" + depends on I2C + help + Say yes here to get support for the Domintech DMARD09 3-axis + accelerometer. + + Choosing M will build the driver as a module. If so, the module + will be called dmard09. + config HID_SENSOR_ACCEL_3D depends on HID_SENSOR_HUB select IIO_BUFFER @@ -98,14 +119,35 @@ config IIO_ST_ACCEL_SPI_3AXIS config KXSD9 tristate "Kionix KXSD9 Accelerometer Driver" - depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say yes here to build support for the Kionix KXSD9 accelerometer. - Currently this only supports the device via an SPI interface. + It can be accessed using an (optional) SPI or I2C interface. To compile this driver as a module, choose M here: the module will be called kxsd9. +config KXSD9_SPI + tristate "Kionix KXSD9 SPI transport" + depends on KXSD9 + depends on SPI + default KXSD9 + select REGMAP_SPI + help + Say yes here to enable the Kionix KXSD9 accelerometer + SPI transport channel. + +config KXSD9_I2C + tristate "Kionix KXSD9 I2C transport" + depends on KXSD9 + depends on I2C + default KXSD9 + select REGMAP_I2C + help + Say yes here to enable the Kionix KXSD9 accelerometer + I2C transport channel. + config KXCJK1013 tristate "Kionix 3-Axis Accelerometer Driver" depends on I2C @@ -119,6 +161,16 @@ config KXCJK1013 To compile this driver as a module, choose M here: the module will be called kxcjk-1013. +config MC3230 + tristate "mCube MC3230 Digital Accelerometer Driver" + depends on I2C + help + Say yes here to build support for the mCube MC3230 low-g tri-axial + digital accelerometer. + + To compile this driver as a module, choose M here: the + module will be called mc3230. + config MMA7455 tristate select IIO_BUFFER diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 6cedbec..f5d3dde 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -8,9 +8,14 @@ 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 +obj-$(CONFIG_DMARD06) += dmard06.o +obj-$(CONFIG_DMARD09) += dmard09.o obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o obj-$(CONFIG_KXSD9) += kxsd9.o +obj-$(CONFIG_KXSD9_SPI) += kxsd9-spi.o +obj-$(CONFIG_KXSD9_I2C) += kxsd9-i2c.o +obj-$(CONFIG_MC3230) += mc3230.o obj-$(CONFIG_MMA7455) += mma7455_core.o obj-$(CONFIG_MMA7455_I2C) += mma7455_i2c.o diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index e3f88ba..0890934 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -469,13 +469,14 @@ static int bma180_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + mutex_lock(&data->mutex); - if (iio_buffer_enabled(indio_dev)) { - mutex_unlock(&data->mutex); - return -EBUSY; - } ret = bma180_get_data_reg(data, chan->scan_index); mutex_unlock(&data->mutex); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; *val = sign_extend32(ret >> chan->scan_type.shift, diff --git b/drivers/iio/accel/dmard06.c b/drivers/iio/accel/dmard06.c new file mode 100644 index 0000000..656ca8e --- /dev/null +++ b/drivers/iio/accel/dmard06.c @@ -0,0 +1,241 @@ +/* + * IIO driver for Domintech DMARD06 accelerometer + * + * Copyright (C) 2016 Aleksei Mamlin + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include + +#define DMARD06_DRV_NAME "dmard06" + +/* Device data registers */ +#define DMARD06_CHIP_ID_REG 0x0f +#define DMARD06_TOUT_REG 0x40 +#define DMARD06_XOUT_REG 0x41 +#define DMARD06_YOUT_REG 0x42 +#define DMARD06_ZOUT_REG 0x43 +#define DMARD06_CTRL1_REG 0x44 + +/* Device ID value */ +#define DMARD05_CHIP_ID 0x05 +#define DMARD06_CHIP_ID 0x06 +#define DMARD07_CHIP_ID 0x07 + +/* Device values */ +#define DMARD05_AXIS_SCALE_VAL 15625 +#define DMARD06_AXIS_SCALE_VAL 31250 +#define DMARD06_TEMP_CENTER_VAL 25 +#define DMARD06_SIGN_BIT 7 + +/* Device power modes */ +#define DMARD06_MODE_NORMAL 0x27 +#define DMARD06_MODE_POWERDOWN 0x00 + +/* Device channels */ +#define DMARD06_ACCEL_CHANNEL(_axis, _reg) { \ + .type = IIO_ACCEL, \ + .address = _reg, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .modified = 1, \ +} + +#define DMARD06_TEMP_CHANNEL(_reg) { \ + .type = IIO_TEMP, \ + .address = _reg, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ +} + +struct dmard06_data { + struct i2c_client *client; + u8 chip_id; +}; + +static const struct iio_chan_spec dmard06_channels[] = { + DMARD06_ACCEL_CHANNEL(X, DMARD06_XOUT_REG), + DMARD06_ACCEL_CHANNEL(Y, DMARD06_YOUT_REG), + DMARD06_ACCEL_CHANNEL(Z, DMARD06_ZOUT_REG), + DMARD06_TEMP_CHANNEL(DMARD06_TOUT_REG), +}; + +static int dmard06_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dmard06_data *dmard06 = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = i2c_smbus_read_byte_data(dmard06->client, + chan->address); + if (ret < 0) { + dev_err(&dmard06->client->dev, + "Error reading data: %d\n", ret); + return ret; + } + + *val = sign_extend32(ret, DMARD06_SIGN_BIT); + + if (dmard06->chip_id == DMARD06_CHIP_ID) + *val = *val >> 1; + + switch (chan->type) { + case IIO_ACCEL: + return IIO_VAL_INT; + case IIO_TEMP: + if (dmard06->chip_id != DMARD06_CHIP_ID) + *val = *val / 2; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + *val = DMARD06_TEMP_CENTER_VAL; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ACCEL: + *val = 0; + if (dmard06->chip_id == DMARD06_CHIP_ID) + *val2 = DMARD06_AXIS_SCALE_VAL; + else + *val2 = DMARD05_AXIS_SCALE_VAL; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct iio_info dmard06_info = { + .driver_module = THIS_MODULE, + .read_raw = dmard06_read_raw, +}; + +static int dmard06_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct iio_dev *indio_dev; + struct dmard06_data *dmard06; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C check functionality failed\n"); + return -ENXIO; + } + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dmard06)); + if (!indio_dev) { + dev_err(&client->dev, "Failed to allocate iio device\n"); + return -ENOMEM; + } + + dmard06 = iio_priv(indio_dev); + dmard06->client = client; + + ret = i2c_smbus_read_byte_data(dmard06->client, DMARD06_CHIP_ID_REG); + if (ret < 0) { + dev_err(&client->dev, "Error reading chip id: %d\n", ret); + return ret; + } + + if (ret != DMARD05_CHIP_ID && ret != DMARD06_CHIP_ID && + ret != DMARD07_CHIP_ID) { + dev_err(&client->dev, "Invalid chip id: %02d\n", ret); + return -ENODEV; + } + + dmard06->chip_id = ret; + + i2c_set_clientdata(client, indio_dev); + indio_dev->dev.parent = &client->dev; + indio_dev->name = DMARD06_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = dmard06_channels; + indio_dev->num_channels = ARRAY_SIZE(dmard06_channels); + indio_dev->info = &dmard06_info; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +#ifdef CONFIG_PM_SLEEP +static int dmard06_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct dmard06_data *dmard06 = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG, + DMARD06_MODE_POWERDOWN); + if (ret < 0) + return ret; + + return 0; +} + +static int dmard06_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct dmard06_data *dmard06 = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG, + DMARD06_MODE_NORMAL); + if (ret < 0) + return ret; + + return 0; +} + +static SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, dmard06_resume); +#define DMARD06_PM_OPS (&dmard06_pm_ops) +#else +#define DMARD06_PM_OPS NULL +#endif + +static const struct i2c_device_id dmard06_id[] = { + { "dmard05", 0 }, + { "dmard06", 0 }, + { "dmard07", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, dmard06_id); + +static const struct of_device_id dmard06_of_match[] = { + { .compatible = "domintech,dmard05" }, + { .compatible = "domintech,dmard06" }, + { .compatible = "domintech,dmard07" }, + { } +}; +MODULE_DEVICE_TABLE(of, dmard06_of_match); + +static struct i2c_driver dmard06_driver = { + .probe = dmard06_probe, + .id_table = dmard06_id, + .driver = { + .name = DMARD06_DRV_NAME, + .of_match_table = of_match_ptr(dmard06_of_match), + .pm = DMARD06_PM_OPS, + }, +}; +module_i2c_driver(dmard06_driver); + +MODULE_AUTHOR("Aleksei Mamlin "); +MODULE_DESCRIPTION("Domintech DMARD06 accelerometer driver"); +MODULE_LICENSE("GPL v2"); diff --git b/drivers/iio/accel/dmard09.c b/drivers/iio/accel/dmard09.c new file mode 100644 index 0000000..d3a28f9 --- /dev/null +++ b/drivers/iio/accel/dmard09.c @@ -0,0 +1,157 @@ +/* + * IIO driver for the 3-axis accelerometer Domintech DMARD09. + * + * Copyright (c) 2016, Jelle van der Waa + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include + +#define DMARD09_DRV_NAME "dmard09" + +#define DMARD09_REG_CHIPID 0x18 +#define DMARD09_REG_STAT 0x0A +#define DMARD09_REG_X 0x0C +#define DMARD09_REG_Y 0x0E +#define DMARD09_REG_Z 0x10 +#define DMARD09_CHIPID 0x95 + +#define DMARD09_BUF_LEN 8 +#define DMARD09_AXIS_X 0 +#define DMARD09_AXIS_Y 1 +#define DMARD09_AXIS_Z 2 +#define DMARD09_AXIS_X_OFFSET ((DMARD09_AXIS_X + 1) * 2) +#define DMARD09_AXIS_Y_OFFSET ((DMARD09_AXIS_Y + 1 )* 2) +#define DMARD09_AXIS_Z_OFFSET ((DMARD09_AXIS_Z + 1) * 2) + +struct dmard09_data { + struct i2c_client *client; +}; + +#define DMARD09_CHANNEL(_axis, offset) { \ + .type = IIO_ACCEL, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .modified = 1, \ + .address = offset, \ + .channel2 = IIO_MOD_##_axis, \ +} + +static const struct iio_chan_spec dmard09_channels[] = { + DMARD09_CHANNEL(X, DMARD09_AXIS_X_OFFSET), + DMARD09_CHANNEL(Y, DMARD09_AXIS_Y_OFFSET), + DMARD09_CHANNEL(Z, DMARD09_AXIS_Z_OFFSET), +}; + +static int dmard09_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dmard09_data *data = iio_priv(indio_dev); + u8 buf[DMARD09_BUF_LEN]; + int ret; + s16 accel; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* + * Read from the DMAR09_REG_STAT register, since the chip + * caches reads from the individual X, Y, Z registers. + */ + ret = i2c_smbus_read_i2c_block_data(data->client, + DMARD09_REG_STAT, + DMARD09_BUF_LEN, buf); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg %d\n", + DMARD09_REG_STAT); + return ret; + } + + accel = get_unaligned_le16(&buf[chan->address]); + + /* Remove lower 3 bits and sign extend */ + accel <<= 4; + accel >>= 7; + + *val = accel; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info dmard09_info = { + .driver_module = THIS_MODULE, + .read_raw = dmard09_read_raw, +}; + +static int dmard09_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct iio_dev *indio_dev; + struct dmard09_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; + + ret = i2c_smbus_read_byte_data(data->client, DMARD09_REG_CHIPID); + if (ret < 0) { + dev_err(&client->dev, "Error reading chip id %d\n", ret); + return ret; + } + + if (ret != DMARD09_CHIPID) { + dev_err(&client->dev, "Invalid chip id %d\n", ret); + return -ENODEV; + } + + i2c_set_clientdata(client, indio_dev); + indio_dev->dev.parent = &client->dev; + indio_dev->name = DMARD09_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = dmard09_channels; + indio_dev->num_channels = ARRAY_SIZE(dmard09_channels); + indio_dev->info = &dmard09_info; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id dmard09_id[] = { + { "dmard09", 0}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, dmard09_id); + +static struct i2c_driver dmard09_driver = { + .driver = { + .name = DMARD09_DRV_NAME + }, + .probe = dmard09_probe, + .id_table = dmard09_id, +}; + +module_i2c_driver(dmard09_driver); + +MODULE_AUTHOR("Jelle van der Waa "); +MODULE_DESCRIPTION("DMARD09 3-axis accelerometer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 765a723..3f968c4 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1392,6 +1392,7 @@ static const struct acpi_device_id kx_acpi_match[] = { {"KXCJ1013", KXCJK1013}, {"KXCJ1008", KXCJ91008}, {"KXCJ9000", KXCJ91008}, + {"KIOX000A", KXCJ91008}, {"KXTJ1009", KXTJ21009}, {"SMO8500", KXCJ91008}, { }, diff --git b/drivers/iio/accel/kxsd9-i2c.c b/drivers/iio/accel/kxsd9-i2c.c new file mode 100644 index 0000000..95e2085 --- /dev/null +++ b/drivers/iio/accel/kxsd9-i2c.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "kxsd9.h" + +static int kxsd9_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x0e, + }; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &config); + if (IS_ERR(regmap)) { + dev_err(&i2c->dev, "Failed to register i2c regmap %d\n", + (int)PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return kxsd9_common_probe(&i2c->dev, + regmap, + i2c->name); +} + +static int kxsd9_i2c_remove(struct i2c_client *client) +{ + return kxsd9_common_remove(&client->dev); +} + +#ifdef CONFIG_OF +static const struct of_device_id kxsd9_of_match[] = { + { .compatible = "kionix,kxsd9", }, + { }, +}; +MODULE_DEVICE_TABLE(of, kxsd9_of_match); +#else +#define kxsd9_of_match NULL +#endif + +static const struct i2c_device_id kxsd9_i2c_id[] = { + {"kxsd9", 0}, + { }, +}; +MODULE_DEVICE_TABLE(i2c, kxsd9_i2c_id); + +static struct i2c_driver kxsd9_i2c_driver = { + .driver = { + .name = "kxsd9", + .of_match_table = of_match_ptr(kxsd9_of_match), + .pm = &kxsd9_dev_pm_ops, + }, + .probe = kxsd9_i2c_probe, + .remove = kxsd9_i2c_remove, + .id_table = kxsd9_i2c_id, +}; +module_i2c_driver(kxsd9_i2c_driver); diff --git b/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c new file mode 100644 index 0000000..b7d0078 --- /dev/null +++ b/drivers/iio/accel/kxsd9-spi.c @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include + +#include "kxsd9.h" + +static int kxsd9_spi_probe(struct spi_device *spi) +{ + static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x0e, + }; + struct regmap *regmap; + + spi->mode = SPI_MODE_0; + regmap = devm_regmap_init_spi(spi, &config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "%s: regmap allocation failed: %ld\n", + __func__, PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return kxsd9_common_probe(&spi->dev, + regmap, + spi_get_device_id(spi)->name); +} + +static int kxsd9_spi_remove(struct spi_device *spi) +{ + return kxsd9_common_remove(&spi->dev); +} + +static const struct spi_device_id kxsd9_spi_id[] = { + {"kxsd9", 0}, + { }, +}; +MODULE_DEVICE_TABLE(spi, kxsd9_spi_id); + +static struct spi_driver kxsd9_spi_driver = { + .driver = { + .name = "kxsd9", + .pm = &kxsd9_dev_pm_ops, + }, + .probe = kxsd9_spi_probe, + .remove = kxsd9_spi_remove, + .id_table = kxsd9_spi_id, +}; +module_spi_driver(kxsd9_spi_driver); + +MODULE_AUTHOR("Jonathan Cameron "); +MODULE_DESCRIPTION("Kionix KXSD9 SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index 9d72d4b..9af60ac 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -12,19 +12,25 @@ * I have a suitable wire made up. * * TODO: Support the motion detector - * Uses register address incrementing so could have a - * heavily optimized ring buffer access function. */ #include #include -#include #include #include #include - +#include +#include +#include +#include +#include #include #include +#include +#include +#include + +#include "kxsd9.h" #define KXSD9_REG_X 0x00 #define KXSD9_REG_Y 0x02 @@ -33,28 +39,45 @@ #define KXSD9_REG_RESET 0x0a #define KXSD9_REG_CTRL_C 0x0c -#define KXSD9_FS_MASK 0x03 +#define KXSD9_CTRL_C_FS_MASK 0x03 +#define KXSD9_CTRL_C_FS_8G 0x00 +#define KXSD9_CTRL_C_FS_6G 0x01 +#define KXSD9_CTRL_C_FS_4G 0x02 +#define KXSD9_CTRL_C_FS_2G 0x03 +#define KXSD9_CTRL_C_MOT_LAT BIT(3) +#define KXSD9_CTRL_C_MOT_LEV BIT(4) +#define KXSD9_CTRL_C_LP_MASK 0xe0 +#define KXSD9_CTRL_C_LP_NONE 0x00 +#define KXSD9_CTRL_C_LP_2000HZC BIT(5) +#define KXSD9_CTRL_C_LP_2000HZB BIT(6) +#define KXSD9_CTRL_C_LP_2000HZA (BIT(5)|BIT(6)) +#define KXSD9_CTRL_C_LP_1000HZ BIT(7) +#define KXSD9_CTRL_C_LP_500HZ (BIT(7)|BIT(5)) +#define KXSD9_CTRL_C_LP_100HZ (BIT(7)|BIT(6)) +#define KXSD9_CTRL_C_LP_50HZ (BIT(7)|BIT(6)|BIT(5)) #define KXSD9_REG_CTRL_B 0x0d -#define KXSD9_REG_CTRL_A 0x0e -#define KXSD9_READ(a) (0x80 | (a)) -#define KXSD9_WRITE(a) (a) +#define KXSD9_CTRL_B_CLK_HLD BIT(7) +#define KXSD9_CTRL_B_ENABLE BIT(6) +#define KXSD9_CTRL_B_ST BIT(5) /* Self-test */ + +#define KXSD9_REG_CTRL_A 0x0e -#define KXSD9_STATE_RX_SIZE 2 -#define KXSD9_STATE_TX_SIZE 2 /** * struct kxsd9_state - device related storage - * @buf_lock: protect the rx and tx buffers. - * @us: spi device - * @rx: single rx buffer storage - * @tx: single tx buffer storage - **/ + * @dev: pointer to the parent device + * @map: regmap to the device + * @orientation: mounting matrix, flipped axis etc + * @regs: regulators for this device, VDD and IOVDD + * @scale: the current scaling setting + */ struct kxsd9_state { - struct mutex buf_lock; - struct spi_device *us; - u8 rx[KXSD9_STATE_RX_SIZE] ____cacheline_aligned; - u8 tx[KXSD9_STATE_TX_SIZE]; + struct device *dev; + struct regmap *map; + struct iio_mount_matrix orientation; + struct regulator_bulk_data regs[2]; + u8 scale; }; #define KXSD9_SCALE_2G "0.011978" @@ -65,6 +88,14 @@ struct kxsd9_state { /* reverse order */ static const int kxsd9_micro_scales[4] = { 47853, 35934, 23927, 11978 }; +#define KXSD9_ZERO_G_OFFSET -2048 + +/* + * Regulator names + */ +static const char kxsd9_reg_vdd[] = "vdd"; +static const char kxsd9_reg_iovdd[] = "iovdd"; + static int kxsd9_write_scale(struct iio_dev *indio_dev, int micro) { int ret, i; @@ -79,42 +110,17 @@ static int kxsd9_write_scale(struct iio_dev *indio_dev, int micro) if (!foundit) return -EINVAL; - mutex_lock(&st->buf_lock); - ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C)); + ret = regmap_update_bits(st->map, + KXSD9_REG_CTRL_C, + KXSD9_CTRL_C_FS_MASK, + i); if (ret < 0) goto error_ret; - st->tx[0] = KXSD9_WRITE(KXSD9_REG_CTRL_C); - st->tx[1] = (ret & ~KXSD9_FS_MASK) | i; - ret = spi_write(st->us, st->tx, 2); -error_ret: - mutex_unlock(&st->buf_lock); - return ret; -} + /* Cached scale when the sensor is powered down */ + st->scale = i; -static int kxsd9_read(struct iio_dev *indio_dev, u8 address) -{ - int ret; - struct kxsd9_state *st = iio_priv(indio_dev); - struct spi_transfer xfers[] = { - { - .bits_per_word = 8, - .len = 1, - .delay_usecs = 200, - .tx_buf = st->tx, - }, { - .bits_per_word = 8, - .len = 2, - .rx_buf = st->rx, - }, - }; - - mutex_lock(&st->buf_lock); - st->tx[0] = KXSD9_READ(address); - ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); - if (!ret) - ret = (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0); - mutex_unlock(&st->buf_lock); +error_ret: return ret; } @@ -136,6 +142,9 @@ static int kxsd9_write_raw(struct iio_dev *indio_dev, long mask) { int ret = -EINVAL; + struct kxsd9_state *st = iio_priv(indio_dev); + + pm_runtime_get_sync(st->dev); if (mask == IIO_CHAN_INFO_SCALE) { /* Check no integer component */ @@ -144,6 +153,9 @@ static int kxsd9_write_raw(struct iio_dev *indio_dev, ret = kxsd9_write_scale(indio_dev, val2); } + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_autosuspend(st->dev); + return ret; } @@ -153,46 +165,154 @@ static int kxsd9_read_raw(struct iio_dev *indio_dev, { int ret = -EINVAL; struct kxsd9_state *st = iio_priv(indio_dev); + unsigned int regval; + __be16 raw_val; + u16 nval; + + pm_runtime_get_sync(st->dev); switch (mask) { case IIO_CHAN_INFO_RAW: - ret = kxsd9_read(indio_dev, chan->address); - if (ret < 0) + ret = regmap_bulk_read(st->map, chan->address, &raw_val, + sizeof(raw_val)); + if (ret) goto error_ret; - *val = ret; + nval = be16_to_cpu(raw_val); + /* Only 12 bits are valid */ + nval >>= 4; + *val = nval; + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_OFFSET: + /* This has a bias of -2048 */ + *val = KXSD9_ZERO_G_OFFSET; ret = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: - ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C)); + ret = regmap_read(st->map, + KXSD9_REG_CTRL_C, + ®val); if (ret < 0) goto error_ret; *val = 0; - *val2 = kxsd9_micro_scales[ret & KXSD9_FS_MASK]; + *val2 = kxsd9_micro_scales[regval & KXSD9_CTRL_C_FS_MASK]; ret = IIO_VAL_INT_PLUS_MICRO; break; } error_ret: + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_autosuspend(st->dev); + return ret; }; -#define KXSD9_ACCEL_CHAN(axis) \ + +static irqreturn_t kxsd9_trigger_handler(int irq, void *p) +{ + const struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct kxsd9_state *st = iio_priv(indio_dev); + int ret; + /* 4 * 16bit values AND timestamp */ + __be16 hw_values[8]; + + ret = regmap_bulk_read(st->map, + KXSD9_REG_X, + &hw_values, + 8); + if (ret) { + dev_err(st->dev, + "error reading data\n"); + return ret; + } + + iio_push_to_buffers_with_timestamp(indio_dev, + hw_values, + iio_get_time_ns(indio_dev)); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int kxsd9_buffer_preenable(struct iio_dev *indio_dev) +{ + struct kxsd9_state *st = iio_priv(indio_dev); + + pm_runtime_get_sync(st->dev); + + return 0; +} + +static int kxsd9_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct kxsd9_state *st = iio_priv(indio_dev); + + pm_runtime_mark_last_busy(st->dev); + pm_runtime_put_autosuspend(st->dev); + + return 0; +} + +static const struct iio_buffer_setup_ops kxsd9_buffer_setup_ops = { + .preenable = kxsd9_buffer_preenable, + .postenable = iio_triggered_buffer_postenable, + .predisable = iio_triggered_buffer_predisable, + .postdisable = kxsd9_buffer_postdisable, +}; + +static const struct iio_mount_matrix * +kxsd9_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct kxsd9_state *st = iio_priv(indio_dev); + + return &st->orientation; +} + +static const struct iio_chan_spec_ext_info kxsd9_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, kxsd9_get_mount_matrix), + { }, +}; + +#define KXSD9_ACCEL_CHAN(axis, index) \ { \ .type = IIO_ACCEL, \ .modified = 1, \ .channel2 = IIO_MOD_##axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .ext_info = kxsd9_ext_info, \ .address = KXSD9_REG_##axis, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_BE, \ + }, \ } static const struct iio_chan_spec kxsd9_channels[] = { - KXSD9_ACCEL_CHAN(X), KXSD9_ACCEL_CHAN(Y), KXSD9_ACCEL_CHAN(Z), + KXSD9_ACCEL_CHAN(X, 0), + KXSD9_ACCEL_CHAN(Y, 1), + KXSD9_ACCEL_CHAN(Z, 2), { .type = IIO_VOLTAGE, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .indexed = 1, .address = KXSD9_REG_AUX, - } + .scan_index = 3, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + .shift = 4, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(4), }; static const struct attribute_group kxsd9_attribute_group = { @@ -203,17 +323,69 @@ static int kxsd9_power_up(struct kxsd9_state *st) { int ret; - st->tx[0] = 0x0d; - st->tx[1] = 0x40; - ret = spi_write(st->us, st->tx, 2); + /* Enable the regulators */ + ret = regulator_bulk_enable(ARRAY_SIZE(st->regs), st->regs); + if (ret) { + dev_err(st->dev, "Cannot enable regulators\n"); + return ret; + } + + /* Power up */ + ret = regmap_write(st->map, + KXSD9_REG_CTRL_B, + KXSD9_CTRL_B_ENABLE); if (ret) return ret; - st->tx[0] = 0x0c; - st->tx[1] = 0x9b; - return spi_write(st->us, st->tx, 2); + /* + * Set 1000Hz LPF, 2g fullscale, motion wakeup threshold 1g, + * latched wakeup + */ + ret = regmap_write(st->map, + KXSD9_REG_CTRL_C, + KXSD9_CTRL_C_LP_1000HZ | + KXSD9_CTRL_C_MOT_LEV | + KXSD9_CTRL_C_MOT_LAT | + st->scale); + if (ret) + return ret; + + /* + * Power-up time depends on the LPF setting, but typ 15.9 ms, let's + * set 20 ms to allow for some slack. + */ + msleep(20); + + return 0; }; +static int kxsd9_power_down(struct kxsd9_state *st) +{ + int ret; + + /* + * Set into low power mode - since there may be more users of the + * regulators this is the first step of the power saving: it will + * make sure we conserve power even if there are others users on the + * regulators. + */ + ret = regmap_update_bits(st->map, + KXSD9_REG_CTRL_B, + KXSD9_CTRL_B_ENABLE, + 0); + if (ret) + return ret; + + /* Disable the regulators */ + ret = regulator_bulk_disable(ARRAY_SIZE(st->regs), st->regs); + if (ret) { + dev_err(st->dev, "Cannot disable regulators\n"); + return ret; + } + + return 0; +} + static const struct iio_info kxsd9_info = { .read_raw = &kxsd9_read_raw, .write_raw = &kxsd9_write_raw, @@ -221,57 +393,136 @@ static const struct iio_info kxsd9_info = { .driver_module = THIS_MODULE, }; -static int kxsd9_probe(struct spi_device *spi) +/* Four channels apart from timestamp, scan mask = 0x0f */ +static const unsigned long kxsd9_scan_masks[] = { 0xf, 0 }; + +int kxsd9_common_probe(struct device *dev, + struct regmap *map, + const char *name) { struct iio_dev *indio_dev; struct kxsd9_state *st; + int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); + st->dev = dev; + st->map = map; - st->us = spi; - mutex_init(&st->buf_lock); indio_dev->channels = kxsd9_channels; indio_dev->num_channels = ARRAY_SIZE(kxsd9_channels); - indio_dev->name = spi_get_device_id(spi)->name; - indio_dev->dev.parent = &spi->dev; + indio_dev->name = name; + indio_dev->dev.parent = dev; indio_dev->info = &kxsd9_info; indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->available_scan_masks = kxsd9_scan_masks; + + /* Read the mounting matrix, if present */ + ret = of_iio_read_mount_matrix(dev, + "mount-matrix", + &st->orientation); + if (ret) + return ret; + + /* Fetch and turn on regulators */ + st->regs[0].supply = kxsd9_reg_vdd; + st->regs[1].supply = kxsd9_reg_iovdd; + ret = devm_regulator_bulk_get(dev, + ARRAY_SIZE(st->regs), + st->regs); + if (ret) { + dev_err(dev, "Cannot get regulators\n"); + return ret; + } + /* Default scaling */ + st->scale = KXSD9_CTRL_C_FS_2G; - spi->mode = SPI_MODE_0; - spi_setup(spi); kxsd9_power_up(st); - return iio_device_register(indio_dev); + ret = iio_triggered_buffer_setup(indio_dev, + iio_pollfunc_store_time, + kxsd9_trigger_handler, + &kxsd9_buffer_setup_ops); + if (ret) { + dev_err(dev, "triggered buffer setup failed\n"); + goto err_power_down; + } + + ret = iio_device_register(indio_dev); + if (ret) + goto err_cleanup_buffer; + + dev_set_drvdata(dev, indio_dev); + + /* 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. 20ms start-up time means 2000ms autosuspend, + * i.e. 2 seconds. + */ + pm_runtime_set_autosuspend_delay(dev, 2000); + pm_runtime_use_autosuspend(dev); + pm_runtime_put(dev); + + return 0; + +err_cleanup_buffer: + iio_triggered_buffer_cleanup(indio_dev); +err_power_down: + kxsd9_power_down(st); + + return ret; } +EXPORT_SYMBOL(kxsd9_common_probe); -static int kxsd9_remove(struct spi_device *spi) +int kxsd9_common_remove(struct device *dev) { - iio_device_unregister(spi_get_drvdata(spi)); + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct kxsd9_state *st = iio_priv(indio_dev); + + iio_triggered_buffer_cleanup(indio_dev); + iio_device_unregister(indio_dev); + pm_runtime_get_sync(dev); + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + kxsd9_power_down(st); return 0; } +EXPORT_SYMBOL(kxsd9_common_remove); -static const struct spi_device_id kxsd9_id[] = { - {"kxsd9", 0}, - { }, -}; -MODULE_DEVICE_TABLE(spi, kxsd9_id); +#ifdef CONFIG_PM +static int kxsd9_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct kxsd9_state *st = iio_priv(indio_dev); -static struct spi_driver kxsd9_driver = { - .driver = { - .name = "kxsd9", - }, - .probe = kxsd9_probe, - .remove = kxsd9_remove, - .id_table = kxsd9_id, + return kxsd9_power_down(st); +} + +static int kxsd9_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct kxsd9_state *st = iio_priv(indio_dev); + + return kxsd9_power_up(st); +} +#endif /* CONFIG_PM */ + +const struct dev_pm_ops kxsd9_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(kxsd9_runtime_suspend, + kxsd9_runtime_resume, NULL) }; -module_spi_driver(kxsd9_driver); +EXPORT_SYMBOL(kxsd9_dev_pm_ops); MODULE_AUTHOR("Jonathan Cameron "); -MODULE_DESCRIPTION("Kionix KXSD9 SPI driver"); +MODULE_DESCRIPTION("Kionix KXSD9 driver"); MODULE_LICENSE("GPL v2"); diff --git b/drivers/iio/accel/kxsd9.h b/drivers/iio/accel/kxsd9.h new file mode 100644 index 0000000..7e8a281 --- /dev/null +++ b/drivers/iio/accel/kxsd9.h @@ -0,0 +1,12 @@ +#include +#include + +#define KXSD9_STATE_RX_SIZE 2 +#define KXSD9_STATE_TX_SIZE 2 + +int kxsd9_common_probe(struct device *dev, + struct regmap *map, + const char *name); +int kxsd9_common_remove(struct device *dev); + +extern const struct dev_pm_ops kxsd9_dev_pm_ops; diff --git b/drivers/iio/accel/mc3230.c b/drivers/iio/accel/mc3230.c new file mode 100644 index 0000000..4ea2ff6 --- /dev/null +++ b/drivers/iio/accel/mc3230.c @@ -0,0 +1,211 @@ +/** + * mCube MC3230 3-Axis Accelerometer + * + * Copyright (c) 2016 Hans de Goede + * + * 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. + * + * IIO driver for mCube MC3230; 7-bit I2C address: 0x4c. + */ + +#include +#include +#include +#include + +#define MC3230_REG_XOUT 0x00 +#define MC3230_REG_YOUT 0x01 +#define MC3230_REG_ZOUT 0x02 + +#define MC3230_REG_MODE 0x07 +#define MC3230_MODE_OPCON_MASK 0x03 +#define MC3230_MODE_OPCON_WAKE 0x01 +#define MC3230_MODE_OPCON_STANDBY 0x03 + +#define MC3230_REG_CHIP_ID 0x18 +#define MC3230_CHIP_ID 0x01 + +#define MC3230_REG_PRODUCT_CODE 0x3b +#define MC3230_PRODUCT_CODE 0x19 + +/* + * The accelerometer has one measurement range: + * + * -1.5g - +1.5g (8-bit, signed) + * + * scale = (1.5 + 1.5) * 9.81 / (2^8 - 1) = 0.115411765 + */ + +static const int mc3230_nscale = 115411765; + +#define MC3230_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 mc3230_channels[] = { + MC3230_CHANNEL(MC3230_REG_XOUT, X), + MC3230_CHANNEL(MC3230_REG_YOUT, Y), + MC3230_CHANNEL(MC3230_REG_ZOUT, Z), +}; + +struct mc3230_data { + struct i2c_client *client; +}; + +static int mc3230_set_opcon(struct mc3230_data *data, int opcon) +{ + int ret; + struct i2c_client *client = data->client; + + ret = i2c_smbus_read_byte_data(client, MC3230_REG_MODE); + if (ret < 0) { + dev_err(&client->dev, "failed to read mode reg: %d\n", ret); + return ret; + } + + ret &= ~MC3230_MODE_OPCON_MASK; + ret |= opcon; + + ret = i2c_smbus_write_byte_data(client, MC3230_REG_MODE, ret); + if (ret < 0) { + dev_err(&client->dev, "failed to write mode reg: %d\n", ret); + return ret; + } + + return 0; +} + +static int mc3230_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mc3230_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = i2c_smbus_read_byte_data(data->client, chan->address); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 7); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = mc3230_nscale; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static const struct iio_info mc3230_info = { + .driver_module = THIS_MODULE, + .read_raw = mc3230_read_raw, +}; + +static int mc3230_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct iio_dev *indio_dev; + struct mc3230_data *data; + + /* First check chip-id and product-id */ + ret = i2c_smbus_read_byte_data(client, MC3230_REG_CHIP_ID); + if (ret != MC3230_CHIP_ID) + return (ret < 0) ? ret : -ENODEV; + + ret = i2c_smbus_read_byte_data(client, MC3230_REG_PRODUCT_CODE); + if (ret != MC3230_PRODUCT_CODE) + return (ret < 0) ? ret : -ENODEV; + + 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); + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &mc3230_info; + indio_dev->name = "mc3230"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mc3230_channels; + indio_dev->num_channels = ARRAY_SIZE(mc3230_channels); + + ret = mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE); + if (ret < 0) + return ret; + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "device_register failed\n"); + mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY); + } + + return ret; +} + +static int mc3230_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + + return mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY); +} + +#ifdef CONFIG_PM_SLEEP +static int mc3230_suspend(struct device *dev) +{ + struct mc3230_data *data; + + data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + return mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY); +} + +static int mc3230_resume(struct device *dev) +{ + struct mc3230_data *data; + + data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + return mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE); +} +#endif + +static SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume); + +static const struct i2c_device_id mc3230_i2c_id[] = { + {"mc3230", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mc3230_i2c_id); + +static struct i2c_driver mc3230_driver = { + .driver = { + .name = "mc3230", + .pm = &mc3230_pm_ops, + }, + .probe = mc3230_probe, + .remove = mc3230_remove, + .id_table = mc3230_i2c_id, +}; + +module_i2c_driver(mc3230_driver); + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("mCube MC3230 3-Axis Accelerometer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c index 0acdee5..03beadf 100644 --- a/drivers/iio/accel/mma7660.c +++ b/drivers/iio/accel/mma7660.c @@ -251,6 +251,7 @@ static const struct i2c_device_id mma7660_i2c_id[] = { {"mma7660", 0}, {} }; +MODULE_DEVICE_TABLE(i2c, mma7660_i2c_id); static const struct acpi_device_id mma7660_acpi_id[] = { {"MMA7660", 0}, diff --git a/drivers/iio/accel/mxc6255.c b/drivers/iio/accel/mxc6255.c index 97ccde7..0abad69 100644 --- a/drivers/iio/accel/mxc6255.c +++ b/drivers/iio/accel/mxc6255.c @@ -154,7 +154,7 @@ static int mxc6255_probe(struct i2c_client *client, return ret; } - if (chip_id != MXC6255_CHIP_ID) { + if ((chip_id & 0x1f) != MXC6255_CHIP_ID) { dev_err(&client->dev, "Invalid chip id %x\n", chip_id); return -ENODEV; } @@ -171,12 +171,14 @@ static int mxc6255_probe(struct i2c_client *client, } static const struct acpi_device_id mxc6255_acpi_match[] = { + {"MXC6225", 0}, {"MXC6255", 0}, { } }; MODULE_DEVICE_TABLE(acpi, mxc6255_acpi_match); static const struct i2c_device_id mxc6255_id[] = { + {"mxc6225", 0}, {"mxc6255", 0}, { } }; diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c index 4ae05fc..31db009 100644 --- a/drivers/iio/accel/ssp_accel_sensor.c +++ b/drivers/iio/accel/ssp_accel_sensor.c @@ -74,7 +74,7 @@ static int ssp_accel_write_raw(struct iio_dev *indio_dev, return -EINVAL; } -static struct iio_info ssp_accel_iio_info = { +static const struct iio_info ssp_accel_iio_info = { .read_raw = &ssp_accel_read_raw, .write_raw = &ssp_accel_write_raw, }; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 7675772..99c0514 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -264,6 +264,15 @@ config LPC18XX_ADC To compile this driver as a module, choose M here: the module will be called lpc18xx_adc. +config LTC2485 + tristate "Linear Technology LTC2485 ADC driver" + depends on I2C + help + Say yes here to build support for Linear Technology LTC2485 ADC. + + To compile this driver as a module, choose M here: the module will be + called ltc2485. + config MAX1027 tristate "Maxim max1027 ADC driver" depends on SPI @@ -317,6 +326,19 @@ config MCP3422 This driver can also be built as a module. If so, the module will be called mcp3422. +config MEDIATEK_MT6577_AUXADC + tristate "MediaTek AUXADC driver" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to enable support for MediaTek mt65xx AUXADC. + + The driver supports immediate mode operation to read from one of sixteen + channels (external or internal). + + This driver can also be built as a module. If so, the module will be + called mt6577_auxadc. + config MEN_Z188_ADC tristate "MEN 16z188 ADC IP Core support" depends on MCB @@ -397,9 +419,26 @@ config ROCKCHIP_SARADC To compile this driver as a module, choose M here: the module will be called rockchip_saradc. +config STX104 + tristate "Apex Embedded Systems STX104 driver" + depends on X86 && ISA_BUS_API + select GPIOLIB + help + Say yes here to build support for the Apex Embedded Systems STX104 + integrated analog PC/104 card. + + This driver supports the 16 channels of single-ended (8 channels of + differential) analog inputs, 2 channels of analog output, 4 digital + inputs, and 4 digital outputs provided by the STX104. + + The base port addresses for the devices may be configured via the base + array module parameter. + config TI_ADC081C tristate "Texas Instruments ADC081C/ADC101C/ADC121C family" depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help If you say yes here you get support for Texas Instruments ADC081C, ADC101C and ADC121C ADC chips. @@ -417,6 +456,18 @@ config TI_ADC0832 This driver can also be built as a module. If so, the module will be called ti-adc0832. +config TI_ADC12138 + tristate "Texas Instruments ADC12130/ADC12132/ADC12138" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADC12130, + ADC12132 and ADC12138 chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc12138. + config TI_ADC128S052 tristate "Texas Instruments ADC128S052/ADC122S021/ADC124S021" depends on SPI @@ -427,6 +478,18 @@ config TI_ADC128S052 This driver can also be built as a module. If so, the module will be called ti-adc128s052. +config TI_ADC161S626 + tristate "Texas Instruments ADC161S626 1-channel differential ADC" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADC141S626, + and ADC161S626 chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc161s626. + config TI_ADS1015 tristate "Texas Instruments ADS1015 ADC" depends on I2C && !SENSORS_ADS1015 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 0ba0d50..7a40c04 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -27,10 +27,12 @@ obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o +obj-$(CONFIG_LTC2485) += ltc2485.o obj-$(CONFIG_MAX1027) += max1027.o obj-$(CONFIG_MAX1363) += max1363.o obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o +obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o obj-$(CONFIG_NAU7802) += nau7802.o @@ -38,9 +40,12 @@ obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o +obj-$(CONFIG_STX104) += stx104.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o +obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o +obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index c0f6a98..b8d5cfd 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -481,7 +481,7 @@ error_free_gpios: if (!st->fixed_addr) gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios)); error_disable_reg: - if (!IS_ERR_OR_NULL(st->reg)) + if (!IS_ERR(st->reg)) regulator_disable(st->reg); return ret; @@ -496,7 +496,7 @@ static int ad7266_remove(struct spi_device *spi) iio_triggered_buffer_cleanup(indio_dev); if (!st->fixed_addr) gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios)); - if (!IS_ERR_OR_NULL(st->reg)) + if (!IS_ERR(st->reg)) regulator_disable(st->reg); return 0; diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index 10ec8fc..e399bf0 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -239,16 +239,16 @@ static int ad7298_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { - ret = -EBUSY; - } else { - if (chan->address == AD7298_CH_TEMP) - ret = ad7298_scan_temp(st, val); - else - ret = ad7298_scan_direct(st, chan->address); - } - mutex_unlock(&indio_dev->mlock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + if (chan->address == AD7298_CH_TEMP) + ret = ad7298_scan_temp(st, val); + else + ret = ad7298_scan_direct(st, chan->address); + + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 847789b..e6706a0 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -519,11 +519,9 @@ static int ad7793_write_raw(struct iio_dev *indio_dev, int ret, i; unsigned int tmp; - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { - mutex_unlock(&indio_dev->mlock); - return -EBUSY; - } + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; switch (mask) { case IIO_CHAN_INFO_SCALE: @@ -548,7 +546,7 @@ static int ad7793_write_raw(struct iio_dev *indio_dev, ret = -EINVAL; } - mutex_unlock(&indio_dev->mlock); + iio_device_release_direct_mode(indio_dev); return ret; } diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 0438c68..bbdac07 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -113,6 +113,7 @@ #define AT91_ADC_TSMR_TSAV (3 << 4) /* Averages samples */ #define AT91_ADC_TSMR_TSAV_(x) ((x) << 4) #define AT91_ADC_TSMR_SCTIM (0x0f << 16) /* Switch closure time */ +#define AT91_ADC_TSMR_SCTIM_(x) ((x) << 16) #define AT91_ADC_TSMR_PENDBC (0x0f << 28) /* Pen Debounce time */ #define AT91_ADC_TSMR_PENDBC_(x) ((x) << 28) #define AT91_ADC_TSMR_NOTSDMA (1 << 22) /* No Touchscreen DMA */ @@ -150,6 +151,7 @@ #define MAX_RLPOS_BITS 10 #define TOUCH_SAMPLE_PERIOD_US_RL 10000 /* 10ms, the SoC can't keep up with 2ms */ #define TOUCH_SHTIM 0xa +#define TOUCH_SCTIM_US 10 /* 10us for the Touchscreen Switches Closure Time */ /** * struct at91_adc_reg_desc - Various informations relative to registers @@ -1001,7 +1003,9 @@ static void atmel_ts_close(struct input_dev *dev) static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz) { + struct iio_dev *idev = iio_priv_to_dev(st); u32 reg = 0; + u32 tssctim = 0; int i = 0; /* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid @@ -1034,11 +1038,20 @@ static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz) return 0; } + /* Touchscreen Switches Closure time needed for allowing the value to + * stabilize. + * Switch Closure Time = (TSSCTIM * 4) ADCClock periods + */ + tssctim = DIV_ROUND_UP(TOUCH_SCTIM_US * adc_clk_khz / 1000, 4); + dev_dbg(&idev->dev, "adc_clk at: %d KHz, tssctim at: %d\n", + adc_clk_khz, tssctim); + if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE) reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS; else reg = AT91_ADC_TSMR_TSMODE_5WIRE; + reg |= AT91_ADC_TSMR_SCTIM_(tssctim) & AT91_ADC_TSMR_SCTIM; reg |= AT91_ADC_TSMR_TSAV_(st->caps->ts_filter_average) & AT91_ADC_TSMR_TSAV; reg |= AT91_ADC_TSMR_PENDBC_(st->ts_pendbc) & AT91_ADC_TSMR_PENDBC; diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 955f3fd..59b7d76 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -114,7 +114,6 @@ struct ina2xx_chip_info { struct mutex state_lock; unsigned int shunt_resistor; int avg; - s64 prev_ns; /* track buffer capture time, check for underruns */ int int_time_vbus; /* Bus voltage integration time uS */ int int_time_vshunt; /* Shunt voltage integration time uS */ bool allow_async_readout; @@ -509,8 +508,6 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) iio_push_to_buffers_with_timestamp(indio_dev, (unsigned int *)data, time_a); - chip->prev_ns = time_a; - return (unsigned long)(time_b - time_a) / 1000; }; @@ -554,8 +551,6 @@ 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(indio_dev); - chip->task = kthread_run(ina2xx_capture_thread, (void *)indio_dev, "%s:%d-%uus", indio_dev->name, indio_dev->id, sampling_us); diff --git b/drivers/iio/adc/ltc2485.c b/drivers/iio/adc/ltc2485.c new file mode 100644 index 0000000..eab91f1 --- /dev/null +++ b/drivers/iio/adc/ltc2485.c @@ -0,0 +1,148 @@ +/* + * ltc2485.c - Driver for Linear Technology LTC2485 ADC + * + * Copyright (C) 2016 Alison Schofield + * + * 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: http://cds.linear.com/docs/en/datasheet/2485fd.pdf + */ + +#include +#include +#include + +#include +#include + +/* Power-on configuration: rejects both 50/60Hz, operates at 1x speed */ +#define LTC2485_CONFIG_DEFAULT 0 + +struct ltc2485_data { + struct i2c_client *client; + ktime_t time_prev; /* last conversion */ +}; + +static void ltc2485_wait_conv(struct ltc2485_data *data) +{ + const unsigned int conv_time = 147; /* conversion time ms */ + unsigned int time_elapsed; + + /* delay if conversion time not passed since last read or write */ + time_elapsed = ktime_ms_delta(ktime_get(), data->time_prev); + + if (time_elapsed < conv_time) + msleep(conv_time - time_elapsed); +} + +static int ltc2485_read(struct ltc2485_data *data, int *val) +{ + struct i2c_client *client = data->client; + __be32 buf = 0; + int ret; + + ltc2485_wait_conv(data); + + ret = i2c_master_recv(client, (char *)&buf, 4); + if (ret < 0) { + dev_err(&client->dev, "i2c_master_recv failed\n"); + return ret; + } + data->time_prev = ktime_get(); + *val = sign_extend32(be32_to_cpu(buf) >> 6, 24); + + return ret; +} + +static int ltc2485_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ltc2485_data *data = iio_priv(indio_dev); + int ret; + + if (mask == IIO_CHAN_INFO_RAW) { + ret = ltc2485_read(data, val); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + } else if (mask == IIO_CHAN_INFO_SCALE) { + *val = 5000; /* on board vref millivolts */ + *val2 = 25; /* 25 (24 + sign) data bits */ + return IIO_VAL_FRACTIONAL_LOG2; + + } else { + return -EINVAL; + } +} + +static const struct iio_chan_spec ltc2485_channel[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) + }, +}; + +static const struct iio_info ltc2485_info = { + .read_raw = ltc2485_read_raw, + .driver_module = THIS_MODULE, +}; + +static int ltc2485_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct ltc2485_data *data; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_WRITE_BYTE)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + indio_dev->dev.parent = &client->dev; + indio_dev->name = id->name; + indio_dev->info = <c2485_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ltc2485_channel; + indio_dev->num_channels = ARRAY_SIZE(ltc2485_channel); + + ret = i2c_smbus_write_byte(data->client, LTC2485_CONFIG_DEFAULT); + if (ret < 0) + return ret; + + data->time_prev = ktime_get(); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id ltc2485_id[] = { + { "ltc2485", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc2485_id); + +static struct i2c_driver ltc2485_driver = { + .driver = { + .name = "ltc2485", + }, + .probe = ltc2485_probe, + .id_table = ltc2485_id, +}; +module_i2c_driver(ltc2485_driver); + +MODULE_AUTHOR("Alison Schofield "); +MODULE_DESCRIPTION("Linear Technology LTC2485 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c index d095efe..8f3606d 100644 --- a/drivers/iio/adc/men_z188_adc.c +++ b/drivers/iio/adc/men_z188_adc.c @@ -78,7 +78,7 @@ static int z188_iio_read_raw(struct iio_dev *iio_dev, return ret; } -static struct iio_info z188_adc_info = { +static const struct iio_info z188_adc_info = { .read_raw = &z188_iio_read_raw, .driver_module = THIS_MODULE, }; diff --git b/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c new file mode 100644 index 0000000..2d104c8 --- /dev/null +++ b/drivers/iio/adc/mt6577_auxadc.c @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Zhiyong Tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define MT6577_AUXADC_CON0 0x00 +#define MT6577_AUXADC_CON1 0x04 +#define MT6577_AUXADC_CON2 0x10 +#define MT6577_AUXADC_STA BIT(0) + +#define MT6577_AUXADC_DAT0 0x14 +#define MT6577_AUXADC_RDY0 BIT(12) + +#define MT6577_AUXADC_MISC 0x94 +#define MT6577_AUXADC_PDN_EN BIT(14) + +#define MT6577_AUXADC_DAT_MASK 0xfff +#define MT6577_AUXADC_SLEEP_US 1000 +#define MT6577_AUXADC_TIMEOUT_US 10000 +#define MT6577_AUXADC_POWER_READY_MS 1 +#define MT6577_AUXADC_SAMPLE_READY_US 25 + +struct mt6577_auxadc_device { + void __iomem *reg_base; + struct clk *adc_clk; + struct mutex lock; +}; + +#define MT6577_AUXADC_CHANNEL(idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ +} + +static const struct iio_chan_spec mt6577_auxadc_iio_channels[] = { + MT6577_AUXADC_CHANNEL(0), + MT6577_AUXADC_CHANNEL(1), + MT6577_AUXADC_CHANNEL(2), + MT6577_AUXADC_CHANNEL(3), + MT6577_AUXADC_CHANNEL(4), + MT6577_AUXADC_CHANNEL(5), + MT6577_AUXADC_CHANNEL(6), + MT6577_AUXADC_CHANNEL(7), + MT6577_AUXADC_CHANNEL(8), + MT6577_AUXADC_CHANNEL(9), + MT6577_AUXADC_CHANNEL(10), + MT6577_AUXADC_CHANNEL(11), + MT6577_AUXADC_CHANNEL(12), + MT6577_AUXADC_CHANNEL(13), + MT6577_AUXADC_CHANNEL(14), + MT6577_AUXADC_CHANNEL(15), +}; + +static inline void mt6577_auxadc_mod_reg(void __iomem *reg, + u32 or_mask, u32 and_mask) +{ + u32 val; + + val = readl(reg); + val |= or_mask; + val &= ~and_mask; + writel(val, reg); +} + +static int mt6577_auxadc_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + u32 val; + void __iomem *reg_channel; + int ret; + struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); + + reg_channel = adc_dev->reg_base + MT6577_AUXADC_DAT0 + + chan->channel * 0x04; + + mutex_lock(&adc_dev->lock); + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1, + 0, 1 << chan->channel); + + /* read channel and make sure old ready bit == 0 */ + ret = readl_poll_timeout(reg_channel, val, + ((val & MT6577_AUXADC_RDY0) == 0), + MT6577_AUXADC_SLEEP_US, + MT6577_AUXADC_TIMEOUT_US); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "wait for channel[%d] ready bit clear time out\n", + chan->channel); + goto err_timeout; + } + + /* set bit to trigger sample */ + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1, + 1 << chan->channel, 0); + + /* we must delay here for hardware sample channel data */ + udelay(MT6577_AUXADC_SAMPLE_READY_US); + + /* check MTK_AUXADC_CON2 if auxadc is idle */ + ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2, val, + ((val & MT6577_AUXADC_STA) == 0), + MT6577_AUXADC_SLEEP_US, + MT6577_AUXADC_TIMEOUT_US); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "wait for auxadc idle time out\n"); + goto err_timeout; + } + + /* read channel and make sure ready bit == 1 */ + ret = readl_poll_timeout(reg_channel, val, + ((val & MT6577_AUXADC_RDY0) != 0), + MT6577_AUXADC_SLEEP_US, + MT6577_AUXADC_TIMEOUT_US); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "wait for channel[%d] data ready time out\n", + chan->channel); + goto err_timeout; + } + + /* read data */ + val = readl(reg_channel) & MT6577_AUXADC_DAT_MASK; + + mutex_unlock(&adc_dev->lock); + + return val; + +err_timeout: + + mutex_unlock(&adc_dev->lock); + + return -ETIMEDOUT; +} + +static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_PROCESSED: + *val = mt6577_auxadc_read(indio_dev, chan); + if (*val < 0) { + dev_err(indio_dev->dev.parent, + "failed to sample data on channel[%d]\n", + chan->channel); + return *val; + } + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static const struct iio_info mt6577_auxadc_info = { + .driver_module = THIS_MODULE, + .read_raw = &mt6577_auxadc_read_raw, +}; + +static int mt6577_auxadc_probe(struct platform_device *pdev) +{ + struct mt6577_auxadc_device *adc_dev; + unsigned long adc_clk_rate; + struct resource *res; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev)); + if (!indio_dev) + return -ENOMEM; + + adc_dev = iio_priv(indio_dev); + indio_dev->dev.parent = &pdev->dev; + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &mt6577_auxadc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mt6577_auxadc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(adc_dev->reg_base)) { + dev_err(&pdev->dev, "failed to get auxadc base address\n"); + return PTR_ERR(adc_dev->reg_base); + } + + adc_dev->adc_clk = devm_clk_get(&pdev->dev, "main"); + if (IS_ERR(adc_dev->adc_clk)) { + dev_err(&pdev->dev, "failed to get auxadc clock\n"); + return PTR_ERR(adc_dev->adc_clk); + } + + ret = clk_prepare_enable(adc_dev->adc_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable auxadc clock\n"); + return ret; + } + + adc_clk_rate = clk_get_rate(adc_dev->adc_clk); + if (!adc_clk_rate) { + ret = -EINVAL; + dev_err(&pdev->dev, "null clock rate\n"); + goto err_disable_clk; + } + + mutex_init(&adc_dev->lock); + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + MT6577_AUXADC_PDN_EN, 0); + mdelay(MT6577_AUXADC_POWER_READY_MS); + + platform_set_drvdata(pdev, indio_dev); + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register iio device\n"); + goto err_power_off; + } + + return 0; + +err_power_off: + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + 0, MT6577_AUXADC_PDN_EN); +err_disable_clk: + clk_disable_unprepare(adc_dev->adc_clk); + return ret; +} + +static int mt6577_auxadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + 0, MT6577_AUXADC_PDN_EN); + + clk_disable_unprepare(adc_dev->adc_clk); + + return 0; +} + +static const struct of_device_id mt6577_auxadc_of_match[] = { + { .compatible = "mediatek,mt2701-auxadc", }, + { .compatible = "mediatek,mt8173-auxadc", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match); + +static struct platform_driver mt6577_auxadc_driver = { + .driver = { + .name = "mt6577-auxadc", + .of_match_table = mt6577_auxadc_of_match, + }, + .probe = mt6577_auxadc_probe, + .remove = mt6577_auxadc_remove, +}; +module_platform_driver(mt6577_auxadc_driver); + +MODULE_AUTHOR("Zhiyong Tao "); +MODULE_DESCRIPTION("MTK AUXADC Device Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c index db9b829..08f4466 100644 --- a/drivers/iio/adc/nau7802.c +++ b/drivers/iio/adc/nau7802.c @@ -197,7 +197,7 @@ static irqreturn_t nau7802_eoc_trigger(int irq, void *private) if (st->conversion_count < NAU7802_MIN_CONVERSIONS) st->conversion_count++; if (st->conversion_count >= NAU7802_MIN_CONVERSIONS) - complete_all(&st->value_ok); + complete(&st->value_ok); return IRQ_HANDLED; } diff --git b/drivers/iio/adc/stx104.c b/drivers/iio/adc/stx104.c new file mode 100644 index 0000000..7e36457 --- /dev/null +++ b/drivers/iio/adc/stx104.c @@ -0,0 +1,380 @@ +/* + * IIO driver for the Apex Embedded Systems STX104 + * Copyright (C) 2016 William Breathitt Gray + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STX104_OUT_CHAN(chan) { \ + .type = IIO_VOLTAGE, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, \ + .output = 1 \ +} +#define STX104_IN_CHAN(chan, diff) { \ + .type = IIO_VOLTAGE, \ + .channel = chan, \ + .channel2 = chan, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \ + BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, \ + .differential = diff \ +} + +#define STX104_NUM_OUT_CHAN 2 + +#define STX104_EXTENT 16 + +static unsigned int base[max_num_isa_dev(STX104_EXTENT)]; +static unsigned int num_stx104; +module_param_array(base, uint, &num_stx104, 0); +MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses"); + +/** + * struct stx104_iio - IIO device private data structure + * @chan_out_states: channels' output states + * @base: base port address of the IIO device + */ +struct stx104_iio { + unsigned int chan_out_states[STX104_NUM_OUT_CHAN]; + unsigned int 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) +{ + struct stx104_iio *const priv = iio_priv(indio_dev); + unsigned int adc_config; + int adbu; + int gain; + + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + /* get gain configuration */ + adc_config = inb(priv->base + 11); + gain = adc_config & 0x3; + + *val = 1 << gain; + return IIO_VAL_INT; + case IIO_CHAN_INFO_RAW: + if (chan->output) { + *val = priv->chan_out_states[chan->channel]; + return IIO_VAL_INT; + } + + /* select ADC channel */ + outb(chan->channel | (chan->channel << 4), priv->base + 2); + + /* trigger ADC sample capture and wait for completion */ + outb(0, priv->base); + while (inb(priv->base + 8) & BIT(7)); + + *val = inw(priv->base); + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + /* get ADC bipolar/unipolar configuration */ + adc_config = inb(priv->base + 11); + adbu = !(adc_config & BIT(2)); + + *val = -32768 * adbu; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* get ADC bipolar/unipolar and gain configuration */ + adc_config = inb(priv->base + 11); + adbu = !(adc_config & BIT(2)); + gain = adc_config & 0x3; + + *val = 5; + *val2 = 15 - adbu + gain; + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static int stx104_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + struct stx104_iio *const priv = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + /* Only four gain states (x1, x2, x4, x8) */ + switch (val) { + case 1: + outb(0, priv->base + 11); + break; + case 2: + outb(1, priv->base + 11); + break; + case 4: + outb(2, priv->base + 11); + break; + case 8: + outb(3, priv->base + 11); + break; + default: + return -EINVAL; + } + + return 0; + case IIO_CHAN_INFO_RAW: + if (chan->output) { + /* DAC can only accept up to a 16-bit value */ + if ((unsigned int)val > 65535) + return -EINVAL; + + priv->chan_out_states[chan->channel] = val; + outw(val, priv->base + 4 + 2 * chan->channel); + + return 0; + } + return -EINVAL; + } + + return -EINVAL; +} + +static const struct iio_info stx104_info = { + .driver_module = THIS_MODULE, + .read_raw = stx104_read_raw, + .write_raw = stx104_write_raw +}; + +/* single-ended input channels configuration */ +static const struct iio_chan_spec stx104_channels_sing[] = { + STX104_OUT_CHAN(0), STX104_OUT_CHAN(1), + STX104_IN_CHAN(0, 0), STX104_IN_CHAN(1, 0), STX104_IN_CHAN(2, 0), + STX104_IN_CHAN(3, 0), STX104_IN_CHAN(4, 0), STX104_IN_CHAN(5, 0), + STX104_IN_CHAN(6, 0), STX104_IN_CHAN(7, 0), STX104_IN_CHAN(8, 0), + STX104_IN_CHAN(9, 0), STX104_IN_CHAN(10, 0), STX104_IN_CHAN(11, 0), + STX104_IN_CHAN(12, 0), STX104_IN_CHAN(13, 0), STX104_IN_CHAN(14, 0), + STX104_IN_CHAN(15, 0) +}; +/* differential input channels configuration */ +static const struct iio_chan_spec stx104_channels_diff[] = { + STX104_OUT_CHAN(0), STX104_OUT_CHAN(1), + STX104_IN_CHAN(0, 1), STX104_IN_CHAN(1, 1), STX104_IN_CHAN(2, 1), + STX104_IN_CHAN(3, 1), STX104_IN_CHAN(4, 1), STX104_IN_CHAN(5, 1), + STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 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", + base[id], base[id] + STX104_EXTENT); + return -EBUSY; + } + + indio_dev->info = &stx104_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + /* determine if differential inputs */ + if (inb(base[id] + 8) & BIT(5)) { + indio_dev->num_channels = ARRAY_SIZE(stx104_channels_diff); + indio_dev->channels = stx104_channels_diff; + } else { + indio_dev->num_channels = ARRAY_SIZE(stx104_channels_sing); + indio_dev->channels = stx104_channels_sing; + } + + indio_dev->name = dev_name(dev); + + priv = iio_priv(indio_dev); + priv->base = base[id]; + + /* configure device for software trigger operation */ + outb(0, base[id] + 9); + + /* initialize gain setting to x1 */ + outb(0, base[id] + 11); + + /* initialize DAC output to 0V */ + outw(0, base[id] + 4); + outw(0, base[id] + 6); + + 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); + +MODULE_AUTHOR("William Breathitt Gray "); +MODULE_DESCRIPTION("Apex Embedded Systems STX104 IIO driver"); +MODULE_LICENSE("GPL v2"); diff --git b/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c new file mode 100644 index 0000000..072f03b --- /dev/null +++ b/drivers/iio/adc/ti-adc12138.c @@ -0,0 +1,552 @@ +/* + * ADC12130/ADC12132/ADC12138 12-bit plus sign ADC driver + * + * Copyright (c) 2016 Akinobu Mita + * + * 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. + * + * Datasheet: http://www.ti.com/lit/ds/symlink/adc12138.pdf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ADC12138_MODE_AUTO_CAL 0x08 +#define ADC12138_MODE_READ_STATUS 0x0c +#define ADC12138_MODE_ACQUISITION_TIME_6 0x0e +#define ADC12138_MODE_ACQUISITION_TIME_10 0x4e +#define ADC12138_MODE_ACQUISITION_TIME_18 0x8e +#define ADC12138_MODE_ACQUISITION_TIME_34 0xce + +#define ADC12138_STATUS_CAL BIT(6) + +enum { + adc12130, + adc12132, + adc12138, +}; + +struct adc12138 { + struct spi_device *spi; + unsigned int id; + /* conversion clock */ + struct clk *cclk; + /* positive analog voltage reference */ + struct regulator *vref_p; + /* negative analog voltage reference */ + struct regulator *vref_n; + struct mutex lock; + struct completion complete; + /* The number of cclk periods for the S/H's acquisition time */ + unsigned int acquisition_time; + + u8 tx_buf[2] ____cacheline_aligned; + u8 rx_buf[2]; +}; + +#define ADC12138_VOLTAGE_CHANNEL(chan) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_index = chan, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 13, \ + .storagebits = 16, \ + .shift = 3, \ + .endianness = IIO_BE, \ + }, \ + } + +#define ADC12138_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (chan1), \ + .channel2 = (chan2), \ + .differential = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_index = si, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 13, \ + .storagebits = 16, \ + .shift = 3, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec adc12132_channels[] = { + ADC12138_VOLTAGE_CHANNEL(0), + ADC12138_VOLTAGE_CHANNEL(1), + ADC12138_VOLTAGE_CHANNEL_DIFF(0, 1, 2), + ADC12138_VOLTAGE_CHANNEL_DIFF(1, 0, 3), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const struct iio_chan_spec adc12138_channels[] = { + ADC12138_VOLTAGE_CHANNEL(0), + ADC12138_VOLTAGE_CHANNEL(1), + ADC12138_VOLTAGE_CHANNEL(2), + ADC12138_VOLTAGE_CHANNEL(3), + ADC12138_VOLTAGE_CHANNEL(4), + ADC12138_VOLTAGE_CHANNEL(5), + ADC12138_VOLTAGE_CHANNEL(6), + ADC12138_VOLTAGE_CHANNEL(7), + ADC12138_VOLTAGE_CHANNEL_DIFF(0, 1, 8), + ADC12138_VOLTAGE_CHANNEL_DIFF(1, 0, 9), + ADC12138_VOLTAGE_CHANNEL_DIFF(2, 3, 10), + ADC12138_VOLTAGE_CHANNEL_DIFF(3, 2, 11), + ADC12138_VOLTAGE_CHANNEL_DIFF(4, 5, 12), + ADC12138_VOLTAGE_CHANNEL_DIFF(5, 4, 13), + ADC12138_VOLTAGE_CHANNEL_DIFF(6, 7, 14), + ADC12138_VOLTAGE_CHANNEL_DIFF(7, 6, 15), + IIO_CHAN_SOFT_TIMESTAMP(16), +}; + +static int adc12138_mode_programming(struct adc12138 *adc, u8 mode, + void *rx_buf, int len) +{ + struct spi_transfer xfer = { + .tx_buf = adc->tx_buf, + .rx_buf = adc->rx_buf, + .len = len, + }; + int ret; + + /* Skip unused bits for ADC12130 and ADC12132 */ + if (adc->id != adc12138) + mode = (mode & 0xc0) | ((mode & 0x0f) << 2); + + adc->tx_buf[0] = mode; + + ret = spi_sync_transfer(adc->spi, &xfer, 1); + if (ret) + return ret; + + memcpy(rx_buf, adc->rx_buf, len); + + return 0; +} + +static int adc12138_read_status(struct adc12138 *adc) +{ + u8 rx_buf[2]; + int ret; + + ret = adc12138_mode_programming(adc, ADC12138_MODE_READ_STATUS, + rx_buf, 2); + if (ret) + return ret; + + return (rx_buf[0] << 1) | (rx_buf[1] >> 7); +} + +static int __adc12138_start_conv(struct adc12138 *adc, + struct iio_chan_spec const *channel, + void *data, int len) + +{ + const u8 ch_to_mux[] = { 0, 4, 1, 5, 2, 6, 3, 7 }; + u8 mode = (ch_to_mux[channel->channel] << 4) | + (channel->differential ? 0 : 0x80); + + return adc12138_mode_programming(adc, mode, data, len); +} + +static int adc12138_start_conv(struct adc12138 *adc, + struct iio_chan_spec const *channel) +{ + u8 trash; + + return __adc12138_start_conv(adc, channel, &trash, 1); +} + +static int adc12138_start_and_read_conv(struct adc12138 *adc, + struct iio_chan_spec const *channel, + __be16 *data) +{ + return __adc12138_start_conv(adc, channel, data, 2); +} + +static int adc12138_read_conv_data(struct adc12138 *adc, __be16 *value) +{ + /* Issue a read status instruction and read previous conversion data */ + return adc12138_mode_programming(adc, ADC12138_MODE_READ_STATUS, + value, sizeof(*value)); +} + +static int adc12138_wait_eoc(struct adc12138 *adc, unsigned long timeout) +{ + if (!wait_for_completion_timeout(&adc->complete, timeout)) + return -ETIMEDOUT; + + return 0; +} + +static int adc12138_adc_conversion(struct adc12138 *adc, + struct iio_chan_spec const *channel, + __be16 *value) +{ + int ret; + + reinit_completion(&adc->complete); + + ret = adc12138_start_conv(adc, channel); + if (ret) + return ret; + + ret = adc12138_wait_eoc(adc, msecs_to_jiffies(100)); + if (ret) + return ret; + + return adc12138_read_conv_data(adc, value); +} + +static int adc12138_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct adc12138 *adc = iio_priv(iio); + int ret; + __be16 data; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + ret = adc12138_adc_conversion(adc, channel, &data); + mutex_unlock(&adc->lock); + if (ret) + return ret; + + *value = sign_extend32(be16_to_cpu(data) >> 3, 12); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(adc->vref_p); + if (ret < 0) + return ret; + *value = ret; + + if (!IS_ERR(adc->vref_n)) { + ret = regulator_get_voltage(adc->vref_n); + if (ret < 0) + return ret; + *value -= ret; + } + + /* convert regulator output voltage to mV */ + *value /= 1000; + *shift = channel->scan_type.realbits - 1; + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + if (!IS_ERR(adc->vref_n)) { + *value = regulator_get_voltage(adc->vref_n); + if (*value < 0) + return *value; + } else { + *value = 0; + } + + /* convert regulator output voltage to mV */ + *value /= 1000; + + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static const struct iio_info adc12138_info = { + .read_raw = adc12138_read_raw, + .driver_module = THIS_MODULE, +}; + +static int adc12138_init(struct adc12138 *adc) +{ + int ret; + int status; + u8 mode; + u8 trash; + + reinit_completion(&adc->complete); + + ret = adc12138_mode_programming(adc, ADC12138_MODE_AUTO_CAL, &trash, 1); + if (ret) + return ret; + + /* data output at this time has no significance */ + status = adc12138_read_status(adc); + if (status < 0) + return status; + + adc12138_wait_eoc(adc, msecs_to_jiffies(100)); + + status = adc12138_read_status(adc); + if (status & ADC12138_STATUS_CAL) { + dev_warn(&adc->spi->dev, + "Auto Cal sequence is still in progress: %#x\n", + status); + return -EIO; + } + + switch (adc->acquisition_time) { + case 6: + mode = ADC12138_MODE_ACQUISITION_TIME_6; + break; + case 10: + mode = ADC12138_MODE_ACQUISITION_TIME_10; + break; + case 18: + mode = ADC12138_MODE_ACQUISITION_TIME_18; + break; + case 34: + mode = ADC12138_MODE_ACQUISITION_TIME_34; + break; + default: + return -EINVAL; + } + + return adc12138_mode_programming(adc, mode, &trash, 1); +} + +static irqreturn_t adc12138_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adc12138 *adc = iio_priv(indio_dev); + __be16 data[20] = { }; /* 16x 2 bytes ADC data + 8 bytes timestamp */ + __be16 trash; + int ret; + int scan_index; + int i = 0; + + mutex_lock(&adc->lock); + + for_each_set_bit(scan_index, indio_dev->active_scan_mask, + indio_dev->masklength) { + const struct iio_chan_spec *scan_chan = + &indio_dev->channels[scan_index]; + + reinit_completion(&adc->complete); + + ret = adc12138_start_and_read_conv(adc, scan_chan, + i ? &data[i - 1] : &trash); + if (ret) { + dev_warn(&adc->spi->dev, + "failed to start conversion\n"); + goto out; + } + + ret = adc12138_wait_eoc(adc, msecs_to_jiffies(100)); + if (ret) { + dev_warn(&adc->spi->dev, "wait eoc timeout\n"); + goto out; + } + + i++; + } + + if (i) { + ret = adc12138_read_conv_data(adc, &data[i - 1]); + if (ret) { + dev_warn(&adc->spi->dev, + "failed to get conversion data\n"); + goto out; + } + } + + iio_push_to_buffers_with_timestamp(indio_dev, data, + iio_get_time_ns(indio_dev)); +out: + mutex_unlock(&adc->lock); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t adc12138_eoc_handler(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct adc12138 *adc = iio_priv(indio_dev); + + complete(&adc->complete); + + return IRQ_HANDLED; +} + +static int adc12138_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct adc12138 *adc; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + adc->id = spi_get_device_id(spi)->driver_data; + mutex_init(&adc->lock); + init_completion(&adc->complete); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &adc12138_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + switch (adc->id) { + case adc12130: + case adc12132: + indio_dev->channels = adc12132_channels; + indio_dev->num_channels = ARRAY_SIZE(adc12132_channels); + break; + case adc12138: + indio_dev->channels = adc12138_channels; + indio_dev->num_channels = ARRAY_SIZE(adc12138_channels); + break; + default: + return -EINVAL; + } + + ret = of_property_read_u32(spi->dev.of_node, "ti,acquisition-time", + &adc->acquisition_time); + if (ret) + adc->acquisition_time = 10; + + adc->cclk = devm_clk_get(&spi->dev, NULL); + if (IS_ERR(adc->cclk)) + return PTR_ERR(adc->cclk); + + adc->vref_p = devm_regulator_get(&spi->dev, "vref-p"); + if (IS_ERR(adc->vref_p)) + return PTR_ERR(adc->vref_p); + + adc->vref_n = devm_regulator_get_optional(&spi->dev, "vref-n"); + if (IS_ERR(adc->vref_n)) { + /* + * Assume vref_n is 0V if an optional regulator is not + * specified, otherwise return the error code. + */ + ret = PTR_ERR(adc->vref_n); + if (ret != -ENODEV) + return ret; + } + + ret = devm_request_irq(&spi->dev, spi->irq, adc12138_eoc_handler, + IRQF_TRIGGER_RISING, indio_dev->name, indio_dev); + if (ret) + return ret; + + ret = clk_prepare_enable(adc->cclk); + if (ret) + return ret; + + ret = regulator_enable(adc->vref_p); + if (ret) + goto err_clk_disable; + + if (!IS_ERR(adc->vref_n)) { + ret = regulator_enable(adc->vref_n); + if (ret) + goto err_vref_p_disable; + } + + ret = adc12138_init(adc); + if (ret) + goto err_vref_n_disable; + + spi_set_drvdata(spi, indio_dev); + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + adc12138_trigger_handler, NULL); + if (ret) + goto err_vref_n_disable; + + ret = iio_device_register(indio_dev); + if (ret) + goto err_buffer_cleanup; + + return 0; +err_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); +err_vref_n_disable: + if (!IS_ERR(adc->vref_n)) + regulator_disable(adc->vref_n); +err_vref_p_disable: + regulator_disable(adc->vref_p); +err_clk_disable: + clk_disable_unprepare(adc->cclk); + + return ret; +} + +static int adc12138_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adc12138 *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (!IS_ERR(adc->vref_n)) + regulator_disable(adc->vref_n); + regulator_disable(adc->vref_p); + clk_disable_unprepare(adc->cclk); + + return 0; +} + +#ifdef CONFIG_OF + +static const struct of_device_id adc12138_dt_ids[] = { + { .compatible = "ti,adc12130", }, + { .compatible = "ti,adc12132", }, + { .compatible = "ti,adc12138", }, + {} +}; +MODULE_DEVICE_TABLE(of, adc12138_dt_ids); + +#endif + +static const struct spi_device_id adc12138_id[] = { + { "adc12130", adc12130 }, + { "adc12132", adc12132 }, + { "adc12138", adc12138 }, + {} +}; +MODULE_DEVICE_TABLE(spi, adc12138_id); + +static struct spi_driver adc12138_driver = { + .driver = { + .name = "adc12138", + .of_match_table = of_match_ptr(adc12138_dt_ids), + }, + .probe = adc12138_probe, + .remove = adc12138_remove, + .id_table = adc12138_id, +}; +module_spi_driver(adc12138_driver); + +MODULE_AUTHOR("Akinobu Mita "); +MODULE_DESCRIPTION("ADC12130/ADC12132/ADC12138 driver"); +MODULE_LICENSE("GPL v2"); diff --git b/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c new file mode 100644 index 0000000..f94b69f --- /dev/null +++ b/drivers/iio/adc/ti-adc161s626.c @@ -0,0 +1,248 @@ +/* + * ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC + * + * ADC Devices Supported: + * adc141s626 - 14-bit ADC + * adc161s626 - 16-bit ADC + * + * Copyright (C) 2016 Matt Ranostay + * + * 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 + +#define TI_ADC_DRV_NAME "ti-adc161s626" + +enum { + TI_ADC141S626, + TI_ADC161S626, +}; + +static const struct iio_chan_spec ti_adc141s626_channels[] = { + { + .type = IIO_VOLTAGE, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 14, + .storagebits = 16, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct iio_chan_spec ti_adc161s626_channels[] = { + { + .type = IIO_VOLTAGE, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +struct ti_adc_data { + struct iio_dev *indio_dev; + struct spi_device *spi; + u8 read_size; + u8 shift; + + u8 buffer[16] ____cacheline_aligned; +}; + +static int ti_adc_read_measurement(struct ti_adc_data *data, + struct iio_chan_spec const *chan, int *val) +{ + int ret; + + switch (data->read_size) { + case 2: { + __be16 buf; + + ret = spi_read(data->spi, (void *) &buf, 2); + if (ret) + return ret; + + *val = be16_to_cpu(buf); + break; + } + case 3: { + __be32 buf; + + ret = spi_read(data->spi, (void *) &buf, 3); + if (ret) + return ret; + + *val = be32_to_cpu(buf) >> 8; + break; + } + default: + return -EINVAL; + } + + *val = sign_extend32(*val >> data->shift, chan->scan_type.realbits - 1); + + return 0; +} + +static irqreturn_t ti_adc_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct ti_adc_data *data = iio_priv(indio_dev); + int ret; + + ret = ti_adc_read_measurement(data, &indio_dev->channels[0], + (int *) &data->buffer); + if (!ret) + iio_push_to_buffers_with_timestamp(indio_dev, + data->buffer, + iio_get_time_ns(indio_dev)); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ti_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ti_adc_data *data = iio_priv(indio_dev); + int ret; + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = ti_adc_read_measurement(data, chan, val); + iio_device_release_direct_mode(indio_dev); + + if (!ret) + return IIO_VAL_INT; + + return 0; +} + +static const struct iio_info ti_adc_info = { + .driver_module = THIS_MODULE, + .read_raw = ti_adc_read_raw, +}; + +static int ti_adc_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ti_adc_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->info = &ti_adc_info; + indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; + indio_dev->name = TI_ADC_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + spi_set_drvdata(spi, indio_dev); + + data = iio_priv(indio_dev); + data->spi = spi; + + switch (spi_get_device_id(spi)->driver_data) { + case TI_ADC141S626: + indio_dev->channels = ti_adc141s626_channels; + indio_dev->num_channels = ARRAY_SIZE(ti_adc141s626_channels); + data->shift = 0; + data->read_size = 2; + break; + case TI_ADC161S626: + indio_dev->channels = ti_adc161s626_channels; + indio_dev->num_channels = ARRAY_SIZE(ti_adc161s626_channels); + data->shift = 6; + data->read_size = 3; + break; + } + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + ti_adc_trigger_handler, NULL); + if (ret) + return ret; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_unreg_buffer; + + return 0; + +error_unreg_buffer: + iio_triggered_buffer_cleanup(indio_dev); + + return ret; +} + +static int ti_adc_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 0; +} + +static const struct of_device_id ti_adc_dt_ids[] = { + { .compatible = "ti,adc141s626", }, + { .compatible = "ti,adc161s626", }, + {} +}; +MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); + +static const struct spi_device_id ti_adc_id[] = { + {"adc141s626", TI_ADC141S626}, + {"adc161s626", TI_ADC161S626}, + {}, +}; +MODULE_DEVICE_TABLE(spi, ti_adc_id); + +static struct spi_driver ti_adc_driver = { + .driver = { + .name = TI_ADC_DRV_NAME, + .of_match_table = of_match_ptr(ti_adc_dt_ids), + }, + .probe = ti_adc_probe, + .remove = ti_adc_remove, + .id_table = ti_adc_id, +}; +module_spi_driver(ti_adc_driver); + +MODULE_AUTHOR("Matt Ranostay "); +MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 066abaf..cde6f13 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -522,6 +522,7 @@ static int ads1015_get_channels_config_of(struct i2c_client *client) if (pga > 6) { dev_err(&client->dev, "invalid gain on %s\n", node->full_name); + of_node_put(node); return -EINVAL; } } @@ -532,6 +533,7 @@ static int ads1015_get_channels_config_of(struct i2c_client *client) dev_err(&client->dev, "invalid data_rate on %s\n", node->full_name); + of_node_put(node); return -EINVAL; } } diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c index c400439..4a16349 100644 --- a/drivers/iio/adc/ti-ads8688.c +++ b/drivers/iio/adc/ti-ads8688.c @@ -438,7 +438,7 @@ static int ads8688_probe(struct spi_device *spi) return 0; error_out: - if (!IS_ERR_OR_NULL(st->reg)) + if (!IS_ERR(st->reg)) regulator_disable(st->reg); return ret; @@ -451,7 +451,7 @@ static int ads8688_remove(struct spi_device *spi) iio_device_unregister(indio_dev); - if (!IS_ERR_OR_NULL(st->reg)) + if (!IS_ERR(st->reg)) regulator_disable(st->reg); return 0; diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c index 323079c..b8f550e 100644 --- a/drivers/iio/buffer/industrialio-buffer-cb.c +++ b/drivers/iio/buffer/industrialio-buffer-cb.c @@ -18,6 +18,7 @@ struct iio_cb_buffer { int (*cb)(const void *data, void *private); void *private; struct iio_channel *channels; + struct iio_dev *indio_dev; }; static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer) @@ -52,7 +53,6 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, { int ret; struct iio_cb_buffer *cb_buff; - struct iio_dev *indio_dev; struct iio_channel *chan; cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL); @@ -72,17 +72,17 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, goto error_free_cb_buff; } - indio_dev = cb_buff->channels[0].indio_dev; + cb_buff->indio_dev = cb_buff->channels[0].indio_dev; cb_buff->buffer.scan_mask - = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long), - GFP_KERNEL); + = kcalloc(BITS_TO_LONGS(cb_buff->indio_dev->masklength), + sizeof(long), GFP_KERNEL); if (cb_buff->buffer.scan_mask == NULL) { ret = -ENOMEM; goto error_release_channels; } chan = &cb_buff->channels[0]; while (chan->indio_dev) { - if (chan->indio_dev != indio_dev) { + if (chan->indio_dev != cb_buff->indio_dev) { ret = -EINVAL; goto error_free_scan_mask; } @@ -105,17 +105,14 @@ EXPORT_SYMBOL_GPL(iio_channel_get_all_cb); int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff) { - return iio_update_buffers(cb_buff->channels[0].indio_dev, - &cb_buff->buffer, + return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer, NULL); } EXPORT_SYMBOL_GPL(iio_channel_start_all_cb); void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff) { - iio_update_buffers(cb_buff->channels[0].indio_dev, - NULL, - &cb_buff->buffer); + iio_update_buffers(cb_buff->indio_dev, NULL, &cb_buff->buffer); } EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb); @@ -133,6 +130,13 @@ struct iio_channel } EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels); +struct iio_dev +*iio_channel_cb_get_iio_dev(const struct iio_cb_buffer *cb_buffer) +{ + return cb_buffer->indio_dev; +} +EXPORT_SYMBOL_GPL(iio_channel_cb_get_iio_dev); + MODULE_AUTHOR("Jonathan Cameron "); MODULE_DESCRIPTION("Industrial I/O callback buffer"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/buffer/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c index 4b2858b..d3db1fc 100644 --- a/drivers/iio/buffer/industrialio-triggered-buffer.c +++ b/drivers/iio/buffer/industrialio-triggered-buffer.c @@ -98,6 +98,48 @@ void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev) } EXPORT_SYMBOL(iio_triggered_buffer_cleanup); +static void devm_iio_triggered_buffer_clean(struct device *dev, void *res) +{ + iio_triggered_buffer_cleanup(*(struct iio_dev **)res); +} + +int devm_iio_triggered_buffer_setup(struct device *dev, + struct iio_dev *indio_dev, + irqreturn_t (*h)(int irq, void *p), + irqreturn_t (*thread)(int irq, void *p), + const struct iio_buffer_setup_ops *ops) +{ + struct iio_dev **ptr; + int ret; + + ptr = devres_alloc(devm_iio_triggered_buffer_clean, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + *ptr = indio_dev; + + ret = iio_triggered_buffer_setup(indio_dev, h, thread, ops); + if (!ret) + devres_add(dev, ptr); + else + devres_free(ptr); + + return ret; +} +EXPORT_SYMBOL_GPL(devm_iio_triggered_buffer_setup); + +void devm_iio_triggered_buffer_cleanup(struct device *dev, + struct iio_dev *indio_dev) +{ + int rc; + + rc = devres_release(dev, devm_iio_triggered_buffer_clean, + devm_iio_device_match, indio_dev); + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_iio_triggered_buffer_cleanup); + MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("IIO helper functions for setting up triggered buffers"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index 4bcc025..cea7f98 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -16,6 +16,7 @@ config ATLAS_PH_SENSOR Atlas Scientific OEM SM sensors: * pH SM sensor * EC SM sensor + * ORP 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 407f141..ef761a5 100644 --- a/drivers/iio/chemical/atlas-ph-sensor.c +++ b/drivers/iio/chemical/atlas-ph-sensor.c @@ -66,12 +66,17 @@ #define ATLAS_REG_TDS_DATA 0x1c #define ATLAS_REG_PSS_DATA 0x20 +#define ATLAS_REG_ORP_CALIB_STATUS 0x0d +#define ATLAS_REG_ORP_DATA 0x0e + #define ATLAS_PH_INT_TIME_IN_US 450000 #define ATLAS_EC_INT_TIME_IN_US 650000 +#define ATLAS_ORP_INT_TIME_IN_US 450000 enum { ATLAS_PH_SM, ATLAS_EC_SM, + ATLAS_ORP_SM, }; struct atlas_data { @@ -84,26 +89,10 @@ struct atlas_data { __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 = { - .yes_ranges = atlas_volatile_ranges, - .n_yes_ranges = ARRAY_SIZE(atlas_volatile_ranges), -}; - static const struct regmap_config atlas_regmap_config = { .name = ATLAS_REGMAP_NAME, - .reg_bits = 8, .val_bits = 8, - - .volatile_table = &atlas_volatile_table, - .max_register = ATLAS_REG_PSS_DATA + 4, - .cache_type = REGCACHE_RBTREE, }; static const struct iio_chan_spec atlas_ph_channels[] = { @@ -175,6 +164,23 @@ static const struct iio_chan_spec atlas_ec_channels[] = { }, }; +static const struct iio_chan_spec atlas_orp_channels[] = { + { + .type = IIO_VOLTAGE, + .address = ATLAS_REG_ORP_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + static int atlas_check_ph_calibration(struct atlas_data *data) { struct device *dev = &data->client->dev; @@ -207,13 +213,14 @@ static int atlas_check_ec_calibration(struct atlas_data *data) struct device *dev = &data->client->dev; int ret; unsigned int val; + __be16 rval; - ret = regmap_bulk_read(data->regmap, ATLAS_REG_EC_PROBE, &val, 2); + ret = regmap_bulk_read(data->regmap, ATLAS_REG_EC_PROBE, &rval, 2); if (ret) return ret; - dev_info(dev, "probe set to K = %d.%.2d", be16_to_cpu(val) / 100, - be16_to_cpu(val) % 100); + val = be16_to_cpu(rval); + dev_info(dev, "probe set to K = %d.%.2d", val / 100, val % 100); ret = regmap_read(data->regmap, ATLAS_REG_EC_CALIB_STATUS, &val); if (ret) @@ -240,6 +247,22 @@ static int atlas_check_ec_calibration(struct atlas_data *data) return 0; } +static int atlas_check_orp_calibration(struct atlas_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + unsigned int val; + + ret = regmap_read(data->regmap, ATLAS_REG_ORP_CALIB_STATUS, &val); + if (ret) + return ret; + + if (!val) + dev_warn(dev, "device has not been calibrated\n"); + + return 0; +}; + struct atlas_device { const struct iio_chan_spec *channels; int num_channels; @@ -264,7 +287,13 @@ static struct atlas_device atlas_devices[] = { .calibration = &atlas_check_ec_calibration, .delay = ATLAS_EC_INT_TIME_IN_US, }, - + [ATLAS_ORP_SM] = { + .channels = atlas_orp_channels, + .num_channels = 2, + .data_reg = ATLAS_REG_ORP_DATA, + .calibration = &atlas_check_orp_calibration, + .delay = ATLAS_ORP_INT_TIME_IN_US, + }, }; static int atlas_set_powermode(struct atlas_data *data, int on) @@ -402,15 +431,14 @@ static int atlas_read_raw(struct iio_dev *indio_dev, case IIO_PH: case IIO_CONCENTRATION: case IIO_ELECTRICALCONDUCTIVITY: - mutex_lock(&indio_dev->mlock); + case IIO_VOLTAGE: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; - if (iio_buffer_enabled(indio_dev)) - ret = -EBUSY; - else - ret = atlas_read_measurement(data, - chan->address, ®); + ret = atlas_read_measurement(data, chan->address, ®); - mutex_unlock(&indio_dev->mlock); + iio_device_release_direct_mode(indio_dev); break; default: ret = -EINVAL; @@ -440,6 +468,10 @@ static int atlas_read_raw(struct iio_dev *indio_dev, *val = 0; /* 0.000000001 */ *val2 = 1000; return IIO_VAL_INT_PLUS_NANO; + case IIO_VOLTAGE: + *val = 1; /* 0.1 */ + *val2 = 10; + break; default: return -EINVAL; } @@ -475,6 +507,7 @@ static const struct iio_info atlas_info = { static const struct i2c_device_id atlas_id[] = { { "atlas-ph-sm", ATLAS_PH_SM}, { "atlas-ec-sm", ATLAS_EC_SM}, + { "atlas-orp-sm", ATLAS_ORP_SM}, {} }; MODULE_DEVICE_TABLE(i2c, atlas_id); @@ -482,6 +515,7 @@ MODULE_DEVICE_TABLE(i2c, atlas_id); 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, }, + { .compatible = "atlas,orp-sm", .data = (void *)ATLAS_ORP_SM, }, { } }; MODULE_DEVICE_TABLE(of, atlas_dt_ids); diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c index 652649d..8e0e441 100644 --- a/drivers/iio/chemical/vz89x.c +++ b/drivers/iio/chemical/vz89x.c @@ -19,25 +19,55 @@ #include #include #include +#include +#include #include #include #define VZ89X_REG_MEASUREMENT 0x09 -#define VZ89X_REG_MEASUREMENT_SIZE 6 +#define VZ89X_REG_MEASUREMENT_RD_SIZE 6 +#define VZ89X_REG_MEASUREMENT_WR_SIZE 3 #define VZ89X_VOC_CO2_IDX 0 #define VZ89X_VOC_SHORT_IDX 1 #define VZ89X_VOC_TVOC_IDX 2 #define VZ89X_VOC_RESISTANCE_IDX 3 +#define VZ89TE_REG_MEASUREMENT 0x0c +#define VZ89TE_REG_MEASUREMENT_RD_SIZE 7 +#define VZ89TE_REG_MEASUREMENT_WR_SIZE 6 + +#define VZ89TE_VOC_TVOC_IDX 0 +#define VZ89TE_VOC_CO2_IDX 1 +#define VZ89TE_VOC_RESISTANCE_IDX 2 + +enum { + VZ89X, + VZ89TE, +}; + +struct vz89x_chip_data; + struct vz89x_data { struct i2c_client *client; + const struct vz89x_chip_data *chip; struct mutex lock; int (*xfer)(struct vz89x_data *data, u8 cmd); + bool is_valid; unsigned long last_update; - u8 buffer[VZ89X_REG_MEASUREMENT_SIZE]; + u8 buffer[VZ89TE_REG_MEASUREMENT_RD_SIZE]; +}; + +struct vz89x_chip_data { + bool (*valid)(struct vz89x_data *data); + const struct iio_chan_spec *channels; + u8 num_channels; + + u8 cmd; + u8 read_size; + u8 write_size; }; static const struct iio_chan_spec vz89x_channels[] = { @@ -70,6 +100,40 @@ static const struct iio_chan_spec vz89x_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), .address = VZ89X_VOC_RESISTANCE_IDX, + .scan_index = -1, + .scan_type = { + .endianness = IIO_LE, + }, + }, +}; + +static const struct iio_chan_spec vz89te_channels[] = { + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_VOC, + .modified = 1, + .info_mask_separate = + BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW), + .address = VZ89TE_VOC_TVOC_IDX, + }, + + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_CO2, + .modified = 1, + .info_mask_separate = + BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW), + .address = VZ89TE_VOC_CO2_IDX, + }, + { + .type = IIO_RESISTANCE, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .address = VZ89TE_VOC_RESISTANCE_IDX, + .scan_index = -1, + .scan_type = { + .endianness = IIO_BE, + }, }, }; @@ -93,29 +157,45 @@ static const struct attribute_group vz89x_attrs_group = { * always zero, and by also confirming the VOC_short isn't zero. */ -static int vz89x_measurement_is_valid(struct vz89x_data *data) +static bool vz89x_measurement_is_valid(struct vz89x_data *data) { if (data->buffer[VZ89X_VOC_SHORT_IDX] == 0) - return 1; + return true; - return !!(data->buffer[VZ89X_REG_MEASUREMENT_SIZE - 1] > 0); + return !!(data->buffer[data->chip->read_size - 1] > 0); +} + +/* VZ89TE device has a modified CRC-8 two complement check */ +static bool vz89te_measurement_is_valid(struct vz89x_data *data) +{ + u8 crc = 0; + int i, sum = 0; + + for (i = 0; i < (data->chip->read_size - 1); i++) { + sum = crc + data->buffer[i]; + crc = sum; + crc += sum / 256; + } + + return !((0xff - crc) == data->buffer[data->chip->read_size - 1]); } static int vz89x_i2c_xfer(struct vz89x_data *data, u8 cmd) { + const struct vz89x_chip_data *chip = data->chip; struct i2c_client *client = data->client; struct i2c_msg msg[2]; int ret; - u8 buf[3] = { cmd, 0, 0}; + u8 buf[6] = { cmd, 0, 0, 0, 0, 0xf3 }; msg[0].addr = client->addr; msg[0].flags = client->flags; - msg[0].len = 3; + msg[0].len = chip->write_size; msg[0].buf = (char *) &buf; msg[1].addr = client->addr; msg[1].flags = client->flags | I2C_M_RD; - msg[1].len = VZ89X_REG_MEASUREMENT_SIZE; + msg[1].len = chip->read_size; msg[1].buf = (char *) &data->buffer; ret = i2c_transfer(client->adapter, msg, 2); @@ -133,7 +213,7 @@ static int vz89x_smbus_xfer(struct vz89x_data *data, u8 cmd) if (ret < 0) return ret; - for (i = 0; i < VZ89X_REG_MEASUREMENT_SIZE; i++) { + for (i = 0; i < data->chip->read_size; i++) { ret = i2c_smbus_read_byte(client); if (ret < 0) return ret; @@ -145,30 +225,47 @@ static int vz89x_smbus_xfer(struct vz89x_data *data, u8 cmd) static int vz89x_get_measurement(struct vz89x_data *data) { + const struct vz89x_chip_data *chip = data->chip; int ret; /* sensor can only be polled once a second max per datasheet */ if (!time_after(jiffies, data->last_update + HZ)) - return 0; + return data->is_valid ? 0 : -EAGAIN; + + data->is_valid = false; + data->last_update = jiffies; - ret = data->xfer(data, VZ89X_REG_MEASUREMENT); + ret = data->xfer(data, chip->cmd); if (ret < 0) return ret; - ret = vz89x_measurement_is_valid(data); + ret = chip->valid(data); if (ret) return -EAGAIN; - data->last_update = jiffies; + data->is_valid = true; return 0; } -static int vz89x_get_resistance_reading(struct vz89x_data *data) +static int vz89x_get_resistance_reading(struct vz89x_data *data, + struct iio_chan_spec const *chan, + int *val) { - u8 *buf = &data->buffer[VZ89X_VOC_RESISTANCE_IDX]; + u8 *tmp = (u8 *) &data->buffer[chan->address]; - return buf[0] | (buf[1] << 8); + switch (chan->scan_type.endianness) { + case IIO_LE: + *val = le32_to_cpup((__le32 *) tmp) & GENMASK(23, 0); + break; + case IIO_BE: + *val = be32_to_cpup((__be32 *) tmp) >> 8; + break; + default: + return -EINVAL; + } + + return 0; } static int vz89x_read_raw(struct iio_dev *indio_dev, @@ -187,15 +284,15 @@ static int vz89x_read_raw(struct iio_dev *indio_dev, if (ret) return ret; - switch (chan->address) { - case VZ89X_VOC_CO2_IDX: - case VZ89X_VOC_SHORT_IDX: - case VZ89X_VOC_TVOC_IDX: + switch (chan->type) { + case IIO_CONCENTRATION: *val = data->buffer[chan->address]; return IIO_VAL_INT; - case VZ89X_VOC_RESISTANCE_IDX: - *val = vz89x_get_resistance_reading(data); - return IIO_VAL_INT; + case IIO_RESISTANCE: + ret = vz89x_get_resistance_reading(data, chan, val); + if (!ret) + return IIO_VAL_INT; + break; default: return -EINVAL; } @@ -210,12 +307,12 @@ static int vz89x_read_raw(struct iio_dev *indio_dev, } break; case IIO_CHAN_INFO_OFFSET: - switch (chan->address) { - case VZ89X_VOC_CO2_IDX: + switch (chan->channel2) { + case IIO_MOD_CO2: *val = 44; *val2 = 250000; return IIO_VAL_INT_PLUS_MICRO; - case VZ89X_VOC_TVOC_IDX: + case IIO_MOD_VOC: *val = -13; return IIO_VAL_INT; default: @@ -232,11 +329,43 @@ static const struct iio_info vz89x_info = { .driver_module = THIS_MODULE, }; +static const struct vz89x_chip_data vz89x_chips[] = { + { + .valid = vz89x_measurement_is_valid, + + .cmd = VZ89X_REG_MEASUREMENT, + .read_size = VZ89X_REG_MEASUREMENT_RD_SIZE, + .write_size = VZ89X_REG_MEASUREMENT_WR_SIZE, + + .channels = vz89x_channels, + .num_channels = ARRAY_SIZE(vz89x_channels), + }, + { + .valid = vz89te_measurement_is_valid, + + .cmd = VZ89TE_REG_MEASUREMENT, + .read_size = VZ89TE_REG_MEASUREMENT_RD_SIZE, + .write_size = VZ89TE_REG_MEASUREMENT_WR_SIZE, + + .channels = vz89te_channels, + .num_channels = ARRAY_SIZE(vz89te_channels), + }, +}; + +static const struct of_device_id vz89x_dt_ids[] = { + { .compatible = "sgx,vz89x", .data = (void *) VZ89X }, + { .compatible = "sgx,vz89te", .data = (void *) VZ89TE }, + { } +}; +MODULE_DEVICE_TABLE(of, vz89x_dt_ids); + static int vz89x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct iio_dev *indio_dev; struct vz89x_data *data; + const struct of_device_id *of_id; + int chip_id; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -251,8 +380,15 @@ static int vz89x_probe(struct i2c_client *client, else return -EOPNOTSUPP; + of_id = of_match_device(vz89x_dt_ids, &client->dev); + if (!of_id) + chip_id = id->driver_data; + else + chip_id = (unsigned long)of_id->data; + i2c_set_clientdata(client, indio_dev); data->client = client; + data->chip = &vz89x_chips[chip_id]; data->last_update = jiffies - HZ; mutex_init(&data->lock); @@ -261,24 +397,19 @@ static int vz89x_probe(struct i2c_client *client, indio_dev->name = dev_name(&client->dev); indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = vz89x_channels; - indio_dev->num_channels = ARRAY_SIZE(vz89x_channels); + indio_dev->channels = data->chip->channels; + indio_dev->num_channels = data->chip->num_channels; return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id vz89x_id[] = { - { "vz89x", 0 }, + { "vz89x", VZ89X }, + { "vz89te", VZ89TE }, { } }; MODULE_DEVICE_TABLE(i2c, vz89x_id); -static const struct of_device_id vz89x_dt_ids[] = { - { .compatible = "sgx,vz89x" }, - { } -}; -MODULE_DEVICE_TABLE(of, vz89x_dt_ids); - static struct i2c_driver vz89x_driver = { .driver = { .name = "vz89x", diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 5b41f9d..a3cce3a 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -122,6 +122,14 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state) #endif } +static void hid_sensor_set_power_work(struct work_struct *work) +{ + struct hid_sensor_common *attrb = container_of(work, + struct hid_sensor_common, + work); + _hid_sensor_power_state(attrb, true); +} + static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, bool state) { @@ -130,6 +138,7 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, void hid_sensor_remove_trigger(struct hid_sensor_common *attrb) { + cancel_work_sync(&attrb->work); iio_trigger_unregister(attrb->trigger); iio_trigger_free(attrb->trigger); } @@ -170,6 +179,9 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, goto error_unreg_trigger; iio_device_set_drvdata(indio_dev, attrb); + + INIT_WORK(&attrb->work, hid_sensor_set_power_work); + pm_suspend_ignore_children(&attrb->pdev->dev, true); pm_runtime_enable(&attrb->pdev->dev); /* Default to 3 seconds, but can be changed from sysfs */ @@ -187,8 +199,7 @@ error_ret: } EXPORT_SYMBOL(hid_sensor_setup_trigger); -#ifdef CONFIG_PM -static int hid_sensor_suspend(struct device *dev) +static int __maybe_unused hid_sensor_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct iio_dev *indio_dev = platform_get_drvdata(pdev); @@ -197,21 +208,27 @@ static int hid_sensor_suspend(struct device *dev) return _hid_sensor_power_state(attrb, false); } -static int hid_sensor_resume(struct device *dev) +static int __maybe_unused hid_sensor_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); + schedule_work(&attrb->work); + return 0; +} +static int __maybe_unused hid_sensor_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); return _hid_sensor_power_state(attrb, true); } -#endif - const struct dev_pm_ops hid_sensor_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(hid_sensor_suspend, hid_sensor_resume) SET_RUNTIME_PM_OPS(hid_sensor_suspend, - hid_sensor_resume, NULL) + hid_sensor_runtime_resume, NULL) }; EXPORT_SYMBOL(hid_sensor_pm_ops); diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c index d06e728..fe7775b 100644 --- a/drivers/iio/common/st_sensors/st_sensors_buffer.c +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c @@ -63,7 +63,7 @@ irqreturn_t st_sensors_trigger_handler(int irq, void *p) * 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) + if (iio_trigger_using_own(indio_dev)) timestamp = sdata->hw_timestamp; else timestamp = iio_get_time_ns(indio_dev); diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 2d5282e..285a64a 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -234,39 +234,35 @@ int st_sensors_power_enable(struct iio_dev *indio_dev) int err; /* Regulators not mandatory, but if requested we should enable them. */ - pdata->vdd = devm_regulator_get_optional(indio_dev->dev.parent, "vdd"); - if (!IS_ERR(pdata->vdd)) { - err = regulator_enable(pdata->vdd); - 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 = devm_regulator_get(indio_dev->dev.parent, "vdd"); + if (IS_ERR(pdata->vdd)) { + dev_err(&indio_dev->dev, "unable to get Vdd supply\n"); + return PTR_ERR(pdata->vdd); + } + err = regulator_enable(pdata->vdd); + if (err != 0) { + dev_warn(&indio_dev->dev, + "Failed to enable specified Vdd supply\n"); + 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) { - dev_warn(&indio_dev->dev, - "Failed to enable specified Vdd_IO supply\n"); - goto st_sensors_disable_vdd; - } - } else { + pdata->vdd_io = devm_regulator_get(indio_dev->dev.parent, "vddio"); + if (IS_ERR(pdata->vdd_io)) { + dev_err(&indio_dev->dev, "unable to get Vdd_IO supply\n"); err = PTR_ERR(pdata->vdd_io); - if (err != -ENODEV) - goto st_sensors_disable_vdd; + goto st_sensors_disable_vdd; + } + err = regulator_enable(pdata->vdd_io); + if (err != 0) { + dev_warn(&indio_dev->dev, + "Failed to enable specified Vdd_IO supply\n"); + goto st_sensors_disable_vdd; } return 0; st_sensors_disable_vdd: - if (!IS_ERR_OR_NULL(pdata->vdd)) - regulator_disable(pdata->vdd); + regulator_disable(pdata->vdd); return err; } EXPORT_SYMBOL(st_sensors_power_enable); @@ -275,11 +271,8 @@ void st_sensors_power_disable(struct iio_dev *indio_dev) { struct st_sensor_data *pdata = iio_priv(indio_dev); - if (!IS_ERR_OR_NULL(pdata->vdd)) - regulator_disable(pdata->vdd); - - if (!IS_ERR_OR_NULL(pdata->vdd_io)) - regulator_disable(pdata->vdd_io); + regulator_disable(pdata->vdd); + regulator_disable(pdata->vdd_io); } EXPORT_SYMBOL(st_sensors_power_disable); diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index e66f12e..fa73e67 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -66,7 +66,7 @@ static int st_sensors_new_samples_available(struct iio_dev *indio_dev, * @irq: irq number * @p: private handler data */ -irqreturn_t st_sensors_irq_handler(int irq, void *p) +static irqreturn_t st_sensors_irq_handler(int irq, void *p) { struct iio_trigger *trig = p; struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); @@ -82,7 +82,7 @@ irqreturn_t st_sensors_irq_handler(int irq, void *p) * @irq: irq number * @p: private handler data */ -irqreturn_t st_sensors_irq_thread(int irq, void *p) +static 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); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index ca81447..120b244 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -181,6 +181,25 @@ config AD7303 To compile this driver as module choose M here: the module will be called ad7303. +config CIO_DAC + tristate "Measurement Computing CIO-DAC IIO driver" + depends on X86 && ISA_BUS_API + help + Say yes here to build support for the Measurement Computing CIO-DAC + analog output device family (CIO-DAC16, CIO-DAC08, PC104-DAC06). The + base port addresses for the devices may be configured via the base + array module parameter. + +config AD8801 + tristate "Analog Devices AD8801/AD8803 DAC driver" + depends on SPI_MASTER + help + Say yes here to build support for Analog Devices AD8801, AD8803 Digital to + Analog Converters (DAC). + + To compile this driver as a module choose M here: the module will be called + ad8801. + config LPC18XX_DAC tristate "NXP LPC18xx DAC driver" depends on ARCH_LPC18XX || COMPILE_TEST @@ -245,16 +264,6 @@ config MCP4922 To compile this driver as a module, choose M here: the module will be called 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 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" depends on OF diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 8b78d5c..27642bb 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -20,11 +20,12 @@ obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o obj-$(CONFIG_AD7303) += ad7303.o +obj-$(CONFIG_AD8801) += ad8801.o +obj-$(CONFIG_CIO_DAC) += cio-dac.o obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o obj-$(CONFIG_M62332) += m62332.o obj-$(CONFIG_MAX517) += max517.o obj-$(CONFIG_MAX5821) += max5821.o obj-$(CONFIG_MCP4725) += mcp4725.o obj-$(CONFIG_MCP4922) += mcp4922.o -obj-$(CONFIG_STX104) += stx104.o obj-$(CONFIG_VF610_DAC) += vf610_dac.o diff --git b/drivers/iio/dac/ad8801.c b/drivers/iio/dac/ad8801.c new file mode 100644 index 0000000..f06faa1 --- /dev/null +++ b/drivers/iio/dac/ad8801.c @@ -0,0 +1,239 @@ +/* + * IIO DAC driver for Analog Devices AD8801 DAC + * + * Copyright (C) 2016 Gwenhael Goavec-Merou + * 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 + +#define AD8801_CFG_ADDR_OFFSET 8 + +enum ad8801_device_ids { + ID_AD8801, + ID_AD8803, +}; + +struct ad8801_state { + struct spi_device *spi; + unsigned char dac_cache[8]; /* Value write on each channel */ + unsigned int vrefh_mv; + unsigned int vrefl_mv; + struct regulator *vrefh_reg; + struct regulator *vrefl_reg; + + __be16 data ____cacheline_aligned; +}; + +static int ad8801_spi_write(struct ad8801_state *state, + u8 channel, unsigned char value) +{ + state->data = cpu_to_be16((channel << AD8801_CFG_ADDR_OFFSET) | value); + return spi_write(state->spi, &state->data, sizeof(state->data)); +} + +static int ad8801_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + struct ad8801_state *state = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val >= 256 || val < 0) + return -EINVAL; + + ret = ad8801_spi_write(state, chan->channel, val); + if (ret == 0) + state->dac_cache[chan->channel] = val; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int ad8801_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long info) +{ + struct ad8801_state *state = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + *val = state->dac_cache[chan->channel]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = state->vrefh_mv - state->vrefl_mv; + *val2 = 8; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + *val = state->vrefl_mv; + return IIO_VAL_INT; + default: + return -EINVAL; + } + + return -EINVAL; +} + +static const struct iio_info ad8801_info = { + .read_raw = ad8801_read_raw, + .write_raw = ad8801_write_raw, + .driver_module = THIS_MODULE, +}; + +#define AD8801_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ +} + +static const struct iio_chan_spec ad8801_channels[] = { + AD8801_CHANNEL(0), + AD8801_CHANNEL(1), + AD8801_CHANNEL(2), + AD8801_CHANNEL(3), + AD8801_CHANNEL(4), + AD8801_CHANNEL(5), + AD8801_CHANNEL(6), + AD8801_CHANNEL(7), +}; + +static int ad8801_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ad8801_state *state; + const struct spi_device_id *id; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); + if (indio_dev == NULL) + return -ENOMEM; + + state = iio_priv(indio_dev); + state->spi = spi; + id = spi_get_device_id(spi); + + state->vrefh_reg = devm_regulator_get(&spi->dev, "vrefh"); + if (IS_ERR(state->vrefh_reg)) { + dev_err(&spi->dev, "Vrefh regulator not specified\n"); + return PTR_ERR(state->vrefh_reg); + } + + ret = regulator_enable(state->vrefh_reg); + if (ret) { + dev_err(&spi->dev, "Failed to enable vrefh regulator: %d\n", + ret); + return ret; + } + + ret = regulator_get_voltage(state->vrefh_reg); + if (ret < 0) { + dev_err(&spi->dev, "Failed to read vrefh regulator: %d\n", + ret); + goto error_disable_vrefh_reg; + } + state->vrefh_mv = ret / 1000; + + if (id->driver_data == ID_AD8803) { + state->vrefl_reg = devm_regulator_get(&spi->dev, "vrefl"); + if (IS_ERR(state->vrefl_reg)) { + dev_err(&spi->dev, "Vrefl regulator not specified\n"); + ret = PTR_ERR(state->vrefl_reg); + goto error_disable_vrefh_reg; + } + + ret = regulator_enable(state->vrefl_reg); + if (ret) { + dev_err(&spi->dev, "Failed to enable vrefl regulator: %d\n", + ret); + goto error_disable_vrefh_reg; + } + + ret = regulator_get_voltage(state->vrefl_reg); + if (ret < 0) { + dev_err(&spi->dev, "Failed to read vrefl regulator: %d\n", + ret); + goto error_disable_vrefl_reg; + } + state->vrefl_mv = ret / 1000; + } else { + state->vrefl_mv = 0; + state->vrefl_reg = NULL; + } + + spi_set_drvdata(spi, indio_dev); + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &ad8801_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad8801_channels; + indio_dev->num_channels = ARRAY_SIZE(ad8801_channels); + indio_dev->name = id->name; + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&spi->dev, "Failed to register iio device: %d\n", + ret); + goto error_disable_vrefl_reg; + } + + return 0; + +error_disable_vrefl_reg: + if (state->vrefl_reg) + regulator_disable(state->vrefl_reg); +error_disable_vrefh_reg: + regulator_disable(state->vrefh_reg); + return ret; +} + +static int ad8801_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad8801_state *state = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (state->vrefl_reg) + regulator_disable(state->vrefl_reg); + regulator_disable(state->vrefh_reg); + + return 0; +} + +static const struct spi_device_id ad8801_ids[] = { + {"ad8801", ID_AD8801}, + {"ad8803", ID_AD8803}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad8801_ids); + +static struct spi_driver ad8801_driver = { + .driver = { + .name = "ad8801", + }, + .probe = ad8801_probe, + .remove = ad8801_remove, + .id_table = ad8801_ids, +}; +module_spi_driver(ad8801_driver); + +MODULE_AUTHOR("Gwenhael Goavec-Merou "); +MODULE_DESCRIPTION("Analog Devices AD8801/AD8803 DAC"); +MODULE_LICENSE("GPL v2"); diff --git b/drivers/iio/dac/cio-dac.c b/drivers/iio/dac/cio-dac.c new file mode 100644 index 0000000..5a743e2 --- /dev/null +++ b/drivers/iio/dac/cio-dac.c @@ -0,0 +1,144 @@ +/* + * IIO driver for the Measurement Computing CIO-DAC + * Copyright (C) 2016 William Breathitt Gray + * + * 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. + * + * This driver supports the following Measurement Computing devices: CIO-DAC16, + * CIO-DAC06, and PC104-DAC06. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CIO_DAC_NUM_CHAN 16 + +#define CIO_DAC_CHAN(chan) { \ + .type = IIO_VOLTAGE, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, \ + .output = 1 \ +} + +#define CIO_DAC_EXTENT 32 + +static unsigned int base[max_num_isa_dev(CIO_DAC_EXTENT)]; +static unsigned int num_cio_dac; +module_param_array(base, uint, &num_cio_dac, 0); +MODULE_PARM_DESC(base, "Measurement Computing CIO-DAC base addresses"); + +/** + * struct cio_dac_iio - IIO device private data structure + * @chan_out_states: channels' output states + * @base: base port address of the IIO device + */ +struct cio_dac_iio { + int chan_out_states[CIO_DAC_NUM_CHAN]; + unsigned int base; +}; + +static int cio_dac_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long mask) +{ + struct cio_dac_iio *const priv = iio_priv(indio_dev); + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + *val = priv->chan_out_states[chan->channel]; + + return IIO_VAL_INT; +} + +static int cio_dac_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + struct cio_dac_iio *const priv = iio_priv(indio_dev); + const unsigned int chan_addr_offset = 2 * chan->channel; + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + /* DAC can only accept up to a 16-bit value */ + if ((unsigned int)val > 65535) + return -EINVAL; + + priv->chan_out_states[chan->channel] = val; + outw(val, priv->base + chan_addr_offset); + + return 0; +} + +static const struct iio_info cio_dac_info = { + .driver_module = THIS_MODULE, + .read_raw = cio_dac_read_raw, + .write_raw = cio_dac_write_raw +}; + +static const struct iio_chan_spec cio_dac_channels[CIO_DAC_NUM_CHAN] = { + CIO_DAC_CHAN(0), CIO_DAC_CHAN(1), CIO_DAC_CHAN(2), CIO_DAC_CHAN(3), + CIO_DAC_CHAN(4), CIO_DAC_CHAN(5), CIO_DAC_CHAN(6), CIO_DAC_CHAN(7), + CIO_DAC_CHAN(8), CIO_DAC_CHAN(9), CIO_DAC_CHAN(10), CIO_DAC_CHAN(11), + CIO_DAC_CHAN(12), CIO_DAC_CHAN(13), CIO_DAC_CHAN(14), CIO_DAC_CHAN(15) +}; + +static int cio_dac_probe(struct device *dev, unsigned int id) +{ + struct iio_dev *indio_dev; + struct cio_dac_iio *priv; + unsigned int i; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + if (!devm_request_region(dev, base[id], CIO_DAC_EXTENT, + dev_name(dev))) { + dev_err(dev, "Unable to request port addresses (0x%X-0x%X)\n", + base[id], base[id] + CIO_DAC_EXTENT); + return -EBUSY; + } + + indio_dev->info = &cio_dac_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = cio_dac_channels; + indio_dev->num_channels = CIO_DAC_NUM_CHAN; + indio_dev->name = dev_name(dev); + + priv = iio_priv(indio_dev); + priv->base = base[id]; + + /* initialize DAC outputs to 0V */ + for (i = 0; i < 32; i += 2) + outw(0, base[id] + i); + + return devm_iio_device_register(dev, indio_dev); +} + +static struct isa_driver cio_dac_driver = { + .probe = cio_dac_probe, + .driver = { + .name = "cio-dac" + } +}; + +module_isa_driver(cio_dac_driver, num_cio_dac); + +MODULE_AUTHOR("William Breathitt Gray "); +MODULE_DESCRIPTION("Measurement Computing CIO-DAC IIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c index 0a8afdd..1f25f40 100644 --- a/drivers/iio/gyro/ssp_gyro_sensor.c +++ b/drivers/iio/gyro/ssp_gyro_sensor.c @@ -74,7 +74,7 @@ static int ssp_gyro_write_raw(struct iio_dev *indio_dev, return -EINVAL; } -static struct iio_info ssp_gyro_iio_info = { +static const struct iio_info ssp_gyro_iio_info = { .read_raw = &ssp_gyro_read_raw, .write_raw = &ssp_gyro_write_raw, }; diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig index d041243..b17e2e2 100644 --- a/drivers/iio/humidity/Kconfig +++ b/drivers/iio/humidity/Kconfig @@ -28,11 +28,11 @@ config HDC100X tristate "TI HDC100x relative humidity and temperature sensor" depends on I2C help - Say yes here to build support for the TI HDC100x series of - relative humidity and temperature sensors. + Say yes here to build support for the Texas Instruments + HDC1000 and HDC1008 relative humidity and temperature sensors. - To compile this driver as a module, choose M here: the module - will be called hdc100x. + To compile this driver as a module, choose M here: the module + will be called hdc100x. config HTU21 tristate "Measurement Specialties HTU21 humidity & temperature sensor" diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index d2b8899..fc340ed 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1308,7 +1308,7 @@ static void devm_iio_device_release(struct device *dev, void *res) iio_device_free(*(struct iio_dev **)res); } -static int devm_iio_device_match(struct device *dev, void *res, void *data) +int devm_iio_device_match(struct device *dev, void *res, void *data) { struct iio_dev **r = res; if (!r || !*r) { @@ -1317,6 +1317,7 @@ static int devm_iio_device_match(struct device *dev, void *res, void *data) } return *r == data; } +EXPORT_SYMBOL_GPL(devm_iio_device_match); /** * devm_iio_device_alloc - Resource-managed iio_device_alloc() diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index 0ebfc92..90fac8e 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -57,6 +57,11 @@ bool iio_event_enabled(const struct iio_event_interface *ev_int) * * Note: The caller must make sure that this function is not running * concurrently for the same indio_dev more than once. + * + * This function may be safely used as soon as a valid reference to iio_dev has + * been obtained via iio_device_alloc(), but any events that are submitted + * before iio_device_register() has successfully completed will be silently + * discarded. **/ int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp) { @@ -64,6 +69,9 @@ int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp) struct iio_event_data ev; int copied; + if (!ev_int) + return 0; + /* Does anyone care? */ if (iio_event_enabled(ev_int)) { diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 7ad82fd..e1e1048 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -119,6 +119,22 @@ void iio_trigger_unregister(struct iio_trigger *trig_info) } EXPORT_SYMBOL(iio_trigger_unregister); +int iio_trigger_set_immutable(struct iio_dev *indio_dev, struct iio_trigger *trig) +{ + if (!indio_dev || !trig) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + WARN_ON(indio_dev->trig_readonly); + + indio_dev->trig = iio_trigger_get(trig); + indio_dev->trig_readonly = true; + mutex_unlock(&indio_dev->mlock); + + return 0; +} +EXPORT_SYMBOL(iio_trigger_set_immutable); + /* Search for trigger by name, assuming iio_trigger_list_lock held */ static struct iio_trigger *__iio_trigger_find_by_name(const char *name) { @@ -255,6 +271,14 @@ static int iio_trigger_attach_poll_func(struct iio_trigger *trig, goto out_free_irq; } + /* + * Check if we just registered to our own trigger: we determine that + * this is the case if the IIO device and the trigger device share the + * same parent device. + */ + if (pf->indio_dev->dev.parent == trig->dev.parent) + trig->attached_own_device = true; + return ret; out_free_irq: @@ -279,6 +303,8 @@ static int iio_trigger_detach_poll_func(struct iio_trigger *trig, if (ret) return ret; } + if (pf->indio_dev->dev.parent == trig->dev.parent) + trig->attached_own_device = false; iio_trigger_put_irq(trig, pf->irq); free_irq(pf->irq, pf); module_put(pf->indio_dev->info->driver_module); @@ -384,6 +410,10 @@ static ssize_t iio_trigger_write_current(struct device *dev, mutex_unlock(&indio_dev->mlock); return -EBUSY; } + if (indio_dev->trig_readonly) { + mutex_unlock(&indio_dev->mlock); + return -EPERM; + } mutex_unlock(&indio_dev->mlock); trig = iio_trigger_find_by_name(buf, len); @@ -622,6 +652,71 @@ void devm_iio_trigger_free(struct device *dev, struct iio_trigger *iio_trig) } EXPORT_SYMBOL_GPL(devm_iio_trigger_free); +static void devm_iio_trigger_unreg(struct device *dev, void *res) +{ + iio_trigger_unregister(*(struct iio_trigger **)res); +} + +/** + * devm_iio_trigger_register - Resource-managed iio_trigger_register() + * @dev: device this trigger was allocated for + * @trig_info: trigger to register + * + * Managed iio_trigger_register(). The IIO trigger registered with this + * function is automatically unregistered on driver detach. This function + * calls iio_trigger_register() internally. Refer to that function for more + * information. + * + * If an iio_trigger registered with this function needs to be unregistered + * separately, devm_iio_trigger_unregister() must be used. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int devm_iio_trigger_register(struct device *dev, struct iio_trigger *trig_info) +{ + struct iio_trigger **ptr; + int ret; + + ptr = devres_alloc(devm_iio_trigger_unreg, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + *ptr = trig_info; + ret = iio_trigger_register(trig_info); + if (!ret) + devres_add(dev, ptr); + else + devres_free(ptr); + + return ret; +} +EXPORT_SYMBOL_GPL(devm_iio_trigger_register); + +/** + * devm_iio_trigger_unregister - Resource-managed iio_trigger_unregister() + * @dev: device this iio_trigger belongs to + * @trig_info: the trigger associated with the device + * + * Unregister trigger registered with devm_iio_trigger_register(). + */ +void devm_iio_trigger_unregister(struct device *dev, + struct iio_trigger *trig_info) +{ + int rc; + + rc = devres_release(dev, devm_iio_trigger_unreg, devm_iio_trigger_match, + trig_info); + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_iio_trigger_unregister); + +bool iio_trigger_using_own(struct iio_dev *indio_dev) +{ + return indio_dev->trig->attached_own_device; +} +EXPORT_SYMBOL(iio_trigger_using_own); + void iio_device_register_trigger_consumer(struct iio_dev *indio_dev) { indio_dev->groups[indio_dev->groupcounter++] = diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 3574945..ba2e64d 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -267,6 +267,19 @@ config PA12203001 This driver can also be built as a module. If so, the module will be called pa12203001. +config SI1145 + tristate "SI1132 and SI1141/2/3/5/6/7 combined ALS, UV index and proximity sensor" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say Y here if you want to build a driver for the Silicon Labs SI1132 or + SI1141/2/3/5/6/7 combined ambient light, UV index and proximity sensor + chips. + + To compile this driver as a module, choose M here: the module will be + called si1145. + config STK3310 tristate "STK3310 ALS and proximity sensor" depends on I2C @@ -334,11 +347,11 @@ config US5182D will be called us5182d. config VCNL4000 - tristate "VCNL4000 combined ALS and proximity sensor" + tristate "VCNL4000/4010/4020 combined ALS and proximity sensor" depends on I2C help - Say Y here if you want to build a driver for the Vishay VCNL4000 - combined ambient light and proximity sensor. + Say Y here if you want to build a driver for the Vishay VCNL4000, + VCNL4010, VCNL4020 combined ambient light and proximity sensor. To compile this driver as a module, choose M here: the module will be called vcnl4000. diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 6f2a3c6..c5768df 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_OPT3001) += opt3001.o obj-$(CONFIG_PA12203001) += pa12203001.o obj-$(CONFIG_RPR0521) += rpr0521.o obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o +obj-$(CONFIG_SI1145) += si1145.o obj-$(CONFIG_STK3310) += stk3310.o obj-$(CONFIG_TCS3414) += tcs3414.o obj-$(CONFIG_TCS3472) += tcs3472.o diff --git b/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c new file mode 100644 index 0000000..096034c --- /dev/null +++ b/drivers/iio/light/si1145.c @@ -0,0 +1,1404 @@ +/* + * si1145.c - Support for Silabs SI1132 and SI1141/2/3/5/6/7 combined ambient + * light, UV index and proximity sensors + * + * Copyright 2014-16 Peter Meerwald-Stadler + * Copyright 2016 Crestez Dan Leonard + * + * 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. + * + * SI1132 (7-bit I2C slave address 0x60) + * SI1141/2/3 (7-bit I2C slave address 0x5a) + * SI1145/6/6 (7-bit I2C slave address 0x60) + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define SI1145_REG_PART_ID 0x00 +#define SI1145_REG_REV_ID 0x01 +#define SI1145_REG_SEQ_ID 0x02 +#define SI1145_REG_INT_CFG 0x03 +#define SI1145_REG_IRQ_ENABLE 0x04 +#define SI1145_REG_IRQ_MODE 0x05 +#define SI1145_REG_HW_KEY 0x07 +#define SI1145_REG_MEAS_RATE 0x08 +#define SI1145_REG_PS_LED21 0x0f +#define SI1145_REG_PS_LED3 0x10 +#define SI1145_REG_UCOEF1 0x13 +#define SI1145_REG_UCOEF2 0x14 +#define SI1145_REG_UCOEF3 0x15 +#define SI1145_REG_UCOEF4 0x16 +#define SI1145_REG_PARAM_WR 0x17 +#define SI1145_REG_COMMAND 0x18 +#define SI1145_REG_RESPONSE 0x20 +#define SI1145_REG_IRQ_STATUS 0x21 +#define SI1145_REG_ALSVIS_DATA 0x22 +#define SI1145_REG_ALSIR_DATA 0x24 +#define SI1145_REG_PS1_DATA 0x26 +#define SI1145_REG_PS2_DATA 0x28 +#define SI1145_REG_PS3_DATA 0x2a +#define SI1145_REG_AUX_DATA 0x2c +#define SI1145_REG_PARAM_RD 0x2e +#define SI1145_REG_CHIP_STAT 0x30 + +#define SI1145_UCOEF1_DEFAULT 0x7b +#define SI1145_UCOEF2_DEFAULT 0x6b +#define SI1145_UCOEF3_DEFAULT 0x01 +#define SI1145_UCOEF4_DEFAULT 0x00 + +/* Helper to figure out PS_LED register / shift per channel */ +#define SI1145_PS_LED_REG(ch) \ + (((ch) == 2) ? SI1145_REG_PS_LED3 : SI1145_REG_PS_LED21) +#define SI1145_PS_LED_SHIFT(ch) \ + (((ch) == 1) ? 4 : 0) + +/* Parameter offsets */ +#define SI1145_PARAM_CHLIST 0x01 +#define SI1145_PARAM_PSLED12_SELECT 0x02 +#define SI1145_PARAM_PSLED3_SELECT 0x03 +#define SI1145_PARAM_PS_ENCODING 0x05 +#define SI1145_PARAM_ALS_ENCODING 0x06 +#define SI1145_PARAM_PS1_ADC_MUX 0x07 +#define SI1145_PARAM_PS2_ADC_MUX 0x08 +#define SI1145_PARAM_PS3_ADC_MUX 0x09 +#define SI1145_PARAM_PS_ADC_COUNTER 0x0a +#define SI1145_PARAM_PS_ADC_GAIN 0x0b +#define SI1145_PARAM_PS_ADC_MISC 0x0c +#define SI1145_PARAM_ALS_ADC_MUX 0x0d +#define SI1145_PARAM_ALSIR_ADC_MUX 0x0e +#define SI1145_PARAM_AUX_ADC_MUX 0x0f +#define SI1145_PARAM_ALSVIS_ADC_COUNTER 0x10 +#define SI1145_PARAM_ALSVIS_ADC_GAIN 0x11 +#define SI1145_PARAM_ALSVIS_ADC_MISC 0x12 +#define SI1145_PARAM_LED_RECOVERY 0x1c +#define SI1145_PARAM_ALSIR_ADC_COUNTER 0x1d +#define SI1145_PARAM_ALSIR_ADC_GAIN 0x1e +#define SI1145_PARAM_ALSIR_ADC_MISC 0x1f +#define SI1145_PARAM_ADC_OFFSET 0x1a + +/* Channel enable masks for CHLIST parameter */ +#define SI1145_CHLIST_EN_PS1 BIT(0) +#define SI1145_CHLIST_EN_PS2 BIT(1) +#define SI1145_CHLIST_EN_PS3 BIT(2) +#define SI1145_CHLIST_EN_ALSVIS BIT(4) +#define SI1145_CHLIST_EN_ALSIR BIT(5) +#define SI1145_CHLIST_EN_AUX BIT(6) +#define SI1145_CHLIST_EN_UV BIT(7) + +/* Proximity measurement mode for ADC_MISC parameter */ +#define SI1145_PS_ADC_MODE_NORMAL BIT(2) +/* Signal range mask for ADC_MISC parameter */ +#define SI1145_ADC_MISC_RANGE BIT(5) + +/* Commands for REG_COMMAND */ +#define SI1145_CMD_NOP 0x00 +#define SI1145_CMD_RESET 0x01 +#define SI1145_CMD_PS_FORCE 0x05 +#define SI1145_CMD_ALS_FORCE 0x06 +#define SI1145_CMD_PSALS_FORCE 0x07 +#define SI1145_CMD_PS_PAUSE 0x09 +#define SI1145_CMD_ALS_PAUSE 0x0a +#define SI1145_CMD_PSALS_PAUSE 0x0b +#define SI1145_CMD_PS_AUTO 0x0d +#define SI1145_CMD_ALS_AUTO 0x0e +#define SI1145_CMD_PSALS_AUTO 0x0f +#define SI1145_CMD_PARAM_QUERY 0x80 +#define SI1145_CMD_PARAM_SET 0xa0 + +#define SI1145_RSP_INVALID_SETTING 0x80 +#define SI1145_RSP_COUNTER_MASK 0x0F + +/* Minimum sleep after each command to ensure it's received */ +#define SI1145_COMMAND_MINSLEEP_MS 5 +/* Return -ETIMEDOUT after this long */ +#define SI1145_COMMAND_TIMEOUT_MS 25 + +/* Interrupt configuration masks for INT_CFG register */ +#define SI1145_INT_CFG_OE BIT(0) /* enable interrupt */ +#define SI1145_INT_CFG_MODE BIT(1) /* auto reset interrupt pin */ + +/* Interrupt enable masks for IRQ_ENABLE register */ +#define SI1145_MASK_ALL_IE (BIT(4) | BIT(3) | BIT(2) | BIT(0)) + +#define SI1145_MUX_TEMP 0x65 +#define SI1145_MUX_VDD 0x75 + +/* Proximity LED current; see Table 2 in datasheet */ +#define SI1145_LED_CURRENT_45mA 0x04 + +enum { + SI1132, + SI1141, + SI1142, + SI1143, + SI1145, + SI1146, + SI1147, +}; + +struct si1145_part_info { + u8 part; + const struct iio_info *iio_info; + const struct iio_chan_spec *channels; + unsigned int num_channels; + unsigned int num_leds; + bool uncompressed_meas_rate; +}; + +/** + * struct si1145_data - si1145 chip state data + * @client: I2C client + * @lock: mutex to protect shared state. + * @cmdlock: Low-level mutex to protect command execution only + * @rsp_seq: Next expected response number or -1 if counter reset required + * @scan_mask: Saved scan mask to avoid duplicate set_chlist + * @autonomous: If automatic measurements are active (for buffer support) + * @part_info: Part information + * @trig: Pointer to iio trigger + * @meas_rate: Value of MEAS_RATE register. Only set in HW in auto mode + */ +struct si1145_data { + struct i2c_client *client; + struct mutex lock; + struct mutex cmdlock; + int rsp_seq; + const struct si1145_part_info *part_info; + unsigned long scan_mask; + bool autonomous; + struct iio_trigger *trig; + int meas_rate; +}; + +/** + * __si1145_command_reset() - Send CMD_NOP and wait for response 0 + * + * Does not modify data->rsp_seq + * + * Return: 0 on success and -errno on error. + */ +static int __si1145_command_reset(struct si1145_data *data) +{ + struct device *dev = &data->client->dev; + unsigned long stop_jiffies; + int ret; + + ret = i2c_smbus_write_byte_data(data->client, SI1145_REG_COMMAND, + SI1145_CMD_NOP); + if (ret < 0) + return ret; + msleep(SI1145_COMMAND_MINSLEEP_MS); + + stop_jiffies = jiffies + SI1145_COMMAND_TIMEOUT_MS * HZ / 1000; + while (true) { + ret = i2c_smbus_read_byte_data(data->client, + SI1145_REG_RESPONSE); + if (ret <= 0) + return ret; + if (time_after(jiffies, stop_jiffies)) { + dev_warn(dev, "timeout on reset\n"); + return -ETIMEDOUT; + } + msleep(SI1145_COMMAND_MINSLEEP_MS); + continue; + } +} + +/** + * si1145_command() - Execute a command and poll the response register + * + * All conversion overflows are reported as -EOVERFLOW + * INVALID_SETTING is reported as -EINVAL + * Timeouts are reported as -ETIMEDOUT + * + * Return: 0 on success or -errno on failure + */ +static int si1145_command(struct si1145_data *data, u8 cmd) +{ + struct device *dev = &data->client->dev; + unsigned long stop_jiffies; + int ret; + + mutex_lock(&data->cmdlock); + + if (data->rsp_seq < 0) { + ret = __si1145_command_reset(data); + if (ret < 0) { + dev_err(dev, "failed to reset command counter, ret=%d\n", + ret); + goto out; + } + data->rsp_seq = 0; + } + + ret = i2c_smbus_write_byte_data(data->client, SI1145_REG_COMMAND, cmd); + if (ret) { + dev_warn(dev, "failed to write command, ret=%d\n", ret); + goto out; + } + /* Sleep a little to ensure the command is received */ + msleep(SI1145_COMMAND_MINSLEEP_MS); + + stop_jiffies = jiffies + SI1145_COMMAND_TIMEOUT_MS * HZ / 1000; + while (true) { + ret = i2c_smbus_read_byte_data(data->client, + SI1145_REG_RESPONSE); + if (ret < 0) { + dev_warn(dev, "failed to read response, ret=%d\n", ret); + break; + } + + if ((ret & ~SI1145_RSP_COUNTER_MASK) == 0) { + if (ret == data->rsp_seq) { + if (time_after(jiffies, stop_jiffies)) { + dev_warn(dev, "timeout on command %#02hhx\n", + cmd); + ret = -ETIMEDOUT; + break; + } + msleep(SI1145_COMMAND_MINSLEEP_MS); + continue; + } + if (ret == ((data->rsp_seq + 1) & + SI1145_RSP_COUNTER_MASK)) { + data->rsp_seq = ret; + ret = 0; + break; + } + dev_warn(dev, "unexpected response counter %d instead of %d\n", + ret, (data->rsp_seq + 1) & + SI1145_RSP_COUNTER_MASK); + ret = -EIO; + } else { + if (ret == SI1145_RSP_INVALID_SETTING) { + dev_warn(dev, "INVALID_SETTING error on command %#02hhx\n", + cmd); + ret = -EINVAL; + } else { + /* All overflows are treated identically */ + dev_dbg(dev, "overflow, ret=%d, cmd=%#02hhx\n", + ret, cmd); + ret = -EOVERFLOW; + } + } + + /* Force a counter reset next time */ + data->rsp_seq = -1; + break; + } + +out: + mutex_unlock(&data->cmdlock); + + return ret; +} + +static int si1145_param_update(struct si1145_data *data, u8 op, u8 param, + u8 value) +{ + int ret; + + ret = i2c_smbus_write_byte_data(data->client, + SI1145_REG_PARAM_WR, value); + if (ret < 0) + return ret; + + return si1145_command(data, op | (param & 0x1F)); +} + +static int si1145_param_set(struct si1145_data *data, u8 param, u8 value) +{ + return si1145_param_update(data, SI1145_CMD_PARAM_SET, param, value); +} + +/* Set param. Returns negative errno or current value */ +static int si1145_param_query(struct si1145_data *data, u8 param) +{ + int ret; + + ret = si1145_command(data, SI1145_CMD_PARAM_QUERY | (param & 0x1F)); + if (ret < 0) + return ret; + + return i2c_smbus_read_byte_data(data->client, SI1145_REG_PARAM_RD); +} + +/* Expand 8 bit compressed value to 16 bit, see Silabs AN498 */ +static u16 si1145_uncompress(u8 x) +{ + u16 result = 0; + u8 exponent = 0; + + if (x < 8) + return 0; + + exponent = (x & 0xf0) >> 4; + result = 0x10 | (x & 0x0f); + + if (exponent >= 4) + return result << (exponent - 4); + return result >> (4 - exponent); +} + +/* Compress 16 bit value to 8 bit, see Silabs AN498 */ +static u8 si1145_compress(u16 x) +{ + u32 exponent = 0; + u32 significand = 0; + u32 tmp = x; + + if (x == 0x0000) + return 0x00; + if (x == 0x0001) + return 0x08; + + while (1) { + tmp >>= 1; + exponent += 1; + if (tmp == 1) + break; + } + + if (exponent < 5) { + significand = x << (4 - exponent); + return (exponent << 4) | (significand & 0xF); + } + + significand = x >> (exponent - 5); + if (significand & 1) { + significand += 2; + if (significand & 0x0040) { + exponent += 1; + significand >>= 1; + } + } + + return (exponent << 4) | ((significand >> 1) & 0xF); +} + +/* Write meas_rate in hardware */ +static int si1145_set_meas_rate(struct si1145_data *data, int interval) +{ + if (data->part_info->uncompressed_meas_rate) + return i2c_smbus_write_word_data(data->client, + SI1145_REG_MEAS_RATE, interval); + else + return i2c_smbus_write_byte_data(data->client, + SI1145_REG_MEAS_RATE, interval); +} + +static int si1145_read_samp_freq(struct si1145_data *data, int *val, int *val2) +{ + *val = 32000; + if (data->part_info->uncompressed_meas_rate) + *val2 = data->meas_rate; + else + *val2 = si1145_uncompress(data->meas_rate); + return IIO_VAL_FRACTIONAL; +} + +/* Set the samp freq in driver private data */ +static int si1145_store_samp_freq(struct si1145_data *data, int val) +{ + int ret = 0; + int meas_rate; + + if (val <= 0 || val > 32000) + return -ERANGE; + meas_rate = 32000 / val; + + mutex_lock(&data->lock); + if (data->autonomous) { + ret = si1145_set_meas_rate(data, meas_rate); + if (ret) + goto out; + } + if (data->part_info->uncompressed_meas_rate) + data->meas_rate = meas_rate; + else + data->meas_rate = si1145_compress(meas_rate); + +out: + mutex_unlock(&data->lock); + + return ret; +} + +static irqreturn_t si1145_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct si1145_data *data = iio_priv(indio_dev); + /* + * Maximum buffer size: + * 6*2 bytes channels data + 4 bytes alignment + + * 8 bytes timestamp + */ + u8 buffer[24]; + int i, j = 0; + int ret; + u8 irq_status = 0; + + if (!data->autonomous) { + ret = si1145_command(data, SI1145_CMD_PSALS_FORCE); + if (ret < 0 && ret != -EOVERFLOW) + goto done; + } else { + irq_status = ret = i2c_smbus_read_byte_data(data->client, + SI1145_REG_IRQ_STATUS); + if (ret < 0) + goto done; + if (!(irq_status & SI1145_MASK_ALL_IE)) + goto done; + } + + for_each_set_bit(i, indio_dev->active_scan_mask, + indio_dev->masklength) { + int run = 1; + + while (i + run < indio_dev->masklength) { + if (!test_bit(i + run, indio_dev->active_scan_mask)) + break; + if (indio_dev->channels[i + run].address != + indio_dev->channels[i].address + 2 * run) + break; + run++; + } + + ret = i2c_smbus_read_i2c_block_data_or_emulated( + data->client, indio_dev->channels[i].address, + sizeof(u16) * run, &buffer[j]); + if (ret < 0) + goto done; + j += run * sizeof(u16); + i += run - 1; + } + + if (data->autonomous) { + ret = i2c_smbus_write_byte_data(data->client, + SI1145_REG_IRQ_STATUS, + irq_status & SI1145_MASK_ALL_IE); + if (ret < 0) + goto done; + } + + iio_push_to_buffers_with_timestamp(indio_dev, buffer, + iio_get_time_ns(indio_dev)); + +done: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int si1145_set_chlist(struct iio_dev *indio_dev, unsigned long scan_mask) +{ + struct si1145_data *data = iio_priv(indio_dev); + u8 reg = 0, mux; + int ret; + int i; + + /* channel list already set, no need to reprogram */ + if (data->scan_mask == scan_mask) + return 0; + + for_each_set_bit(i, &scan_mask, indio_dev->masklength) { + switch (indio_dev->channels[i].address) { + case SI1145_REG_ALSVIS_DATA: + reg |= SI1145_CHLIST_EN_ALSVIS; + break; + case SI1145_REG_ALSIR_DATA: + reg |= SI1145_CHLIST_EN_ALSIR; + break; + case SI1145_REG_PS1_DATA: + reg |= SI1145_CHLIST_EN_PS1; + break; + case SI1145_REG_PS2_DATA: + reg |= SI1145_CHLIST_EN_PS2; + break; + case SI1145_REG_PS3_DATA: + reg |= SI1145_CHLIST_EN_PS3; + break; + case SI1145_REG_AUX_DATA: + switch (indio_dev->channels[i].type) { + case IIO_UVINDEX: + reg |= SI1145_CHLIST_EN_UV; + break; + default: + reg |= SI1145_CHLIST_EN_AUX; + if (indio_dev->channels[i].type == IIO_TEMP) + mux = SI1145_MUX_TEMP; + else + mux = SI1145_MUX_VDD; + ret = si1145_param_set(data, + SI1145_PARAM_AUX_ADC_MUX, mux); + if (ret < 0) + return ret; + + break; + } + } + } + + data->scan_mask = scan_mask; + ret = si1145_param_set(data, SI1145_PARAM_CHLIST, reg); + + return ret < 0 ? ret : 0; +} + +static int si1145_measure(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct si1145_data *data = iio_priv(indio_dev); + u8 cmd; + int ret; + + ret = si1145_set_chlist(indio_dev, BIT(chan->scan_index)); + if (ret < 0) + return ret; + + cmd = (chan->type == IIO_PROXIMITY) ? SI1145_CMD_PS_FORCE : + SI1145_CMD_ALS_FORCE; + ret = si1145_command(data, cmd); + if (ret < 0 && ret != -EOVERFLOW) + return ret; + + return i2c_smbus_read_word_data(data->client, chan->address); +} + +/* + * Conversion between iio scale and ADC_GAIN values + * These could be further adjusted but proximity/intensity are dimensionless + */ +static const int si1145_proximity_scale_available[] = { + 128, 64, 32, 16, 8, 4}; +static const int si1145_intensity_scale_available[] = { + 128, 64, 32, 16, 8, 4, 2, 1}; +static IIO_CONST_ATTR(in_proximity_scale_available, + "128 64 32 16 8 4"); +static IIO_CONST_ATTR(in_intensity_scale_available, + "128 64 32 16 8 4 2 1"); +static IIO_CONST_ATTR(in_intensity_ir_scale_available, + "128 64 32 16 8 4 2 1"); + +static int si1145_scale_from_adcgain(int regval) +{ + return 128 >> regval; +} + +static int si1145_proximity_adcgain_from_scale(int val, int val2) +{ + val = find_closest_descending(val, si1145_proximity_scale_available, + ARRAY_SIZE(si1145_proximity_scale_available)); + if (val < 0 || val > 5 || val2 != 0) + return -EINVAL; + + return val; +} + +static int si1145_intensity_adcgain_from_scale(int val, int val2) +{ + val = find_closest_descending(val, si1145_intensity_scale_available, + ARRAY_SIZE(si1145_intensity_scale_available)); + if (val < 0 || val > 7 || val2 != 0) + return -EINVAL; + + return val; +} + +static int si1145_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct si1145_data *data = iio_priv(indio_dev); + int ret; + u8 reg; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_INTENSITY: + case IIO_PROXIMITY: + case IIO_VOLTAGE: + case IIO_TEMP: + case IIO_UVINDEX: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = si1145_measure(indio_dev, chan); + iio_device_release_direct_mode(indio_dev); + + if (ret < 0) + return ret; + + *val = ret; + + return IIO_VAL_INT; + case IIO_CURRENT: + ret = i2c_smbus_read_byte_data(data->client, + SI1145_PS_LED_REG(chan->channel)); + if (ret < 0) + return ret; + + *val = (ret >> SI1145_PS_LED_SHIFT(chan->channel)) + & 0x0f; + + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_PROXIMITY: + reg = SI1145_PARAM_PS_ADC_GAIN; + break; + case IIO_INTENSITY: + if (chan->channel2 == IIO_MOD_LIGHT_IR) + reg = SI1145_PARAM_ALSIR_ADC_GAIN; + else + reg = SI1145_PARAM_ALSVIS_ADC_GAIN; + break; + case IIO_TEMP: + *val = 28; + *val2 = 571429; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_UVINDEX: + *val = 0; + *val2 = 10000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + + ret = si1145_param_query(data, reg); + if (ret < 0) + return ret; + + *val = si1145_scale_from_adcgain(ret & 0x07); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + /* + * -ADC offset - ADC counts @ 25°C - + * 35 * ADC counts / °C + */ + *val = -256 - 11136 + 25 * 35; + return IIO_VAL_INT; + default: + /* + * All ADC measurements have are by default offset + * by -256 + * See AN498 5.6.3 + */ + ret = si1145_param_query(data, SI1145_PARAM_ADC_OFFSET); + if (ret < 0) + return ret; + *val = -si1145_uncompress(ret); + return IIO_VAL_INT; + } + case IIO_CHAN_INFO_SAMP_FREQ: + return si1145_read_samp_freq(data, val, val2); + default: + return -EINVAL; + } +} + +static int si1145_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct si1145_data *data = iio_priv(indio_dev); + u8 reg1, reg2, shift; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_PROXIMITY: + val = si1145_proximity_adcgain_from_scale(val, val2); + if (val < 0) + return val; + reg1 = SI1145_PARAM_PS_ADC_GAIN; + reg2 = SI1145_PARAM_PS_ADC_COUNTER; + break; + case IIO_INTENSITY: + val = si1145_intensity_adcgain_from_scale(val, val2); + if (val < 0) + return val; + if (chan->channel2 == IIO_MOD_LIGHT_IR) { + reg1 = SI1145_PARAM_ALSIR_ADC_GAIN; + reg2 = SI1145_PARAM_ALSIR_ADC_COUNTER; + } else { + reg1 = SI1145_PARAM_ALSVIS_ADC_GAIN; + reg2 = SI1145_PARAM_ALSVIS_ADC_COUNTER; + } + break; + default: + return -EINVAL; + } + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = si1145_param_set(data, reg1, val); + if (ret < 0) { + iio_device_release_direct_mode(indio_dev); + return ret; + } + /* Set recovery period to one's complement of gain */ + ret = si1145_param_set(data, reg2, (~val & 0x07) << 4); + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_RAW: + if (chan->type != IIO_CURRENT) + return -EINVAL; + + if (val < 0 || val > 15 || val2 != 0) + return -EINVAL; + + reg1 = SI1145_PS_LED_REG(chan->channel); + shift = SI1145_PS_LED_SHIFT(chan->channel); + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = i2c_smbus_read_byte_data(data->client, reg1); + if (ret < 0) { + iio_device_release_direct_mode(indio_dev); + return ret; + } + ret = i2c_smbus_write_byte_data(data->client, reg1, + (ret & ~(0x0f << shift)) | + ((val & 0x0f) << shift)); + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + return si1145_store_samp_freq(data, val); + default: + return -EINVAL; + } +} + +#define SI1145_ST { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ +} + +#define SI1145_INTENSITY_CHANNEL(_si) { \ + .type = IIO_INTENSITY, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_type = SI1145_ST, \ + .scan_index = _si, \ + .address = SI1145_REG_ALSVIS_DATA, \ +} + +#define SI1145_INTENSITY_IR_CHANNEL(_si) { \ + .type = IIO_INTENSITY, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .modified = 1, \ + .channel2 = IIO_MOD_LIGHT_IR, \ + .scan_type = SI1145_ST, \ + .scan_index = _si, \ + .address = SI1145_REG_ALSIR_DATA, \ +} + +#define SI1145_TEMP_CHANNEL(_si) { \ + .type = IIO_TEMP, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_type = SI1145_ST, \ + .scan_index = _si, \ + .address = SI1145_REG_AUX_DATA, \ +} + +#define SI1145_UV_CHANNEL(_si) { \ + .type = IIO_UVINDEX, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_type = SI1145_ST, \ + .scan_index = _si, \ + .address = SI1145_REG_AUX_DATA, \ +} + +#define SI1145_PROXIMITY_CHANNEL(_si, _ch) { \ + .type = IIO_PROXIMITY, \ + .indexed = 1, \ + .channel = _ch, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_type = SI1145_ST, \ + .scan_index = _si, \ + .address = SI1145_REG_PS1_DATA + _ch * 2, \ +} + +#define SI1145_VOLTAGE_CHANNEL(_si) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_type = SI1145_ST, \ + .scan_index = _si, \ + .address = SI1145_REG_AUX_DATA, \ +} + +#define SI1145_CURRENT_CHANNEL(_ch) { \ + .type = IIO_CURRENT, \ + .indexed = 1, \ + .channel = _ch, \ + .output = 1, \ + .scan_index = -1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ +} + +static const struct iio_chan_spec si1132_channels[] = { + SI1145_INTENSITY_CHANNEL(0), + SI1145_INTENSITY_IR_CHANNEL(1), + SI1145_TEMP_CHANNEL(2), + SI1145_VOLTAGE_CHANNEL(3), + SI1145_UV_CHANNEL(4), + IIO_CHAN_SOFT_TIMESTAMP(6), +}; + +static const struct iio_chan_spec si1141_channels[] = { + SI1145_INTENSITY_CHANNEL(0), + SI1145_INTENSITY_IR_CHANNEL(1), + SI1145_PROXIMITY_CHANNEL(2, 0), + SI1145_TEMP_CHANNEL(3), + SI1145_VOLTAGE_CHANNEL(4), + IIO_CHAN_SOFT_TIMESTAMP(5), + SI1145_CURRENT_CHANNEL(0), +}; + +static const struct iio_chan_spec si1142_channels[] = { + SI1145_INTENSITY_CHANNEL(0), + SI1145_INTENSITY_IR_CHANNEL(1), + SI1145_PROXIMITY_CHANNEL(2, 0), + SI1145_PROXIMITY_CHANNEL(3, 1), + SI1145_TEMP_CHANNEL(4), + SI1145_VOLTAGE_CHANNEL(5), + IIO_CHAN_SOFT_TIMESTAMP(6), + SI1145_CURRENT_CHANNEL(0), + SI1145_CURRENT_CHANNEL(1), +}; + +static const struct iio_chan_spec si1143_channels[] = { + SI1145_INTENSITY_CHANNEL(0), + SI1145_INTENSITY_IR_CHANNEL(1), + SI1145_PROXIMITY_CHANNEL(2, 0), + SI1145_PROXIMITY_CHANNEL(3, 1), + SI1145_PROXIMITY_CHANNEL(4, 2), + SI1145_TEMP_CHANNEL(5), + SI1145_VOLTAGE_CHANNEL(6), + IIO_CHAN_SOFT_TIMESTAMP(7), + SI1145_CURRENT_CHANNEL(0), + SI1145_CURRENT_CHANNEL(1), + SI1145_CURRENT_CHANNEL(2), +}; + +static const struct iio_chan_spec si1145_channels[] = { + SI1145_INTENSITY_CHANNEL(0), + SI1145_INTENSITY_IR_CHANNEL(1), + SI1145_PROXIMITY_CHANNEL(2, 0), + SI1145_TEMP_CHANNEL(3), + SI1145_VOLTAGE_CHANNEL(4), + SI1145_UV_CHANNEL(5), + IIO_CHAN_SOFT_TIMESTAMP(6), + SI1145_CURRENT_CHANNEL(0), +}; + +static const struct iio_chan_spec si1146_channels[] = { + SI1145_INTENSITY_CHANNEL(0), + SI1145_INTENSITY_IR_CHANNEL(1), + SI1145_TEMP_CHANNEL(2), + SI1145_VOLTAGE_CHANNEL(3), + SI1145_UV_CHANNEL(4), + SI1145_PROXIMITY_CHANNEL(5, 0), + SI1145_PROXIMITY_CHANNEL(6, 1), + IIO_CHAN_SOFT_TIMESTAMP(7), + SI1145_CURRENT_CHANNEL(0), + SI1145_CURRENT_CHANNEL(1), +}; + +static const struct iio_chan_spec si1147_channels[] = { + SI1145_INTENSITY_CHANNEL(0), + SI1145_INTENSITY_IR_CHANNEL(1), + SI1145_PROXIMITY_CHANNEL(2, 0), + SI1145_PROXIMITY_CHANNEL(3, 1), + SI1145_PROXIMITY_CHANNEL(4, 2), + SI1145_TEMP_CHANNEL(5), + SI1145_VOLTAGE_CHANNEL(6), + SI1145_UV_CHANNEL(7), + IIO_CHAN_SOFT_TIMESTAMP(8), + SI1145_CURRENT_CHANNEL(0), + SI1145_CURRENT_CHANNEL(1), + SI1145_CURRENT_CHANNEL(2), +}; + +static struct attribute *si1132_attributes[] = { + &iio_const_attr_in_intensity_scale_available.dev_attr.attr, + &iio_const_attr_in_intensity_ir_scale_available.dev_attr.attr, + NULL, +}; + +static struct attribute *si114x_attributes[] = { + &iio_const_attr_in_intensity_scale_available.dev_attr.attr, + &iio_const_attr_in_intensity_ir_scale_available.dev_attr.attr, + &iio_const_attr_in_proximity_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group si1132_attribute_group = { + .attrs = si1132_attributes, +}; + +static const struct attribute_group si114x_attribute_group = { + .attrs = si114x_attributes, +}; + + +static const struct iio_info si1132_info = { + .read_raw = si1145_read_raw, + .write_raw = si1145_write_raw, + .driver_module = THIS_MODULE, + .attrs = &si1132_attribute_group, +}; + +static const struct iio_info si114x_info = { + .read_raw = si1145_read_raw, + .write_raw = si1145_write_raw, + .driver_module = THIS_MODULE, + .attrs = &si114x_attribute_group, +}; + +#define SI1145_PART(id, iio_info, chans, leds, uncompressed_meas_rate) \ + {id, iio_info, chans, ARRAY_SIZE(chans), leds, uncompressed_meas_rate} + +static const struct si1145_part_info si1145_part_info[] = { + [SI1132] = SI1145_PART(0x32, &si1132_info, si1132_channels, 0, true), + [SI1141] = SI1145_PART(0x41, &si114x_info, si1141_channels, 1, false), + [SI1142] = SI1145_PART(0x42, &si114x_info, si1142_channels, 2, false), + [SI1143] = SI1145_PART(0x43, &si114x_info, si1143_channels, 3, false), + [SI1145] = SI1145_PART(0x45, &si114x_info, si1145_channels, 1, true), + [SI1146] = SI1145_PART(0x46, &si114x_info, si1146_channels, 2, true), + [SI1147] = SI1145_PART(0x47, &si114x_info, si1147_channels, 3, true), +}; + +static int si1145_initialize(struct si1145_data *data) +{ + struct i2c_client *client = data->client; + int ret; + + ret = i2c_smbus_write_byte_data(client, SI1145_REG_COMMAND, + SI1145_CMD_RESET); + if (ret < 0) + return ret; + msleep(SI1145_COMMAND_TIMEOUT_MS); + + /* Hardware key, magic value */ + ret = i2c_smbus_write_byte_data(client, SI1145_REG_HW_KEY, 0x17); + if (ret < 0) + return ret; + msleep(SI1145_COMMAND_TIMEOUT_MS); + + /* Turn off autonomous mode */ + ret = si1145_set_meas_rate(data, 0); + if (ret < 0) + return ret; + + /* Initialize sampling freq to 10 Hz */ + ret = si1145_store_samp_freq(data, 10); + if (ret < 0) + return ret; + + /* Set LED currents to 45 mA; have 4 bits, see Table 2 in datasheet */ + switch (data->part_info->num_leds) { + case 3: + ret = i2c_smbus_write_byte_data(client, + SI1145_REG_PS_LED3, + SI1145_LED_CURRENT_45mA); + if (ret < 0) + return ret; + /* fallthrough */ + case 2: + ret = i2c_smbus_write_byte_data(client, + SI1145_REG_PS_LED21, + (SI1145_LED_CURRENT_45mA << 4) | + SI1145_LED_CURRENT_45mA); + break; + case 1: + ret = i2c_smbus_write_byte_data(client, + SI1145_REG_PS_LED21, + SI1145_LED_CURRENT_45mA); + break; + default: + ret = 0; + break; + } + if (ret < 0) + return ret; + + /* Set normal proximity measurement mode */ + ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_MISC, + SI1145_PS_ADC_MODE_NORMAL); + if (ret < 0) + return ret; + + ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_GAIN, 0x01); + if (ret < 0) + return ret; + + /* ADC_COUNTER should be one complement of ADC_GAIN */ + ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_COUNTER, 0x06 << 4); + if (ret < 0) + return ret; + + /* Set ALS visible measurement mode */ + ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_MISC, + SI1145_ADC_MISC_RANGE); + if (ret < 0) + return ret; + + ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_GAIN, 0x03); + if (ret < 0) + return ret; + + ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_COUNTER, + 0x04 << 4); + if (ret < 0) + return ret; + + /* Set ALS IR measurement mode */ + ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_MISC, + SI1145_ADC_MISC_RANGE); + if (ret < 0) + return ret; + + ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_GAIN, 0x01); + if (ret < 0) + return ret; + + ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_COUNTER, + 0x06 << 4); + if (ret < 0) + return ret; + + /* + * Initialize UCOEF to default values in datasheet + * These registers are normally zero on reset + */ + if (data->part_info == &si1145_part_info[SI1132] || + data->part_info == &si1145_part_info[SI1145] || + data->part_info == &si1145_part_info[SI1146] || + data->part_info == &si1145_part_info[SI1147]) { + ret = i2c_smbus_write_byte_data(data->client, + SI1145_REG_UCOEF1, + SI1145_UCOEF1_DEFAULT); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(data->client, + SI1145_REG_UCOEF2, SI1145_UCOEF2_DEFAULT); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(data->client, + SI1145_REG_UCOEF3, SI1145_UCOEF3_DEFAULT); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(data->client, + SI1145_REG_UCOEF4, SI1145_UCOEF4_DEFAULT); + if (ret < 0) + return ret; + } + + return 0; +} + +/* + * Program the channels we want to measure with CMD_PSALS_AUTO. No need for + * _postdisable as we stop with CMD_PSALS_PAUSE; single measurement (direct) + * mode reprograms the channels list anyway... + */ +static int si1145_buffer_preenable(struct iio_dev *indio_dev) +{ + struct si1145_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + ret = si1145_set_chlist(indio_dev, *indio_dev->active_scan_mask); + mutex_unlock(&data->lock); + + return ret; +} + +static bool si1145_validate_scan_mask(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct si1145_data *data = iio_priv(indio_dev); + unsigned int count = 0; + int i; + + /* Check that at most one AUX channel is enabled */ + for_each_set_bit(i, scan_mask, data->part_info->num_channels) { + if (indio_dev->channels[i].address == SI1145_REG_AUX_DATA) + count++; + } + + return count <= 1; +} + +static const struct iio_buffer_setup_ops si1145_buffer_setup_ops = { + .preenable = si1145_buffer_preenable, + .postenable = iio_triggered_buffer_postenable, + .predisable = iio_triggered_buffer_predisable, + .validate_scan_mask = si1145_validate_scan_mask, +}; + +/** + * si1145_trigger_set_state() - Set trigger state + * + * When not using triggers interrupts are disabled and measurement rate is + * set to zero in order to minimize power consumption. + */ +static int si1145_trigger_set_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct si1145_data *data = iio_priv(indio_dev); + int err = 0, ret; + + mutex_lock(&data->lock); + + if (state) { + data->autonomous = true; + err = i2c_smbus_write_byte_data(data->client, + SI1145_REG_INT_CFG, SI1145_INT_CFG_OE); + if (err < 0) + goto disable; + err = i2c_smbus_write_byte_data(data->client, + SI1145_REG_IRQ_ENABLE, SI1145_MASK_ALL_IE); + if (err < 0) + goto disable; + err = si1145_set_meas_rate(data, data->meas_rate); + if (err < 0) + goto disable; + err = si1145_command(data, SI1145_CMD_PSALS_AUTO); + if (err < 0) + goto disable; + } else { +disable: + /* Disable as much as possible skipping errors */ + ret = si1145_command(data, SI1145_CMD_PSALS_PAUSE); + if (ret < 0 && !err) + err = ret; + ret = si1145_set_meas_rate(data, 0); + if (ret < 0 && !err) + err = ret; + ret = i2c_smbus_write_byte_data(data->client, + SI1145_REG_IRQ_ENABLE, 0); + if (ret < 0 && !err) + err = ret; + ret = i2c_smbus_write_byte_data(data->client, + SI1145_REG_INT_CFG, 0); + if (ret < 0 && !err) + err = ret; + data->autonomous = false; + } + + mutex_unlock(&data->lock); + return err; +} + +static const struct iio_trigger_ops si1145_trigger_ops = { + .owner = THIS_MODULE, + .set_trigger_state = si1145_trigger_set_state, +}; + +static int si1145_probe_trigger(struct iio_dev *indio_dev) +{ + struct si1145_data *data = iio_priv(indio_dev); + struct i2c_client *client = data->client; + struct iio_trigger *trig; + int ret; + + trig = devm_iio_trigger_alloc(&client->dev, + "%s-dev%d", indio_dev->name, indio_dev->id); + if (!trig) + return -ENOMEM; + + trig->dev.parent = &client->dev; + trig->ops = &si1145_trigger_ops; + iio_trigger_set_drvdata(trig, indio_dev); + + ret = devm_request_irq(&client->dev, client->irq, + iio_trigger_generic_data_rdy_poll, + IRQF_TRIGGER_FALLING, + "si1145_irq", + trig); + if (ret < 0) { + dev_err(&client->dev, "irq request failed\n"); + return ret; + } + + ret = iio_trigger_register(trig); + if (ret) + return ret; + + data->trig = trig; + indio_dev->trig = iio_trigger_get(data->trig); + + return 0; +} + +static void si1145_remove_trigger(struct iio_dev *indio_dev) +{ + struct si1145_data *data = iio_priv(indio_dev); + + if (data->trig) { + iio_trigger_unregister(data->trig); + data->trig = NULL; + } +} + +static int si1145_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct si1145_data *data; + struct iio_dev *indio_dev; + u8 part_id, rev_id, seq_id; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + data->part_info = &si1145_part_info[id->driver_data]; + + part_id = ret = i2c_smbus_read_byte_data(data->client, + SI1145_REG_PART_ID); + if (ret < 0) + return ret; + rev_id = ret = i2c_smbus_read_byte_data(data->client, + SI1145_REG_REV_ID); + if (ret < 0) + return ret; + seq_id = ret = i2c_smbus_read_byte_data(data->client, + SI1145_REG_SEQ_ID); + if (ret < 0) + return ret; + dev_info(&client->dev, "device ID part %#02hhx rev %#02hhx seq %#02hhx\n", + part_id, rev_id, seq_id); + if (part_id != data->part_info->part) { + dev_err(&client->dev, "part ID mismatch got %#02hhx, expected %#02x\n", + part_id, data->part_info->part); + return -ENODEV; + } + + indio_dev->dev.parent = &client->dev; + indio_dev->name = id->name; + indio_dev->channels = data->part_info->channels; + indio_dev->num_channels = data->part_info->num_channels; + indio_dev->info = data->part_info->iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + mutex_init(&data->lock); + mutex_init(&data->cmdlock); + + ret = si1145_initialize(data); + if (ret < 0) + return ret; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + si1145_trigger_handler, &si1145_buffer_setup_ops); + if (ret < 0) + return ret; + + if (client->irq) { + ret = si1145_probe_trigger(indio_dev); + if (ret < 0) + goto error_free_buffer; + } else { + dev_info(&client->dev, "no irq, using polling\n"); + } + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto error_free_trigger; + + return 0; + +error_free_trigger: + si1145_remove_trigger(indio_dev); +error_free_buffer: + iio_triggered_buffer_cleanup(indio_dev); + + return ret; +} + +static const struct i2c_device_id si1145_ids[] = { + { "si1132", SI1132 }, + { "si1141", SI1141 }, + { "si1142", SI1142 }, + { "si1143", SI1143 }, + { "si1145", SI1145 }, + { "si1146", SI1146 }, + { "si1147", SI1147 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, si1145_ids); + +static int si1145_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + si1145_remove_trigger(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + + return 0; +} + +static struct i2c_driver si1145_driver = { + .driver = { + .name = "si1145", + }, + .probe = si1145_probe, + .remove = si1145_remove, + .id_table = si1145_ids, +}; + +module_i2c_driver(si1145_driver); + +MODULE_AUTHOR("Peter Meerwald-Stadler "); +MODULE_DESCRIPTION("Silabs SI1132 and SI1141/2/3/5/6/7 proximity, ambient light and UV index sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index c9d85bb..360b6e9 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -1,6 +1,6 @@ /* - * vcnl4000.c - Support for Vishay VCNL4000 combined ambient light and - * proximity sensor + * vcnl4000.c - Support for Vishay VCNL4000/4010/4020 combined ambient + * light and proximity sensor * * Copyright 2012 Peter Meerwald * @@ -13,6 +13,8 @@ * TODO: * allow to adjust IR current * proximity threshold and event handling + * periodic ALS/proximity measurement (VCNL4010/20) + * interrupts (VCNL4010/20) */ #include @@ -24,6 +26,8 @@ #include #define VCNL4000_DRV_NAME "vcnl4000" +#define VCNL4000_ID 0x01 +#define VCNL4010_ID 0x02 /* for VCNL4020, VCNL4010 */ #define VCNL4000_COMMAND 0x80 /* Command register */ #define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ @@ -37,13 +41,14 @@ #define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ /* Bit masks for COMMAND register */ -#define VCNL4000_AL_RDY 0x40 /* ALS data ready? */ -#define VCNL4000_PS_RDY 0x20 /* proximity data ready? */ -#define VCNL4000_AL_OD 0x10 /* start on-demand ALS measurement */ -#define VCNL4000_PS_OD 0x08 /* start on-demand proximity measurement */ +#define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ +#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ +#define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ +#define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ struct vcnl4000_data { struct i2c_client *client; + struct mutex lock; }; static const struct i2c_device_id vcnl4000_id[] = { @@ -59,16 +64,18 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, __be16 buf; int ret; + mutex_lock(&data->lock); + ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, req_mask); if (ret < 0) - return ret; + goto fail; /* wait for data to become ready */ while (tries--) { ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); if (ret < 0) - return ret; + goto fail; if (ret & rdy_mask) break; msleep(20); /* measurement takes up to 100 ms */ @@ -77,17 +84,23 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, if (tries < 0) { dev_err(&data->client->dev, "vcnl4000_measure() failed, data not ready\n"); - return -EIO; + ret = -EIO; + goto fail; } ret = i2c_smbus_read_i2c_block_data(data->client, data_reg, sizeof(buf), (u8 *) &buf); if (ret < 0) - return ret; + goto fail; + mutex_unlock(&data->lock); *val = be16_to_cpu(buf); return 0; + +fail: + mutex_unlock(&data->lock); + return ret; } static const struct iio_chan_spec vcnl4000_channels[] = { @@ -105,7 +118,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - int ret = -EINVAL; + int ret; struct vcnl4000_data *data = iio_priv(indio_dev); switch (mask) { @@ -117,32 +130,27 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, VCNL4000_AL_RESULT_HI, val); if (ret < 0) return ret; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; case IIO_PROXIMITY: ret = vcnl4000_measure(data, VCNL4000_PS_OD, VCNL4000_PS_RDY, VCNL4000_PS_RESULT_HI, val); if (ret < 0) return ret; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; default: - break; + return -EINVAL; } - break; case IIO_CHAN_INFO_SCALE: - if (chan->type == IIO_LIGHT) { - *val = 0; - *val2 = 250000; - ret = IIO_VAL_INT_PLUS_MICRO; - } - break; + if (chan->type != IIO_LIGHT) + return -EINVAL; + + *val = 0; + *val2 = 250000; + return IIO_VAL_INT_PLUS_MICRO; default: - break; + return -EINVAL; } - - return ret; } static const struct iio_info vcnl4000_info = { @@ -155,7 +163,7 @@ static int vcnl4000_probe(struct i2c_client *client, { struct vcnl4000_data *data; struct iio_dev *indio_dev; - int ret; + int ret, prod_id; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -164,13 +172,19 @@ static int vcnl4000_probe(struct i2c_client *client, data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); data->client = client; + mutex_init(&data->lock); ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); if (ret < 0) return ret; - dev_info(&client->dev, "VCNL4000 Ambient light/proximity sensor, Prod %02x, Rev: %02x\n", - ret >> 4, ret & 0xf); + prod_id = ret >> 4; + if (prod_id != VCNL4010_ID && prod_id != VCNL4000_ID) + return -ENODEV; + + dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", + (prod_id == VCNL4010_ID) ? "VCNL4010/4020" : "VCNL4000", + ret & 0xf); indio_dev->dev.parent = &client->dev; indio_dev->info = &vcnl4000_info; diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 1f842ab..421ad90 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -5,8 +5,22 @@ menu "Magnetometer sensors" +config AK8974 + tristate "Asahi Kasei AK8974 3-Axis Magnetometer" + depends on I2C + depends on OF + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Asahi Kasei AK8974 or + AMI305 I2C-based 3-axis magnetometer chips. + + To compile this driver as a module, choose M here: the module + will be called ak8974. + config AK8975 - tristate "Asahi Kasei AK 3-Axis Magnetometer" + tristate "Asahi Kasei AK8975 3-Axis Magnetometer" depends on I2C depends on GPIOLIB || COMPILE_TEST select IIO_BUFFER diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index 92a745c..b86d6cb 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -3,6 +3,7 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AK8974) += ak8974.o obj-$(CONFIG_AK8975) += ak8975.o obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o obj-$(CONFIG_BMC150_MAGN_I2C) += bmc150_magn_i2c.o diff --git b/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c new file mode 100644 index 0000000..2173531 --- /dev/null +++ b/drivers/iio/magnetometer/ak8974.c @@ -0,0 +1,860 @@ +/* + * Driver for the Asahi Kasei EMD Corporation AK8974 + * and Aichi Steel AMI305 magnetometer chips. + * Based on a patch from Samu Onkalo and the AK8975 IIO driver. + * + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (c) 2010 NVIDIA Corporation. + * Copyright (C) 2016 Linaro Ltd. + * + * Author: Samu Onkalo + * Author: Linus Walleij + */ +#include +#include +#include +#include +#include /* For irq_get_irq_data() */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * 16-bit registers are little-endian. LSB is at the address defined below + * and MSB is at the next higher address. + */ + +/* These registers are common for AK8974 and AMI305 */ +#define AK8974_SELFTEST 0x0C +#define AK8974_SELFTEST_IDLE 0x55 +#define AK8974_SELFTEST_OK 0xAA + +#define AK8974_INFO 0x0D + +#define AK8974_WHOAMI 0x0F +#define AK8974_WHOAMI_VALUE_AMI305 0x47 +#define AK8974_WHOAMI_VALUE_AK8974 0x48 + +#define AK8974_DATA_X 0x10 +#define AK8974_DATA_Y 0x12 +#define AK8974_DATA_Z 0x14 +#define AK8974_INT_SRC 0x16 +#define AK8974_STATUS 0x18 +#define AK8974_INT_CLEAR 0x1A +#define AK8974_CTRL1 0x1B +#define AK8974_CTRL2 0x1C +#define AK8974_CTRL3 0x1D +#define AK8974_INT_CTRL 0x1E +#define AK8974_INT_THRES 0x26 /* Absolute any axis value threshold */ +#define AK8974_PRESET 0x30 + +/* AK8974-specific offsets */ +#define AK8974_OFFSET_X 0x20 +#define AK8974_OFFSET_Y 0x22 +#define AK8974_OFFSET_Z 0x24 +/* AMI305-specific offsets */ +#define AMI305_OFFSET_X 0x6C +#define AMI305_OFFSET_Y 0x72 +#define AMI305_OFFSET_Z 0x78 + +/* Different temperature registers */ +#define AK8974_TEMP 0x31 +#define AMI305_TEMP 0x60 + +#define AK8974_INT_X_HIGH BIT(7) /* Axis over +threshold */ +#define AK8974_INT_Y_HIGH BIT(6) +#define AK8974_INT_Z_HIGH BIT(5) +#define AK8974_INT_X_LOW BIT(4) /* Axis below -threshold */ +#define AK8974_INT_Y_LOW BIT(3) +#define AK8974_INT_Z_LOW BIT(2) +#define AK8974_INT_RANGE BIT(1) /* Range overflow (any axis) */ + +#define AK8974_STATUS_DRDY BIT(6) /* Data ready */ +#define AK8974_STATUS_OVERRUN BIT(5) /* Data overrun */ +#define AK8974_STATUS_INT BIT(4) /* Interrupt occurred */ + +#define AK8974_CTRL1_POWER BIT(7) /* 0 = standby; 1 = active */ +#define AK8974_CTRL1_RATE BIT(4) /* 0 = 10 Hz; 1 = 20 Hz */ +#define AK8974_CTRL1_FORCE_EN BIT(1) /* 0 = normal; 1 = force */ +#define AK8974_CTRL1_MODE2 BIT(0) /* 0 */ + +#define AK8974_CTRL2_INT_EN BIT(4) /* 1 = enable interrupts */ +#define AK8974_CTRL2_DRDY_EN BIT(3) /* 1 = enable data ready signal */ +#define AK8974_CTRL2_DRDY_POL BIT(2) /* 1 = data ready active high */ +#define AK8974_CTRL2_RESDEF (AK8974_CTRL2_DRDY_POL) + +#define AK8974_CTRL3_RESET BIT(7) /* Software reset */ +#define AK8974_CTRL3_FORCE BIT(6) /* Start forced measurement */ +#define AK8974_CTRL3_SELFTEST BIT(4) /* Set selftest register */ +#define AK8974_CTRL3_RESDEF 0x00 + +#define AK8974_INT_CTRL_XEN BIT(7) /* Enable interrupt for this axis */ +#define AK8974_INT_CTRL_YEN BIT(6) +#define AK8974_INT_CTRL_ZEN BIT(5) +#define AK8974_INT_CTRL_XYZEN (BIT(7)|BIT(6)|BIT(5)) +#define AK8974_INT_CTRL_POL BIT(3) /* 0 = active low; 1 = active high */ +#define AK8974_INT_CTRL_PULSE BIT(1) /* 0 = latched; 1 = pulse (50 usec) */ +#define AK8974_INT_CTRL_RESDEF (AK8974_INT_CTRL_XYZEN | AK8974_INT_CTRL_POL) + +/* The AMI305 has elaborate FW version and serial number registers */ +#define AMI305_VER 0xE8 +#define AMI305_SN 0xEA + +#define AK8974_MAX_RANGE 2048 + +#define AK8974_POWERON_DELAY 50 +#define AK8974_ACTIVATE_DELAY 1 +#define AK8974_SELFTEST_DELAY 1 +/* + * Set the autosuspend to two orders of magnitude larger than the poweron + * delay to make sane reasonable power tradeoff savings (5 seconds in + * this case). + */ +#define AK8974_AUTOSUSPEND_DELAY 5000 + +#define AK8974_MEASTIME 3 + +#define AK8974_PWR_ON 1 +#define AK8974_PWR_OFF 0 + +/** + * struct ak8974 - state container for the AK8974 driver + * @i2c: parent I2C client + * @orientation: mounting matrix, flipped axis etc + * @map: regmap to access the AK8974 registers over I2C + * @regs: the avdd and dvdd power regulators + * @name: the name of the part + * @variant: the whoami ID value (for selecting code paths) + * @lock: locks the magnetometer for exclusive use during a measurement + * @drdy_irq: uses the DRDY IRQ line + * @drdy_complete: completion for DRDY + * @drdy_active_low: the DRDY IRQ is active low + */ +struct ak8974 { + struct i2c_client *i2c; + struct iio_mount_matrix orientation; + struct regmap *map; + struct regulator_bulk_data regs[2]; + const char *name; + u8 variant; + struct mutex lock; + bool drdy_irq; + struct completion drdy_complete; + bool drdy_active_low; +}; + +static const char ak8974_reg_avdd[] = "avdd"; +static const char ak8974_reg_dvdd[] = "dvdd"; + +static int ak8974_set_power(struct ak8974 *ak8974, bool mode) +{ + int ret; + u8 val; + + val = mode ? AK8974_CTRL1_POWER : 0; + val |= AK8974_CTRL1_FORCE_EN; + ret = regmap_write(ak8974->map, AK8974_CTRL1, val); + if (ret < 0) + return ret; + + if (mode) + msleep(AK8974_ACTIVATE_DELAY); + + return 0; +} + +static int ak8974_reset(struct ak8974 *ak8974) +{ + int ret; + + /* Power on to get register access. Sets CTRL1 reg to reset state */ + ret = ak8974_set_power(ak8974, AK8974_PWR_ON); + if (ret) + return ret; + ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_RESDEF); + if (ret) + return ret; + ret = regmap_write(ak8974->map, AK8974_CTRL3, AK8974_CTRL3_RESDEF); + if (ret) + return ret; + ret = regmap_write(ak8974->map, AK8974_INT_CTRL, + AK8974_INT_CTRL_RESDEF); + if (ret) + return ret; + + /* After reset, power off is default state */ + return ak8974_set_power(ak8974, AK8974_PWR_OFF); +} + +static int ak8974_configure(struct ak8974 *ak8974) +{ + int ret; + + ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_DRDY_EN | + AK8974_CTRL2_INT_EN); + if (ret) + return ret; + ret = regmap_write(ak8974->map, AK8974_CTRL3, 0); + if (ret) + return ret; + ret = regmap_write(ak8974->map, AK8974_INT_CTRL, AK8974_INT_CTRL_POL); + if (ret) + return ret; + + return regmap_write(ak8974->map, AK8974_PRESET, 0); +} + +static int ak8974_trigmeas(struct ak8974 *ak8974) +{ + unsigned int clear; + u8 mask; + u8 val; + int ret; + + /* Clear any previous measurement overflow status */ + ret = regmap_read(ak8974->map, AK8974_INT_CLEAR, &clear); + if (ret) + return ret; + + /* If we have a DRDY IRQ line, use it */ + if (ak8974->drdy_irq) { + mask = AK8974_CTRL2_INT_EN | + AK8974_CTRL2_DRDY_EN | + AK8974_CTRL2_DRDY_POL; + val = AK8974_CTRL2_DRDY_EN; + + if (!ak8974->drdy_active_low) + val |= AK8974_CTRL2_DRDY_POL; + + init_completion(&ak8974->drdy_complete); + ret = regmap_update_bits(ak8974->map, AK8974_CTRL2, + mask, val); + if (ret) + return ret; + } + + /* Force a measurement */ + return regmap_update_bits(ak8974->map, + AK8974_CTRL3, + AK8974_CTRL3_FORCE, + AK8974_CTRL3_FORCE); +} + +static int ak8974_await_drdy(struct ak8974 *ak8974) +{ + int timeout = 2; + unsigned int val; + int ret; + + if (ak8974->drdy_irq) { + ret = wait_for_completion_timeout(&ak8974->drdy_complete, + 1 + msecs_to_jiffies(1000)); + if (!ret) { + dev_err(&ak8974->i2c->dev, + "timeout waiting for DRDY IRQ\n"); + return -ETIMEDOUT; + } + return 0; + } + + /* Default delay-based poll loop */ + do { + msleep(AK8974_MEASTIME); + ret = regmap_read(ak8974->map, AK8974_STATUS, &val); + if (ret < 0) + return ret; + if (val & AK8974_STATUS_DRDY) + return 0; + } while (--timeout); + if (!timeout) { + dev_err(&ak8974->i2c->dev, + "timeout waiting for DRDY\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int ak8974_getresult(struct ak8974 *ak8974, s16 *result) +{ + unsigned int src; + int ret; + + ret = ak8974_await_drdy(ak8974); + if (ret) + return ret; + ret = regmap_read(ak8974->map, AK8974_INT_SRC, &src); + if (ret < 0) + return ret; + + /* Out of range overflow! Strong magnet close? */ + if (src & AK8974_INT_RANGE) { + dev_err(&ak8974->i2c->dev, + "range overflow in sensor\n"); + return -ERANGE; + } + + ret = regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6); + if (ret) + return ret; + + return ret; +} + +static irqreturn_t ak8974_drdy_irq(int irq, void *d) +{ + struct ak8974 *ak8974 = d; + + if (!ak8974->drdy_irq) + return IRQ_NONE; + + /* TODO: timestamp here to get good measurement stamps */ + return IRQ_WAKE_THREAD; +} + +static irqreturn_t ak8974_drdy_irq_thread(int irq, void *d) +{ + struct ak8974 *ak8974 = d; + unsigned int val; + int ret; + + /* Check if this was a DRDY from us */ + ret = regmap_read(ak8974->map, AK8974_STATUS, &val); + if (ret < 0) { + dev_err(&ak8974->i2c->dev, "error reading DRDY status\n"); + return IRQ_HANDLED; + } + if (val & AK8974_STATUS_DRDY) { + /* Yes this was our IRQ */ + complete(&ak8974->drdy_complete); + return IRQ_HANDLED; + } + + /* We may be on a shared IRQ, let the next client check */ + return IRQ_NONE; +} + +static int ak8974_selftest(struct ak8974 *ak8974) +{ + struct device *dev = &ak8974->i2c->dev; + unsigned int val; + int ret; + + ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val); + if (ret) + return ret; + if (val != AK8974_SELFTEST_IDLE) { + dev_err(dev, "selftest not idle before test\n"); + return -EIO; + } + + /* Trigger self-test */ + ret = regmap_update_bits(ak8974->map, + AK8974_CTRL3, + AK8974_CTRL3_SELFTEST, + AK8974_CTRL3_SELFTEST); + if (ret) { + dev_err(dev, "could not write CTRL3\n"); + return ret; + } + + msleep(AK8974_SELFTEST_DELAY); + + ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val); + if (ret) + return ret; + if (val != AK8974_SELFTEST_OK) { + dev_err(dev, "selftest result NOT OK (%02x)\n", val); + return -EIO; + } + + ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val); + if (ret) + return ret; + if (val != AK8974_SELFTEST_IDLE) { + dev_err(dev, "selftest not idle after test (%02x)\n", val); + return -EIO; + } + dev_dbg(dev, "passed self-test\n"); + + return 0; +} + +static int ak8974_get_u16_val(struct ak8974 *ak8974, u8 reg, u16 *val) +{ + int ret; + u16 bulk; + + ret = regmap_bulk_read(ak8974->map, reg, &bulk, 2); + if (ret) + return ret; + *val = le16_to_cpu(bulk); + + return 0; +} + +static int ak8974_detect(struct ak8974 *ak8974) +{ + unsigned int whoami; + const char *name; + int ret; + unsigned int fw; + u16 sn; + + ret = regmap_read(ak8974->map, AK8974_WHOAMI, &whoami); + if (ret) + return ret; + + switch (whoami) { + case AK8974_WHOAMI_VALUE_AMI305: + name = "ami305"; + ret = regmap_read(ak8974->map, AMI305_VER, &fw); + if (ret) + return ret; + fw &= 0x7f; /* only bits 0 thru 6 valid */ + ret = ak8974_get_u16_val(ak8974, AMI305_SN, &sn); + if (ret) + return ret; + dev_info(&ak8974->i2c->dev, + "detected %s, FW ver %02x, S/N: %04x\n", + name, fw, sn); + break; + case AK8974_WHOAMI_VALUE_AK8974: + name = "ak8974"; + dev_info(&ak8974->i2c->dev, "detected AK8974\n"); + break; + default: + dev_err(&ak8974->i2c->dev, "unsupported device (%02x) ", + whoami); + return -ENODEV; + } + + ak8974->name = name; + ak8974->variant = whoami; + + return 0; +} + +static int ak8974_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct ak8974 *ak8974 = iio_priv(indio_dev); + s16 hw_values[3]; + int ret = -EINVAL; + + pm_runtime_get_sync(&ak8974->i2c->dev); + mutex_lock(&ak8974->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->address > 2) { + dev_err(&ak8974->i2c->dev, "faulty channel address\n"); + ret = -EIO; + goto out_unlock; + } + ret = ak8974_trigmeas(ak8974); + if (ret) + goto out_unlock; + ret = ak8974_getresult(ak8974, hw_values); + if (ret) + goto out_unlock; + + /* + * We read all axes and discard all but one, for optimized + * reading, use the triggered buffer. + */ + *val = le16_to_cpu(hw_values[chan->address]); + + ret = IIO_VAL_INT; + } + + out_unlock: + mutex_unlock(&ak8974->lock); + pm_runtime_mark_last_busy(&ak8974->i2c->dev); + pm_runtime_put_autosuspend(&ak8974->i2c->dev); + + return ret; +} + +static void ak8974_fill_buffer(struct iio_dev *indio_dev) +{ + struct ak8974 *ak8974 = iio_priv(indio_dev); + int ret; + s16 hw_values[8]; /* Three axes + 64bit padding */ + + pm_runtime_get_sync(&ak8974->i2c->dev); + mutex_lock(&ak8974->lock); + + ret = ak8974_trigmeas(ak8974); + if (ret) { + dev_err(&ak8974->i2c->dev, "error triggering measure\n"); + goto out_unlock; + } + ret = ak8974_getresult(ak8974, hw_values); + if (ret) { + dev_err(&ak8974->i2c->dev, "error getting measures\n"); + goto out_unlock; + } + + iio_push_to_buffers_with_timestamp(indio_dev, hw_values, + iio_get_time_ns(indio_dev)); + + out_unlock: + mutex_unlock(&ak8974->lock); + pm_runtime_mark_last_busy(&ak8974->i2c->dev); + pm_runtime_put_autosuspend(&ak8974->i2c->dev); +} + +static irqreturn_t ak8974_handle_trigger(int irq, void *p) +{ + const struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + + ak8974_fill_buffer(indio_dev); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_mount_matrix * +ak8974_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ak8974 *ak8974 = iio_priv(indio_dev); + + return &ak8974->orientation; +} + +static const struct iio_chan_spec_ext_info ak8974_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, ak8974_get_mount_matrix), + { }, +}; + +#define AK8974_AXIS_CHANNEL(axis, index) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .ext_info = ak8974_ext_info, \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE \ + }, \ + } + +static const struct iio_chan_spec ak8974_channels[] = { + AK8974_AXIS_CHANNEL(X, 0), + AK8974_AXIS_CHANNEL(Y, 1), + AK8974_AXIS_CHANNEL(Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const unsigned long ak8974_scan_masks[] = { 0x7, 0 }; + +static const struct iio_info ak8974_info = { + .read_raw = &ak8974_read_raw, + .driver_module = THIS_MODULE, +}; + +static bool ak8974_writeable_reg(struct device *dev, unsigned int reg) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct iio_dev *indio_dev = i2c_get_clientdata(i2c); + struct ak8974 *ak8974 = iio_priv(indio_dev); + + switch (reg) { + case AK8974_CTRL1: + case AK8974_CTRL2: + case AK8974_CTRL3: + case AK8974_INT_CTRL: + case AK8974_INT_THRES: + case AK8974_INT_THRES + 1: + case AK8974_PRESET: + case AK8974_PRESET + 1: + return true; + case AK8974_OFFSET_X: + case AK8974_OFFSET_X + 1: + case AK8974_OFFSET_Y: + case AK8974_OFFSET_Y + 1: + case AK8974_OFFSET_Z: + case AK8974_OFFSET_Z + 1: + if (ak8974->variant == AK8974_WHOAMI_VALUE_AK8974) + return true; + return false; + case AMI305_OFFSET_X: + case AMI305_OFFSET_X + 1: + case AMI305_OFFSET_Y: + case AMI305_OFFSET_Y + 1: + case AMI305_OFFSET_Z: + case AMI305_OFFSET_Z + 1: + if (ak8974->variant == AK8974_WHOAMI_VALUE_AMI305) + return true; + return false; + default: + return false; + } +} + +static const struct regmap_config ak8974_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + .writeable_reg = ak8974_writeable_reg, +}; + +static int ak8974_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct ak8974 *ak8974; + unsigned long irq_trig; + int irq = i2c->irq; + int ret; + + /* Register with IIO */ + indio_dev = devm_iio_device_alloc(&i2c->dev, sizeof(*ak8974)); + if (indio_dev == NULL) + return -ENOMEM; + + ak8974 = iio_priv(indio_dev); + i2c_set_clientdata(i2c, indio_dev); + ak8974->i2c = i2c; + mutex_init(&ak8974->lock); + + ret = of_iio_read_mount_matrix(&i2c->dev, + "mount-matrix", + &ak8974->orientation); + if (ret) + return ret; + + ak8974->regs[0].supply = ak8974_reg_avdd; + ak8974->regs[1].supply = ak8974_reg_dvdd; + + ret = devm_regulator_bulk_get(&i2c->dev, + ARRAY_SIZE(ak8974->regs), + ak8974->regs); + if (ret < 0) { + dev_err(&i2c->dev, "cannot get regulators\n"); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs); + if (ret < 0) { + dev_err(&i2c->dev, "cannot enable regulators\n"); + return ret; + } + + /* Take runtime PM online */ + pm_runtime_get_noresume(&i2c->dev); + pm_runtime_set_active(&i2c->dev); + pm_runtime_enable(&i2c->dev); + + ak8974->map = devm_regmap_init_i2c(i2c, &ak8974_regmap_config); + if (IS_ERR(ak8974->map)) { + dev_err(&i2c->dev, "failed to allocate register map\n"); + return PTR_ERR(ak8974->map); + } + + ret = ak8974_set_power(ak8974, AK8974_PWR_ON); + if (ret) { + dev_err(&i2c->dev, "could not power on\n"); + goto power_off; + } + + ret = ak8974_detect(ak8974); + if (ret) { + dev_err(&i2c->dev, "neither AK8974 nor AMI305 found\n"); + goto power_off; + } + + ret = ak8974_selftest(ak8974); + if (ret) + dev_err(&i2c->dev, "selftest failed (continuing anyway)\n"); + + ret = ak8974_reset(ak8974); + if (ret) { + dev_err(&i2c->dev, "AK8974 reset failed\n"); + goto power_off; + } + + pm_runtime_set_autosuspend_delay(&i2c->dev, + AK8974_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&i2c->dev); + pm_runtime_put(&i2c->dev); + + indio_dev->dev.parent = &i2c->dev; + indio_dev->channels = ak8974_channels; + indio_dev->num_channels = ARRAY_SIZE(ak8974_channels); + indio_dev->info = &ak8974_info; + indio_dev->available_scan_masks = ak8974_scan_masks; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = ak8974->name; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + ak8974_handle_trigger, + NULL); + if (ret) { + dev_err(&i2c->dev, "triggered buffer setup failed\n"); + goto disable_pm; + } + + /* If we have a valid DRDY IRQ, make use of it */ + if (irq > 0) { + irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq)); + if (irq_trig == IRQF_TRIGGER_RISING) { + dev_info(&i2c->dev, "enable rising edge DRDY IRQ\n"); + } else if (irq_trig == IRQF_TRIGGER_FALLING) { + ak8974->drdy_active_low = true; + dev_info(&i2c->dev, "enable falling edge DRDY IRQ\n"); + } else { + irq_trig = IRQF_TRIGGER_RISING; + } + irq_trig |= IRQF_ONESHOT; + irq_trig |= IRQF_SHARED; + + ret = devm_request_threaded_irq(&i2c->dev, + irq, + ak8974_drdy_irq, + ak8974_drdy_irq_thread, + irq_trig, + ak8974->name, + ak8974); + if (ret) { + dev_err(&i2c->dev, "unable to request DRDY IRQ " + "- proceeding without IRQ\n"); + goto no_irq; + } + ak8974->drdy_irq = true; + } + +no_irq: + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&i2c->dev, "device register failed\n"); + goto cleanup_buffer; + } + + return 0; + +cleanup_buffer: + iio_triggered_buffer_cleanup(indio_dev); +disable_pm: + pm_runtime_put_noidle(&i2c->dev); + pm_runtime_disable(&i2c->dev); + ak8974_set_power(ak8974, AK8974_PWR_OFF); +power_off: + regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs); + + return ret; +} + +static int __exit ak8974_remove(struct i2c_client *i2c) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(i2c); + struct ak8974 *ak8974 = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + pm_runtime_get_sync(&i2c->dev); + pm_runtime_put_noidle(&i2c->dev); + pm_runtime_disable(&i2c->dev); + ak8974_set_power(ak8974, AK8974_PWR_OFF); + regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs); + + return 0; +} + +static int __maybe_unused ak8974_runtime_suspend(struct device *dev) +{ + struct ak8974 *ak8974 = + iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + ak8974_set_power(ak8974, AK8974_PWR_OFF); + regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs); + + return 0; +} + +static int __maybe_unused ak8974_runtime_resume(struct device *dev) +{ + struct ak8974 *ak8974 = + iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs); + if (ret) + return ret; + msleep(AK8974_POWERON_DELAY); + ret = ak8974_set_power(ak8974, AK8974_PWR_ON); + if (ret) + goto out_regulator_disable; + + ret = ak8974_configure(ak8974); + if (ret) + goto out_disable_power; + + return 0; + +out_disable_power: + ak8974_set_power(ak8974, AK8974_PWR_OFF); +out_regulator_disable: + regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs); + + return ret; +} + +static const struct dev_pm_ops ak8974_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(ak8974_runtime_suspend, + ak8974_runtime_resume, NULL) +}; + +static const struct i2c_device_id ak8974_id[] = { + {"ami305", 0 }, + {"ak8974", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ak8974_id); + +static const struct of_device_id ak8974_of_match[] = { + { .compatible = "asahi-kasei,ak8974", }, + {} +}; +MODULE_DEVICE_TABLE(of, ak8974_of_match); + +static struct i2c_driver ak8974_driver = { + .driver = { + .name = "ak8974", + .pm = &ak8974_dev_pm_ops, + .of_match_table = of_match_ptr(ak8974_of_match), + }, + .probe = ak8974_probe, + .remove = __exit_p(ak8974_remove), + .id_table = ak8974_id, +}; +module_i2c_driver(ak8974_driver); + +MODULE_DESCRIPTION("AK8974 and AMI305 3-axis magnetometer driver"); +MODULE_AUTHOR("Samu Onkalo"); +MODULE_AUTHOR("Linus Walleij"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index f2be4a0..f2b3bd7 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -154,34 +154,41 @@ static int mag3110_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - if (iio_buffer_enabled(indio_dev)) - return -EBUSY; + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; switch (chan->type) { case IIO_MAGN: /* in 0.1 uT / LSB */ ret = mag3110_read(data, buffer); if (ret < 0) - return ret; + goto release; *val = sign_extend32( be16_to_cpu(buffer[chan->scan_index]), 15); - return IIO_VAL_INT; + ret = IIO_VAL_INT; + break; case IIO_TEMP: /* in 1 C / LSB */ mutex_lock(&data->lock); ret = mag3110_request(data); if (ret < 0) { mutex_unlock(&data->lock); - return ret; + goto release; } ret = i2c_smbus_read_byte_data(data->client, MAG3110_DIE_TEMP); mutex_unlock(&data->lock); if (ret < 0) - return ret; + goto release; *val = sign_extend32(ret, 7); - return IIO_VAL_INT; + ret = IIO_VAL_INT; + break; default: - return -EINVAL; + ret = -EINVAL; } +release: + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_MAGN: diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index d130cdc..15cd416 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -8,8 +8,6 @@ menu "Pressure sensors" config BMP280 tristate "Bosch Sensortec BMP180/BMP280 pressure sensor I2C driver" depends on (I2C || SPI_MASTER) - depends on !(BMP085_I2C=y || BMP085_I2C=m) - depends on !(BMP085_SPI=y || BMP085_SPI=m) select REGMAP select BMP280_I2C if (I2C) select BMP280_SPI if (SPI_MASTER) @@ -187,4 +185,26 @@ config HP206C This driver can also be built as a module. If so, the module will be called hp206c. +config ZPA2326 + tristate "Murata ZPA2326 pressure sensor driver" + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP + select ZPA2326_I2C if I2C + select ZPA2326_SPI if SPI_MASTER + help + Say Y here to build support for the Murata ZPA2326 pressure and + temperature sensor. + + To compile this driver as a module, choose M here: the module will + be called zpa2326. + +config ZPA2326_I2C + tristate + select REGMAP_I2C + +config ZPA2326_SPI + tristate + select REGMAP_SPI + endmenu diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index 7f395be..fff7718 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -22,6 +22,9 @@ st_pressure-y := st_pressure_core.o st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o obj-$(CONFIG_T5403) += t5403.o obj-$(CONFIG_HP206C) += hp206c.o +obj-$(CONFIG_ZPA2326) += zpa2326.o +obj-$(CONFIG_ZPA2326_I2C) += zpa2326_i2c.o +obj-$(CONFIG_ZPA2326_SPI) += zpa2326_spi.o obj-$(CONFIG_IIO_ST_PRESS_I2C) += st_pressure_i2c.o obj-$(CONFIG_IIO_ST_PRESS_SPI) += st_pressure_spi.o diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index feb41f8..a74ed1f 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -416,8 +416,7 @@ static int ms5611_init(struct iio_dev *indio_dev) return 0; err_regulator_disable: - if (!IS_ERR_OR_NULL(st->vdd)) - regulator_disable(st->vdd); + regulator_disable(st->vdd); return ret; } @@ -425,8 +424,7 @@ static void ms5611_fini(const struct iio_dev *indio_dev) { const struct ms5611_state *st = iio_priv(indio_dev); - if (!IS_ERR_OR_NULL(st->vdd)) - regulator_disable(st->vdd); + regulator_disable(st->vdd); } int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, diff --git b/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c new file mode 100644 index 0000000..19d2eb4 --- /dev/null +++ b/drivers/iio/pressure/zpa2326.c @@ -0,0 +1,1735 @@ +/* + * Murata ZPA2326 pressure and temperature sensor IIO driver + * + * Copyright (c) 2016 Parrot S.A. + * + * Author: Gregor Boirie + * + * 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. + */ + +/** + * DOC: ZPA2326 theory of operations + * + * This driver supports %INDIO_DIRECT_MODE and %INDIO_BUFFER_TRIGGERED IIO + * modes. + * A internal hardware trigger is also implemented to dispatch registered IIO + * trigger consumers upon "sample ready" interrupts. + * + * ZPA2326 hardware supports 2 sampling mode: one shot and continuous. + * + * A complete one shot sampling cycle gets device out of low power mode, + * performs pressure and temperature measurements, then automatically switches + * back to low power mode. It is meant for on demand sampling with optimal power + * saving at the cost of lower sampling rate and higher software overhead. + * This is a natural candidate for IIO read_raw hook implementation + * (%INDIO_DIRECT_MODE). It is also used for triggered buffering support to + * ensure explicit synchronization with external trigger events + * (%INDIO_BUFFER_TRIGGERED). + * + * The continuous mode works according to a periodic hardware measurement + * process continuously pushing samples into an internal hardware FIFO (for + * pressure samples only). Measurement cycle completion may be signaled by a + * "sample ready" interrupt. + * Typical software sequence of operations : + * - get device out of low power mode, + * - setup hardware sampling period, + * - at end of period, upon data ready interrupt: pop pressure samples out of + * hardware FIFO and fetch temperature sample + * - when no longer needed, stop sampling process by putting device into + * low power mode. + * This mode is used to implement %INDIO_BUFFER_TRIGGERED mode if device tree + * declares a valid interrupt line. In this case, the internal hardware trigger + * drives acquisition. + * + * Note that hardware sampling frequency is taken into account only when + * internal hardware trigger is attached as the highest sampling rate seems to + * be the most energy efficient. + * + * TODO: + * preset pressure threshold crossing / IIO events ; + * differential pressure sampling ; + * hardware samples averaging. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "zpa2326.h" + +/* 200 ms should be enough for the longest conversion time in one-shot mode. */ +#define ZPA2326_CONVERSION_JIFFIES (HZ / 5) + +/* There should be a 1 ms delay (Tpup) after getting out of reset. */ +#define ZPA2326_TPUP_USEC_MIN (1000) +#define ZPA2326_TPUP_USEC_MAX (2000) + +/** + * struct zpa2326_frequency - Hardware sampling frequency descriptor + * @hz : Frequency in Hertz. + * @odr: Output Data Rate word as expected by %ZPA2326_CTRL_REG3_REG. + */ +struct zpa2326_frequency { + int hz; + u16 odr; +}; + +/* + * Keep these in strict ascending order: last array entry is expected to + * correspond to the highest sampling frequency. + */ +static const struct zpa2326_frequency zpa2326_sampling_frequencies[] = { + { .hz = 1, .odr = 1 << ZPA2326_CTRL_REG3_ODR_SHIFT }, + { .hz = 5, .odr = 5 << ZPA2326_CTRL_REG3_ODR_SHIFT }, + { .hz = 11, .odr = 6 << ZPA2326_CTRL_REG3_ODR_SHIFT }, + { .hz = 23, .odr = 7 << ZPA2326_CTRL_REG3_ODR_SHIFT }, +}; + +/* Return the highest hardware sampling frequency available. */ +static const struct zpa2326_frequency *zpa2326_highest_frequency(void) +{ + return &zpa2326_sampling_frequencies[ + ARRAY_SIZE(zpa2326_sampling_frequencies) - 1]; +} + +/** + * struct zpa_private - Per-device internal private state + * @timestamp: Buffered samples ready datum. + * @regmap: Underlying I2C / SPI bus adapter used to abstract slave register + * accesses. + * @result: Allows sampling logic to get completion status of operations + * that interrupt handlers perform asynchronously. + * @data_ready: Interrupt handler uses this to wake user context up at sampling + * operation completion. + * @trigger: Optional hardware / interrupt driven trigger used to notify + * external devices a new sample is ready. + * @waken: Flag indicating whether or not device has just been powered on. + * @irq: Optional interrupt line: negative or zero if not declared into + * DT, in which case sampling logic keeps polling status register + * to detect completion. + * @frequency: Current hardware sampling frequency. + * @vref: Power / voltage reference. + * @vdd: Power supply. + */ +struct zpa2326_private { + s64 timestamp; + struct regmap *regmap; + int result; + struct completion data_ready; + struct iio_trigger *trigger; + bool waken; + int irq; + const struct zpa2326_frequency *frequency; + struct regulator *vref; + struct regulator *vdd; +}; + +#define zpa2326_err(_idev, _format, _arg...) \ + dev_err(_idev->dev.parent, _format, ##_arg) + +#define zpa2326_warn(_idev, _format, _arg...) \ + dev_warn(_idev->dev.parent, _format, ##_arg) + +#ifdef DEBUG +#define zpa2326_dbg(_idev, _format, _arg...) \ + dev_dbg(_idev->dev.parent, _format, ##_arg) +#else +#define zpa2326_dbg(_idev, _format, _arg...) +#endif + +bool zpa2326_isreg_writeable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ZPA2326_REF_P_XL_REG: + case ZPA2326_REF_P_L_REG: + case ZPA2326_REF_P_H_REG: + case ZPA2326_RES_CONF_REG: + case ZPA2326_CTRL_REG0_REG: + case ZPA2326_CTRL_REG1_REG: + case ZPA2326_CTRL_REG2_REG: + case ZPA2326_CTRL_REG3_REG: + case ZPA2326_THS_P_LOW_REG: + case ZPA2326_THS_P_HIGH_REG: + return true; + + default: + return false; + } +} +EXPORT_SYMBOL_GPL(zpa2326_isreg_writeable); + +bool zpa2326_isreg_readable(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ZPA2326_REF_P_XL_REG: + case ZPA2326_REF_P_L_REG: + case ZPA2326_REF_P_H_REG: + case ZPA2326_DEVICE_ID_REG: + case ZPA2326_RES_CONF_REG: + case ZPA2326_CTRL_REG0_REG: + case ZPA2326_CTRL_REG1_REG: + case ZPA2326_CTRL_REG2_REG: + case ZPA2326_CTRL_REG3_REG: + case ZPA2326_INT_SOURCE_REG: + case ZPA2326_THS_P_LOW_REG: + case ZPA2326_THS_P_HIGH_REG: + case ZPA2326_STATUS_REG: + case ZPA2326_PRESS_OUT_XL_REG: + case ZPA2326_PRESS_OUT_L_REG: + case ZPA2326_PRESS_OUT_H_REG: + case ZPA2326_TEMP_OUT_L_REG: + case ZPA2326_TEMP_OUT_H_REG: + return true; + + default: + return false; + } +} +EXPORT_SYMBOL_GPL(zpa2326_isreg_readable); + +bool zpa2326_isreg_precious(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ZPA2326_INT_SOURCE_REG: + case ZPA2326_PRESS_OUT_H_REG: + return true; + + default: + return false; + } +} +EXPORT_SYMBOL_GPL(zpa2326_isreg_precious); + +/** + * zpa2326_enable_device() - Enable device, i.e. get out of low power mode. + * @indio_dev: The IIO device associated with the hardware to enable. + * + * Required to access complete register space and to perform any sampling + * or control operations. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_enable_device(const struct iio_dev *indio_dev) +{ + int err; + + err = regmap_write(((struct zpa2326_private *) + iio_priv(indio_dev))->regmap, + ZPA2326_CTRL_REG0_REG, ZPA2326_CTRL_REG0_ENABLE); + if (err) { + zpa2326_err(indio_dev, "failed to enable device (%d)", err); + return err; + } + + zpa2326_dbg(indio_dev, "enabled"); + + return 0; +} + +/** + * zpa2326_sleep() - Disable device, i.e. switch to low power mode. + * @indio_dev: The IIO device associated with the hardware to disable. + * + * Only %ZPA2326_DEVICE_ID_REG and %ZPA2326_CTRL_REG0_REG registers may be + * accessed once device is in the disabled state. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_sleep(const struct iio_dev *indio_dev) +{ + int err; + + err = regmap_write(((struct zpa2326_private *) + iio_priv(indio_dev))->regmap, + ZPA2326_CTRL_REG0_REG, 0); + if (err) { + zpa2326_err(indio_dev, "failed to sleep (%d)", err); + return err; + } + + zpa2326_dbg(indio_dev, "sleeping"); + + return 0; +} + +/** + * zpa2326_reset_device() - Reset device to default hardware state. + * @indio_dev: The IIO device associated with the hardware to reset. + * + * Disable sampling and empty hardware FIFO. + * Device must be enabled before reset, i.e. not in low power mode. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_reset_device(const struct iio_dev *indio_dev) +{ + int err; + + err = regmap_write(((struct zpa2326_private *) + iio_priv(indio_dev))->regmap, + ZPA2326_CTRL_REG2_REG, ZPA2326_CTRL_REG2_SWRESET); + if (err) { + zpa2326_err(indio_dev, "failed to reset device (%d)", err); + return err; + } + + usleep_range(ZPA2326_TPUP_USEC_MIN, ZPA2326_TPUP_USEC_MAX); + + zpa2326_dbg(indio_dev, "reset"); + + return 0; +} + +/** + * zpa2326_start_oneshot() - Start a single sampling cycle, i.e. in one shot + * mode. + * @indio_dev: The IIO device associated with the sampling hardware. + * + * Device must have been previously enabled and configured for one shot mode. + * Device will be switched back to low power mode at end of cycle. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_start_oneshot(const struct iio_dev *indio_dev) +{ + int err; + + err = regmap_write(((struct zpa2326_private *) + iio_priv(indio_dev))->regmap, + ZPA2326_CTRL_REG0_REG, + ZPA2326_CTRL_REG0_ENABLE | + ZPA2326_CTRL_REG0_ONE_SHOT); + if (err) { + zpa2326_err(indio_dev, "failed to start one shot cycle (%d)", + err); + return err; + } + + zpa2326_dbg(indio_dev, "one shot cycle started"); + + return 0; +} + +/** + * zpa2326_power_on() - Power on device to allow subsequent configuration. + * @indio_dev: The IIO device associated with the sampling hardware. + * @private: Internal private state related to @indio_dev. + * + * Sampling will be disabled, preventing strange things from happening in our + * back. Hardware FIFO content will be cleared. + * When successful, device will be left in the enabled state to allow further + * configuration. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_power_on(const struct iio_dev *indio_dev, + const struct zpa2326_private *private) +{ + int err; + + err = regulator_enable(private->vref); + if (err) + return err; + + err = regulator_enable(private->vdd); + if (err) + goto vref; + + zpa2326_dbg(indio_dev, "powered on"); + + err = zpa2326_enable_device(indio_dev); + if (err) + goto vdd; + + err = zpa2326_reset_device(indio_dev); + if (err) + goto sleep; + + return 0; + +sleep: + zpa2326_sleep(indio_dev); +vdd: + regulator_disable(private->vdd); +vref: + regulator_disable(private->vref); + + zpa2326_dbg(indio_dev, "powered off"); + + return err; +} + +/** + * zpa2326_power_off() - Power off device, i.e. disable attached power + * regulators. + * @indio_dev: The IIO device associated with the sampling hardware. + * @private: Internal private state related to @indio_dev. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static void zpa2326_power_off(const struct iio_dev *indio_dev, + const struct zpa2326_private *private) +{ + regulator_disable(private->vdd); + regulator_disable(private->vref); + + zpa2326_dbg(indio_dev, "powered off"); +} + +/** + * zpa2326_config_oneshot() - Setup device for one shot / on demand mode. + * @indio_dev: The IIO device associated with the sampling hardware. + * @irq: Optional interrupt line the hardware uses to notify new data + * samples are ready. Negative or zero values indicate no interrupts + * are available, meaning polling is required. + * + * Output Data Rate is configured for the highest possible rate so that + * conversion time and power consumption are reduced to a minimum. + * Note that hardware internal averaging machinery (not implemented in this + * driver) is not applicable in this mode. + * + * Device must have been previously enabled before calling + * zpa2326_config_oneshot(). + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_config_oneshot(const struct iio_dev *indio_dev, + int irq) +{ + struct regmap *regs = ((struct zpa2326_private *) + iio_priv(indio_dev))->regmap; + const struct zpa2326_frequency *freq = zpa2326_highest_frequency(); + int err; + + /* Setup highest available Output Data Rate for one shot mode. */ + err = regmap_write(regs, ZPA2326_CTRL_REG3_REG, freq->odr); + if (err) + return err; + + if (irq > 0) { + /* Request interrupt when new sample is available. */ + err = regmap_write(regs, ZPA2326_CTRL_REG1_REG, + (u8)~ZPA2326_CTRL_REG1_MASK_DATA_READY); + + if (err) { + dev_err(indio_dev->dev.parent, + "failed to setup one shot mode (%d)", err); + return err; + } + } + + zpa2326_dbg(indio_dev, "one shot mode setup @%dHz", freq->hz); + + return 0; +} + +/** + * zpa2326_clear_fifo() - Clear remaining entries in hardware FIFO. + * @indio_dev: The IIO device associated with the sampling hardware. + * @min_count: Number of samples present within hardware FIFO. + * + * @min_count argument is a hint corresponding to the known minimum number of + * samples currently living in the FIFO. This allows to reduce the number of bus + * accesses by skipping status register read operation as long as we know for + * sure there are still entries left. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_clear_fifo(const struct iio_dev *indio_dev, + unsigned int min_count) +{ + struct regmap *regs = ((struct zpa2326_private *) + iio_priv(indio_dev))->regmap; + int err; + unsigned int val; + + if (!min_count) { + /* + * No hint: read status register to determine whether FIFO is + * empty or not. + */ + err = regmap_read(regs, ZPA2326_STATUS_REG, &val); + + if (err < 0) + goto err; + + if (val & ZPA2326_STATUS_FIFO_E) + /* Fifo is empty: nothing to trash. */ + return 0; + } + + /* Clear FIFO. */ + do { + /* + * A single fetch from pressure MSB register is enough to pop + * values out of FIFO. + */ + err = regmap_read(regs, ZPA2326_PRESS_OUT_H_REG, &val); + if (err < 0) + goto err; + + if (min_count) { + /* + * We know for sure there are at least min_count entries + * left in FIFO. Skip status register read. + */ + min_count--; + continue; + } + + err = regmap_read(regs, ZPA2326_STATUS_REG, &val); + if (err < 0) + goto err; + + } while (!(val & ZPA2326_STATUS_FIFO_E)); + + zpa2326_dbg(indio_dev, "FIFO cleared"); + + return 0; + +err: + zpa2326_err(indio_dev, "failed to clear FIFO (%d)", err); + + return err; +} + +/** + * zpa2326_dequeue_pressure() - Retrieve the most recent pressure sample from + * hardware FIFO. + * @indio_dev: The IIO device associated with the sampling hardware. + * @pressure: Sampled pressure output. + * + * Note that ZPA2326 hardware FIFO stores pressure samples only. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_dequeue_pressure(const struct iio_dev *indio_dev, + u32 *pressure) +{ + struct regmap *regs = ((struct zpa2326_private *) + iio_priv(indio_dev))->regmap; + unsigned int val; + int err; + int cleared = -1; + + err = regmap_read(regs, ZPA2326_STATUS_REG, &val); + if (err < 0) + return err; + + *pressure = 0; + + if (val & ZPA2326_STATUS_P_OR) { + /* + * Fifo overrun : first sample dequeued from FIFO is the + * newest. + */ + zpa2326_warn(indio_dev, "FIFO overflow"); + + err = regmap_bulk_read(regs, ZPA2326_PRESS_OUT_XL_REG, pressure, + 3); + if (err) + return err; + +#define ZPA2326_FIFO_DEPTH (16U) + /* Hardware FIFO may hold no more than 16 pressure samples. */ + return zpa2326_clear_fifo(indio_dev, ZPA2326_FIFO_DEPTH - 1); + } + + /* + * Fifo has not overflown : retrieve newest sample. We need to pop + * values out until FIFO is empty : last fetched pressure is the newest. + * In nominal cases, we should find a single queued sample only. + */ + do { + err = regmap_bulk_read(regs, ZPA2326_PRESS_OUT_XL_REG, pressure, + 3); + if (err) + return err; + + err = regmap_read(regs, ZPA2326_STATUS_REG, &val); + if (err < 0) + return err; + + cleared++; + } while (!(val & ZPA2326_STATUS_FIFO_E)); + + if (cleared) + /* + * Samples were pushed by hardware during previous rounds but we + * didn't consume them fast enough: inform user. + */ + zpa2326_dbg(indio_dev, "cleared %d FIFO entries", cleared); + + return 0; +} + +/** + * zpa2326_fill_sample_buffer() - Enqueue new channel samples to IIO buffer. + * @indio_dev: The IIO device associated with the sampling hardware. + * @private: Internal private state related to @indio_dev. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_fill_sample_buffer(struct iio_dev *indio_dev, + const struct zpa2326_private *private) +{ + struct { + u32 pressure; + u16 temperature; + u64 timestamp; + } sample; + int err; + + if (test_bit(0, indio_dev->active_scan_mask)) { + /* Get current pressure from hardware FIFO. */ + err = zpa2326_dequeue_pressure(indio_dev, &sample.pressure); + if (err) { + zpa2326_warn(indio_dev, "failed to fetch pressure (%d)", + err); + return err; + } + } + + if (test_bit(1, indio_dev->active_scan_mask)) { + /* Get current temperature. */ + err = regmap_bulk_read(private->regmap, ZPA2326_TEMP_OUT_L_REG, + &sample.temperature, 2); + if (err) { + zpa2326_warn(indio_dev, + "failed to fetch temperature (%d)", err); + return err; + } + } + + /* + * Now push samples using timestamp stored either : + * - by hardware interrupt handler if interrupt is available: see + * zpa2326_handle_irq(), + * - or oneshot completion polling machinery : see + * zpa2326_trigger_handler(). + */ + zpa2326_dbg(indio_dev, "filling raw samples buffer"); + + iio_push_to_buffers_with_timestamp(indio_dev, &sample, + private->timestamp); + + return 0; +} + +#ifdef CONFIG_PM +static int zpa2326_runtime_suspend(struct device *parent) +{ + const struct iio_dev *indio_dev = dev_get_drvdata(parent); + + if (pm_runtime_autosuspend_expiration(parent)) + /* Userspace changed autosuspend delay. */ + return -EAGAIN; + + zpa2326_power_off(indio_dev, iio_priv(indio_dev)); + + return 0; +} + +static int zpa2326_runtime_resume(struct device *parent) +{ + const struct iio_dev *indio_dev = dev_get_drvdata(parent); + + return zpa2326_power_on(indio_dev, iio_priv(indio_dev)); +} + +const struct dev_pm_ops zpa2326_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(zpa2326_runtime_suspend, zpa2326_runtime_resume, + NULL) +}; +EXPORT_SYMBOL_GPL(zpa2326_pm_ops); + +/** + * zpa2326_resume() - Request the PM layer to power supply the device. + * @indio_dev: The IIO device associated with the sampling hardware. + * + * Return: + * < 0 - a negative error code meaning failure ; + * 0 - success, device has just been powered up ; + * 1 - success, device was already powered. + */ +static int zpa2326_resume(const struct iio_dev *indio_dev) +{ + int err; + + err = pm_runtime_get_sync(indio_dev->dev.parent); + if (err < 0) + return err; + + if (err > 0) { + /* + * Device was already power supplied: get it out of low power + * mode and inform caller. + */ + zpa2326_enable_device(indio_dev); + return 1; + } + + /* Inform caller device has just been brought back to life. */ + return 0; +} + +/** + * zpa2326_suspend() - Schedule a power down using autosuspend feature of PM + * layer. + * @indio_dev: The IIO device associated with the sampling hardware. + * + * Device is switched to low power mode at first to save power even when + * attached regulator is a "dummy" one. + */ +static void zpa2326_suspend(struct iio_dev *indio_dev) +{ + struct device *parent = indio_dev->dev.parent; + + zpa2326_sleep(indio_dev); + + pm_runtime_mark_last_busy(parent); + pm_runtime_put_autosuspend(parent); +} + +static void zpa2326_init_runtime(struct device *parent) +{ + pm_runtime_get_noresume(parent); + pm_runtime_set_active(parent); + pm_runtime_enable(parent); + pm_runtime_set_autosuspend_delay(parent, 1000); + pm_runtime_use_autosuspend(parent); + pm_runtime_mark_last_busy(parent); + pm_runtime_put_autosuspend(parent); +} + +static void zpa2326_fini_runtime(struct device *parent) +{ + pm_runtime_disable(parent); + pm_runtime_set_suspended(parent); +} +#else /* !CONFIG_PM */ +static int zpa2326_resume(const struct iio_dev *indio_dev) +{ + zpa2326_enable_device(indio_dev); + + return 0; +} + +static void zpa2326_suspend(struct iio_dev *indio_dev) +{ + zpa2326_sleep(indio_dev); +} + +#define zpa2326_init_runtime(_parent) +#define zpa2326_fini_runtime(_parent) +#endif /* !CONFIG_PM */ + +/** + * zpa2326_handle_irq() - Process hardware interrupts. + * @irq: Interrupt line the hardware uses to notify new data has arrived. + * @data: The IIO device associated with the sampling hardware. + * + * Timestamp buffered samples as soon as possible then schedule threaded bottom + * half. + * + * Return: Always successful. + */ +static irqreturn_t zpa2326_handle_irq(int irq, void *data) +{ + struct iio_dev *indio_dev = (struct iio_dev *)data; + + if (iio_buffer_enabled(indio_dev)) { + /* Timestamping needed for buffered sampling only. */ + ((struct zpa2326_private *) + iio_priv(indio_dev))->timestamp = iio_get_time_ns(indio_dev); + } + + return IRQ_WAKE_THREAD; +} + +/** + * zpa2326_handle_threaded_irq() - Interrupt bottom-half handler. + * @irq: Interrupt line the hardware uses to notify new data has arrived. + * @data: The IIO device associated with the sampling hardware. + * + * Mainly ensures interrupt is caused by a real "new sample available" + * condition. This relies upon the ability to perform blocking / sleeping bus + * accesses to slave's registers. This is why zpa2326_handle_threaded_irq() is + * called from within a thread, i.e. not called from hard interrupt context. + * + * When device is using its own internal hardware trigger in continuous sampling + * mode, data are available into hardware FIFO once interrupt has occurred. All + * we have to do is to dispatch the trigger, which in turn will fetch data and + * fill IIO buffer. + * + * When not using its own internal hardware trigger, the device has been + * configured in one-shot mode either by an external trigger or the IIO read_raw + * hook. This means one of the latter is currently waiting for sampling + * completion, in which case we must simply wake it up. + * + * See zpa2326_trigger_handler(). + * + * Return: + * %IRQ_NONE - no consistent interrupt happened ; + * %IRQ_HANDLED - there was new samples available. + */ +static irqreturn_t zpa2326_handle_threaded_irq(int irq, void *data) +{ + struct iio_dev *indio_dev = (struct iio_dev *)data; + struct zpa2326_private *priv = iio_priv(indio_dev); + unsigned int val; + bool cont; + irqreturn_t ret = IRQ_NONE; + + /* + * Are we using our own internal trigger in triggered buffer mode, i.e., + * currently working in continuous sampling mode ? + */ + cont = (iio_buffer_enabled(indio_dev) && + iio_trigger_using_own(indio_dev)); + + /* + * Device works according to a level interrupt scheme: reading interrupt + * status de-asserts interrupt line. + */ + priv->result = regmap_read(priv->regmap, ZPA2326_INT_SOURCE_REG, &val); + if (priv->result < 0) { + if (cont) + return IRQ_NONE; + + goto complete; + } + + /* Data ready is the only interrupt source we requested. */ + if (!(val & ZPA2326_INT_SOURCE_DATA_READY)) { + /* + * Interrupt happened but no new sample available: likely caused + * by spurious interrupts, in which case, returning IRQ_NONE + * allows to benefit from the generic spurious interrupts + * handling. + */ + zpa2326_warn(indio_dev, "unexpected interrupt status %02x", + val); + + if (cont) + return IRQ_NONE; + + priv->result = -ENODATA; + goto complete; + } + + /* New sample available: dispatch internal trigger consumers. */ + iio_trigger_poll_chained(priv->trigger); + + if (cont) + /* + * Internal hardware trigger has been scheduled above : it will + * fetch data on its own. + */ + return IRQ_HANDLED; + + ret = IRQ_HANDLED; + +complete: + /* + * Wake up direct or externaly triggered buffer mode waiters: see + * zpa2326_sample_oneshot() and zpa2326_trigger_handler(). + */ + complete(&priv->data_ready); + + return ret; +} + +/** + * zpa2326_wait_oneshot_completion() - Wait for oneshot data ready interrupt. + * @indio_dev: The IIO device associated with the sampling hardware. + * @private: Internal private state related to @indio_dev. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_wait_oneshot_completion(const struct iio_dev *indio_dev, + struct zpa2326_private *private) +{ + int ret; + unsigned int val; + + zpa2326_dbg(indio_dev, "waiting for one shot completion interrupt"); + + ret = wait_for_completion_interruptible_timeout( + &private->data_ready, ZPA2326_CONVERSION_JIFFIES); + if (ret > 0) + /* + * Interrupt handler completed before timeout: return operation + * status. + */ + return private->result; + + /* Clear all interrupts just to be sure. */ + regmap_read(private->regmap, ZPA2326_INT_SOURCE_REG, &val); + + if (!ret) + /* Timed out. */ + ret = -ETIME; + + if (ret != -ERESTARTSYS) + zpa2326_warn(indio_dev, "no one shot interrupt occurred (%d)", + ret); + + return ret; +} + +static int zpa2326_init_managed_irq(struct device *parent, + struct iio_dev *indio_dev, + struct zpa2326_private *private, + int irq) +{ + int err; + + private->irq = irq; + + if (irq <= 0) { + /* + * Platform declared no interrupt line: device will be polled + * for data availability. + */ + dev_info(parent, "no interrupt found, running in polling mode"); + return 0; + } + + init_completion(&private->data_ready); + + /* Request handler to be scheduled into threaded interrupt context. */ + err = devm_request_threaded_irq(parent, irq, zpa2326_handle_irq, + zpa2326_handle_threaded_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + dev_name(parent), indio_dev); + if (err) { + dev_err(parent, "failed to request interrupt %d (%d)", irq, + err); + return err; + } + + dev_info(parent, "using interrupt %d", irq); + + return 0; +} + +/** + * zpa2326_poll_oneshot_completion() - Actively poll for one shot data ready. + * @indio_dev: The IIO device associated with the sampling hardware. + * + * Loop over registers content to detect end of sampling cycle. Used when DT + * declared no valid interrupt lines. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_poll_oneshot_completion(const struct iio_dev *indio_dev) +{ + unsigned long tmout = jiffies + ZPA2326_CONVERSION_JIFFIES; + struct regmap *regs = ((struct zpa2326_private *) + iio_priv(indio_dev))->regmap; + unsigned int val; + int err; + + zpa2326_dbg(indio_dev, "polling for one shot completion"); + + /* + * At least, 100 ms is needed for the device to complete its one-shot + * cycle. + */ + if (msleep_interruptible(100)) + return -ERESTARTSYS; + + /* Poll for conversion completion in hardware. */ + while (true) { + err = regmap_read(regs, ZPA2326_CTRL_REG0_REG, &val); + if (err < 0) + goto err; + + if (!(val & ZPA2326_CTRL_REG0_ONE_SHOT)) + /* One-shot bit self clears at conversion end. */ + break; + + if (time_after(jiffies, tmout)) { + /* Prevent from waiting forever : let's time out. */ + err = -ETIME; + goto err; + } + + usleep_range(10000, 20000); + } + + /* + * In oneshot mode, pressure sample availability guarantees that + * temperature conversion has also completed : just check pressure + * status bit to keep things simple. + */ + err = regmap_read(regs, ZPA2326_STATUS_REG, &val); + if (err < 0) + goto err; + + if (!(val & ZPA2326_STATUS_P_DA)) { + /* No sample available. */ + err = -ENODATA; + goto err; + } + + return 0; + +err: + zpa2326_warn(indio_dev, "failed to poll one shot completion (%d)", err); + + return err; +} + +/** + * zpa2326_fetch_raw_sample() - Retrieve a raw sample and convert it to CPU + * endianness. + * @indio_dev: The IIO device associated with the sampling hardware. + * @type: Type of measurement / channel to fetch from. + * @value: Sample output. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_fetch_raw_sample(const struct iio_dev *indio_dev, + enum iio_chan_type type, + int *value) +{ + struct regmap *regs = ((struct zpa2326_private *) + iio_priv(indio_dev))->regmap; + int err; + + switch (type) { + case IIO_PRESSURE: + zpa2326_dbg(indio_dev, "fetching raw pressure sample"); + + err = regmap_bulk_read(regs, ZPA2326_PRESS_OUT_XL_REG, value, + 3); + if (err) { + zpa2326_warn(indio_dev, "failed to fetch pressure (%d)", + err); + return err; + } + + /* Pressure is a 24 bits wide little-endian unsigned int. */ + *value = (((u8 *)value)[2] << 16) | (((u8 *)value)[1] << 8) | + ((u8 *)value)[0]; + + return IIO_VAL_INT; + + case IIO_TEMP: + zpa2326_dbg(indio_dev, "fetching raw temperature sample"); + + err = regmap_bulk_read(regs, ZPA2326_TEMP_OUT_L_REG, value, 2); + if (err) { + zpa2326_warn(indio_dev, + "failed to fetch temperature (%d)", err); + return err; + } + + /* Temperature is a 16 bits wide little-endian signed int. */ + *value = (int)le16_to_cpup((__le16 *)value); + + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +/** + * zpa2326_sample_oneshot() - Perform a complete one shot sampling cycle. + * @indio_dev: The IIO device associated with the sampling hardware. + * @type: Type of measurement / channel to fetch from. + * @value: Sample output. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_sample_oneshot(struct iio_dev *indio_dev, + enum iio_chan_type type, + int *value) +{ + int ret; + struct zpa2326_private *priv; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = zpa2326_resume(indio_dev); + if (ret < 0) + goto release; + + priv = iio_priv(indio_dev); + + if (ret > 0) { + /* + * We were already power supplied. Just clear hardware FIFO to + * get rid of samples acquired during previous rounds (if any). + * Sampling operation always generates both temperature and + * pressure samples. The latter are always enqueued into + * hardware FIFO. This may lead to situations were pressure + * samples still sit into FIFO when previous cycle(s) fetched + * temperature data only. + * Hence, we need to clear hardware FIFO content to prevent from + * getting outdated values at the end of current cycle. + */ + if (type == IIO_PRESSURE) { + ret = zpa2326_clear_fifo(indio_dev, 0); + if (ret) + goto suspend; + } + } else { + /* + * We have just been power supplied, i.e. device is in default + * "out of reset" state, meaning we need to reconfigure it + * entirely. + */ + ret = zpa2326_config_oneshot(indio_dev, priv->irq); + if (ret) + goto suspend; + } + + /* Start a sampling cycle in oneshot mode. */ + ret = zpa2326_start_oneshot(indio_dev); + if (ret) + goto suspend; + + /* Wait for sampling cycle to complete. */ + if (priv->irq > 0) + ret = zpa2326_wait_oneshot_completion(indio_dev, priv); + else + ret = zpa2326_poll_oneshot_completion(indio_dev); + + if (ret) + goto suspend; + + /* Retrieve raw sample value and convert it to CPU endianness. */ + ret = zpa2326_fetch_raw_sample(indio_dev, type, value); + +suspend: + zpa2326_suspend(indio_dev); +release: + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +/** + * zpa2326_trigger_handler() - Perform an IIO buffered sampling round in one + * shot mode. + * @irq: The software interrupt assigned to @data + * @data: The IIO poll function dispatched by external trigger our device is + * attached to. + * + * Bottom-half handler called by the IIO trigger to which our device is + * currently attached. Allows us to synchronize this device buffered sampling + * either with external events (such as timer expiration, external device sample + * ready, etc...) or with its own interrupt (internal hardware trigger). + * + * When using an external trigger, basically run the same sequence of operations + * as for zpa2326_sample_oneshot() with the following hereafter. Hardware FIFO + * is not cleared since already done at buffering enable time and samples + * dequeueing always retrieves the most recent value. + * + * Otherwise, when internal hardware trigger has dispatched us, just fetch data + * from hardware FIFO. + * + * Fetched data will pushed unprocessed to IIO buffer since samples conversion + * is delegated to userspace in buffered mode (endianness, etc...). + * + * Return: + * %IRQ_NONE - no consistent interrupt happened ; + * %IRQ_HANDLED - there was new samples available. + */ +static irqreturn_t zpa2326_trigger_handler(int irq, void *data) +{ + struct iio_dev *indio_dev = ((struct iio_poll_func *) + data)->indio_dev; + struct zpa2326_private *priv = iio_priv(indio_dev); + bool cont; + + /* + * We have been dispatched, meaning we are in triggered buffer mode. + * Using our own internal trigger implies we are currently in continuous + * hardware sampling mode. + */ + cont = iio_trigger_using_own(indio_dev); + + if (!cont) { + /* On demand sampling : start a one shot cycle. */ + if (zpa2326_start_oneshot(indio_dev)) + goto out; + + /* Wait for sampling cycle to complete. */ + if (priv->irq <= 0) { + /* No interrupt available: poll for completion. */ + if (zpa2326_poll_oneshot_completion(indio_dev)) + goto out; + + /* Only timestamp sample once it is ready. */ + priv->timestamp = iio_get_time_ns(indio_dev); + } else { + /* Interrupt handlers will timestamp for us. */ + if (zpa2326_wait_oneshot_completion(indio_dev, priv)) + goto out; + } + } + + /* Enqueue to IIO buffer / userspace. */ + zpa2326_fill_sample_buffer(indio_dev, priv); + +out: + if (!cont) + /* Don't switch to low power if sampling continuously. */ + zpa2326_sleep(indio_dev); + + /* Inform attached trigger we are done. */ + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +/** + * zpa2326_preenable_buffer() - Prepare device for configuring triggered + * sampling + * modes. + * @indio_dev: The IIO device associated with the sampling hardware. + * + * Basically power up device. + * Called with IIO device's lock held. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_preenable_buffer(struct iio_dev *indio_dev) +{ + int ret = zpa2326_resume(indio_dev); + + if (ret < 0) + return ret; + + /* Tell zpa2326_postenable_buffer() if we have just been powered on. */ + ((struct zpa2326_private *) + iio_priv(indio_dev))->waken = iio_priv(indio_dev); + + return 0; +} + +/** + * zpa2326_postenable_buffer() - Configure device for triggered sampling. + * @indio_dev: The IIO device associated with the sampling hardware. + * + * Basically setup one-shot mode if plugging external trigger. + * Otherwise, let internal trigger configure continuous sampling : + * see zpa2326_set_trigger_state(). + * + * If an error is returned, IIO layer will call our postdisable hook for us, + * i.e. no need to explicitly power device off here. + * Called with IIO device's lock held. + * + * Called with IIO device's lock held. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_postenable_buffer(struct iio_dev *indio_dev) +{ + const struct zpa2326_private *priv = iio_priv(indio_dev); + int err; + + if (!priv->waken) { + /* + * We were already power supplied. Just clear hardware FIFO to + * get rid of samples acquired during previous rounds (if any). + */ + err = zpa2326_clear_fifo(indio_dev, 0); + if (err) + goto err; + } + + if (!iio_trigger_using_own(indio_dev) && priv->waken) { + /* + * We are using an external trigger and we have just been + * powered up: reconfigure one-shot mode. + */ + err = zpa2326_config_oneshot(indio_dev, priv->irq); + if (err) + goto err; + } + + /* Plug our own trigger event handler. */ + err = iio_triggered_buffer_postenable(indio_dev); + if (err) + goto err; + + return 0; + +err: + zpa2326_err(indio_dev, "failed to enable buffering (%d)", err); + + return err; +} + +static int zpa2326_postdisable_buffer(struct iio_dev *indio_dev) +{ + zpa2326_suspend(indio_dev); + + return 0; +} + +static const struct iio_buffer_setup_ops zpa2326_buffer_setup_ops = { + .preenable = zpa2326_preenable_buffer, + .postenable = zpa2326_postenable_buffer, + .predisable = iio_triggered_buffer_predisable, + .postdisable = zpa2326_postdisable_buffer +}; + +/** + * zpa2326_set_trigger_state() - Start / stop continuous sampling. + * @trig: The trigger being attached to IIO device associated with the sampling + * hardware. + * @state: Tell whether to start (true) or stop (false) + * + * Basically enable / disable hardware continuous sampling mode. + * + * Called with IIO device's lock held at postenable() or predisable() time. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_set_trigger_state(struct iio_trigger *trig, bool state) +{ + const struct iio_dev *indio_dev = dev_get_drvdata( + trig->dev.parent); + const struct zpa2326_private *priv = iio_priv(indio_dev); + int err; + + if (!state) { + /* + * Switch trigger off : in case of failure, interrupt is left + * disabled in order to prevent handler from accessing released + * resources. + */ + unsigned int val; + + /* + * As device is working in continuous mode, handlers may be + * accessing resources we are currently freeing... + * Prevent this by disabling interrupt handlers and ensure + * the device will generate no more interrupts unless explicitly + * required to, i.e. by restoring back to default one shot mode. + */ + disable_irq(priv->irq); + + /* + * Disable continuous sampling mode to restore settings for + * one shot / direct sampling operations. + */ + err = regmap_write(priv->regmap, ZPA2326_CTRL_REG3_REG, + zpa2326_highest_frequency()->odr); + if (err) + return err; + + /* + * Now that device won't generate interrupts on its own, + * acknowledge any currently active interrupts (may happen on + * rare occasions while stopping continuous mode). + */ + err = regmap_read(priv->regmap, ZPA2326_INT_SOURCE_REG, &val); + if (err < 0) + return err; + + /* + * Re-enable interrupts only if we can guarantee the device will + * generate no more interrupts to prevent handlers from + * accessing released resources. + */ + enable_irq(priv->irq); + + zpa2326_dbg(indio_dev, "continuous mode stopped"); + } else { + /* + * Switch trigger on : start continuous sampling at required + * frequency. + */ + + if (priv->waken) { + /* Enable interrupt if getting out of reset. */ + err = regmap_write(priv->regmap, ZPA2326_CTRL_REG1_REG, + (u8) + ~ZPA2326_CTRL_REG1_MASK_DATA_READY); + if (err) + return err; + } + + /* Enable continuous sampling at specified frequency. */ + err = regmap_write(priv->regmap, ZPA2326_CTRL_REG3_REG, + ZPA2326_CTRL_REG3_ENABLE_MEAS | + priv->frequency->odr); + if (err) + return err; + + zpa2326_dbg(indio_dev, "continuous mode setup @%dHz", + priv->frequency->hz); + } + + return 0; +} + +static const struct iio_trigger_ops zpa2326_trigger_ops = { + .owner = THIS_MODULE, + .set_trigger_state = zpa2326_set_trigger_state, +}; + +/** + * zpa2326_init_trigger() - Create an interrupt driven / hardware trigger + * allowing to notify external devices a new sample is + * ready. + * @parent: Hardware sampling device @indio_dev is a child of. + * @indio_dev: The IIO device associated with the sampling hardware. + * @private: Internal private state related to @indio_dev. + * @irq: Optional interrupt line the hardware uses to notify new data + * samples are ready. Negative or zero values indicate no interrupts + * are available, meaning polling is required. + * + * Only relevant when DT declares a valid interrupt line. + * + * Return: Zero when successful, a negative error code otherwise. + */ +static int zpa2326_init_managed_trigger(struct device *parent, + struct iio_dev *indio_dev, + struct zpa2326_private *private, + int irq) +{ + struct iio_trigger *trigger; + int ret; + + if (irq <= 0) + return 0; + + trigger = devm_iio_trigger_alloc(parent, "%s-dev%d", + indio_dev->name, indio_dev->id); + if (!trigger) + return -ENOMEM; + + /* Basic setup. */ + trigger->dev.parent = parent; + trigger->ops = &zpa2326_trigger_ops; + + private->trigger = trigger; + + /* Register to triggers space. */ + ret = devm_iio_trigger_register(parent, trigger); + if (ret) + dev_err(parent, "failed to register hardware trigger (%d)", + ret); + + return ret; +} + +static int zpa2326_get_frequency(const struct iio_dev *indio_dev) +{ + return ((struct zpa2326_private *)iio_priv(indio_dev))->frequency->hz; +} + +static int zpa2326_set_frequency(struct iio_dev *indio_dev, int hz) +{ + struct zpa2326_private *priv = iio_priv(indio_dev); + int freq; + int err; + + /* Check if requested frequency is supported. */ + for (freq = 0; freq < ARRAY_SIZE(zpa2326_sampling_frequencies); freq++) + if (zpa2326_sampling_frequencies[freq].hz == hz) + break; + if (freq == ARRAY_SIZE(zpa2326_sampling_frequencies)) + return -EINVAL; + + /* Don't allow changing frequency if buffered sampling is ongoing. */ + err = iio_device_claim_direct_mode(indio_dev); + if (err) + return err; + + priv->frequency = &zpa2326_sampling_frequencies[freq]; + + iio_device_release_direct_mode(indio_dev); + + return 0; +} + +/* Expose supported hardware sampling frequencies (Hz) through sysfs. */ +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1 5 11 23"); + +static struct attribute *zpa2326_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group zpa2326_attribute_group = { + .attrs = zpa2326_attributes, +}; + +static int zpa2326_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_RAW: + return zpa2326_sample_oneshot(indio_dev, chan->type, val); + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_PRESSURE: + /* + * Pressure resolution is 1/64 Pascal. Scale to kPascal + * as required by IIO ABI. + */ + *val = 1; + *val2 = 64000; + return IIO_VAL_FRACTIONAL; + + case IIO_TEMP: + /* + * Temperature follows the equation: + * Temp[degC] = Tempcode * 0.00649 - 176.83 + * where: + * Tempcode is composed the raw sampled 16 bits. + * + * Hence, to produce a temperature in milli-degrees + * Celsius according to IIO ABI, we need to apply the + * following equation to raw samples: + * Temp[milli degC] = (Tempcode + Offset) * Scale + * where: + * Offset = -176.83 / 0.00649 + * Scale = 0.00649 * 1000 + */ + *val = 6; + *val2 = 490000; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + *val = -17683000; + *val2 = 649; + return IIO_VAL_FRACTIONAL; + + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = zpa2326_get_frequency(indio_dev); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int zpa2326_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int val, + int val2, + long mask) +{ + if ((mask != IIO_CHAN_INFO_SAMP_FREQ) || val2) + return -EINVAL; + + return zpa2326_set_frequency(indio_dev, val); +} + +static const struct iio_chan_spec zpa2326_channels[] = { + [0] = { + .type = IIO_PRESSURE, + .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), + }, + [1] = { + .type = IIO_TEMP, + .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) | + BIT(IIO_CHAN_INFO_OFFSET), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + }, + [2] = IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static const struct iio_info zpa2326_info = { + .driver_module = THIS_MODULE, + .attrs = &zpa2326_attribute_group, + .read_raw = zpa2326_read_raw, + .write_raw = zpa2326_write_raw, +}; + +static struct iio_dev *zpa2326_create_managed_iiodev(struct device *device, + const char *name, + struct regmap *regmap) +{ + struct iio_dev *indio_dev; + + /* Allocate space to hold IIO device internal state. */ + indio_dev = devm_iio_device_alloc(device, + sizeof(struct zpa2326_private)); + if (!indio_dev) + return NULL; + + /* Setup for userspace synchronous on demand sampling. */ + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->dev.parent = device; + indio_dev->channels = zpa2326_channels; + indio_dev->num_channels = ARRAY_SIZE(zpa2326_channels); + indio_dev->name = name; + indio_dev->info = &zpa2326_info; + + return indio_dev; +} + +int zpa2326_probe(struct device *parent, + const char *name, + int irq, + unsigned int hwid, + struct regmap *regmap) +{ + struct iio_dev *indio_dev; + struct zpa2326_private *priv; + int err; + unsigned int id; + + indio_dev = zpa2326_create_managed_iiodev(parent, name, regmap); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + + priv->vref = devm_regulator_get(parent, "vref"); + if (IS_ERR(priv->vref)) + return PTR_ERR(priv->vref); + + priv->vdd = devm_regulator_get(parent, "vdd"); + if (IS_ERR(priv->vdd)) + return PTR_ERR(priv->vdd); + + /* Set default hardware sampling frequency to highest rate supported. */ + priv->frequency = zpa2326_highest_frequency(); + + /* + * Plug device's underlying bus abstraction : this MUST be set before + * registering interrupt handlers since an interrupt might happen if + * power up sequence is not properly applied. + */ + priv->regmap = regmap; + + err = devm_iio_triggered_buffer_setup(parent, indio_dev, NULL, + zpa2326_trigger_handler, + &zpa2326_buffer_setup_ops); + if (err) + return err; + + err = zpa2326_init_managed_trigger(parent, indio_dev, priv, irq); + if (err) + return err; + + err = zpa2326_init_managed_irq(parent, indio_dev, priv, irq); + if (err) + return err; + + /* Power up to check device ID and perform initial hardware setup. */ + err = zpa2326_power_on(indio_dev, priv); + if (err) + return err; + + /* Read id register to check we are talking to the right slave. */ + err = regmap_read(regmap, ZPA2326_DEVICE_ID_REG, &id); + if (err) + goto sleep; + + if (id != hwid) { + dev_err(parent, "found device with unexpected id %02x", id); + err = -ENODEV; + goto sleep; + } + + err = zpa2326_config_oneshot(indio_dev, irq); + if (err) + goto sleep; + + /* Setup done : go sleeping. Device will be awaken upon user request. */ + err = zpa2326_sleep(indio_dev); + if (err) + goto poweroff; + + dev_set_drvdata(parent, indio_dev); + + zpa2326_init_runtime(parent); + + err = iio_device_register(indio_dev); + if (err) { + zpa2326_fini_runtime(parent); + goto poweroff; + } + + return 0; + +sleep: + /* Put to sleep just in case power regulators are "dummy" ones. */ + zpa2326_sleep(indio_dev); +poweroff: + zpa2326_power_off(indio_dev, priv); + + return err; +} +EXPORT_SYMBOL_GPL(zpa2326_probe); + +void zpa2326_remove(const struct device *parent) +{ + struct iio_dev *indio_dev = dev_get_drvdata(parent); + + iio_device_unregister(indio_dev); + zpa2326_fini_runtime(indio_dev->dev.parent); + zpa2326_sleep(indio_dev); + zpa2326_power_off(indio_dev, iio_priv(indio_dev)); +} +EXPORT_SYMBOL_GPL(zpa2326_remove); + +MODULE_AUTHOR("Gregor Boirie "); +MODULE_DESCRIPTION("Core driver for Murata ZPA2326 pressure sensor"); +MODULE_LICENSE("GPL v2"); diff --git b/drivers/iio/pressure/zpa2326.h b/drivers/iio/pressure/zpa2326.h new file mode 100644 index 0000000..05d3e1e --- /dev/null +++ b/drivers/iio/pressure/zpa2326.h @@ -0,0 +1,89 @@ +/* + * Murata ZPA2326 pressure and temperature sensor IIO driver + * + * Copyright (c) 2016 Parrot S.A. + * + * Author: Gregor Boirie + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef _ZPA2326_H +#define _ZPA2326_H + +/* Register map. */ +#define ZPA2326_REF_P_XL_REG (0x8) +#define ZPA2326_REF_P_L_REG (0x9) +#define ZPA2326_REF_P_H_REG (0xa) +#define ZPA2326_DEVICE_ID_REG (0xf) +#define ZPA2326_DEVICE_ID (0xb9) +#define ZPA2326_RES_CONF_REG (0x10) +#define ZPA2326_CTRL_REG0_REG (0x20) +#define ZPA2326_CTRL_REG0_ONE_SHOT BIT(0) +#define ZPA2326_CTRL_REG0_ENABLE BIT(1) +#define ZPA2326_CTRL_REG1_REG (0x21) +#define ZPA2326_CTRL_REG1_MASK_DATA_READY BIT(2) +#define ZPA2326_CTRL_REG2_REG (0x22) +#define ZPA2326_CTRL_REG2_SWRESET BIT(2) +#define ZPA2326_CTRL_REG3_REG (0x23) +#define ZPA2326_CTRL_REG3_ODR_SHIFT (4) +#define ZPA2326_CTRL_REG3_ENABLE_MEAS BIT(7) +#define ZPA2326_INT_SOURCE_REG (0x24) +#define ZPA2326_INT_SOURCE_DATA_READY BIT(2) +#define ZPA2326_THS_P_LOW_REG (0x25) +#define ZPA2326_THS_P_HIGH_REG (0x26) +#define ZPA2326_STATUS_REG (0x27) +#define ZPA2326_STATUS_P_DA BIT(1) +#define ZPA2326_STATUS_FIFO_E BIT(2) +#define ZPA2326_STATUS_P_OR BIT(5) +#define ZPA2326_PRESS_OUT_XL_REG (0x28) +#define ZPA2326_PRESS_OUT_L_REG (0x29) +#define ZPA2326_PRESS_OUT_H_REG (0x2a) +#define ZPA2326_TEMP_OUT_L_REG (0x2b) +#define ZPA2326_TEMP_OUT_H_REG (0x2c) + +struct device; +struct regmap; + +bool zpa2326_isreg_writeable(struct device *dev, unsigned int reg); +bool zpa2326_isreg_readable(struct device *dev, unsigned int reg); +bool zpa2326_isreg_precious(struct device *dev, unsigned int reg); + +/** + * zpa2326_probe() - Instantiate and register core ZPA2326 IIO device + * @parent: Hardware sampling device the created IIO device will be a child of. + * @name: Arbitrary name to identify the device. + * @irq: Interrupt line, negative if none. + * @hwid: Expected device hardware id. + * @regmap: Registers map used to abstract underlying bus accesses. + * + * Return: Zero when successful, a negative error code otherwise. + */ +int zpa2326_probe(struct device *parent, + const char *name, + int irq, + unsigned int hwid, + struct regmap *regmap); + +/** + * zpa2326_remove() - Unregister and destroy core ZPA2326 IIO device. + * @parent: Hardware sampling device the IIO device to remove is a child of. + */ +void zpa2326_remove(const struct device *parent); + +#ifdef CONFIG_PM +#include +extern const struct dev_pm_ops zpa2326_pm_ops; +#define ZPA2326_PM_OPS (&zpa2326_pm_ops) +#else +#define ZPA2326_PM_OPS (NULL) +#endif + +#endif diff --git b/drivers/iio/pressure/zpa2326_i2c.c b/drivers/iio/pressure/zpa2326_i2c.c new file mode 100644 index 0000000..e4d27dd --- /dev/null +++ b/drivers/iio/pressure/zpa2326_i2c.c @@ -0,0 +1,99 @@ +/* + * Murata ZPA2326 I2C pressure and temperature sensor driver + * + * Copyright (c) 2016 Parrot S.A. + * + * Author: Gregor Boirie + * + * 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 "zpa2326.h" + +/* + * read_flag_mask: + * - address bit 7 must be set to request a register read operation + */ +static const struct regmap_config zpa2326_regmap_i2c_config = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = zpa2326_isreg_writeable, + .readable_reg = zpa2326_isreg_readable, + .precious_reg = zpa2326_isreg_precious, + .max_register = ZPA2326_TEMP_OUT_H_REG, + .read_flag_mask = BIT(7), + .cache_type = REGCACHE_NONE, +}; + +static unsigned int zpa2326_i2c_hwid(const struct i2c_client *client) +{ +#define ZPA2326_SA0(_addr) (_addr & BIT(0)) +#define ZPA2326_DEVICE_ID_SA0_SHIFT (1) + + /* Identification register bit 1 mirrors device address bit 0. */ + return (ZPA2326_DEVICE_ID | + (ZPA2326_SA0(client->addr) << ZPA2326_DEVICE_ID_SA0_SHIFT)); +} + +static int zpa2326_probe_i2c(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &zpa2326_regmap_i2c_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "failed to init registers map"); + return PTR_ERR(regmap); + } + + return zpa2326_probe(&client->dev, i2c_id->name, client->irq, + zpa2326_i2c_hwid(client), regmap); +} + +static int zpa2326_remove_i2c(struct i2c_client *client) +{ + zpa2326_remove(&client->dev); + + return 0; +} + +static const struct i2c_device_id zpa2326_i2c_ids[] = { + { "zpa2326", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, zpa2326_i2c_ids); + +#if defined(CONFIG_OF) +static const struct of_device_id zpa2326_i2c_matches[] = { + { .compatible = "murata,zpa2326" }, + { } +}; +MODULE_DEVICE_TABLE(of, zpa2326_i2c_matches); +#endif + +static struct i2c_driver zpa2326_i2c_driver = { + .driver = { + .name = "zpa2326-i2c", + .of_match_table = of_match_ptr(zpa2326_i2c_matches), + .pm = ZPA2326_PM_OPS, + }, + .probe = zpa2326_probe_i2c, + .remove = zpa2326_remove_i2c, + .id_table = zpa2326_i2c_ids, +}; +module_i2c_driver(zpa2326_i2c_driver); + +MODULE_AUTHOR("Gregor Boirie "); +MODULE_DESCRIPTION("I2C driver for Murata ZPA2326 pressure sensor"); +MODULE_LICENSE("GPL v2"); diff --git b/drivers/iio/pressure/zpa2326_spi.c b/drivers/iio/pressure/zpa2326_spi.c new file mode 100644 index 0000000..bd2c1c3 --- /dev/null +++ b/drivers/iio/pressure/zpa2326_spi.c @@ -0,0 +1,103 @@ +/* + * Murata ZPA2326 SPI pressure and temperature sensor driver + * + * Copyright (c) 2016 Parrot S.A. + * + * Author: Gregor Boirie + * + * 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 "zpa2326.h" + +/* + * read_flag_mask: + * - address bit 7 must be set to request a register read operation + * - address bit 6 must be set to request register address auto increment + */ +static const struct regmap_config zpa2326_regmap_spi_config = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = zpa2326_isreg_writeable, + .readable_reg = zpa2326_isreg_readable, + .precious_reg = zpa2326_isreg_precious, + .max_register = ZPA2326_TEMP_OUT_H_REG, + .read_flag_mask = BIT(7) | BIT(6), + .cache_type = REGCACHE_NONE, +}; + +static int zpa2326_probe_spi(struct spi_device *spi) +{ + struct regmap *regmap; + int err; + + regmap = devm_regmap_init_spi(spi, &zpa2326_regmap_spi_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "failed to init registers map"); + return PTR_ERR(regmap); + } + + /* + * Enforce SPI slave settings to prevent from DT misconfiguration. + * + * Clock is idle high. Sampling happens on trailing edge, i.e., rising + * edge. Maximum bus frequency is 1 MHz. Registers are 8 bits wide. + */ + spi->mode = SPI_MODE_3; + spi->max_speed_hz = min(spi->max_speed_hz, 1000000U); + spi->bits_per_word = 8; + err = spi_setup(spi); + if (err < 0) + return err; + + return zpa2326_probe(&spi->dev, spi_get_device_id(spi)->name, + spi->irq, ZPA2326_DEVICE_ID, regmap); +} + +static int zpa2326_remove_spi(struct spi_device *spi) +{ + zpa2326_remove(&spi->dev); + + return 0; +} + +static const struct spi_device_id zpa2326_spi_ids[] = { + { "zpa2326", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, zpa2326_spi_ids); + +#if defined(CONFIG_OF) +static const struct of_device_id zpa2326_spi_matches[] = { + { .compatible = "murata,zpa2326" }, + { } +}; +MODULE_DEVICE_TABLE(of, zpa2326_spi_matches); +#endif + +static struct spi_driver zpa2326_spi_driver = { + .driver = { + .name = "zpa2326-spi", + .of_match_table = of_match_ptr(zpa2326_spi_matches), + .pm = ZPA2326_PM_OPS, + }, + .probe = zpa2326_probe_spi, + .remove = zpa2326_remove_spi, + .id_table = zpa2326_spi_ids, +}; +module_spi_driver(zpa2326_spi_driver); + +MODULE_AUTHOR("Gregor Boirie "); +MODULE_DESCRIPTION("SPI driver for Murata ZPA2326 pressure sensor"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 1d74b3a..1f06282 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -516,7 +516,7 @@ static irqreturn_t sx9500_irq_thread_handler(int irq, void *private) sx9500_push_events(indio_dev); if (val & SX9500_CONVDONE_IRQ) - complete_all(&data->completion); + complete(&data->completion); out: mutex_unlock(&data->mutex); @@ -1025,6 +1025,12 @@ static const struct acpi_device_id sx9500_acpi_match[] = { }; MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match); +static const struct of_device_id sx9500_of_match[] = { + { .compatible = "semtech,sx9500", }, + { } +}; +MODULE_DEVICE_TABLE(of, sx9500_of_match); + static const struct i2c_device_id sx9500_id[] = { {"sx9500", 0}, { }, @@ -1035,6 +1041,7 @@ static struct i2c_driver sx9500_driver = { .driver = { .name = SX9500_DRIVER_NAME, .acpi_match_table = ACPI_PTR(sx9500_acpi_match), + .of_match_table = of_match_ptr(sx9500_of_match), .pm = &sx9500_pm_ops, }, .probe = sx9500_probe, diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig index c4664e5..5ea77a7 100644 --- a/drivers/iio/temperature/Kconfig +++ b/drivers/iio/temperature/Kconfig @@ -3,6 +3,22 @@ # menu "Temperature sensors" +config MAXIM_THERMOCOUPLE + tristate "Maxim thermocouple sensors" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for the Maxim series of + thermocouple sensors connected via SPI. + + Supported sensors: + * MAX6675 + * MAX31855 + + This driver can also be built as a module. If so, the module will + be called maxim_thermocouple. + config MLX90614 tristate "MLX90614 contact-less infrared sensor" depends on I2C diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index 02bc79d..78c3de0 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -2,6 +2,7 @@ # Makefile for industrial I/O temperature drivers # +obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o obj-$(CONFIG_MLX90614) += mlx90614.o obj-$(CONFIG_TMP006) += tmp006.o obj-$(CONFIG_TSYS01) += tsys01.o diff --git b/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c new file mode 100644 index 0000000..066161a --- /dev/null +++ b/drivers/iio/temperature/maxim_thermocouple.c @@ -0,0 +1,283 @@ +/* + * maxim_thermocouple.c - Support for Maxim thermocouple chips + * + * Copyright (C) 2016 Matt Ranostay + * + * 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 + +#define MAXIM_THERMOCOUPLE_DRV_NAME "maxim_thermocouple" + +enum { + MAX6675, + MAX31855, +}; + +static const struct iio_chan_spec max6675_channels[] = { + { /* thermocouple temperature */ + .type = IIO_TEMP, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 13, + .storagebits = 16, + .shift = 3, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct iio_chan_spec max31855_channels[] = { + { /* thermocouple temperature */ + .type = IIO_TEMP, + .address = 2, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 14, + .storagebits = 16, + .shift = 2, + .endianness = IIO_BE, + }, + }, + { /* cold junction temperature */ + .type = IIO_TEMP, + .address = 0, + .channel2 = IIO_MOD_TEMP_AMBIENT, + .modified = 1, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 1, + .scan_type = { + .sign = 's', + .realbits = 12, + .storagebits = 16, + .shift = 4, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static const unsigned long max31855_scan_masks[] = {0x3, 0}; + +struct maxim_thermocouple_chip { + const struct iio_chan_spec *channels; + const unsigned long *scan_masks; + u8 num_channels; + u8 read_size; + + /* bit-check for valid input */ + u32 status_bit; +}; + +static const struct maxim_thermocouple_chip maxim_thermocouple_chips[] = { + [MAX6675] = { + .channels = max6675_channels, + .num_channels = ARRAY_SIZE(max6675_channels), + .read_size = 2, + .status_bit = BIT(2), + }, + [MAX31855] = { + .channels = max31855_channels, + .num_channels = ARRAY_SIZE(max31855_channels), + .read_size = 4, + .scan_masks = max31855_scan_masks, + .status_bit = BIT(16), + }, +}; + +struct maxim_thermocouple_data { + struct spi_device *spi; + const struct maxim_thermocouple_chip *chip; + + u8 buffer[16] ____cacheline_aligned; +}; + +static int maxim_thermocouple_read(struct maxim_thermocouple_data *data, + struct iio_chan_spec const *chan, int *val) +{ + unsigned int storage_bytes = data->chip->read_size; + unsigned int shift = chan->scan_type.shift + (chan->address * 8); + __be16 buf16; + __be32 buf32; + int ret; + + switch (storage_bytes) { + case 2: + ret = spi_read(data->spi, (void *)&buf16, storage_bytes); + *val = be16_to_cpu(buf16); + break; + case 4: + ret = spi_read(data->spi, (void *)&buf32, storage_bytes); + *val = be32_to_cpu(buf32); + break; + } + + if (ret) + return ret; + + /* check to be sure this is a valid reading */ + if (*val & data->chip->status_bit) + return -EINVAL; + + *val = sign_extend32(*val >> shift, chan->scan_type.realbits - 1); + + return 0; +} + +static irqreturn_t maxim_thermocouple_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct maxim_thermocouple_data *data = iio_priv(indio_dev); + int ret; + + ret = spi_read(data->spi, data->buffer, data->chip->read_size); + if (!ret) { + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_get_time_ns(indio_dev)); + } + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int maxim_thermocouple_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct maxim_thermocouple_data *data = iio_priv(indio_dev); + int ret = -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = maxim_thermocouple_read(data, chan, val); + iio_device_release_direct_mode(indio_dev); + + if (!ret) + return IIO_VAL_INT; + + break; + case IIO_CHAN_INFO_SCALE: + switch (chan->channel2) { + case IIO_MOD_TEMP_AMBIENT: + *val = 62; + *val2 = 500000; /* 1000 * 0.0625 */ + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + *val = 250; /* 1000 * 0.25 */ + ret = IIO_VAL_INT; + }; + break; + } + + return ret; +} + +static const struct iio_info maxim_thermocouple_info = { + .driver_module = THIS_MODULE, + .read_raw = maxim_thermocouple_read_raw, +}; + +static int maxim_thermocouple_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct iio_dev *indio_dev; + struct maxim_thermocouple_data *data; + const struct maxim_thermocouple_chip *chip = + &maxim_thermocouple_chips[id->driver_data]; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->info = &maxim_thermocouple_info; + indio_dev->name = MAXIM_THERMOCOUPLE_DRV_NAME; + indio_dev->channels = chip->channels; + indio_dev->available_scan_masks = chip->scan_masks; + indio_dev->num_channels = chip->num_channels; + indio_dev->modes = INDIO_DIRECT_MODE; + + data = iio_priv(indio_dev); + data->spi = spi; + data->chip = chip; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + maxim_thermocouple_trigger_handler, NULL); + if (ret) + return ret; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_unreg_buffer; + + return 0; + +error_unreg_buffer: + iio_triggered_buffer_cleanup(indio_dev); + + return ret; +} + +static int maxim_thermocouple_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 0; +} + +static const struct spi_device_id maxim_thermocouple_id[] = { + {"max6675", MAX6675}, + {"max31855", MAX31855}, + {}, +}; +MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id); + +static struct spi_driver maxim_thermocouple_driver = { + .driver = { + .name = MAXIM_THERMOCOUPLE_DRV_NAME, + }, + .probe = maxim_thermocouple_probe, + .remove = maxim_thermocouple_remove, + .id_table = maxim_thermocouple_id, +}; +module_spi_driver(maxim_thermocouple_driver); + +MODULE_AUTHOR("Matt Ranostay "); +MODULE_DESCRIPTION("Maxim thermocouple sensors"); +MODULE_LICENSE("GPL"); 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 d002528..e979ee5 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -794,6 +794,25 @@ 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. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" @@ -803,7 +822,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 fb32516..9808964 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -51,11 +51,15 @@ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.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 lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.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; + + /* already instantiated */ + if (dev->of_node) { + pr_debug("%s: dev=%s of_node=%s\n", __func__, + kobject_name(&dev->kobj), + of_node_full_name(dev->of_node)); + return 0; + } + + bus_dev = &pdev->bus->dev; + + pr_debug("%s: %s: %02x:%02x.%02x - node %s%s\n", __func__, + kobject_name(&dev->kobj), + pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), + bus_dev->of_node ? of_node_full_name(bus_dev->of_node) : "", + pci_is_bridge(pdev) ? " bridge" : ""); + + /* to create the node, the bus must be present */ + if (!bus_dev->of_node) { + pr_err("%s: No node for %s because no bus device node\n", + __func__, kobject_name(&dev->kobj)); + return 0; + } + + of_changeset_init(&cset); + + np = of_changeset_create_device_node(&cset, bus_dev->of_node, + "%s/pci-%04x-%02x-%02x.%d", + of_node_full_name(bus_dev->of_node), + pci_domain_nr(pdev->bus), pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + if (IS_ERR(np)) { + ret = PTR_ERR(np); + goto out_cset_fail; + } + + ret = of_changeset_add_property_stringf(&cset, np, "compatible", + "pciclass,%04x", (pdev->class >> 8) & 0xffffff); + if (ret != 0) + goto out_cset_fail; + + ret = of_changeset_add_property_u32(&cset, np, "vendor", + pdev->vendor); + if (ret != 0) + goto out_cset_fail; + + ret = of_changeset_add_property_u32(&cset, np, "device", + pdev->device); + if (ret != 0) + goto out_cset_fail; + + ret = of_changeset_add_property_string(&cset, np, "status", "okay"); + if (ret != 0) + goto out_cset_fail; + + ret = of_changeset_add_property_bool(&cset, np, "auto-generated"); + if (ret != 0) + goto out_cset_fail; + + ret = of_changeset_attach_node(&cset, np); + if (ret != 0) + goto out_cset_fail; + + /* are we creating a bridge; swell */ + npb = NULL; + if (pci_is_bridge(pdev) && !pdev->subordinate->dev.of_node) { + + pr_debug("%s: %s: bus->dev=%s subordinate=%s\n", __func__, + kobject_name(&dev->kobj), + kobject_name(&pdev->bus->dev.kobj), + kobject_name(&pdev->subordinate->dev.kobj)); + + npb = of_changeset_create_device_node(&cset, bus_dev->of_node, + "%s/pci-%04x-%02x", + of_node_full_name(bus_dev->of_node), + pci_domain_nr(pdev->subordinate), + pdev->subordinate->number); + if (IS_ERR(npb)) { + ret = PTR_ERR(npb); + goto out_cset_fail; + } + + ret = of_changeset_add_property_string(&cset, npb, "compatible", "generic,pci-bus"); + if (ret != 0) + goto out_cset_fail; + + ret = of_changeset_add_property_string(&cset, npb, "device_type", "pci"); + if (ret != 0) + goto out_cset_fail; + + ret = of_changeset_add_property_string(&cset, npb, "status", "okay"); + if (ret != 0) + goto out_cset_fail; + + ret = of_changeset_add_property_bool(&cset, npb, "auto-generated"); + if (ret != 0) + goto out_cset_fail; + + ret = of_changeset_attach_node(&cset, npb); + if (ret != 0) + goto out_cset_fail; + } + + ret = of_changeset_apply(&cset); + if (ret != 0) + goto out_cset_fail; + + /* permanently commit */ + of_changeset_destroy(&cset); + + /* bind the node to the device */ + dev->of_node = np; + ret = sysfs_create_link(&dev->kobj, &dev->of_node->kobj, + "of_node"); + if (ret) + pr_warn("%s: %s Error %d creating of_node link\n", + __func__, kobject_name(&dev->kobj), ret); + + if (npb) { + pdev->subordinate->dev.of_node = npb; + ret = sysfs_create_link(&pdev->subordinate->dev.kobj, &npb->kobj, + "of_node"); + if (ret) + pr_warn("%s: %s Error %d creating of_node link\n", + __func__, kobject_name(&dev->kobj), ret); + } + + + return 0; + +out_cset_fail: + pr_err("%s: %s Failed to apply changeset (err=%d)\n", __func__, + kobject_name(&dev->kobj), ret); + of_changeset_destroy(&cset); + return ret; +} + +static int pci_dev_uninstantiate(struct pci_dev *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np, *npb; + struct of_changeset cset; + int ret; + + /* device node must exist */ + np = dev->of_node; + if (!np) + return 0; + + /* and the auto-generated property */ + if (!of_property_read_bool(np, "auto-generated")) + return 0; + + of_changeset_init(&cset); + + ret = of_changeset_detach_node(&cset, np); + if (ret != 0) + goto out_cset_fail; + + npb = NULL; + if (pci_is_bridge(pdev)) + npb = pdev->subordinate->dev.of_node; + + if (npb != NULL) { + ret = of_changeset_detach_node(&cset, npb); + if (ret != 0) + goto out_cset_fail; + } + + ret = of_changeset_apply(&cset); + if (ret != 0) + goto out_cset_fail; + + dev->of_node = NULL; + if (npb != NULL) + pdev->subordinate->dev.of_node = NULL; + + pr_debug("%s: %s: %02x:%02x.%02x\n", __func__, + kobject_name(&dev->kobj), + pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + + /* TODO iterate over the properties and free */ + + return 0; + +out_cset_fail: + of_changeset_destroy(&cset); + + return ret; +} + +static int dovmgr_pci_notify(struct notifier_block *nb, + unsigned long action, void *arg) +{ + int ret; + + if (action == BUS_NOTIFY_ADD_DEVICE) + pci_dev_instantiate(to_pci_dev(arg)); + + ret = dovmgr_notifier_action(&dovmgr_pci_group, action, arg, + dovmgr_pci_item_match, dovmgr_item_notify); + + if (action == BUS_NOTIFY_REMOVED_DEVICE) + pci_dev_uninstantiate(to_pci_dev(arg)); + + return ret; +} + +static struct notifier_block dovmgr_pci_notifier = { + .notifier_call = dovmgr_pci_notify, +}; + +static int pci_instantiate_iterator(struct device *dev, void *data) +{ + return pci_dev_instantiate(to_pci_dev(dev)); +} + +static int dovmgr_pci_init(void) +{ + int ret; + + config_group_init_type_name(&dovmgr_pci_group, "pci", &dovmgr_pci_type); + ret = bus_register_notifier(&pci_bus_type, &dovmgr_pci_notifier); + if (ret != 0) { + pr_err("%s: bus_register_notifier() failed\n", __func__); + return ret; + } + + ret = bus_for_each_dev(&pci_bus_type, NULL, NULL, + pci_instantiate_iterator); + if (ret != 0) { + pr_err("%s: bus_for_each_dev() failed\n", __func__); + return ret; + } + + return 0; +} + +static void dovmgr_pci_cleanup(void) +{ + bus_unregister_notifier(&pci_bus_type, &dovmgr_pci_notifier); +} +#endif + +#if IS_ENABLED(CONFIG_USB) +static int dovmgr_usb_notify(struct notifier_block *nb, + unsigned long action, void *arg) +{ + return dovmgr_notifier_action(&dovmgr_usb_group, action, arg, + dovmgr_usb_item_match, dovmgr_item_notify); +} + +static struct notifier_block dovmgr_usb_notifier = { + .notifier_call = dovmgr_usb_notify, +}; + +static int dovmgr_usb_init(void) +{ + int ret; + + config_group_init_type_name(&dovmgr_usb_group, "usb", &dovmgr_usb_type); + ret = bus_register_notifier(&usb_bus_type, &dovmgr_usb_notifier); + if (ret != 0) { + pr_err("%s: bus_register_notifier() failed\n", __func__); + return ret; + } + return 0; +} + +static void dovmgr_usb_cleanup(void) +{ + bus_unregister_notifier(&usb_bus_type, &dovmgr_usb_notifier); +} +#endif + +static int __init dovmgr_init(void) +{ + int ret; + + config_group_init(&dovmgr_subsys.su_group); +#if IS_ENABLED(CONFIG_PCI) + configfs_add_default_group(&dovmgr_pci_group, + &dovmgr_subsys.su_group); +#endif +#if IS_ENABLED(CONFIG_USB) + configfs_add_default_group(&dovmgr_usb_group, + &dovmgr_subsys.su_group); +#endif + +#if IS_ENABLED(CONFIG_PCI) + ret = dovmgr_pci_init(); + if (ret != 0) + goto err_no_pci_init; +#endif +#if IS_ENABLED(CONFIG_USB) + ret = dovmgr_usb_init(); + if (ret != 0) + goto err_no_usb_init; +#endif + + ret = configfs_register_subsystem(&dovmgr_subsys); + if (ret != 0) { + pr_err("%s: failed to register subsys\n", __func__); + goto err_no_configfs; + } + pr_info("%s: OK\n", __func__); + return 0; + +err_no_configfs: +#if IS_ENABLED(CONFIG_USB) + dovmgr_usb_cleanup(); +err_no_usb_init: +#endif +#if IS_ENABLED(CONFIG_PCI) + dovmgr_pci_cleanup(); +err_no_pci_init: +#endif + return ret; +} +late_initcall(dovmgr_init); diff --git a/drivers/net/ethernet/allwinner/Kconfig b/drivers/net/ethernet/allwinner/Kconfig index 47da7e7..060569c 100644 --- a/drivers/net/ethernet/allwinner/Kconfig +++ b/drivers/net/ethernet/allwinner/Kconfig @@ -33,4 +33,17 @@ config SUN4I_EMAC To compile this driver as a module, choose M here. The module will be called sun4i-emac. +config SUN8I_EMAC + tristate "Allwinner sun8i EMAC support" + depends on ARCH_SUNXI || COMPILE_TEST + depends on OF + select MII + select PHYLIB + ---help--- + This driver support the sun8i EMAC ethernet driver present on + H3/A83T/A64 Allwinner SoCs. + + To compile this driver as a module, choose M here. The module + will be called sun8i-emac. + endif # NET_VENDOR_ALLWINNER diff --git a/drivers/net/ethernet/allwinner/Makefile b/drivers/net/ethernet/allwinner/Makefile index 03129f7..8bd1693 100644 --- a/drivers/net/ethernet/allwinner/Makefile +++ b/drivers/net/ethernet/allwinner/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_SUN4I_EMAC) += sun4i-emac.o +obj-$(CONFIG_SUN8I_EMAC) += sun8i-emac.o diff --git b/drivers/net/ethernet/allwinner/sun8i-emac.c b/drivers/net/ethernet/allwinner/sun8i-emac.c new file mode 100644 index 0000000..fc0c1dd --- /dev/null +++ b/drivers/net/ethernet/allwinner/sun8i-emac.c @@ -0,0 +1,2129 @@ +/* + * sun8i-emac driver + * + * Copyright (C) 2015-2016 Corentin LABBE + * + * This is the driver for Allwinner Ethernet MAC found in H3/A83T/A64 SoC + * + * TODO: + * - MAC filtering + * - Jumbo frame + * - features rx-all (NETIF_F_RXALL_BIT) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SUN8I_EMAC_BASIC_CTL0 0x00 +#define SUN8I_EMAC_BASIC_CTL1 0x04 +#define SUN8I_EMAC_INT_STA 0x08 +#define SUN8I_EMAC_INT_EN 0x0C +#define SUN8I_EMAC_TX_CTL0 0x10 +#define SUN8I_EMAC_TX_CTL1 0x14 +#define SUN8I_EMAC_TX_FLOW_CTL 0x1C +#define SUN8I_EMAC_RX_CTL0 0x24 +#define SUN8I_EMAC_RX_CTL1 0x28 +#define SUN8I_EMAC_RX_FRM_FLT 0x38 +#define SUN8I_EMAC_MDIO_CMD 0x48 +#define SUN8I_EMAC_MDIO_DATA 0x4C +#define SUN8I_EMAC_TX_DMA_STA 0xB0 +#define SUN8I_EMAC_TX_CUR_DESC 0xB4 +#define SUN8I_EMAC_TX_CUR_BUF 0xB8 +#define SUN8I_EMAC_RX_DMA_STA 0xC0 + +#define MDIO_CMD_MII_BUSY BIT(0) +#define MDIO_CMD_MII_WRITE BIT(1) +#define MDIO_CMD_MII_PHY_REG_ADDR_MASK GENMASK(8, 4) +#define MDIO_CMD_MII_PHY_REG_ADDR_SHIFT 4 +#define MDIO_CMD_MII_PHY_ADDR_MASK GENMASK(16, 12) +#define MDIO_CMD_MII_PHY_ADDR_SHIFT 12 + +#define SUN8I_EMAC_MACADDR_HI 0x50 +#define SUN8I_EMAC_MACADDR_LO 0x54 + +#define SUN8I_EMAC_RX_DESC_LIST 0x34 +#define SUN8I_EMAC_TX_DESC_LIST 0x20 + +#define SUN8I_EMAC_RX_DO_CRC BIT(27) +#define SUN8I_EMAC_RX_STRIP_FCS BIT(28) + +#define SUN8I_COULD_BE_USED_BY_DMA BIT(31) + +/* Used in RX_CTL1*/ +#define RX_DMA_EN BIT(30) +#define RX_DMA_START BIT(31) +/* Used in TX_CTL1*/ +#define TX_DMA_EN BIT(30) +#define TX_DMA_START BIT(31) + +/* Used in RX_CTL0 */ +#define RX_RECEIVER_EN BIT(31) +/* Used in TX_CTL0 */ +#define TX_TRANSMITTER_EN BIT(31) + +/* Basic CTL0 */ +#define BCTL0_FD BIT(0) +#define BCTL0_SPEED_10 2 +#define BCTL0_SPEED_100 3 +#define BCTL0_SPEED_MASK GENMASK(3, 2) +#define BCTL0_SPEED_SHIFT 2 + +#define FLOW_RX 1 +#define FLOW_TX 2 + +#define RX_INT BIT(8) +#define TX_INT BIT(0) + +/* Bits used in frame RX status */ +#define DSC_RX_FIRST BIT(9) +#define DSC_RX_LAST BIT(8) + +/* Bits used in frame TX ctl */ +#define SUN8I_EMAC_MAGIC_TX_BIT BIT(24) +#define SUN8I_EMAC_TX_DO_CRC (BIT(27) | BIT(28)) +#define DSC_TX_FIRST BIT(29) +#define DSC_TX_LAST BIT(30) +#define SUN8I_EMAC_WANT_INT BIT(31) + +enum emac_variant { + NONE_EMAC,/* for be sure that variant is non-0 if set */ + A83T_EMAC, + H3_EMAC, + A64_EMAC, +}; + +static const char const estats_str[][ETH_GSTRING_LEN] = { + /* errors */ + "rx_payload_error", + "rx_CRC_error", + "rx_phy_error", + "rx_length_error", + "rx_col_error", + "rx_header_error", + "rx_overflow_error", + "rx_saf_error", + "rx_daf_error", + "rx_buf_error", + /* misc infos */ + "tx_stop_queue", + "rx_dma_ua", + "rx_dma_stop", + "tx_dma_ua", + "tx_dma_stop", + "rx_hw_csum", + "tx_hw_csum", + /* interrupts */ + "rx_int", + "tx_int", + "rx_early_int", + "tx_early_int", + "tx_underflow_int", + /* debug */ + "tx_used_desc", + "napi_schedule", + "napi_underflow", +}; + +struct sun8i_emac_stats { + u64 rx_payload_error; + u64 rx_crc_error; + u64 rx_phy_error; + u64 rx_length_error; + u64 rx_col_error; + u64 rx_header_error; + u64 rx_overflow_error; + u64 rx_saf_fail; + u64 rx_daf_fail; + u64 rx_buf_error; + + u64 tx_stop_queue; + u64 rx_dma_ua; + u64 rx_dma_stop; + u64 tx_dma_ua; + u64 tx_dma_stop; + u64 rx_hw_csum; + u64 tx_hw_csum; + + u64 rx_int; + u64 tx_int; + u64 rx_early_int; + u64 tx_early_int; + u64 tx_underflow_int; + + u64 tx_used_desc; + u64 napi_schedule; + u64 napi_underflow; +}; + +/* The datasheet said that each descriptor can transfers up to 4096bytes + * But latter, a register documentation reduce that value to 2048 + * Anyway using 2048 cause strange behaviours and even BSP driver use 2047 + */ +#define DESC_BUF_MAX 2044 +#if (DESC_BUF_MAX < (ETH_FRAME_LEN + 4)) +#error "DESC_BUF_MAX must be set at minimum to ETH_FRAME_LEN + 4" +#endif + +/* MAGIC value for knowing if a descriptor is available or not */ +#define DCLEAN (BIT(16) | BIT(14) | BIT(12) | BIT(10) | BIT(9)) + +/* struct dma_desc - Structure of DMA descriptor used by the hardware + * @status: Status of the frame written by HW, so RO for the + * driver (except for BIT(31) which is R/W) + * @ctl: Information on the frame written by the driver (INT, len,...) + * @buf_addr: physical address of the frame data + * @next: physical address of next dma_desc + */ +struct dma_desc { + u32 status; + u32 ctl; + u32 buf_addr; + u32 next; +}; + +/* Describe how data from skb are DMA mapped (used in txinfo map member) */ +#define MAP_SINGLE 1 +#define MAP_PAGE 2 + +/* Structure for storing information about data in TX ring buffer */ +struct txinfo { + struct sk_buff *skb; + int map; +}; + +struct sun8i_emac_priv { + void __iomem *base; + void __iomem *syscon; + int irq; + struct device *dev; + struct net_device *ndev; + struct mii_bus *mdio; + struct napi_struct napi; + spinlock_t tx_lock;/* control the access of transmit descriptors */ + int duplex; + int speed; + int link; + int phy_interface; + enum emac_variant variant; + struct device_node *phy_node; + struct clk *ahb_clk; + struct clk *ephy_clk; + bool use_internal_phy; + + struct reset_control *rst; + struct reset_control *rst_ephy; + + struct dma_desc *dd_rx; + dma_addr_t dd_rx_phy; + struct dma_desc *dd_tx; + dma_addr_t dd_tx_phy; + struct sk_buff **rx_skb; + struct txinfo *txl; + + int nbdesc_tx; + int nbdesc_rx; + int tx_slot; + int tx_dirty; + int rx_dirty; + struct sun8i_emac_stats estats; + u32 msg_enable; + int flow_ctrl; + int pause; +}; + +static irqreturn_t sun8i_emac_dma_interrupt(int irq, void *dev_id); + +static void rb_inc(int *p, const int max) +{ + (*p)++; + (*p) %= max; +} + +/* Return the number of contiguous free descriptors + * starting from tx_slot + */ +static int rb_tx_numfreedesc(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + + if (priv->tx_slot < priv->tx_dirty) + return priv->tx_dirty - priv->tx_slot; + + return (priv->nbdesc_tx - priv->tx_slot) + priv->tx_dirty; +} + +/* Allocate a skb in a DMA descriptor + * + * @i index of slot to fill +*/ +static int sun8i_emac_rx_skb(struct net_device *ndev, int i) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + struct dma_desc *ddesc; + struct sk_buff *skb; + + ddesc = priv->dd_rx + i; + + ddesc->ctl = 0; + + skb = netdev_alloc_skb_ip_align(ndev, DESC_BUF_MAX); + if (!skb) + return -ENOMEM; + + /* should not happen */ + if (unlikely(priv->rx_skb[i])) + dev_warn(priv->dev, "BUG: Leaking a skbuff\n"); + + priv->rx_skb[i] = skb; + + ddesc->buf_addr = dma_map_single(priv->dev, skb->data, + DESC_BUF_MAX, DMA_FROM_DEVICE); + if (dma_mapping_error(priv->dev, ddesc->buf_addr)) { + dev_err(priv->dev, "ERROR: Cannot map RX buffer for DMA\n"); + dev_kfree_skb(skb); + return -EFAULT; + } + ddesc->ctl |= DESC_BUF_MAX; + wmb();/* SUN8I_COULD_BE_USED_BY_DMA must be the last value written */ + ddesc->status = SUN8I_COULD_BE_USED_BY_DMA; + + return 0; +} + +static void sun8i_emac_stop_tx(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + u32 v; + + netif_stop_queue(ndev); + + v = readl(priv->base + SUN8I_EMAC_TX_CTL0); + v &= ~TX_TRANSMITTER_EN;/*Disable transmitter after current reception*/ + writel(v, priv->base + SUN8I_EMAC_TX_CTL0); + v = readl(priv->base + SUN8I_EMAC_TX_CTL1); + v &= ~TX_DMA_EN; /* Stop TX DMA */ + writel(v, priv->base + SUN8I_EMAC_TX_CTL1); +} + +static void sun8i_emac_stop_rx(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + u32 v; + + v = readl(priv->base + SUN8I_EMAC_RX_CTL0); + v &= ~RX_RECEIVER_EN; /* Disable receiver after current reception */ + writel(v, priv->base + SUN8I_EMAC_RX_CTL0); + v = readl(priv->base + SUN8I_EMAC_RX_CTL1); + v &= ~RX_DMA_EN; /* Stop RX DMA */ + writel(v, priv->base + SUN8I_EMAC_RX_CTL1); +} + +static void sun8i_emac_start_rx(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + u32 v; + + v = readl(priv->base + SUN8I_EMAC_RX_CTL0); + v |= RX_RECEIVER_EN;/* Enable receiver */ + writel(v, priv->base + SUN8I_EMAC_RX_CTL0); + + v = readl(priv->base + SUN8I_EMAC_RX_CTL1); + v |= RX_DMA_START; + v |= RX_DMA_EN; + writel(v, priv->base + SUN8I_EMAC_RX_CTL1); +} + +static void sun8i_emac_start_tx(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + u32 v; + + v = readl(priv->base + SUN8I_EMAC_TX_CTL0); + v |= TX_TRANSMITTER_EN; + writel(v, priv->base + SUN8I_EMAC_TX_CTL0); + + v = readl(priv->base + SUN8I_EMAC_TX_CTL1); + v |= TX_DMA_START; + v |= TX_DMA_EN; + writel(v, priv->base + SUN8I_EMAC_TX_CTL1); +} + +/* Set MAC address for slot index + * @addr: the MAC address to set + * @index: The index of slot where to set address. + * The slot 0 is the main MACaddr + */ +static void sun8i_emac_set_macaddr(struct sun8i_emac_priv *priv, + const u8 *addr, int index) +{ + u32 v; + + dev_info(priv->dev, "device MAC address slot %d %02x:%02x:%02x:%02x:%02x:%02x\n", + index, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + v = (addr[5] << 8) | addr[4]; + writel(v, priv->base + SUN8I_EMAC_MACADDR_HI + index * 8); + v = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; + writel(v, priv->base + SUN8I_EMAC_MACADDR_LO + index * 8); +} + +static void sun8i_emac_set_link_mode(struct sun8i_emac_priv *priv) +{ + u32 v; + + v = readl(priv->base + SUN8I_EMAC_BASIC_CTL0); + + if (priv->duplex) + v |= BCTL0_FD; + else + v &= ~BCTL0_FD; + + v &= ~BCTL0_SPEED_MASK; + switch (priv->speed) { + case 1000: + break; + case 100: + v |= BCTL0_SPEED_100 << BCTL0_SPEED_SHIFT; + break; + case 10: + v |= BCTL0_SPEED_10 << BCTL0_SPEED_SHIFT; + break; + } + + writel(v, priv->base + SUN8I_EMAC_BASIC_CTL0); +} + +static void sun8i_emac_flow_ctrl(struct sun8i_emac_priv *priv, int duplex, + int fc) +{ + u32 flow = 0; + + netif_dbg(priv, link, priv->ndev, "%s %d %d\n", __func__, + duplex, fc); + + flow = readl(priv->base + SUN8I_EMAC_RX_CTL0); + if (fc & FLOW_RX) + flow |= BIT(16); + else + flow &= ~BIT(16); + writel(flow, priv->base + SUN8I_EMAC_RX_CTL0); + + flow = readl(priv->base + SUN8I_EMAC_TX_FLOW_CTL); + if (fc & FLOW_TX) + flow |= BIT(0); + else + flow &= ~BIT(0); + writel(flow, priv->base + SUN8I_EMAC_TX_FLOW_CTL); +} + +/* Grab a frame into a skb from descriptor number i */ +static int sun8i_emac_rx_from_ddesc(struct net_device *ndev, int i) +{ + struct sk_buff *skb; + struct sun8i_emac_priv *priv = netdev_priv(ndev); + struct dma_desc *ddesc = priv->dd_rx + i; + int frame_len; + int rxcsum_done = 0; + + if (ndev->features & NETIF_F_RXCSUM) + rxcsum_done = 1; + + /* bit0/bit7 work only on IPv4/IPv6 TCP traffic, + * (not on ARP for example) so we dont raise rx_errors/discard frame + */ + /* the checksum or length of received frame's payload is wrong*/ + if (ddesc->status & BIT(0)) { + priv->estats.rx_payload_error++; + rxcsum_done = 0; + } + /* RX_CRC_ERR */ + if (ddesc->status & BIT(1)) { + priv->ndev->stats.rx_errors++; + priv->ndev->stats.rx_crc_errors++; + priv->estats.rx_crc_error++; + goto discard_frame; + } + /* RX_PHY_ERR */ + if ((ddesc->status & BIT(3))) { + priv->ndev->stats.rx_errors++; + priv->estats.rx_phy_error++; + goto discard_frame; + } + /* RX_LENGTH_ERR */ + if ((ddesc->status & BIT(4))) { + priv->ndev->stats.rx_errors++; + priv->ndev->stats.rx_length_errors++; + priv->estats.rx_length_error++; + goto discard_frame; + } + /* RX_COL_ERR */ + if ((ddesc->status & BIT(6))) { + priv->ndev->stats.rx_errors++; + priv->estats.rx_col_error++; + goto discard_frame; + } + /* RX_HEADER_ERR */ + if ((ddesc->status & BIT(7))) { + priv->estats.rx_header_error++; + rxcsum_done = 0; + } + /* RX_OVERFLOW_ERR */ + if ((ddesc->status & BIT(11))) { + priv->ndev->stats.rx_over_errors++; + priv->estats.rx_overflow_error++; + goto discard_frame; + } + /* RX_NO_ENOUGTH_BUF_ERR */ + if ((ddesc->status & BIT(14))) { + priv->ndev->stats.rx_errors++; + priv->estats.rx_buf_error++; + goto discard_frame; + } + + /* BIT(9) is for the first frame, not having it is bad since we do not + * handle Jumbo frame + */ + if ((ddesc->status & DSC_RX_FIRST) == 0) { + dev_warn_ratelimited(priv->dev, "BUG: Non-first frame received. This should not happen\n"); + goto discard_frame; + } + frame_len = (ddesc->status >> 16) & 0x3FFF; + if (!(ndev->features & NETIF_F_RXFCS)) + frame_len -= ETH_FCS_LEN; + + skb = priv->rx_skb[i]; + + netif_dbg(priv, rx_status, priv->ndev, + "%s from %02d %pad len=%d status=%x st=%x\n", + __func__, i, &ddesc, frame_len, ddesc->status, ddesc->ctl); + + skb_put(skb, frame_len); + + dma_unmap_single(priv->dev, ddesc->buf_addr, DESC_BUF_MAX, + DMA_FROM_DEVICE); + skb->protocol = eth_type_trans(skb, priv->ndev); + if (rxcsum_done) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + priv->estats.rx_hw_csum++; + } else { + skb->ip_summed = CHECKSUM_PARTIAL; + } + + priv->ndev->stats.rx_packets++; + priv->ndev->stats.rx_bytes += frame_len; + priv->rx_skb[i] = NULL; + + /* this frame is not the last */ + if ((ddesc->status & DSC_RX_LAST) == 0) { + dev_warn(priv->dev, "Multi frame not implemented currlen=%d\n", + frame_len); + } + + sun8i_emac_rx_skb(ndev, i); + napi_gro_receive(&priv->napi, skb); + + return 0; + /* If the frame need to be dropped, we simply reuse the buffer */ +discard_frame: + ddesc->ctl = DESC_BUF_MAX; + wmb();/* SUN8I_COULD_BE_USED_BY_DMA must be the last value written */ + ddesc->status = SUN8I_COULD_BE_USED_BY_DMA; + return 0; +} + +/* iterate over dma_desc for finding completed xmit. + * Called from interrupt context, so no need to spinlock tx + * + * The problem is: how to know that a descriptor is sent and not just in + * preparation. + * Need to have status=0 and st set but this is the state of first frame just + * before setting the own-by-DMA bit. + * The solution is to used the artificial value DCLEAN. + */ +static int sun8i_emac_complete_xmit(struct net_device *ndev, int budget) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + struct dma_desc *ddesc; + int frame_len; + int work = 0; + + spin_lock(&priv->tx_lock); + do { + ddesc = priv->dd_tx + priv->tx_dirty; + + if (ddesc->status & SUN8I_COULD_BE_USED_BY_DMA) + goto xmit_end; + + if (ddesc->status == DCLEAN) + goto xmit_end; + + if (ddesc->status == 0 && !ddesc->ctl) { + dev_err(priv->dev, "BUG: reached the void %d %d\n", + priv->tx_dirty, priv->tx_slot); + goto xmit_end; + } + + /* TX_UNDERFLOW_ERR */ + if (ddesc->status & BIT(1)) + priv->ndev->stats.tx_errors++; + /* TX_DEFER_ERR */ + if (ddesc->status & BIT(2)) + priv->ndev->stats.tx_errors++; + /* BIT 6:3 numbers of collisions */ + if (ddesc->status & 0x78) + priv->ndev->stats.collisions += + (ddesc->status & 0x78) >> 3; + /* TX_COL_ERR_1 */ + if (ddesc->status & BIT(8)) + priv->ndev->stats.tx_errors++; + /* TX_COL_ERR_0 */ + if (ddesc->status & BIT(9)) + priv->ndev->stats.tx_errors++; + /* TX_CRS_ERR */ + if (ddesc->status & BIT(10)) + priv->ndev->stats.tx_carrier_errors++; + /* TX_PAYLOAD_ERR */ + if (ddesc->status & BIT(12)) + priv->ndev->stats.tx_errors++; + /* TX_LENGTH_ERR */ + if (ddesc->status & BIT(14)) + priv->ndev->stats.tx_errors++; + /* TX_HEADER_ERR */ + if (ddesc->status & BIT(16)) + priv->ndev->stats.tx_errors++; + frame_len = ddesc->ctl & 0x3FFF; + if (priv->txl[priv->tx_dirty].map == MAP_SINGLE) + dma_unmap_single(priv->dev, ddesc->buf_addr, + frame_len, DMA_TO_DEVICE); + else + dma_unmap_page(priv->dev, ddesc->buf_addr, + frame_len, DMA_TO_DEVICE); + /* we can free skb only on last frame */ + if (priv->txl[priv->tx_dirty].skb && (ddesc->ctl & DSC_TX_LAST)) + dev_kfree_skb_irq(priv->txl[priv->tx_dirty].skb); + + priv->txl[priv->tx_dirty].skb = NULL; + priv->txl[priv->tx_dirty].map = 0; + ddesc->ctl = 0; + wmb(); /* setting to DCLEAN is the last value to be set */ + ddesc->status = DCLEAN; + work++; + + rb_inc(&priv->tx_dirty, priv->nbdesc_tx); + ddesc = priv->dd_tx + priv->tx_dirty; + } while (ddesc->ctl && !(ddesc->status & SUN8I_COULD_BE_USED_BY_DMA)); + + if (netif_queue_stopped(ndev) && + rb_tx_numfreedesc(ndev) > MAX_SKB_FRAGS + 1) + netif_wake_queue(ndev); +xmit_end: + spin_unlock(&priv->tx_lock); + return work; +} + +static int sun8i_emac_poll(struct napi_struct *napi, int budget) +{ + struct sun8i_emac_priv *priv = + container_of(napi, struct sun8i_emac_priv, napi); + struct net_device *ndev = priv->ndev; + int worked; + struct dma_desc *ddesc; + + priv->estats.napi_schedule++; + worked = sun8i_emac_complete_xmit(ndev, budget); + + ddesc = priv->dd_rx + priv->rx_dirty; + while (!(ddesc->status & SUN8I_COULD_BE_USED_BY_DMA) && + worked < budget) { + sun8i_emac_rx_from_ddesc(ndev, priv->rx_dirty); + worked++; + rb_inc(&priv->rx_dirty, priv->nbdesc_rx); + ddesc = priv->dd_rx + priv->rx_dirty; + }; + if (worked < budget) { + priv->estats.napi_underflow++; + napi_complete(&priv->napi); + writel(RX_INT | TX_INT, priv->base + SUN8I_EMAC_INT_EN); + } + return worked; +} + +static int sun8i_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg) +{ + struct net_device *ndev = bus->priv; + struct sun8i_emac_priv *priv = netdev_priv(ndev); + int err; + u32 reg; + + err = readl_poll_timeout(priv->base + SUN8I_EMAC_MDIO_CMD, reg, + !(reg & MDIO_CMD_MII_BUSY), 100, 10000); + if (err) { + dev_err(priv->dev, "%s timeout %x\n", __func__, reg); + return err; + } + + reg &= ~MDIO_CMD_MII_WRITE; + reg &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; + reg |= (phy_reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & + MDIO_CMD_MII_PHY_REG_ADDR_MASK; + + reg &= ~MDIO_CMD_MII_PHY_ADDR_MASK; + + reg |= (phy_addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & + MDIO_CMD_MII_PHY_ADDR_MASK; + + reg |= MDIO_CMD_MII_BUSY; + + writel(reg, priv->base + SUN8I_EMAC_MDIO_CMD); + + err = readl_poll_timeout(priv->base + SUN8I_EMAC_MDIO_CMD, reg, + !(reg & MDIO_CMD_MII_BUSY), 100, 10000); + + if (err) { + dev_err(priv->dev, "%s timeout %x\n", __func__, reg); + return err; + } + + return readl(priv->base + SUN8I_EMAC_MDIO_DATA); +} + +static int sun8i_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, + u16 data) +{ + struct net_device *ndev = bus->priv; + struct sun8i_emac_priv *priv = netdev_priv(ndev); + u32 reg; + int err; + + err = readl_poll_timeout(priv->base + SUN8I_EMAC_MDIO_CMD, reg, + !(reg & MDIO_CMD_MII_BUSY), 100, 10000); + if (err) { + dev_err(priv->dev, "%s timeout %x\n", __func__, reg); + return err; + } + + reg &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; + reg |= (phy_reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & + MDIO_CMD_MII_PHY_REG_ADDR_MASK; + + reg &= ~MDIO_CMD_MII_PHY_ADDR_MASK; + reg |= (phy_addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & + MDIO_CMD_MII_PHY_ADDR_MASK; + + reg |= MDIO_CMD_MII_WRITE; + reg |= MDIO_CMD_MII_BUSY; + + writel(reg, priv->base + SUN8I_EMAC_MDIO_CMD); + writel(data, priv->base + SUN8I_EMAC_MDIO_DATA); + dev_dbg(priv->dev, "%s %d %d %x %x\n", __func__, phy_addr, phy_reg, + reg, data); + + err = readl_poll_timeout(priv->base + SUN8I_EMAC_MDIO_CMD, reg, + !(reg & MDIO_CMD_MII_BUSY), 100, 10000); + if (err) { + dev_err(priv->dev, "%s timeout %x\n", __func__, reg); + return err; + } + + return 0; +} + +static int sun8i_emac_mdio_register(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + struct mii_bus *bus; + int ret; + + bus = mdiobus_alloc(); + if (!bus) { + netdev_err(ndev, "Failed to allocate a new mdio bus\n"); + return -ENOMEM; + } + + bus->name = dev_name(priv->dev); + bus->read = &sun8i_mdio_read; + bus->write = &sun8i_mdio_write; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%x", bus->name, priv->dev->id); + + bus->parent = priv->dev; + bus->priv = ndev; + + ret = of_mdiobus_register(bus, priv->dev->of_node); + if (ret) { + netdev_err(ndev, "Could not register a MDIO bus: %d\n", ret); + mdiobus_free(bus); + return ret; + } + + priv->mdio = bus; + + return 0; +} + +static void sun8i_emac_mdio_unregister(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + + mdiobus_unregister(priv->mdio); + mdiobus_free(priv->mdio); +} + +/* Run within phydev->lock */ +static void sun8i_emac_adjust_link(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + struct phy_device *phydev = ndev->phydev; + int new_state = 0; + + netif_dbg(priv, link, priv->ndev, + "%s link=%x duplex=%x speed=%x\n", __func__, + phydev->link, phydev->duplex, phydev->speed); + if (!phydev) + return; + + if (phydev->link) { + if (phydev->duplex != priv->duplex) { + new_state = 1; + priv->duplex = phydev->duplex; + } + if (phydev->pause) + sun8i_emac_flow_ctrl(priv, phydev->duplex, + priv->flow_ctrl); + + if (phydev->speed != priv->speed) { + new_state = 1; + priv->speed = phydev->speed; + } + + if (priv->link == 0) { + new_state = 1; + priv->link = phydev->link; + } + + netif_dbg(priv, link, priv->ndev, + "%s new=%d link=%d pause=%d\n", + __func__, new_state, priv->link, phydev->pause); + if (new_state) + sun8i_emac_set_link_mode(priv); + } else if (priv->link != phydev->link) { + new_state = 1; + priv->link = 0; + priv->speed = 0; + priv->duplex = -1; + } + + if (new_state) + phy_print_status(phydev); +} + +/* H3 specific bits for EPHY */ +#define H3_EPHY_ADDR_SHIFT 20 +#define H3_EPHY_LED_POL BIT(17) /* 1: active low, 0: active high */ +#define H3_EPHY_SHUTDOWN BIT(16) /* 1: shutdown, 0: power up */ +#define H3_EPHY_SELECT BIT(15) /* 1: internal PHY, 0: external PHY */ +#define H3_EPHY_DEFAULT_VALUE 0x58000 +#define H3_EPHY_DEFAULT_MASK GENMASK(31, 15) + +/* H3/A64 specific bits */ +#define SC_RMII_EN BIT(13) /* 1: enable RMII (overrides EPIT) */ + +/* Generic system control EMAC_CLK bits */ +#define SC_ETXDC_MASK GENMASK(2, 0) +#define SC_ETXDC_SHIFT 10 +#define SC_ERXDC_MASK GENMASK(4, 0) +#define SC_ERXDC_SHIFT 5 +#define SC_EPIT BIT(2) /* 1: RGMII, 0: MII */ +#define SC_ETCS_MASK GENMASK(1, 0) +#define SC_ETCS_MII 0x0 +#define SC_ETCS_EXT_GMII 0x1 +#define SC_ETCS_INT_GMII 0x2 + +static int sun8i_emac_set_syscon_ephy(struct net_device *ndev, u32 *reg) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + struct device_node *node = priv->dev->of_node; + int ret; + + *reg &= ~H3_EPHY_DEFAULT_MASK; + *reg |= H3_EPHY_DEFAULT_VALUE; + + if (!priv->use_internal_phy) { + /* switch to external PHY interface */ + *reg &= ~H3_EPHY_SELECT; + return 0; + } + + if (priv->phy_interface != PHY_INTERFACE_MODE_MII) { + netdev_warn(ndev, + "Internal PHY requested, forcing MII mode.\n"); + priv->phy_interface = PHY_INTERFACE_MODE_MII; + } + + *reg |= H3_EPHY_SELECT; + *reg &= ~H3_EPHY_SHUTDOWN; + + if (of_property_read_bool(node, "allwinner,leds-active-low")) + *reg |= H3_EPHY_LED_POL; + + ret = of_mdio_parse_addr(priv->dev, priv->phy_node); + if (ret < 0) { + netdev_err(ndev, "Could not parse MDIO addr\n"); + return ret; + } + + /* of_mdio_parse_addr returns a valid (0 ~ 31) PHY + * address. No need to mask it again. + */ + *reg |= ret << H3_EPHY_ADDR_SHIFT; + + return 0; +} + +static int sun8i_emac_set_syscon(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + struct device_node *node = priv->dev->of_node; + int ret; + u32 reg, val; + + reg = readl(priv->syscon); + + if (priv->variant == H3_EMAC) { + ret = sun8i_emac_set_syscon_ephy(ndev, ®); + if (ret) + return ret; + } + + if (!of_property_read_u32(node, "allwinner,tx-delay", &val)) { + if (val <= SC_ETXDC_MASK) { + reg &= ~(SC_ETXDC_MASK << SC_ETXDC_SHIFT); + reg |= (val << SC_ETXDC_SHIFT); + } else { + netdev_warn(ndev, "Invalid TX clock delay: %d\n", val); + } + } + + if (!of_property_read_u32(node, "allwinner,rx-delay", &val)) { + if (val <= SC_ERXDC_MASK) { + reg &= ~(SC_ERXDC_MASK << SC_ERXDC_SHIFT); + reg |= (val << SC_ERXDC_SHIFT); + } else { + netdev_warn(ndev, "Invalid RX clock delay: %d\n", val); + } + } + + /* Clear interface mode bits */ + reg &= ~(SC_ETCS_MASK | SC_EPIT); + if (priv->variant == H3_EMAC || priv->variant == A64_EMAC) + reg &= ~SC_RMII_EN; + + switch (priv->phy_interface) { + case PHY_INTERFACE_MODE_MII: + /* default */ + break; + case PHY_INTERFACE_MODE_RGMII: + reg |= SC_EPIT | SC_ETCS_INT_GMII; + break; + case PHY_INTERFACE_MODE_RMII: + if (priv->variant == H3_EMAC || priv->variant == A64_EMAC) { + reg |= SC_RMII_EN | SC_ETCS_EXT_GMII; + break; + } + /* RMII not supported on A83T */ + default: + netdev_err(ndev, "Unsupported interface mode: %s", + phy_modes(priv->phy_interface)); + return -EINVAL; + } + + writel(reg, priv->syscon); + + return 0; +} + +static void sun8i_emac_unset_syscon(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + u32 reg = 0; + + if (priv->variant == H3_EMAC) + reg = H3_EPHY_DEFAULT_VALUE; + + writel(reg, priv->syscon); +} + +/* Set Management Data Clock, must be call after device reset */ +static void sun8i_emac_set_mdc(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + unsigned long rate; + u32 reg; + + rate = clk_get_rate(priv->ahb_clk); + if (rate > 160000000) + reg = 0x3 << 20; /* AHB / 128 */ + else if (rate > 80000000) + reg = 0x2 << 20; /* AHB / 64 */ + else if (rate > 40000000) + reg = 0x1 << 20; /* AHB / 32 */ + else + reg = 0x0 << 20; /* AHB / 16 */ + netif_dbg(priv, link, ndev, "MDC auto : %x\n", reg); + writel(reg, priv->base + SUN8I_EMAC_MDIO_CMD); +} + +/* "power" the device, by enabling clk/reset/regulators */ +static int sun8i_emac_power(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + int ret; + + ret = clk_prepare_enable(priv->ahb_clk); + if (ret) { + netdev_err(ndev, "Could not enable AHB clock\n"); + return ret; + } + + if (priv->rst) { + ret = reset_control_deassert(priv->rst); + if (ret) { + netdev_err(ndev, "Could not deassert reset\n"); + goto err_reset; + } + } + + if (priv->ephy_clk) { + ret = clk_prepare_enable(priv->ephy_clk); + if (ret) { + netdev_err(ndev, "Could not enable EPHY clock\n"); + goto err_ephy_clk; + } + } + + if (priv->rst_ephy) { + ret = reset_control_deassert(priv->rst_ephy); + if (ret) { + netdev_err(ndev, "Could not deassert EPHY reset\n"); + goto err_ephy_reset; + } + } + + return 0; + +err_ephy_reset: + if (priv->ephy_clk) + clk_disable_unprepare(priv->ephy_clk); +err_ephy_clk: + if (priv->rst) + reset_control_assert(priv->rst); +err_reset: + clk_disable_unprepare(priv->ahb_clk); + return ret; +} + +/* "Unpower" the device, disabling clocks and regulators, asserting reset */ +static void sun8i_emac_unpower(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + + if (priv->rst_ephy) + reset_control_assert(priv->rst_ephy); + + if (priv->ephy_clk) + clk_disable_unprepare(priv->ephy_clk); + + if (priv->rst) + reset_control_assert(priv->rst); + + clk_disable_unprepare(priv->ahb_clk); +} + +static int sun8i_emac_init(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + struct device_node *node = priv->dev->of_node; + const u8 *addr; + + /* Try to get MAC address from DT, or assign a random one */ + addr = of_get_mac_address(node); + if (addr) + ether_addr_copy(ndev->dev_addr, addr); + else + eth_hw_addr_random(ndev); + + priv->phy_interface = of_get_phy_mode(node); + if (priv->phy_interface < 0) { + netdev_err(ndev, "PHY interface mode node unspecified\n"); + return priv->phy_interface; + } + + return sun8i_emac_power(ndev); +} + +static void sun8i_emac_uninit(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + + mdiobus_unregister(priv->mdio); + + sun8i_emac_unpower(ndev); +} + +static int sun8i_emac_mdio_probe(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + struct phy_device *phydev = NULL; + + phydev = of_phy_connect(ndev, priv->phy_node, &sun8i_emac_adjust_link, + 0, priv->phy_interface); + + if (!phydev) { + netdev_err(ndev, "Could not attach to PHY\n"); + return -ENODEV; + } + + phy_attached_info(phydev); + + /* mask with MAC supported features */ + phydev->supported &= PHY_GBIT_FEATURES; + phydev->advertising = phydev->supported; + + priv->link = 0; + priv->speed = 0; + priv->duplex = -1; + + return 0; +} + +/* Allocate both RX and TX ring buffer and init them + * This function also write the startbase of thoses ring in the device. + * All structures that help managing thoses rings are also handled + * by this functions (rx_skb/txl) + */ +static int sun8i_emac_alloc_rings(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + struct dma_desc *ddesc; + int err, i; + + priv->rx_skb = kcalloc(priv->nbdesc_rx, sizeof(struct sk_buff *), + GFP_KERNEL); + if (!priv->rx_skb) { + err = -ENOMEM; + goto rx_skb_error; + } + priv->txl = kcalloc(priv->nbdesc_tx, sizeof(struct txinfo), GFP_KERNEL); + if (!priv->txl) { + err = -ENOMEM; + goto tx_error; + } + + /* allocate/init RX ring */ + priv->dd_rx = dma_zalloc_coherent(priv->dev, + priv->nbdesc_rx * sizeof(struct dma_desc), + &priv->dd_rx_phy, GFP_KERNEL); + if (!priv->dd_rx) { + dev_err(priv->dev, "ERROR: cannot allocate DMA RX buffer"); + err = -ENOMEM; + goto dma_rx_error; + } + ddesc = priv->dd_rx; + for (i = 0; i < priv->nbdesc_rx; i++) { + sun8i_emac_rx_skb(ndev, i); + ddesc->next = (u32)priv->dd_rx_phy + (i + 1) + * sizeof(struct dma_desc); + ddesc++; + } + /* last descriptor point back to first one */ + ddesc--; + ddesc->next = (u32)priv->dd_rx_phy; + + /* allocate/init TX ring */ + priv->dd_tx = dma_zalloc_coherent(priv->dev, + priv->nbdesc_tx * sizeof(struct dma_desc), + &priv->dd_tx_phy, GFP_KERNEL); + if (!priv->dd_tx) { + dev_err(priv->dev, "ERROR: cannot allocate DMA TX buffer"); + err = -ENOMEM; + goto dma_tx_error; + } + ddesc = priv->dd_tx; + for (i = 0; i < priv->nbdesc_tx; i++) { + ddesc->status = DCLEAN; + ddesc->ctl = 0; + ddesc->next = (u32)(priv->dd_tx_phy + (i + 1) + * sizeof(struct dma_desc)); + ddesc++; + } + /* last descriptor point back to first one */ + ddesc--; + ddesc->next = (u32)priv->dd_tx_phy; + i--; + + priv->tx_slot = 0; + priv->tx_dirty = 0; + priv->rx_dirty = 0; + + /* write start of RX ring descriptor */ + writel(priv->dd_rx_phy, priv->base + SUN8I_EMAC_RX_DESC_LIST); + /* write start of TX ring descriptor */ + writel(priv->dd_tx_phy, priv->base + SUN8I_EMAC_TX_DESC_LIST); + + return 0; +dma_tx_error: + dma_free_coherent(priv->dev, priv->nbdesc_rx * sizeof(struct dma_desc), + priv->dd_rx, priv->dd_rx_phy); +dma_rx_error: + kfree(priv->txl); +tx_error: + kfree(priv->rx_skb); +rx_skb_error: + return err; +} + +static int sun8i_emac_open(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + int err; + u32 v; + + err = request_irq(priv->irq, sun8i_emac_dma_interrupt, 0, + dev_name(priv->dev), ndev); + if (err) { + dev_err(priv->dev, "Cannot request IRQ: %d\n", err); + return err; + } + + /* Set interface mode (and configure internal PHY on H3) */ + err = sun8i_emac_set_syscon(ndev); + if (err) + goto err_irq; + + /* Do SOFT RST */ + v = readl(priv->base + SUN8I_EMAC_BASIC_CTL1); + writel(v | 0x01, priv->base + SUN8I_EMAC_BASIC_CTL1); + + err = readl_poll_timeout(priv->base + SUN8I_EMAC_BASIC_CTL1, v, + !(v & 0x01), 100, 10000); + if (err) { + dev_err(priv->dev, "EMAC reset timeout\n"); + err = -EFAULT; + goto err_syscon; + } + + sun8i_emac_set_mdc(ndev); + + err = sun8i_emac_mdio_register(ndev); + if (err) + goto err_syscon; + + err = sun8i_emac_mdio_probe(ndev); + if (err) + goto err_syscon; + + /* DMA */ + v = (8 << 24);/* burst len */ + writel(v, priv->base + SUN8I_EMAC_BASIC_CTL1); + + writel(RX_INT | TX_INT, priv->base + SUN8I_EMAC_INT_EN); + + v = readl(priv->base + SUN8I_EMAC_RX_CTL0); + /* CHECK_CRC */ + if (ndev->features & NETIF_F_RXCSUM) + v |= SUN8I_EMAC_RX_DO_CRC; + else + v &= ~SUN8I_EMAC_RX_DO_CRC; + /* STRIP_FCS */ + if (ndev->features & NETIF_F_RXFCS) + v &= ~SUN8I_EMAC_RX_STRIP_FCS; + else + v |= SUN8I_EMAC_RX_STRIP_FCS; + writel(v, priv->base + SUN8I_EMAC_RX_CTL0); + + v = readl(priv->base + SUN8I_EMAC_TX_CTL1); + /* TX_MD Transmission starts after a full frame located in TX DMA FIFO*/ + v |= BIT(1); + /* Undocumented bit (called TX_NEXT_FRM in BSP), the original comment is + * "Operating on second frame increase the performance + * especially when transmit store-and-forward is used." + */ + v |= BIT(2); + writel(v, priv->base + SUN8I_EMAC_TX_CTL1); + + v = readl(priv->base + SUN8I_EMAC_RX_CTL1); + /* RX_MD RX DMA reads data from RX DMA FIFO to host memory after a + * complete frame has been written to RX DMA FIFO + */ + v |= BIT(1); + writel(v, priv->base + SUN8I_EMAC_RX_CTL1); + + sun8i_emac_set_macaddr(priv, ndev->dev_addr, 0); + + err = sun8i_emac_alloc_rings(ndev); + if (err) { + netdev_err(ndev, "Fail to allocate rings\n"); + goto err_mdio; + } + + phy_start(ndev->phydev); + + sun8i_emac_start_rx(ndev); + sun8i_emac_start_tx(ndev); + + netif_napi_add(ndev, &priv->napi, sun8i_emac_poll, 64); + napi_enable(&priv->napi); + netif_start_queue(ndev); + + return 0; +err_mdio: + phy_disconnect(ndev->phydev); +err_syscon: + sun8i_emac_unset_syscon(ndev); +err_irq: + free_irq(priv->irq, ndev); + return err; +} + +/* Clean the TX ring of any accepted skb for xmit */ +static void sun8i_emac_tx_clean(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + int i; + struct dma_desc *ddesc; + int frame_len; + + spin_lock(&priv->tx_lock); + + for (i = 0; i < priv->nbdesc_tx; i++) { + if (priv->txl[i].skb) { + ddesc = priv->dd_tx + i; + frame_len = ddesc->ctl & 0x3FFF; + switch (priv->txl[i].map) { + case MAP_SINGLE: + dma_unmap_single(priv->dev, ddesc->buf_addr, + frame_len, DMA_TO_DEVICE); + break; + case MAP_PAGE: + dma_unmap_page(priv->dev, ddesc->buf_addr, + frame_len, DMA_TO_DEVICE); + break; + default: + dev_err(priv->dev, "Trying to free an empty slot\n"); + continue; + } + dev_kfree_skb_any(priv->txl[i].skb); + priv->txl[i].skb = NULL; + ddesc->ctl = 0; + ddesc->status = DCLEAN; + } + } + priv->tx_slot = 0; + priv->tx_dirty = 0; + + spin_unlock(&priv->tx_lock); +} + +/* Clean the RX ring */ +static void sun8i_emac_rx_clean(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + int i; + struct dma_desc *ddesc; + + /* clean RX ring */ + for (i = 0; i < priv->nbdesc_rx; i++) + if (priv->rx_skb[i]) { + ddesc = priv->dd_rx + i; + dma_unmap_single(priv->dev, ddesc->buf_addr, + DESC_BUF_MAX, DMA_FROM_DEVICE); + dev_kfree_skb_any(priv->rx_skb[i]); + priv->rx_skb[i] = NULL; + } +} + +static int sun8i_emac_stop(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + + napi_disable(&priv->napi); + + sun8i_emac_stop_tx(ndev); + sun8i_emac_stop_rx(ndev); + + phy_stop(ndev->phydev); + phy_disconnect(ndev->phydev); + + sun8i_emac_mdio_unregister(ndev); + + sun8i_emac_unset_syscon(ndev); + + free_irq(priv->irq, ndev); + + sun8i_emac_rx_clean(ndev); + sun8i_emac_tx_clean(ndev); + + kfree(priv->rx_skb); + kfree(priv->txl); + + dma_free_coherent(priv->dev, priv->nbdesc_rx * sizeof(struct dma_desc), + priv->dd_rx, priv->dd_rx_phy); + dma_free_coherent(priv->dev, priv->nbdesc_tx * sizeof(struct dma_desc), + priv->dd_tx, priv->dd_tx_phy); + + return 0; +} + +static netdev_tx_t sun8i_emac_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + struct dma_desc *ddesc; + struct dma_desc *first; + int i = 0, rbd_first; + unsigned int len, fraglen, tlen; + u32 v; + int n; + int nf; + const skb_frag_t *frag; + int do_csum = 0; + + if (skb_put_padto(skb, ETH_ZLEN)) + return NETDEV_TX_OK; + len = skb_headlen(skb); + + n = skb_shinfo(skb)->nr_frags; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + do_csum = 1; + priv->estats.tx_hw_csum++; + } + netif_dbg(priv, tx_queued, ndev, "%s len=%u skblen=%u %x\n", __func__, + len, skb->len, + (skb->ip_summed == CHECKSUM_PARTIAL)); + + spin_lock(&priv->tx_lock); + + /* check for contigous space + * We need at least 1(skb->data) + n(numfrags) + 1(one clean slot) + */ + if (rb_tx_numfreedesc(ndev) < n + 2) { + dev_err_ratelimited(priv->dev, "BUG!: TX is full %d %d\n", + priv->tx_dirty, priv->tx_slot); + netif_stop_queue(ndev); + spin_unlock(&priv->tx_lock); + return NETDEV_TX_BUSY; + } + i = priv->tx_slot; + + ddesc = priv->dd_tx + i; + first = priv->dd_tx + i; + rbd_first = i; + + priv->tx_slot = (i + 1 + n) % priv->nbdesc_tx; + + ddesc->buf_addr = dma_map_single(priv->dev, skb->data, len, + DMA_TO_DEVICE); + if (dma_mapping_error(priv->dev, ddesc->buf_addr)) { + dev_err(priv->dev, "ERROR: Cannot map buffer for DMA\n"); + goto xmit_error; + } + priv->txl[i].map = MAP_SINGLE; + priv->txl[i].skb = skb; + + tlen = len; + ddesc->ctl = len; + /* Undocumented bit that make it works + * Without it, packets never be sent on H3 SoC + */ + ddesc->ctl |= SUN8I_EMAC_MAGIC_TX_BIT; + if (do_csum) + ddesc->ctl |= SUN8I_EMAC_TX_DO_CRC; + + /* handle fragmented skb, one descriptor per fragment */ + for (nf = 0; nf < n; nf++) { + frag = &skb_shinfo(skb)->frags[nf]; + rb_inc(&i, priv->nbdesc_tx); + priv->txl[i].skb = skb; + ddesc = priv->dd_tx + i; + fraglen = skb_frag_size(frag); + ddesc->ctl = fraglen; + tlen += fraglen, + ddesc->ctl |= SUN8I_EMAC_MAGIC_TX_BIT; + if (do_csum) + ddesc->ctl |= SUN8I_EMAC_TX_DO_CRC; + + ddesc->buf_addr = skb_frag_dma_map(priv->dev, frag, 0, + fraglen, DMA_TO_DEVICE); + if (dma_mapping_error(priv->dev, ddesc->buf_addr)) { + dev_err(priv->dev, "Cannot map buffer for DMA\n"); + goto xmit_error; + } + priv->txl[i].map = MAP_PAGE; + ddesc->status = SUN8I_COULD_BE_USED_BY_DMA; + } + + /* frame end */ + ddesc->ctl |= DSC_TX_LAST; + /* We want an interrupt after transmission */ + ddesc->ctl |= SUN8I_EMAC_WANT_INT; + + rb_inc(&i, priv->nbdesc_tx); + + /* frame begin */ + first->ctl |= DSC_TX_FIRST; + wmb();/* SUN8I_COULD_BE_USED_BY_DMA must be the last value written */ + first->status = SUN8I_COULD_BE_USED_BY_DMA; + priv->tx_slot = i; + + /* Trying to optimize this (recording DMA start/stop) seems + * to lead to errors. So we always start DMA. + */ + v = readl(priv->base + SUN8I_EMAC_TX_CTL1); + v |= TX_DMA_START; + v |= TX_DMA_EN; + writel(v, priv->base + SUN8I_EMAC_TX_CTL1); + + if (rb_tx_numfreedesc(ndev) < MAX_SKB_FRAGS + 1) { + netif_stop_queue(ndev); + priv->estats.tx_stop_queue++; + } + priv->estats.tx_used_desc = rb_tx_numfreedesc(ndev); + priv->ndev->stats.tx_packets++; + priv->ndev->stats.tx_bytes += tlen; + + spin_unlock(&priv->tx_lock); + + return NETDEV_TX_OK; + +xmit_error: + /* destroy skb and return TX OK Documentation/DMA-API-HOWTO.txt */ + /* clean descritors from rbd_first to i */ + ddesc->ctl = 0; + wmb(); /* setting to DCLEAN is the last value to be set */ + ddesc->status = DCLEAN; + do { + ddesc = priv->dd_tx + rbd_first; + ddesc->ctl = 0; + wmb(); /* setting to DCLEAN is the last value to be set */ + ddesc->status = DCLEAN; + rb_inc(&rbd_first, priv->nbdesc_tx); + } while (rbd_first != i); + spin_unlock(&priv->tx_lock); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +static int sun8i_emac_change_mtu(struct net_device *ndev, int new_mtu) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + int max_mtu; + + dev_info(priv->dev, "%s set MTU to %d\n", __func__, new_mtu); + + if (netif_running(ndev)) { + dev_err(priv->dev, "%s: must be stopped to change its MTU\n", + ndev->name); + return -EBUSY; + } + + max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN); + + if ((new_mtu < 68) || (new_mtu > max_mtu)) { + dev_err(priv->dev, "%s: invalid MTU, max MTU is: %d\n", + ndev->name, max_mtu); + return -EINVAL; + } + + ndev->mtu = new_mtu; + netdev_update_features(ndev); + return 0; +} + +static netdev_features_t sun8i_emac_fix_features(struct net_device *ndev, + netdev_features_t features) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + + netif_dbg(priv, drv, ndev, "%s %llx\n", __func__, features); + return features; +} + +static int sun8i_emac_set_features(struct net_device *ndev, + netdev_features_t features) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + u32 v; + + v = readl(priv->base + SUN8I_EMAC_BASIC_CTL0); + if (features & NETIF_F_LOOPBACK && netif_running(ndev)) { + netif_info(priv, hw, ndev, "Set loopback features"); + v |= BIT(1); + } else { + netif_info(priv, hw, ndev, "Unset loopback features"); + v &= ~BIT(1); + } + writel(v, priv->base + SUN8I_EMAC_BASIC_CTL0); + + v = readl(priv->base + SUN8I_EMAC_RX_CTL0); + if (features & NETIF_F_RXCSUM) { + v |= SUN8I_EMAC_RX_DO_CRC; + netif_info(priv, hw, ndev, "Doing RX CRC check by hardware"); + } else { + v &= ~SUN8I_EMAC_RX_DO_CRC; + netif_info(priv, hw, ndev, "No RX CRC check by hardware"); + } + if (features & NETIF_F_RXFCS) { + v &= ~SUN8I_EMAC_RX_STRIP_FCS; + netif_info(priv, hw, ndev, "Keep FCS"); + } else { + v |= SUN8I_EMAC_RX_STRIP_FCS; + netif_info(priv, hw, ndev, "Strip FCS"); + } + writel(v, priv->base + SUN8I_EMAC_RX_CTL0); + + netif_dbg(priv, drv, ndev, "%s %llx %x\n", __func__, features, v); + + return 0; +} + +static void sun8i_emac_set_rx_mode(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + u32 v = 0; + int i = 0; + struct netdev_hw_addr *ha; + + /* Receive all multicast frames */ + v |= BIT(16); + /* Receive all control frames */ + v |= BIT(13); + if (ndev->flags & IFF_PROMISC) + v |= BIT(1); + if (netdev_uc_count(ndev) > 7) { + v |= BIT(1); + } else { + netdev_for_each_uc_addr(ha, ndev) { + i++; + sun8i_emac_set_macaddr(priv, ha->addr, i); + } + } + writel(v, priv->base + SUN8I_EMAC_RX_FRM_FLT); +} + +static void sun8i_emac_tx_timeout(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + + netdev_err(ndev, "%s\n", __func__); + + sun8i_emac_stop_tx(ndev); + + sun8i_emac_tx_clean(ndev); + + /* write start of tx ring descriptor */ + writel(priv->dd_tx_phy, priv->base + SUN8I_EMAC_TX_DESC_LIST); + + sun8i_emac_start_tx(ndev); + + netdev_reset_queue(ndev); + + ndev->stats.tx_errors++; + netif_wake_queue(ndev); +} + +static int sun8i_emac_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) +{ + struct phy_device *phydev = ndev->phydev; + + if (!netif_running(ndev)) + return -EINVAL; + + if (!phydev) + return -ENODEV; + + return phy_mii_ioctl(phydev, rq, cmd); +} + +static int sun8i_emac_check_if_running(struct net_device *ndev) +{ + if (!netif_running(ndev)) + return -EINVAL; + return 0; +} + +static int sun8i_emac_get_sset_count(struct net_device *ndev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(estats_str); + } + return -EOPNOTSUPP; +} + +static int sun8i_emac_ethtool_get_settings(struct net_device *ndev, + struct ethtool_cmd *cmd) +{ + struct phy_device *phy = ndev->phydev; + struct sun8i_emac_priv *priv = netdev_priv(ndev); + + if (!phy) { + netdev_err(ndev, "%s: %s: PHY is not registered\n", + __func__, ndev->name); + return -ENODEV; + } + + if (!netif_running(ndev)) { + dev_err(priv->dev, "interface disabled: we cannot track link speed / duplex setting\n"); + return -EBUSY; + } + + return phy_ethtool_gset(phy, cmd); +} + +static int sun8i_emac_ethtool_set_settings(struct net_device *ndev, + struct ethtool_cmd *cmd) +{ + struct phy_device *phy = ndev->phydev; + + return phy_ethtool_sset(phy, cmd); +} + +static void sun8i_emac_ethtool_getdrvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, "sun8i_emac", sizeof(info->driver)); + strcpy(info->version, "00"); + info->fw_version[0] = '\0'; +} + +static void sun8i_emac_ethtool_stats(struct net_device *ndev, + struct ethtool_stats *dummy, u64 *data) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + + memcpy(data, &priv->estats, + sun8i_emac_get_sset_count(ndev, ETH_SS_STATS) * sizeof(u64)); +} + +static void sun8i_emac_ethtool_strings(struct net_device *dev, u32 stringset, + u8 *buffer) +{ + switch (stringset) { + case ETH_SS_STATS: + memcpy(buffer, &estats_str, sizeof(estats_str)); + break; + } +} + +static u32 sun8i_emac_ethtool_getmsglevel(struct net_device *ndev) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + + return priv->msg_enable; +} + +static void sun8i_emac_ethtool_setmsglevel(struct net_device *ndev, u32 level) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + + priv->msg_enable = level; +} + +static void sun8i_emac_get_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + + pause->rx_pause = 0; + pause->tx_pause = 0; + pause->autoneg = ndev->phydev->autoneg; + + if (priv->flow_ctrl & FLOW_RX) + pause->rx_pause = 1; + if (priv->flow_ctrl & FLOW_TX) + pause->tx_pause = 1; +} + +static int sun8i_emac_set_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + struct phy_device *phy = ndev->phydev; + int new_pause = 0; + int ret = 0; + + if (pause->rx_pause) + new_pause |= FLOW_RX; + if (pause->tx_pause) + new_pause |= FLOW_TX; + + priv->flow_ctrl = new_pause; + phy->autoneg = pause->autoneg; + + if (phy->autoneg) { + if (netif_running(ndev)) + ret = phy_start_aneg(phy); + } else { + sun8i_emac_flow_ctrl(priv, phy->duplex, priv->flow_ctrl); + } + return ret; +} + +static void sun8i_emac_ethtool_get_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + + ring->rx_pending = priv->nbdesc_rx; + ring->tx_pending = priv->nbdesc_tx; +} + +static int sun8i_emac_ethtool_set_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring) +{ + struct sun8i_emac_priv *priv = netdev_priv(ndev); + int err; + + if (ring->rx_max_pending || ring->rx_mini_max_pending || + ring->rx_jumbo_max_pending || ring->rx_mini_pending || + ring->rx_jumbo_pending || ring->tx_max_pending) + return -EINVAL; + + if (ring->tx_pending < MAX_SKB_FRAGS + 1) { + netdev_err(ndev, "The number of TX descriptors is too low"); + return -EINVAL; + } + + sun8i_emac_stop_tx(ndev); + sun8i_emac_stop_rx(ndev); + + sun8i_emac_rx_clean(ndev); + sun8i_emac_tx_clean(ndev); + + kfree(priv->rx_skb); + kfree(priv->txl); + + dma_free_coherent(priv->dev, priv->nbdesc_rx * sizeof(struct dma_desc), + priv->dd_rx, priv->dd_rx_phy); + dma_free_coherent(priv->dev, priv->nbdesc_tx * sizeof(struct dma_desc), + priv->dd_tx, priv->dd_tx_phy); + + priv->nbdesc_rx = ring->rx_pending; + priv->nbdesc_tx = ring->tx_pending; + err = sun8i_emac_alloc_rings(ndev); + if (err) { + /* Fatal error, we cannot re start */ + netdev_err(ndev, "Fail to allocate rings\n"); + return -EFAULT; + } + + sun8i_emac_start_rx(ndev); + sun8i_emac_start_tx(ndev); + + netif_start_queue(ndev); + + netdev_info(ndev, "Ring Param settings: rx: %d, tx %d\n", + ring->rx_pending, ring->tx_pending); + return 0; +} + +static const struct ethtool_ops sun8i_emac_ethtool_ops = { + .begin = sun8i_emac_check_if_running, + .get_settings = sun8i_emac_ethtool_get_settings, + .set_settings = sun8i_emac_ethtool_set_settings, + .get_link = ethtool_op_get_link, + .get_pauseparam = sun8i_emac_get_pauseparam, + .set_pauseparam = sun8i_emac_set_pauseparam, + .get_ethtool_stats = sun8i_emac_ethtool_stats, + .get_strings = sun8i_emac_ethtool_strings, + .get_sset_count = sun8i_emac_get_sset_count, + .get_drvinfo = sun8i_emac_ethtool_getdrvinfo, + .get_msglevel = sun8i_emac_ethtool_getmsglevel, + .set_msglevel = sun8i_emac_ethtool_setmsglevel, + .get_ringparam = sun8i_emac_ethtool_get_ringparam, + .set_ringparam = sun8i_emac_ethtool_set_ringparam, +}; + +static const struct net_device_ops sun8i_emac_netdev_ops = { + .ndo_init = sun8i_emac_init, + .ndo_uninit = sun8i_emac_uninit, + .ndo_open = sun8i_emac_open, + .ndo_start_xmit = sun8i_emac_xmit, + .ndo_stop = sun8i_emac_stop, + .ndo_change_mtu = sun8i_emac_change_mtu, + .ndo_fix_features = sun8i_emac_fix_features, + .ndo_set_features = sun8i_emac_set_features, + .ndo_set_rx_mode = sun8i_emac_set_rx_mode, + .ndo_tx_timeout = sun8i_emac_tx_timeout, + .ndo_do_ioctl = sun8i_emac_ioctl, + .ndo_set_mac_address = eth_mac_addr, +}; + +static irqreturn_t sun8i_emac_dma_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = dev_id; + struct sun8i_emac_priv *priv = netdev_priv(ndev); + u32 v, u; + + v = readl(priv->base + SUN8I_EMAC_INT_STA); + + /* When this bit is asserted, a frame transmission is completed. */ + if (v & BIT(0)) { + priv->estats.tx_int++; + writel(0, priv->base + SUN8I_EMAC_INT_EN); + napi_schedule(&priv->napi); + } + + /* When this bit is asserted, the TX DMA FSM is stopped. */ + if (v & BIT(1)) + priv->estats.tx_dma_stop++; + + /* When this asserted, the TX DMA can not acquire next TX descriptor + * and TX DMA FSM is suspended. + */ + if (v & BIT(2)) + priv->estats.tx_dma_ua++; + + if (v & BIT(3)) + netif_dbg(priv, intr, ndev, "Unhandled interrupt TX TIMEOUT\n"); + + if (v & BIT(4)) { + netif_dbg(priv, intr, ndev, "Unhandled interrupt TX underflow\n"); + priv->estats.tx_underflow_int++; + } + + /* When this bit asserted , the frame is transmitted to FIFO totally. */ + if (v & BIT(5)) { + netif_dbg(priv, intr, ndev, "Unhandled interrupt TX_EARLY_INT\n"); + priv->estats.tx_early_int++; + } + + /* When this bit is asserted, a frame reception is completed */ + if (v & BIT(8)) { + priv->estats.rx_int++; + writel(0, priv->base + SUN8I_EMAC_INT_EN); + napi_schedule(&priv->napi); + } + + /* When this asserted, the RX DMA can not acquire next TX descriptor + * and RX DMA FSM is suspended. + */ + if (v & BIT(9)) { + u = readl(priv->base + SUN8I_EMAC_RX_CTL1); + netif_info(priv, intr, ndev, "Re-run RX DMA %x\n", u); + writel(u | RX_DMA_START, priv->base + SUN8I_EMAC_RX_CTL1); + priv->estats.rx_dma_ua++; + } + + if (v & BIT(10)) { + netif_dbg(priv, intr, ndev, "Unhandled interrupt RX_DMA_STOPPED_INT\n"); + priv->estats.rx_dma_stop++; + } + if (v & BIT(11)) + netif_dbg(priv, intr, ndev, "Unhandled interrupt RX_TIMEOUT\n"); + if (v & BIT(12)) + netif_dbg(priv, intr, ndev, "Unhandled interrupt RX OVERFLOW\n"); + if (v & BIT(13)) { + netif_dbg(priv, intr, ndev, "Unhandled interrupt RX EARLY\n"); + priv->estats.rx_early_int++; + } + if (v & BIT(16)) + netif_dbg(priv, intr, ndev, "Unhandled interrupt RGMII\n"); + + /* the datasheet state those register as read-only + * but nothing work(freeze) without writing to it + */ + writel(v & 0x3FFF, priv->base + SUN8I_EMAC_INT_STA); + + return IRQ_HANDLED; +} + +static int sun8i_emac_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct sun8i_emac_priv *priv; + struct net_device *ndev; + struct resource *res; + int ret; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "No suitable DMA available\n"); + return ret; + } + + ndev = alloc_etherdev(sizeof(*priv)); + if (!ndev) + return -ENOMEM; + + SET_NETDEV_DEV(ndev, &pdev->dev); + priv = netdev_priv(ndev); + platform_set_drvdata(pdev, ndev); + + priv->variant = (enum emac_variant)of_device_get_match_data(&pdev->dev); + if (!priv->variant) { + dev_err(&pdev->dev, "Missing sun8i-emac variant\n"); + return -EINVAL; + } + + priv->phy_node = of_parse_phandle(node, "phy", 0); + if (!priv->phy_node) { + netdev_err(ndev, "No associated PHY\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) { + ret = PTR_ERR(priv->base); + dev_err(&pdev->dev, "Cannot request MMIO: %d\n", ret); + return ret; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "syscon"); + priv->syscon = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->syscon)) { + ret = PTR_ERR(priv->syscon); + dev_err(&pdev->dev, + "Cannot map system control registers: %d\n", ret); + return ret; + } + + priv->ahb_clk = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(priv->ahb_clk)) { + ret = PTR_ERR(priv->ahb_clk); + dev_err(&pdev->dev, "Cannot get AHB clock err=%d\n", ret); + goto probe_err; + } + + priv->rst = devm_reset_control_get_optional(&pdev->dev, "ahb"); + if (IS_ERR(priv->rst)) { + ret = PTR_ERR(priv->rst); + if (ret == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_info(&pdev->dev, "No MAC reset control found %d\n", ret); + priv->rst = NULL; + } + + if (priv->variant == H3_EMAC) + priv->use_internal_phy = of_property_read_bool(node, + "allwinner,use-internal-phy"); + + if (priv->use_internal_phy) { + priv->ephy_clk = devm_clk_get(&pdev->dev, "ephy"); + if (IS_ERR(priv->ephy_clk)) { + ret = PTR_ERR(priv->ephy_clk); + dev_err(&pdev->dev, "Cannot get EPHY clock err=%d\n", + ret); + goto probe_err; + } + + priv->rst_ephy = devm_reset_control_get_optional(&pdev->dev, + "ephy"); + if (IS_ERR(priv->rst_ephy)) { + ret = PTR_ERR(priv->rst_ephy); + if (ret == -EPROBE_DEFER) + goto probe_err; + dev_info(&pdev->dev, + "No EPHY reset control found %d\n", ret); + priv->rst_ephy = NULL; + } + } + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq < 0) { + ret = priv->irq; + dev_err(&pdev->dev, "Cannot claim IRQ: %d\n", ret); + goto probe_err; + } + + spin_lock_init(&priv->tx_lock); + + ndev->netdev_ops = &sun8i_emac_netdev_ops; + ndev->ethtool_ops = &sun8i_emac_ethtool_ops; + + priv->ndev = ndev; + priv->dev = &pdev->dev; + + ndev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA; + ndev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM; + ndev->features |= ndev->hw_features; + ndev->hw_features |= NETIF_F_RXFCS; + ndev->hw_features |= NETIF_F_RXALL; + ndev->hw_features |= NETIF_F_LOOPBACK; + ndev->priv_flags |= IFF_UNICAST_FLT; + + ndev->watchdog_timeo = msecs_to_jiffies(5000); + netif_carrier_off(ndev); + + /* Benched on OPIPC with 100M, setting more than 256 does not give any + * perf boost + */ + priv->nbdesc_rx = 128; + priv->nbdesc_tx = 256; + + ret = register_netdev(ndev); + if (ret) { + dev_err(&pdev->dev, "ERROR: Register %s failed\n", ndev->name); + goto probe_err; + } + + return 0; + +probe_err: + free_netdev(ndev); + return ret; +} + +static int sun8i_emac_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + + unregister_netdev(ndev); + platform_set_drvdata(pdev, NULL); + free_netdev(ndev); + + return 0; +} + +static const struct of_device_id sun8i_emac_of_match_table[] = { + { .compatible = "allwinner,sun8i-a83t-emac", + .data = (void *)A83T_EMAC }, + { .compatible = "allwinner,sun8i-h3-emac", + .data = (void *)H3_EMAC }, + { .compatible = "allwinner,sun50i-a64-emac", + .data = (void *)A64_EMAC }, + {} +}; +MODULE_DEVICE_TABLE(of, sun8i_emac_of_match_table); + +static struct platform_driver sun8i_emac_driver = { + .probe = sun8i_emac_probe, + .remove = sun8i_emac_remove, + .driver = { + .name = "sun8i-emac", + .of_match_table = sun8i_emac_of_match_table, + }, +}; + +module_platform_driver(sun8i_emac_driver); + +MODULE_DESCRIPTION("sun8i Ethernet driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("LABBE Corentin dev, "detected phy mask %x\n", ~phy_mask); phy_mask = ~phy_mask; + + #if IS_ENABLED(CONFIG_OF) + if (of_machine_is_compatible("ti,am335x-bone")) + davinci_mdio_update_dt_from_phymask(phy_mask); + #endif + } else { /* desperately scan all phys */ dev_warn(data->dev, "no live phy, scanning all\n"); @@ -334,6 +344,93 @@ static int davinci_mdio_probe_dt(struct mdio_platform_data *data, return 0; } +static void davinci_mdio_update_dt_from_phymask(u32 phy_mask) +{ + int i, len, skip; + u32 addr; + __be32 *old_phy_p, *phy_id_p; + struct property *phy_id_property = NULL; + struct device_node *node_p, *slave_p; + + addr = 0; + + for (i = 0; i < PHY_MAX_ADDR; i++) { + if ((phy_mask & (1 << i)) == 0) { + addr = (u32) i; + break; + } + } + + for_each_compatible_node(node_p, NULL, "ti,cpsw") { + for_each_node_by_name(slave_p, "slave") { + +#if IS_ENABLED(CONFIG_OF_OVERLAY) + skip = 1; + // Hack, the overlay fixup "slave" doesn't have phy-mode... + old_phy_p = (__be32 *) of_get_property(slave_p, "phy-mode", &len); + + if (len != (sizeof(__be32 *) * 1)) + { + skip = 0; + } + + if (skip) { +#endif + + old_phy_p = (__be32 *) of_get_property(slave_p, "phy_id", &len); + + if (len != (sizeof(__be32 *) * 2)) + goto err_out; + + if (old_phy_p) { + + phy_id_property = kzalloc(sizeof(*phy_id_property), GFP_KERNEL); + + if (! phy_id_property) + goto err_out; + + phy_id_property->length = len; + phy_id_property->name = kstrdup("phy_id", GFP_KERNEL); + phy_id_property->value = kzalloc(len, GFP_KERNEL); + + if (! phy_id_property->name) + goto err_out; + + if (! phy_id_property->value) + goto err_out; + + memcpy(phy_id_property->value, old_phy_p, len); + + phy_id_p = (__be32 *) phy_id_property->value + 1; + + *phy_id_p = cpu_to_be32(addr); + + of_update_property(slave_p, phy_id_property); + pr_info("davinci_mdio: dt: updated phy_id[%d] from phy_mask[%x]\n", addr, phy_mask); + + ++addr; + } +#if IS_ENABLED(CONFIG_OF_OVERLAY) + } +#endif + } + } + + return; + +err_out: + + if (phy_id_property) { + if (phy_id_property->name) + kfree(phy_id_property->name); + + if (phy_id_property->value) + kfree(phy_id_property->value); + + if (phy_id_property) + kfree(phy_id_property); + } +} #endif #if IS_ENABLED(CONFIG_OF) diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index bc07ad3..168ffc0 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -116,4 +116,11 @@ config OF_OVERLAY config OF_NUMA bool +config OF_CONFIGFS + bool "Device Tree Overlay ConfigFS interface" + select CONFIGFS_FS + depends on OF_OVERLAY + help + Enable a simple user-space driven DT overlay interface. + endif # OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index d7efd9d..aa5ef9d 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,4 +1,5 @@ obj-y = base.o device.o platform.o +obj-$(CONFIG_OF_CONFIGFS) += configfs.o obj-$(CONFIG_OF_DYNAMIC) += dynamic.o obj-$(CONFIG_OF_FLATTREE) += fdt.o obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o diff --git a/drivers/of/base.c b/drivers/of/base.c index 3ce6953..08a77f2 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "of_private.h" @@ -44,6 +45,18 @@ static const char *of_stdout_options; struct kset *of_kset; +const struct rhashtable_params of_phandle_ht_params = { + .key_offset = offsetof(struct device_node, phandle), /* base offset */ + .key_len = sizeof(phandle), + .head_offset = offsetof(struct device_node, ht_node), + .automatic_shrinking = true, +}; + +struct rhashtable *of_phandle_ht; + +/* default is false */ +bool of_phandle_ht_is_disabled; + /* * Used to protect the of_aliases, to hold off addition of nodes to sysfs. * This mutex must be held whenever modifications are being made to the @@ -163,13 +176,19 @@ int __of_add_property_sysfs(struct device_node *np, struct property *pp) return rc; } -int __of_attach_node_sysfs(struct device_node *np) +int __of_attach_node_post(struct device_node *np) { const char *name; struct kobject *parent; struct property *pp; int rc; + if (of_phandle_ht_available()) { + rc = of_phandle_ht_insert(np); + WARN(rc, "insert to phandle hash fail @%s\n", + of_node_full_name(np)); + } + if (!IS_ENABLED(CONFIG_SYSFS)) return 0; @@ -201,6 +220,18 @@ int __of_attach_node_sysfs(struct device_node *np) void __init of_core_init(void) { struct device_node *np; + int ret; + + of_phandle_ht = kzalloc(sizeof(*of_phandle_ht), GFP_KERNEL); + if (!of_phandle_ht) { + pr_warn("devicetree: Failed to allocate hashtable\n"); + return; + } + ret = rhashtable_init(of_phandle_ht, &of_phandle_ht_params); + if (ret) { + pr_warn("devicetree: Failed to initialize hashtable\n"); + return; + } /* Create the kset, and register existing nodes */ mutex_lock(&of_mutex); @@ -211,12 +242,16 @@ void __init of_core_init(void) return; } for_each_of_allnodes(np) - __of_attach_node_sysfs(np); + __of_attach_node_post(np); mutex_unlock(&of_mutex); /* Symlink in /proc as required by userspace ABI */ if (of_root) proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base"); + + ret = of_overlay_init(); + if (ret != 0) + pr_warn("of_init: of_overlay_init failed!\n"); } static struct property *__of_find_property(const struct device_node *np, @@ -1100,9 +1135,14 @@ struct device_node *of_find_node_by_phandle(phandle handle) return NULL; raw_spin_lock_irqsave(&devtree_lock, flags); - for_each_of_allnodes(np) - if (np->phandle == handle) - break; + /* when we're ready use the hash table (and not disabled) */ + if (of_phandle_ht_available() && !of_phandle_ht_is_disabled) + np = of_phandle_ht_lookup(handle); + else { /* fallback */ + for_each_of_allnodes(np) + if (np->phandle == handle) + break; + } of_node_get(np); raw_spin_unlock_irqrestore(&devtree_lock, flags); return np; diff --git b/drivers/of/configfs.c b/drivers/of/configfs.c new file mode 100644 index 0000000..c7e999c --- /dev/null +++ b/drivers/of/configfs.c @@ -0,0 +1,307 @@ +/* + * Configfs entries for device-tree + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "of_private.h" + +struct cfs_overlay_item { + struct config_item item; + + char path[PATH_MAX]; + + const struct firmware *fw; + struct device_node *overlay; + int ov_id; + + void *dtbo; + int dtbo_size; +}; + +static int create_overlay(struct cfs_overlay_item *overlay, void *blob) +{ + int err; + + /* unflatten the tree */ + of_fdt_unflatten_tree(blob, NULL, &overlay->overlay); + if (overlay->overlay == NULL) { + pr_err("%s: failed to unflatten tree\n", __func__); + err = -EINVAL; + goto out_err; + } + pr_debug("%s: unflattened OK\n", __func__); + + /* mark it as detached */ + of_node_set_flag(overlay->overlay, OF_DETACHED); + + /* perform resolution */ + err = of_resolve_phandles(overlay->overlay); + if (err != 0) { + pr_err("%s: Failed to resolve tree\n", __func__); + goto out_err; + } + pr_debug("%s: resolved OK\n", __func__); + + err = of_overlay_create(overlay->overlay); + if (err < 0) { + pr_err("%s: Failed to create overlay (err=%d)\n", + __func__, err); + goto out_err; + } + overlay->ov_id = err; + +out_err: + return err; +} + +static inline struct cfs_overlay_item *to_cfs_overlay_item( + struct config_item *item) +{ + return item ? container_of(item, struct cfs_overlay_item, item) : NULL; +} + +static ssize_t cfs_overlay_item_path_show(struct config_item *item, char *page) +{ + return sprintf(page, "%s\n", to_cfs_overlay_item(item)->path); +} + +static ssize_t cfs_overlay_item_path_store(struct config_item *item, + const char *page, size_t count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + const char *p = page; + char *s; + int err; + + /* if it's set do not allow changes */ + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) + return -EPERM; + + /* copy to path buffer (and make sure it's always zero terminated */ + count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p); + overlay->path[sizeof(overlay->path) - 1] = '\0'; + + /* strip trailing newlines */ + s = overlay->path + strlen(overlay->path); + while (s > overlay->path && *--s == '\n') + *s = '\0'; + + pr_debug("%s: path is '%s'\n", __func__, overlay->path); + + err = request_firmware(&overlay->fw, overlay->path, NULL); + if (err != 0) + goto out_err; + + err = create_overlay(overlay, (void *)overlay->fw->data); + if (err < 0) + goto out_err; + + return count; + +out_err: + + release_firmware(overlay->fw); + overlay->fw = NULL; + + overlay->path[0] = '\0'; + return err; +} + +static ssize_t cfs_overlay_item_status_show(struct config_item *item, + char *page) +{ + return sprintf(page, "%s\n", to_cfs_overlay_item(item)->ov_id >= 0 ? + "applied" : "unapplied"); +} + +CONFIGFS_ATTR(cfs_overlay_item_, path); +CONFIGFS_ATTR_RO(cfs_overlay_item_, status); + +static struct configfs_attribute *cfs_overlay_attrs[] = { + &cfs_overlay_item_attr_path, + &cfs_overlay_item_attr_status, + NULL, +}; + +ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, void *buf, + size_t max_count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + pr_debug("%s: buf=%p max_count=%u\n", __func__, + buf, max_count); + + if (overlay->dtbo == NULL) + return 0; + + /* copy if buffer provided */ + if (buf != NULL) { + /* the buffer must be large enough */ + if (overlay->dtbo_size > max_count) + return -ENOSPC; + + memcpy(buf, overlay->dtbo, overlay->dtbo_size); + } + + return overlay->dtbo_size; +} + +ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, const void *buf, + size_t count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + int err; + + /* if it's set do not allow changes */ + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) + return -EPERM; + + /* copy the contents */ + overlay->dtbo = kmemdup(buf, count, GFP_KERNEL); + if (overlay->dtbo == NULL) + return -ENOMEM; + + overlay->dtbo_size = count; + + err = create_overlay(overlay, overlay->dtbo); + if (err < 0) + goto out_err; + + return count; + +out_err: + kfree(overlay->dtbo); + overlay->dtbo = NULL; + overlay->dtbo_size = 0; + + return err; +} + +CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M); + +static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = { + &cfs_overlay_item_attr_dtbo, + NULL, +}; + +static void cfs_overlay_release(struct config_item *item) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + if (overlay->ov_id >= 0) + of_overlay_destroy(overlay->ov_id); + if (overlay->fw) + release_firmware(overlay->fw); + /* kfree with NULL is safe */ + kfree(overlay->dtbo); + kfree(overlay); +} + +static struct configfs_item_operations cfs_overlay_item_ops = { + .release = cfs_overlay_release, +}; + +static struct config_item_type cfs_overlay_type = { + .ct_item_ops = &cfs_overlay_item_ops, + .ct_attrs = cfs_overlay_attrs, + .ct_bin_attrs = cfs_overlay_bin_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *cfs_overlay_group_make_item( + struct config_group *group, const char *name) +{ + struct cfs_overlay_item *overlay; + + overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); + if (!overlay) + return ERR_PTR(-ENOMEM); + overlay->ov_id = -1; + + config_item_init_type_name(&overlay->item, name, &cfs_overlay_type); + return &overlay->item; +} + +static void cfs_overlay_group_drop_item(struct config_group *group, + struct config_item *item) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + config_item_put(&overlay->item); +} + +static struct configfs_group_operations overlays_ops = { + .make_item = cfs_overlay_group_make_item, + .drop_item = cfs_overlay_group_drop_item, +}; + +static struct config_item_type overlays_type = { + .ct_group_ops = &overlays_ops, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_group_operations of_cfs_ops = { + /* empty - we don't allow anything to be created */ +}; + +static struct config_item_type of_cfs_type = { + .ct_group_ops = &of_cfs_ops, + .ct_owner = THIS_MODULE, +}; + +struct config_group of_cfs_overlay_group; + +static struct configfs_subsystem of_cfs_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "device-tree", + .ci_type = &of_cfs_type, + }, + }, + .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex), +}; + +static int __init of_cfs_init(void) +{ + int ret; + + pr_info("%s\n", __func__); + + config_group_init(&of_cfs_subsys.su_group); + config_group_init_type_name(&of_cfs_overlay_group, "overlays", + &overlays_type); + configfs_add_default_group(&of_cfs_overlay_group, + &of_cfs_subsys.su_group); + + ret = configfs_register_subsystem(&of_cfs_subsys); + if (ret != 0) { + pr_err("%s: failed to register subsys\n", __func__); + goto out; + } + pr_info("%s: OK\n", __func__); +out: + return ret; +} +late_initcall(of_cfs_init); diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 888fdbc..2a89b17 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "of_private.h" @@ -43,9 +44,16 @@ void of_node_put(struct device_node *node) } EXPORT_SYMBOL(of_node_put); -void __of_detach_node_sysfs(struct device_node *np) +void __of_detach_node_post(struct device_node *np) { struct property *pp; + int rc; + + if (of_phandle_ht_available()) { + rc = of_phandle_ht_remove(np); + WARN(rc, "remove from phandle hash fail @%s\n", + of_node_full_name(np)); + } if (!IS_ENABLED(CONFIG_SYSFS)) return; @@ -253,7 +261,7 @@ int of_attach_node(struct device_node *np) __of_attach_node(np); raw_spin_unlock_irqrestore(&devtree_lock, flags); - __of_attach_node_sysfs(np); + __of_attach_node_post(np); mutex_unlock(&of_mutex); of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, &rd); @@ -306,7 +314,7 @@ int of_detach_node(struct device_node *np) __of_detach_node(np); raw_spin_unlock_irqrestore(&devtree_lock, flags); - __of_detach_node_sysfs(np); + __of_detach_node_post(np); mutex_unlock(&of_mutex); of_reconfig_notify(OF_RECONFIG_DETACH_NODE, &rd); @@ -397,8 +405,9 @@ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags) } /** - * __of_node_dup() - Duplicate or create an empty device node dynamically. - * @fmt: Format string (plus vargs) for new full name of the device node + * __of_node_dupv() - Duplicate or create an empty device node dynamically. + * @fmt: Format string for new full name of the device node + * @vargs: va_list containing the arugments for the node full name * * Create an device tree node, either by duplicating an empty node or by allocating * an empty one suitable for further modification. The node data are @@ -406,17 +415,15 @@ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags) * OF_DETACHED bits set. Returns the newly allocated node or NULL on out of * memory error. */ -struct device_node *__of_node_dup(const struct device_node *np, const char *fmt, ...) +struct device_node *__of_node_dupv(const struct device_node *np, + const char *fmt, va_list vargs) { - va_list vargs; struct device_node *node; node = kzalloc(sizeof(*node), GFP_KERNEL); if (!node) return NULL; - va_start(vargs, fmt); node->full_name = kvasprintf(GFP_KERNEL, fmt, vargs); - va_end(vargs); if (!node->full_name) { kfree(node); return NULL; @@ -448,6 +455,24 @@ struct device_node *__of_node_dup(const struct device_node *np, const char *fmt, return NULL; } +/** + * __of_node_dup() - Duplicate or create an empty device node dynamically. + * @fmt: Format string (plus vargs) for new full name of the device node + * + * See: __of_node_dupv() + */ +struct device_node *__of_node_dup(const struct device_node *np, + const char *fmt, ...) +{ + va_list vargs; + struct device_node *node; + + va_start(vargs, fmt); + node = __of_node_dupv(np, fmt, vargs); + va_end(vargs); + return node; +} + static void __of_changeset_entry_destroy(struct of_changeset_entry *ce) { of_node_put(ce->np); @@ -614,10 +639,10 @@ static int __of_changeset_entry_apply(struct of_changeset_entry *ce) switch (ce->action) { case OF_RECONFIG_ATTACH_NODE: - __of_attach_node_sysfs(ce->np); + __of_attach_node_post(ce->np); break; case OF_RECONFIG_DETACH_NODE: - __of_detach_node_sysfs(ce->np); + __of_detach_node_post(ce->np); break; case OF_RECONFIG_ADD_PROPERTY: /* ignore duplicate names */ @@ -813,3 +838,295 @@ int of_changeset_action(struct of_changeset *ocs, unsigned long action, return 0; } EXPORT_SYMBOL_GPL(of_changeset_action); + +/* changeset helpers */ + +/** + * of_changeset_create_device_node - Create an empty device node + * + * @ocs: changeset pointer + * @parent: parent device node + * @fmt: format string for the node's full_name + * @args: argument list for the format string + * + * Create an empty device node, marking it as detached and allocated. + * + * Returns a device node on success, an error encoded pointer otherwise + */ +struct device_node *of_changeset_create_device_nodev( + struct of_changeset *ocs, struct device_node *parent, + const char *fmt, va_list vargs) +{ + struct device_node *node; + + node = __of_node_dupv(NULL, fmt, vargs); + if (!node) + return ERR_PTR(-ENOMEM); + + node->parent = parent; + return node; +} +EXPORT_SYMBOL_GPL(of_changeset_create_device_nodev); + +/** + * of_changeset_create_device_node - Create an empty device node + * + * @ocs: changeset pointer + * @parent: parent device node + * @fmt: Format string for the node's full_name + * ... Arguments + * + * Create an empty device node, marking it as detached and allocated. + * + * Returns a device node on success, an error encoded pointer otherwise + */ +__printf(3, 4) struct device_node * +of_changeset_create_device_node(struct of_changeset *ocs, + struct device_node *parent, const char *fmt, ...) +{ + va_list vargs; + struct device_node *node; + + va_start(vargs, fmt); + node = of_changeset_create_device_nodev(ocs, parent, fmt, vargs); + va_end(vargs); + return node; +} +EXPORT_SYMBOL_GPL(of_changeset_create_device_node); + +/** + * __of_changeset_add_property_copy - Create/update a new property copying + * name & value + * + * @ocs: changeset pointer + * @np: device node pointer + * @name: name of the property + * @value: pointer to the value data + * @length: length of the value in bytes + * @update: True on update operation + * + * Adds/updates a property to the changeset by making copies of the name & value + * entries. The @update parameter controls whether an add or update takes place. + * + * Returns zero on success, a negative error value otherwise. + */ +int __of_changeset_add_update_property_copy(struct of_changeset *ocs, + struct device_node *np, const char *name, const void *value, + int length, bool update) +{ + struct property *prop; + char *new_name; + void *new_value; + int ret = -ENOMEM; + + prop = kzalloc(sizeof(*prop), GFP_KERNEL); + if (!prop) + return -ENOMEM; + + new_name = kstrdup(name, GFP_KERNEL); + if (!new_name) + goto out_err; + + /* + * NOTE: There is no check for zero length value. + * In case of a boolean property, this will allocate a value + * of zero bytes. We do this to work around the use + * of of_get_property() calls on boolean values. + */ + new_value = kmemdup(value, length, GFP_KERNEL); + if (!new_value) + goto out_err; + + of_property_set_flag(prop, OF_DYNAMIC); + + prop->name = new_name; + prop->value = new_value; + prop->length = length; + + if (!update) + ret = of_changeset_add_property(ocs, np, prop); + else + ret = of_changeset_update_property(ocs, np, prop); + + if (!ret) + return 0; + +out_err: + kfree(prop->value); + kfree(prop->name); + kfree(prop); + return ret; +} +EXPORT_SYMBOL_GPL(__of_changeset_add_update_property_copy); + +/** + * of_changeset_add_property_stringf - Create a new formatted string property + * + * @ocs: changeset pointer + * @np: device node pointer + * @name: name of the property + * @fmt: format of string property + * ... arguments of the format string + * + * Adds a string property to the changeset by making copies of the name + * and the formatted value. + * + * Returns zero on success, a negative error value otherwise. + */ +__printf(4, 5) int of_changeset_add_property_stringf( + struct of_changeset *ocs, struct device_node *np, + const char *name, const char *fmt, ...) +{ + va_list vargs; + int ret; + + va_start(vargs, fmt); + ret = __of_changeset_add_update_property_stringv(ocs, np, name, fmt, + vargs, false); + va_end(vargs); + return ret; +} +EXPORT_SYMBOL_GPL(of_changeset_add_property_stringf); + +/** + * of_changeset_update_property_stringf - Update formatted string property + * + * @ocs: changeset pointer + * @np: device node pointer + * @name: name of the property + * @fmt: format of string property + * ... arguments of the format string + * + * Updates a string property to the changeset by making copies of the name + * and the formatted value. + * + * Returns zero on success, a negative error value otherwise. + */ +int of_changeset_update_property_stringf( + struct of_changeset *ocs, struct device_node *np, + const char *name, const char *fmt, ...) +{ + va_list vargs; + int ret; + + va_start(vargs, fmt); + ret = __of_changeset_add_update_property_stringv(ocs, np, name, fmt, + vargs, true); + va_end(vargs); + return ret; +} +EXPORT_SYMBOL_GPL(of_changeset_update_property_stringf); + +/** + * __of_changeset_add_update_property_string_list - Create/update a string + * list property + * + * @ocs: changeset pointer + * @np: device node pointer + * @name: name of the property + * @strs: pointer to the string list + * @count: string count + * @update: True on update operation + * + * Adds a string list property to the changeset. + * + * Returns zero on success, a negative error value otherwise. + */ +int __of_changeset_add_update_property_string_list( + struct of_changeset *ocs, struct device_node *np, + const char *name, const char **strs, int count, bool update) +{ + int total = 0, i, ret; + char *value, *s; + + for (i = 0; i < count; i++) { + /* check if it's NULL */ + if (!strs[i]) + return -EINVAL; + total += strlen(strs[i]) + 1; + } + + value = kmalloc(total, GFP_KERNEL); + if (!value) + return -ENOMEM; + + for (i = 0, s = value; i < count; i++) { + /* no need to check for NULL, check above */ + strcpy(s, strs[i]); + s += strlen(strs[i]) + 1; + } + + ret = __of_changeset_add_update_property_copy(ocs, np, name, value, + total, update); + + kfree(value); + + return ret; +} +EXPORT_SYMBOL_GPL(__of_changeset_add_update_property_string_list); + +static struct device_node * +__of_changeset_node_move_one(struct of_changeset *ocs, + struct device_node *np, struct device_node *new_parent) +{ + struct device_node *np2; + const char *unitname; + int err; + + err = of_changeset_detach_node(ocs, np); + if (err) + return ERR_PTR(err); + + unitname = strrchr(np->full_name, '/'); + if (!unitname) + unitname = np->full_name; + + np2 = __of_node_dup(np, "%s/%s", + new_parent->full_name, unitname); + if (!np2) + return ERR_PTR(-ENOMEM); + np2->parent = new_parent; + + err = of_changeset_attach_node(ocs, np2); + if (err) + return ERR_PTR(err); + + return np2; +} + +/** + * of_changeset_node_move_to - Moves a subtree to a new place in + * the tree + * + * @ocs: changeset pointer + * @np: device node pointer to be moved + * @to: device node of the new parent + * + * Moves a subtree to a new place in the tree. + * Note that a move is a safe operation because the phandles + * remain valid. + * + * Returns zero on success, a negative error value otherwise. + */ +int of_changeset_node_move(struct of_changeset *ocs, + struct device_node *np, struct device_node *new_parent) +{ + struct device_node *npc, *nppc; + + /* move the root first */ + nppc = __of_changeset_node_move_one(ocs, np, new_parent); + if (IS_ERR(nppc)) + return PTR_ERR(nppc); + + /* move the subtrees next */ + for_each_child_of_node(np, npc) { + nppc = __of_changeset_node_move_one(ocs, npc, nppc); + if (IS_ERR(nppc)) { + of_node_put(npc); + return PTR_ERR(nppc); + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(of_changeset_node_move); diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 18bbb45..2f906dd 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -79,9 +79,9 @@ extern void __of_update_property_sysfs(struct device_node *np, struct property *newprop, struct property *oldprop); extern void __of_attach_node(struct device_node *np); -extern int __of_attach_node_sysfs(struct device_node *np); +extern int __of_attach_node_post(struct device_node *np); extern void __of_detach_node(struct device_node *np); -extern void __of_detach_node_sysfs(struct device_node *np); +extern void __of_detach_node_post(struct device_node *np); extern void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop); @@ -95,4 +95,46 @@ extern void __of_sysfs_remove_bin_file(struct device_node *np, #define for_each_transaction_entry_reverse(_oft, _te) \ list_for_each_entry_reverse(_te, &(_oft)->te_list, node) +#if defined(CONFIG_OF_OVERLAY) +extern int of_overlay_init(void); +#else +static inline int of_overlay_init(void) +{ + return 0; +} +#endif + +extern const struct rhashtable_params of_phandle_ht_params; +extern struct rhashtable *of_phandle_ht; + +/* for unittest use */ +extern bool of_phandle_ht_is_disabled; + +static inline bool of_phandle_ht_available(void) +{ + return of_phandle_ht != NULL; +} + +static inline int of_phandle_ht_insert(struct device_node *np) +{ + if (!np || !np->phandle) + return 0; + return rhashtable_insert_fast(of_phandle_ht, + &np->ht_node, of_phandle_ht_params); +} + +static inline int of_phandle_ht_remove(struct device_node *np) +{ + if (!np || !np->phandle) + return 0; + return rhashtable_remove_fast(of_phandle_ht, + &np->ht_node, of_phandle_ht_params); +} + +static inline struct device_node *of_phandle_ht_lookup(phandle handle) +{ + return rhashtable_lookup_fast(of_phandle_ht, + &handle, of_phandle_ht_params); +} + #endif /* _LINUX_OF_PRIVATE_H */ diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 318dbb5..27e5c21 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -22,11 +22,28 @@ #include #include #include +#include +#include #include "of_private.h" +/* fwd. decl */ +struct of_overlay; +struct of_overlay_info; + +/* an attribute for each fragment */ +struct fragment_attribute { + struct attribute attr; + ssize_t (*show)(struct kobject *kobj, struct fragment_attribute *fattr, + char *buf); + ssize_t (*store)(struct kobject *kobj, struct fragment_attribute *fattr, + const char *buf, size_t count); + struct of_overlay_info *ovinfo; +}; + /** * struct of_overlay_info - Holds a single overlay info + * @info: info node that contains the target and overlay * @target: target of the overlay operation * @overlay: pointer to the overlay contents node * @@ -34,8 +51,13 @@ * records. */ struct of_overlay_info { + struct of_overlay *ov; + struct device_node *info; struct device_node *target; struct device_node *overlay; + struct attribute_group attr_group; + struct attribute *attrs[2]; + struct fragment_attribute target_attr; }; /** @@ -52,11 +74,26 @@ struct of_overlay { struct list_head node; int count; struct of_overlay_info *ovinfo_tab; + const struct attribute_group **attr_groups; struct of_changeset cset; + struct kobject kobj; + int target_index; + struct device_node *target_root; }; +/* master enable switch; once set to 0 can't be re-enabled */ +static atomic_t ov_enable = ATOMIC_INIT(1); + +static int __init of_overlay_disable_setup(char *str __always_unused) +{ + atomic_set(&ov_enable, 0); + return 1; +} +__setup("of_overlay_disable", of_overlay_disable_setup); + static int of_overlay_apply_one(struct of_overlay *ov, struct device_node *target, const struct device_node *overlay); +static int overlay_removal_is_ok(struct of_overlay *ov); static int of_overlay_apply_single_property(struct of_overlay *ov, struct device_node *target, struct property *prop) @@ -187,30 +224,92 @@ static int of_overlay_apply(struct of_overlay *ov) /* * Find the target node using a number of different strategies - * in order of preference + * in order of preference. Respects the target index if available. * * "target" property containing the phandle of the target * "target-path" property containing the path of the target */ -static struct device_node *find_target_node(struct device_node *info_node) +static struct device_node *find_target_node(struct of_overlay *ov, + struct device_node *info_node, int index) { + struct device_node *target = NULL, *np; const char *path; + char *newpath; u32 val; int ret; /* first try to go by using the target as a phandle */ - ret = of_property_read_u32(info_node, "target", &val); - if (ret == 0) - return of_find_node_by_phandle(val); + ret = of_property_read_u32_index(info_node, "target", index, &val); + if (ret == 0) { + target = of_find_node_by_phandle(val); + if (!target) { + pr_err("%s: Could not find target phandle 0x%x\n", + __func__, val); + return NULL; + } + goto check_root; + } - /* now try to locate by path */ - ret = of_property_read_string(info_node, "target-path", &path); - if (ret == 0) - return of_find_node_by_path(path); + /* failed, try to locate by path */ + ret = of_property_read_string_index(info_node, "target-path", index, + &path); + if (ret == 0) { + + if (!ov->target_root) { + target = of_find_node_by_path(path); + if (!target) + pr_err("%s: Could not find target path \"%s\"\n", + __func__, path); + return target; + } + + /* remove preceding '/' from path; relative path */ + if (*path == '/') { + while (*path == '/') + path++; + + newpath = kasprintf(GFP_KERNEL, "%s%s%s", + of_node_full_name(ov->target_root), + *path ? "/" : "", path); + if (!newpath) { + pr_err("%s: Could not allocate \"%s%s%s\"\n", + __func__, + of_node_full_name(ov->target_root), + *path ? "/" : "", path); + return NULL; + } + target = of_find_node_by_path(newpath); + kfree(newpath); - pr_err("Failed to find target for node %p (%s)\n", - info_node, info_node->name); + return target; + } + /* target is an alias, need to check */ + target = of_find_node_by_path(path); + if (!target) { + pr_err("%s: Could not find alias \"%s\"\n", + __func__, path); + return NULL; + } + goto check_root; + } + + return NULL; + +check_root: + if (!ov->target_root) + return target; + + /* got a target, but we have to check it's under target root */ + for (np = target; np; np = np->parent) { + if (np == ov->target_root) + return target; + } + pr_err("%s: target \"%s\" not under target_root \"%s\"\n", + __func__, of_node_full_name(target), + of_node_full_name(ov->target_root)); + /* target is not under target_root */ + of_node_put(target); return NULL; } @@ -235,10 +334,12 @@ static int of_fill_overlay_info(struct of_overlay *ov, if (ovinfo->overlay == NULL) goto err_fail; - ovinfo->target = find_target_node(info_node); + ovinfo->target = find_target_node(ov, info_node, ov->target_index); if (ovinfo->target == NULL) goto err_fail; + ovinfo->info = of_node_get(info_node); + return 0; err_fail: @@ -249,6 +350,17 @@ err_fail: return -EINVAL; } +static ssize_t target_show(struct kobject *kobj, + struct fragment_attribute *fattr, char *buf) +{ + struct of_overlay_info *ovinfo = fattr->ovinfo; + + return snprintf(buf, PAGE_SIZE, "%s\n", + of_node_full_name(ovinfo->target)); +} + +static const struct fragment_attribute target_template_attr = __ATTR_RO(target); + /** * of_build_overlay_info() - Build an overlay info array * @ov Overlay to build @@ -266,7 +378,7 @@ static int of_build_overlay_info(struct of_overlay *ov, { struct device_node *node; struct of_overlay_info *ovinfo; - int cnt, err; + int i, cnt, err; /* worst case; every child is a node */ cnt = 0; @@ -287,14 +399,45 @@ static int of_build_overlay_info(struct of_overlay *ov, /* if nothing filled, return error */ if (cnt == 0) { - kfree(ovinfo); - return -ENODEV; + err = -ENODEV; + goto err_free_ovinfo; } ov->count = cnt; ov->ovinfo_tab = ovinfo; + ov->attr_groups = kcalloc(cnt + 1, + sizeof(struct attribute_group *), GFP_KERNEL); + if (ov->attr_groups == NULL) { + err = -ENOMEM; + goto err_free_ovinfo; + } + + for (i = 0; i < cnt; i++) { + ovinfo = &ov->ovinfo_tab[i]; + + ov->attr_groups[i] = &ovinfo->attr_group; + + ovinfo->target_attr = target_template_attr; + /* make lockdep happy */ + sysfs_attr_init(&ovinfo->target_attr.attr); + ovinfo->target_attr.ovinfo = ovinfo; + + ovinfo->attrs[0] = &ovinfo->target_attr.attr; + ovinfo->attrs[1] = NULL; + + /* NOTE: direct reference to the full_name */ + ovinfo->attr_group.name = kbasename(ovinfo->info->full_name); + ovinfo->attr_group.attrs = ovinfo->attrs; + + } + ov->attr_groups[i] = NULL; + return 0; + +err_free_ovinfo: + kfree(ovinfo); + return err; } /** @@ -311,46 +454,201 @@ static int of_free_overlay_info(struct of_overlay *ov) struct of_overlay_info *ovinfo; int i; + /* free attribute groups space */ + kfree(ov->attr_groups); + /* do it in reverse */ for (i = ov->count - 1; i >= 0; i--) { ovinfo = &ov->ovinfo_tab[i]; of_node_put(ovinfo->target); of_node_put(ovinfo->overlay); + of_node_put(ovinfo->info); } kfree(ov->ovinfo_tab); return 0; } +static int of_overlay_add_symbols( + struct device_node *tree, + struct of_overlay *ov) +{ + struct of_overlay_info *ovinfo; + struct device_node *root_sym = NULL; + struct device_node *child = NULL; + struct property *prop; + const char *path, *s; + char *new_path; + int i, len, err; + + /* both may fail (if no fixups are required) */ + root_sym = of_find_node_by_path("/__symbols__"); + child = of_get_child_by_name(tree, "__symbols__"); + + err = 0; + /* do nothing if either is NULL */ + if (!root_sym || !child) + goto out; + + for_each_property_of_node(child, prop) { + + /* skip properties added automatically */ + if (of_prop_cmp(prop->name, "name") == 0) + continue; + + err = of_property_read_string(child, + prop->name, &path); + if (err != 0) { + pr_err("Could not find symbol '%s'\n", prop->name); + continue; + } + + /* now find fragment index */ + s = path; + + /* compare paths to find fragment index */ + for (i = 0, ovinfo = NULL, len = -1; i < ov->count; i++) { + ovinfo = &ov->ovinfo_tab[i]; + + pr_debug("#%d: overlay->name=%s target->name=%s\n", + i, ovinfo->overlay->full_name, + ovinfo->target->full_name); + + len = strlen(ovinfo->overlay->full_name); + if (strncasecmp(path, ovinfo->overlay->full_name, + len) == 0 && path[len] == '/') + break; + } + + if (i >= ov->count) + continue; + + pr_debug("found target at #%d\n", i); + new_path = kasprintf(GFP_KERNEL, "%s%s", + ovinfo->target->full_name, + path + len); + if (!new_path) { + pr_err("Failed to allocate propname for \"%s\"\n", + prop->name); + err = -ENOMEM; + break; + } + + err = of_changeset_add_property_string(&ov->cset, root_sym, + prop->name, new_path); + + /* free always */ + kfree(new_path); + + if (err) { + pr_err("Failed to add property for \"%s\"\n", + prop->name); + break; + } + } + +out: + of_node_put(child); + of_node_put(root_sym); + + return err; +} + static LIST_HEAD(ov_list); static DEFINE_IDR(ov_idr); -/** - * of_overlay_create() - Create and apply an overlay - * @tree: Device node containing all the overlays - * - * Creates and applies an overlay while also keeping track - * of the overlay in a list. This list can be used to prevent - * illegal overlay removals. - * - * Returns the id of the created overlay, or a negative error number - */ -int of_overlay_create(struct device_node *tree) +static inline struct of_overlay *kobj_to_overlay(struct kobject *kobj) +{ + return container_of(kobj, struct of_overlay, kobj); +} + +void of_overlay_release(struct kobject *kobj) +{ + struct of_overlay *ov = kobj_to_overlay(kobj); + + of_node_put(ov->target_root); + kfree(ov); +} + +static ssize_t enable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&ov_enable)); +} + +static ssize_t enable_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret; + bool new_enable; + + ret = strtobool(buf, &new_enable); + if (ret != 0) + return ret; + /* if we've disabled it, no going back */ + if (atomic_read(&ov_enable) == 0) + return -EPERM; + atomic_set(&ov_enable, (int)new_enable); + return count; +} + +static struct kobj_attribute enable_attr = __ATTR_RW(enable); + +static const struct attribute *overlay_global_attrs[] = { + &enable_attr.attr, + NULL +}; + +static ssize_t can_remove_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct of_overlay *ov = kobj_to_overlay(kobj); + + return snprintf(buf, PAGE_SIZE, "%d\n", overlay_removal_is_ok(ov)); +} + +static struct kobj_attribute can_remove_attr = __ATTR_RO(can_remove); + +static struct attribute *overlay_attrs[] = { + &can_remove_attr.attr, + NULL +}; + +static struct kobj_type of_overlay_ktype = { + .release = of_overlay_release, + .sysfs_ops = &kobj_sysfs_ops, /* default kobj sysfs ops */ + .default_attrs = overlay_attrs, +}; + +static struct kset *ov_kset; + +static int __of_overlay_create(struct device_node *tree, + int target_index, struct device_node *target_root) { struct of_overlay *ov; int err, id; + /* administratively disabled */ + if (!atomic_read(&ov_enable)) + return -EPERM; + /* allocate the overlay structure */ ov = kzalloc(sizeof(*ov), GFP_KERNEL); if (ov == NULL) return -ENOMEM; ov->id = -1; + ov->target_index = target_index; + ov->target_root = of_node_get(target_root); + INIT_LIST_HEAD(&ov->node); of_changeset_init(&ov->cset); + /* initialize kobject */ + kobject_init(&ov->kobj, &of_overlay_ktype); + mutex_lock(&of_mutex); id = idr_alloc(&ov_idr, ov, 0, 0, GFP_KERNEL); @@ -373,19 +671,44 @@ int of_overlay_create(struct device_node *tree) if (err) goto err_abort_trans; + err = of_overlay_add_symbols(tree, ov); + if (err) { + pr_err("%s: of_overlay_add_symbols() failed for tree@%s\n", + __func__, tree->full_name); + goto err_abort_trans; + } + /* apply the changeset */ err = __of_changeset_apply(&ov->cset); if (err) goto err_revert_overlay; + ov->kobj.kset = ov_kset; + err = kobject_add(&ov->kobj, NULL, "%d", id); + if (err != 0) { + pr_err("%s: kobject_add() failed for tree@%s\n", + __func__, tree->full_name); + goto err_cancel_overlay; + } + + err = sysfs_create_groups(&ov->kobj, ov->attr_groups); + if (err != 0) { + pr_err("%s: sysfs_create_groups() failed for tree@%s\n", + __func__, tree->full_name); + goto err_remove_kobj; + } + /* add to the tail of the overlay list */ list_add_tail(&ov->node, &ov_list); mutex_unlock(&of_mutex); return id; - +err_remove_kobj: + kobject_put(&ov->kobj); +err_cancel_overlay: + of_changeset_revert(&ov->cset); err_revert_overlay: err_abort_trans: of_free_overlay_info(ov); @@ -393,13 +716,66 @@ err_free_idr: idr_remove(&ov_idr, ov->id); err_destroy_trans: of_changeset_destroy(&ov->cset); + of_node_put(ov->target_root); kfree(ov); mutex_unlock(&of_mutex); return err; } + +/** + * of_overlay_create() - Create and apply an overlay + * @tree: Device node containing all the overlays + * + * Creates and applies an overlay while also keeping track + * of the overlay in a list. This list can be used to prevent + * illegal overlay removals. + * + * Returns the id of the created overlay, or a negative error number + */ +int of_overlay_create(struct device_node *tree) +{ + return __of_overlay_create(tree, 0, NULL); +} EXPORT_SYMBOL_GPL(of_overlay_create); +/** + * of_overlay_create_target_index() - Create and apply an overlay + * @tree: Device node containing all the overlays + * @index: Index to use in the target properties + * + * Creates and applies an overlay while also keeping track + * of the overlay in a list. This list can be used to prevent + * illegal overlay removals. + * + * Returns the id of the created overlay, or a negative error number + */ +int of_overlay_create_target_index(struct device_node *tree, int index) +{ + return __of_overlay_create(tree, index, NULL); +} +EXPORT_SYMBOL_GPL(of_overlay_create_target_index); + +/** + * of_overlay_create_target_root() - Create and apply an overlay + * under which will be limited to target_root + * @tree: Device node containing all the overlays + * @target_root: Target root for the overlay. + * + * Creates and applies an overlay while also keeping track + * of the overlay in a list. This list can be used to prevent + * illegal overlay removals. The overlay is only allowed to + * target nodes under the target_root node. + * + * Returns the id of the created overlay, or an negative error number + */ +int of_overlay_create_target_root(struct device_node *tree, + struct device_node *target_root) +{ + return __of_overlay_create(tree, 0, target_root); +} +EXPORT_SYMBOL_GPL(of_overlay_create_target_root); + /* check whether the given node, lies under the given tree */ static int overlay_subtree_check(struct device_node *tree, struct device_node *dn) @@ -500,11 +876,13 @@ int of_overlay_destroy(int id) list_del(&ov->node); + sysfs_remove_groups(&ov->kobj, ov->attr_groups); __of_changeset_revert(&ov->cset); of_free_overlay_info(ov); idr_remove(&ov_idr, id); of_changeset_destroy(&ov->cset); - kfree(ov); + + kobject_put(&ov->kobj); err = 0; @@ -534,7 +912,7 @@ int of_overlay_destroy_all(void) __of_changeset_revert(&ov->cset); of_free_overlay_info(ov); idr_remove(&ov_idr, ov->id); - kfree(ov); + kobject_put(&ov->kobj); } mutex_unlock(&of_mutex); @@ -542,3 +920,18 @@ int of_overlay_destroy_all(void) return 0; } EXPORT_SYMBOL_GPL(of_overlay_destroy_all); + +/* called from of_init() */ +int of_overlay_init(void) +{ + int rc; + + ov_kset = kset_create_and_add("overlays", NULL, &of_kset->kobj); + if (!ov_kset) + return -ENOMEM; + + rc = sysfs_create_files(&ov_kset->kobj, overlay_global_attrs); + WARN(rc, "%s: error adding global attributes\n", __func__); + + return rc; +} diff --git a/drivers/of/unittest-data/testcases.dts b/drivers/of/unittest-data/testcases.dts index 12f7c3d..a6ded1b 100644 --- a/drivers/of/unittest-data/testcases.dts +++ b/drivers/of/unittest-data/testcases.dts @@ -75,5 +75,15 @@ target = <0x00000000>; }; }; + overlay16 { + fragment@0 { + target = <0x00000000 0x00000004>; + }; + }; + overlay18 { + fragment@0 { + target = <0x00000000>; + }; + }; }; }; }; diff --git a/drivers/of/unittest-data/tests-overlay.dtsi b/drivers/of/unittest-data/tests-overlay.dtsi index 02ba56c..170b04d 100644 --- a/drivers/of/unittest-data/tests-overlay.dtsi +++ b/drivers/of/unittest-data/tests-overlay.dtsi @@ -110,6 +110,30 @@ }; }; }; + + unittest16: test-unittest16 { + compatible = "unittest"; + status = "disabled"; + reg = <16>; + }; + + unittest17: test-unittest17 { + compatible = "unittest"; + status = "disabled"; + reg = <17>; + }; + + unittest18: test-unittest18 { + compatible = "unittest"; + status = "disabled"; + reg = <18>; + }; + + unittest19: test-unittest19 { + compatible = "unittest"; + status = "disabled"; + reg = <19>; + }; }; }; @@ -325,5 +349,44 @@ }; }; + /* test enable using indirect functionality */ + overlay16 { + fragment@0 { + target = <&unittest17>, <&unittest16>; + __overlay__ { + status = "okay"; + }; + }; + }; + + /* test enable using target root (relative path) */ + overlay17 { + fragment@0 { + target-path = "/"; + __overlay__ { + status = "okay"; + }; + }; + }; + + /* test enable using target phandle */ + overlay18 { + fragment@0 { + target = <&unittest18>; + __overlay__ { + status = "okay"; + }; + }; + }; + + /* test trying to enable out of root (should fail) */ + overlay19 { + fragment@0 { + target = <&unittest19>; + __overlay__ { + status = "okay"; + }; + }; + }; }; }; diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 53c83d6..b560ae7 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -24,6 +24,9 @@ #include +#include +#include + #include "of_private.h" static struct unittest_results { @@ -542,6 +545,59 @@ static void __init of_unittest_changeset(void) #endif } +static void __init of_unittest_changeset_helper(void) +{ +#ifdef CONFIG_OF_DYNAMIC + struct device_node *n1, *n2, *n21, *parent, *np; + struct of_changeset chgset; + + of_changeset_init(&chgset); + + parent = of_find_node_by_path("/testcase-data/changeset"); + + unittest(parent, "testcase setup failure\n"); + n1 = of_changeset_create_device_node(&chgset, + parent, "/testcase-data/changeset/n1"); + unittest(n1, "testcase setup failure\n"); + n2 = of_changeset_create_device_node(&chgset, + parent, "/testcase-data/changeset/n2"); + unittest(n2, "testcase setup failure\n"); + n21 = of_changeset_create_device_node(&chgset, n2, "%s/%s", + "/testcase-data/changeset/n2", "n21"); + unittest(n21, "testcase setup failure\n"); + + unittest(!of_changeset_add_property_string(&chgset, parent, + "prop-add", "foo"), "fail add prop\n"); + + unittest(!of_changeset_attach_node(&chgset, n1), "fail n1 attach\n"); + unittest(!of_changeset_attach_node(&chgset, n2), "fail n2 attach\n"); + unittest(!of_changeset_attach_node(&chgset, n21), "fail n21 attach\n"); + + unittest(!of_changeset_apply(&chgset), "apply failed\n"); + + /* Make sure node names are constructed correctly */ + np = of_find_node_by_path("/testcase-data/changeset/n1"); + unittest(np, "'%s' not added\n", n1->full_name); + of_node_put(np); + + /* Make sure node names are constructed correctly */ + np = of_find_node_by_path("/testcase-data/changeset/n2"); + unittest(np, "'%s' not added\n", n2->full_name); + of_node_put(np); + + np = of_find_node_by_path("/testcase-data/changeset/n2/n21"); + unittest(np, "'%s' not added\n", n21->full_name); + of_node_put(np); + + unittest(!of_changeset_revert(&chgset), "revert failed\n"); + + of_changeset_destroy(&chgset); + + of_node_put(parent); +#endif +} + + static void __init of_unittest_parse_interrupts(void) { struct device_node *np; @@ -877,7 +933,7 @@ static int attach_node_and_children(struct device_node *np) of_node_clear_flag(np, OF_DETACHED); raw_spin_unlock_irqrestore(&devtree_lock, flags); - __of_attach_node_sysfs(np); + __of_attach_node_post(np); mutex_unlock(&of_mutex); while (child) { @@ -935,7 +991,7 @@ static int __init unittest_data_add(void) if (!of_root) { of_root = unittest_data_node; for_each_of_allnodes(np) - __of_attach_node_sysfs(np); + __of_attach_node_post(np); of_aliases = of_find_node_by_path("/aliases"); of_chosen = of_find_node_by_path("/chosen"); return 0; @@ -1854,6 +1910,273 @@ static inline void of_unittest_overlay_i2c_15(void) { } #endif +static void of_unittest_overlay_16(void) +{ + int ret; + int overlay_nr = 16; + int unittest_nr = 16; + enum overlay_type ovtype = PDEV_OVERLAY; + int before = 0; + int after = 1; + struct device_node *np = NULL; + int id = -1; + + /* unittest device must not be in before state */ + if (of_unittest_device_exists(unittest_nr, ovtype) != before) { + unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n", + overlay_path(overlay_nr), + unittest_path(unittest_nr, ovtype), + !before ? "enabled" : "disabled"); + return; + } + + np = of_find_node_by_path(overlay_path(overlay_nr)); + if (np == NULL) { + unittest(0, "could not find overlay node @\"%s\"\n", + overlay_path(overlay_nr)); + ret = -EINVAL; + goto out; + } + + /* unittest16 is at index #1 */ + ret = of_overlay_create_target_index(np, 1); + if (ret < 0) { + unittest(0, "could not create overlay from \"%s\"\n", + overlay_path(overlay_nr)); + goto out; + } + id = ret; + of_unittest_track_overlay(id); + + ret = 0; + +out: + of_node_put(np); + + if (ret) + return; + + /* unittest device must be to set to after state */ + if (of_unittest_device_exists(unittest_nr, ovtype) != after) { + unittest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n", + overlay_path(overlay_nr), + unittest_path(unittest_nr, ovtype), + !after ? "enabled" : "disabled"); + return; + } + + unittest(1, "overlay test %d passed\n", 16); +} + +static void of_unittest_overlay_17(void) +{ + int ret; + int overlay_nr = 17; + int unittest_nr = 17; + enum overlay_type ovtype = PDEV_OVERLAY; + int before = 0; + int after = 1; + const char *root_path; + struct device_node *np = NULL, *target_root = NULL; + int id = -1; + + /* unittest device must not be in before state */ + if (of_unittest_device_exists(unittest_nr, ovtype) != before) { + unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n", + overlay_path(overlay_nr), + unittest_path(unittest_nr, ovtype), + !before ? "enabled" : "disabled"); + return; + } + + np = of_find_node_by_path(overlay_path(overlay_nr)); + if (np == NULL) { + unittest(0, "could not find overlay node @\"%s\"\n", + overlay_path(overlay_nr)); + ret = -EINVAL; + goto out; + } + + root_path = "/testcase-data/overlay-node/test-bus/test-unittest17"; + target_root = of_find_node_by_path(root_path); + if (!target_root) { + unittest(0, "could not find target_root node @\"%s\"\n", + root_path); + ret = -EINVAL; + goto out; + } + + ret = of_overlay_create_target_root(np, target_root); + of_node_put(target_root); + + if (ret < 0) { + unittest(0, "could not create overlay from \"%s\"\n", + overlay_path(overlay_nr)); + goto out; + } + id = ret; + of_unittest_track_overlay(id); + + ret = 0; + +out: + of_node_put(np); + + if (ret) + return; + + /* unittest device must be to set to after state */ + if (of_unittest_device_exists(unittest_nr, ovtype) != after) { + unittest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n", + overlay_path(overlay_nr), + unittest_path(unittest_nr, ovtype), + !after ? "enabled" : "disabled"); + return; + } + + unittest(1, "overlay test %d passed\n", 17); +} + +static void of_unittest_overlay_18(void) +{ + int ret; + int overlay_nr = 18; + int unittest_nr = 18; + enum overlay_type ovtype = PDEV_OVERLAY; + int before = 0; + int after = 1; + const char *root_path; + struct device_node *np = NULL, *target_root = NULL; + int id = -1; + + /* unittest device must not be in before state */ + if (of_unittest_device_exists(unittest_nr, ovtype) != before) { + unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n", + overlay_path(overlay_nr), + unittest_path(unittest_nr, ovtype), + !before ? "enabled" : "disabled"); + return; + } + + np = of_find_node_by_path(overlay_path(overlay_nr)); + if (np == NULL) { + unittest(0, "could not find overlay node @\"%s\"\n", + overlay_path(overlay_nr)); + ret = -EINVAL; + goto out; + } + + root_path = "/testcase-data/overlay-node/test-bus/test-unittest18"; + target_root = of_find_node_by_path(root_path); + if (!target_root) { + unittest(0, "could not find target_root node @\"%s\"\n", + root_path); + ret = -EINVAL; + goto out; + } + + ret = of_overlay_create_target_root(np, target_root); + of_node_put(target_root); + + if (ret < 0) { + unittest(0, "could not create overlay from \"%s\"\n", + overlay_path(overlay_nr)); + goto out; + } + id = ret; + of_unittest_track_overlay(id); + + ret = 0; + +out: + of_node_put(np); + + if (ret) + return; + + /* unittest device must be to set to after state */ + if (of_unittest_device_exists(unittest_nr, ovtype) != after) { + unittest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n", + overlay_path(overlay_nr), + unittest_path(unittest_nr, ovtype), + !after ? "enabled" : "disabled"); + return; + } + + unittest(1, "overlay test %d passed\n", 18); +} + +static void of_unittest_overlay_19(void) +{ + int ret; + int overlay_nr = 19; + int unittest_nr = 19; + enum overlay_type ovtype = PDEV_OVERLAY; + int before = 0; + int after = 0; + const char *root_path; + struct device_node *np = NULL, *target_root = NULL; + int id = -1; + + /* unittest device must not be in before state */ + if (of_unittest_device_exists(unittest_nr, ovtype) != before) { + unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n", + overlay_path(overlay_nr), + unittest_path(unittest_nr, ovtype), + !before ? "enabled" : "disabled"); + return; + } + + np = of_find_node_by_path(overlay_path(overlay_nr)); + if (np == NULL) { + unittest(0, "could not find overlay node @\"%s\"\n", + overlay_path(overlay_nr)); + ret = -EINVAL; + goto out; + } + + root_path = "/testcase-data/overlay-node/test-bus/test-unittest19"; + target_root = of_find_node_by_path(root_path); + if (!target_root) { + unittest(0, "could not find target_root node @\"%s\"\n", + root_path); + ret = -EINVAL; + goto out; + } + + ret = of_overlay_create_target_root(np, target_root); + of_node_put(target_root); + + if (ret >= 0) { + unittest(0, "created overlay from \"%s\" while we shouldn't\n", + overlay_path(overlay_nr)); + id = ret; + of_unittest_track_overlay(id); + ret = -EINVAL; + goto out; + } + + ret = 0; + +out: + of_node_put(np); + + if (ret) + return; + + /* unittest device must be to set to after state */ + if (of_unittest_device_exists(unittest_nr, ovtype) != after) { + unittest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n", + overlay_path(overlay_nr), + unittest_path(unittest_nr, ovtype), + !after ? "enabled" : "disabled"); + return; + } + + unittest(1, "overlay test %d passed\n", 16); +} + + static void __init of_unittest_overlay(void) { struct device_node *bus_np = NULL; @@ -1904,6 +2227,12 @@ static void __init of_unittest_overlay(void) of_unittest_overlay_10(); of_unittest_overlay_11(); + of_unittest_overlay_16(); + + of_unittest_overlay_17(); + of_unittest_overlay_18(); + of_unittest_overlay_19(); + #if IS_BUILTIN(CONFIG_I2C) if (unittest(of_unittest_overlay_i2c_init() == 0, "i2c init failed\n")) goto out; @@ -1926,6 +2255,70 @@ out: static inline void __init of_unittest_overlay(void) { } #endif +#define PHANDLE_LOOKUPS 1000 + +static void __init of_unittest_phandle_hash(void) +{ + struct device_node *node; + phandle max_phandle; + u32 ph; + unsigned long flags; + int i, j, total; + ktime_t start, end; + s64 dur[2]; + int dec, frac; + + /* test only available when hashing is available */ + if (!of_phandle_ht_available()) { + pr_warn("phandle hash test requires hash to be initialized\n"); + return; + } + + /* find the maximum phandle of the tree */ + raw_spin_lock_irqsave(&devtree_lock, flags); + max_phandle = 0; + total = 0; + for_each_of_allnodes(node) { + if (node->phandle != (phandle)-1U && + node->phandle > max_phandle) + max_phandle = node->phandle; + total++; + } + raw_spin_unlock_irqrestore(&devtree_lock, flags); + max_phandle++; + + pr_debug("phandle: max-phandle #%u, #%d total nodes\n", + (u32)max_phandle, total); + + /* perform random lookups using the hash */ + for (j = 0; j < 2; j++) { + + /* disabled for pass #0, enabled for pass #1 */ + of_phandle_ht_is_disabled = j == 0; + + start = ktime_get_raw(); + for (i = 0; i < PHANDLE_LOOKUPS; i++) { + ph = prandom_u32() % max_phandle; + node = of_find_node_by_phandle(ph); + of_node_put(node); + } + end = ktime_get_raw(); + + dur[j] = ktime_to_us(end) - ktime_to_us(start); + pr_debug("#%d lookups in %lld us (%s)\n", + PHANDLE_LOOKUPS, dur[j], + j == 0 ? "original" : "hashed"); + } + + unittest(dur[0] > dur[1], "Non hashing phandles are faster!?"); + + dec = (int)div64_s64(dur[0] * 10 + 5, dur[1]); + frac = dec % 10; + dec /= 10; + pr_info("the hash method is %d.%d times faster than the original\n", + dec, frac); +} + static int __init of_unittest(void) { struct device_node *np; @@ -1954,11 +2347,13 @@ static int __init of_unittest(void) of_unittest_property_string(); of_unittest_property_copy(); of_unittest_changeset(); + of_unittest_changeset_helper(); of_unittest_parse_interrupts(); of_unittest_parse_interrupts_extended(); of_unittest_match_node(); of_unittest_platform_populate(); of_unittest_overlay(); + of_unittest_phandle_hash(); /* Double check linkage after removing testcase data */ of_unittest_check_tree_linkage(); diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index b3fe1d3..6f9a380 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -179,6 +179,18 @@ config PINCTRL_ST select PINCONF select GPIOLIB_IRQCHIP +config PINCTRL_TI_IODELAY + bool "TI IODelay Module pinconf driver" + depends on OF + select PINCONF + select GENERIC_PINCONF + select REGMAP_MMIO + help + Say Y here to support Texas Instruments' IODelay pinconf driver. + IODelay module is used for the DRA7 SoC family. This driver is in + addition to PINCTRL_SINGLE which controls the mux. + + config PINCTRL_TZ1090 bool "Toumaz Xenif TZ1090 pin control driver" depends on SOC_TZ1090 diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 8ebd7b8..499eb1e 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_SIRF) += sirf/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ +obj-$(CONFIG_PINCTRL_TI_IODELAY)+= pinctrl-ti-iodelay.o obj-$(CONFIG_PINCTRL_TZ1090) += pinctrl-tz1090.o obj-$(CONFIG_PINCTRL_TZ1090_PDC) += pinctrl-tz1090-pdc.o obj-$(CONFIG_PINCTRL_U300) += pinctrl-u300.o diff --git b/drivers/pinctrl/pinctrl-ti-iodelay.c b/drivers/pinctrl/pinctrl-ti-iodelay.c new file mode 100644 index 0000000..8d33414 --- /dev/null +++ b/drivers/pinctrl/pinctrl-ti-iodelay.c @@ -0,0 +1,968 @@ +/* + * Support for configuration of IO Delay module found on Texas Instruments SoCs + * such as DRA7 + * + * Copyright (C) 2015 Texas Instruments, Inc. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IODELAY_REG_NAME_LEN ((sizeof(u32) * 2) + 3) +#define DRIVER_NAME "ti-io-delay" +/* Should I change this? Abuse? */ +#define IODELAY_MUX_PINS_NAME "pinctrl-single,pins" + +/* Device tree match, populated later */ +static const struct of_device_id ti_iodelay_of_match[]; + +/** + * struct ti_iodelay_conf_vals - Description of each configuration parameters. + * @offset: Configuration register offset + * @a_delay: Agnostic Delay (in ps) + * @g_delay: Gnostic Delay (in ps) + */ +struct ti_iodelay_conf_vals { + u16 offset; + u16 a_delay; + u16 g_delay; +}; + +/** + * struct ti_iodelay_reg_data - Describes the registers for the IOdelay instance + * @signature_mask: Conf reg- mask for the signature bits + * @signature_value: Conf reg- signature value to be written (see TRM) + * @lock_mask: Conf reg- mask for the lock bits + * @lock_val: Conf reg- lock value for the lock bits (see TRM) + * @unlock_val: Conf reg- unlock value for the lock bits (see TRM) + * @binary_data_coarse_mask: Conf reg- coarse mask (see TRM) + * @binary_data_fine_mask: Conf reg- fine mask (see TRM) + * @reg_refclk_offset: Refclk register offset + * @refclk_period_mask: Refclk mask + * @reg_coarse_offset: Coarse register configuration offset + * @coarse_delay_count_mask: Coarse delay count mask + * @coarse_ref_count_mask: Coarse ref count mask + * @reg_fine_offset: Fine register configuration offset + * @fine_delay_count_mask: Fine delay count mask + * @fine_ref_count_mask: Fine ref count mask + * @reg_global_lock_offset: Global(for the IOdelay module) lock register offset + * @global_lock_mask: Lock mask + * @global_unlock_val: unlock value + * @global_lock_val: lock value + * @reg_start_offset: Where does the configuration registers start? + * @regmap_config: Regmap configuration for the IODelay region + */ +struct ti_iodelay_reg_data { + u32 signature_mask; + u32 signature_value; + u32 lock_mask; + u32 lock_val; + u32 unlock_val; + u32 binary_data_coarse_mask; + u32 binary_data_fine_mask; + + u32 reg_refclk_offset; + u32 refclk_period_mask; + + u32 reg_coarse_offset; + u32 coarse_delay_count_mask; + u32 coarse_ref_count_mask; + + u32 reg_fine_offset; + u32 fine_delay_count_mask; + u32 fine_ref_count_mask; + + u32 reg_global_lock_offset; + u32 global_lock_mask; + u32 global_unlock_val; + u32 global_lock_val; + + u32 reg_start_offset; + + struct regmap_config *regmap_config; +}; + +/** + * struct ti_iodelay_reg_values - Computed io_reg configuration values (see TRM) + * @coarse_ref_count: Coarse reference count + * @coarse_delay_count: Coarse delay count + * @fine_ref_count: Fine reference count + * @fine_delay_count: Fine Delay count + * @ref_clk_period: Reference Clock period + * @cdpe: Coarse delay parameter + * @fdpe: Fine delay parameter + */ +struct ti_iodelay_reg_values { + u16 coarse_ref_count; + u16 coarse_delay_count; + + u16 fine_ref_count; + u16 fine_delay_count; + + u16 ref_clk_period; + + u32 cdpe; + u32 fdpe; +}; + +/** + * struct ti_iodelay_pin_name - name of the pins + * @name: name + */ +struct ti_iodelay_pin_name { + char name[IODELAY_REG_NAME_LEN]; +}; + +/** + * struct ti_iodelay_pingroup - Structure that describes one group + * @np: Node pointer (device tree) + * @name: Name of the group + * @map: pinctrl map allocated for the group + * @vals: configuration values allocated for the group (from dt) + * @nvals: number of configuration values allocated + * @config: pinconf "Config" - currently a dummy value + * @node: list node to next group + */ +struct ti_iodelay_pingroup { + struct device_node *np; + const char *name; + struct pinctrl_map *map; + struct ti_iodelay_conf_vals *vals; + int nvals; + unsigned long config; + struct list_head node; +}; + +/** + * struct ti_iodelay_device - Represents information for a IOdelay instance + * @dev: device pointer + * @reg_base: Remapped virtual address + * @regmap: Regmap for this IOdelay instance + * @pctl: Pinctrl device + * @desc: pinctrl descriptor for pctl + * @pa: pinctrl pin wise description + * @names: names of the pins + * @groups: list of pinconf groups for iodelay instance + * @ngroups: number of groups in the list + * @mutex: mutex to protect group list modification + * @reg_data: Register definition data for the IODelay instance + * @reg_init_conf_values: Initial configuration values. + */ +struct ti_iodelay_device { + struct device *dev; + void __iomem *reg_base; + struct regmap *regmap; + + struct pinctrl_dev *pctl; + struct pinctrl_desc desc; + struct pinctrl_pin_desc *pa; + struct ti_iodelay_pin_name *names; + + struct list_head groups; + int ngroups; + struct mutex mutex; /* list protection */ + + const struct ti_iodelay_reg_data *reg_data; + struct ti_iodelay_reg_values reg_init_conf_values; +}; + +/*--- IOdelay configuration stuff ----*/ + +/** + * ti_iodelay_extract() - extract bits for a field + * @val: register value + * @mask: Mask + * + * Return: extracted value which is appropriately shifted + */ +static inline u32 ti_iodelay_extract(u32 val, u32 mask) +{ + return (val & mask) >> __ffs(mask); +} + +/** + * ti_iodelay_compute_dpe() - Compute equation for delay parameter + * @period: Period to use + * @ref: Reference Count + * @delay: Delay count + * @delay_m: Delay multiplier + * + * Return: Computed delay parameter + */ +static inline u32 ti_iodelay_compute_dpe(u16 period, u16 ref, u16 delay, + u16 delay_m) +{ + u64 m, d; + + /* Handle overflow conditions */ + m = 10 * (u64)period * (u64)ref; + d = 2 * (u64)delay * (u64)delay_m; + + /* Truncate result back to 32 bits */ + return div64_u64(m, d); +} + +/** + * ti_iodelay_pinconf_set() - Configure the pin configuration + * @iod: IODelay device + * @val: Configuration value + * + * Update the configuration register as per TRM and lockup once done. + * *IMPORTANT NOTE* SoC TRM does recommend doing iodelay programmation only + * while in Isolation. But, then, isolation also implies that every pin + * on the SoC(including DDR) will be isolated out. The only benefit being + * a glitchless configuration, However, the intent of this driver is purely + * to support a "glitchy" configuration where applicable. + * + * Return: 0 in case of success, else appropriate error value + */ +static int ti_iodelay_pinconf_set(struct ti_iodelay_device *iod, + struct ti_iodelay_conf_vals *val) +{ + const struct ti_iodelay_reg_data *reg = iod->reg_data; + struct ti_iodelay_reg_values *ival = &iod->reg_init_conf_values; + struct device *dev = iod->dev; + u32 g_delay_coarse, g_delay_fine; + u32 a_delay_coarse, a_delay_fine; + u32 c_elements, f_elements; + u32 total_delay; + u32 reg_mask, reg_val, tmp_val; + int r; + + /* NOTE: Truncation is expected in all division below */ + g_delay_coarse = val->g_delay / 920; + g_delay_fine = ((val->g_delay % 920) * 10) / 60; + + a_delay_coarse = val->a_delay / ival->cdpe; + a_delay_fine = ((val->a_delay % ival->cdpe) * 10) / ival->fdpe; + + c_elements = g_delay_coarse + a_delay_coarse; + f_elements = (g_delay_fine + a_delay_fine) / 10; + + if (f_elements > 22) { + total_delay = c_elements * ival->cdpe + f_elements * ival->fdpe; + c_elements = total_delay / ival->cdpe; + f_elements = (total_delay % ival->cdpe) / ival->fdpe; + } + + reg_mask = reg->signature_mask; + reg_val = reg->signature_value << __ffs(reg->signature_mask); + + reg_mask |= reg->binary_data_coarse_mask; + tmp_val = c_elements << __ffs(reg->binary_data_coarse_mask); + if (tmp_val & ~reg->binary_data_coarse_mask) { + dev_err(dev, "Masking overflow of coarse elements %08x\n", + tmp_val); + tmp_val &= reg->binary_data_coarse_mask; + } + reg_val |= tmp_val; + + reg_mask |= reg->binary_data_fine_mask; + tmp_val = f_elements << __ffs(reg->binary_data_fine_mask); + if (tmp_val & ~reg->binary_data_fine_mask) { + dev_err(dev, "Masking overflow of fine elements %08x\n", + tmp_val); + tmp_val &= reg->binary_data_fine_mask; + } + reg_val |= tmp_val; + + /* + * NOTE: we leave the iodelay values unlocked - this is to work around + * situations such as those found with mmc mode change. + * However, this leaves open any unwarranted changes to padconf register + * impacting iodelay configuration. Use with care! + */ + reg_mask |= reg->lock_mask; + reg_val |= reg->unlock_val << __ffs(reg->lock_mask); + r = regmap_update_bits(iod->regmap, val->offset, reg_mask, reg_val); + + dev_dbg(dev, "Set reg 0x%x Delay(a=%d g=%d), Elements(C=%d F=%d)0x%x\n", + val->offset, val->a_delay, val->g_delay, c_elements, + f_elements, reg_val); + + return r; +} + +/** + * ti_iodelay_pinconf_init_dev() - Initialize IODelay device + * @iod: IODelay device + * + * Unlocks the IODelay region, computes the common parameters + * + * Return: 0 in case of success, else appropriate error value + */ +static int ti_iodelay_pinconf_init_dev(struct ti_iodelay_device *iod) +{ + const struct ti_iodelay_reg_data *reg = iod->reg_data; + struct device *dev = iod->dev; + struct ti_iodelay_reg_values *ival = &iod->reg_init_conf_values; + u32 val; + int r; + + /* unlock the IOdelay region */ + r = regmap_update_bits(iod->regmap, reg->reg_global_lock_offset, + reg->global_lock_mask, reg->global_unlock_val); + if (r) + return r; + + /* Read up Recalibration sequence done by bootloader */ + r = regmap_read(iod->regmap, reg->reg_refclk_offset, &val); + if (r) + return r; + ival->ref_clk_period = ti_iodelay_extract(val, reg->refclk_period_mask); + dev_dbg(dev, "refclk_period=0x%04x\n", ival->ref_clk_period); + + r = regmap_read(iod->regmap, reg->reg_coarse_offset, &val); + if (r) + return r; + ival->coarse_ref_count = + ti_iodelay_extract(val, reg->coarse_ref_count_mask); + ival->coarse_delay_count = + ti_iodelay_extract(val, reg->coarse_delay_count_mask); + if (!ival->coarse_delay_count) { + dev_err(dev, "Invalid Coarse delay count (0) (reg=0x%08x)\n", + val); + return -EINVAL; + } + ival->cdpe = ti_iodelay_compute_dpe(ival->ref_clk_period, + ival->coarse_ref_count, + ival->coarse_delay_count, 88); + if (!ival->cdpe) { + dev_err(dev, "Invalid cdpe computed params = %d %d %d\n", + ival->ref_clk_period, ival->coarse_ref_count, + ival->coarse_delay_count); + return -EINVAL; + } + dev_dbg(iod->dev, "coarse: ref=0x%04x delay=0x%04x cdpe=0x%08x\n", + ival->coarse_ref_count, ival->coarse_delay_count, ival->cdpe); + + r = regmap_read(iod->regmap, reg->reg_fine_offset, &val); + if (r) + return r; + ival->fine_ref_count = + ti_iodelay_extract(val, reg->fine_ref_count_mask); + ival->fine_delay_count = + ti_iodelay_extract(val, reg->fine_delay_count_mask); + if (!ival->fine_delay_count) { + dev_err(dev, "Invalid Fine delay count (0) (reg=0x%08x)\n", + val); + return -EINVAL; + } + ival->fdpe = ti_iodelay_compute_dpe(ival->ref_clk_period, + ival->fine_ref_count, + ival->fine_delay_count, 264); + if (!ival->fdpe) { + dev_err(dev, "Invalid fdpe(0) computed params = %d %d %d\n", + ival->ref_clk_period, ival->fine_ref_count, + ival->fine_delay_count); + return -EINVAL; + } + dev_dbg(iod->dev, "fine: ref=0x%04x delay=0x%04x fdpe=0x%08x\n", + ival->fine_ref_count, ival->fine_delay_count, ival->fdpe); + + return 0; +} + +/** + * ti_iodelay_pinconf_deinit_dev() - deinit the IOdelay device + * @iod: IODelay device + * + * Deinitialize the IODelay device (basically just lock the region back up. + */ +static void ti_iodelay_pinconf_deinit_dev(struct ti_iodelay_device *iod) +{ + const struct ti_iodelay_reg_data *reg = iod->reg_data; + + /* lock the IOdelay region back again */ + regmap_update_bits(iod->regmap, reg->reg_global_lock_offset, + reg->global_lock_mask, reg->global_lock_val); +} + +/*--- Pinctrl/pinconf framework stuff ----*/ + +/** + * ti_iodelay_get_group() - Find the group mapped by a group selector + * @iod: IODelay device + * @gselector: Group Selector + * + * Return: Corresponding group representing group selector in list of groups + * managed in IOdelay device OR NULL if not found. + */ +static struct ti_iodelay_pingroup *ti_iodelay_get_group(struct ti_iodelay_device + *iod, + unsigned gselector) +{ + struct ti_iodelay_pingroup *group; + int gid = 0; + + list_for_each_entry(group, &iod->groups, node) { + if (gid == gselector) + return group; + gid++; + } + + dev_err(iod->dev, "%s could not find pingroup %i\n", __func__, + gselector); + return NULL; +} + +/** + * ti_iodelay_dt_node_to_map() - Map a device tree node to appropriate group + * @pctldev: pinctrl device representing IODelay device + * @np: Node Pointer (device tree) + * @map: Pinctrl Map returned back to pinctrl framework + * @num_maps: Number of maps (1) + * + * Maps the device tree description into a group of configuration parameters + * for IOdelay block entry. + * + * Return: 0 in case of success, else appropriate error value + */ +static int ti_iodelay_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned *num_maps) +{ + struct ti_iodelay_device *iod; + struct device *dev; + /* const char **pgnames; */ + int ret = 0; + const __be32 *mux; + struct ti_iodelay_conf_vals *vals; + struct ti_iodelay_pingroup *group; + int size, index, idx, rows; + u32 offset, val; + + iod = pinctrl_dev_get_drvdata(pctldev); + if (!iod) + return -EINVAL; + dev = iod->dev; + + *map = devm_kzalloc(dev, sizeof(**map), GFP_KERNEL); + if (!*map) + return -ENOMEM; + *num_maps = 0; + + group = devm_kzalloc(dev, sizeof(*group), GFP_KERNEL); + if (!group) { + ret = -ENOMEM; + goto free_map; + } + + mux = of_get_property(np, IODELAY_MUX_PINS_NAME, &size); + if ((!mux) || (size < sizeof(*mux) * 2)) { + dev_err(dev, "bad data for mux %s\n", np->name); + ret = -EINVAL; + goto free_group; + } + + size /= sizeof(*mux); /* Number of elements in array */ + rows = size / 2; + + vals = devm_kzalloc(dev, sizeof(*vals) * rows, GFP_KERNEL); + if (!vals) { + ret = -ENOMEM; + goto free_group; + } + + index = 0; + idx = 0; + while (index < size) { + offset = be32_to_cpup(mux + index++); + val = be32_to_cpup(mux + index++); + vals[idx].offset = offset; + vals[idx].a_delay = val & 0xFFFF; + vals[idx].g_delay = (val & 0xFFFF0000) >> 16; + if (offset > iod->reg_data->regmap_config->max_register) { + dev_err(dev, "Invalid offset for %s 0x%x\n", + np->name, offset); + break; + } + dev_dbg(dev, "%s offset=%x a_delay = %d g_delay = %d\n", + np->name, vals[idx].offset, vals[idx].a_delay, + vals[idx].g_delay); + idx++; + } + + group->name = np->name; + group->np = np; + group->vals = vals; + group->nvals = idx; + group->config = PIN_CONFIG_END; + group->map = *map; + + /* Add to group list */ + mutex_lock(&iod->mutex); + list_add_tail(&group->node, &iod->groups); + iod->ngroups++; + mutex_unlock(&iod->mutex); + + (*map)->type = PIN_MAP_TYPE_CONFIGS_GROUP; + (*map)->data.configs.group_or_pin = np->name; + (*map)->data.configs.configs = &group->config; + (*map)->data.configs.num_configs = 1; + *num_maps = 1; + + return 0; + +free_group: + devm_kfree(dev, group); +free_map: + devm_kfree(dev, *map); + return ret; +} + +/** + * ti_iodelay_dt_free_map() - Free map and resource alloted as per the map + * @pctldev: pinctrl device representing IODelay device + * @map: Map allocated by ti_iodelay_dt_node_to_map + * @num_maps: Num maps (1) + * + * Removes the group associated with the map and frees all resources allocated + * for the group. + */ +static void ti_iodelay_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ + struct ti_iodelay_device *iod; + struct device *dev; + struct ti_iodelay_pingroup *group; + bool found = false; + + if (!map) + return; + + iod = pinctrl_dev_get_drvdata(pctldev); + if (!iod) + return; + dev = iod->dev; + + mutex_lock(&iod->mutex); + list_for_each_entry(group, &iod->groups, node) { + if (group->map == map) { + found = true; + list_del(&group->node); + iod->ngroups--; + break; + } + } + mutex_unlock(&iod->mutex); + + /* If some freaky pinconf framework bug... */ + if (!found) + return; + + devm_kfree(dev, group->vals); + devm_kfree(dev, group); + devm_kfree(dev, map); +} + +/** + * ti_iodelay_pinctrl_get_groups_count() - Get number of groups registered + * @pctldev: pinctrl device representing IODelay device + * + * Return: number of groups mapped on the IODelay + */ +static int ti_iodelay_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct ti_iodelay_device *iod; + struct device *dev; + + iod = pinctrl_dev_get_drvdata(pctldev); + dev = iod->dev; + + return iod->ngroups; +} + +/** + * ti_iodelay_pinctrl_get_group_name() - Get the group name + * @pctldev: pinctrl device representing IODelay device + * @gselector: group selector + * + * Return: name of the Group given a valid gselector, else NULL. + */ +static const char *ti_iodelay_pinctrl_get_group_name(struct pinctrl_dev + *pctldev, + unsigned gselector) +{ + struct ti_iodelay_device *iod; + struct device *dev; + struct ti_iodelay_pingroup *group; + + iod = pinctrl_dev_get_drvdata(pctldev); + dev = iod->dev; + + group = ti_iodelay_get_group(iod, gselector); + if (!group) + return NULL; + + return group->name; +} + +/** + * ti_iodelay_pinctrl_get_group_pins() - get group pins + * @pctldev: pinctrl device representing IODelay device + * @gselector: Group selector + * @pins: pointer to the pins + * @npins: number of pins + * + * Dummy implementation since we do not track pins, we track configurations + * Forced by pinctrl's pinctrl_check_ops() + * + * Return: -EINVAL + */ +static int ti_iodelay_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned gselector, + const unsigned **pins, + unsigned *npins) +{ + /* Dummy implementation - we dont do pin mux */ + return -EINVAL; +} + +/** + * ti_iodelay_pinconf_group_get() - Get the group configuration + * @pctldev: pinctrl device representing IODelay device + * @gselector: Group selector + * @config: configuration returned + * + * Return: The configuration if the group is valid, else returns -EINVAL + */ +static int ti_iodelay_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned gselector, + unsigned long *config) +{ + struct ti_iodelay_device *iod; + struct device *dev; + struct ti_iodelay_pingroup *group; + + iod = pinctrl_dev_get_drvdata(pctldev); + dev = iod->dev; + group = ti_iodelay_get_group(iod, gselector); + + if (!group) + return -EINVAL; + + *config = group->config; + return 0; +} + +/** + * ti_iodelay_pinconf_group_set() - Configure the groups of pins + * @pctldev: pinctrl device representing IODelay device + * @gselector: Group selector + * @configs: Configurations + * @num_configs: Number of configurations + * + * Return: 0 if all went fine, else appropriate error value. + */ +static int ti_iodelay_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned gselector, + unsigned long *configs, + unsigned num_configs) +{ + struct ti_iodelay_device *iod; + struct device *dev; + struct ti_iodelay_pingroup *group; + int i; + + iod = pinctrl_dev_get_drvdata(pctldev); + dev = iod->dev; + group = ti_iodelay_get_group(iod, gselector); + + if (num_configs != 1) { + dev_err(dev, "Unsupported number of configurations %d\n", + num_configs); + return -EINVAL; + } + + if (*configs != PIN_CONFIG_END) { + dev_err(dev, "Unsupported configuration\n"); + return -EINVAL; + } + + for (i = 0; i < group->nvals; i++) { + if (ti_iodelay_pinconf_set(iod, &group->vals[i])) + return -ENOTSUPP; + } + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +/** + * ti_iodelay_pinconf_group_dbg_show() - show the group information + * @pctldev: Show the group information + * @s: Sequence file + * @gselector: group selector + * + * Provide the configuration information of the selected group + */ +static void ti_iodelay_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned gselector) +{ + struct ti_iodelay_device *iod; + struct device *dev; + struct ti_iodelay_pingroup *group; + int i; + + iod = pinctrl_dev_get_drvdata(pctldev); + dev = iod->dev; + group = ti_iodelay_get_group(iod, gselector); + if (!group) + return; + + for (i = 0; i < group->nvals; i++) { + struct ti_iodelay_conf_vals *val; + u32 reg = 0; + + val = &group->vals[i]; + regmap_read(iod->regmap, val->offset, ®), + seq_printf(s, "\n\t0x%08x = 0x%08x (%3d, %3d)", + val->offset, reg, val->a_delay, val->g_delay); + } +} +#endif + +static struct pinctrl_ops ti_iodelay_pinctrl_ops = { + .dt_node_to_map = ti_iodelay_dt_node_to_map, + .dt_free_map = ti_iodelay_dt_free_map, + .get_groups_count = ti_iodelay_pinctrl_get_groups_count, + .get_group_name = ti_iodelay_pinctrl_get_group_name, + .get_group_pins = ti_iodelay_pinctrl_get_group_pins, +}; + +static struct pinconf_ops ti_iodelay_pinctrl_pinconf_ops = { + .pin_config_group_get = ti_iodelay_pinconf_group_get, + .pin_config_group_set = ti_iodelay_pinconf_group_set, +#ifdef CONFIG_DEBUG_FS + .pin_config_group_dbg_show = ti_iodelay_pinconf_group_dbg_show, +#endif +}; + +/** + * ti_iodelay_alloc_pins() - Allocate structures needed for pins for IOdelay + * @dev: device pointer + * @iod: IODelay device + * @base_phy: Base Physical Address + * + * Return: 0 if all went fine, else appropriate error value. + */ +static int ti_iodelay_alloc_pins(struct device *dev, + struct ti_iodelay_device *iod, u32 base_phy) +{ + const struct ti_iodelay_reg_data *r = iod->reg_data; + struct pinctrl_pin_desc *pin; + struct ti_iodelay_pin_name *pn; + u32 phy_reg; + int nr_pins, i; + + nr_pins = (r->regmap_config->max_register - r->reg_start_offset) / 4; + + dev_dbg(dev, "Allocating %i pins\n", nr_pins); + + iod->pa = devm_kzalloc(dev, sizeof(*iod->pa) * nr_pins, GFP_KERNEL); + if (!iod->pa) + return -ENOMEM; + + iod->names = + devm_kzalloc(dev, sizeof(struct ti_iodelay_pin_name) * nr_pins, + GFP_KERNEL); + if (!iod->names) + return -ENOMEM; + + iod->desc.pins = iod->pa; + iod->desc.npins = nr_pins; + + phy_reg = r->reg_start_offset + base_phy; + pn = iod->names; + for (i = 0; i < nr_pins; i++, pn++, phy_reg += 4) { + pin = &iod->pa[i]; + sprintf(pn->name, "%x.%d", phy_reg, i); + pin->number = i; + pin->name = pn->name; + } + + return 0; +} + +/** + * ti_iodelay_probe() - Standard probe + * @pdev: platform device + * + * Return: 0 if all went fine, else appropriate error value. + */ +static int ti_iodelay_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = of_node_get(dev->of_node); + const struct of_device_id *match; + struct resource *res; + struct ti_iodelay_device *iod; + int ret = 0; + + if (!np) { + ret = -EINVAL; + dev_err(dev, "No OF node\n"); + goto exit_out; + } + + match = of_match_device(ti_iodelay_of_match, dev); + if (!match) { + ret = -EINVAL; + dev_err(dev, "No DATA match\n"); + goto exit_out; + } + + iod = devm_kzalloc(dev, sizeof(*iod), GFP_KERNEL); + if (!iod) { + ret = -ENOMEM; + goto exit_out; + } + iod->dev = dev; + iod->reg_data = match->data; + + /* So far We can assume there is only 1 bank of registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "Missing MEM resource\n"); + ret = -ENODEV; + goto exit_out; + } + + iod->reg_base = devm_ioremap_resource(dev, res); + if (IS_ERR(iod->reg_base)) { + ret = PTR_ERR(iod->reg_base); + goto exit_out; + } + + iod->regmap = devm_regmap_init_mmio(dev, iod->reg_base, + iod->reg_data->regmap_config); + if (IS_ERR(iod->regmap)) { + dev_err(dev, "Regmap MMIO init failed.\n"); + ret = PTR_ERR(iod->regmap); + goto exit_out; + } + + if (ti_iodelay_pinconf_init_dev(iod)) + goto exit_out; + + ret = ti_iodelay_alloc_pins(dev, iod, res->start); + if (ret) + goto exit_out; + + INIT_LIST_HEAD(&iod->groups); + mutex_init(&iod->mutex); + + iod->desc.pctlops = &ti_iodelay_pinctrl_ops; + /* no pinmux ops - we are pinconf */ + iod->desc.confops = &ti_iodelay_pinctrl_pinconf_ops; + iod->desc.name = dev_name(dev); + iod->desc.owner = THIS_MODULE; + + iod->pctl = pinctrl_register(&iod->desc, dev, iod); + if (!iod->pctl) { + dev_err(dev, "Failed to register pinctrl\n"); + ret = -ENODEV; + goto exit_out; + } + + platform_set_drvdata(pdev, iod); + +exit_out: + of_node_put(np); + return ret; +} + +/** + * ti_iodelay_remove() - standard remove + * @pdev: platform device + * + * Return: 0 if all went fine, else appropriate error value. + */ +static int ti_iodelay_remove(struct platform_device *pdev) +{ + struct ti_iodelay_device *iod = platform_get_drvdata(pdev); + + if (!iod) + return 0; + if (iod->pctl) + pinctrl_unregister(iod->pctl); + + ti_iodelay_pinconf_deinit_dev(iod); + + /* Expect other allocations to be freed by devm */ + + return 0; +} + +static struct regmap_config dra7_iodelay_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xD1C, +}; + +static struct ti_iodelay_reg_data dra7_iodelay_data = { + .signature_mask = 0x0003F000, + .signature_value = 0x29, + .lock_mask = 0x00000400, + .lock_val = 1, + .unlock_val = 0, + .binary_data_coarse_mask = 0x000003E0, + .binary_data_fine_mask = 0x0000001F, + + .reg_refclk_offset = 0x14, + .refclk_period_mask = 0xFFFF, + + .reg_coarse_offset = 0x18, + .coarse_delay_count_mask = 0xFFFF0000, + .coarse_ref_count_mask = 0x0000FFFF, + + .reg_fine_offset = 0x1C, + .fine_delay_count_mask = 0xFFFF0000, + .fine_ref_count_mask = 0x0000FFFF, + + .reg_global_lock_offset = 0x2C, + .global_lock_mask = 0x0000FFFF, + .global_unlock_val = 0x0000AAAA, + .global_lock_val = 0x0000AAAB, + + .reg_start_offset = 0x30, + .regmap_config = &dra7_iodelay_regmap_config, +}; + +static const struct of_device_id ti_iodelay_of_match[] = { + {.compatible = "ti,dra7-iodelay", .data = &dra7_iodelay_data}, + { /* Hopefully no more.. */ }, +}; +MODULE_DEVICE_TABLE(of, ti_iodelay_of_match); + +static struct platform_driver ti_iodelay_driver = { + .probe = ti_iodelay_probe, + .remove = ti_iodelay_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = ti_iodelay_of_match, + }, +}; +module_platform_driver(ti_iodelay_driver); + +MODULE_AUTHOR("Texas Instruments, Inc."); +MODULE_DESCRIPTION("Pinconf driver for TI's IO Delay module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index acd4a15..8e2f764 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -513,5 +513,6 @@ config AXP20X_POWER endif # POWER_SUPPLY +source "drivers/power/pwrseq/Kconfig" source "drivers/power/reset/Kconfig" source "drivers/power/avs/Kconfig" diff --git a/drivers/power/Makefile b/drivers/power/Makefile index e46b75d..1b2a146 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -72,5 +72,6 @@ obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o obj-$(CONFIG_POWER_RESET) += reset/ +obj-$(CONFIG_POWER_SEQUENCE) += pwrseq/ obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o obj-$(CONFIG_AXP288_CHARGER) += axp288_charger.o diff --git b/drivers/power/pwrseq/Kconfig b/drivers/power/pwrseq/Kconfig new file mode 100644 index 0000000..dff5e35 --- /dev/null +++ b/drivers/power/pwrseq/Kconfig @@ -0,0 +1,45 @@ +# +# Power Sequence library +# + +config POWER_SEQUENCE + bool + +menu "Power Sequence Support" + +config PWRSEQ_GENERIC + bool "Generic power sequence control" + depends on OF + select POWER_SEQUENCE + help + It is used for drivers which needs to do power sequence + (eg, turn on clock, toggle reset gpio) before the related + devices can be found by hardware. This generic one can be + used for common power sequence control. + +config PWRSEQ_GENERIC_INSTANCE_NUMBER + int "Number of Generic Power Sequence Instance" + depends on PWRSEQ_GENERIC + range 1 10 + default 2 + help + Usually, there are not so many devices needs power sequence, we set two + as default value. + +config PWRSEQ_SAMPLE + bool "sample power sequence control using compatible string" + depends on OF + select POWER_SEQUENCE + help + It is a sample library which implements power sequence for device id, + it is an example purpose. + +config PWRSEQ_SAMPLE_INSTANCE_NUMBER + int "Number of Sample Power Sequence Instance" + depends on PWRSEQ_SAMPLE + range 1 5 + default 1 + help + Usually, this file is special for certain device, so the default for this number + is 1. +endmenu diff --git b/drivers/power/pwrseq/Makefile b/drivers/power/pwrseq/Makefile new file mode 100644 index 0000000..62f3cbf --- /dev/null +++ b/drivers/power/pwrseq/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_POWER_SEQUENCE) += core.o +obj-$(CONFIG_PWRSEQ_GENERIC) += pwrseq_generic.o +obj-$(CONFIG_PWRSEQ_SAMPLE) += pwrseq_compatible_sample.o diff --git b/drivers/power/pwrseq/core.c b/drivers/power/pwrseq/core.c new file mode 100644 index 0000000..6beae20 --- /dev/null +++ b/drivers/power/pwrseq/core.c @@ -0,0 +1,190 @@ +/* + * core.c power sequence core file + * + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Author: Peter Chen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(pwrseq_list_mutex); +static LIST_HEAD(pwrseq_list); + +int pwrseq_get(struct device_node *np, struct pwrseq *p) +{ + if (p && p->get) + return p->get(np, p); + + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(pwrseq_get); + +int pwrseq_on(struct pwrseq *p) +{ + if (p && p->on) + return p->on(p); + + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(pwrseq_on); + +void pwrseq_off(struct pwrseq *p) +{ + if (p && p->off) + p->off(p); +} +EXPORT_SYMBOL_GPL(pwrseq_off); + +void pwrseq_put(struct pwrseq *p) +{ + if (p && p->put) + p->put(p); +} +EXPORT_SYMBOL_GPL(pwrseq_put); + +void pwrseq_free(struct pwrseq *p) +{ + if (p && p->free) + p->free(p); +} +EXPORT_SYMBOL_GPL(pwrseq_free); + +int pwrseq_suspend(struct pwrseq *p) +{ + if (p && p->suspend) + return p->suspend(p); + + return 0; +} +EXPORT_SYMBOL_GPL(pwrseq_suspend); + +int pwrseq_resume(struct pwrseq *p) +{ + if (p && p->resume) + return p->resume(p); + + return 0; +} +EXPORT_SYMBOL_GPL(pwrseq_resume); + +void pwrseq_register(struct pwrseq *pwrseq) +{ + mutex_lock(&pwrseq_list_mutex); + list_add(&pwrseq->node, &pwrseq_list); + mutex_unlock(&pwrseq_list_mutex); +} +EXPORT_SYMBOL_GPL(pwrseq_register); + +static struct pwrseq *pwrseq_find_available_instance(struct device_node *np) +{ + struct pwrseq *pwrseq; + + list_for_each_entry(pwrseq, &pwrseq_list, node) { + if (pwrseq->used) + continue; + + /* compare compatible string for pwrseq node */ + if (of_match_node(pwrseq->pwrseq_of_match_table, np)) { + pwrseq->used = true; + return pwrseq; + } + + /* return generic pwrseq instance */ + if (!strcmp(pwrseq->pwrseq_of_match_table->compatible, + "generic")) { + pr_debug("using generic pwrseq instance for %s\n", + np->full_name); + pwrseq->used = true; + return pwrseq; + } + } + pr_warn("Can't find any pwrseq instances for %s\n", np->full_name); + + return NULL; +} + +struct pwrseq *of_pwrseq_on(struct device_node *np) +{ + struct pwrseq *pwrseq; + int ret; + + pwrseq = pwrseq_find_available_instance(np); + if (!pwrseq) + return ERR_PTR(-ENONET); + + ret = pwrseq_get(np, pwrseq); + if (ret) + goto pwr_free; + + ret = pwrseq_on(pwrseq); + if (ret) + goto pwr_put; + + return pwrseq; + +pwr_put: + pwrseq_put(pwrseq); +pwr_free: + pwrseq_free(pwrseq); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(of_pwrseq_on); + +void of_pwrseq_off(struct pwrseq *pwrseq) +{ + pwrseq_off(pwrseq); + pwrseq_put(pwrseq); + pwrseq_free(pwrseq); +} +EXPORT_SYMBOL_GPL(of_pwrseq_off); + +int of_pwrseq_on_list(struct device_node *np, struct list_head *head) +{ + struct pwrseq *pwrseq; + struct pwrseq_list_per_dev *pwrseq_list_node; + + pwrseq = of_pwrseq_on(np); + if (IS_ERR(pwrseq)) + return PTR_ERR(pwrseq); + + pwrseq_list_node = kzalloc(sizeof(*pwrseq_list_node), GFP_KERNEL); + if (!pwrseq_list_node) { + of_pwrseq_off(pwrseq); + return -ENOMEM; + } + pwrseq_list_node->pwrseq = pwrseq; + list_add(&pwrseq_list_node->list, head); + + return 0; +} +EXPORT_SYMBOL_GPL(of_pwrseq_on_list); + +void of_pwrseq_off_list(struct list_head *head) +{ + struct pwrseq *pwrseq; + struct pwrseq_list_per_dev *pwrseq_list_node, *tmp_node; + + list_for_each_entry_safe(pwrseq_list_node, tmp_node, head, list) { + pwrseq = pwrseq_list_node->pwrseq; + of_pwrseq_off(pwrseq); + list_del(&pwrseq_list_node->list); + kfree(pwrseq_list_node); + } +} +EXPORT_SYMBOL_GPL(of_pwrseq_off_list); diff --git b/drivers/power/pwrseq/pwrseq_compatible_sample.c b/drivers/power/pwrseq/pwrseq_compatible_sample.c new file mode 100644 index 0000000..d6bcc6d --- /dev/null +++ b/drivers/power/pwrseq/pwrseq_compatible_sample.c @@ -0,0 +1,178 @@ +/* + * pwrseq_compatible_sample.c Sample power sequence handling for compatible + * + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Author: Peter Chen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct pwrseq_sample { + struct pwrseq pwrseq; + struct gpio_desc *gpiod_reset; + struct clk *clks[PWRSEQ_MAX_CLKS]; + u32 duration_us; +}; + +#define to_generic_pwrseq(p) container_of(p, struct pwrseq_sample, pwrseq) + +static void pwrseq_sample_free(struct pwrseq *pwrseq) +{ + pwrseq->used = false; +} + +static void pwrseq_sample_put(struct pwrseq *pwrseq) +{ + struct pwrseq_sample *pwrseq_sam = to_generic_pwrseq(pwrseq); + int clk; + + if (pwrseq_sam->gpiod_reset) + gpiod_put(pwrseq_sam->gpiod_reset); + + for (clk = 0; clk < PWRSEQ_MAX_CLKS; clk++) + clk_put(pwrseq_sam->clks[clk]); +} + +static void pwrseq_sample_off(struct pwrseq *pwrseq) +{ + struct pwrseq_sample *pwrseq_sam = to_generic_pwrseq(pwrseq); + int clk; + + for (clk = PWRSEQ_MAX_CLKS - 1; clk >= 0; clk--) + clk_disable_unprepare(pwrseq_sam->clks[clk]); +} + +static int pwrseq_sample_on(struct pwrseq *pwrseq) +{ + struct pwrseq_sample *pwrseq_sam = to_generic_pwrseq(pwrseq); + int clk, ret = 0; + struct gpio_desc *gpiod_reset = pwrseq_sam->gpiod_reset; + + for (clk = 0; clk < PWRSEQ_MAX_CLKS && pwrseq_sam->clks[clk]; clk++) { + ret = clk_prepare_enable(pwrseq_sam->clks[clk]); + if (ret) { + pr_err("Can't enable clock, ret=%d\n", ret); + goto err_disable_clks; + } + } + + if (gpiod_reset) { + u32 duration_us = pwrseq_sam->duration_us; + + if (duration_us <= 10) + udelay(10); + else + usleep_range(duration_us, duration_us + 100); + gpiod_set_value(gpiod_reset, 0); + } + + return ret; + +err_disable_clks: + while (--clk >= 0) + clk_disable_unprepare(pwrseq_sam->clks[clk]); + + return ret; +} + +static int pwrseq_sample_get(struct device_node *np, struct pwrseq *pwrseq) +{ + struct pwrseq_sample *pwrseq_sam = to_generic_pwrseq(pwrseq); + enum of_gpio_flags flags; + int reset_gpio, clk, ret = 0; + + for (clk = 0; clk < PWRSEQ_MAX_CLKS; clk++) { + pwrseq_sam->clks[clk] = of_clk_get(np, clk); + if (IS_ERR(pwrseq_sam->clks[clk])) { + ret = PTR_ERR(pwrseq_sam->clks[clk]); + if (ret != -ENOENT) + goto err_put_clks; + pwrseq_sam->clks[clk] = NULL; + break; + } + } + + reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &flags); + if (gpio_is_valid(reset_gpio)) { + unsigned long gpio_flags; + + if (flags & OF_GPIO_ACTIVE_LOW) + gpio_flags = GPIOF_ACTIVE_LOW | GPIOF_OUT_INIT_LOW; + else + gpio_flags = GPIOF_OUT_INIT_HIGH; + + ret = gpio_request_one(reset_gpio, gpio_flags, + "pwrseq-reset-gpios"); + if (ret) + goto err_put_clks; + + pwrseq_sam->gpiod_reset = gpio_to_desc(reset_gpio); + of_property_read_u32(np, "reset-duration-us", + &pwrseq_sam->duration_us); + } else { + if (reset_gpio == -ENOENT) + return 0; + + ret = reset_gpio; + pr_err("Failed to get reset gpio on %s, err = %d\n", + np->full_name, reset_gpio); + goto err_put_clks; + } + + return ret; + +err_put_clks: + while (--clk >= 0) + clk_put(pwrseq_sam->clks[clk]); + return ret; +} + +static const struct of_device_id sample_id_table[] = { + { .compatible = "usb5e3,608",}, + { .compatible = "usbb95,1708",}, + { /* sentinel */ } +}; + +static int __init pwrseq_compatible_sample_register(void) +{ + struct pwrseq_sample *pwrseq_sam; + int i; + + for (i = 0; i < CONFIG_PWRSEQ_SAMPLE_INSTANCE_NUMBER; i++) { + pwrseq_sam = kzalloc(sizeof(*pwrseq_sam), GFP_KERNEL); + if (!pwrseq_sam) + return -ENOMEM; + + pwrseq_sam->pwrseq.pwrseq_of_match_table = sample_id_table; + pwrseq_sam->pwrseq.get = pwrseq_sample_get; + pwrseq_sam->pwrseq.on = pwrseq_sample_on; + pwrseq_sam->pwrseq.off = pwrseq_sample_off; + pwrseq_sam->pwrseq.put = pwrseq_sample_put; + pwrseq_sam->pwrseq.free = pwrseq_sample_free; + + pwrseq_register(&pwrseq_sam->pwrseq); + } + + return 0; +} +postcore_initcall(pwrseq_compatible_sample_register) diff --git b/drivers/power/pwrseq/pwrseq_generic.c b/drivers/power/pwrseq/pwrseq_generic.c new file mode 100644 index 0000000..bcd16c3 --- /dev/null +++ b/drivers/power/pwrseq/pwrseq_generic.c @@ -0,0 +1,177 @@ +/* + * pwrseq_generic.c Generic power sequence handling + * + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Author: Peter Chen + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct pwrseq_generic { + struct pwrseq pwrseq; + struct gpio_desc *gpiod_reset; + struct clk *clks[PWRSEQ_MAX_CLKS]; + u32 duration_us; +}; + +#define to_generic_pwrseq(p) container_of(p, struct pwrseq_generic, pwrseq) + +static void pwrseq_generic_free(struct pwrseq *pwrseq) +{ + pwrseq->used = false; +} + +static void pwrseq_generic_put(struct pwrseq *pwrseq) +{ + struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq); + int clk; + + if (pwrseq_gen->gpiod_reset) + gpiod_put(pwrseq_gen->gpiod_reset); + + for (clk = 0; clk < PWRSEQ_MAX_CLKS; clk++) + clk_put(pwrseq_gen->clks[clk]); +} + +static void pwrseq_generic_off(struct pwrseq *pwrseq) +{ + struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq); + int clk; + + for (clk = PWRSEQ_MAX_CLKS - 1; clk >= 0; clk--) + clk_disable_unprepare(pwrseq_gen->clks[clk]); +} + +static int pwrseq_generic_on(struct pwrseq *pwrseq) +{ + struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq); + int clk, ret = 0; + struct gpio_desc *gpiod_reset = pwrseq_gen->gpiod_reset; + + for (clk = 0; clk < PWRSEQ_MAX_CLKS && pwrseq_gen->clks[clk]; clk++) { + ret = clk_prepare_enable(pwrseq_gen->clks[clk]); + if (ret) { + pr_err("Can't enable clock, ret=%d\n", ret); + goto err_disable_clks; + } + } + + if (gpiod_reset) { + u32 duration_us = pwrseq_gen->duration_us; + + if (duration_us <= 10) + udelay(10); + else + usleep_range(duration_us, duration_us + 100); + gpiod_set_value(gpiod_reset, 0); + } + + return ret; + +err_disable_clks: + while (--clk >= 0) + clk_disable_unprepare(pwrseq_gen->clks[clk]); + + return ret; +} + +static int pwrseq_generic_get(struct device_node *np, struct pwrseq *pwrseq) +{ + struct pwrseq_generic *pwrseq_gen = to_generic_pwrseq(pwrseq); + enum of_gpio_flags flags; + int reset_gpio, clk, ret = 0; + + for (clk = 0; clk < PWRSEQ_MAX_CLKS; clk++) { + pwrseq_gen->clks[clk] = of_clk_get(np, clk); + if (IS_ERR(pwrseq_gen->clks[clk])) { + ret = PTR_ERR(pwrseq_gen->clks[clk]); + if (ret != -ENOENT) + goto err_put_clks; + pwrseq_gen->clks[clk] = NULL; + break; + } + } + + reset_gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &flags); + if (gpio_is_valid(reset_gpio)) { + unsigned long gpio_flags; + + if (flags & OF_GPIO_ACTIVE_LOW) + gpio_flags = GPIOF_ACTIVE_LOW | GPIOF_OUT_INIT_LOW; + else + gpio_flags = GPIOF_OUT_INIT_HIGH; + + ret = gpio_request_one(reset_gpio, gpio_flags, + "pwrseq-reset-gpios"); + if (ret) + goto err_put_clks; + + pwrseq_gen->gpiod_reset = gpio_to_desc(reset_gpio); + of_property_read_u32(np, "reset-duration-us", + &pwrseq_gen->duration_us); + } else { + if (reset_gpio == -ENOENT) + return 0; + + ret = reset_gpio; + pr_err("Failed to get reset gpio on %s, err = %d\n", + np->full_name, reset_gpio); + goto err_put_clks; + } + + return ret; + +err_put_clks: + while (--clk >= 0) + clk_put(pwrseq_gen->clks[clk]); + return ret; +} + +static const struct of_device_id generic_id_table[] = { + { .compatible = "generic",}, + { /* sentinel */ } +}; + +static int __init pwrseq_generic_register(void) +{ + struct pwrseq_generic *pwrseq_gen; + int i; + + for (i = 0; i < CONFIG_PWRSEQ_GENERIC_INSTANCE_NUMBER; i++) { + pwrseq_gen = kzalloc(sizeof(*pwrseq_gen), GFP_KERNEL); + if (!pwrseq_gen) + return -ENOMEM; + + pwrseq_gen->pwrseq.pwrseq_of_match_table = generic_id_table; + pwrseq_gen->pwrseq.get = pwrseq_generic_get; + pwrseq_gen->pwrseq.on = pwrseq_generic_on; + pwrseq_gen->pwrseq.off = pwrseq_generic_off; + pwrseq_gen->pwrseq.put = pwrseq_generic_put; + pwrseq_gen->pwrseq.free = pwrseq_generic_free; + + pwrseq_register(&pwrseq_gen->pwrseq); + } + + return 0; +} +postcore_initcall(pwrseq_generic_register) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 2e05046..3c6dfad 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -751,11 +751,11 @@ static int spidev_probe(struct spi_device *spi) * compatible string, it is a Linux implementation thing * rather than a description of the hardware. */ - if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) { - dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n"); - WARN_ON(spi->dev.of_node && - !of_match_device(spidev_dt_ids, &spi->dev)); - } +// if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) { +// dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n"); +// WARN_ON(spi->dev.of_node && +// !of_match_device(spidev_dt_ids, &spi->dev)); +// } spidev_probe_acpi(spi); diff --git a/drivers/staging/iio/accel/sca3000.h b/drivers/staging/iio/accel/sca3000.h index 9c8a958..4dcc857 100644 --- a/drivers/staging/iio/accel/sca3000.h +++ b/drivers/staging/iio/accel/sca3000.h @@ -113,6 +113,7 @@ #define SCA3000_OUT_CTRL_BUF_X_EN 0x10 #define SCA3000_OUT_CTRL_BUF_Y_EN 0x08 #define SCA3000_OUT_CTRL_BUF_Z_EN 0x04 +#define SCA3000_OUT_CTRL_BUF_DIV_MASK 0x03 #define SCA3000_OUT_CTRL_BUF_DIV_4 0x02 #define SCA3000_OUT_CTRL_BUF_DIV_2 0x01 diff --git a/drivers/staging/iio/accel/sca3000_core.c b/drivers/staging/iio/accel/sca3000_core.c index b5625f5..564b36d 100644 --- a/drivers/staging/iio/accel/sca3000_core.c +++ b/drivers/staging/iio/accel/sca3000_core.c @@ -402,6 +402,7 @@ static const struct iio_event_spec sca3000_event = { .channel2 = mod, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ .address = index, \ .scan_index = index, \ .scan_type = { \ @@ -412,7 +413,7 @@ static const struct iio_event_spec sca3000_event = { }, \ .event_spec = &sca3000_event, \ .num_event_specs = 1, \ - } + } static const struct iio_chan_spec sca3000_channels[] = { SCA3000_CHAN(0, IIO_MOD_X), @@ -443,6 +444,99 @@ static u8 sca3000_addresses[3][3] = { SCA3000_MD_CTRL_OR_Z}, }; +/** + * __sca3000_get_base_freq() obtain mode specific base frequency + * + * lock must be held + **/ +static inline int __sca3000_get_base_freq(struct sca3000_state *st, + const struct sca3000_chip_info *info, + int *base_freq) +{ + int ret; + + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); + if (ret) + goto error_ret; + switch (0x03 & st->rx[0]) { + case SCA3000_MEAS_MODE_NORMAL: + *base_freq = info->measurement_mode_freq; + break; + case SCA3000_MEAS_MODE_OP_1: + *base_freq = info->option_mode_1_freq; + break; + case SCA3000_MEAS_MODE_OP_2: + *base_freq = info->option_mode_2_freq; + break; + default: + ret = -EINVAL; + } +error_ret: + return ret; +} + +/** + * read_raw handler for IIO_CHAN_INFO_SAMP_FREQ + * + * lock must be held + **/ +static int read_raw_samp_freq(struct sca3000_state *st, int *val) +{ + int ret; + + ret = __sca3000_get_base_freq(st, st->info, val); + if (ret) + return ret; + + ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL); + if (ret < 0) + return ret; + + if (*val > 0) { + ret &= SCA3000_OUT_CTRL_BUF_DIV_MASK; + switch (ret) { + case SCA3000_OUT_CTRL_BUF_DIV_2: + *val /= 2; + break; + case SCA3000_OUT_CTRL_BUF_DIV_4: + *val /= 4; + break; + } + } + + return 0; +} + +/** + * write_raw handler for IIO_CHAN_INFO_SAMP_FREQ + * + * lock must be held + **/ +static int write_raw_samp_freq(struct sca3000_state *st, int val) +{ + int ret, base_freq, ctrlval; + + ret = __sca3000_get_base_freq(st, st->info, &base_freq); + if (ret) + return ret; + + ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL); + if (ret < 0) + return ret; + + ctrlval = ret & ~SCA3000_OUT_CTRL_BUF_DIV_MASK; + + if (val == base_freq / 2) + ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_2; + if (val == base_freq / 4) + ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_4; + else if (val != base_freq) + return -EINVAL; + + return sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL, + ctrlval); +} + static int sca3000_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, @@ -495,9 +589,36 @@ static int sca3000_read_raw(struct iio_dev *indio_dev, *val = -214; *val2 = 600000; return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + mutex_lock(&st->lock); + ret = read_raw_samp_freq(st, val); + mutex_unlock(&st->lock); + return ret ? ret : IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int sca3000_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct sca3000_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (val2) + return -EINVAL; + mutex_lock(&st->lock); + ret = write_raw_samp_freq(st, val); + mutex_unlock(&st->lock); + return ret; default: return -EINVAL; } + + return ret; } /** @@ -548,133 +669,12 @@ error_ret: return ret; } -/** - * __sca3000_get_base_freq() obtain mode specific base frequency - * - * lock must be held - **/ -static inline int __sca3000_get_base_freq(struct sca3000_state *st, - const struct sca3000_chip_info *info, - int *base_freq) -{ - int ret; - - ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); - if (ret) - goto error_ret; - switch (0x03 & st->rx[0]) { - case SCA3000_MEAS_MODE_NORMAL: - *base_freq = info->measurement_mode_freq; - break; - case SCA3000_MEAS_MODE_OP_1: - *base_freq = info->option_mode_1_freq; - break; - case SCA3000_MEAS_MODE_OP_2: - *base_freq = info->option_mode_2_freq; - break; - } -error_ret: - return ret; -} - -/** - * sca3000_read_frequency() sysfs interface to get the current frequency - **/ -static ssize_t sca3000_read_frequency(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct sca3000_state *st = iio_priv(indio_dev); - int ret, len = 0, base_freq = 0, val; - - mutex_lock(&st->lock); - ret = __sca3000_get_base_freq(st, st->info, &base_freq); - if (ret) - goto error_ret_mut; - ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL); - mutex_unlock(&st->lock); - if (ret < 0) - goto error_ret; - val = ret; - if (base_freq > 0) - switch (val & 0x03) { - case 0x00: - case 0x03: - len = sprintf(buf, "%d\n", base_freq); - break; - case 0x01: - len = sprintf(buf, "%d\n", base_freq / 2); - break; - case 0x02: - len = sprintf(buf, "%d\n", base_freq / 4); - break; - } - - return len; -error_ret_mut: - mutex_unlock(&st->lock); -error_ret: - return ret; -} - -/** - * sca3000_set_frequency() sysfs interface to set the current frequency - **/ -static ssize_t sca3000_set_frequency(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct sca3000_state *st = iio_priv(indio_dev); - int ret, base_freq = 0; - int ctrlval; - int val; - - ret = kstrtoint(buf, 10, &val); - if (ret) - return ret; - - mutex_lock(&st->lock); - /* What mode are we in? */ - ret = __sca3000_get_base_freq(st, st->info, &base_freq); - if (ret) - goto error_free_lock; - - ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL); - if (ret < 0) - goto error_free_lock; - ctrlval = ret; - /* clear the bits */ - ctrlval &= ~0x03; - - if (val == base_freq / 2) { - ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_2; - } else if (val == base_freq / 4) { - ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_4; - } else if (val != base_freq) { - ret = -EINVAL; - goto error_free_lock; - } - ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL, - ctrlval); -error_free_lock: - mutex_unlock(&st->lock); - - return ret ? ret : len; -} - /* * Should only really be registered if ring buffer support is compiled in. * Does no harm however and doing it right would add a fair bit of complexity */ static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sca3000_read_av_freq); -static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, - sca3000_read_frequency, - sca3000_set_frequency); - /** * sca3000_read_thresh() - query of a threshold **/ @@ -751,7 +751,6 @@ static struct attribute *sca3000_attributes[] = { &iio_dev_attr_measurement_mode_available.dev_attr.attr, &iio_dev_attr_measurement_mode.dev_attr.attr, &iio_dev_attr_sampling_frequency_available.dev_attr.attr, - &iio_dev_attr_sampling_frequency.dev_attr.attr, NULL, }; @@ -1086,6 +1085,7 @@ error_ret: static const struct iio_info sca3000_info = { .attrs = &sca3000_attribute_group, .read_raw = &sca3000_read_raw, + .write_raw = &sca3000_write_raw, .event_attrs = &sca3000_event_attribute_group, .read_event_value = &sca3000_read_thresh, .write_event_value = &sca3000_write_thresh, diff --git a/drivers/staging/iio/adc/ad7280a.c b/drivers/staging/iio/adc/ad7280a.c index 2177f1d..b460dda 100644 --- a/drivers/staging/iio/adc/ad7280a.c +++ b/drivers/staging/iio/adc/ad7280a.c @@ -478,7 +478,7 @@ static ssize_t ad7280_store_balance_timer(struct device *dev, static struct attribute *ad7280_attributes[AD7280A_MAX_CHAIN * AD7280A_CELLS_PER_DEV * 2 + 1]; -static struct attribute_group ad7280_attrs_group = { +static const struct attribute_group ad7280_attrs_group = { .attrs = ad7280_attributes, }; diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index 24c348d..5eecf1c 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -156,8 +156,7 @@ static const struct iio_chan_spec ad5933_channels[] = { }, }; -static int ad5933_i2c_write(struct i2c_client *client, - u8 reg, u8 len, u8 *data) +static int ad5933_i2c_write(struct i2c_client *client, u8 reg, u8 len, u8 *data) { int ret; @@ -171,8 +170,7 @@ static int ad5933_i2c_write(struct i2c_client *client, return 0; } -static int ad5933_i2c_read(struct i2c_client *client, - u8 reg, u8 len, u8 *data) +static int ad5933_i2c_read(struct i2c_client *client, u8 reg, u8 len, u8 *data) { int ret; @@ -269,7 +267,8 @@ static int ad5933_setup(struct ad5933_state *st) dat = cpu_to_be16(st->settling_cycles); ret = ad5933_i2c_write(st->client, - AD5933_REG_SETTLING_CYCLES, 2, (u8 *)&dat); + AD5933_REG_SETTLING_CYCLES, + 2, (u8 *)&dat); if (ret < 0) return ret; @@ -294,8 +293,8 @@ static void ad5933_calc_out_ranges(struct ad5933_state *st) */ static ssize_t ad5933_show_frequency(struct device *dev, - struct device_attribute *attr, - char *buf) + struct device_attribute *attr, + char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad5933_state *st = iio_priv(indio_dev); @@ -322,9 +321,9 @@ static ssize_t ad5933_show_frequency(struct device *dev, } static ssize_t ad5933_store_frequency(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) + struct device_attribute *attr, + const char *buf, + size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad5933_state *st = iio_priv(indio_dev); @@ -357,8 +356,8 @@ static IIO_DEVICE_ATTR(out_voltage0_freq_increment, S_IRUGO | S_IWUSR, AD5933_REG_FREQ_INC); static ssize_t ad5933_show(struct device *dev, - struct device_attribute *attr, - char *buf) + struct device_attribute *attr, + char *buf)