* [PATCH 0/2] pinctrl: Introduce support for iodelay module in TI SoCs @ 2015-03-04 0:00 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-04 0:00 UTC (permalink / raw) To: Tony Lindgren, Linus Walleij Cc: linux-gpio, linux-kernel, devicetree, linux-omap, linux-arm-kernel, Lokesh Vutla, Nishanth Menon Hi, SoCs such as DRA7 family from Texas Instruments also include a highly configurable hardware block called the IOdelay block. This block allows very specific custom fine tuning for electrical characteristics of IO pins that are necessary for functionality and device lifetime requirements. IODelay module exists on it's own register space with registers allocated for various pins. A set of values (derived from either automated tools OR from device datasheet) is then computed on a per-chip basis(to account for die specific variations) to generate a set of delay parameters that are programmed to one or more registers for the delay path of the io line. This is used in specific instances such as MMC to achieve high-throughput modes, and most pins dont usually need tweaking functionality provided by this driver. Even though this kind of operates similar to a pinctrl model, functionally, it is a characteristics tuning which does not completely fit the standard pinconf model either. In addition to the regular pin muxing modes supported by the pinctrl-single, additional configuration for this block for specific pins may also be mandatory in certain cases. Series based on v4.0-rc1 Nishanth Menon (2): pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration pinctrl: Introduce TI IOdelay configuration driver .../bindings/pinctrl/ti,iodelay-pinctrl.txt | 86 ++ drivers/pinctrl/Kconfig | 11 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-ti-iodelay.c | 963 ++++++++++++++++++++ include/dt-bindings/pinctrl/dra.h | 6 + 5 files changed, 1067 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt create mode 100644 drivers/pinctrl/pinctrl-ti-iodelay.c -- 1.7.9.5 ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 0/2] pinctrl: Introduce support for iodelay module in TI SoCs @ 2015-03-04 0:00 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-04 0:00 UTC (permalink / raw) To: linux-arm-kernel Hi, SoCs such as DRA7 family from Texas Instruments also include a highly configurable hardware block called the IOdelay block. This block allows very specific custom fine tuning for electrical characteristics of IO pins that are necessary for functionality and device lifetime requirements. IODelay module exists on it's own register space with registers allocated for various pins. A set of values (derived from either automated tools OR from device datasheet) is then computed on a per-chip basis(to account for die specific variations) to generate a set of delay parameters that are programmed to one or more registers for the delay path of the io line. This is used in specific instances such as MMC to achieve high-throughput modes, and most pins dont usually need tweaking functionality provided by this driver. Even though this kind of operates similar to a pinctrl model, functionally, it is a characteristics tuning which does not completely fit the standard pinconf model either. In addition to the regular pin muxing modes supported by the pinctrl-single, additional configuration for this block for specific pins may also be mandatory in certain cases. Series based on v4.0-rc1 Nishanth Menon (2): pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration pinctrl: Introduce TI IOdelay configuration driver .../bindings/pinctrl/ti,iodelay-pinctrl.txt | 86 ++ drivers/pinctrl/Kconfig | 11 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-ti-iodelay.c | 963 ++++++++++++++++++++ include/dt-bindings/pinctrl/dra.h | 6 + 5 files changed, 1067 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt create mode 100644 drivers/pinctrl/pinctrl-ti-iodelay.c -- 1.7.9.5 ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 0/2] pinctrl: Introduce support for iodelay module in TI SoCs @ 2015-03-04 0:00 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-04 0:00 UTC (permalink / raw) To: Tony Lindgren, Linus Walleij Cc: linux-gpio, linux-kernel, devicetree, linux-omap, linux-arm-kernel, Lokesh Vutla, Nishanth Menon Hi, SoCs such as DRA7 family from Texas Instruments also include a highly configurable hardware block called the IOdelay block. This block allows very specific custom fine tuning for electrical characteristics of IO pins that are necessary for functionality and device lifetime requirements. IODelay module exists on it's own register space with registers allocated for various pins. A set of values (derived from either automated tools OR from device datasheet) is then computed on a per-chip basis(to account for die specific variations) to generate a set of delay parameters that are programmed to one or more registers for the delay path of the io line. This is used in specific instances such as MMC to achieve high-throughput modes, and most pins dont usually need tweaking functionality provided by this driver. Even though this kind of operates similar to a pinctrl model, functionally, it is a characteristics tuning which does not completely fit the standard pinconf model either. In addition to the regular pin muxing modes supported by the pinctrl-single, additional configuration for this block for specific pins may also be mandatory in certain cases. Series based on v4.0-rc1 Nishanth Menon (2): pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration pinctrl: Introduce TI IOdelay configuration driver .../bindings/pinctrl/ti,iodelay-pinctrl.txt | 86 ++ drivers/pinctrl/Kconfig | 11 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-ti-iodelay.c | 963 ++++++++++++++++++++ include/dt-bindings/pinctrl/dra.h | 6 + 5 files changed, 1067 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt create mode 100644 drivers/pinctrl/pinctrl-ti-iodelay.c -- 1.7.9.5 ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration 2015-03-04 0:00 ` Nishanth Menon (?) @ 2015-03-04 0:00 ` Nishanth Menon -1 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-04 0:00 UTC (permalink / raw) To: Tony Lindgren, Linus Walleij Cc: linux-gpio, linux-kernel, devicetree, linux-omap, linux-arm-kernel, Lokesh Vutla, Nishanth Menon SoCs such as DRA7 family from Texas Instruments also include a highly configurable hardware block called the IOdelay block. This block allows very specific custom fine tuning for electrical characteristics of IO pins. In addition to the regular pin muxing modes supported by the pinctrl-single, additional configuration for this block for specific pins may also be mandatory in certain cases. It is advocated strongly in TI's official documentation considering the existing design of the DRA7 family of processors, which during mux or IODelay reconfiguration, has a potential for a significant glitch which may cause functional impairment to certain hardware. It is hence recommended to do as little of muxing as absolutely necessary without IO isolation (which can only be done in initial stages of bootloader). Even with the above limitation, certain functionality such as MMC may mandate the need of IODelay reconfiguration depending on speed of transfer. Hence, introduce a new binding to facilitate programming the same. Signed-off-by: Nishanth Menon <nm@ti.com> --- .../bindings/pinctrl/ti,iodelay-pinctrl.txt | 86 ++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt diff --git a/Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt new file mode 100644 index 000000000000..e12f4e5a3f25 --- /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>; +}; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-04 0:00 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-04 0:00 UTC (permalink / raw) To: linux-arm-kernel SoCs such as DRA7 family from Texas Instruments also include a highly configurable hardware block called the IOdelay block. This block allows very specific custom fine tuning for electrical characteristics of IO pins. In addition to the regular pin muxing modes supported by the pinctrl-single, additional configuration for this block for specific pins may also be mandatory in certain cases. It is advocated strongly in TI's official documentation considering the existing design of the DRA7 family of processors, which during mux or IODelay reconfiguration, has a potential for a significant glitch which may cause functional impairment to certain hardware. It is hence recommended to do as little of muxing as absolutely necessary without IO isolation (which can only be done in initial stages of bootloader). Even with the above limitation, certain functionality such as MMC may mandate the need of IODelay reconfiguration depending on speed of transfer. Hence, introduce a new binding to facilitate programming the same. Signed-off-by: Nishanth Menon <nm@ti.com> --- .../bindings/pinctrl/ti,iodelay-pinctrl.txt | 86 ++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt diff --git a/Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt new file mode 100644 index 000000000000..e12f4e5a3f25 --- /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 at 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>; +}; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-04 0:00 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-04 0:00 UTC (permalink / raw) To: Tony Lindgren, Linus Walleij Cc: linux-gpio, linux-kernel, devicetree, linux-omap, linux-arm-kernel, Lokesh Vutla, Nishanth Menon SoCs such as DRA7 family from Texas Instruments also include a highly configurable hardware block called the IOdelay block. This block allows very specific custom fine tuning for electrical characteristics of IO pins. In addition to the regular pin muxing modes supported by the pinctrl-single, additional configuration for this block for specific pins may also be mandatory in certain cases. It is advocated strongly in TI's official documentation considering the existing design of the DRA7 family of processors, which during mux or IODelay reconfiguration, has a potential for a significant glitch which may cause functional impairment to certain hardware. It is hence recommended to do as little of muxing as absolutely necessary without IO isolation (which can only be done in initial stages of bootloader). Even with the above limitation, certain functionality such as MMC may mandate the need of IODelay reconfiguration depending on speed of transfer. Hence, introduce a new binding to facilitate programming the same. Signed-off-by: Nishanth Menon <nm@ti.com> --- .../bindings/pinctrl/ti,iodelay-pinctrl.txt | 86 ++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt diff --git a/Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/ti,iodelay-pinctrl.txt new file mode 100644 index 000000000000..e12f4e5a3f25 --- /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>; +}; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration 2015-03-04 0:00 ` Nishanth Menon (?) @ 2015-03-10 10:39 ` Linus Walleij -1 siblings, 0 replies; 74+ messages in thread From: Linus Walleij @ 2015-03-10 10:39 UTC (permalink / raw) To: Nishanth Menon Cc: Tony Lindgren, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > +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 */ > + >; > + }; > +}; But wait. The promise when we merged pinctrl-single was that this driver was to be used when the system had a one-register-per-pin layout and it was easy to do device trees based on that. We were very reluctant to accept that even though we didn't even have the generic pin control bindings in place, the argument being that the driver should know the detailed register layout, it should not be described in the device tree. We eventually caved in and accepted it as an exception. Since this pin controller is not using pinctrl-single it does not enjoy that privilege and you need to explain why this pin controller cannot use the generic bindings like so many other pin controllers have since started to do. ("It is in the same SoC" is not an acceptable argument.) What is wrong with this: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt We can add generic delay bindings to the list, it even seems like a good idea to do so, as it is likely something that will come up in other hardware and will be needed for ACPI etc going forward. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-10 10:39 ` Linus Walleij 0 siblings, 0 replies; 74+ messages in thread From: Linus Walleij @ 2015-03-10 10:39 UTC (permalink / raw) To: linux-arm-kernel On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > +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 */ > + >; > + }; > +}; But wait. The promise when we merged pinctrl-single was that this driver was to be used when the system had a one-register-per-pin layout and it was easy to do device trees based on that. We were very reluctant to accept that even though we didn't even have the generic pin control bindings in place, the argument being that the driver should know the detailed register layout, it should not be described in the device tree. We eventually caved in and accepted it as an exception. Since this pin controller is not using pinctrl-single it does not enjoy that privilege and you need to explain why this pin controller cannot use the generic bindings like so many other pin controllers have since started to do. ("It is in the same SoC" is not an acceptable argument.) What is wrong with this: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt We can add generic delay bindings to the list, it even seems like a good idea to do so, as it is likely something that will come up in other hardware and will be needed for ACPI etc going forward. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-10 10:39 ` Linus Walleij 0 siblings, 0 replies; 74+ messages in thread From: Linus Walleij @ 2015-03-10 10:39 UTC (permalink / raw) To: Nishanth Menon Cc: Tony Lindgren, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > +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 */ > + >; > + }; > +}; But wait. The promise when we merged pinctrl-single was that this driver was to be used when the system had a one-register-per-pin layout and it was easy to do device trees based on that. We were very reluctant to accept that even though we didn't even have the generic pin control bindings in place, the argument being that the driver should know the detailed register layout, it should not be described in the device tree. We eventually caved in and accepted it as an exception. Since this pin controller is not using pinctrl-single it does not enjoy that privilege and you need to explain why this pin controller cannot use the generic bindings like so many other pin controllers have since started to do. ("It is in the same SoC" is not an acceptable argument.) What is wrong with this: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt We can add generic delay bindings to the list, it even seems like a good idea to do so, as it is likely something that will come up in other hardware and will be needed for ACPI etc going forward. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration 2015-03-10 10:39 ` Linus Walleij (?) @ 2015-03-10 15:06 ` Nishanth Menon -1 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-10 15:06 UTC (permalink / raw) To: Linus Walleij Cc: Tony Lindgren, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On 03/10/2015 05:39 AM, Linus Walleij wrote: > On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > >> +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 */ >> + >; >> + }; >> +}; > > But wait. > > The promise when we merged pinctrl-single was that this driver was to be used > when the system had a one-register-per-pin layout and it was easy to do device > trees based on that. > > We were very reluctant to accept that even though we didn't even have the > generic pin control bindings in place, the argument being that the driver > should know the detailed register layout, it should not be described in the > device tree. We eventually caved in and accepted it as an exception. > > Since this pin controller is not using pinctrl-single it does not enjoy that > privilege and you need to explain why this pin controller cannot use the > generic bindings like so many other pin controllers have since started > to do. ("It is in the same SoC" is not an acceptable argument.) > > What is wrong with this: > Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt > > We can add generic delay bindings to the list, it even seems like > a good idea to do so, as it is likely something that will come up in > other hardware and will be needed for ACPI etc going forward. Let me try to explain how the hardware works in this instance - it is a quirk that we had'nt understood as being mandatory until very recently. Apologies on pointing to TRM. Unfortunately, understanding this kind of needs us to understand the hardware a little deeper. http://www.ti.com/lit/ug/spruhz6/spruhz6.pdf 18.5.2 CTRL_MODULE_CORE registers (page 4040) -> mux starts from CTRL_CORE_PAD_GPMC_AD0 (page 4390) This is the basic support needed for DRA7 family of SoC handled by pinctrl-single. a single register for a single pin -> write the mux mode, internal pull, wakeup capability etc. handled today as part of pinctrl-single compatible "ti,dra7-padconf". This works for almost 95%+ of the pins. few pins need tweaking of the delay parameters to address per die, operational and board(rarely) characteristics. Here is where it gets a little complicated. 18.6.1 IODELAYCONFIG Module Instance Summary (page 4798) - starts at CFG_RMII_MHZ_50_CLK_IN. These registers are programmed per "18.4.6.1.4 Manual IO Timing Modes" (page 4004). Initial sequence of recalibration involves IO isolation - process involving isolating every DRA7 pin from the external world - The only logical place to do this is obviously as part of bootloader. Doing this from kernel can be more than rationally complicated (IO isolation for doing recalibration while a video playback or coprocessor like DSP is active is just plain ridiculous in complexity). The calibrated values then are read for programming these iodelay registers per pin as described in the Section "18.4.6.1.4 Manual IO Timing Modes" (page 4005). In summary: - This does not really control traditional points of pinctrl control such as mux mode, pull direction etc. It is, in short, a tweaking of delay paths from the IP to the external pin. - Most pins do not need iodelay register programming. The ones that do may have upto 3 other registers that may need programming (IN, OUT, OUTEN) - Unlike pinctrl-single where a value is read from dts and programmed straight to the register, programming iodelay involves taking two parameter(a_delay and g_delay) per iodelay register, value for the registers computed based on two other parameters(cdpe, fdpe). Where cdpe and fdpe are computed based on "recalibration sequence" generated values programmed in register fields for ref_count, delay_count and ref_clk_period. - This is also why pinctrl-single wont work here - it is not a copy from dts to register sequence, it is a compute from dts to register sequence. - The recalibration parameters ref_count, delay_count and ref_clk_period are die dependent (hence the need of recalibration sequence). - The parameters a_delay, g_delay are dependent on operational mode/board properties. - This is why such a data in kernel may not scale with multitude of board variations and operational needs of certain pins. - The values themselves come from some automated tool/data sheet with per-board tweaking done under simulation. Considering this was a "configuration of per pin iodelay", i chose pinconf-like model here. Unfortunately, The traditional pinconf is a set of properties for a pin, but it does not give information as to which register those properties belong to. since these registers do not form the same pattern of offsets as padconf register, that iodelay register information will need to be encoded in some form. The traditional pinctrl does not fit this hardware either. it does not control mux, rather given that pinctrl has selected a specific mux mode, it tweaks the delay parameters corresponding to that mux. This is one of those modules that I cant seem to fit neatly in any of the existing frameworks, I am most happy to understand if there are alternatives that can be proposed.. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-10 15:06 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-10 15:06 UTC (permalink / raw) To: linux-arm-kernel On 03/10/2015 05:39 AM, Linus Walleij wrote: > On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > >> +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 */ >> + >; >> + }; >> +}; > > But wait. > > The promise when we merged pinctrl-single was that this driver was to be used > when the system had a one-register-per-pin layout and it was easy to do device > trees based on that. > > We were very reluctant to accept that even though we didn't even have the > generic pin control bindings in place, the argument being that the driver > should know the detailed register layout, it should not be described in the > device tree. We eventually caved in and accepted it as an exception. > > Since this pin controller is not using pinctrl-single it does not enjoy that > privilege and you need to explain why this pin controller cannot use the > generic bindings like so many other pin controllers have since started > to do. ("It is in the same SoC" is not an acceptable argument.) > > What is wrong with this: > Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt > > We can add generic delay bindings to the list, it even seems like > a good idea to do so, as it is likely something that will come up in > other hardware and will be needed for ACPI etc going forward. Let me try to explain how the hardware works in this instance - it is a quirk that we had'nt understood as being mandatory until very recently. Apologies on pointing to TRM. Unfortunately, understanding this kind of needs us to understand the hardware a little deeper. http://www.ti.com/lit/ug/spruhz6/spruhz6.pdf 18.5.2 CTRL_MODULE_CORE registers (page 4040) -> mux starts from CTRL_CORE_PAD_GPMC_AD0 (page 4390) This is the basic support needed for DRA7 family of SoC handled by pinctrl-single. a single register for a single pin -> write the mux mode, internal pull, wakeup capability etc. handled today as part of pinctrl-single compatible "ti,dra7-padconf". This works for almost 95%+ of the pins. few pins need tweaking of the delay parameters to address per die, operational and board(rarely) characteristics. Here is where it gets a little complicated. 18.6.1 IODELAYCONFIG Module Instance Summary (page 4798) - starts at CFG_RMII_MHZ_50_CLK_IN. These registers are programmed per "18.4.6.1.4 Manual IO Timing Modes" (page 4004). Initial sequence of recalibration involves IO isolation - process involving isolating every DRA7 pin from the external world - The only logical place to do this is obviously as part of bootloader. Doing this from kernel can be more than rationally complicated (IO isolation for doing recalibration while a video playback or coprocessor like DSP is active is just plain ridiculous in complexity). The calibrated values then are read for programming these iodelay registers per pin as described in the Section "18.4.6.1.4 Manual IO Timing Modes" (page 4005). In summary: - This does not really control traditional points of pinctrl control such as mux mode, pull direction etc. It is, in short, a tweaking of delay paths from the IP to the external pin. - Most pins do not need iodelay register programming. The ones that do may have upto 3 other registers that may need programming (IN, OUT, OUTEN) - Unlike pinctrl-single where a value is read from dts and programmed straight to the register, programming iodelay involves taking two parameter(a_delay and g_delay) per iodelay register, value for the registers computed based on two other parameters(cdpe, fdpe). Where cdpe and fdpe are computed based on "recalibration sequence" generated values programmed in register fields for ref_count, delay_count and ref_clk_period. - This is also why pinctrl-single wont work here - it is not a copy from dts to register sequence, it is a compute from dts to register sequence. - The recalibration parameters ref_count, delay_count and ref_clk_period are die dependent (hence the need of recalibration sequence). - The parameters a_delay, g_delay are dependent on operational mode/board properties. - This is why such a data in kernel may not scale with multitude of board variations and operational needs of certain pins. - The values themselves come from some automated tool/data sheet with per-board tweaking done under simulation. Considering this was a "configuration of per pin iodelay", i chose pinconf-like model here. Unfortunately, The traditional pinconf is a set of properties for a pin, but it does not give information as to which register those properties belong to. since these registers do not form the same pattern of offsets as padconf register, that iodelay register information will need to be encoded in some form. The traditional pinctrl does not fit this hardware either. it does not control mux, rather given that pinctrl has selected a specific mux mode, it tweaks the delay parameters corresponding to that mux. This is one of those modules that I cant seem to fit neatly in any of the existing frameworks, I am most happy to understand if there are alternatives that can be proposed.. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-10 15:06 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-10 15:06 UTC (permalink / raw) To: Linus Walleij Cc: Tony Lindgren, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On 03/10/2015 05:39 AM, Linus Walleij wrote: > On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > >> +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 */ >> + >; >> + }; >> +}; > > But wait. > > The promise when we merged pinctrl-single was that this driver was to be used > when the system had a one-register-per-pin layout and it was easy to do device > trees based on that. > > We were very reluctant to accept that even though we didn't even have the > generic pin control bindings in place, the argument being that the driver > should know the detailed register layout, it should not be described in the > device tree. We eventually caved in and accepted it as an exception. > > Since this pin controller is not using pinctrl-single it does not enjoy that > privilege and you need to explain why this pin controller cannot use the > generic bindings like so many other pin controllers have since started > to do. ("It is in the same SoC" is not an acceptable argument.) > > What is wrong with this: > Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt > > We can add generic delay bindings to the list, it even seems like > a good idea to do so, as it is likely something that will come up in > other hardware and will be needed for ACPI etc going forward. Let me try to explain how the hardware works in this instance - it is a quirk that we had'nt understood as being mandatory until very recently. Apologies on pointing to TRM. Unfortunately, understanding this kind of needs us to understand the hardware a little deeper. http://www.ti.com/lit/ug/spruhz6/spruhz6.pdf 18.5.2 CTRL_MODULE_CORE registers (page 4040) -> mux starts from CTRL_CORE_PAD_GPMC_AD0 (page 4390) This is the basic support needed for DRA7 family of SoC handled by pinctrl-single. a single register for a single pin -> write the mux mode, internal pull, wakeup capability etc. handled today as part of pinctrl-single compatible "ti,dra7-padconf". This works for almost 95%+ of the pins. few pins need tweaking of the delay parameters to address per die, operational and board(rarely) characteristics. Here is where it gets a little complicated. 18.6.1 IODELAYCONFIG Module Instance Summary (page 4798) - starts at CFG_RMII_MHZ_50_CLK_IN. These registers are programmed per "18.4.6.1.4 Manual IO Timing Modes" (page 4004). Initial sequence of recalibration involves IO isolation - process involving isolating every DRA7 pin from the external world - The only logical place to do this is obviously as part of bootloader. Doing this from kernel can be more than rationally complicated (IO isolation for doing recalibration while a video playback or coprocessor like DSP is active is just plain ridiculous in complexity). The calibrated values then are read for programming these iodelay registers per pin as described in the Section "18.4.6.1.4 Manual IO Timing Modes" (page 4005). In summary: - This does not really control traditional points of pinctrl control such as mux mode, pull direction etc. It is, in short, a tweaking of delay paths from the IP to the external pin. - Most pins do not need iodelay register programming. The ones that do may have upto 3 other registers that may need programming (IN, OUT, OUTEN) - Unlike pinctrl-single where a value is read from dts and programmed straight to the register, programming iodelay involves taking two parameter(a_delay and g_delay) per iodelay register, value for the registers computed based on two other parameters(cdpe, fdpe). Where cdpe and fdpe are computed based on "recalibration sequence" generated values programmed in register fields for ref_count, delay_count and ref_clk_period. - This is also why pinctrl-single wont work here - it is not a copy from dts to register sequence, it is a compute from dts to register sequence. - The recalibration parameters ref_count, delay_count and ref_clk_period are die dependent (hence the need of recalibration sequence). - The parameters a_delay, g_delay are dependent on operational mode/board properties. - This is why such a data in kernel may not scale with multitude of board variations and operational needs of certain pins. - The values themselves come from some automated tool/data sheet with per-board tweaking done under simulation. Considering this was a "configuration of per pin iodelay", i chose pinconf-like model here. Unfortunately, The traditional pinconf is a set of properties for a pin, but it does not give information as to which register those properties belong to. since these registers do not form the same pattern of offsets as padconf register, that iodelay register information will need to be encoded in some form. The traditional pinctrl does not fit this hardware either. it does not control mux, rather given that pinctrl has selected a specific mux mode, it tweaks the delay parameters corresponding to that mux. This is one of those modules that I cant seem to fit neatly in any of the existing frameworks, I am most happy to understand if there are alternatives that can be proposed.. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration 2015-03-10 10:39 ` Linus Walleij (?) @ 2015-03-10 15:33 ` Tony Lindgren -1 siblings, 0 replies; 74+ messages in thread From: Tony Lindgren @ 2015-03-10 15:33 UTC (permalink / raw) To: Linus Walleij Cc: Nishanth Menon, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: > On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > > > +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 */ > > + >; > > + }; > > +}; > > But wait. > > The promise when we merged pinctrl-single was that this driver was to be used > when the system had a one-register-per-pin layout and it was easy to do device > trees based on that. > > We were very reluctant to accept that even though we didn't even have the > generic pin control bindings in place, the argument being that the driver > should know the detailed register layout, it should not be described in the > device tree. We eventually caved in and accepted it as an exception. Hey let's get few things straight here. There's nothing stopping the driver from knowing a detailed register layout with the pinctrl-single binding. This can be very easily done based on the compatible flag and match data as needed and check the values provided. And let's also recap the reasons for the pinctrl-single binding. The the main reason for the pinctrl-single binding is to avoid mapping individual register bits to device tree compatible string properties. Imagine doing that for hundreds of similar registers. Believe me, I tried using device tree properties initially and it just sucked big time. For larger amounts of dts data, it's best to stick to numeric values and phandles in the device tree data and rely on the preprocessor for getting the values right. Finally, we don't want to build databases into the kernel drivers for every SoC. We certainly have plenty fo those already. > Since this pin controller is not using pinctrl-single it does not enjoy that > privilege and you need to explain why this pin controller cannot use the > generic bindings like so many other pin controllers have since started > to do. ("It is in the same SoC" is not an acceptable argument.) > > What is wrong with this: > Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt Nishanth, care to explain your reasons for using pinctrl-single binding here vs the generic binding? Is the amount of string parsing with the data an issue here? > We can add generic delay bindings to the list, it even seems like > a good idea to do so, as it is likely something that will come up in > other hardware and will be needed for ACPI etc going forward. We certainly need to make setting delays (with values) generic, no doubt about that. If the large amount of data is not an issue here, we could maybe set each iodelay register as a separate device? Then the binding could be just along the interrupts-extended type binding: foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; Where the 0x18c is the instance offset of the register within the ti,dra7-iodelay compatible controller. Regards, Tony ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-10 15:33 ` Tony Lindgren 0 siblings, 0 replies; 74+ messages in thread From: Tony Lindgren @ 2015-03-10 15:33 UTC (permalink / raw) To: linux-arm-kernel * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: > On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > > > +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 */ > > + >; > > + }; > > +}; > > But wait. > > The promise when we merged pinctrl-single was that this driver was to be used > when the system had a one-register-per-pin layout and it was easy to do device > trees based on that. > > We were very reluctant to accept that even though we didn't even have the > generic pin control bindings in place, the argument being that the driver > should know the detailed register layout, it should not be described in the > device tree. We eventually caved in and accepted it as an exception. Hey let's get few things straight here. There's nothing stopping the driver from knowing a detailed register layout with the pinctrl-single binding. This can be very easily done based on the compatible flag and match data as needed and check the values provided. And let's also recap the reasons for the pinctrl-single binding. The the main reason for the pinctrl-single binding is to avoid mapping individual register bits to device tree compatible string properties. Imagine doing that for hundreds of similar registers. Believe me, I tried using device tree properties initially and it just sucked big time. For larger amounts of dts data, it's best to stick to numeric values and phandles in the device tree data and rely on the preprocessor for getting the values right. Finally, we don't want to build databases into the kernel drivers for every SoC. We certainly have plenty fo those already. > Since this pin controller is not using pinctrl-single it does not enjoy that > privilege and you need to explain why this pin controller cannot use the > generic bindings like so many other pin controllers have since started > to do. ("It is in the same SoC" is not an acceptable argument.) > > What is wrong with this: > Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt Nishanth, care to explain your reasons for using pinctrl-single binding here vs the generic binding? Is the amount of string parsing with the data an issue here? > We can add generic delay bindings to the list, it even seems like > a good idea to do so, as it is likely something that will come up in > other hardware and will be needed for ACPI etc going forward. We certainly need to make setting delays (with values) generic, no doubt about that. If the large amount of data is not an issue here, we could maybe set each iodelay register as a separate device? Then the binding could be just along the interrupts-extended type binding: foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; Where the 0x18c is the instance offset of the register within the ti,dra7-iodelay compatible controller. Regards, Tony ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-10 15:33 ` Tony Lindgren 0 siblings, 0 replies; 74+ messages in thread From: Tony Lindgren @ 2015-03-10 15:33 UTC (permalink / raw) To: Linus Walleij Cc: Nishanth Menon, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: > On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > > > +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 */ > > + >; > > + }; > > +}; > > But wait. > > The promise when we merged pinctrl-single was that this driver was to be used > when the system had a one-register-per-pin layout and it was easy to do device > trees based on that. > > We were very reluctant to accept that even though we didn't even have the > generic pin control bindings in place, the argument being that the driver > should know the detailed register layout, it should not be described in the > device tree. We eventually caved in and accepted it as an exception. Hey let's get few things straight here. There's nothing stopping the driver from knowing a detailed register layout with the pinctrl-single binding. This can be very easily done based on the compatible flag and match data as needed and check the values provided. And let's also recap the reasons for the pinctrl-single binding. The the main reason for the pinctrl-single binding is to avoid mapping individual register bits to device tree compatible string properties. Imagine doing that for hundreds of similar registers. Believe me, I tried using device tree properties initially and it just sucked big time. For larger amounts of dts data, it's best to stick to numeric values and phandles in the device tree data and rely on the preprocessor for getting the values right. Finally, we don't want to build databases into the kernel drivers for every SoC. We certainly have plenty fo those already. > Since this pin controller is not using pinctrl-single it does not enjoy that > privilege and you need to explain why this pin controller cannot use the > generic bindings like so many other pin controllers have since started > to do. ("It is in the same SoC" is not an acceptable argument.) > > What is wrong with this: > Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt Nishanth, care to explain your reasons for using pinctrl-single binding here vs the generic binding? Is the amount of string parsing with the data an issue here? > We can add generic delay bindings to the list, it even seems like > a good idea to do so, as it is likely something that will come up in > other hardware and will be needed for ACPI etc going forward. We certainly need to make setting delays (with values) generic, no doubt about that. If the large amount of data is not an issue here, we could maybe set each iodelay register as a separate device? Then the binding could be just along the interrupts-extended type binding: foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; Where the 0x18c is the instance offset of the register within the ti,dra7-iodelay compatible controller. Regards, Tony ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration 2015-03-10 15:33 ` Tony Lindgren (?) @ 2015-03-10 17:25 ` Nishanth Menon -1 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-10 17:25 UTC (permalink / raw) To: Tony Lindgren, Linus Walleij Cc: devicetree, Lokesh Vutla, linux-kernel, linux-gpio, Linux-OMAP, linux-arm-kernel On 03/10/2015 10:33 AM, Tony Lindgren wrote: > * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: >> On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: >> >>> +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 */ >>> + >; >>> + }; >>> +}; >> >> But wait. >> >> The promise when we merged pinctrl-single was that this driver was to be used >> when the system had a one-register-per-pin layout and it was easy to do device >> trees based on that. >> >> We were very reluctant to accept that even though we didn't even have the >> generic pin control bindings in place, the argument being that the driver >> should know the detailed register layout, it should not be described in the >> device tree. We eventually caved in and accepted it as an exception. > > Hey let's get few things straight here. There's nothing stopping the > driver from knowing a detailed register layout with the pinctrl-single > binding. This can be very easily done based on the compatible flag and > match data as needed and check the values provided. > > And let's also recap the reasons for the pinctrl-single binding. The > the main reason for the pinctrl-single binding is to avoid mapping > individual register bits to device tree compatible string properties. > > Imagine doing that for hundreds of similar registers. Believe me, I tried > using device tree properties initially and it just sucked big time. For > larger amounts of dts data, it's best to stick to numeric values and > phandles in the device tree data and rely on the preprocessor for > getting the values right. > > Finally, we don't want to build databases into the kernel drivers for > every SoC. We certainly have plenty fo those already. > >> Since this pin controller is not using pinctrl-single it does not enjoy that >> privilege and you need to explain why this pin controller cannot use the >> generic bindings like so many other pin controllers have since started >> to do. ("It is in the same SoC" is not an acceptable argument.) >> >> What is wrong with this: >> Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt > > Nishanth, care to explain your reasons for using pinctrl-single binding > here vs the generic binding? Is the amount of string parsing with the > data an issue here? Wrong option chosen, I suppose :( - alright, lets discuss the alternative. > >> We can add generic delay bindings to the list, it even seems like >> a good idea to do so, as it is likely something that will come up in >> other hardware and will be needed for ACPI etc going forward. > > We certainly need to make setting delays (with values) generic, no > doubt about that. > > If the large amount of data is not an issue here, we could maybe set > each iodelay register as a separate device? Then the binding could > be just along the interrupts-extended type binding: > > foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; > > Where the 0x18c is the instance offset of the register within the > ti,dra7-iodelay compatible controller. if mmc2_pins_default point at pins for mmc pin configuration for control_core (pinctrl-single), are you proposing the following? 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 */ >; }; &mmc2 { ... pinctrl-1 = &mmc2_pins_default, /* points to mmc control core pins */ <&iodelay 0x18c A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A19_IN */ <&iodelay 0x1a4 A_DELAY(265) | G_DELAY(360)>, /* CFG_GPMC_A20_IN */ <&iodelay 0x1b0 A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A21_IN */ <&iodelay 0x1bc A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A22_IN */ <&iodelay 0x1c8 A_DELAY(287) | G_DELAY(420)>, /* CFG_GPMC_A23_IN */ <&iodelay 0x1d4 A_DELAY(144) | G_DELAY(240)>, /* CFG_GPMC_A24_IN */ <&iodelay 0x1e0 A_DELAY(0) | G_DELAY(0)>, /* CFG_GPMC_A25_IN */ <&iodelay 0x1ec A_DELAY(120) | G_DELAY(0)>, /* CFG_GPMC_A26_IN */ <&iodelay 0x1f8 A_DELAY(120) | G_DELAY(180)>, /* CFG_GPMC_A27_IN */ <&iodelay 0x360 A_DELAY(0) | G_DELAY(0)>; /* CFG_GPMC_CS1_IN */ I have to check if we are capable of parsing that. but if that is the approach chosen, I suppose we might be able to figure something, I suppose.. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-10 17:25 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-10 17:25 UTC (permalink / raw) To: linux-arm-kernel On 03/10/2015 10:33 AM, Tony Lindgren wrote: > * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: >> On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: >> >>> +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 */ >>> + >; >>> + }; >>> +}; >> >> But wait. >> >> The promise when we merged pinctrl-single was that this driver was to be used >> when the system had a one-register-per-pin layout and it was easy to do device >> trees based on that. >> >> We were very reluctant to accept that even though we didn't even have the >> generic pin control bindings in place, the argument being that the driver >> should know the detailed register layout, it should not be described in the >> device tree. We eventually caved in and accepted it as an exception. > > Hey let's get few things straight here. There's nothing stopping the > driver from knowing a detailed register layout with the pinctrl-single > binding. This can be very easily done based on the compatible flag and > match data as needed and check the values provided. > > And let's also recap the reasons for the pinctrl-single binding. The > the main reason for the pinctrl-single binding is to avoid mapping > individual register bits to device tree compatible string properties. > > Imagine doing that for hundreds of similar registers. Believe me, I tried > using device tree properties initially and it just sucked big time. For > larger amounts of dts data, it's best to stick to numeric values and > phandles in the device tree data and rely on the preprocessor for > getting the values right. > > Finally, we don't want to build databases into the kernel drivers for > every SoC. We certainly have plenty fo those already. > >> Since this pin controller is not using pinctrl-single it does not enjoy that >> privilege and you need to explain why this pin controller cannot use the >> generic bindings like so many other pin controllers have since started >> to do. ("It is in the same SoC" is not an acceptable argument.) >> >> What is wrong with this: >> Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt > > Nishanth, care to explain your reasons for using pinctrl-single binding > here vs the generic binding? Is the amount of string parsing with the > data an issue here? Wrong option chosen, I suppose :( - alright, lets discuss the alternative. > >> We can add generic delay bindings to the list, it even seems like >> a good idea to do so, as it is likely something that will come up in >> other hardware and will be needed for ACPI etc going forward. > > We certainly need to make setting delays (with values) generic, no > doubt about that. > > If the large amount of data is not an issue here, we could maybe set > each iodelay register as a separate device? Then the binding could > be just along the interrupts-extended type binding: > > foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; > > Where the 0x18c is the instance offset of the register within the > ti,dra7-iodelay compatible controller. if mmc2_pins_default point at pins for mmc pin configuration for control_core (pinctrl-single), are you proposing the following? 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 */ >; }; &mmc2 { ... pinctrl-1 = &mmc2_pins_default, /* points to mmc control core pins */ <&iodelay 0x18c A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A19_IN */ <&iodelay 0x1a4 A_DELAY(265) | G_DELAY(360)>, /* CFG_GPMC_A20_IN */ <&iodelay 0x1b0 A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A21_IN */ <&iodelay 0x1bc A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A22_IN */ <&iodelay 0x1c8 A_DELAY(287) | G_DELAY(420)>, /* CFG_GPMC_A23_IN */ <&iodelay 0x1d4 A_DELAY(144) | G_DELAY(240)>, /* CFG_GPMC_A24_IN */ <&iodelay 0x1e0 A_DELAY(0) | G_DELAY(0)>, /* CFG_GPMC_A25_IN */ <&iodelay 0x1ec A_DELAY(120) | G_DELAY(0)>, /* CFG_GPMC_A26_IN */ <&iodelay 0x1f8 A_DELAY(120) | G_DELAY(180)>, /* CFG_GPMC_A27_IN */ <&iodelay 0x360 A_DELAY(0) | G_DELAY(0)>; /* CFG_GPMC_CS1_IN */ I have to check if we are capable of parsing that. but if that is the approach chosen, I suppose we might be able to figure something, I suppose.. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-10 17:25 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-10 17:25 UTC (permalink / raw) To: Tony Lindgren, Linus Walleij Cc: linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On 03/10/2015 10:33 AM, Tony Lindgren wrote: > * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: >> On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: >> >>> +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 */ >>> + >; >>> + }; >>> +}; >> >> But wait. >> >> The promise when we merged pinctrl-single was that this driver was to be used >> when the system had a one-register-per-pin layout and it was easy to do device >> trees based on that. >> >> We were very reluctant to accept that even though we didn't even have the >> generic pin control bindings in place, the argument being that the driver >> should know the detailed register layout, it should not be described in the >> device tree. We eventually caved in and accepted it as an exception. > > Hey let's get few things straight here. There's nothing stopping the > driver from knowing a detailed register layout with the pinctrl-single > binding. This can be very easily done based on the compatible flag and > match data as needed and check the values provided. > > And let's also recap the reasons for the pinctrl-single binding. The > the main reason for the pinctrl-single binding is to avoid mapping > individual register bits to device tree compatible string properties. > > Imagine doing that for hundreds of similar registers. Believe me, I tried > using device tree properties initially and it just sucked big time. For > larger amounts of dts data, it's best to stick to numeric values and > phandles in the device tree data and rely on the preprocessor for > getting the values right. > > Finally, we don't want to build databases into the kernel drivers for > every SoC. We certainly have plenty fo those already. > >> Since this pin controller is not using pinctrl-single it does not enjoy that >> privilege and you need to explain why this pin controller cannot use the >> generic bindings like so many other pin controllers have since started >> to do. ("It is in the same SoC" is not an acceptable argument.) >> >> What is wrong with this: >> Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt > > Nishanth, care to explain your reasons for using pinctrl-single binding > here vs the generic binding? Is the amount of string parsing with the > data an issue here? Wrong option chosen, I suppose :( - alright, lets discuss the alternative. > >> We can add generic delay bindings to the list, it even seems like >> a good idea to do so, as it is likely something that will come up in >> other hardware and will be needed for ACPI etc going forward. > > We certainly need to make setting delays (with values) generic, no > doubt about that. > > If the large amount of data is not an issue here, we could maybe set > each iodelay register as a separate device? Then the binding could > be just along the interrupts-extended type binding: > > foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; > > Where the 0x18c is the instance offset of the register within the > ti,dra7-iodelay compatible controller. if mmc2_pins_default point at pins for mmc pin configuration for control_core (pinctrl-single), are you proposing the following? 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 */ >; }; &mmc2 { ... pinctrl-1 = &mmc2_pins_default, /* points to mmc control core pins */ <&iodelay 0x18c A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A19_IN */ <&iodelay 0x1a4 A_DELAY(265) | G_DELAY(360)>, /* CFG_GPMC_A20_IN */ <&iodelay 0x1b0 A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A21_IN */ <&iodelay 0x1bc A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A22_IN */ <&iodelay 0x1c8 A_DELAY(287) | G_DELAY(420)>, /* CFG_GPMC_A23_IN */ <&iodelay 0x1d4 A_DELAY(144) | G_DELAY(240)>, /* CFG_GPMC_A24_IN */ <&iodelay 0x1e0 A_DELAY(0) | G_DELAY(0)>, /* CFG_GPMC_A25_IN */ <&iodelay 0x1ec A_DELAY(120) | G_DELAY(0)>, /* CFG_GPMC_A26_IN */ <&iodelay 0x1f8 A_DELAY(120) | G_DELAY(180)>, /* CFG_GPMC_A27_IN */ <&iodelay 0x360 A_DELAY(0) | G_DELAY(0)>; /* CFG_GPMC_CS1_IN */ I have to check if we are capable of parsing that. but if that is the approach chosen, I suppose we might be able to figure something, I suppose.. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration 2015-03-10 17:25 ` Nishanth Menon (?) @ 2015-03-10 17:31 ` Tony Lindgren -1 siblings, 0 replies; 74+ messages in thread From: Tony Lindgren @ 2015-03-10 17:31 UTC (permalink / raw) To: Nishanth Menon Cc: Linus Walleij, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla * Nishanth Menon <nm@ti.com> [150310 10:25]: > On 03/10/2015 10:33 AM, Tony Lindgren wrote: > > * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: > >> On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > >> > >>> +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 */ > >>> + >; > >>> + }; > >>> +}; > >> > >> But wait. > >> > >> The promise when we merged pinctrl-single was that this driver was to be used > >> when the system had a one-register-per-pin layout and it was easy to do device > >> trees based on that. > >> > >> We were very reluctant to accept that even though we didn't even have the > >> generic pin control bindings in place, the argument being that the driver > >> should know the detailed register layout, it should not be described in the > >> device tree. We eventually caved in and accepted it as an exception. > > > > Hey let's get few things straight here. There's nothing stopping the > > driver from knowing a detailed register layout with the pinctrl-single > > binding. This can be very easily done based on the compatible flag and > > match data as needed and check the values provided. > > > > And let's also recap the reasons for the pinctrl-single binding. The > > the main reason for the pinctrl-single binding is to avoid mapping > > individual register bits to device tree compatible string properties. > > > > Imagine doing that for hundreds of similar registers. Believe me, I tried > > using device tree properties initially and it just sucked big time. For > > larger amounts of dts data, it's best to stick to numeric values and > > phandles in the device tree data and rely on the preprocessor for > > getting the values right. > > > > Finally, we don't want to build databases into the kernel drivers for > > every SoC. We certainly have plenty fo those already. > > > >> Since this pin controller is not using pinctrl-single it does not enjoy that > >> privilege and you need to explain why this pin controller cannot use the > >> generic bindings like so many other pin controllers have since started > >> to do. ("It is in the same SoC" is not an acceptable argument.) > >> > >> What is wrong with this: > >> Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt > > > > Nishanth, care to explain your reasons for using pinctrl-single binding > > here vs the generic binding? Is the amount of string parsing with the > > data an issue here? > > Wrong option chosen, I suppose :( - alright, lets discuss the alternative. Heh well now we know :) > >> We can add generic delay bindings to the list, it even seems like > >> a good idea to do so, as it is likely something that will come up in > >> other hardware and will be needed for ACPI etc going forward. > > > > We certainly need to make setting delays (with values) generic, no > > doubt about that. > > > > If the large amount of data is not an issue here, we could maybe set > > each iodelay register as a separate device? Then the binding could > > be just along the interrupts-extended type binding: > > > > foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; > > > > Where the 0x18c is the instance offset of the register within the > > ti,dra7-iodelay compatible controller. > > if mmc2_pins_default point at pins for mmc pin configuration for > control_core (pinctrl-single), are you proposing the following? > > 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 */ > >; > }; Yeah so existing pinctrl-single binding above, with additional iodelay binding below.. > &mmc2 { > ... > pinctrl-1 = > &mmc2_pins_default, /* points to mmc control core pins */ > <&iodelay 0x18c A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A19_IN */ > <&iodelay 0x1a4 A_DELAY(265) | G_DELAY(360)>, /* CFG_GPMC_A20_IN */ > <&iodelay 0x1b0 A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A21_IN */ > <&iodelay 0x1bc A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A22_IN */ > <&iodelay 0x1c8 A_DELAY(287) | G_DELAY(420)>, /* CFG_GPMC_A23_IN */ > <&iodelay 0x1d4 A_DELAY(144) | G_DELAY(240)>, /* CFG_GPMC_A24_IN */ > <&iodelay 0x1e0 A_DELAY(0) | G_DELAY(0)>, /* CFG_GPMC_A25_IN */ > <&iodelay 0x1ec A_DELAY(120) | G_DELAY(0)>, /* CFG_GPMC_A26_IN */ > <&iodelay 0x1f8 A_DELAY(120) | G_DELAY(180)>, /* CFG_GPMC_A27_IN */ > <&iodelay 0x360 A_DELAY(0) | G_DELAY(0)>; /* CFG_GPMC_CS1_IN */ > > I have to check if we are capable of parsing that. but if that is the > approach chosen, I suppose we might be able to figure something, I > suppose.. Yes except I'd make use of some kind of #pinctrl-cells here just like interrupt controller has #interrupt-cells. Then you can have the values seprate and the controller knows what to do with them based on the compatible flag and #pinctrl-cells. Regards, Tony ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-10 17:31 ` Tony Lindgren 0 siblings, 0 replies; 74+ messages in thread From: Tony Lindgren @ 2015-03-10 17:31 UTC (permalink / raw) To: linux-arm-kernel * Nishanth Menon <nm@ti.com> [150310 10:25]: > On 03/10/2015 10:33 AM, Tony Lindgren wrote: > > * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: > >> On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > >> > >>> +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 */ > >>> + >; > >>> + }; > >>> +}; > >> > >> But wait. > >> > >> The promise when we merged pinctrl-single was that this driver was to be used > >> when the system had a one-register-per-pin layout and it was easy to do device > >> trees based on that. > >> > >> We were very reluctant to accept that even though we didn't even have the > >> generic pin control bindings in place, the argument being that the driver > >> should know the detailed register layout, it should not be described in the > >> device tree. We eventually caved in and accepted it as an exception. > > > > Hey let's get few things straight here. There's nothing stopping the > > driver from knowing a detailed register layout with the pinctrl-single > > binding. This can be very easily done based on the compatible flag and > > match data as needed and check the values provided. > > > > And let's also recap the reasons for the pinctrl-single binding. The > > the main reason for the pinctrl-single binding is to avoid mapping > > individual register bits to device tree compatible string properties. > > > > Imagine doing that for hundreds of similar registers. Believe me, I tried > > using device tree properties initially and it just sucked big time. For > > larger amounts of dts data, it's best to stick to numeric values and > > phandles in the device tree data and rely on the preprocessor for > > getting the values right. > > > > Finally, we don't want to build databases into the kernel drivers for > > every SoC. We certainly have plenty fo those already. > > > >> Since this pin controller is not using pinctrl-single it does not enjoy that > >> privilege and you need to explain why this pin controller cannot use the > >> generic bindings like so many other pin controllers have since started > >> to do. ("It is in the same SoC" is not an acceptable argument.) > >> > >> What is wrong with this: > >> Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt > > > > Nishanth, care to explain your reasons for using pinctrl-single binding > > here vs the generic binding? Is the amount of string parsing with the > > data an issue here? > > Wrong option chosen, I suppose :( - alright, lets discuss the alternative. Heh well now we know :) > >> We can add generic delay bindings to the list, it even seems like > >> a good idea to do so, as it is likely something that will come up in > >> other hardware and will be needed for ACPI etc going forward. > > > > We certainly need to make setting delays (with values) generic, no > > doubt about that. > > > > If the large amount of data is not an issue here, we could maybe set > > each iodelay register as a separate device? Then the binding could > > be just along the interrupts-extended type binding: > > > > foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; > > > > Where the 0x18c is the instance offset of the register within the > > ti,dra7-iodelay compatible controller. > > if mmc2_pins_default point at pins for mmc pin configuration for > control_core (pinctrl-single), are you proposing the following? > > 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 */ > >; > }; Yeah so existing pinctrl-single binding above, with additional iodelay binding below.. > &mmc2 { > ... > pinctrl-1 = > &mmc2_pins_default, /* points to mmc control core pins */ > <&iodelay 0x18c A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A19_IN */ > <&iodelay 0x1a4 A_DELAY(265) | G_DELAY(360)>, /* CFG_GPMC_A20_IN */ > <&iodelay 0x1b0 A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A21_IN */ > <&iodelay 0x1bc A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A22_IN */ > <&iodelay 0x1c8 A_DELAY(287) | G_DELAY(420)>, /* CFG_GPMC_A23_IN */ > <&iodelay 0x1d4 A_DELAY(144) | G_DELAY(240)>, /* CFG_GPMC_A24_IN */ > <&iodelay 0x1e0 A_DELAY(0) | G_DELAY(0)>, /* CFG_GPMC_A25_IN */ > <&iodelay 0x1ec A_DELAY(120) | G_DELAY(0)>, /* CFG_GPMC_A26_IN */ > <&iodelay 0x1f8 A_DELAY(120) | G_DELAY(180)>, /* CFG_GPMC_A27_IN */ > <&iodelay 0x360 A_DELAY(0) | G_DELAY(0)>; /* CFG_GPMC_CS1_IN */ > > I have to check if we are capable of parsing that. but if that is the > approach chosen, I suppose we might be able to figure something, I > suppose.. Yes except I'd make use of some kind of #pinctrl-cells here just like interrupt controller has #interrupt-cells. Then you can have the values seprate and the controller knows what to do with them based on the compatible flag and #pinctrl-cells. Regards, Tony ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-10 17:31 ` Tony Lindgren 0 siblings, 0 replies; 74+ messages in thread From: Tony Lindgren @ 2015-03-10 17:31 UTC (permalink / raw) To: Nishanth Menon Cc: Linus Walleij, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla * Nishanth Menon <nm@ti.com> [150310 10:25]: > On 03/10/2015 10:33 AM, Tony Lindgren wrote: > > * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: > >> On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > >> > >>> +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 */ > >>> + >; > >>> + }; > >>> +}; > >> > >> But wait. > >> > >> The promise when we merged pinctrl-single was that this driver was to be used > >> when the system had a one-register-per-pin layout and it was easy to do device > >> trees based on that. > >> > >> We were very reluctant to accept that even though we didn't even have the > >> generic pin control bindings in place, the argument being that the driver > >> should know the detailed register layout, it should not be described in the > >> device tree. We eventually caved in and accepted it as an exception. > > > > Hey let's get few things straight here. There's nothing stopping the > > driver from knowing a detailed register layout with the pinctrl-single > > binding. This can be very easily done based on the compatible flag and > > match data as needed and check the values provided. > > > > And let's also recap the reasons for the pinctrl-single binding. The > > the main reason for the pinctrl-single binding is to avoid mapping > > individual register bits to device tree compatible string properties. > > > > Imagine doing that for hundreds of similar registers. Believe me, I tried > > using device tree properties initially and it just sucked big time. For > > larger amounts of dts data, it's best to stick to numeric values and > > phandles in the device tree data and rely on the preprocessor for > > getting the values right. > > > > Finally, we don't want to build databases into the kernel drivers for > > every SoC. We certainly have plenty fo those already. > > > >> Since this pin controller is not using pinctrl-single it does not enjoy that > >> privilege and you need to explain why this pin controller cannot use the > >> generic bindings like so many other pin controllers have since started > >> to do. ("It is in the same SoC" is not an acceptable argument.) > >> > >> What is wrong with this: > >> Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt > > > > Nishanth, care to explain your reasons for using pinctrl-single binding > > here vs the generic binding? Is the amount of string parsing with the > > data an issue here? > > Wrong option chosen, I suppose :( - alright, lets discuss the alternative. Heh well now we know :) > >> We can add generic delay bindings to the list, it even seems like > >> a good idea to do so, as it is likely something that will come up in > >> other hardware and will be needed for ACPI etc going forward. > > > > We certainly need to make setting delays (with values) generic, no > > doubt about that. > > > > If the large amount of data is not an issue here, we could maybe set > > each iodelay register as a separate device? Then the binding could > > be just along the interrupts-extended type binding: > > > > foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; > > > > Where the 0x18c is the instance offset of the register within the > > ti,dra7-iodelay compatible controller. > > if mmc2_pins_default point at pins for mmc pin configuration for > control_core (pinctrl-single), are you proposing the following? > > 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 */ > >; > }; Yeah so existing pinctrl-single binding above, with additional iodelay binding below.. > &mmc2 { > ... > pinctrl-1 = > &mmc2_pins_default, /* points to mmc control core pins */ > <&iodelay 0x18c A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A19_IN */ > <&iodelay 0x1a4 A_DELAY(265) | G_DELAY(360)>, /* CFG_GPMC_A20_IN */ > <&iodelay 0x1b0 A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A21_IN */ > <&iodelay 0x1bc A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A22_IN */ > <&iodelay 0x1c8 A_DELAY(287) | G_DELAY(420)>, /* CFG_GPMC_A23_IN */ > <&iodelay 0x1d4 A_DELAY(144) | G_DELAY(240)>, /* CFG_GPMC_A24_IN */ > <&iodelay 0x1e0 A_DELAY(0) | G_DELAY(0)>, /* CFG_GPMC_A25_IN */ > <&iodelay 0x1ec A_DELAY(120) | G_DELAY(0)>, /* CFG_GPMC_A26_IN */ > <&iodelay 0x1f8 A_DELAY(120) | G_DELAY(180)>, /* CFG_GPMC_A27_IN */ > <&iodelay 0x360 A_DELAY(0) | G_DELAY(0)>; /* CFG_GPMC_CS1_IN */ > > I have to check if we are capable of parsing that. but if that is the > approach chosen, I suppose we might be able to figure something, I > suppose.. Yes except I'd make use of some kind of #pinctrl-cells here just like interrupt controller has #interrupt-cells. Then you can have the values seprate and the controller knows what to do with them based on the compatible flag and #pinctrl-cells. Regards, Tony ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration 2015-03-10 17:31 ` Tony Lindgren (?) @ 2015-03-10 18:33 ` Nishanth Menon -1 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-10 18:33 UTC (permalink / raw) To: Tony Lindgren Cc: Linus Walleij, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On 03/10/2015 12:31 PM, Tony Lindgren wrote: > * Nishanth Menon <nm@ti.com> [150310 10:25]: >> On 03/10/2015 10:33 AM, Tony Lindgren wrote: >>> * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: >>>> On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: >>>> >>>>> +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 */ >>>>> + >; >>>>> + }; >>>>> +}; >>>> >>>> But wait. >>>> >>>> The promise when we merged pinctrl-single was that this driver was to be used >>>> when the system had a one-register-per-pin layout and it was easy to do device >>>> trees based on that. >>>> >>>> We were very reluctant to accept that even though we didn't even have the >>>> generic pin control bindings in place, the argument being that the driver >>>> should know the detailed register layout, it should not be described in the >>>> device tree. We eventually caved in and accepted it as an exception. >>> >>> Hey let's get few things straight here. There's nothing stopping the >>> driver from knowing a detailed register layout with the pinctrl-single >>> binding. This can be very easily done based on the compatible flag and >>> match data as needed and check the values provided. >>> >>> And let's also recap the reasons for the pinctrl-single binding. The >>> the main reason for the pinctrl-single binding is to avoid mapping >>> individual register bits to device tree compatible string properties. >>> >>> Imagine doing that for hundreds of similar registers. Believe me, I tried >>> using device tree properties initially and it just sucked big time. For >>> larger amounts of dts data, it's best to stick to numeric values and >>> phandles in the device tree data and rely on the preprocessor for >>> getting the values right. >>> >>> Finally, we don't want to build databases into the kernel drivers for >>> every SoC. We certainly have plenty fo those already. >>> >>>> Since this pin controller is not using pinctrl-single it does not enjoy that >>>> privilege and you need to explain why this pin controller cannot use the >>>> generic bindings like so many other pin controllers have since started >>>> to do. ("It is in the same SoC" is not an acceptable argument.) >>>> >>>> What is wrong with this: >>>> Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt >>> >>> Nishanth, care to explain your reasons for using pinctrl-single binding >>> here vs the generic binding? Is the amount of string parsing with the >>> data an issue here? >> >> Wrong option chosen, I suppose :( - alright, lets discuss the alternative. > > Heh well now we know :) > >>>> We can add generic delay bindings to the list, it even seems like >>>> a good idea to do so, as it is likely something that will come up in >>>> other hardware and will be needed for ACPI etc going forward. >>> >>> We certainly need to make setting delays (with values) generic, no >>> doubt about that. >>> >>> If the large amount of data is not an issue here, we could maybe set >>> each iodelay register as a separate device? Then the binding could >>> be just along the interrupts-extended type binding: >>> >>> foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; >>> >>> Where the 0x18c is the instance offset of the register within the >>> ti,dra7-iodelay compatible controller. >> >> if mmc2_pins_default point at pins for mmc pin configuration for >> control_core (pinctrl-single), are you proposing the following? >> >> 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 */ >> >; >> }; > > Yeah so existing pinctrl-single binding above, with additional iodelay > binding below.. > >> &mmc2 { >> ... >> pinctrl-1 = >> &mmc2_pins_default, /* points to mmc control core pins */ >> <&iodelay 0x18c A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A19_IN */ >> <&iodelay 0x1a4 A_DELAY(265) | G_DELAY(360)>, /* CFG_GPMC_A20_IN */ >> <&iodelay 0x1b0 A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A21_IN */ >> <&iodelay 0x1bc A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A22_IN */ >> <&iodelay 0x1c8 A_DELAY(287) | G_DELAY(420)>, /* CFG_GPMC_A23_IN */ >> <&iodelay 0x1d4 A_DELAY(144) | G_DELAY(240)>, /* CFG_GPMC_A24_IN */ >> <&iodelay 0x1e0 A_DELAY(0) | G_DELAY(0)>, /* CFG_GPMC_A25_IN */ >> <&iodelay 0x1ec A_DELAY(120) | G_DELAY(0)>, /* CFG_GPMC_A26_IN */ >> <&iodelay 0x1f8 A_DELAY(120) | G_DELAY(180)>, /* CFG_GPMC_A27_IN */ >> <&iodelay 0x360 A_DELAY(0) | G_DELAY(0)>; /* CFG_GPMC_CS1_IN */ >> >> I have to check if we are capable of parsing that. but if that is the >> approach chosen, I suppose we might be able to figure something, I >> suppose.. > > Yes except I'd make use of some kind of #pinctrl-cells here just like > interrupt controller has #interrupt-cells. Then you can have the values > seprate and the controller knows what to do with them based on the > compatible flag and #pinctrl-cells. Something like the following I suppose, where pinctrl-cells is optional? dra7_pmx_core: pinmux@4a003400 { compatible = "ti,dra7-padconf", "pinctrl-single"; reg = <0x4a003400 0x0464>; #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; interrupt-controller; pinctrl-single,register-width = <32>; pinctrl-single,function-mask = <0x3fffffff>; }; dra7_iodelay_core: padconf@4844a000 { compatible = "ti,dra7-iodelay"; reg = <0x4844a000 0x0d1c>; #address-cells = <1>; #size-cells = <0>; #pinctrl-cells = <2>; }; Linus, I hope you are ok with the above? -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-10 18:33 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-10 18:33 UTC (permalink / raw) To: linux-arm-kernel On 03/10/2015 12:31 PM, Tony Lindgren wrote: > * Nishanth Menon <nm@ti.com> [150310 10:25]: >> On 03/10/2015 10:33 AM, Tony Lindgren wrote: >>> * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: >>>> On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: >>>> >>>>> +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 */ >>>>> + >; >>>>> + }; >>>>> +}; >>>> >>>> But wait. >>>> >>>> The promise when we merged pinctrl-single was that this driver was to be used >>>> when the system had a one-register-per-pin layout and it was easy to do device >>>> trees based on that. >>>> >>>> We were very reluctant to accept that even though we didn't even have the >>>> generic pin control bindings in place, the argument being that the driver >>>> should know the detailed register layout, it should not be described in the >>>> device tree. We eventually caved in and accepted it as an exception. >>> >>> Hey let's get few things straight here. There's nothing stopping the >>> driver from knowing a detailed register layout with the pinctrl-single >>> binding. This can be very easily done based on the compatible flag and >>> match data as needed and check the values provided. >>> >>> And let's also recap the reasons for the pinctrl-single binding. The >>> the main reason for the pinctrl-single binding is to avoid mapping >>> individual register bits to device tree compatible string properties. >>> >>> Imagine doing that for hundreds of similar registers. Believe me, I tried >>> using device tree properties initially and it just sucked big time. For >>> larger amounts of dts data, it's best to stick to numeric values and >>> phandles in the device tree data and rely on the preprocessor for >>> getting the values right. >>> >>> Finally, we don't want to build databases into the kernel drivers for >>> every SoC. We certainly have plenty fo those already. >>> >>>> Since this pin controller is not using pinctrl-single it does not enjoy that >>>> privilege and you need to explain why this pin controller cannot use the >>>> generic bindings like so many other pin controllers have since started >>>> to do. ("It is in the same SoC" is not an acceptable argument.) >>>> >>>> What is wrong with this: >>>> Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt >>> >>> Nishanth, care to explain your reasons for using pinctrl-single binding >>> here vs the generic binding? Is the amount of string parsing with the >>> data an issue here? >> >> Wrong option chosen, I suppose :( - alright, lets discuss the alternative. > > Heh well now we know :) > >>>> We can add generic delay bindings to the list, it even seems like >>>> a good idea to do so, as it is likely something that will come up in >>>> other hardware and will be needed for ACPI etc going forward. >>> >>> We certainly need to make setting delays (with values) generic, no >>> doubt about that. >>> >>> If the large amount of data is not an issue here, we could maybe set >>> each iodelay register as a separate device? Then the binding could >>> be just along the interrupts-extended type binding: >>> >>> foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; >>> >>> Where the 0x18c is the instance offset of the register within the >>> ti,dra7-iodelay compatible controller. >> >> if mmc2_pins_default point at pins for mmc pin configuration for >> control_core (pinctrl-single), are you proposing the following? >> >> 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 */ >> >; >> }; > > Yeah so existing pinctrl-single binding above, with additional iodelay > binding below.. > >> &mmc2 { >> ... >> pinctrl-1 = >> &mmc2_pins_default, /* points to mmc control core pins */ >> <&iodelay 0x18c A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A19_IN */ >> <&iodelay 0x1a4 A_DELAY(265) | G_DELAY(360)>, /* CFG_GPMC_A20_IN */ >> <&iodelay 0x1b0 A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A21_IN */ >> <&iodelay 0x1bc A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A22_IN */ >> <&iodelay 0x1c8 A_DELAY(287) | G_DELAY(420)>, /* CFG_GPMC_A23_IN */ >> <&iodelay 0x1d4 A_DELAY(144) | G_DELAY(240)>, /* CFG_GPMC_A24_IN */ >> <&iodelay 0x1e0 A_DELAY(0) | G_DELAY(0)>, /* CFG_GPMC_A25_IN */ >> <&iodelay 0x1ec A_DELAY(120) | G_DELAY(0)>, /* CFG_GPMC_A26_IN */ >> <&iodelay 0x1f8 A_DELAY(120) | G_DELAY(180)>, /* CFG_GPMC_A27_IN */ >> <&iodelay 0x360 A_DELAY(0) | G_DELAY(0)>; /* CFG_GPMC_CS1_IN */ >> >> I have to check if we are capable of parsing that. but if that is the >> approach chosen, I suppose we might be able to figure something, I >> suppose.. > > Yes except I'd make use of some kind of #pinctrl-cells here just like > interrupt controller has #interrupt-cells. Then you can have the values > seprate and the controller knows what to do with them based on the > compatible flag and #pinctrl-cells. Something like the following I suppose, where pinctrl-cells is optional? dra7_pmx_core: pinmux at 4a003400 { compatible = "ti,dra7-padconf", "pinctrl-single"; reg = <0x4a003400 0x0464>; #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; interrupt-controller; pinctrl-single,register-width = <32>; pinctrl-single,function-mask = <0x3fffffff>; }; dra7_iodelay_core: padconf at 4844a000 { compatible = "ti,dra7-iodelay"; reg = <0x4844a000 0x0d1c>; #address-cells = <1>; #size-cells = <0>; #pinctrl-cells = <2>; }; Linus, I hope you are ok with the above? -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-10 18:33 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-10 18:33 UTC (permalink / raw) To: Tony Lindgren Cc: Linus Walleij, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On 03/10/2015 12:31 PM, Tony Lindgren wrote: > * Nishanth Menon <nm@ti.com> [150310 10:25]: >> On 03/10/2015 10:33 AM, Tony Lindgren wrote: >>> * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: >>>> On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: >>>> >>>>> +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 */ >>>>> + >; >>>>> + }; >>>>> +}; >>>> >>>> But wait. >>>> >>>> The promise when we merged pinctrl-single was that this driver was to be used >>>> when the system had a one-register-per-pin layout and it was easy to do device >>>> trees based on that. >>>> >>>> We were very reluctant to accept that even though we didn't even have the >>>> generic pin control bindings in place, the argument being that the driver >>>> should know the detailed register layout, it should not be described in the >>>> device tree. We eventually caved in and accepted it as an exception. >>> >>> Hey let's get few things straight here. There's nothing stopping the >>> driver from knowing a detailed register layout with the pinctrl-single >>> binding. This can be very easily done based on the compatible flag and >>> match data as needed and check the values provided. >>> >>> And let's also recap the reasons for the pinctrl-single binding. The >>> the main reason for the pinctrl-single binding is to avoid mapping >>> individual register bits to device tree compatible string properties. >>> >>> Imagine doing that for hundreds of similar registers. Believe me, I tried >>> using device tree properties initially and it just sucked big time. For >>> larger amounts of dts data, it's best to stick to numeric values and >>> phandles in the device tree data and rely on the preprocessor for >>> getting the values right. >>> >>> Finally, we don't want to build databases into the kernel drivers for >>> every SoC. We certainly have plenty fo those already. >>> >>>> Since this pin controller is not using pinctrl-single it does not enjoy that >>>> privilege and you need to explain why this pin controller cannot use the >>>> generic bindings like so many other pin controllers have since started >>>> to do. ("It is in the same SoC" is not an acceptable argument.) >>>> >>>> What is wrong with this: >>>> Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt >>> >>> Nishanth, care to explain your reasons for using pinctrl-single binding >>> here vs the generic binding? Is the amount of string parsing with the >>> data an issue here? >> >> Wrong option chosen, I suppose :( - alright, lets discuss the alternative. > > Heh well now we know :) > >>>> We can add generic delay bindings to the list, it even seems like >>>> a good idea to do so, as it is likely something that will come up in >>>> other hardware and will be needed for ACPI etc going forward. >>> >>> We certainly need to make setting delays (with values) generic, no >>> doubt about that. >>> >>> If the large amount of data is not an issue here, we could maybe set >>> each iodelay register as a separate device? Then the binding could >>> be just along the interrupts-extended type binding: >>> >>> foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; >>> >>> Where the 0x18c is the instance offset of the register within the >>> ti,dra7-iodelay compatible controller. >> >> if mmc2_pins_default point at pins for mmc pin configuration for >> control_core (pinctrl-single), are you proposing the following? >> >> 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 */ >> >; >> }; > > Yeah so existing pinctrl-single binding above, with additional iodelay > binding below.. > >> &mmc2 { >> ... >> pinctrl-1 = >> &mmc2_pins_default, /* points to mmc control core pins */ >> <&iodelay 0x18c A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A19_IN */ >> <&iodelay 0x1a4 A_DELAY(265) | G_DELAY(360)>, /* CFG_GPMC_A20_IN */ >> <&iodelay 0x1b0 A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A21_IN */ >> <&iodelay 0x1bc A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A22_IN */ >> <&iodelay 0x1c8 A_DELAY(287) | G_DELAY(420)>, /* CFG_GPMC_A23_IN */ >> <&iodelay 0x1d4 A_DELAY(144) | G_DELAY(240)>, /* CFG_GPMC_A24_IN */ >> <&iodelay 0x1e0 A_DELAY(0) | G_DELAY(0)>, /* CFG_GPMC_A25_IN */ >> <&iodelay 0x1ec A_DELAY(120) | G_DELAY(0)>, /* CFG_GPMC_A26_IN */ >> <&iodelay 0x1f8 A_DELAY(120) | G_DELAY(180)>, /* CFG_GPMC_A27_IN */ >> <&iodelay 0x360 A_DELAY(0) | G_DELAY(0)>; /* CFG_GPMC_CS1_IN */ >> >> I have to check if we are capable of parsing that. but if that is the >> approach chosen, I suppose we might be able to figure something, I >> suppose.. > > Yes except I'd make use of some kind of #pinctrl-cells here just like > interrupt controller has #interrupt-cells. Then you can have the values > seprate and the controller knows what to do with them based on the > compatible flag and #pinctrl-cells. Something like the following I suppose, where pinctrl-cells is optional? dra7_pmx_core: pinmux@4a003400 { compatible = "ti,dra7-padconf", "pinctrl-single"; reg = <0x4a003400 0x0464>; #address-cells = <1>; #size-cells = <0>; #interrupt-cells = <1>; interrupt-controller; pinctrl-single,register-width = <32>; pinctrl-single,function-mask = <0x3fffffff>; }; dra7_iodelay_core: padconf@4844a000 { compatible = "ti,dra7-iodelay"; reg = <0x4844a000 0x0d1c>; #address-cells = <1>; #size-cells = <0>; #pinctrl-cells = <2>; }; Linus, I hope you are ok with the above? -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration 2015-03-10 18:33 ` Nishanth Menon (?) @ 2015-03-10 19:20 ` Nishanth Menon -1 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-10 19:20 UTC (permalink / raw) To: Tony Lindgren Cc: Linus Walleij, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On 03/10/2015 01:33 PM, Nishanth Menon wrote: > On 03/10/2015 12:31 PM, Tony Lindgren wrote: >> * Nishanth Menon <nm@ti.com> [150310 10:25]: >>> On 03/10/2015 10:33 AM, Tony Lindgren wrote: >>>> * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: >>>>> On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: >>>>> >>>>>> +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 */ >>>>>> + >; >>>>>> + }; >>>>>> +}; >>>>> >>>>> But wait. >>>>> >>>>> The promise when we merged pinctrl-single was that this driver was to be used >>>>> when the system had a one-register-per-pin layout and it was easy to do device >>>>> trees based on that. >>>>> >>>>> We were very reluctant to accept that even though we didn't even have the >>>>> generic pin control bindings in place, the argument being that the driver >>>>> should know the detailed register layout, it should not be described in the >>>>> device tree. We eventually caved in and accepted it as an exception. >>>> >>>> Hey let's get few things straight here. There's nothing stopping the >>>> driver from knowing a detailed register layout with the pinctrl-single >>>> binding. This can be very easily done based on the compatible flag and >>>> match data as needed and check the values provided. >>>> >>>> And let's also recap the reasons for the pinctrl-single binding. The >>>> the main reason for the pinctrl-single binding is to avoid mapping >>>> individual register bits to device tree compatible string properties. >>>> >>>> Imagine doing that for hundreds of similar registers. Believe me, I tried >>>> using device tree properties initially and it just sucked big time. For >>>> larger amounts of dts data, it's best to stick to numeric values and >>>> phandles in the device tree data and rely on the preprocessor for >>>> getting the values right. >>>> >>>> Finally, we don't want to build databases into the kernel drivers for >>>> every SoC. We certainly have plenty fo those already. >>>> >>>>> Since this pin controller is not using pinctrl-single it does not enjoy that >>>>> privilege and you need to explain why this pin controller cannot use the >>>>> generic bindings like so many other pin controllers have since started >>>>> to do. ("It is in the same SoC" is not an acceptable argument.) >>>>> >>>>> What is wrong with this: >>>>> Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt >>>> >>>> Nishanth, care to explain your reasons for using pinctrl-single binding >>>> here vs the generic binding? Is the amount of string parsing with the >>>> data an issue here? >>> >>> Wrong option chosen, I suppose :( - alright, lets discuss the alternative. >> >> Heh well now we know :) >> >>>>> We can add generic delay bindings to the list, it even seems like >>>>> a good idea to do so, as it is likely something that will come up in >>>>> other hardware and will be needed for ACPI etc going forward. >>>> >>>> We certainly need to make setting delays (with values) generic, no >>>> doubt about that. >>>> >>>> If the large amount of data is not an issue here, we could maybe set >>>> each iodelay register as a separate device? Then the binding could >>>> be just along the interrupts-extended type binding: >>>> >>>> foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; >>>> >>>> Where the 0x18c is the instance offset of the register within the >>>> ti,dra7-iodelay compatible controller. >>> >>> if mmc2_pins_default point at pins for mmc pin configuration for >>> control_core (pinctrl-single), are you proposing the following? >>> >>> 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 */ >>> >; >>> }; >> >> Yeah so existing pinctrl-single binding above, with additional iodelay >> binding below.. >> >>> &mmc2 { >>> ... >>> pinctrl-1 = >>> &mmc2_pins_default, /* points to mmc control core pins */ >>> <&iodelay 0x18c A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A19_IN */ >>> <&iodelay 0x1a4 A_DELAY(265) | G_DELAY(360)>, /* CFG_GPMC_A20_IN */ >>> <&iodelay 0x1b0 A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A21_IN */ >>> <&iodelay 0x1bc A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A22_IN */ >>> <&iodelay 0x1c8 A_DELAY(287) | G_DELAY(420)>, /* CFG_GPMC_A23_IN */ >>> <&iodelay 0x1d4 A_DELAY(144) | G_DELAY(240)>, /* CFG_GPMC_A24_IN */ >>> <&iodelay 0x1e0 A_DELAY(0) | G_DELAY(0)>, /* CFG_GPMC_A25_IN */ >>> <&iodelay 0x1ec A_DELAY(120) | G_DELAY(0)>, /* CFG_GPMC_A26_IN */ >>> <&iodelay 0x1f8 A_DELAY(120) | G_DELAY(180)>, /* CFG_GPMC_A27_IN */ >>> <&iodelay 0x360 A_DELAY(0) | G_DELAY(0)>; /* CFG_GPMC_CS1_IN */ >>> >>> I have to check if we are capable of parsing that. but if that is the >>> approach chosen, I suppose we might be able to figure something, I >>> suppose.. >> >> Yes except I'd make use of some kind of #pinctrl-cells here just like >> interrupt controller has #interrupt-cells. Then you can have the values >> seprate and the controller knows what to do with them based on the >> compatible flag and #pinctrl-cells. > > Something like the following I suppose, where pinctrl-cells is optional? > > dra7_pmx_core: pinmux@4a003400 { > compatible = "ti,dra7-padconf", "pinctrl-single"; > reg = <0x4a003400 0x0464>; > #address-cells = <1>; > #size-cells = <0>; > #interrupt-cells = <1>; > interrupt-controller; > pinctrl-single,register-width = <32>; > pinctrl-single,function-mask = <0x3fffffff>; > }; > > dra7_iodelay_core: padconf@4844a000 { > compatible = "ti,dra7-iodelay"; > reg = <0x4844a000 0x0d1c>; > #address-cells = <1>; > #size-cells = <0>; > #pinctrl-cells = <2>; > }; > > Linus, > > I hope you are ok with the above? > a and g delays are in pico seconds parsed by iodelay driver, so, revising proposal (and dropping the macros) to something as follows: pinctrl-1 = &mmc2_pins_default, /* points to mmc control core pins */ <&iodelay IODELAY_OFFSET(0x18c) 0 120>, /* CFG_GPMC_A19_IN */ <&iodelay IODELAY_OFFSET(0x1a4) 265 360>, /* CFG_GPMC_A20_IN */ <&iodelay IODELAY_OFFSET(0x1b0) 0 120>, /* CFG_GPMC_A21_IN */ <&iodelay IODELAY_OFFSET(0x1bc) 0 120>, /* CFG_GPMC_A22_IN */ <&iodelay IODELAY_OFFSET(0x1c8) 287 420>, /* CFG_GPMC_A23_IN */ <&iodelay IODELAY_OFFSET(0x1d4) 144 240>, /* CFG_GPMC_A24_IN */ <&iodelay IODELAY_OFFSET(0x1e0) 0 0>, /* CFG_GPMC_A25_IN */ <&iodelay IODELAY_OFFSET(0x1ec) 120 0>, /* CFG_GPMC_A26_IN */ <&iodelay IODELAY_OFFSET(0x1f8) 120 180>, /* CFG_GPMC_A27_IN */ <&iodelay IODELAY_OFFSET(0x360) 0 0>; /* CFG_GPMC_CS1_IN */ it might just need us to define the right parse path etc.. but should let us scale with other phandle implementations as well. offsets are computed by a macro IODELAY_OFFSET() -> just throwing in an example set of values for illustration here.. but just to get the idea. Will be nice to have some comments before I go down this path. Thanks for helping review this to a reasonable direction. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-10 19:20 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-10 19:20 UTC (permalink / raw) To: linux-arm-kernel On 03/10/2015 01:33 PM, Nishanth Menon wrote: > On 03/10/2015 12:31 PM, Tony Lindgren wrote: >> * Nishanth Menon <nm@ti.com> [150310 10:25]: >>> On 03/10/2015 10:33 AM, Tony Lindgren wrote: >>>> * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: >>>>> On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: >>>>> >>>>>> +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 */ >>>>>> + >; >>>>>> + }; >>>>>> +}; >>>>> >>>>> But wait. >>>>> >>>>> The promise when we merged pinctrl-single was that this driver was to be used >>>>> when the system had a one-register-per-pin layout and it was easy to do device >>>>> trees based on that. >>>>> >>>>> We were very reluctant to accept that even though we didn't even have the >>>>> generic pin control bindings in place, the argument being that the driver >>>>> should know the detailed register layout, it should not be described in the >>>>> device tree. We eventually caved in and accepted it as an exception. >>>> >>>> Hey let's get few things straight here. There's nothing stopping the >>>> driver from knowing a detailed register layout with the pinctrl-single >>>> binding. This can be very easily done based on the compatible flag and >>>> match data as needed and check the values provided. >>>> >>>> And let's also recap the reasons for the pinctrl-single binding. The >>>> the main reason for the pinctrl-single binding is to avoid mapping >>>> individual register bits to device tree compatible string properties. >>>> >>>> Imagine doing that for hundreds of similar registers. Believe me, I tried >>>> using device tree properties initially and it just sucked big time. For >>>> larger amounts of dts data, it's best to stick to numeric values and >>>> phandles in the device tree data and rely on the preprocessor for >>>> getting the values right. >>>> >>>> Finally, we don't want to build databases into the kernel drivers for >>>> every SoC. We certainly have plenty fo those already. >>>> >>>>> Since this pin controller is not using pinctrl-single it does not enjoy that >>>>> privilege and you need to explain why this pin controller cannot use the >>>>> generic bindings like so many other pin controllers have since started >>>>> to do. ("It is in the same SoC" is not an acceptable argument.) >>>>> >>>>> What is wrong with this: >>>>> Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt >>>> >>>> Nishanth, care to explain your reasons for using pinctrl-single binding >>>> here vs the generic binding? Is the amount of string parsing with the >>>> data an issue here? >>> >>> Wrong option chosen, I suppose :( - alright, lets discuss the alternative. >> >> Heh well now we know :) >> >>>>> We can add generic delay bindings to the list, it even seems like >>>>> a good idea to do so, as it is likely something that will come up in >>>>> other hardware and will be needed for ACPI etc going forward. >>>> >>>> We certainly need to make setting delays (with values) generic, no >>>> doubt about that. >>>> >>>> If the large amount of data is not an issue here, we could maybe set >>>> each iodelay register as a separate device? Then the binding could >>>> be just along the interrupts-extended type binding: >>>> >>>> foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; >>>> >>>> Where the 0x18c is the instance offset of the register within the >>>> ti,dra7-iodelay compatible controller. >>> >>> if mmc2_pins_default point at pins for mmc pin configuration for >>> control_core (pinctrl-single), are you proposing the following? >>> >>> 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 */ >>> >; >>> }; >> >> Yeah so existing pinctrl-single binding above, with additional iodelay >> binding below.. >> >>> &mmc2 { >>> ... >>> pinctrl-1 = >>> &mmc2_pins_default, /* points to mmc control core pins */ >>> <&iodelay 0x18c A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A19_IN */ >>> <&iodelay 0x1a4 A_DELAY(265) | G_DELAY(360)>, /* CFG_GPMC_A20_IN */ >>> <&iodelay 0x1b0 A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A21_IN */ >>> <&iodelay 0x1bc A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A22_IN */ >>> <&iodelay 0x1c8 A_DELAY(287) | G_DELAY(420)>, /* CFG_GPMC_A23_IN */ >>> <&iodelay 0x1d4 A_DELAY(144) | G_DELAY(240)>, /* CFG_GPMC_A24_IN */ >>> <&iodelay 0x1e0 A_DELAY(0) | G_DELAY(0)>, /* CFG_GPMC_A25_IN */ >>> <&iodelay 0x1ec A_DELAY(120) | G_DELAY(0)>, /* CFG_GPMC_A26_IN */ >>> <&iodelay 0x1f8 A_DELAY(120) | G_DELAY(180)>, /* CFG_GPMC_A27_IN */ >>> <&iodelay 0x360 A_DELAY(0) | G_DELAY(0)>; /* CFG_GPMC_CS1_IN */ >>> >>> I have to check if we are capable of parsing that. but if that is the >>> approach chosen, I suppose we might be able to figure something, I >>> suppose.. >> >> Yes except I'd make use of some kind of #pinctrl-cells here just like >> interrupt controller has #interrupt-cells. Then you can have the values >> seprate and the controller knows what to do with them based on the >> compatible flag and #pinctrl-cells. > > Something like the following I suppose, where pinctrl-cells is optional? > > dra7_pmx_core: pinmux at 4a003400 { > compatible = "ti,dra7-padconf", "pinctrl-single"; > reg = <0x4a003400 0x0464>; > #address-cells = <1>; > #size-cells = <0>; > #interrupt-cells = <1>; > interrupt-controller; > pinctrl-single,register-width = <32>; > pinctrl-single,function-mask = <0x3fffffff>; > }; > > dra7_iodelay_core: padconf at 4844a000 { > compatible = "ti,dra7-iodelay"; > reg = <0x4844a000 0x0d1c>; > #address-cells = <1>; > #size-cells = <0>; > #pinctrl-cells = <2>; > }; > > Linus, > > I hope you are ok with the above? > a and g delays are in pico seconds parsed by iodelay driver, so, revising proposal (and dropping the macros) to something as follows: pinctrl-1 = &mmc2_pins_default, /* points to mmc control core pins */ <&iodelay IODELAY_OFFSET(0x18c) 0 120>, /* CFG_GPMC_A19_IN */ <&iodelay IODELAY_OFFSET(0x1a4) 265 360>, /* CFG_GPMC_A20_IN */ <&iodelay IODELAY_OFFSET(0x1b0) 0 120>, /* CFG_GPMC_A21_IN */ <&iodelay IODELAY_OFFSET(0x1bc) 0 120>, /* CFG_GPMC_A22_IN */ <&iodelay IODELAY_OFFSET(0x1c8) 287 420>, /* CFG_GPMC_A23_IN */ <&iodelay IODELAY_OFFSET(0x1d4) 144 240>, /* CFG_GPMC_A24_IN */ <&iodelay IODELAY_OFFSET(0x1e0) 0 0>, /* CFG_GPMC_A25_IN */ <&iodelay IODELAY_OFFSET(0x1ec) 120 0>, /* CFG_GPMC_A26_IN */ <&iodelay IODELAY_OFFSET(0x1f8) 120 180>, /* CFG_GPMC_A27_IN */ <&iodelay IODELAY_OFFSET(0x360) 0 0>; /* CFG_GPMC_CS1_IN */ it might just need us to define the right parse path etc.. but should let us scale with other phandle implementations as well. offsets are computed by a macro IODELAY_OFFSET() -> just throwing in an example set of values for illustration here.. but just to get the idea. Will be nice to have some comments before I go down this path. Thanks for helping review this to a reasonable direction. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-10 19:20 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-10 19:20 UTC (permalink / raw) To: Tony Lindgren Cc: Linus Walleij, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On 03/10/2015 01:33 PM, Nishanth Menon wrote: > On 03/10/2015 12:31 PM, Tony Lindgren wrote: >> * Nishanth Menon <nm@ti.com> [150310 10:25]: >>> On 03/10/2015 10:33 AM, Tony Lindgren wrote: >>>> * Linus Walleij <linus.walleij@linaro.org> [150310 03:39]: >>>>> On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: >>>>> >>>>>> +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 */ >>>>>> + >; >>>>>> + }; >>>>>> +}; >>>>> >>>>> But wait. >>>>> >>>>> The promise when we merged pinctrl-single was that this driver was to be used >>>>> when the system had a one-register-per-pin layout and it was easy to do device >>>>> trees based on that. >>>>> >>>>> We were very reluctant to accept that even though we didn't even have the >>>>> generic pin control bindings in place, the argument being that the driver >>>>> should know the detailed register layout, it should not be described in the >>>>> device tree. We eventually caved in and accepted it as an exception. >>>> >>>> Hey let's get few things straight here. There's nothing stopping the >>>> driver from knowing a detailed register layout with the pinctrl-single >>>> binding. This can be very easily done based on the compatible flag and >>>> match data as needed and check the values provided. >>>> >>>> And let's also recap the reasons for the pinctrl-single binding. The >>>> the main reason for the pinctrl-single binding is to avoid mapping >>>> individual register bits to device tree compatible string properties. >>>> >>>> Imagine doing that for hundreds of similar registers. Believe me, I tried >>>> using device tree properties initially and it just sucked big time. For >>>> larger amounts of dts data, it's best to stick to numeric values and >>>> phandles in the device tree data and rely on the preprocessor for >>>> getting the values right. >>>> >>>> Finally, we don't want to build databases into the kernel drivers for >>>> every SoC. We certainly have plenty fo those already. >>>> >>>>> Since this pin controller is not using pinctrl-single it does not enjoy that >>>>> privilege and you need to explain why this pin controller cannot use the >>>>> generic bindings like so many other pin controllers have since started >>>>> to do. ("It is in the same SoC" is not an acceptable argument.) >>>>> >>>>> What is wrong with this: >>>>> Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt >>>> >>>> Nishanth, care to explain your reasons for using pinctrl-single binding >>>> here vs the generic binding? Is the amount of string parsing with the >>>> data an issue here? >>> >>> Wrong option chosen, I suppose :( - alright, lets discuss the alternative. >> >> Heh well now we know :) >> >>>>> We can add generic delay bindings to the list, it even seems like >>>>> a good idea to do so, as it is likely something that will come up in >>>>> other hardware and will be needed for ACPI etc going forward. >>>> >>>> We certainly need to make setting delays (with values) generic, no >>>> doubt about that. >>>> >>>> If the large amount of data is not an issue here, we could maybe set >>>> each iodelay register as a separate device? Then the binding could >>>> be just along the interrupts-extended type binding: >>>> >>>> foo = <&bar 0x18c A_DELAY(0) G_DELAY(120)>; >>>> >>>> Where the 0x18c is the instance offset of the register within the >>>> ti,dra7-iodelay compatible controller. >>> >>> if mmc2_pins_default point at pins for mmc pin configuration for >>> control_core (pinctrl-single), are you proposing the following? >>> >>> 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 */ >>> >; >>> }; >> >> Yeah so existing pinctrl-single binding above, with additional iodelay >> binding below.. >> >>> &mmc2 { >>> ... >>> pinctrl-1 = >>> &mmc2_pins_default, /* points to mmc control core pins */ >>> <&iodelay 0x18c A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A19_IN */ >>> <&iodelay 0x1a4 A_DELAY(265) | G_DELAY(360)>, /* CFG_GPMC_A20_IN */ >>> <&iodelay 0x1b0 A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A21_IN */ >>> <&iodelay 0x1bc A_DELAY(0) | G_DELAY(120)>, /* CFG_GPMC_A22_IN */ >>> <&iodelay 0x1c8 A_DELAY(287) | G_DELAY(420)>, /* CFG_GPMC_A23_IN */ >>> <&iodelay 0x1d4 A_DELAY(144) | G_DELAY(240)>, /* CFG_GPMC_A24_IN */ >>> <&iodelay 0x1e0 A_DELAY(0) | G_DELAY(0)>, /* CFG_GPMC_A25_IN */ >>> <&iodelay 0x1ec A_DELAY(120) | G_DELAY(0)>, /* CFG_GPMC_A26_IN */ >>> <&iodelay 0x1f8 A_DELAY(120) | G_DELAY(180)>, /* CFG_GPMC_A27_IN */ >>> <&iodelay 0x360 A_DELAY(0) | G_DELAY(0)>; /* CFG_GPMC_CS1_IN */ >>> >>> I have to check if we are capable of parsing that. but if that is the >>> approach chosen, I suppose we might be able to figure something, I >>> suppose.. >> >> Yes except I'd make use of some kind of #pinctrl-cells here just like >> interrupt controller has #interrupt-cells. Then you can have the values >> seprate and the controller knows what to do with them based on the >> compatible flag and #pinctrl-cells. > > Something like the following I suppose, where pinctrl-cells is optional? > > dra7_pmx_core: pinmux@4a003400 { > compatible = "ti,dra7-padconf", "pinctrl-single"; > reg = <0x4a003400 0x0464>; > #address-cells = <1>; > #size-cells = <0>; > #interrupt-cells = <1>; > interrupt-controller; > pinctrl-single,register-width = <32>; > pinctrl-single,function-mask = <0x3fffffff>; > }; > > dra7_iodelay_core: padconf@4844a000 { > compatible = "ti,dra7-iodelay"; > reg = <0x4844a000 0x0d1c>; > #address-cells = <1>; > #size-cells = <0>; > #pinctrl-cells = <2>; > }; > > Linus, > > I hope you are ok with the above? > a and g delays are in pico seconds parsed by iodelay driver, so, revising proposal (and dropping the macros) to something as follows: pinctrl-1 = &mmc2_pins_default, /* points to mmc control core pins */ <&iodelay IODELAY_OFFSET(0x18c) 0 120>, /* CFG_GPMC_A19_IN */ <&iodelay IODELAY_OFFSET(0x1a4) 265 360>, /* CFG_GPMC_A20_IN */ <&iodelay IODELAY_OFFSET(0x1b0) 0 120>, /* CFG_GPMC_A21_IN */ <&iodelay IODELAY_OFFSET(0x1bc) 0 120>, /* CFG_GPMC_A22_IN */ <&iodelay IODELAY_OFFSET(0x1c8) 287 420>, /* CFG_GPMC_A23_IN */ <&iodelay IODELAY_OFFSET(0x1d4) 144 240>, /* CFG_GPMC_A24_IN */ <&iodelay IODELAY_OFFSET(0x1e0) 0 0>, /* CFG_GPMC_A25_IN */ <&iodelay IODELAY_OFFSET(0x1ec) 120 0>, /* CFG_GPMC_A26_IN */ <&iodelay IODELAY_OFFSET(0x1f8) 120 180>, /* CFG_GPMC_A27_IN */ <&iodelay IODELAY_OFFSET(0x360) 0 0>; /* CFG_GPMC_CS1_IN */ it might just need us to define the right parse path etc.. but should let us scale with other phandle implementations as well. offsets are computed by a macro IODELAY_OFFSET() -> just throwing in an example set of values for illustration here.. but just to get the idea. Will be nice to have some comments before I go down this path. Thanks for helping review this to a reasonable direction. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration 2015-03-10 18:33 ` Nishanth Menon (?) @ 2015-03-18 1:30 ` Linus Walleij -1 siblings, 0 replies; 74+ messages in thread From: Linus Walleij @ 2015-03-18 1:30 UTC (permalink / raw) To: Nishanth Menon Cc: Tony Lindgren, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On Tue, Mar 10, 2015 at 7:33 PM, Nishanth Menon <nm@ti.com> wrote: > On 03/10/2015 12:31 PM, Tony Lindgren wrote: >> Yes except I'd make use of some kind of #pinctrl-cells here just like >> interrupt controller has #interrupt-cells. Then you can have the values >> seprate and the controller knows what to do with them based on the >> compatible flag and #pinctrl-cells. > > Something like the following I suppose, where pinctrl-cells is optional? > > dra7_pmx_core: pinmux@4a003400 { > compatible = "ti,dra7-padconf", "pinctrl-single"; > reg = <0x4a003400 0x0464>; > #address-cells = <1>; > #size-cells = <0>; > #interrupt-cells = <1>; > interrupt-controller; > pinctrl-single,register-width = <32>; > pinctrl-single,function-mask = <0x3fffffff>; > }; > > dra7_iodelay_core: padconf@4844a000 { > compatible = "ti,dra7-iodelay"; > reg = <0x4844a000 0x0d1c>; > #address-cells = <1>; > #size-cells = <0>; > #pinctrl-cells = <2>; > }; > > Linus, > > I hope you are ok with the above? Hm depends on where the documentation hits I guess? Such a generic cell count property has to be to the generic pinctrl-bindings.txt document if I read it right. Overall I guess this will be acceptable but you really need to reuse some more code between this driver and pinctrl-single.c if I read it right. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-18 1:30 ` Linus Walleij 0 siblings, 0 replies; 74+ messages in thread From: Linus Walleij @ 2015-03-18 1:30 UTC (permalink / raw) To: linux-arm-kernel On Tue, Mar 10, 2015 at 7:33 PM, Nishanth Menon <nm@ti.com> wrote: > On 03/10/2015 12:31 PM, Tony Lindgren wrote: >> Yes except I'd make use of some kind of #pinctrl-cells here just like >> interrupt controller has #interrupt-cells. Then you can have the values >> seprate and the controller knows what to do with them based on the >> compatible flag and #pinctrl-cells. > > Something like the following I suppose, where pinctrl-cells is optional? > > dra7_pmx_core: pinmux at 4a003400 { > compatible = "ti,dra7-padconf", "pinctrl-single"; > reg = <0x4a003400 0x0464>; > #address-cells = <1>; > #size-cells = <0>; > #interrupt-cells = <1>; > interrupt-controller; > pinctrl-single,register-width = <32>; > pinctrl-single,function-mask = <0x3fffffff>; > }; > > dra7_iodelay_core: padconf at 4844a000 { > compatible = "ti,dra7-iodelay"; > reg = <0x4844a000 0x0d1c>; > #address-cells = <1>; > #size-cells = <0>; > #pinctrl-cells = <2>; > }; > > Linus, > > I hope you are ok with the above? Hm depends on where the documentation hits I guess? Such a generic cell count property has to be to the generic pinctrl-bindings.txt document if I read it right. Overall I guess this will be acceptable but you really need to reuse some more code between this driver and pinctrl-single.c if I read it right. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-18 1:30 ` Linus Walleij 0 siblings, 0 replies; 74+ messages in thread From: Linus Walleij @ 2015-03-18 1:30 UTC (permalink / raw) To: Nishanth Menon Cc: Tony Lindgren, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On Tue, Mar 10, 2015 at 7:33 PM, Nishanth Menon <nm@ti.com> wrote: > On 03/10/2015 12:31 PM, Tony Lindgren wrote: >> Yes except I'd make use of some kind of #pinctrl-cells here just like >> interrupt controller has #interrupt-cells. Then you can have the values >> seprate and the controller knows what to do with them based on the >> compatible flag and #pinctrl-cells. > > Something like the following I suppose, where pinctrl-cells is optional? > > dra7_pmx_core: pinmux@4a003400 { > compatible = "ti,dra7-padconf", "pinctrl-single"; > reg = <0x4a003400 0x0464>; > #address-cells = <1>; > #size-cells = <0>; > #interrupt-cells = <1>; > interrupt-controller; > pinctrl-single,register-width = <32>; > pinctrl-single,function-mask = <0x3fffffff>; > }; > > dra7_iodelay_core: padconf@4844a000 { > compatible = "ti,dra7-iodelay"; > reg = <0x4844a000 0x0d1c>; > #address-cells = <1>; > #size-cells = <0>; > #pinctrl-cells = <2>; > }; > > Linus, > > I hope you are ok with the above? Hm depends on where the documentation hits I guess? Such a generic cell count property has to be to the generic pinctrl-bindings.txt document if I read it right. Overall I guess this will be acceptable but you really need to reuse some more code between this driver and pinctrl-single.c if I read it right. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration 2015-03-18 1:30 ` Linus Walleij (?) @ 2015-03-18 1:41 ` Tony Lindgren -1 siblings, 0 replies; 74+ messages in thread From: Tony Lindgren @ 2015-03-18 1:41 UTC (permalink / raw) To: Linus Walleij Cc: Nishanth Menon, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla * Linus Walleij <linus.walleij@linaro.org> [150317 18:31]: > On Tue, Mar 10, 2015 at 7:33 PM, Nishanth Menon <nm@ti.com> wrote: > > On 03/10/2015 12:31 PM, Tony Lindgren wrote: > > >> Yes except I'd make use of some kind of #pinctrl-cells here just like > >> interrupt controller has #interrupt-cells. Then you can have the values > >> seprate and the controller knows what to do with them based on the > >> compatible flag and #pinctrl-cells. > > > > Something like the following I suppose, where pinctrl-cells is optional? > > > > dra7_pmx_core: pinmux@4a003400 { > > compatible = "ti,dra7-padconf", "pinctrl-single"; > > reg = <0x4a003400 0x0464>; > > #address-cells = <1>; > > #size-cells = <0>; > > #interrupt-cells = <1>; > > interrupt-controller; > > pinctrl-single,register-width = <32>; > > pinctrl-single,function-mask = <0x3fffffff>; > > }; > > > > dra7_iodelay_core: padconf@4844a000 { > > compatible = "ti,dra7-iodelay"; > > reg = <0x4844a000 0x0d1c>; > > #address-cells = <1>; > > #size-cells = <0>; > > #pinctrl-cells = <2>; > > }; > > > > Linus, > > > > I hope you are ok with the above? > > Hm depends on where the documentation hits I guess? > > Such a generic cell count property has to be to the generic > pinctrl-bindings.txt document if I read it right. Yeah agreed. I suggest discussing the binding and the generic parsing code for it first :) > Overall I guess this will be acceptable but you really need to > reuse some more code between this driver and pinctrl-single.c > if I read it right. It seems with the generic binding the actual driver should be just the hardware specific code hopefully. Regards, Tony ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-18 1:41 ` Tony Lindgren 0 siblings, 0 replies; 74+ messages in thread From: Tony Lindgren @ 2015-03-18 1:41 UTC (permalink / raw) To: linux-arm-kernel * Linus Walleij <linus.walleij@linaro.org> [150317 18:31]: > On Tue, Mar 10, 2015 at 7:33 PM, Nishanth Menon <nm@ti.com> wrote: > > On 03/10/2015 12:31 PM, Tony Lindgren wrote: > > >> Yes except I'd make use of some kind of #pinctrl-cells here just like > >> interrupt controller has #interrupt-cells. Then you can have the values > >> seprate and the controller knows what to do with them based on the > >> compatible flag and #pinctrl-cells. > > > > Something like the following I suppose, where pinctrl-cells is optional? > > > > dra7_pmx_core: pinmux at 4a003400 { > > compatible = "ti,dra7-padconf", "pinctrl-single"; > > reg = <0x4a003400 0x0464>; > > #address-cells = <1>; > > #size-cells = <0>; > > #interrupt-cells = <1>; > > interrupt-controller; > > pinctrl-single,register-width = <32>; > > pinctrl-single,function-mask = <0x3fffffff>; > > }; > > > > dra7_iodelay_core: padconf at 4844a000 { > > compatible = "ti,dra7-iodelay"; > > reg = <0x4844a000 0x0d1c>; > > #address-cells = <1>; > > #size-cells = <0>; > > #pinctrl-cells = <2>; > > }; > > > > Linus, > > > > I hope you are ok with the above? > > Hm depends on where the documentation hits I guess? > > Such a generic cell count property has to be to the generic > pinctrl-bindings.txt document if I read it right. Yeah agreed. I suggest discussing the binding and the generic parsing code for it first :) > Overall I guess this will be acceptable but you really need to > reuse some more code between this driver and pinctrl-single.c > if I read it right. It seems with the generic binding the actual driver should be just the hardware specific code hopefully. Regards, Tony ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-03-18 1:41 ` Tony Lindgren 0 siblings, 0 replies; 74+ messages in thread From: Tony Lindgren @ 2015-03-18 1:41 UTC (permalink / raw) To: Linus Walleij Cc: Nishanth Menon, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla * Linus Walleij <linus.walleij@linaro.org> [150317 18:31]: > On Tue, Mar 10, 2015 at 7:33 PM, Nishanth Menon <nm@ti.com> wrote: > > On 03/10/2015 12:31 PM, Tony Lindgren wrote: > > >> Yes except I'd make use of some kind of #pinctrl-cells here just like > >> interrupt controller has #interrupt-cells. Then you can have the values > >> seprate and the controller knows what to do with them based on the > >> compatible flag and #pinctrl-cells. > > > > Something like the following I suppose, where pinctrl-cells is optional? > > > > dra7_pmx_core: pinmux@4a003400 { > > compatible = "ti,dra7-padconf", "pinctrl-single"; > > reg = <0x4a003400 0x0464>; > > #address-cells = <1>; > > #size-cells = <0>; > > #interrupt-cells = <1>; > > interrupt-controller; > > pinctrl-single,register-width = <32>; > > pinctrl-single,function-mask = <0x3fffffff>; > > }; > > > > dra7_iodelay_core: padconf@4844a000 { > > compatible = "ti,dra7-iodelay"; > > reg = <0x4844a000 0x0d1c>; > > #address-cells = <1>; > > #size-cells = <0>; > > #pinctrl-cells = <2>; > > }; > > > > Linus, > > > > I hope you are ok with the above? > > Hm depends on where the documentation hits I guess? > > Such a generic cell count property has to be to the generic > pinctrl-bindings.txt document if I read it right. Yeah agreed. I suggest discussing the binding and the generic parsing code for it first :) > Overall I guess this will be acceptable but you really need to > reuse some more code between this driver and pinctrl-single.c > if I read it right. It seems with the generic binding the actual driver should be just the hardware specific code hopefully. Regards, Tony ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration 2015-03-18 1:41 ` Tony Lindgren (?) @ 2015-04-15 1:29 ` Lennart Sorensen -1 siblings, 0 replies; 74+ messages in thread From: Lennart Sorensen @ 2015-04-15 1:29 UTC (permalink / raw) To: Tony Lindgren Cc: Linus Walleij, Nishanth Menon, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On Tue, Mar 17, 2015 at 06:41:51PM -0700, Tony Lindgren wrote: > Yeah agreed. I suggest discussing the binding and the generic > parsing code for it first :) > > It seems with the generic binding the actual driver should be > just the hardware specific code hopefully. Did this thread go anywhere in the last month? I am certainly looking forward to seeing what the resolution is to this, given for our use the boot loader setup is not appealing at all. -- Len Sorensen ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-04-15 1:29 ` Lennart Sorensen 0 siblings, 0 replies; 74+ messages in thread From: Lennart Sorensen @ 2015-04-15 1:29 UTC (permalink / raw) To: linux-arm-kernel On Tue, Mar 17, 2015 at 06:41:51PM -0700, Tony Lindgren wrote: > Yeah agreed. I suggest discussing the binding and the generic > parsing code for it first :) > > It seems with the generic binding the actual driver should be > just the hardware specific code hopefully. Did this thread go anywhere in the last month? I am certainly looking forward to seeing what the resolution is to this, given for our use the boot loader setup is not appealing at all. -- Len Sorensen ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-04-15 1:29 ` Lennart Sorensen 0 siblings, 0 replies; 74+ messages in thread From: Lennart Sorensen @ 2015-04-15 1:29 UTC (permalink / raw) To: Tony Lindgren Cc: Linus Walleij, Nishanth Menon, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On Tue, Mar 17, 2015 at 06:41:51PM -0700, Tony Lindgren wrote: > Yeah agreed. I suggest discussing the binding and the generic > parsing code for it first :) > > It seems with the generic binding the actual driver should be > just the hardware specific code hopefully. Did this thread go anywhere in the last month? I am certainly looking forward to seeing what the resolution is to this, given for our use the boot loader setup is not appealing at all. -- Len Sorensen ^ permalink raw reply [flat|nested] 74+ messages in thread
[parent not found: <20150415012910.GA29560-1wCw9BSqJbv44Nm34jS7GywD8/FfD2ys@public.gmane.org>]
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration 2015-04-15 1:29 ` Lennart Sorensen (?) @ 2015-04-15 16:51 ` Nishanth Menon -1 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-04-15 16:51 UTC (permalink / raw) To: Lennart Sorensen, Tony Lindgren Cc: Linus Walleij, linux-gpio-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, devicetree-u79uwXL29TY76Z2rM5mHXA, Linux-OMAP, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Lokesh Vutla On 04/14/2015 08:29 PM, Lennart Sorensen wrote: > On Tue, Mar 17, 2015 at 06:41:51PM -0700, Tony Lindgren wrote: >> Yeah agreed. I suggest discussing the binding and the generic >> parsing code for it first :) >> >> It seems with the generic binding the actual driver should be >> just the hardware specific code hopefully. > > Did this thread go anywhere in the last month? I am certainly looking > forward to seeing what the resolution is to this, given for our use the > boot loader setup is not appealing at all. > I am yet to post a new revision to this series - few other stuff got in the way. IODelay driver in no way removes the constraint that the SoC architecture has - most of the pins still need to be muxed in bootloader - we cannot escape that. The reasoning for doing the mux in bootloader is independent of the need for iodelay. Reasoning for mux in bootloader is because the mux and pull fields are glitchy - much more than previous generations of TI SoCs and significantly long enough to cause issues depending on the pins being muxed. Reasoning for iodelay is different - it is a hardware block meant to control the timing of signals in a particular signal path to ensure that specification compliance is met. Lets try not to mix the two. -- Regards, Nishanth Menon -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-04-15 16:51 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-04-15 16:51 UTC (permalink / raw) To: linux-arm-kernel On 04/14/2015 08:29 PM, Lennart Sorensen wrote: > On Tue, Mar 17, 2015 at 06:41:51PM -0700, Tony Lindgren wrote: >> Yeah agreed. I suggest discussing the binding and the generic >> parsing code for it first :) >> >> It seems with the generic binding the actual driver should be >> just the hardware specific code hopefully. > > Did this thread go anywhere in the last month? I am certainly looking > forward to seeing what the resolution is to this, given for our use the > boot loader setup is not appealing at all. > I am yet to post a new revision to this series - few other stuff got in the way. IODelay driver in no way removes the constraint that the SoC architecture has - most of the pins still need to be muxed in bootloader - we cannot escape that. The reasoning for doing the mux in bootloader is independent of the need for iodelay. Reasoning for mux in bootloader is because the mux and pull fields are glitchy - much more than previous generations of TI SoCs and significantly long enough to cause issues depending on the pins being muxed. Reasoning for iodelay is different - it is a hardware block meant to control the timing of signals in a particular signal path to ensure that specification compliance is met. Lets try not to mix the two. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-04-15 16:51 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-04-15 16:51 UTC (permalink / raw) To: Lennart Sorensen, Tony Lindgren Cc: Linus Walleij, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On 04/14/2015 08:29 PM, Lennart Sorensen wrote: > On Tue, Mar 17, 2015 at 06:41:51PM -0700, Tony Lindgren wrote: >> Yeah agreed. I suggest discussing the binding and the generic >> parsing code for it first :) >> >> It seems with the generic binding the actual driver should be >> just the hardware specific code hopefully. > > Did this thread go anywhere in the last month? I am certainly looking > forward to seeing what the resolution is to this, given for our use the > boot loader setup is not appealing at all. > I am yet to post a new revision to this series - few other stuff got in the way. IODelay driver in no way removes the constraint that the SoC architecture has - most of the pins still need to be muxed in bootloader - we cannot escape that. The reasoning for doing the mux in bootloader is independent of the need for iodelay. Reasoning for mux in bootloader is because the mux and pull fields are glitchy - much more than previous generations of TI SoCs and significantly long enough to cause issues depending on the pins being muxed. Reasoning for iodelay is different - it is a hardware block meant to control the timing of signals in a particular signal path to ensure that specification compliance is met. Lets try not to mix the two. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration 2015-04-15 16:51 ` Nishanth Menon (?) @ 2015-04-15 18:44 ` Lennart Sorensen -1 siblings, 0 replies; 74+ messages in thread From: Lennart Sorensen @ 2015-04-15 18:44 UTC (permalink / raw) To: Nishanth Menon Cc: Tony Lindgren, Linus Walleij, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On Wed, Apr 15, 2015 at 11:51:32AM -0500, Nishanth Menon wrote: > I am yet to post a new revision to this series - few other stuff got > in the way. IODelay driver in no way removes the constraint that the > SoC architecture has - most of the pins still need to be muxed in > bootloader - we cannot escape that. The reasoning for doing the mux in > bootloader is independent of the need for iodelay. > > Reasoning for mux in bootloader is because the mux and pull fields are > glitchy - much more than previous generations of TI SoCs and > significantly long enough to cause issues depending on the pins being > muxed. Well if we know glitching is NOT an issue on our boards, then we don't have to do anything in the boot loader other than the basic setup for the serial console and emmc and SD, which has always been necesary. I consider moving the mux setup to the bootloader a terrible design and won't go along with it. We make sure all external devices have reset lines being held while the pinmux is being setup, so glitching is a non issue. > Reasoning for iodelay is different - it is a hardware block meant to > control the timing of signals in a particular signal path to ensure > that specification compliance is met. > > Lets try not to mix the two. Well I was told by multiple people from TI that the reason for moving the pinmux setup to the bootloader was because of the iodelay issue, so you will have to get the message made clear within TI then. -- Len Sorensen ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-04-15 18:44 ` Lennart Sorensen 0 siblings, 0 replies; 74+ messages in thread From: Lennart Sorensen @ 2015-04-15 18:44 UTC (permalink / raw) To: linux-arm-kernel On Wed, Apr 15, 2015 at 11:51:32AM -0500, Nishanth Menon wrote: > I am yet to post a new revision to this series - few other stuff got > in the way. IODelay driver in no way removes the constraint that the > SoC architecture has - most of the pins still need to be muxed in > bootloader - we cannot escape that. The reasoning for doing the mux in > bootloader is independent of the need for iodelay. > > Reasoning for mux in bootloader is because the mux and pull fields are > glitchy - much more than previous generations of TI SoCs and > significantly long enough to cause issues depending on the pins being > muxed. Well if we know glitching is NOT an issue on our boards, then we don't have to do anything in the boot loader other than the basic setup for the serial console and emmc and SD, which has always been necesary. I consider moving the mux setup to the bootloader a terrible design and won't go along with it. We make sure all external devices have reset lines being held while the pinmux is being setup, so glitching is a non issue. > Reasoning for iodelay is different - it is a hardware block meant to > control the timing of signals in a particular signal path to ensure > that specification compliance is met. > > Lets try not to mix the two. Well I was told by multiple people from TI that the reason for moving the pinmux setup to the bootloader was because of the iodelay issue, so you will have to get the message made clear within TI then. -- Len Sorensen ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-04-15 18:44 ` Lennart Sorensen 0 siblings, 0 replies; 74+ messages in thread From: Lennart Sorensen @ 2015-04-15 18:44 UTC (permalink / raw) To: Nishanth Menon Cc: Tony Lindgren, Linus Walleij, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On Wed, Apr 15, 2015 at 11:51:32AM -0500, Nishanth Menon wrote: > I am yet to post a new revision to this series - few other stuff got > in the way. IODelay driver in no way removes the constraint that the > SoC architecture has - most of the pins still need to be muxed in > bootloader - we cannot escape that. The reasoning for doing the mux in > bootloader is independent of the need for iodelay. > > Reasoning for mux in bootloader is because the mux and pull fields are > glitchy - much more than previous generations of TI SoCs and > significantly long enough to cause issues depending on the pins being > muxed. Well if we know glitching is NOT an issue on our boards, then we don't have to do anything in the boot loader other than the basic setup for the serial console and emmc and SD, which has always been necesary. I consider moving the mux setup to the bootloader a terrible design and won't go along with it. We make sure all external devices have reset lines being held while the pinmux is being setup, so glitching is a non issue. > Reasoning for iodelay is different - it is a hardware block meant to > control the timing of signals in a particular signal path to ensure > that specification compliance is met. > > Lets try not to mix the two. Well I was told by multiple people from TI that the reason for moving the pinmux setup to the bootloader was because of the iodelay issue, so you will have to get the message made clear within TI then. -- Len Sorensen ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration 2015-04-15 18:44 ` Lennart Sorensen (?) @ 2015-04-15 18:53 ` Nishanth Menon -1 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-04-15 18:53 UTC (permalink / raw) To: Lennart Sorensen Cc: Tony Lindgren, Linus Walleij, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On 04/15/2015 01:44 PM, Lennart Sorensen wrote: > On Wed, Apr 15, 2015 at 11:51:32AM -0500, Nishanth Menon wrote: >> I am yet to post a new revision to this series - few other stuff got >> in the way. IODelay driver in no way removes the constraint that the >> SoC architecture has - most of the pins still need to be muxed in >> bootloader - we cannot escape that. The reasoning for doing the mux in >> bootloader is independent of the need for iodelay. >> >> Reasoning for mux in bootloader is because the mux and pull fields are >> glitchy - much more than previous generations of TI SoCs and >> significantly long enough to cause issues depending on the pins being >> muxed. > > Well if we know glitching is NOT an issue on our boards, then we don't > have to do anything in the boot loader other than the basic setup for > the serial console and emmc and SD, which has always been necesary. > > I consider moving the mux setup to the bootloader a terrible design and > won't go along with it. We make sure all external devices have reset > lines being held while the pinmux is being setup, so glitching is a > non issue. I cannot discuss customer boards on this list - the right forum for TI support is e2e.ti.com or in cases where FAE (Field Applications Engineer) is involved, via appropriate support path. Now, that said, even with personal opinions in place, I have to stick with what the SoC constraints on hand and suggested architecture we have discussed to ensure safe platform operation at least for the platforms we are contributing to. again... muxing in the bootloader IS NOT what this patch is about. If we can stick to the topic in discussion, it is probably more effective. Any improvement suggestions to the code is more than appreciated. >> Reasoning for iodelay is different - it is a hardware block meant to >> control the timing of signals in a particular signal path to ensure >> that specification compliance is met. >> >> Lets try not to mix the two. > > Well I was told by multiple people from TI that the reason for moving > the pinmux setup to the bootloader was because of the iodelay issue, > so you will have to get the message made clear within TI then. > I have passed on this message. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-04-15 18:53 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-04-15 18:53 UTC (permalink / raw) To: linux-arm-kernel On 04/15/2015 01:44 PM, Lennart Sorensen wrote: > On Wed, Apr 15, 2015 at 11:51:32AM -0500, Nishanth Menon wrote: >> I am yet to post a new revision to this series - few other stuff got >> in the way. IODelay driver in no way removes the constraint that the >> SoC architecture has - most of the pins still need to be muxed in >> bootloader - we cannot escape that. The reasoning for doing the mux in >> bootloader is independent of the need for iodelay. >> >> Reasoning for mux in bootloader is because the mux and pull fields are >> glitchy - much more than previous generations of TI SoCs and >> significantly long enough to cause issues depending on the pins being >> muxed. > > Well if we know glitching is NOT an issue on our boards, then we don't > have to do anything in the boot loader other than the basic setup for > the serial console and emmc and SD, which has always been necesary. > > I consider moving the mux setup to the bootloader a terrible design and > won't go along with it. We make sure all external devices have reset > lines being held while the pinmux is being setup, so glitching is a > non issue. I cannot discuss customer boards on this list - the right forum for TI support is e2e.ti.com or in cases where FAE (Field Applications Engineer) is involved, via appropriate support path. Now, that said, even with personal opinions in place, I have to stick with what the SoC constraints on hand and suggested architecture we have discussed to ensure safe platform operation at least for the platforms we are contributing to. again... muxing in the bootloader IS NOT what this patch is about. If we can stick to the topic in discussion, it is probably more effective. Any improvement suggestions to the code is more than appreciated. >> Reasoning for iodelay is different - it is a hardware block meant to >> control the timing of signals in a particular signal path to ensure >> that specification compliance is met. >> >> Lets try not to mix the two. > > Well I was told by multiple people from TI that the reason for moving > the pinmux setup to the bootloader was because of the iodelay issue, > so you will have to get the message made clear within TI then. > I have passed on this message. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration @ 2015-04-15 18:53 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-04-15 18:53 UTC (permalink / raw) To: Lennart Sorensen Cc: Tony Lindgren, Linus Walleij, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On 04/15/2015 01:44 PM, Lennart Sorensen wrote: > On Wed, Apr 15, 2015 at 11:51:32AM -0500, Nishanth Menon wrote: >> I am yet to post a new revision to this series - few other stuff got >> in the way. IODelay driver in no way removes the constraint that the >> SoC architecture has - most of the pins still need to be muxed in >> bootloader - we cannot escape that. The reasoning for doing the mux in >> bootloader is independent of the need for iodelay. >> >> Reasoning for mux in bootloader is because the mux and pull fields are >> glitchy - much more than previous generations of TI SoCs and >> significantly long enough to cause issues depending on the pins being >> muxed. > > Well if we know glitching is NOT an issue on our boards, then we don't > have to do anything in the boot loader other than the basic setup for > the serial console and emmc and SD, which has always been necesary. > > I consider moving the mux setup to the bootloader a terrible design and > won't go along with it. We make sure all external devices have reset > lines being held while the pinmux is being setup, so glitching is a > non issue. I cannot discuss customer boards on this list - the right forum for TI support is e2e.ti.com or in cases where FAE (Field Applications Engineer) is involved, via appropriate support path. Now, that said, even with personal opinions in place, I have to stick with what the SoC constraints on hand and suggested architecture we have discussed to ensure safe platform operation at least for the platforms we are contributing to. again... muxing in the bootloader IS NOT what this patch is about. If we can stick to the topic in discussion, it is probably more effective. Any improvement suggestions to the code is more than appreciated. >> Reasoning for iodelay is different - it is a hardware block meant to >> control the timing of signals in a particular signal path to ensure >> that specification compliance is met. >> >> Lets try not to mix the two. > > Well I was told by multiple people from TI that the reason for moving > the pinmux setup to the bootloader was because of the iodelay issue, > so you will have to get the message made clear within TI then. > I have passed on this message. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver 2015-03-04 0:00 ` Nishanth Menon (?) @ 2015-03-04 0:00 ` Nishanth Menon -1 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-04 0:00 UTC (permalink / raw) To: Tony Lindgren, Linus Walleij Cc: linux-gpio, linux-kernel, devicetree, linux-omap, linux-arm-kernel, Lokesh Vutla, Nishanth Menon SoC family such as DRA7 family of processors have, in addition to the regular muxing of pins (as done by pinctrl-single), an additional hardware module called IODelay which is also expected to be configured. This "IODelay" module has it's own register space that is independent of the control module. It is advocated strongly in TI's official documentation considering the existing design of the DRA7 family of processors during mux or IODelay reconfiguration, there is a potential for a significant glitch which may cause functional impairment to certain hardware. It is hence recommended to do as little of muxing as absolutely necessary without I/O isolation (which can only be done in initial stages of bootloader). NOTE: with the system wide I/O isolation scheme present in DRA7 SoC family, it is not reasonable to do stop all I/O operations for every such pad configuration scheme. So, we will let it glitch when used in this mode. Even with the above limitation, certain functionality such as MMC has mandatory need for IODelay reconfiguration requirements, depending on speed of transfer. In these cases, with careful examination of usecase involved, the expected glitch can be controlled such that it does not impact functionality. In short, IODelay module support as a padconf driver being introduced here is not expected to do SoC wide I/O Isolation and is meant for a limited subset of IODelay configuration requirements that need to be dynamic and whose glitchy behavior will not cause functionality failure for that interface. Signed-off-by: Nishanth Menon <nm@ti.com> Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> --- drivers/pinctrl/Kconfig | 11 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-ti-iodelay.c | 963 ++++++++++++++++++++++++++++++++++ include/dt-bindings/pinctrl/dra.h | 6 + 4 files changed, 981 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-ti-iodelay.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index ee9f44ad7f02..8e463d75fbb2 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -160,6 +160,17 @@ config PINCTRL_TEGRA_XUSB select PINCONF select PINMUX +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 0475206dd600..e441cd71aedf 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o obj-$(CONFIG_PINCTRL_TEGRA124) += pinctrl-tegra124.o obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o +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 a/drivers/pinctrl/pinctrl-ti-iodelay.c b/drivers/pinctrl/pinctrl-ti-iodelay.c new file mode 100644 index 000000000000..e4c6e25a781c --- /dev/null +++ b/drivers/pinctrl/pinctrl-ti-iodelay.c @@ -0,0 +1,963 @@ +/* + * 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 <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#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; + + /* Write with lock value - we DONOT want h/w updates */ + reg_mask |= reg->lock_mask; + reg_val |= reg->lock_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/include/dt-bindings/pinctrl/dra.h b/include/dt-bindings/pinctrl/dra.h index 7448edff4723..0a26495c4df5 100644 --- a/include/dt-bindings/pinctrl/dra.h +++ b/include/dt-bindings/pinctrl/dra.h @@ -30,6 +30,8 @@ #define MUX_MODE14 0xe #define MUX_MODE15 0xf +#define MANUAL_MODE MODE_SELECT + #define PULL_ENA (0 << 16) #define PULL_DIS (1 << 16) #define PULL_UP (1 << 17) @@ -47,5 +49,9 @@ #define PIN_INPUT_PULLUP (PULL_ENA | INPUT_EN | PULL_UP) #define PIN_INPUT_PULLDOWN (PULL_ENA | INPUT_EN) +/* DRA7 IODELAY configuration parameters */ +#define A_DELAY(val) ((val) & 0xFFFF) +#define G_DELAY(val) (((val) & 0xFFFF) << 16) + #endif -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver @ 2015-03-04 0:00 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-04 0:00 UTC (permalink / raw) To: linux-arm-kernel SoC family such as DRA7 family of processors have, in addition to the regular muxing of pins (as done by pinctrl-single), an additional hardware module called IODelay which is also expected to be configured. This "IODelay" module has it's own register space that is independent of the control module. It is advocated strongly in TI's official documentation considering the existing design of the DRA7 family of processors during mux or IODelay reconfiguration, there is a potential for a significant glitch which may cause functional impairment to certain hardware. It is hence recommended to do as little of muxing as absolutely necessary without I/O isolation (which can only be done in initial stages of bootloader). NOTE: with the system wide I/O isolation scheme present in DRA7 SoC family, it is not reasonable to do stop all I/O operations for every such pad configuration scheme. So, we will let it glitch when used in this mode. Even with the above limitation, certain functionality such as MMC has mandatory need for IODelay reconfiguration requirements, depending on speed of transfer. In these cases, with careful examination of usecase involved, the expected glitch can be controlled such that it does not impact functionality. In short, IODelay module support as a padconf driver being introduced here is not expected to do SoC wide I/O Isolation and is meant for a limited subset of IODelay configuration requirements that need to be dynamic and whose glitchy behavior will not cause functionality failure for that interface. Signed-off-by: Nishanth Menon <nm@ti.com> Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> --- drivers/pinctrl/Kconfig | 11 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-ti-iodelay.c | 963 ++++++++++++++++++++++++++++++++++ include/dt-bindings/pinctrl/dra.h | 6 + 4 files changed, 981 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-ti-iodelay.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index ee9f44ad7f02..8e463d75fbb2 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -160,6 +160,17 @@ config PINCTRL_TEGRA_XUSB select PINCONF select PINMUX +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 0475206dd600..e441cd71aedf 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o obj-$(CONFIG_PINCTRL_TEGRA124) += pinctrl-tegra124.o obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o +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 a/drivers/pinctrl/pinctrl-ti-iodelay.c b/drivers/pinctrl/pinctrl-ti-iodelay.c new file mode 100644 index 000000000000..e4c6e25a781c --- /dev/null +++ b/drivers/pinctrl/pinctrl-ti-iodelay.c @@ -0,0 +1,963 @@ +/* + * 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 <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#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; + + /* Write with lock value - we DONOT want h/w updates */ + reg_mask |= reg->lock_mask; + reg_val |= reg->lock_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/include/dt-bindings/pinctrl/dra.h b/include/dt-bindings/pinctrl/dra.h index 7448edff4723..0a26495c4df5 100644 --- a/include/dt-bindings/pinctrl/dra.h +++ b/include/dt-bindings/pinctrl/dra.h @@ -30,6 +30,8 @@ #define MUX_MODE14 0xe #define MUX_MODE15 0xf +#define MANUAL_MODE MODE_SELECT + #define PULL_ENA (0 << 16) #define PULL_DIS (1 << 16) #define PULL_UP (1 << 17) @@ -47,5 +49,9 @@ #define PIN_INPUT_PULLUP (PULL_ENA | INPUT_EN | PULL_UP) #define PIN_INPUT_PULLDOWN (PULL_ENA | INPUT_EN) +/* DRA7 IODELAY configuration parameters */ +#define A_DELAY(val) ((val) & 0xFFFF) +#define G_DELAY(val) (((val) & 0xFFFF) << 16) + #endif -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 74+ messages in thread
* [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver @ 2015-03-04 0:00 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-04 0:00 UTC (permalink / raw) To: Tony Lindgren, Linus Walleij Cc: linux-gpio, linux-kernel, devicetree, linux-omap, linux-arm-kernel, Lokesh Vutla, Nishanth Menon SoC family such as DRA7 family of processors have, in addition to the regular muxing of pins (as done by pinctrl-single), an additional hardware module called IODelay which is also expected to be configured. This "IODelay" module has it's own register space that is independent of the control module. It is advocated strongly in TI's official documentation considering the existing design of the DRA7 family of processors during mux or IODelay reconfiguration, there is a potential for a significant glitch which may cause functional impairment to certain hardware. It is hence recommended to do as little of muxing as absolutely necessary without I/O isolation (which can only be done in initial stages of bootloader). NOTE: with the system wide I/O isolation scheme present in DRA7 SoC family, it is not reasonable to do stop all I/O operations for every such pad configuration scheme. So, we will let it glitch when used in this mode. Even with the above limitation, certain functionality such as MMC has mandatory need for IODelay reconfiguration requirements, depending on speed of transfer. In these cases, with careful examination of usecase involved, the expected glitch can be controlled such that it does not impact functionality. In short, IODelay module support as a padconf driver being introduced here is not expected to do SoC wide I/O Isolation and is meant for a limited subset of IODelay configuration requirements that need to be dynamic and whose glitchy behavior will not cause functionality failure for that interface. Signed-off-by: Nishanth Menon <nm@ti.com> Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> --- drivers/pinctrl/Kconfig | 11 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-ti-iodelay.c | 963 ++++++++++++++++++++++++++++++++++ include/dt-bindings/pinctrl/dra.h | 6 + 4 files changed, 981 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-ti-iodelay.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index ee9f44ad7f02..8e463d75fbb2 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -160,6 +160,17 @@ config PINCTRL_TEGRA_XUSB select PINCONF select PINMUX +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 0475206dd600..e441cd71aedf 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o obj-$(CONFIG_PINCTRL_TEGRA124) += pinctrl-tegra124.o obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o +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 a/drivers/pinctrl/pinctrl-ti-iodelay.c b/drivers/pinctrl/pinctrl-ti-iodelay.c new file mode 100644 index 000000000000..e4c6e25a781c --- /dev/null +++ b/drivers/pinctrl/pinctrl-ti-iodelay.c @@ -0,0 +1,963 @@ +/* + * 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 <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#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; + + /* Write with lock value - we DONOT want h/w updates */ + reg_mask |= reg->lock_mask; + reg_val |= reg->lock_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/include/dt-bindings/pinctrl/dra.h b/include/dt-bindings/pinctrl/dra.h index 7448edff4723..0a26495c4df5 100644 --- a/include/dt-bindings/pinctrl/dra.h +++ b/include/dt-bindings/pinctrl/dra.h @@ -30,6 +30,8 @@ #define MUX_MODE14 0xe #define MUX_MODE15 0xf +#define MANUAL_MODE MODE_SELECT + #define PULL_ENA (0 << 16) #define PULL_DIS (1 << 16) #define PULL_UP (1 << 17) @@ -47,5 +49,9 @@ #define PIN_INPUT_PULLUP (PULL_ENA | INPUT_EN | PULL_UP) #define PIN_INPUT_PULLDOWN (PULL_ENA | INPUT_EN) +/* DRA7 IODELAY configuration parameters */ +#define A_DELAY(val) ((val) & 0xFFFF) +#define G_DELAY(val) (((val) & 0xFFFF) << 16) + #endif -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 74+ messages in thread
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver 2015-03-04 0:00 ` Nishanth Menon @ 2015-03-04 22:58 ` Paul Bolle -1 siblings, 0 replies; 74+ messages in thread From: Paul Bolle @ 2015-03-04 22:58 UTC (permalink / raw) To: Nishanth Menon Cc: Tony Lindgren, Linus Walleij, linux-gpio, linux-kernel, devicetree, linux-omap, linux-arm-kernel, Lokesh Vutla Nishanth Menon schreef op di 03-03-2015 om 18:00 [-0600]: > diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig > index ee9f44ad7f02..8e463d75fbb2 100644 > --- a/drivers/pinctrl/Kconfig > +++ b/drivers/pinctrl/Kconfig > @@ -160,6 +160,17 @@ config PINCTRL_TEGRA_XUSB > select PINCONF > select PINMUX > > +config PINCTRL_TI_IODELAY > + bool "TI IODelay Module pinconf driver" This adds a boolean Kconfig symbol. > + 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 0475206dd600..e441cd71aedf 100644 > --- a/drivers/pinctrl/Makefile > +++ b/drivers/pinctrl/Makefile > @@ -28,6 +28,7 @@ obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o > obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o > obj-$(CONFIG_PINCTRL_TEGRA124) += pinctrl-tegra124.o > obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o > +obj-$(CONFIG_PINCTRL_TI_IODELAY) += pinctrl-ti-iodelay.o So this objectfile will either be built-in or not be built at all. > 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 a/drivers/pinctrl/pinctrl-ti-iodelay.c b/drivers/pinctrl/pinctrl-ti-iodelay.c > new file mode 100644 > index 000000000000..e4c6e25a781c > --- /dev/null > +++ b/drivers/pinctrl/pinctrl-ti-iodelay.c > @@ -0,0 +1,963 @@ >[...] > +#include <linux/err.h> > +#include <linux/init.h> > +#include <linux/io.h> > +#include <linux/list.h> > +#include <linux/module.h> This is, I think, a bit of a red flag. > +#include <linux/of_device.h> > +#include <linux/of.h> > +#include <linux/pinctrl/pinconf-generic.h> > +#include <linux/pinctrl/pinconf.h> > +#include <linux/pinctrl/pinctrl.h> > +#include <linux/regmap.h> > +#include <linux/slab.h> [...] > +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, So THIS_MODULE will, basically, be NULL. > + .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"); And these three macros will be preprocessed away. (Perhaps there are non-modular alternatives to MODULE_DEVICE_TABLE and module_platform_driver, but I'm not sure how those two macros actually work.) Paul Bolle ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver @ 2015-03-04 22:58 ` Paul Bolle 0 siblings, 0 replies; 74+ messages in thread From: Paul Bolle @ 2015-03-04 22:58 UTC (permalink / raw) To: linux-arm-kernel Nishanth Menon schreef op di 03-03-2015 om 18:00 [-0600]: > diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig > index ee9f44ad7f02..8e463d75fbb2 100644 > --- a/drivers/pinctrl/Kconfig > +++ b/drivers/pinctrl/Kconfig > @@ -160,6 +160,17 @@ config PINCTRL_TEGRA_XUSB > select PINCONF > select PINMUX > > +config PINCTRL_TI_IODELAY > + bool "TI IODelay Module pinconf driver" This adds a boolean Kconfig symbol. > + 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 0475206dd600..e441cd71aedf 100644 > --- a/drivers/pinctrl/Makefile > +++ b/drivers/pinctrl/Makefile > @@ -28,6 +28,7 @@ obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o > obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o > obj-$(CONFIG_PINCTRL_TEGRA124) += pinctrl-tegra124.o > obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o > +obj-$(CONFIG_PINCTRL_TI_IODELAY) += pinctrl-ti-iodelay.o So this objectfile will either be built-in or not be built at all. > 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 a/drivers/pinctrl/pinctrl-ti-iodelay.c b/drivers/pinctrl/pinctrl-ti-iodelay.c > new file mode 100644 > index 000000000000..e4c6e25a781c > --- /dev/null > +++ b/drivers/pinctrl/pinctrl-ti-iodelay.c > @@ -0,0 +1,963 @@ >[...] > +#include <linux/err.h> > +#include <linux/init.h> > +#include <linux/io.h> > +#include <linux/list.h> > +#include <linux/module.h> This is, I think, a bit of a red flag. > +#include <linux/of_device.h> > +#include <linux/of.h> > +#include <linux/pinctrl/pinconf-generic.h> > +#include <linux/pinctrl/pinconf.h> > +#include <linux/pinctrl/pinctrl.h> > +#include <linux/regmap.h> > +#include <linux/slab.h> [...] > +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, So THIS_MODULE will, basically, be NULL. > + .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"); And these three macros will be preprocessed away. (Perhaps there are non-modular alternatives to MODULE_DEVICE_TABLE and module_platform_driver, but I'm not sure how those two macros actually work.) Paul Bolle ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver 2015-03-04 22:58 ` Paul Bolle @ 2015-03-04 22:58 ` Tony Lindgren -1 siblings, 0 replies; 74+ messages in thread From: Tony Lindgren @ 2015-03-04 22:58 UTC (permalink / raw) To: Paul Bolle Cc: Nishanth Menon, Linus Walleij, linux-gpio, linux-kernel, devicetree, linux-omap, linux-arm-kernel, Lokesh Vutla * Paul Bolle <pebolle@tiscali.nl> [150304 14:58]: > Nishanth Menon schreef op di 03-03-2015 om 18:00 [-0600]: > > diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig > > index ee9f44ad7f02..8e463d75fbb2 100644 > > --- a/drivers/pinctrl/Kconfig > > +++ b/drivers/pinctrl/Kconfig > > @@ -160,6 +160,17 @@ config PINCTRL_TEGRA_XUSB > > select PINCONF > > select PINMUX > > > > +config PINCTRL_TI_IODELAY > > + bool "TI IODelay Module pinconf driver" > > This adds a boolean Kconfig symbol. > > > + 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 0475206dd600..e441cd71aedf 100644 > > --- a/drivers/pinctrl/Makefile > > +++ b/drivers/pinctrl/Makefile > > @@ -28,6 +28,7 @@ obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o > > obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o > > obj-$(CONFIG_PINCTRL_TEGRA124) += pinctrl-tegra124.o > > obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o > > +obj-$(CONFIG_PINCTRL_TI_IODELAY) += pinctrl-ti-iodelay.o > > So this objectfile will either be built-in or not be built at all. > > > 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 a/drivers/pinctrl/pinctrl-ti-iodelay.c b/drivers/pinctrl/pinctrl-ti-iodelay.c > > new file mode 100644 > > index 000000000000..e4c6e25a781c > > --- /dev/null > > +++ b/drivers/pinctrl/pinctrl-ti-iodelay.c > > @@ -0,0 +1,963 @@ > >[...] > > +#include <linux/err.h> > > +#include <linux/init.h> > > +#include <linux/io.h> > > +#include <linux/list.h> > > +#include <linux/module.h> > > This is, I think, a bit of a red flag. > > > +#include <linux/of_device.h> > > +#include <linux/of.h> > > +#include <linux/pinctrl/pinconf-generic.h> > > +#include <linux/pinctrl/pinconf.h> > > +#include <linux/pinctrl/pinctrl.h> > > +#include <linux/regmap.h> > > +#include <linux/slab.h> > > [...] > > > +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, > > So THIS_MODULE will, basically, be NULL. > > > + .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"); > > And these three macros will be preprocessed away. > > (Perhaps there are non-modular alternatives to MODULE_DEVICE_TABLE and > module_platform_driver, but I'm not sure how those two macros actually > work.) This should be just made to work as a regular loadable module. We already have pinctrl-single for the same platforms working as a loadable module. Regards, Tony ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver @ 2015-03-04 22:58 ` Tony Lindgren 0 siblings, 0 replies; 74+ messages in thread From: Tony Lindgren @ 2015-03-04 22:58 UTC (permalink / raw) To: linux-arm-kernel * Paul Bolle <pebolle@tiscali.nl> [150304 14:58]: > Nishanth Menon schreef op di 03-03-2015 om 18:00 [-0600]: > > diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig > > index ee9f44ad7f02..8e463d75fbb2 100644 > > --- a/drivers/pinctrl/Kconfig > > +++ b/drivers/pinctrl/Kconfig > > @@ -160,6 +160,17 @@ config PINCTRL_TEGRA_XUSB > > select PINCONF > > select PINMUX > > > > +config PINCTRL_TI_IODELAY > > + bool "TI IODelay Module pinconf driver" > > This adds a boolean Kconfig symbol. > > > + 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 0475206dd600..e441cd71aedf 100644 > > --- a/drivers/pinctrl/Makefile > > +++ b/drivers/pinctrl/Makefile > > @@ -28,6 +28,7 @@ obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o > > obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o > > obj-$(CONFIG_PINCTRL_TEGRA124) += pinctrl-tegra124.o > > obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o > > +obj-$(CONFIG_PINCTRL_TI_IODELAY) += pinctrl-ti-iodelay.o > > So this objectfile will either be built-in or not be built at all. > > > 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 a/drivers/pinctrl/pinctrl-ti-iodelay.c b/drivers/pinctrl/pinctrl-ti-iodelay.c > > new file mode 100644 > > index 000000000000..e4c6e25a781c > > --- /dev/null > > +++ b/drivers/pinctrl/pinctrl-ti-iodelay.c > > @@ -0,0 +1,963 @@ > >[...] > > +#include <linux/err.h> > > +#include <linux/init.h> > > +#include <linux/io.h> > > +#include <linux/list.h> > > +#include <linux/module.h> > > This is, I think, a bit of a red flag. > > > +#include <linux/of_device.h> > > +#include <linux/of.h> > > +#include <linux/pinctrl/pinconf-generic.h> > > +#include <linux/pinctrl/pinconf.h> > > +#include <linux/pinctrl/pinctrl.h> > > +#include <linux/regmap.h> > > +#include <linux/slab.h> > > [...] > > > +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, > > So THIS_MODULE will, basically, be NULL. > > > + .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"); > > And these three macros will be preprocessed away. > > (Perhaps there are non-modular alternatives to MODULE_DEVICE_TABLE and > module_platform_driver, but I'm not sure how those two macros actually > work.) This should be just made to work as a regular loadable module. We already have pinctrl-single for the same platforms working as a loadable module. Regards, Tony ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver 2015-03-04 22:58 ` Tony Lindgren (?) @ 2015-03-05 2:22 ` Nishanth Menon -1 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-05 2:22 UTC (permalink / raw) To: Tony Lindgren, Paul Bolle Cc: Linus Walleij, linux-gpio, linux-kernel, devicetree, linux-omap, linux-arm-kernel, Lokesh Vutla On 03/04/2015 04:58 PM, Tony Lindgren wrote: > * Paul Bolle <pebolle@tiscali.nl> [150304 14:58]: >> Nishanth Menon schreef op di 03-03-2015 om 18:00 [-0600]: >>> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig >>> index ee9f44ad7f02..8e463d75fbb2 100644 >>> --- a/drivers/pinctrl/Kconfig >>> +++ b/drivers/pinctrl/Kconfig >>> @@ -160,6 +160,17 @@ config PINCTRL_TEGRA_XUSB >>> select PINCONF >>> select PINMUX >>> >>> +config PINCTRL_TI_IODELAY >>> + bool "TI IODelay Module pinconf driver" >> >> This adds a boolean Kconfig symbol. Uggh.. thanks for the review and catching this.. >> >>> + 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 0475206dd600..e441cd71aedf 100644 >>> --- a/drivers/pinctrl/Makefile >>> +++ b/drivers/pinctrl/Makefile >>> @@ -28,6 +28,7 @@ obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o >>> obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o >>> obj-$(CONFIG_PINCTRL_TEGRA124) += pinctrl-tegra124.o >>> obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o >>> +obj-$(CONFIG_PINCTRL_TI_IODELAY) += pinctrl-ti-iodelay.o >> >> So this objectfile will either be built-in or not be built at all. >> >>> 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 a/drivers/pinctrl/pinctrl-ti-iodelay.c b/drivers/pinctrl/pinctrl-ti-iodelay.c >>> new file mode 100644 >>> index 000000000000..e4c6e25a781c >>> --- /dev/null >>> +++ b/drivers/pinctrl/pinctrl-ti-iodelay.c >>> @@ -0,0 +1,963 @@ >>> [...] >>> +#include <linux/err.h> >>> +#include <linux/init.h> >>> +#include <linux/io.h> >>> +#include <linux/list.h> >>> +#include <linux/module.h> >> >> This is, I think, a bit of a red flag. >> >>> +#include <linux/of_device.h> >>> +#include <linux/of.h> >>> +#include <linux/pinctrl/pinconf-generic.h> >>> +#include <linux/pinctrl/pinconf.h> >>> +#include <linux/pinctrl/pinctrl.h> >>> +#include <linux/regmap.h> >>> +#include <linux/slab.h> >> >> [...] >> >>> +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, >> >> So THIS_MODULE will, basically, be NULL. >> >>> + .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"); >> >> And these three macros will be preprocessed away. >> >> (Perhaps there are non-modular alternatives to MODULE_DEVICE_TABLE and >> module_platform_driver, but I'm not sure how those two macros actually >> work.) > > This should be just made to work as a regular loadable module. > We already have pinctrl-single for the same platforms working > as a loadable module. > Yes, indeed. this can easily be fixed. my bad. will convert this to a module in the next rev. I will wait a week before reposting next rev to see if there are improvements necessary in other areas - specifically in the way pinconf is being used and if there are any binding related comments. Will be great to hear any feedback on the same as well. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver @ 2015-03-05 2:22 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-05 2:22 UTC (permalink / raw) To: linux-arm-kernel On 03/04/2015 04:58 PM, Tony Lindgren wrote: > * Paul Bolle <pebolle@tiscali.nl> [150304 14:58]: >> Nishanth Menon schreef op di 03-03-2015 om 18:00 [-0600]: >>> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig >>> index ee9f44ad7f02..8e463d75fbb2 100644 >>> --- a/drivers/pinctrl/Kconfig >>> +++ b/drivers/pinctrl/Kconfig >>> @@ -160,6 +160,17 @@ config PINCTRL_TEGRA_XUSB >>> select PINCONF >>> select PINMUX >>> >>> +config PINCTRL_TI_IODELAY >>> + bool "TI IODelay Module pinconf driver" >> >> This adds a boolean Kconfig symbol. Uggh.. thanks for the review and catching this.. >> >>> + 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 0475206dd600..e441cd71aedf 100644 >>> --- a/drivers/pinctrl/Makefile >>> +++ b/drivers/pinctrl/Makefile >>> @@ -28,6 +28,7 @@ obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o >>> obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o >>> obj-$(CONFIG_PINCTRL_TEGRA124) += pinctrl-tegra124.o >>> obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o >>> +obj-$(CONFIG_PINCTRL_TI_IODELAY) += pinctrl-ti-iodelay.o >> >> So this objectfile will either be built-in or not be built at all. >> >>> 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 a/drivers/pinctrl/pinctrl-ti-iodelay.c b/drivers/pinctrl/pinctrl-ti-iodelay.c >>> new file mode 100644 >>> index 000000000000..e4c6e25a781c >>> --- /dev/null >>> +++ b/drivers/pinctrl/pinctrl-ti-iodelay.c >>> @@ -0,0 +1,963 @@ >>> [...] >>> +#include <linux/err.h> >>> +#include <linux/init.h> >>> +#include <linux/io.h> >>> +#include <linux/list.h> >>> +#include <linux/module.h> >> >> This is, I think, a bit of a red flag. >> >>> +#include <linux/of_device.h> >>> +#include <linux/of.h> >>> +#include <linux/pinctrl/pinconf-generic.h> >>> +#include <linux/pinctrl/pinconf.h> >>> +#include <linux/pinctrl/pinctrl.h> >>> +#include <linux/regmap.h> >>> +#include <linux/slab.h> >> >> [...] >> >>> +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, >> >> So THIS_MODULE will, basically, be NULL. >> >>> + .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"); >> >> And these three macros will be preprocessed away. >> >> (Perhaps there are non-modular alternatives to MODULE_DEVICE_TABLE and >> module_platform_driver, but I'm not sure how those two macros actually >> work.) > > This should be just made to work as a regular loadable module. > We already have pinctrl-single for the same platforms working > as a loadable module. > Yes, indeed. this can easily be fixed. my bad. will convert this to a module in the next rev. I will wait a week before reposting next rev to see if there are improvements necessary in other areas - specifically in the way pinconf is being used and if there are any binding related comments. Will be great to hear any feedback on the same as well. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver @ 2015-03-05 2:22 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-05 2:22 UTC (permalink / raw) To: Tony Lindgren, Paul Bolle Cc: Linus Walleij, linux-gpio, linux-kernel, devicetree, linux-omap, linux-arm-kernel, Lokesh Vutla On 03/04/2015 04:58 PM, Tony Lindgren wrote: > * Paul Bolle <pebolle@tiscali.nl> [150304 14:58]: >> Nishanth Menon schreef op di 03-03-2015 om 18:00 [-0600]: >>> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig >>> index ee9f44ad7f02..8e463d75fbb2 100644 >>> --- a/drivers/pinctrl/Kconfig >>> +++ b/drivers/pinctrl/Kconfig >>> @@ -160,6 +160,17 @@ config PINCTRL_TEGRA_XUSB >>> select PINCONF >>> select PINMUX >>> >>> +config PINCTRL_TI_IODELAY >>> + bool "TI IODelay Module pinconf driver" >> >> This adds a boolean Kconfig symbol. Uggh.. thanks for the review and catching this.. >> >>> + 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 0475206dd600..e441cd71aedf 100644 >>> --- a/drivers/pinctrl/Makefile >>> +++ b/drivers/pinctrl/Makefile >>> @@ -28,6 +28,7 @@ obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o >>> obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o >>> obj-$(CONFIG_PINCTRL_TEGRA124) += pinctrl-tegra124.o >>> obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o >>> +obj-$(CONFIG_PINCTRL_TI_IODELAY) += pinctrl-ti-iodelay.o >> >> So this objectfile will either be built-in or not be built at all. >> >>> 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 a/drivers/pinctrl/pinctrl-ti-iodelay.c b/drivers/pinctrl/pinctrl-ti-iodelay.c >>> new file mode 100644 >>> index 000000000000..e4c6e25a781c >>> --- /dev/null >>> +++ b/drivers/pinctrl/pinctrl-ti-iodelay.c >>> @@ -0,0 +1,963 @@ >>> [...] >>> +#include <linux/err.h> >>> +#include <linux/init.h> >>> +#include <linux/io.h> >>> +#include <linux/list.h> >>> +#include <linux/module.h> >> >> This is, I think, a bit of a red flag. >> >>> +#include <linux/of_device.h> >>> +#include <linux/of.h> >>> +#include <linux/pinctrl/pinconf-generic.h> >>> +#include <linux/pinctrl/pinconf.h> >>> +#include <linux/pinctrl/pinctrl.h> >>> +#include <linux/regmap.h> >>> +#include <linux/slab.h> >> >> [...] >> >>> +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, >> >> So THIS_MODULE will, basically, be NULL. >> >>> + .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"); >> >> And these three macros will be preprocessed away. >> >> (Perhaps there are non-modular alternatives to MODULE_DEVICE_TABLE and >> module_platform_driver, but I'm not sure how those two macros actually >> work.) > > This should be just made to work as a regular loadable module. > We already have pinctrl-single for the same platforms working > as a loadable module. > Yes, indeed. this can easily be fixed. my bad. will convert this to a module in the next rev. I will wait a week before reposting next rev to see if there are improvements necessary in other areas - specifically in the way pinconf is being used and if there are any binding related comments. Will be great to hear any feedback on the same as well. -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver 2015-03-04 0:00 ` Nishanth Menon (?) @ 2015-03-10 11:03 ` Linus Walleij -1 siblings, 0 replies; 74+ messages in thread From: Linus Walleij @ 2015-03-10 11:03 UTC (permalink / raw) To: Nishanth Menon Cc: Tony Lindgren, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > SoC family such as DRA7 family of processors have, in addition > to the regular muxing of pins (as done by pinctrl-single), an > additional hardware module called IODelay which is also expected to be > configured. This "IODelay" module has it's own register space that is > independent of the control module. > > It is advocated strongly in TI's official documentation considering > the existing design of the DRA7 family of processors during mux or > IODelay reconfiguration, there is a potential for a significant glitch > which may cause functional impairment to certain hardware. It is > hence recommended to do as little of muxing as absolutely necessary > without I/O isolation (which can only be done in initial stages of > bootloader). > > NOTE: with the system wide I/O isolation scheme present in DRA7 SoC > family, it is not reasonable to do stop all I/O operations for every > such pad configuration scheme. So, we will let it glitch when used in > this mode. > > Even with the above limitation, certain functionality such as MMC has > mandatory need for IODelay reconfiguration requirements, depending on > speed of transfer. In these cases, with careful examination of usecase > involved, the expected glitch can be controlled such that it does not > impact functionality. > > In short, IODelay module support as a padconf driver being introduced > here is not expected to do SoC wide I/O Isolation and is meant for > a limited subset of IODelay configuration requirements that need to > be dynamic and whose glitchy behavior will not cause functionality > failure for that interface. > > Signed-off-by: Nishanth Menon <nm@ti.com> > Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> Pending the conclusion of my remark on the bindings that this should use the generic pin config bindings... here are some "normal" review comments. > +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. If the driver is "in addition to PINCTRL_SINGLE", then it should depend on it. > +/* Should I change this? Abuse? */ > +#define IODELAY_MUX_PINS_NAME "pinctrl-single,pins" Yeah this is abuse and is why you should use the generic bindings with just "pins" instead, and use the generic pinconf parameters to set them up. > + * 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; > +}; So I don't think it's a good idea to get the register offset from the device tree. The driver should know these. > +/** > + * 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; If you're doing reference counting, use kref. <linux/kref.h> > + u16 coarse_delay_count; > + > + u16 fine_ref_count; Dito. > + 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]; > +}; What is this? The pin control subsystem already has a struct for naming pins. struct pinctrl_pin_desc. Are you reimplementing core functionality? The generic pin config reference pins by name, and since you obviously like to encode these into you data you can just as well use that method for this too. Again I think this is a side effect from using the pinctrl-single concepts, if you think of this as some other pinctrl driver I think this will fall out nicely. > +/** > + * 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; Unhappy about that. pinctrl_desc contains struct pinctrl_pin_desc const *pins; ! > + > + struct list_head groups; > + int ngroups; > + struct mutex mutex; /* list protection */ Why is this needed? Usually the pin control core serialize calls into the drivers. Care to explain? Have the potential race really triggered? > +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); > +} Serious business ... > +/** > + * 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; > + > + /* Write with lock value - we DONOT want h/w updates */ > + reg_mask |= reg->lock_mask; > + reg_val |= reg->lock_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; > +} Looking at this terse thing I think it's obviously easier to use a more verbose device tree description with drive-delay = <some SI unit>; > + /* 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); That sounds hightec. So the bootloader calibrates the delays and computes the refclock period? So is that sime PLL or something? > + 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); 88? What does that mean... This should be #defined I think. > +static int ti_iodelay_dt_node_to_map(struct pinctrl_dev *pctldev, > + struct device_node *np, > + struct pinctrl_map **map, > + unsigned *num_maps) If you use the generic bindings this quite complex parsing and everything associated with it goes away into the core. > +static void ti_iodelay_dt_free_map(struct pinctrl_dev *pctldev, > + struct pinctrl_map *map, unsigned num_maps) Dito. BTW even if you proceed this way I suspect these are verbatim copies from pinctrl-single so they shold obviouslyt be abstracted out as library functions in that case. > +/** > + * 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; > +} This is not about muxing. It is about enumerating the pins in the group that will be affected by the setting, which is something you will want to do when using the generic bindings. > + group = ti_iodelay_get_group(iod, gselector); And this seems to be your re-implementation of exactly that pin control core functionality. Which again should be shared with pinctrl-single if you anyway go down this path. I'll halt review here pending discussion on the bindings & stuff. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver @ 2015-03-10 11:03 ` Linus Walleij 0 siblings, 0 replies; 74+ messages in thread From: Linus Walleij @ 2015-03-10 11:03 UTC (permalink / raw) To: linux-arm-kernel On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > SoC family such as DRA7 family of processors have, in addition > to the regular muxing of pins (as done by pinctrl-single), an > additional hardware module called IODelay which is also expected to be > configured. This "IODelay" module has it's own register space that is > independent of the control module. > > It is advocated strongly in TI's official documentation considering > the existing design of the DRA7 family of processors during mux or > IODelay reconfiguration, there is a potential for a significant glitch > which may cause functional impairment to certain hardware. It is > hence recommended to do as little of muxing as absolutely necessary > without I/O isolation (which can only be done in initial stages of > bootloader). > > NOTE: with the system wide I/O isolation scheme present in DRA7 SoC > family, it is not reasonable to do stop all I/O operations for every > such pad configuration scheme. So, we will let it glitch when used in > this mode. > > Even with the above limitation, certain functionality such as MMC has > mandatory need for IODelay reconfiguration requirements, depending on > speed of transfer. In these cases, with careful examination of usecase > involved, the expected glitch can be controlled such that it does not > impact functionality. > > In short, IODelay module support as a padconf driver being introduced > here is not expected to do SoC wide I/O Isolation and is meant for > a limited subset of IODelay configuration requirements that need to > be dynamic and whose glitchy behavior will not cause functionality > failure for that interface. > > Signed-off-by: Nishanth Menon <nm@ti.com> > Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> Pending the conclusion of my remark on the bindings that this should use the generic pin config bindings... here are some "normal" review comments. > +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. If the driver is "in addition to PINCTRL_SINGLE", then it should depend on it. > +/* Should I change this? Abuse? */ > +#define IODELAY_MUX_PINS_NAME "pinctrl-single,pins" Yeah this is abuse and is why you should use the generic bindings with just "pins" instead, and use the generic pinconf parameters to set them up. > + * 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; > +}; So I don't think it's a good idea to get the register offset from the device tree. The driver should know these. > +/** > + * 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; If you're doing reference counting, use kref. <linux/kref.h> > + u16 coarse_delay_count; > + > + u16 fine_ref_count; Dito. > + 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]; > +}; What is this? The pin control subsystem already has a struct for naming pins. struct pinctrl_pin_desc. Are you reimplementing core functionality? The generic pin config reference pins by name, and since you obviously like to encode these into you data you can just as well use that method for this too. Again I think this is a side effect from using the pinctrl-single concepts, if you think of this as some other pinctrl driver I think this will fall out nicely. > +/** > + * 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; Unhappy about that. pinctrl_desc contains struct pinctrl_pin_desc const *pins; ! > + > + struct list_head groups; > + int ngroups; > + struct mutex mutex; /* list protection */ Why is this needed? Usually the pin control core serialize calls into the drivers. Care to explain? Have the potential race really triggered? > +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); > +} Serious business ... > +/** > + * 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; > + > + /* Write with lock value - we DONOT want h/w updates */ > + reg_mask |= reg->lock_mask; > + reg_val |= reg->lock_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; > +} Looking at this terse thing I think it's obviously easier to use a more verbose device tree description with drive-delay = <some SI unit>; > + /* 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); That sounds hightec. So the bootloader calibrates the delays and computes the refclock period? So is that sime PLL or something? > + 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); 88? What does that mean... This should be #defined I think. > +static int ti_iodelay_dt_node_to_map(struct pinctrl_dev *pctldev, > + struct device_node *np, > + struct pinctrl_map **map, > + unsigned *num_maps) If you use the generic bindings this quite complex parsing and everything associated with it goes away into the core. > +static void ti_iodelay_dt_free_map(struct pinctrl_dev *pctldev, > + struct pinctrl_map *map, unsigned num_maps) Dito. BTW even if you proceed this way I suspect these are verbatim copies from pinctrl-single so they shold obviouslyt be abstracted out as library functions in that case. > +/** > + * 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; > +} This is not about muxing. It is about enumerating the pins in the group that will be affected by the setting, which is something you will want to do when using the generic bindings. > + group = ti_iodelay_get_group(iod, gselector); And this seems to be your re-implementation of exactly that pin control core functionality. Which again should be shared with pinctrl-single if you anyway go down this path. I'll halt review here pending discussion on the bindings & stuff. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver @ 2015-03-10 11:03 ` Linus Walleij 0 siblings, 0 replies; 74+ messages in thread From: Linus Walleij @ 2015-03-10 11:03 UTC (permalink / raw) To: Nishanth Menon Cc: Tony Lindgren, linux-gpio, linux-kernel, devicetree, Linux-OMAP, linux-arm-kernel, Lokesh Vutla On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > SoC family such as DRA7 family of processors have, in addition > to the regular muxing of pins (as done by pinctrl-single), an > additional hardware module called IODelay which is also expected to be > configured. This "IODelay" module has it's own register space that is > independent of the control module. > > It is advocated strongly in TI's official documentation considering > the existing design of the DRA7 family of processors during mux or > IODelay reconfiguration, there is a potential for a significant glitch > which may cause functional impairment to certain hardware. It is > hence recommended to do as little of muxing as absolutely necessary > without I/O isolation (which can only be done in initial stages of > bootloader). > > NOTE: with the system wide I/O isolation scheme present in DRA7 SoC > family, it is not reasonable to do stop all I/O operations for every > such pad configuration scheme. So, we will let it glitch when used in > this mode. > > Even with the above limitation, certain functionality such as MMC has > mandatory need for IODelay reconfiguration requirements, depending on > speed of transfer. In these cases, with careful examination of usecase > involved, the expected glitch can be controlled such that it does not > impact functionality. > > In short, IODelay module support as a padconf driver being introduced > here is not expected to do SoC wide I/O Isolation and is meant for > a limited subset of IODelay configuration requirements that need to > be dynamic and whose glitchy behavior will not cause functionality > failure for that interface. > > Signed-off-by: Nishanth Menon <nm@ti.com> > Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> Pending the conclusion of my remark on the bindings that this should use the generic pin config bindings... here are some "normal" review comments. > +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. If the driver is "in addition to PINCTRL_SINGLE", then it should depend on it. > +/* Should I change this? Abuse? */ > +#define IODELAY_MUX_PINS_NAME "pinctrl-single,pins" Yeah this is abuse and is why you should use the generic bindings with just "pins" instead, and use the generic pinconf parameters to set them up. > + * 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; > +}; So I don't think it's a good idea to get the register offset from the device tree. The driver should know these. > +/** > + * 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; If you're doing reference counting, use kref. <linux/kref.h> > + u16 coarse_delay_count; > + > + u16 fine_ref_count; Dito. > + 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]; > +}; What is this? The pin control subsystem already has a struct for naming pins. struct pinctrl_pin_desc. Are you reimplementing core functionality? The generic pin config reference pins by name, and since you obviously like to encode these into you data you can just as well use that method for this too. Again I think this is a side effect from using the pinctrl-single concepts, if you think of this as some other pinctrl driver I think this will fall out nicely. > +/** > + * 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; Unhappy about that. pinctrl_desc contains struct pinctrl_pin_desc const *pins; ! > + > + struct list_head groups; > + int ngroups; > + struct mutex mutex; /* list protection */ Why is this needed? Usually the pin control core serialize calls into the drivers. Care to explain? Have the potential race really triggered? > +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); > +} Serious business ... > +/** > + * 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; > + > + /* Write with lock value - we DONOT want h/w updates */ > + reg_mask |= reg->lock_mask; > + reg_val |= reg->lock_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; > +} Looking at this terse thing I think it's obviously easier to use a more verbose device tree description with drive-delay = <some SI unit>; > + /* 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); That sounds hightec. So the bootloader calibrates the delays and computes the refclock period? So is that sime PLL or something? > + 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); 88? What does that mean... This should be #defined I think. > +static int ti_iodelay_dt_node_to_map(struct pinctrl_dev *pctldev, > + struct device_node *np, > + struct pinctrl_map **map, > + unsigned *num_maps) If you use the generic bindings this quite complex parsing and everything associated with it goes away into the core. > +static void ti_iodelay_dt_free_map(struct pinctrl_dev *pctldev, > + struct pinctrl_map *map, unsigned num_maps) Dito. BTW even if you proceed this way I suspect these are verbatim copies from pinctrl-single so they shold obviouslyt be abstracted out as library functions in that case. > +/** > + * 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; > +} This is not about muxing. It is about enumerating the pins in the group that will be affected by the setting, which is something you will want to do when using the generic bindings. > + group = ti_iodelay_get_group(iod, gselector); And this seems to be your re-implementation of exactly that pin control core functionality. Which again should be shared with pinctrl-single if you anyway go down this path. I'll halt review here pending discussion on the bindings & stuff. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver 2015-03-10 11:03 ` Linus Walleij (?) @ 2015-03-11 12:39 ` Nishanth Menon -1 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-11 12:39 UTC (permalink / raw) To: Linus Walleij Cc: devicetree, Tony Lindgren, linux-kernel, linux-gpio, Lokesh Vutla, Linux-OMAP, linux-arm-kernel On Tue, Mar 10, 2015 at 6:03 AM, Linus Walleij <linus.walleij@linaro.org> wrote: > On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > >> SoC family such as DRA7 family of processors have, in addition >> to the regular muxing of pins (as done by pinctrl-single), an >> additional hardware module called IODelay which is also expected to be >> configured. This "IODelay" module has it's own register space that is >> independent of the control module. >> >> It is advocated strongly in TI's official documentation considering >> the existing design of the DRA7 family of processors during mux or >> IODelay reconfiguration, there is a potential for a significant glitch >> which may cause functional impairment to certain hardware. It is >> hence recommended to do as little of muxing as absolutely necessary >> without I/O isolation (which can only be done in initial stages of >> bootloader). >> >> NOTE: with the system wide I/O isolation scheme present in DRA7 SoC >> family, it is not reasonable to do stop all I/O operations for every >> such pad configuration scheme. So, we will let it glitch when used in >> this mode. >> >> Even with the above limitation, certain functionality such as MMC has >> mandatory need for IODelay reconfiguration requirements, depending on >> speed of transfer. In these cases, with careful examination of usecase >> involved, the expected glitch can be controlled such that it does not >> impact functionality. >> >> In short, IODelay module support as a padconf driver being introduced >> here is not expected to do SoC wide I/O Isolation and is meant for >> a limited subset of IODelay configuration requirements that need to >> be dynamic and whose glitchy behavior will not cause functionality >> failure for that interface. >> >> Signed-off-by: Nishanth Menon <nm@ti.com> >> Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> > > Pending the conclusion of my remark on the bindings that this > should use the generic pin config bindings... here are some > "normal" review comments. > >> +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. > > If the driver is "in addition to PINCTRL_SINGLE", then it > should depend on it. > >> +/* Should I change this? Abuse? */ >> +#define IODELAY_MUX_PINS_NAME "pinctrl-single,pins" > > Yeah this is abuse and is why you should use the generic > bindings with just "pins" instead, and use the generic pinconf > parameters to set them up. > >> + * 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; >> +}; > > So I don't think it's a good idea to get the register offset from > the device tree. The driver should know these. > >> +/** >> + * 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; > > If you're doing reference counting, use kref. > <linux/kref.h> > >> + u16 coarse_delay_count; >> + >> + u16 fine_ref_count; > > Dito. > >> + 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]; >> +}; > > What is this? The pin control subsystem already has a struct > for naming pins. struct pinctrl_pin_desc. > > Are you reimplementing core functionality? > > The generic pin config reference pins by name, and since you > obviously like to encode these into you data you can just > as well use that method for this too. > > Again I think this is a side effect from using the pinctrl-single > concepts, if you think of this as some other pinctrl driver > I think this will fall out nicely. > >> +/** >> + * 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; > > Unhappy about that. pinctrl_desc contains > struct pinctrl_pin_desc const *pins; ! > >> + >> + struct list_head groups; >> + int ngroups; >> + struct mutex mutex; /* list protection */ > > Why is this needed? Usually the pin control core > serialize calls into the drivers. Care to explain? Have > the potential race really triggered? > >> +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); >> +} > > Serious business ... > >> +/** >> + * 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; >> + >> + /* Write with lock value - we DONOT want h/w updates */ >> + reg_mask |= reg->lock_mask; >> + reg_val |= reg->lock_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; >> +} > > Looking at this terse thing I think it's obviously easier to use > a more verbose device tree description with > > drive-delay = <some SI unit>; > > >> + /* 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); > > That sounds hightec. So the bootloader calibrates the delays > and computes the refclock period? So is that sime PLL or something? > >> + 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); > > 88? What does that mean... This should be #defined I think. > >> +static int ti_iodelay_dt_node_to_map(struct pinctrl_dev *pctldev, >> + struct device_node *np, >> + struct pinctrl_map **map, >> + unsigned *num_maps) > > If you use the generic bindings this quite complex parsing and > everything associated with it goes away into the core. > >> +static void ti_iodelay_dt_free_map(struct pinctrl_dev *pctldev, >> + struct pinctrl_map *map, unsigned num_maps) > > Dito. > > BTW even if you proceed this way I suspect these are verbatim > copies from pinctrl-single so they shold obviouslyt be abstracted > out as library functions in that case. > >> +/** >> + * 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; >> +} > > This is not about muxing. It is about enumerating the pins in the > group that will be affected by the setting, which is something you > will want to do when using the generic bindings. > >> + group = ti_iodelay_get_group(iod, gselector); > > And this seems to be your re-implementation of exactly that pin control > core functionality. Which again should be shared with pinctrl-single if > you anyway go down this path. > > I'll halt review here pending discussion on the bindings & stuff. I assume we want to close on binding before looking at implementation - definitely needs a rewrite - only guidance I'd probably need is about which model you'd suggest I should follow(pinconf/pinctrl) once we are done with the bindings discussion. -- --- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver @ 2015-03-11 12:39 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-11 12:39 UTC (permalink / raw) To: linux-arm-kernel On Tue, Mar 10, 2015 at 6:03 AM, Linus Walleij <linus.walleij@linaro.org> wrote: > On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > >> SoC family such as DRA7 family of processors have, in addition >> to the regular muxing of pins (as done by pinctrl-single), an >> additional hardware module called IODelay which is also expected to be >> configured. This "IODelay" module has it's own register space that is >> independent of the control module. >> >> It is advocated strongly in TI's official documentation considering >> the existing design of the DRA7 family of processors during mux or >> IODelay reconfiguration, there is a potential for a significant glitch >> which may cause functional impairment to certain hardware. It is >> hence recommended to do as little of muxing as absolutely necessary >> without I/O isolation (which can only be done in initial stages of >> bootloader). >> >> NOTE: with the system wide I/O isolation scheme present in DRA7 SoC >> family, it is not reasonable to do stop all I/O operations for every >> such pad configuration scheme. So, we will let it glitch when used in >> this mode. >> >> Even with the above limitation, certain functionality such as MMC has >> mandatory need for IODelay reconfiguration requirements, depending on >> speed of transfer. In these cases, with careful examination of usecase >> involved, the expected glitch can be controlled such that it does not >> impact functionality. >> >> In short, IODelay module support as a padconf driver being introduced >> here is not expected to do SoC wide I/O Isolation and is meant for >> a limited subset of IODelay configuration requirements that need to >> be dynamic and whose glitchy behavior will not cause functionality >> failure for that interface. >> >> Signed-off-by: Nishanth Menon <nm@ti.com> >> Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> > > Pending the conclusion of my remark on the bindings that this > should use the generic pin config bindings... here are some > "normal" review comments. > >> +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. > > If the driver is "in addition to PINCTRL_SINGLE", then it > should depend on it. > >> +/* Should I change this? Abuse? */ >> +#define IODELAY_MUX_PINS_NAME "pinctrl-single,pins" > > Yeah this is abuse and is why you should use the generic > bindings with just "pins" instead, and use the generic pinconf > parameters to set them up. > >> + * 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; >> +}; > > So I don't think it's a good idea to get the register offset from > the device tree. The driver should know these. > >> +/** >> + * 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; > > If you're doing reference counting, use kref. > <linux/kref.h> > >> + u16 coarse_delay_count; >> + >> + u16 fine_ref_count; > > Dito. > >> + 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]; >> +}; > > What is this? The pin control subsystem already has a struct > for naming pins. struct pinctrl_pin_desc. > > Are you reimplementing core functionality? > > The generic pin config reference pins by name, and since you > obviously like to encode these into you data you can just > as well use that method for this too. > > Again I think this is a side effect from using the pinctrl-single > concepts, if you think of this as some other pinctrl driver > I think this will fall out nicely. > >> +/** >> + * 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; > > Unhappy about that. pinctrl_desc contains > struct pinctrl_pin_desc const *pins; ! > >> + >> + struct list_head groups; >> + int ngroups; >> + struct mutex mutex; /* list protection */ > > Why is this needed? Usually the pin control core > serialize calls into the drivers. Care to explain? Have > the potential race really triggered? > >> +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); >> +} > > Serious business ... > >> +/** >> + * 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; >> + >> + /* Write with lock value - we DONOT want h/w updates */ >> + reg_mask |= reg->lock_mask; >> + reg_val |= reg->lock_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; >> +} > > Looking at this terse thing I think it's obviously easier to use > a more verbose device tree description with > > drive-delay = <some SI unit>; > > >> + /* 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); > > That sounds hightec. So the bootloader calibrates the delays > and computes the refclock period? So is that sime PLL or something? > >> + 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); > > 88? What does that mean... This should be #defined I think. > >> +static int ti_iodelay_dt_node_to_map(struct pinctrl_dev *pctldev, >> + struct device_node *np, >> + struct pinctrl_map **map, >> + unsigned *num_maps) > > If you use the generic bindings this quite complex parsing and > everything associated with it goes away into the core. > >> +static void ti_iodelay_dt_free_map(struct pinctrl_dev *pctldev, >> + struct pinctrl_map *map, unsigned num_maps) > > Dito. > > BTW even if you proceed this way I suspect these are verbatim > copies from pinctrl-single so they shold obviouslyt be abstracted > out as library functions in that case. > >> +/** >> + * 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; >> +} > > This is not about muxing. It is about enumerating the pins in the > group that will be affected by the setting, which is something you > will want to do when using the generic bindings. > >> + group = ti_iodelay_get_group(iod, gselector); > > And this seems to be your re-implementation of exactly that pin control > core functionality. Which again should be shared with pinctrl-single if > you anyway go down this path. > > I'll halt review here pending discussion on the bindings & stuff. I assume we want to close on binding before looking at implementation - definitely needs a rewrite - only guidance I'd probably need is about which model you'd suggest I should follow(pinconf/pinctrl) once we are done with the bindings discussion. -- --- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver @ 2015-03-11 12:39 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2015-03-11 12:39 UTC (permalink / raw) To: Linus Walleij Cc: devicetree, Tony Lindgren, linux-kernel, linux-gpio, Lokesh Vutla, Linux-OMAP, linux-arm-kernel On Tue, Mar 10, 2015 at 6:03 AM, Linus Walleij <linus.walleij@linaro.org> wrote: > On Wed, Mar 4, 2015 at 1:00 AM, Nishanth Menon <nm@ti.com> wrote: > >> SoC family such as DRA7 family of processors have, in addition >> to the regular muxing of pins (as done by pinctrl-single), an >> additional hardware module called IODelay which is also expected to be >> configured. This "IODelay" module has it's own register space that is >> independent of the control module. >> >> It is advocated strongly in TI's official documentation considering >> the existing design of the DRA7 family of processors during mux or >> IODelay reconfiguration, there is a potential for a significant glitch >> which may cause functional impairment to certain hardware. It is >> hence recommended to do as little of muxing as absolutely necessary >> without I/O isolation (which can only be done in initial stages of >> bootloader). >> >> NOTE: with the system wide I/O isolation scheme present in DRA7 SoC >> family, it is not reasonable to do stop all I/O operations for every >> such pad configuration scheme. So, we will let it glitch when used in >> this mode. >> >> Even with the above limitation, certain functionality such as MMC has >> mandatory need for IODelay reconfiguration requirements, depending on >> speed of transfer. In these cases, with careful examination of usecase >> involved, the expected glitch can be controlled such that it does not >> impact functionality. >> >> In short, IODelay module support as a padconf driver being introduced >> here is not expected to do SoC wide I/O Isolation and is meant for >> a limited subset of IODelay configuration requirements that need to >> be dynamic and whose glitchy behavior will not cause functionality >> failure for that interface. >> >> Signed-off-by: Nishanth Menon <nm@ti.com> >> Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> > > Pending the conclusion of my remark on the bindings that this > should use the generic pin config bindings... here are some > "normal" review comments. > >> +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. > > If the driver is "in addition to PINCTRL_SINGLE", then it > should depend on it. > >> +/* Should I change this? Abuse? */ >> +#define IODELAY_MUX_PINS_NAME "pinctrl-single,pins" > > Yeah this is abuse and is why you should use the generic > bindings with just "pins" instead, and use the generic pinconf > parameters to set them up. > >> + * 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; >> +}; > > So I don't think it's a good idea to get the register offset from > the device tree. The driver should know these. > >> +/** >> + * 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; > > If you're doing reference counting, use kref. > <linux/kref.h> > >> + u16 coarse_delay_count; >> + >> + u16 fine_ref_count; > > Dito. > >> + 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]; >> +}; > > What is this? The pin control subsystem already has a struct > for naming pins. struct pinctrl_pin_desc. > > Are you reimplementing core functionality? > > The generic pin config reference pins by name, and since you > obviously like to encode these into you data you can just > as well use that method for this too. > > Again I think this is a side effect from using the pinctrl-single > concepts, if you think of this as some other pinctrl driver > I think this will fall out nicely. > >> +/** >> + * 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; > > Unhappy about that. pinctrl_desc contains > struct pinctrl_pin_desc const *pins; ! > >> + >> + struct list_head groups; >> + int ngroups; >> + struct mutex mutex; /* list protection */ > > Why is this needed? Usually the pin control core > serialize calls into the drivers. Care to explain? Have > the potential race really triggered? > >> +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); >> +} > > Serious business ... > >> +/** >> + * 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; >> + >> + /* Write with lock value - we DONOT want h/w updates */ >> + reg_mask |= reg->lock_mask; >> + reg_val |= reg->lock_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; >> +} > > Looking at this terse thing I think it's obviously easier to use > a more verbose device tree description with > > drive-delay = <some SI unit>; > > >> + /* 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); > > That sounds hightec. So the bootloader calibrates the delays > and computes the refclock period? So is that sime PLL or something? > >> + 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); > > 88? What does that mean... This should be #defined I think. > >> +static int ti_iodelay_dt_node_to_map(struct pinctrl_dev *pctldev, >> + struct device_node *np, >> + struct pinctrl_map **map, >> + unsigned *num_maps) > > If you use the generic bindings this quite complex parsing and > everything associated with it goes away into the core. > >> +static void ti_iodelay_dt_free_map(struct pinctrl_dev *pctldev, >> + struct pinctrl_map *map, unsigned num_maps) > > Dito. > > BTW even if you proceed this way I suspect these are verbatim > copies from pinctrl-single so they shold obviouslyt be abstracted > out as library functions in that case. > >> +/** >> + * 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; >> +} > > This is not about muxing. It is about enumerating the pins in the > group that will be affected by the setting, which is something you > will want to do when using the generic bindings. > >> + group = ti_iodelay_get_group(iod, gselector); > > And this seems to be your re-implementation of exactly that pin control > core functionality. Which again should be shared with pinctrl-single if > you anyway go down this path. > > I'll halt review here pending discussion on the bindings & stuff. I assume we want to close on binding before looking at implementation - definitely needs a rewrite - only guidance I'd probably need is about which model you'd suggest I should follow(pinconf/pinctrl) once we are done with the bindings discussion. -- --- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 0/2] Add TI iodelay driver using #pinctrl-cells @ 2016-12-30 18:37 Tony Lindgren 2016-12-30 18:37 ` [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver Tony Lindgren 0 siblings, 1 reply; 74+ messages in thread From: Tony Lindgren @ 2016-12-30 18:37 UTC (permalink / raw) To: Linus Walleij Cc: Gary Bisson, Grygorii Strashko, Mark Rutland, Nishanth Menon, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, linux-gpio-u79uwXL29TY76Z2rM5mHXA, linux-omap-u79uwXL29TY76Z2rM5mHXA Hi all, Here's a respin of Nishanth's iodelay driver that I've updated to use #pinctrl-cells and generic pinctrl group and function code. Gary, note that this one has an iterator function that you may be able to use too, and hopefully we can simplify things further eventually by introducing a generic iterator. Note that these patches are against the current pinctrl devel branch at commit b61d1546906d ("pinctrl: single: Use generic pinmux helpers for managing functions"). Regards, Tony Nishanth Menon (1): pinctrl: Introduce TI IOdelay configuration driver Tony Lindgren (1): pinctrl: core: Make dt_free_map optional .../devicetree/bindings/pinctrl/ti,iodelay.txt | 47 + drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/core.c | 3 - drivers/pinctrl/devicetree.c | 3 +- drivers/pinctrl/ti/Kconfig | 10 + drivers/pinctrl/ti/Makefile | 1 + drivers/pinctrl/ti/pinctrl-ti-iodelay.c | 945 +++++++++++++++++++++ 8 files changed, 1007 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt create mode 100644 drivers/pinctrl/ti/Kconfig create mode 100644 drivers/pinctrl/ti/Makefile create mode 100644 drivers/pinctrl/ti/pinctrl-ti-iodelay.c -- 2.11.0 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver 2016-12-30 18:37 [PATCH 0/2] Add TI iodelay driver using #pinctrl-cells Tony Lindgren @ 2016-12-30 18:37 ` Tony Lindgren [not found] ` <20161230183732.5595-3-tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> 0 siblings, 1 reply; 74+ messages in thread From: Tony Lindgren @ 2016-12-30 18:37 UTC (permalink / raw) To: Linus Walleij Cc: Gary Bisson, Grygorii Strashko, Mark Rutland, Nishanth Menon, Rob Herring, devicetree, linux-gpio, linux-omap, Lokesh Vutla From: Nishanth Menon <nm@ti.com> SoC family such as DRA7 family of processors have, in addition to the regular muxing of pins (as done by pinctrl-single), a separate hardware module called IODelay which is also expected to be configured. The "IODelay" module has it's own register space that is independent of the control module and the padconf register area. With recent changes to the pinctrl framework, we can now support this hardware with a reasonably minimal driver by using #pinctrl-cells, GENERIC_PINCTRL_GROUPS and GENERIC_PINMUX_FUNCTIONS. It is advocated strongly in TI's official documentation considering the existing design of the DRA7 family of processors during mux or IODelay reconfiguration, there is a potential for a significant glitch which may cause functional impairment to certain hardware. It is hence recommended to do as little of muxing as absolutely necessary without I/O isolation (which can only be done in initial stages of bootloader). NOTE: with the system wide I/O isolation scheme present in DRA7 SoC family, it is not reasonable to do stop all I/O operations for every such pad configuration scheme. So, we will let it glitch when used in this mode. Even with the above limitation, certain functionality such as MMC has mandatory need for IODelay reconfiguration requirements, depending on speed of transfer. In these cases, with careful examination of usecase involved, the expected glitch can be controlled such that it does not impact functionality. In short, IODelay module support as a padconf driver being introduced here is not expected to do SoC wide I/O Isolation and is meant for a limited subset of IODelay configuration requirements that need to be dynamic and whose glitchy behavior will not cause functionality failure for that interface. IMPORTANT NOTE: we take the approach of keeping LOCK_BITs cleared to 0x0 at all times, even when configuring Manual IO Timing Modes. This is done by eliminating the LOCK_BIT=1 setting from Step of the Manual IO timing Mode configuration procedure. This option leaves the CFG_* registers unprotected from unintended writes to the CTRL_CORE_PAD_* registers while Manual IO Timing Modes are configured. This approach is taken to allow for a generic driver to exist in kernel world that has to be used carefully in required usecases. Signed-off-by: Nishanth Menon <nm@ti.com> Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> [tony@atomide.com: updated to use generic pinctrl functions, added binding documentation, updated comments] Signed-off-by: Tony Lindgren <tony@atomide.com> --- .../devicetree/bindings/pinctrl/ti,iodelay.txt | 47 + drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/ti/Kconfig | 10 + drivers/pinctrl/ti/Makefile | 1 + drivers/pinctrl/ti/pinctrl-ti-iodelay.c | 945 +++++++++++++++++++++ 6 files changed, 1005 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt create mode 100644 drivers/pinctrl/ti/Kconfig create mode 100644 drivers/pinctrl/ti/Makefile create mode 100644 drivers/pinctrl/ti/pinctrl-ti-iodelay.c diff --git a/Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt b/Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt new file mode 100644 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt @@ -0,0 +1,47 @@ +* Pin configuration for TI IODELAY controller + +TI dra7 based SoCs such as am57xx have a controller for setting the IO delay +for each pin. For most part the IO delay values are programmed by the bootloader, +but some pins need to be configured dynamically by the kernel such as the +MMC pins. + +Required Properties: + + - compatible: Must be "ti,dra7-iodelay" + - reg: Base address and length of the memory resource used + - #address-cells: Number of address cells + - #size-cells: Size of cells + - #pinctrl-cells: Number of pinctrl cells, must be 2. See also + Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt + +Example +------- + +In the SoC specific dtsi file: + + dra7_iodelay_core: padconf@4844a000 { + compatible = "ti,dra7-iodelay"; + reg = <0x4844a000 0x0d1c>; + #address-cells = <1>; + #size-cells = <0>; + #pinctrl-cells = <2>; + }; + +In board-specific file: + +&dra7_iodelay_core { + mmc2_iodelay_3v3_conf: mmc2_iodelay_3v3_conf { + pinctrl-pin-array = < + 0x18c A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A19_IN */ + 0x1a4 A_DELAY_PS(265) G_DELAY_PS(360) /* CFG_GPMC_A20_IN */ + 0x1b0 A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A21_IN */ + 0x1bc A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A22_IN */ + 0x1c8 A_DELAY_PS(287) G_DELAY_PS(420) /* CFG_GPMC_A23_IN */ + 0x1d4 A_DELAY_PS(144) G_DELAY_PS(240) /* CFG_GPMC_A24_IN */ + 0x1e0 A_DELAY_PS(0) G_DELAY_PS(0) /* CFG_GPMC_A25_IN */ + 0x1ec A_DELAY_PS(120) G_DELAY_PS(0) /* CFG_GPMC_A26_IN */ + 0x1f8 A_DELAY_PS(120) G_DELAY_PS(180) /* CFG_GPMC_A27_IN */ + 0x360 A_DELAY_PS(0) G_DELAY_PS(0) /* CFG_GPMC_CS1_IN */ + >; + }; +}; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -300,6 +300,7 @@ source "drivers/pinctrl/spear/Kconfig" source "drivers/pinctrl/stm32/Kconfig" source "drivers/pinctrl/sunxi/Kconfig" source "drivers/pinctrl/tegra/Kconfig" +source "drivers/pinctrl/ti/Kconfig" source "drivers/pinctrl/uniphier/Kconfig" source "drivers/pinctrl/vt8500/Kconfig" source "drivers/pinctrl/mediatek/Kconfig" diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_PINCTRL_SH_PFC) += sh-pfc/ obj-$(CONFIG_PINCTRL_SPEAR) += spear/ obj-$(CONFIG_PINCTRL_STM32) += stm32/ obj-$(CONFIG_PINCTRL_SUNXI) += sunxi/ +obj-y += ti/ obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/ obj-$(CONFIG_ARCH_VT8500) += vt8500/ obj-$(CONFIG_PINCTRL_MTK) += mediatek/ diff --git a/drivers/pinctrl/ti/Kconfig b/drivers/pinctrl/ti/Kconfig new file mode 100644 --- /dev/null +++ b/drivers/pinctrl/ti/Kconfig @@ -0,0 +1,10 @@ +config PINCTRL_TI_IODELAY + tristate "TI IODelay Module pinconf driver" + depends on OF + select GENERIC_PINCTRL_GROUPS + select GENERIC_PINMUX_FUNCTIONS + select GENERIC_PINCONF + select REGMAP_MMIO + help + Say Y here to support Texas Instruments' IO delay pinconf driver. + IO delay module is used for the DRA7 SoC family. diff --git a/drivers/pinctrl/ti/Makefile b/drivers/pinctrl/ti/Makefile new file mode 100644 --- /dev/null +++ b/drivers/pinctrl/ti/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PINCTRL_TI_IODELAY) += pinctrl-ti-iodelay.o diff --git a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c new file mode 100644 --- /dev/null +++ b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c @@ -0,0 +1,945 @@ +/* + * 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 <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include "../core.h" +#include "../devicetree.h" + +#define DRIVER_NAME "ti-iodelay" + +/** + * struct ti_iodelay_reg_data - Describes the registers for the iodelay instance + * @signature_mask: CONFIG_REG mask for the signature bits (see TRM) + * @signature_value: CONFIG_REG signature value to be written (see TRM) + * @lock_mask: CONFIG_REG mask for the lock bits (see TRM) + * @lock_val: CONFIG_REG lock value for the lock bits (see TRM) + * @unlock_val:CONFIG_REG unlock value for the lock bits (see TRM) + * @binary_data_coarse_mask: CONFIG_REG coarse mask (see TRM) + * @binary_data_fine_mask: CONFIG_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 iodelay module lock register offset + * @global_lock_mask: Lock mask + * @global_unlock_val: Unlock value + * @global_lock_val: Lock value + * @reg_start_offset: Offset to iodelay registers after the CONFIG_REG_0 to 8 + * @reg_nr_per_pin: Number of iodelay registers for each pin + * @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; + u32 reg_nr_per_pin; + + 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_cfg - Description of each configuration parameters + * @offset: Configuration register offset + * @a_delay: Agnostic Delay (in ps) + * @g_delay: Gnostic Delay (in ps) + */ +struct ti_iodelay_cfg { + u16 offset; + u16 a_delay; + u16 g_delay; +}; + +/** + * struct ti_iodelay_pingroup - Structure that describes one group + * @name: Name of the group + * @map: pinctrl map allocated for the group + * @cfg: configuration array for the pin (from dt) + * @ncfg: number of configuration values allocated + * @config: pinconf "Config" - currently a dummy value + */ +struct ti_iodelay_pingroup { + struct ti_iodelay_cfg *cfg; + int ncfg; + unsigned long config; +}; + +/** + * struct ti_iodelay_device - Represents information for a iodelay instance + * @dev: Device pointer + * @phys_base: Physical address base of the iodelay device + * @reg_base: Virtual address base of the iodelay device + * @regmap: Regmap for this iodelay instance + * @pctl: Pinctrl device + * @desc: pinctrl descriptor for pctl + * @pa: pinctrl pin wise description + * @names: names of the pins + * @reg_data: Register definition data for the IODelay instance + * @reg_init_conf_values: Initial configuration values. + */ +struct ti_iodelay_device { + struct device *dev; + unsigned long phys_base; + void __iomem *reg_base; + struct regmap *regmap; + + struct pinctrl_dev *pctl; + struct pinctrl_desc desc; + struct pinctrl_pin_desc *pa; + + const struct ti_iodelay_reg_data *reg_data; + struct ti_iodelay_reg_values reg_init_conf_values; +}; + +/** + * 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_cfg *cfg) +{ + 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 = cfg->g_delay / 920; + g_delay_fine = ((cfg->g_delay % 920) * 10) / 60; + + a_delay_coarse = cfg->a_delay / ival->cdpe; + a_delay_fine = ((cfg->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, cfg->offset, reg_mask, reg_val); + + dev_info(dev, "Set reg 0x%x Delay(a: %d g: %d), Elements(C=%d F=%d)0x%x\n", + cfg->offset, cfg->a_delay, cfg->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); +} + +/** + * ti_iodelay_get_pingroup() - Find the group mapped by a group selector + * @iod: iodelay device + * @selector: Group Selector + * + * Return: Corresponding group representing group selector + */ +static struct ti_iodelay_pingroup * +ti_iodelay_get_pingroup(struct ti_iodelay_device *iod, unsigned int selector) +{ + struct group_desc *g; + + g = pinctrl_generic_get_group(iod->pctl, selector); + if (!g) { + dev_err(iod->dev, "%s could not find pingroup %i\n", __func__, + selector); + + return NULL; + } + + return g->data; +} + +/** + * ti_iodelay_pin_to_offset() - get pin register offset based on the pin index + * @iod: iodelay driver instance + * @selector: Pin index + */ +static unsigned int ti_iodelay_pin_to_offset(struct ti_iodelay_device *iod, + unsigned int selector) +{ + const struct ti_iodelay_reg_data *r = iod->reg_data; + unsigned int offset; + + offset = selector * r->regmap_config->reg_stride; + offset *= r->reg_nr_per_pin; + offset += r->reg_start_offset; + + return offset; +} + +/** + * ti_iodelay_offset_to_pin() - get a pin index based on the register offset + * @iod: iodelay driver instance + * @offset: register offset from the base + */ +static int ti_iodelay_offset_to_pin(struct ti_iodelay_device *iod, + unsigned int offset) +{ + const struct ti_iodelay_reg_data *r = iod->reg_data; + unsigned int index; + + if (offset > r->regmap_config->max_register) { + dev_err(iod->dev, "mux offset out of range: 0x%x (0x%x)\n", + offset, r->regmap_config->max_register); + return -EINVAL; + } + + index = (offset - r->reg_start_offset) / r->regmap_config->reg_stride; + index /= r->reg_nr_per_pin; + + return index; +} + +/** + * + * @pctldev: Pin controller driver + * @np: Device node + * @pinctrl_spec: Parsed arguments from device tree + * @pins: Array of pins in the pin group + * @pin_index: Pin index in the pin array + * @data: Pin controller driver specific data + * + */ +static int ti_iodelay_node_iterator(struct pinctrl_dev *pctldev, + struct device_node *np, + const struct of_phandle_args *pinctrl_spec, + int *pins, int pin_index, void *data) +{ + struct ti_iodelay_device *iod; + struct ti_iodelay_cfg *cfg = data; + const struct ti_iodelay_reg_data *r; + struct pinctrl_pin_desc *pd; + int pin; + + iod = pinctrl_dev_get_drvdata(pctldev); + if (!iod) + return -EINVAL; + + r = iod->reg_data; + + if (pinctrl_spec->args_count < r->reg_nr_per_pin) { + dev_err(iod->dev, "invalid args_count for spec: %i\n", + pinctrl_spec->args_count); + + return -EINVAL; + } + + /* Index plus two value cells */ + cfg[pin_index].offset = pinctrl_spec->args[0]; + cfg[pin_index].a_delay = pinctrl_spec->args[1] & 0xffff; + cfg[pin_index].g_delay = pinctrl_spec->args[2] & 0xffff; + + pin = ti_iodelay_offset_to_pin(iod, cfg[pin_index].offset); + if (pin < 0) { + dev_err(iod->dev, "could not add functions for %s %ux\n", + np->name, cfg[pin_index].offset); + return -ENODEV; + } + pins[pin_index] = pin; + + pd = &iod->pa[pin]; + pd->drv_data = &cfg[pin_index]; + + dev_dbg(iod->dev, "%s offset=%x a_delay = %d g_delay = %d\n", + np->name, cfg[pin_index].offset, cfg[pin_index].a_delay, + cfg[pin_index].g_delay); + + return 0; +} + +/** + * 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 int *num_maps) +{ + struct ti_iodelay_device *iod; + struct ti_iodelay_cfg *cfg; + struct ti_iodelay_pingroup *g; + const char *name = "pinctrl-pin-array"; + int rows, *pins, error = -EINVAL, i; + + iod = pinctrl_dev_get_drvdata(pctldev); + if (!iod) + return -EINVAL; + + rows = pinctrl_count_index_with_args(np, name); + if (rows == -EINVAL) + return rows; + + *map = devm_kzalloc(iod->dev, sizeof(**map), GFP_KERNEL); + if (!*map) + return -ENOMEM; + *num_maps = 0; + + g = devm_kzalloc(iod->dev, sizeof(*g), GFP_KERNEL); + if (!g) { + error = -ENOMEM; + goto free_map; + } + + pins = devm_kzalloc(iod->dev, sizeof(*pins) * rows, GFP_KERNEL); + if (!pins) + goto free_group; + + cfg = devm_kzalloc(iod->dev, sizeof(*cfg) * rows, GFP_KERNEL); + if (!cfg) { + error = -ENOMEM; + goto free_pins; + } + + for (i = 0; i < rows; i++) { + struct of_phandle_args pinctrl_spec; + + error = pinctrl_parse_index_with_args(np, name, i, + &pinctrl_spec); + if (error) + goto free_data; + + error = ti_iodelay_node_iterator(pctldev, np, &pinctrl_spec, + pins, i, cfg); + if (error) + goto free_data; + } + + g->cfg = cfg; + g->ncfg = i; + g->config = PIN_CONFIG_END; + + error = pinctrl_generic_add_group(iod->pctl, np->name, pins, i, g); + if (error < 0) + goto free_data; + + (*map)->type = PIN_MAP_TYPE_CONFIGS_GROUP; + (*map)->data.configs.group_or_pin = np->name; + (*map)->data.configs.configs = &g->config; + (*map)->data.configs.num_configs = 1; + *num_maps = 1; + + return 0; + +free_data: + devm_kfree(iod->dev, cfg); +free_pins: + devm_kfree(iod->dev, pins); +free_group: + devm_kfree(iod->dev, g); +free_map: + devm_kfree(iod->dev, *map); + + return error; +} + +/** + * ti_iodelay_pinconf_group_get() - Get the group configuration + * @pctldev: pinctrl device representing IODelay device + * @selector: 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 int selector, + 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_pingroup(iod, selector); + + 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 + * @selector: 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 int selector, + unsigned long *configs, + unsigned int 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_pingroup(iod, selector); + + 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->ncfg; i++) { + if (ti_iodelay_pinconf_set(iod, &group->cfg[i])) + return -ENOTSUPP; + } + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void ti_iodelay_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned int pin) +{ + struct ti_iodelay_device *iod; + struct pinctrl_pin_desc *pd; + struct ti_iodelay_cfg *cfg; + const struct ti_iodelay_reg_data *r; + unsigned long offset; + u32 in, oen, out; + + iod = pinctrl_dev_get_drvdata(pctldev); + r = iod->reg_data; + + offset = ti_iodelay_pin_to_offset(iod, pin); + if (pin < 0) { + dev_err(iod->dev, "invalid pin offset for pin%i\n", pin); + + return; + } + + pd = &iod->pa[pin]; + cfg = pd->drv_data; + + regmap_read(iod->regmap, offset, &in); + regmap_read(iod->regmap, offset + r->regmap_config->reg_stride, &oen); + regmap_read(iod->regmap, offset + r->regmap_config->reg_stride * 2, + &out); + + seq_printf(s, "%lx a: %i g: %i (%08x %08x %08x) %s ", + iod->phys_base + offset, + cfg ? cfg->a_delay : -1, + cfg ? cfg->g_delay : -1, + in, oen, out, DRIVER_NAME); +} + +/** + * ti_iodelay_pinconf_group_dbg_show() - show the group information + * @pctldev: Show the group information + * @s: Sequence file + * @selector: 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 int selector) +{ + 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_pingroup(iod, selector); + if (!group) + return; + + for (i = 0; i < group->ncfg; i++) { + struct ti_iodelay_cfg *cfg; + u32 reg = 0; + + cfg = &group->cfg[i]; + regmap_read(iod->regmap, cfg->offset, ®), + seq_printf(s, "\n\t0x%08x = 0x%08x (%3d, %3d)", + cfg->offset, reg, cfg->a_delay, + cfg->g_delay); + } +} +#endif + +static struct pinctrl_ops ti_iodelay_pinctrl_ops = { + .get_groups_count = pinctrl_generic_get_group_count, + .get_group_name = pinctrl_generic_get_group_name, + .get_group_pins = pinctrl_generic_get_group_pins, + .pin_dbg_show = ti_iodelay_pin_dbg_show, + .dt_node_to_map = ti_iodelay_dt_node_to_map, +}; + +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; + u32 phy_reg; + int nr_pins, i; + + nr_pins = ti_iodelay_offset_to_pin(iod, r->regmap_config->max_register); + 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->desc.pins = iod->pa; + iod->desc.npins = nr_pins; + + phy_reg = r->reg_start_offset + base_phy; + + for (i = 0; i < nr_pins; i++, phy_reg += 4) { + pin = &iod->pa[i]; + pin->number = i; + } + + 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, + .reg_nr_per_pin = 3, + .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); + +/** + * 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->phys_base = res->start; + 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; + + 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 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"); -- 2.11.0 ^ permalink raw reply [flat|nested] 74+ messages in thread
[parent not found: <20161230183732.5595-3-tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>]
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver [not found] ` <20161230183732.5595-3-tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> @ 2016-12-31 22:52 ` kbuild test robot [not found] ` <201701010645.EBjk2p8Y%fengguang.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> 2017-01-01 2:02 ` kbuild test robot 2017-01-02 22:12 ` Tony Lindgren 2 siblings, 1 reply; 74+ messages in thread From: kbuild test robot @ 2016-12-31 22:52 UTC (permalink / raw) To: Tony Lindgren Cc: kbuild-all-JC7UmRfGjtg, Linus Walleij, Gary Bisson, Grygorii Strashko, Mark Rutland, Nishanth Menon, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, linux-gpio-u79uwXL29TY76Z2rM5mHXA, linux-omap-u79uwXL29TY76Z2rM5mHXA, Lokesh Vutla [-- Attachment #1: Type: text/plain, Size: 3592 bytes --] Hi Nishanth, [auto build test ERROR on pinctrl/for-next] [also build test ERROR on v4.10-rc1 next-20161224] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Tony-Lindgren/Add-TI-iodelay-driver-using-pinctrl-cells/20161231-024542 base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git for-next config: arm64-allmodconfig (attached as .config) compiler: aarch64-linux-gnu-gcc (Debian 6.1.1-9) 6.1.1 20160705 reproduce: wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # save the attached .config to linux build tree make.cross ARCH=arm64 All errors (new ones prefixed by >>): drivers/pinctrl/ti/pinctrl-ti-iodelay.c: In function 'ti_iodelay_get_pingroup': >> drivers/pinctrl/ti/pinctrl-ti-iodelay.c:383:6: error: implicit declaration of function 'pinctrl_generic_get_group' [-Werror=implicit-function-declaration] g = pinctrl_generic_get_group(iod->pctl, selector); ^~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pinctrl/ti/pinctrl-ti-iodelay.c:383:4: warning: assignment makes pointer from integer without a cast [-Wint-conversion] g = pinctrl_generic_get_group(iod->pctl, selector); ^ drivers/pinctrl/ti/pinctrl-ti-iodelay.c:391:10: error: dereferencing pointer to incomplete type 'struct group_desc' return g->data; ^~ drivers/pinctrl/ti/pinctrl-ti-iodelay.c: In function 'ti_iodelay_dt_node_to_map': drivers/pinctrl/ti/pinctrl-ti-iodelay.c:562:10: error: implicit declaration of function 'pinctrl_generic_add_group' [-Werror=implicit-function-declaration] error = pinctrl_generic_add_group(iod->pctl, np->name, pins, i, g); ^~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pinctrl/ti/pinctrl-ti-iodelay.c: At top level: >> drivers/pinctrl/ti/pinctrl-ti-iodelay.c:729:22: error: 'pinctrl_generic_get_group_count' undeclared here (not in a function) .get_groups_count = pinctrl_generic_get_group_count, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pinctrl/ti/pinctrl-ti-iodelay.c:730:20: error: 'pinctrl_generic_get_group_name' undeclared here (not in a function) .get_group_name = pinctrl_generic_get_group_name, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pinctrl/ti/pinctrl-ti-iodelay.c:731:20: error: 'pinctrl_generic_get_group_pins' undeclared here (not in a function) .get_group_pins = pinctrl_generic_get_group_pins, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pinctrl/ti/pinctrl-ti-iodelay.c: In function 'ti_iodelay_get_pingroup': drivers/pinctrl/ti/pinctrl-ti-iodelay.c:392:1: warning: control reaches end of non-void function [-Wreturn-type] } ^ cc1: some warnings being treated as errors vim +/pinctrl_generic_get_group +383 drivers/pinctrl/ti/pinctrl-ti-iodelay.c 377 */ 378 static struct ti_iodelay_pingroup * 379 ti_iodelay_get_pingroup(struct ti_iodelay_device *iod, unsigned int selector) 380 { 381 struct group_desc *g; 382 > 383 g = pinctrl_generic_get_group(iod->pctl, selector); 384 if (!g) { 385 dev_err(iod->dev, "%s could not find pingroup %i\n", __func__, 386 selector); 387 388 return NULL; 389 } 390 > 391 return g->data; 392 } 393 394 /** --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/gzip, Size: 53495 bytes --] ^ permalink raw reply [flat|nested] 74+ messages in thread
[parent not found: <201701010645.EBjk2p8Y%fengguang.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>]
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver [not found] ` <201701010645.EBjk2p8Y%fengguang.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> @ 2017-01-02 18:04 ` Tony Lindgren 0 siblings, 0 replies; 74+ messages in thread From: Tony Lindgren @ 2017-01-02 18:04 UTC (permalink / raw) To: kbuild test robot Cc: kbuild-all-JC7UmRfGjtg, Linus Walleij, Gary Bisson, Grygorii Strashko, Mark Rutland, Nishanth Menon, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, linux-gpio-u79uwXL29TY76Z2rM5mHXA, linux-omap-u79uwXL29TY76Z2rM5mHXA, Lokesh Vutla Hi, * kbuild test robot <lkp-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> [161231 14:53]: > Hi Nishanth, > > [auto build test ERROR on pinctrl/for-next] > [also build test ERROR on v4.10-rc1 next-20161224] > [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] This patch is against pinctrl/devel as noted in the cover letter. Next does not yet have the dependencies. I did note another build error with COMPILE_TEST though if DEBUG_FS is not selected, will send a fix for that. Regards, Tony -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver [not found] ` <20161230183732.5595-3-tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> 2016-12-31 22:52 ` kbuild test robot @ 2017-01-01 2:02 ` kbuild test robot 2017-01-02 22:12 ` Tony Lindgren 2 siblings, 0 replies; 74+ messages in thread From: kbuild test robot @ 2017-01-01 2:02 UTC (permalink / raw) To: Tony Lindgren Cc: kbuild-all-JC7UmRfGjtg, Linus Walleij, Gary Bisson, Grygorii Strashko, Mark Rutland, Nishanth Menon, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, linux-gpio-u79uwXL29TY76Z2rM5mHXA, linux-omap-u79uwXL29TY76Z2rM5mHXA, Lokesh Vutla [-- Attachment #1: Type: text/plain, Size: 8995 bytes --] Hi Nishanth, [auto build test ERROR on pinctrl/for-next] [also build test ERROR on v4.10-rc1 next-20161224] [if your patch is applied to the wrong git tree, please drop us a note to help improve the system] url: https://github.com/0day-ci/linux/commits/Tony-Lindgren/Add-TI-iodelay-driver-using-pinctrl-cells/20161231-024542 base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git for-next config: i386-allyesconfig (attached as .config) compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901 reproduce: # save the attached .config to linux build tree make ARCH=i386 All errors (new ones prefixed by >>): drivers/pinctrl/ti/pinctrl-ti-iodelay.c: In function 'ti_iodelay_get_pingroup': drivers/pinctrl/ti/pinctrl-ti-iodelay.c:383:6: error: implicit declaration of function 'pinctrl_generic_get_group' [-Werror=implicit-function-declaration] g = pinctrl_generic_get_group(iod->pctl, selector); ^~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pinctrl/ti/pinctrl-ti-iodelay.c:383:4: warning: assignment makes pointer from integer without a cast [-Wint-conversion] g = pinctrl_generic_get_group(iod->pctl, selector); ^ >> drivers/pinctrl/ti/pinctrl-ti-iodelay.c:391:10: error: dereferencing pointer to incomplete type 'struct group_desc' return g->data; ^~ drivers/pinctrl/ti/pinctrl-ti-iodelay.c: In function 'ti_iodelay_dt_node_to_map': >> drivers/pinctrl/ti/pinctrl-ti-iodelay.c:562:10: error: implicit declaration of function 'pinctrl_generic_add_group' [-Werror=implicit-function-declaration] error = pinctrl_generic_add_group(iod->pctl, np->name, pins, i, g); ^~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pinctrl/ti/pinctrl-ti-iodelay.c: At top level: drivers/pinctrl/ti/pinctrl-ti-iodelay.c:729:22: error: 'pinctrl_generic_get_group_count' undeclared here (not in a function) .get_groups_count = pinctrl_generic_get_group_count, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> drivers/pinctrl/ti/pinctrl-ti-iodelay.c:730:20: error: 'pinctrl_generic_get_group_name' undeclared here (not in a function) .get_group_name = pinctrl_generic_get_group_name, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >> drivers/pinctrl/ti/pinctrl-ti-iodelay.c:731:20: error: 'pinctrl_generic_get_group_pins' undeclared here (not in a function) .get_group_pins = pinctrl_generic_get_group_pins, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/pinctrl/ti/pinctrl-ti-iodelay.c: In function 'ti_iodelay_get_pingroup': drivers/pinctrl/ti/pinctrl-ti-iodelay.c:392:1: warning: control reaches end of non-void function [-Wreturn-type] } ^ cc1: some warnings being treated as errors vim +391 drivers/pinctrl/ti/pinctrl-ti-iodelay.c 377 */ 378 static struct ti_iodelay_pingroup * 379 ti_iodelay_get_pingroup(struct ti_iodelay_device *iod, unsigned int selector) 380 { 381 struct group_desc *g; 382 > 383 g = pinctrl_generic_get_group(iod->pctl, selector); 384 if (!g) { 385 dev_err(iod->dev, "%s could not find pingroup %i\n", __func__, 386 selector); 387 388 return NULL; 389 } 390 > 391 return g->data; 392 } 393 394 /** 395 * ti_iodelay_pin_to_offset() - get pin register offset based on the pin index 396 * @iod: iodelay driver instance 397 * @selector: Pin index 398 */ 399 static unsigned int ti_iodelay_pin_to_offset(struct ti_iodelay_device *iod, 400 unsigned int selector) 401 { 402 const struct ti_iodelay_reg_data *r = iod->reg_data; 403 unsigned int offset; 404 405 offset = selector * r->regmap_config->reg_stride; 406 offset *= r->reg_nr_per_pin; 407 offset += r->reg_start_offset; 408 409 return offset; 410 } 411 412 /** 413 * ti_iodelay_offset_to_pin() - get a pin index based on the register offset 414 * @iod: iodelay driver instance 415 * @offset: register offset from the base 416 */ 417 static int ti_iodelay_offset_to_pin(struct ti_iodelay_device *iod, 418 unsigned int offset) 419 { 420 const struct ti_iodelay_reg_data *r = iod->reg_data; 421 unsigned int index; 422 423 if (offset > r->regmap_config->max_register) { 424 dev_err(iod->dev, "mux offset out of range: 0x%x (0x%x)\n", 425 offset, r->regmap_config->max_register); 426 return -EINVAL; 427 } 428 429 index = (offset - r->reg_start_offset) / r->regmap_config->reg_stride; 430 index /= r->reg_nr_per_pin; 431 432 return index; 433 } 434 435 /** 436 * 437 * @pctldev: Pin controller driver 438 * @np: Device node 439 * @pinctrl_spec: Parsed arguments from device tree 440 * @pins: Array of pins in the pin group 441 * @pin_index: Pin index in the pin array 442 * @data: Pin controller driver specific data 443 * 444 */ 445 static int ti_iodelay_node_iterator(struct pinctrl_dev *pctldev, 446 struct device_node *np, 447 const struct of_phandle_args *pinctrl_spec, 448 int *pins, int pin_index, void *data) 449 { 450 struct ti_iodelay_device *iod; 451 struct ti_iodelay_cfg *cfg = data; 452 const struct ti_iodelay_reg_data *r; 453 struct pinctrl_pin_desc *pd; 454 int pin; 455 456 iod = pinctrl_dev_get_drvdata(pctldev); 457 if (!iod) 458 return -EINVAL; 459 460 r = iod->reg_data; 461 462 if (pinctrl_spec->args_count < r->reg_nr_per_pin) { 463 dev_err(iod->dev, "invalid args_count for spec: %i\n", 464 pinctrl_spec->args_count); 465 466 return -EINVAL; 467 } 468 469 /* Index plus two value cells */ 470 cfg[pin_index].offset = pinctrl_spec->args[0]; 471 cfg[pin_index].a_delay = pinctrl_spec->args[1] & 0xffff; 472 cfg[pin_index].g_delay = pinctrl_spec->args[2] & 0xffff; 473 474 pin = ti_iodelay_offset_to_pin(iod, cfg[pin_index].offset); 475 if (pin < 0) { 476 dev_err(iod->dev, "could not add functions for %s %ux\n", 477 np->name, cfg[pin_index].offset); 478 return -ENODEV; 479 } 480 pins[pin_index] = pin; 481 482 pd = &iod->pa[pin]; 483 pd->drv_data = &cfg[pin_index]; 484 485 dev_dbg(iod->dev, "%s offset=%x a_delay = %d g_delay = %d\n", 486 np->name, cfg[pin_index].offset, cfg[pin_index].a_delay, 487 cfg[pin_index].g_delay); 488 489 return 0; 490 } 491 492 /** 493 * ti_iodelay_dt_node_to_map() - Map a device tree node to appropriate group 494 * @pctldev: pinctrl device representing IODelay device 495 * @np: Node Pointer (device tree) 496 * @map: Pinctrl Map returned back to pinctrl framework 497 * @num_maps: Number of maps (1) 498 * 499 * Maps the device tree description into a group of configuration parameters 500 * for iodelay block entry. 501 * 502 * Return: 0 in case of success, else appropriate error value 503 */ 504 static int ti_iodelay_dt_node_to_map(struct pinctrl_dev *pctldev, 505 struct device_node *np, 506 struct pinctrl_map **map, 507 unsigned int *num_maps) 508 { 509 struct ti_iodelay_device *iod; 510 struct ti_iodelay_cfg *cfg; 511 struct ti_iodelay_pingroup *g; 512 const char *name = "pinctrl-pin-array"; 513 int rows, *pins, error = -EINVAL, i; 514 515 iod = pinctrl_dev_get_drvdata(pctldev); 516 if (!iod) 517 return -EINVAL; 518 519 rows = pinctrl_count_index_with_args(np, name); 520 if (rows == -EINVAL) 521 return rows; 522 523 *map = devm_kzalloc(iod->dev, sizeof(**map), GFP_KERNEL); 524 if (!*map) 525 return -ENOMEM; 526 *num_maps = 0; 527 528 g = devm_kzalloc(iod->dev, sizeof(*g), GFP_KERNEL); 529 if (!g) { 530 error = -ENOMEM; 531 goto free_map; 532 } 533 534 pins = devm_kzalloc(iod->dev, sizeof(*pins) * rows, GFP_KERNEL); 535 if (!pins) 536 goto free_group; 537 538 cfg = devm_kzalloc(iod->dev, sizeof(*cfg) * rows, GFP_KERNEL); 539 if (!cfg) { 540 error = -ENOMEM; 541 goto free_pins; 542 } 543 544 for (i = 0; i < rows; i++) { 545 struct of_phandle_args pinctrl_spec; 546 547 error = pinctrl_parse_index_with_args(np, name, i, 548 &pinctrl_spec); 549 if (error) 550 goto free_data; 551 552 error = ti_iodelay_node_iterator(pctldev, np, &pinctrl_spec, 553 pins, i, cfg); 554 if (error) 555 goto free_data; 556 } 557 558 g->cfg = cfg; 559 g->ncfg = i; 560 g->config = PIN_CONFIG_END; 561 > 562 error = pinctrl_generic_add_group(iod->pctl, np->name, pins, i, g); 563 if (error < 0) 564 goto free_data; 565 --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation [-- Attachment #2: .config.gz --] [-- Type: application/gzip, Size: 57326 bytes --] ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver [not found] ` <20161230183732.5595-3-tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> 2016-12-31 22:52 ` kbuild test robot 2017-01-01 2:02 ` kbuild test robot @ 2017-01-02 22:12 ` Tony Lindgren 2017-01-04 13:32 ` Rob Herring ` (2 more replies) 2 siblings, 3 replies; 74+ messages in thread From: Tony Lindgren @ 2017-01-02 22:12 UTC (permalink / raw) To: Linus Walleij Cc: Gary Bisson, Grygorii Strashko, Mark Rutland, Nishanth Menon, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, linux-gpio-u79uwXL29TY76Z2rM5mHXA, linux-omap-u79uwXL29TY76Z2rM5mHXA, Lokesh Vutla * Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> [161230 10:38]: > +static struct pinctrl_ops ti_iodelay_pinctrl_ops = { > + .get_groups_count = pinctrl_generic_get_group_count, > + .get_group_name = pinctrl_generic_get_group_name, > + .get_group_pins = pinctrl_generic_get_group_pins, > + .pin_dbg_show = ti_iodelay_pin_dbg_show, > + .dt_node_to_map = ti_iodelay_dt_node_to_map, > +}; > + > +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 I noticed that .pin_dbg_show above needs ifdef CONFIG_DEBUGFS. Updated patch below. Regards, Tony 8< -------------------- >From tony Mon Sep 17 00:00:00 2001 From: Nishanth Menon <nm-l0cyMroinI0@public.gmane.org> Date: Tue, 27 Dec 2016 08:03:43 -0800 Subject: [PATCH] pinctrl: Introduce TI IOdelay configuration driver SoC family such as DRA7 family of processors have, in addition to the regular muxing of pins (as done by pinctrl-single), a separate hardware module called IODelay which is also expected to be configured. The "IODelay" module has it's own register space that is independent of the control module and the padconf register area. With recent changes to the pinctrl framework, we can now support this hardware with a reasonably minimal driver by using #pinctrl-cells, GENERIC_PINCTRL_GROUPS and GENERIC_PINMUX_FUNCTIONS. It is advocated strongly in TI's official documentation considering the existing design of the DRA7 family of processors during mux or IODelay reconfiguration, there is a potential for a significant glitch which may cause functional impairment to certain hardware. It is hence recommended to do as little of muxing as absolutely necessary without I/O isolation (which can only be done in initial stages of bootloader). NOTE: with the system wide I/O isolation scheme present in DRA7 SoC family, it is not reasonable to do stop all I/O operations for every such pad configuration scheme. So, we will let it glitch when used in this mode. Even with the above limitation, certain functionality such as MMC has mandatory need for IODelay reconfiguration requirements, depending on speed of transfer. In these cases, with careful examination of usecase involved, the expected glitch can be controlled such that it does not impact functionality. In short, IODelay module support as a padconf driver being introduced here is not expected to do SoC wide I/O Isolation and is meant for a limited subset of IODelay configuration requirements that need to be dynamic and whose glitchy behavior will not cause functionality failure for that interface. IMPORTANT NOTE: we take the approach of keeping LOCK_BITs cleared to 0x0 at all times, even when configuring Manual IO Timing Modes. This is done by eliminating the LOCK_BIT=1 setting from Step of the Manual IO timing Mode configuration procedure. This option leaves the CFG_* registers unprotected from unintended writes to the CTRL_CORE_PAD_* registers while Manual IO Timing Modes are configured. This approach is taken to allow for a generic driver to exist in kernel world that has to be used carefully in required usecases. Signed-off-by: Nishanth Menon <nm-l0cyMroinI0@public.gmane.org> Signed-off-by: Lokesh Vutla <lokeshvutla-l0cyMroinI0@public.gmane.org> [tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org: updated to use generic pinctrl functions, added binding documentation, updated comments] Signed-off-by: Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> --- .../devicetree/bindings/pinctrl/ti,iodelay.txt | 47 + drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/ti/Kconfig | 10 + drivers/pinctrl/ti/Makefile | 1 + drivers/pinctrl/ti/pinctrl-ti-iodelay.c | 947 +++++++++++++++++++++ 6 files changed, 1007 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt create mode 100644 drivers/pinctrl/ti/Kconfig create mode 100644 drivers/pinctrl/ti/Makefile create mode 100644 drivers/pinctrl/ti/pinctrl-ti-iodelay.c diff --git a/Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt b/Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt new file mode 100644 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt @@ -0,0 +1,47 @@ +* Pin configuration for TI IODELAY controller + +TI dra7 based SoCs such as am57xx have a controller for setting the IO delay +for each pin. For most part the IO delay values are programmed by the bootloader, +but some pins need to be configured dynamically by the kernel such as the +MMC pins. + +Required Properties: + + - compatible: Must be "ti,dra7-iodelay" + - reg: Base address and length of the memory resource used + - #address-cells: Number of address cells + - #size-cells: Size of cells + - #pinctrl-cells: Number of pinctrl cells, must be 2. See also + Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt + +Example +------- + +In the SoC specific dtsi file: + + dra7_iodelay_core: padconf@4844a000 { + compatible = "ti,dra7-iodelay"; + reg = <0x4844a000 0x0d1c>; + #address-cells = <1>; + #size-cells = <0>; + #pinctrl-cells = <2>; + }; + +In board-specific file: + +&dra7_iodelay_core { + mmc2_iodelay_3v3_conf: mmc2_iodelay_3v3_conf { + pinctrl-pin-array = < + 0x18c A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A19_IN */ + 0x1a4 A_DELAY_PS(265) G_DELAY_PS(360) /* CFG_GPMC_A20_IN */ + 0x1b0 A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A21_IN */ + 0x1bc A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A22_IN */ + 0x1c8 A_DELAY_PS(287) G_DELAY_PS(420) /* CFG_GPMC_A23_IN */ + 0x1d4 A_DELAY_PS(144) G_DELAY_PS(240) /* CFG_GPMC_A24_IN */ + 0x1e0 A_DELAY_PS(0) G_DELAY_PS(0) /* CFG_GPMC_A25_IN */ + 0x1ec A_DELAY_PS(120) G_DELAY_PS(0) /* CFG_GPMC_A26_IN */ + 0x1f8 A_DELAY_PS(120) G_DELAY_PS(180) /* CFG_GPMC_A27_IN */ + 0x360 A_DELAY_PS(0) G_DELAY_PS(0) /* CFG_GPMC_CS1_IN */ + >; + }; +}; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -300,6 +300,7 @@ source "drivers/pinctrl/spear/Kconfig" source "drivers/pinctrl/stm32/Kconfig" source "drivers/pinctrl/sunxi/Kconfig" source "drivers/pinctrl/tegra/Kconfig" +source "drivers/pinctrl/ti/Kconfig" source "drivers/pinctrl/uniphier/Kconfig" source "drivers/pinctrl/vt8500/Kconfig" source "drivers/pinctrl/mediatek/Kconfig" diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_PINCTRL_SH_PFC) += sh-pfc/ obj-$(CONFIG_PINCTRL_SPEAR) += spear/ obj-$(CONFIG_PINCTRL_STM32) += stm32/ obj-$(CONFIG_PINCTRL_SUNXI) += sunxi/ +obj-y += ti/ obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/ obj-$(CONFIG_ARCH_VT8500) += vt8500/ obj-$(CONFIG_PINCTRL_MTK) += mediatek/ diff --git a/drivers/pinctrl/ti/Kconfig b/drivers/pinctrl/ti/Kconfig new file mode 100644 --- /dev/null +++ b/drivers/pinctrl/ti/Kconfig @@ -0,0 +1,10 @@ +config PINCTRL_TI_IODELAY + tristate "TI IODelay Module pinconf driver" + depends on OF + select GENERIC_PINCTRL_GROUPS + select GENERIC_PINMUX_FUNCTIONS + select GENERIC_PINCONF + select REGMAP_MMIO + help + Say Y here to support Texas Instruments' IO delay pinconf driver. + IO delay module is used for the DRA7 SoC family. diff --git a/drivers/pinctrl/ti/Makefile b/drivers/pinctrl/ti/Makefile new file mode 100644 --- /dev/null +++ b/drivers/pinctrl/ti/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PINCTRL_TI_IODELAY) += pinctrl-ti-iodelay.o diff --git a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c new file mode 100644 --- /dev/null +++ b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c @@ -0,0 +1,947 @@ +/* + * 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 <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include "../core.h" +#include "../devicetree.h" + +#define DRIVER_NAME "ti-iodelay" + +/** + * struct ti_iodelay_reg_data - Describes the registers for the iodelay instance + * @signature_mask: CONFIG_REG mask for the signature bits (see TRM) + * @signature_value: CONFIG_REG signature value to be written (see TRM) + * @lock_mask: CONFIG_REG mask for the lock bits (see TRM) + * @lock_val: CONFIG_REG lock value for the lock bits (see TRM) + * @unlock_val:CONFIG_REG unlock value for the lock bits (see TRM) + * @binary_data_coarse_mask: CONFIG_REG coarse mask (see TRM) + * @binary_data_fine_mask: CONFIG_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 iodelay module lock register offset + * @global_lock_mask: Lock mask + * @global_unlock_val: Unlock value + * @global_lock_val: Lock value + * @reg_start_offset: Offset to iodelay registers after the CONFIG_REG_0 to 8 + * @reg_nr_per_pin: Number of iodelay registers for each pin + * @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; + u32 reg_nr_per_pin; + + 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_cfg - Description of each configuration parameters + * @offset: Configuration register offset + * @a_delay: Agnostic Delay (in ps) + * @g_delay: Gnostic Delay (in ps) + */ +struct ti_iodelay_cfg { + u16 offset; + u16 a_delay; + u16 g_delay; +}; + +/** + * struct ti_iodelay_pingroup - Structure that describes one group + * @name: Name of the group + * @map: pinctrl map allocated for the group + * @cfg: configuration array for the pin (from dt) + * @ncfg: number of configuration values allocated + * @config: pinconf "Config" - currently a dummy value + */ +struct ti_iodelay_pingroup { + struct ti_iodelay_cfg *cfg; + int ncfg; + unsigned long config; +}; + +/** + * struct ti_iodelay_device - Represents information for a iodelay instance + * @dev: Device pointer + * @phys_base: Physical address base of the iodelay device + * @reg_base: Virtual address base of the iodelay device + * @regmap: Regmap for this iodelay instance + * @pctl: Pinctrl device + * @desc: pinctrl descriptor for pctl + * @pa: pinctrl pin wise description + * @names: names of the pins + * @reg_data: Register definition data for the IODelay instance + * @reg_init_conf_values: Initial configuration values. + */ +struct ti_iodelay_device { + struct device *dev; + unsigned long phys_base; + void __iomem *reg_base; + struct regmap *regmap; + + struct pinctrl_dev *pctl; + struct pinctrl_desc desc; + struct pinctrl_pin_desc *pa; + + const struct ti_iodelay_reg_data *reg_data; + struct ti_iodelay_reg_values reg_init_conf_values; +}; + +/** + * 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_cfg *cfg) +{ + 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 = cfg->g_delay / 920; + g_delay_fine = ((cfg->g_delay % 920) * 10) / 60; + + a_delay_coarse = cfg->a_delay / ival->cdpe; + a_delay_fine = ((cfg->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, cfg->offset, reg_mask, reg_val); + + dev_info(dev, "Set reg 0x%x Delay(a: %d g: %d), Elements(C=%d F=%d)0x%x\n", + cfg->offset, cfg->a_delay, cfg->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); +} + +/** + * ti_iodelay_get_pingroup() - Find the group mapped by a group selector + * @iod: iodelay device + * @selector: Group Selector + * + * Return: Corresponding group representing group selector + */ +static struct ti_iodelay_pingroup * +ti_iodelay_get_pingroup(struct ti_iodelay_device *iod, unsigned int selector) +{ + struct group_desc *g; + + g = pinctrl_generic_get_group(iod->pctl, selector); + if (!g) { + dev_err(iod->dev, "%s could not find pingroup %i\n", __func__, + selector); + + return NULL; + } + + return g->data; +} + +/** + * ti_iodelay_offset_to_pin() - get a pin index based on the register offset + * @iod: iodelay driver instance + * @offset: register offset from the base + */ +static int ti_iodelay_offset_to_pin(struct ti_iodelay_device *iod, + unsigned int offset) +{ + const struct ti_iodelay_reg_data *r = iod->reg_data; + unsigned int index; + + if (offset > r->regmap_config->max_register) { + dev_err(iod->dev, "mux offset out of range: 0x%x (0x%x)\n", + offset, r->regmap_config->max_register); + return -EINVAL; + } + + index = (offset - r->reg_start_offset) / r->regmap_config->reg_stride; + index /= r->reg_nr_per_pin; + + return index; +} + +/** + * + * @pctldev: Pin controller driver + * @np: Device node + * @pinctrl_spec: Parsed arguments from device tree + * @pins: Array of pins in the pin group + * @pin_index: Pin index in the pin array + * @data: Pin controller driver specific data + * + */ +static int ti_iodelay_node_iterator(struct pinctrl_dev *pctldev, + struct device_node *np, + const struct of_phandle_args *pinctrl_spec, + int *pins, int pin_index, void *data) +{ + struct ti_iodelay_device *iod; + struct ti_iodelay_cfg *cfg = data; + const struct ti_iodelay_reg_data *r; + struct pinctrl_pin_desc *pd; + int pin; + + iod = pinctrl_dev_get_drvdata(pctldev); + if (!iod) + return -EINVAL; + + r = iod->reg_data; + + if (pinctrl_spec->args_count < r->reg_nr_per_pin) { + dev_err(iod->dev, "invalid args_count for spec: %i\n", + pinctrl_spec->args_count); + + return -EINVAL; + } + + /* Index plus two value cells */ + cfg[pin_index].offset = pinctrl_spec->args[0]; + cfg[pin_index].a_delay = pinctrl_spec->args[1] & 0xffff; + cfg[pin_index].g_delay = pinctrl_spec->args[2] & 0xffff; + + pin = ti_iodelay_offset_to_pin(iod, cfg[pin_index].offset); + if (pin < 0) { + dev_err(iod->dev, "could not add functions for %s %ux\n", + np->name, cfg[pin_index].offset); + return -ENODEV; + } + pins[pin_index] = pin; + + pd = &iod->pa[pin]; + pd->drv_data = &cfg[pin_index]; + + dev_dbg(iod->dev, "%s offset=%x a_delay = %d g_delay = %d\n", + np->name, cfg[pin_index].offset, cfg[pin_index].a_delay, + cfg[pin_index].g_delay); + + return 0; +} + +/** + * 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 int *num_maps) +{ + struct ti_iodelay_device *iod; + struct ti_iodelay_cfg *cfg; + struct ti_iodelay_pingroup *g; + const char *name = "pinctrl-pin-array"; + int rows, *pins, error = -EINVAL, i; + + iod = pinctrl_dev_get_drvdata(pctldev); + if (!iod) + return -EINVAL; + + rows = pinctrl_count_index_with_args(np, name); + if (rows == -EINVAL) + return rows; + + *map = devm_kzalloc(iod->dev, sizeof(**map), GFP_KERNEL); + if (!*map) + return -ENOMEM; + *num_maps = 0; + + g = devm_kzalloc(iod->dev, sizeof(*g), GFP_KERNEL); + if (!g) { + error = -ENOMEM; + goto free_map; + } + + pins = devm_kzalloc(iod->dev, sizeof(*pins) * rows, GFP_KERNEL); + if (!pins) + goto free_group; + + cfg = devm_kzalloc(iod->dev, sizeof(*cfg) * rows, GFP_KERNEL); + if (!cfg) { + error = -ENOMEM; + goto free_pins; + } + + for (i = 0; i < rows; i++) { + struct of_phandle_args pinctrl_spec; + + error = pinctrl_parse_index_with_args(np, name, i, + &pinctrl_spec); + if (error) + goto free_data; + + error = ti_iodelay_node_iterator(pctldev, np, &pinctrl_spec, + pins, i, cfg); + if (error) + goto free_data; + } + + g->cfg = cfg; + g->ncfg = i; + g->config = PIN_CONFIG_END; + + error = pinctrl_generic_add_group(iod->pctl, np->name, pins, i, g); + if (error < 0) + goto free_data; + + (*map)->type = PIN_MAP_TYPE_CONFIGS_GROUP; + (*map)->data.configs.group_or_pin = np->name; + (*map)->data.configs.configs = &g->config; + (*map)->data.configs.num_configs = 1; + *num_maps = 1; + + return 0; + +free_data: + devm_kfree(iod->dev, cfg); +free_pins: + devm_kfree(iod->dev, pins); +free_group: + devm_kfree(iod->dev, g); +free_map: + devm_kfree(iod->dev, *map); + + return error; +} + +/** + * ti_iodelay_pinconf_group_get() - Get the group configuration + * @pctldev: pinctrl device representing IODelay device + * @selector: 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 int selector, + 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_pingroup(iod, selector); + + 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 + * @selector: 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 int selector, + unsigned long *configs, + unsigned int 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_pingroup(iod, selector); + + 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->ncfg; i++) { + if (ti_iodelay_pinconf_set(iod, &group->cfg[i])) + return -ENOTSUPP; + } + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +/** + * ti_iodelay_pin_to_offset() - get pin register offset based on the pin index + * @iod: iodelay driver instance + * @selector: Pin index + */ +static unsigned int ti_iodelay_pin_to_offset(struct ti_iodelay_device *iod, + unsigned int selector) +{ + const struct ti_iodelay_reg_data *r = iod->reg_data; + unsigned int offset; + + offset = selector * r->regmap_config->reg_stride; + offset *= r->reg_nr_per_pin; + offset += r->reg_start_offset; + + return offset; +} + +static void ti_iodelay_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned int pin) +{ + struct ti_iodelay_device *iod; + struct pinctrl_pin_desc *pd; + struct ti_iodelay_cfg *cfg; + const struct ti_iodelay_reg_data *r; + unsigned long offset; + u32 in, oen, out; + + iod = pinctrl_dev_get_drvdata(pctldev); + r = iod->reg_data; + + offset = ti_iodelay_pin_to_offset(iod, pin); + if (pin < 0) { + dev_err(iod->dev, "invalid pin offset for pin%i\n", pin); + + return; + } + + pd = &iod->pa[pin]; + cfg = pd->drv_data; + + regmap_read(iod->regmap, offset, &in); + regmap_read(iod->regmap, offset + r->regmap_config->reg_stride, &oen); + regmap_read(iod->regmap, offset + r->regmap_config->reg_stride * 2, + &out); + + seq_printf(s, "%lx a: %i g: %i (%08x %08x %08x) %s ", + iod->phys_base + offset, + cfg ? cfg->a_delay : -1, + cfg ? cfg->g_delay : -1, + in, oen, out, DRIVER_NAME); +} + +/** + * ti_iodelay_pinconf_group_dbg_show() - show the group information + * @pctldev: Show the group information + * @s: Sequence file + * @selector: 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 int selector) +{ + 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_pingroup(iod, selector); + if (!group) + return; + + for (i = 0; i < group->ncfg; i++) { + struct ti_iodelay_cfg *cfg; + u32 reg = 0; + + cfg = &group->cfg[i]; + regmap_read(iod->regmap, cfg->offset, ®), + seq_printf(s, "\n\t0x%08x = 0x%08x (%3d, %3d)", + cfg->offset, reg, cfg->a_delay, + cfg->g_delay); + } +} +#endif + +static struct pinctrl_ops ti_iodelay_pinctrl_ops = { + .get_groups_count = pinctrl_generic_get_group_count, + .get_group_name = pinctrl_generic_get_group_name, + .get_group_pins = pinctrl_generic_get_group_pins, +#ifdef CONFIG_DEBUG_FS + .pin_dbg_show = ti_iodelay_pin_dbg_show, +#endif + .dt_node_to_map = ti_iodelay_dt_node_to_map, +}; + +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; + u32 phy_reg; + int nr_pins, i; + + nr_pins = ti_iodelay_offset_to_pin(iod, r->regmap_config->max_register); + 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->desc.pins = iod->pa; + iod->desc.npins = nr_pins; + + phy_reg = r->reg_start_offset + base_phy; + + for (i = 0; i < nr_pins; i++, phy_reg += 4) { + pin = &iod->pa[i]; + pin->number = i; + } + + 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, + .reg_nr_per_pin = 3, + .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); + +/** + * 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->phys_base = res->start; + 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; + + 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 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"); -- 2.11.0 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver 2017-01-02 22:12 ` Tony Lindgren @ 2017-01-04 13:32 ` Rob Herring 2017-01-04 15:38 ` Nishanth Menon [not found] ` <20170102221228.GH9325-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> 2 siblings, 0 replies; 74+ messages in thread From: Rob Herring @ 2017-01-04 13:32 UTC (permalink / raw) To: Tony Lindgren Cc: Linus Walleij, Gary Bisson, Grygorii Strashko, Mark Rutland, Nishanth Menon, devicetree, linux-gpio, linux-omap, Lokesh Vutla On Mon, Jan 02, 2017 at 02:12:28PM -0800, Tony Lindgren wrote: > * Tony Lindgren <tony@atomide.com> [161230 10:38]: > > +static struct pinctrl_ops ti_iodelay_pinctrl_ops = { > > + .get_groups_count = pinctrl_generic_get_group_count, > > + .get_group_name = pinctrl_generic_get_group_name, > > + .get_group_pins = pinctrl_generic_get_group_pins, > > + .pin_dbg_show = ti_iodelay_pin_dbg_show, > > + .dt_node_to_map = ti_iodelay_dt_node_to_map, > > +}; > > + > > +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 > > I noticed that .pin_dbg_show above needs ifdef CONFIG_DEBUGFS. > Updated patch below. > > Regards, > > Tony > > 8< -------------------- > From tony Mon Sep 17 00:00:00 2001 > From: Nishanth Menon <nm@ti.com> > Date: Tue, 27 Dec 2016 08:03:43 -0800 > Subject: [PATCH] pinctrl: Introduce TI IOdelay configuration driver > > SoC family such as DRA7 family of processors have, in addition > to the regular muxing of pins (as done by pinctrl-single), a separate > hardware module called IODelay which is also expected to be configured. > The "IODelay" module has it's own register space that is independent > of the control module and the padconf register area. > > With recent changes to the pinctrl framework, we can now support > this hardware with a reasonably minimal driver by using #pinctrl-cells, > GENERIC_PINCTRL_GROUPS and GENERIC_PINMUX_FUNCTIONS. > > It is advocated strongly in TI's official documentation considering > the existing design of the DRA7 family of processors during mux or > IODelay reconfiguration, there is a potential for a significant glitch > which may cause functional impairment to certain hardware. It is > hence recommended to do as little of muxing as absolutely necessary > without I/O isolation (which can only be done in initial stages of > bootloader). > > NOTE: with the system wide I/O isolation scheme present in DRA7 SoC > family, it is not reasonable to do stop all I/O operations for every > such pad configuration scheme. So, we will let it glitch when used in > this mode. > > Even with the above limitation, certain functionality such as MMC has > mandatory need for IODelay reconfiguration requirements, depending on > speed of transfer. In these cases, with careful examination of usecase > involved, the expected glitch can be controlled such that it does not > impact functionality. > > In short, IODelay module support as a padconf driver being introduced > here is not expected to do SoC wide I/O Isolation and is meant for > a limited subset of IODelay configuration requirements that need to > be dynamic and whose glitchy behavior will not cause functionality > failure for that interface. > > IMPORTANT NOTE: we take the approach of keeping LOCK_BITs cleared > to 0x0 at all times, even when configuring Manual IO Timing Modes. > This is done by eliminating the LOCK_BIT=1 setting from Step > of the Manual IO timing Mode configuration procedure. This option > leaves the CFG_* registers unprotected from unintended writes to the > CTRL_CORE_PAD_* registers while Manual IO Timing Modes are configured. > > This approach is taken to allow for a generic driver to exist in kernel > world that has to be used carefully in required usecases. > > Signed-off-by: Nishanth Menon <nm@ti.com> > Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> > [tony@atomide.com: updated to use generic pinctrl functions, added > binding documentation, updated comments] > Signed-off-by: Tony Lindgren <tony@atomide.com> > --- > .../devicetree/bindings/pinctrl/ti,iodelay.txt | 47 + Acked-by: Rob Herring <robh@kernel.org> > drivers/pinctrl/Kconfig | 1 + > drivers/pinctrl/Makefile | 1 + > drivers/pinctrl/ti/Kconfig | 10 + > drivers/pinctrl/ti/Makefile | 1 + > drivers/pinctrl/ti/pinctrl-ti-iodelay.c | 947 +++++++++++++++++++++ > 6 files changed, 1007 insertions(+) > create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt > create mode 100644 drivers/pinctrl/ti/Kconfig > create mode 100644 drivers/pinctrl/ti/Makefile > create mode 100644 drivers/pinctrl/ti/pinctrl-ti-iodelay.c ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver 2017-01-02 22:12 ` Tony Lindgren 2017-01-04 13:32 ` Rob Herring @ 2017-01-04 15:38 ` Nishanth Menon [not found] ` <63a8a1ec-343a-8c96-a0d2-21d81f7ad10e-l0cyMroinI0@public.gmane.org> [not found] ` <20170102221228.GH9325-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> 2 siblings, 1 reply; 74+ messages in thread From: Nishanth Menon @ 2017-01-04 15:38 UTC (permalink / raw) To: Tony Lindgren, Linus Walleij Cc: Gary Bisson, Grygorii Strashko, Mark Rutland, Rob Herring, devicetree, linux-gpio, linux-omap, Lokesh Vutla On 01/02/2017 04:12 PM, Tony Lindgren wrote: [...] Minor one: > + * Copyright (C) 2015 Texas Instruments, Inc. Could you make this: Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ -- Regards, Nishanth Menon ^ permalink raw reply [flat|nested] 74+ messages in thread
[parent not found: <63a8a1ec-343a-8c96-a0d2-21d81f7ad10e-l0cyMroinI0@public.gmane.org>]
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver [not found] ` <63a8a1ec-343a-8c96-a0d2-21d81f7ad10e-l0cyMroinI0@public.gmane.org> @ 2017-01-04 16:05 ` Tony Lindgren [not found] ` <20170104160511.GF25222-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> 0 siblings, 1 reply; 74+ messages in thread From: Tony Lindgren @ 2017-01-04 16:05 UTC (permalink / raw) To: Nishanth Menon Cc: Linus Walleij, Gary Bisson, Grygorii Strashko, Mark Rutland, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, linux-gpio-u79uwXL29TY76Z2rM5mHXA, linux-omap-u79uwXL29TY76Z2rM5mHXA, Lokesh Vutla * Nishanth Menon <nm-l0cyMroinI0@public.gmane.org> [170104 07:39]: > On 01/02/2017 04:12 PM, Tony Lindgren wrote: > [...] > Minor one: > > + * Copyright (C) 2015 Texas Instruments, Inc. > Could you make this: > Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ Sure, it's your patch :) Will repost. Regards, Tony -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 74+ messages in thread
[parent not found: <20170104160511.GF25222-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>]
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver [not found] ` <20170104160511.GF25222-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> @ 2017-01-04 16:51 ` Nishanth Menon 0 siblings, 0 replies; 74+ messages in thread From: Nishanth Menon @ 2017-01-04 16:51 UTC (permalink / raw) To: Tony Lindgren Cc: Linus Walleij, Gary Bisson, Grygorii Strashko, Mark Rutland, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, linux-gpio-u79uwXL29TY76Z2rM5mHXA, linux-omap-u79uwXL29TY76Z2rM5mHXA, Lokesh Vutla On 01/04/2017 10:05 AM, Tony Lindgren wrote: > * Nishanth Menon <nm-l0cyMroinI0@public.gmane.org> [170104 07:39]: >> On 01/02/2017 04:12 PM, Tony Lindgren wrote: >> [...] >> Minor one: >>> + * Copyright (C) 2015 Texas Instruments, Inc. >> Could you make this: >> Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ > > Sure, it's your patch :) Will repost. Thanks. I just finished testing it as well and well it still does the job it used to do. thanks for taking this forward. I did get a few kernel-doc warnings, which i need to look closer at. will comment back today if I see something of use for us to fix up. -- Regards, Nishanth Menon -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 74+ messages in thread
[parent not found: <20170102221228.GH9325-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>]
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver [not found] ` <20170102221228.GH9325-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> @ 2017-01-04 17:57 ` Nishanth Menon 2017-01-05 18:50 ` Tony Lindgren 0 siblings, 1 reply; 74+ messages in thread From: Nishanth Menon @ 2017-01-04 17:57 UTC (permalink / raw) To: Tony Lindgren, Linus Walleij Cc: Gary Bisson, Grygorii Strashko, Mark Rutland, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, linux-gpio-u79uwXL29TY76Z2rM5mHXA, linux-omap-u79uwXL29TY76Z2rM5mHXA, Lokesh Vutla On 01/02/2017 04:12 PM, Tony Lindgren wrote: [...] I got the following warnings with kernel-doc: > $ ./scripts/kernel-doc drivers/pinctrl/ti/pinctrl-ti-iodelay.c>/dev/null > drivers/pinctrl/ti/pinctrl-ti-iodelay.c:132: warning: Excess struct/union/enum/typedef member 'name' description in 'ti_iodelay_pingroup' > drivers/pinctrl/ti/pinctrl-ti-iodelay.c:132: warning: Excess struct/union/enum/typedef member 'map' description in 'ti_iodelay_pingroup' > drivers/pinctrl/ti/pinctrl-ti-iodelay.c:159: warning: Excess struct/union/enum/typedef member 'names' description in 'ti_iodelay_device' > drivers/pinctrl/ti/pinctrl-ti-iodelay.c:211: warning: No description found for parameter 'cfg' > drivers/pinctrl/ti/pinctrl-ti-iodelay.c:211: warning: Excess function parameter 'val' description in 'ti_iodelay_pinconf_set' > drivers/pinctrl/ti/pinctrl-ti-iodelay.c:418: warning: Cannot understand * > on line 418 - I thought it was a doc line Marking them below > diff --git a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c > new file mode 100644 > --- /dev/null > +++ b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c [...] > +/** > + * struct ti_iodelay_pingroup - Structure that describes one group > + * @name: Name of the group ^^--> we should drop this > + * @map: pinctrl map allocated for the group ^^--> we should drop this > + * @cfg: configuration array for the pin (from dt) > + * @ncfg: number of configuration values allocated > + * @config: pinconf "Config" - currently a dummy value > + */ > +struct ti_iodelay_pingroup { > + struct ti_iodelay_cfg *cfg; > + int ncfg; > + unsigned long config; > +}; > + > +/** > + * struct ti_iodelay_device - Represents information for a iodelay instance > + * @dev: Device pointer > + * @phys_base: Physical address base of the iodelay device > + * @reg_base: Virtual address base of the iodelay device > + * @regmap: Regmap for this iodelay instance > + * @pctl: Pinctrl device > + * @desc: pinctrl descriptor for pctl > + * @pa: pinctrl pin wise description > + * @names: names of the pins ^^--> we should drop this > + * @reg_data: Register definition data for the IODelay instance > + * @reg_init_conf_values: Initial configuration values. > + */ > +struct ti_iodelay_device { > + struct device *dev; > + unsigned long phys_base; > + void __iomem *reg_base; > + struct regmap *regmap; > + > + struct pinctrl_dev *pctl; > + struct pinctrl_desc desc; > + struct pinctrl_pin_desc *pa; > + > + const struct ti_iodelay_reg_data *reg_data; > + struct ti_iodelay_reg_values reg_init_conf_values; > +}; > + [...] > +/** > + * ti_iodelay_pinconf_set() - Configure the pin configuration > + * @iod: iodelay device > + * @val: Configuration value ^^ --> should be cfg ? > + * > + * 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_cfg *cfg) [...] > + > +/** > + * Could you make this: * ti_iodelay_node_iterator() - Iterate iodelay node > + * @pctldev: Pin controller driver > + * @np: Device node > + * @pinctrl_spec: Parsed arguments from device tree > + * @pins: Array of pins in the pin group > + * @pin_index: Pin index in the pin array > + * @data: Pin controller driver specific data > + * > + */ > +static int ti_iodelay_node_iterator(struct pinctrl_dev *pctldev, > + struct device_node *np, > + const struct of_phandle_args *pinctrl_spec, > + int *pins, int pin_index, void *data) [...] Otherwise, the patch looks fine. Thanks for doing this. -- Regards, Nishanth Menon -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver 2017-01-04 17:57 ` Nishanth Menon @ 2017-01-05 18:50 ` Tony Lindgren 0 siblings, 0 replies; 74+ messages in thread From: Tony Lindgren @ 2017-01-05 18:50 UTC (permalink / raw) To: Nishanth Menon Cc: Linus Walleij, Gary Bisson, Grygorii Strashko, Mark Rutland, Rob Herring, devicetree, linux-gpio, linux-omap, Lokesh Vutla * Nishanth Menon <nm@ti.com> [170104 09:58]: > On 01/02/2017 04:12 PM, Tony Lindgren wrote: > [...] > > I got the following warnings with kernel-doc: > > $ ./scripts/kernel-doc drivers/pinctrl/ti/pinctrl-ti-iodelay.c>/dev/null > > drivers/pinctrl/ti/pinctrl-ti-iodelay.c:132: warning: Excess struct/union/enum/typedef member 'name' description in 'ti_iodelay_pingroup' > > drivers/pinctrl/ti/pinctrl-ti-iodelay.c:132: warning: Excess struct/union/enum/typedef member 'map' description in 'ti_iodelay_pingroup' > > drivers/pinctrl/ti/pinctrl-ti-iodelay.c:159: warning: Excess struct/union/enum/typedef member 'names' description in 'ti_iodelay_device' > > drivers/pinctrl/ti/pinctrl-ti-iodelay.c:211: warning: No description found for parameter 'cfg' > > drivers/pinctrl/ti/pinctrl-ti-iodelay.c:211: warning: Excess function parameter 'val' description in 'ti_iodelay_pinconf_set' > > drivers/pinctrl/ti/pinctrl-ti-iodelay.c:418: warning: Cannot understand * > > on line 418 - I thought it was a doc line > > Marking them below OK thanks will repost v3 shortly. Regards, Tony ^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCHv3 0/2] Add TI iodelay driver using #pinctrl-cells @ 2017-01-05 18:54 Tony Lindgren [not found] ` <20170105185414.27247-1-tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> 0 siblings, 1 reply; 74+ messages in thread From: Tony Lindgren @ 2017-01-05 18:54 UTC (permalink / raw) To: Linus Walleij Cc: Gary Bisson, Grygorii Strashko, Mark Rutland, Nishanth Menon, Rob Herring, devicetree, linux-gpio, linux-omap Hi, Here's a v3 respin of Nishanth's iodelay driver that I've updated to use #pinctrl-cells and generic pinctrl group and function code. Gary, note that this one has an iterator function that you may be able to use too, and hopefully we can simplify things further eventually by introducing a generic iterator. Regards, Tony Changes since v1 - Fix compile error if CONFIG_DEBUG_FS is not set Changes since v2 - Update with Nishanth's kerneldoc comments - Added Rob's ack Nishanth Menon (1): pinctrl: Introduce TI IOdelay configuration driver Tony Lindgren (1): pinctrl: core: Make dt_free_map optional .../devicetree/bindings/pinctrl/ti,iodelay.txt | 47 + drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/core.c | 3 - drivers/pinctrl/devicetree.c | 3 +- drivers/pinctrl/ti/Kconfig | 10 + drivers/pinctrl/ti/Makefile | 1 + drivers/pinctrl/ti/pinctrl-ti-iodelay.c | 944 +++++++++++++++++++++ 8 files changed, 1006 insertions(+), 4 deletions(-) create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt create mode 100644 drivers/pinctrl/ti/Kconfig create mode 100644 drivers/pinctrl/ti/Makefile create mode 100644 drivers/pinctrl/ti/pinctrl-ti-iodelay.c -- 2.11.0 ^ permalink raw reply [flat|nested] 74+ messages in thread
[parent not found: <20170105185414.27247-1-tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>]
* [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver [not found] ` <20170105185414.27247-1-tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> @ 2017-01-05 18:54 ` Tony Lindgren 2017-01-09 18:46 ` Linus Walleij 0 siblings, 1 reply; 74+ messages in thread From: Tony Lindgren @ 2017-01-05 18:54 UTC (permalink / raw) To: Linus Walleij Cc: Gary Bisson, Grygorii Strashko, Mark Rutland, Nishanth Menon, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, linux-gpio-u79uwXL29TY76Z2rM5mHXA, linux-omap-u79uwXL29TY76Z2rM5mHXA, Lokesh Vutla From: Nishanth Menon <nm-l0cyMroinI0@public.gmane.org> SoC family such as DRA7 family of processors have, in addition to the regular muxing of pins (as done by pinctrl-single), a separate hardware module called IODelay which is also expected to be configured. The "IODelay" module has it's own register space that is independent of the control module and the padconf register area. With recent changes to the pinctrl framework, we can now support this hardware with a reasonably minimal driver by using #pinctrl-cells, GENERIC_PINCTRL_GROUPS and GENERIC_PINMUX_FUNCTIONS. It is advocated strongly in TI's official documentation considering the existing design of the DRA7 family of processors during mux or IODelay reconfiguration, there is a potential for a significant glitch which may cause functional impairment to certain hardware. It is hence recommended to do as little of muxing as absolutely necessary without I/O isolation (which can only be done in initial stages of bootloader). NOTE: with the system wide I/O isolation scheme present in DRA7 SoC family, it is not reasonable to do stop all I/O operations for every such pad configuration scheme. So, we will let it glitch when used in this mode. Even with the above limitation, certain functionality such as MMC has mandatory need for IODelay reconfiguration requirements, depending on speed of transfer. In these cases, with careful examination of usecase involved, the expected glitch can be controlled such that it does not impact functionality. In short, IODelay module support as a padconf driver being introduced here is not expected to do SoC wide I/O Isolation and is meant for a limited subset of IODelay configuration requirements that need to be dynamic and whose glitchy behavior will not cause functionality failure for that interface. IMPORTANT NOTE: we take the approach of keeping LOCK_BITs cleared to 0x0 at all times, even when configuring Manual IO Timing Modes. This is done by eliminating the LOCK_BIT=1 setting from Step of the Manual IO timing Mode configuration procedure. This option leaves the CFG_* registers unprotected from unintended writes to the CTRL_CORE_PAD_* registers while Manual IO Timing Modes are configured. This approach is taken to allow for a generic driver to exist in kernel world that has to be used carefully in required usecases. Signed-off-by: Nishanth Menon <nm-l0cyMroinI0@public.gmane.org> Signed-off-by: Lokesh Vutla <lokeshvutla-l0cyMroinI0@public.gmane.org> [tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org: updated to use generic pinctrl functions, added binding documentation, updated comments] Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> Signed-off-by: Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> --- .../devicetree/bindings/pinctrl/ti,iodelay.txt | 47 + drivers/pinctrl/Kconfig | 1 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/ti/Kconfig | 10 + drivers/pinctrl/ti/Makefile | 1 + drivers/pinctrl/ti/pinctrl-ti-iodelay.c | 944 +++++++++++++++++++++ 6 files changed, 1004 insertions(+) create mode 100644 Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt create mode 100644 drivers/pinctrl/ti/Kconfig create mode 100644 drivers/pinctrl/ti/Makefile create mode 100644 drivers/pinctrl/ti/pinctrl-ti-iodelay.c diff --git a/Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt b/Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt new file mode 100644 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/ti,iodelay.txt @@ -0,0 +1,47 @@ +* Pin configuration for TI IODELAY controller + +TI dra7 based SoCs such as am57xx have a controller for setting the IO delay +for each pin. For most part the IO delay values are programmed by the bootloader, +but some pins need to be configured dynamically by the kernel such as the +MMC pins. + +Required Properties: + + - compatible: Must be "ti,dra7-iodelay" + - reg: Base address and length of the memory resource used + - #address-cells: Number of address cells + - #size-cells: Size of cells + - #pinctrl-cells: Number of pinctrl cells, must be 2. See also + Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt + +Example +------- + +In the SoC specific dtsi file: + + dra7_iodelay_core: padconf@4844a000 { + compatible = "ti,dra7-iodelay"; + reg = <0x4844a000 0x0d1c>; + #address-cells = <1>; + #size-cells = <0>; + #pinctrl-cells = <2>; + }; + +In board-specific file: + +&dra7_iodelay_core { + mmc2_iodelay_3v3_conf: mmc2_iodelay_3v3_conf { + pinctrl-pin-array = < + 0x18c A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A19_IN */ + 0x1a4 A_DELAY_PS(265) G_DELAY_PS(360) /* CFG_GPMC_A20_IN */ + 0x1b0 A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A21_IN */ + 0x1bc A_DELAY_PS(0) G_DELAY_PS(120) /* CFG_GPMC_A22_IN */ + 0x1c8 A_DELAY_PS(287) G_DELAY_PS(420) /* CFG_GPMC_A23_IN */ + 0x1d4 A_DELAY_PS(144) G_DELAY_PS(240) /* CFG_GPMC_A24_IN */ + 0x1e0 A_DELAY_PS(0) G_DELAY_PS(0) /* CFG_GPMC_A25_IN */ + 0x1ec A_DELAY_PS(120) G_DELAY_PS(0) /* CFG_GPMC_A26_IN */ + 0x1f8 A_DELAY_PS(120) G_DELAY_PS(180) /* CFG_GPMC_A27_IN */ + 0x360 A_DELAY_PS(0) G_DELAY_PS(0) /* CFG_GPMC_CS1_IN */ + >; + }; +}; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -300,6 +300,7 @@ source "drivers/pinctrl/spear/Kconfig" source "drivers/pinctrl/stm32/Kconfig" source "drivers/pinctrl/sunxi/Kconfig" source "drivers/pinctrl/tegra/Kconfig" +source "drivers/pinctrl/ti/Kconfig" source "drivers/pinctrl/uniphier/Kconfig" source "drivers/pinctrl/vt8500/Kconfig" source "drivers/pinctrl/mediatek/Kconfig" diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_PINCTRL_SH_PFC) += sh-pfc/ obj-$(CONFIG_PINCTRL_SPEAR) += spear/ obj-$(CONFIG_PINCTRL_STM32) += stm32/ obj-$(CONFIG_PINCTRL_SUNXI) += sunxi/ +obj-y += ti/ obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/ obj-$(CONFIG_ARCH_VT8500) += vt8500/ obj-$(CONFIG_PINCTRL_MTK) += mediatek/ diff --git a/drivers/pinctrl/ti/Kconfig b/drivers/pinctrl/ti/Kconfig new file mode 100644 --- /dev/null +++ b/drivers/pinctrl/ti/Kconfig @@ -0,0 +1,10 @@ +config PINCTRL_TI_IODELAY + tristate "TI IODelay Module pinconf driver" + depends on OF + select GENERIC_PINCTRL_GROUPS + select GENERIC_PINMUX_FUNCTIONS + select GENERIC_PINCONF + select REGMAP_MMIO + help + Say Y here to support Texas Instruments' IO delay pinconf driver. + IO delay module is used for the DRA7 SoC family. diff --git a/drivers/pinctrl/ti/Makefile b/drivers/pinctrl/ti/Makefile new file mode 100644 --- /dev/null +++ b/drivers/pinctrl/ti/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PINCTRL_TI_IODELAY) += pinctrl-ti-iodelay.o diff --git a/drivers/pinctrl/ti/pinctrl-ti-iodelay.c b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c new file mode 100644 --- /dev/null +++ b/drivers/pinctrl/ti/pinctrl-ti-iodelay.c @@ -0,0 +1,944 @@ +/* + * Support for configuration of IO Delay module found on Texas Instruments SoCs + * such as DRA7 + * + * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include "../core.h" +#include "../devicetree.h" + +#define DRIVER_NAME "ti-iodelay" + +/** + * struct ti_iodelay_reg_data - Describes the registers for the iodelay instance + * @signature_mask: CONFIG_REG mask for the signature bits (see TRM) + * @signature_value: CONFIG_REG signature value to be written (see TRM) + * @lock_mask: CONFIG_REG mask for the lock bits (see TRM) + * @lock_val: CONFIG_REG lock value for the lock bits (see TRM) + * @unlock_val:CONFIG_REG unlock value for the lock bits (see TRM) + * @binary_data_coarse_mask: CONFIG_REG coarse mask (see TRM) + * @binary_data_fine_mask: CONFIG_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 iodelay module lock register offset + * @global_lock_mask: Lock mask + * @global_unlock_val: Unlock value + * @global_lock_val: Lock value + * @reg_start_offset: Offset to iodelay registers after the CONFIG_REG_0 to 8 + * @reg_nr_per_pin: Number of iodelay registers for each pin + * @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; + u32 reg_nr_per_pin; + + 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_cfg - Description of each configuration parameters + * @offset: Configuration register offset + * @a_delay: Agnostic Delay (in ps) + * @g_delay: Gnostic Delay (in ps) + */ +struct ti_iodelay_cfg { + u16 offset; + u16 a_delay; + u16 g_delay; +}; + +/** + * struct ti_iodelay_pingroup - Structure that describes one group + * @cfg: configuration array for the pin (from dt) + * @ncfg: number of configuration values allocated + * @config: pinconf "Config" - currently a dummy value + */ +struct ti_iodelay_pingroup { + struct ti_iodelay_cfg *cfg; + int ncfg; + unsigned long config; +}; + +/** + * struct ti_iodelay_device - Represents information for a iodelay instance + * @dev: Device pointer + * @phys_base: Physical address base of the iodelay device + * @reg_base: Virtual address base of the iodelay device + * @regmap: Regmap for this iodelay instance + * @pctl: Pinctrl device + * @desc: pinctrl descriptor for pctl + * @pa: pinctrl pin wise description + * @reg_data: Register definition data for the IODelay instance + * @reg_init_conf_values: Initial configuration values. + */ +struct ti_iodelay_device { + struct device *dev; + unsigned long phys_base; + void __iomem *reg_base; + struct regmap *regmap; + + struct pinctrl_dev *pctl; + struct pinctrl_desc desc; + struct pinctrl_pin_desc *pa; + + const struct ti_iodelay_reg_data *reg_data; + struct ti_iodelay_reg_values reg_init_conf_values; +}; + +/** + * 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 + * @cfg: Configuration + * + * 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_cfg *cfg) +{ + 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 = cfg->g_delay / 920; + g_delay_fine = ((cfg->g_delay % 920) * 10) / 60; + + a_delay_coarse = cfg->a_delay / ival->cdpe; + a_delay_fine = ((cfg->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, cfg->offset, reg_mask, reg_val); + + dev_info(dev, "Set reg 0x%x Delay(a: %d g: %d), Elements(C=%d F=%d)0x%x\n", + cfg->offset, cfg->a_delay, cfg->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); +} + +/** + * ti_iodelay_get_pingroup() - Find the group mapped by a group selector + * @iod: iodelay device + * @selector: Group Selector + * + * Return: Corresponding group representing group selector + */ +static struct ti_iodelay_pingroup * +ti_iodelay_get_pingroup(struct ti_iodelay_device *iod, unsigned int selector) +{ + struct group_desc *g; + + g = pinctrl_generic_get_group(iod->pctl, selector); + if (!g) { + dev_err(iod->dev, "%s could not find pingroup %i\n", __func__, + selector); + + return NULL; + } + + return g->data; +} + +/** + * ti_iodelay_offset_to_pin() - get a pin index based on the register offset + * @iod: iodelay driver instance + * @offset: register offset from the base + */ +static int ti_iodelay_offset_to_pin(struct ti_iodelay_device *iod, + unsigned int offset) +{ + const struct ti_iodelay_reg_data *r = iod->reg_data; + unsigned int index; + + if (offset > r->regmap_config->max_register) { + dev_err(iod->dev, "mux offset out of range: 0x%x (0x%x)\n", + offset, r->regmap_config->max_register); + return -EINVAL; + } + + index = (offset - r->reg_start_offset) / r->regmap_config->reg_stride; + index /= r->reg_nr_per_pin; + + return index; +} + +/** + * ti_iodelay_node_iterator() - Iterate iodelay node + * @pctldev: Pin controller driver + * @np: Device node + * @pinctrl_spec: Parsed arguments from device tree + * @pins: Array of pins in the pin group + * @pin_index: Pin index in the pin array + * @data: Pin controller driver specific data + * + */ +static int ti_iodelay_node_iterator(struct pinctrl_dev *pctldev, + struct device_node *np, + const struct of_phandle_args *pinctrl_spec, + int *pins, int pin_index, void *data) +{ + struct ti_iodelay_device *iod; + struct ti_iodelay_cfg *cfg = data; + const struct ti_iodelay_reg_data *r; + struct pinctrl_pin_desc *pd; + int pin; + + iod = pinctrl_dev_get_drvdata(pctldev); + if (!iod) + return -EINVAL; + + r = iod->reg_data; + + if (pinctrl_spec->args_count < r->reg_nr_per_pin) { + dev_err(iod->dev, "invalid args_count for spec: %i\n", + pinctrl_spec->args_count); + + return -EINVAL; + } + + /* Index plus two value cells */ + cfg[pin_index].offset = pinctrl_spec->args[0]; + cfg[pin_index].a_delay = pinctrl_spec->args[1] & 0xffff; + cfg[pin_index].g_delay = pinctrl_spec->args[2] & 0xffff; + + pin = ti_iodelay_offset_to_pin(iod, cfg[pin_index].offset); + if (pin < 0) { + dev_err(iod->dev, "could not add functions for %s %ux\n", + np->name, cfg[pin_index].offset); + return -ENODEV; + } + pins[pin_index] = pin; + + pd = &iod->pa[pin]; + pd->drv_data = &cfg[pin_index]; + + dev_dbg(iod->dev, "%s offset=%x a_delay = %d g_delay = %d\n", + np->name, cfg[pin_index].offset, cfg[pin_index].a_delay, + cfg[pin_index].g_delay); + + return 0; +} + +/** + * 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 int *num_maps) +{ + struct ti_iodelay_device *iod; + struct ti_iodelay_cfg *cfg; + struct ti_iodelay_pingroup *g; + const char *name = "pinctrl-pin-array"; + int rows, *pins, error = -EINVAL, i; + + iod = pinctrl_dev_get_drvdata(pctldev); + if (!iod) + return -EINVAL; + + rows = pinctrl_count_index_with_args(np, name); + if (rows == -EINVAL) + return rows; + + *map = devm_kzalloc(iod->dev, sizeof(**map), GFP_KERNEL); + if (!*map) + return -ENOMEM; + *num_maps = 0; + + g = devm_kzalloc(iod->dev, sizeof(*g), GFP_KERNEL); + if (!g) { + error = -ENOMEM; + goto free_map; + } + + pins = devm_kzalloc(iod->dev, sizeof(*pins) * rows, GFP_KERNEL); + if (!pins) + goto free_group; + + cfg = devm_kzalloc(iod->dev, sizeof(*cfg) * rows, GFP_KERNEL); + if (!cfg) { + error = -ENOMEM; + goto free_pins; + } + + for (i = 0; i < rows; i++) { + struct of_phandle_args pinctrl_spec; + + error = pinctrl_parse_index_with_args(np, name, i, + &pinctrl_spec); + if (error) + goto free_data; + + error = ti_iodelay_node_iterator(pctldev, np, &pinctrl_spec, + pins, i, cfg); + if (error) + goto free_data; + } + + g->cfg = cfg; + g->ncfg = i; + g->config = PIN_CONFIG_END; + + error = pinctrl_generic_add_group(iod->pctl, np->name, pins, i, g); + if (error < 0) + goto free_data; + + (*map)->type = PIN_MAP_TYPE_CONFIGS_GROUP; + (*map)->data.configs.group_or_pin = np->name; + (*map)->data.configs.configs = &g->config; + (*map)->data.configs.num_configs = 1; + *num_maps = 1; + + return 0; + +free_data: + devm_kfree(iod->dev, cfg); +free_pins: + devm_kfree(iod->dev, pins); +free_group: + devm_kfree(iod->dev, g); +free_map: + devm_kfree(iod->dev, *map); + + return error; +} + +/** + * ti_iodelay_pinconf_group_get() - Get the group configuration + * @pctldev: pinctrl device representing IODelay device + * @selector: 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 int selector, + 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_pingroup(iod, selector); + + 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 + * @selector: 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 int selector, + unsigned long *configs, + unsigned int 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_pingroup(iod, selector); + + 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->ncfg; i++) { + if (ti_iodelay_pinconf_set(iod, &group->cfg[i])) + return -ENOTSUPP; + } + + return 0; +} + +#ifdef CONFIG_DEBUG_FS +/** + * ti_iodelay_pin_to_offset() - get pin register offset based on the pin index + * @iod: iodelay driver instance + * @selector: Pin index + */ +static unsigned int ti_iodelay_pin_to_offset(struct ti_iodelay_device *iod, + unsigned int selector) +{ + const struct ti_iodelay_reg_data *r = iod->reg_data; + unsigned int offset; + + offset = selector * r->regmap_config->reg_stride; + offset *= r->reg_nr_per_pin; + offset += r->reg_start_offset; + + return offset; +} + +static void ti_iodelay_pin_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned int pin) +{ + struct ti_iodelay_device *iod; + struct pinctrl_pin_desc *pd; + struct ti_iodelay_cfg *cfg; + const struct ti_iodelay_reg_data *r; + unsigned long offset; + u32 in, oen, out; + + iod = pinctrl_dev_get_drvdata(pctldev); + r = iod->reg_data; + + offset = ti_iodelay_pin_to_offset(iod, pin); + if (pin < 0) { + dev_err(iod->dev, "invalid pin offset for pin%i\n", pin); + + return; + } + + pd = &iod->pa[pin]; + cfg = pd->drv_data; + + regmap_read(iod->regmap, offset, &in); + regmap_read(iod->regmap, offset + r->regmap_config->reg_stride, &oen); + regmap_read(iod->regmap, offset + r->regmap_config->reg_stride * 2, + &out); + + seq_printf(s, "%lx a: %i g: %i (%08x %08x %08x) %s ", + iod->phys_base + offset, + cfg ? cfg->a_delay : -1, + cfg ? cfg->g_delay : -1, + in, oen, out, DRIVER_NAME); +} + +/** + * ti_iodelay_pinconf_group_dbg_show() - show the group information + * @pctldev: Show the group information + * @s: Sequence file + * @selector: 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 int selector) +{ + 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_pingroup(iod, selector); + if (!group) + return; + + for (i = 0; i < group->ncfg; i++) { + struct ti_iodelay_cfg *cfg; + u32 reg = 0; + + cfg = &group->cfg[i]; + regmap_read(iod->regmap, cfg->offset, ®), + seq_printf(s, "\n\t0x%08x = 0x%08x (%3d, %3d)", + cfg->offset, reg, cfg->a_delay, + cfg->g_delay); + } +} +#endif + +static struct pinctrl_ops ti_iodelay_pinctrl_ops = { + .get_groups_count = pinctrl_generic_get_group_count, + .get_group_name = pinctrl_generic_get_group_name, + .get_group_pins = pinctrl_generic_get_group_pins, +#ifdef CONFIG_DEBUG_FS + .pin_dbg_show = ti_iodelay_pin_dbg_show, +#endif + .dt_node_to_map = ti_iodelay_dt_node_to_map, +}; + +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; + u32 phy_reg; + int nr_pins, i; + + nr_pins = ti_iodelay_offset_to_pin(iod, r->regmap_config->max_register); + 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->desc.pins = iod->pa; + iod->desc.npins = nr_pins; + + phy_reg = r->reg_start_offset + base_phy; + + for (i = 0; i < nr_pins; i++, phy_reg += 4) { + pin = &iod->pa[i]; + pin->number = i; + } + + 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, + .reg_nr_per_pin = 3, + .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); + +/** + * 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->phys_base = res->start; + 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; + + 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 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"); -- 2.11.0 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver 2017-01-05 18:54 ` [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver Tony Lindgren @ 2017-01-09 18:46 ` Linus Walleij 0 siblings, 0 replies; 74+ messages in thread From: Linus Walleij @ 2017-01-09 18:46 UTC (permalink / raw) To: Tony Lindgren Cc: Gary Bisson, Grygorii Strashko, Mark Rutland, Nishanth Menon, Rob Herring, devicetree, linux-gpio, Linux-OMAP, Lokesh Vutla On Thu, Jan 5, 2017 at 7:54 PM, Tony Lindgren <tony@atomide.com> wrote: > From: Nishanth Menon <nm@ti.com> > > SoC family such as DRA7 family of processors have, in addition > to the regular muxing of pins (as done by pinctrl-single), a separate > hardware module called IODelay which is also expected to be configured. > The "IODelay" module has it's own register space that is independent > of the control module and the padconf register area. > > With recent changes to the pinctrl framework, we can now support > this hardware with a reasonably minimal driver by using #pinctrl-cells, > GENERIC_PINCTRL_GROUPS and GENERIC_PINMUX_FUNCTIONS. > > It is advocated strongly in TI's official documentation considering > the existing design of the DRA7 family of processors during mux or > IODelay reconfiguration, there is a potential for a significant glitch > which may cause functional impairment to certain hardware. It is > hence recommended to do as little of muxing as absolutely necessary > without I/O isolation (which can only be done in initial stages of > bootloader). > > NOTE: with the system wide I/O isolation scheme present in DRA7 SoC > family, it is not reasonable to do stop all I/O operations for every > such pad configuration scheme. So, we will let it glitch when used in > this mode. > > Even with the above limitation, certain functionality such as MMC has > mandatory need for IODelay reconfiguration requirements, depending on > speed of transfer. In these cases, with careful examination of usecase > involved, the expected glitch can be controlled such that it does not > impact functionality. > > In short, IODelay module support as a padconf driver being introduced > here is not expected to do SoC wide I/O Isolation and is meant for > a limited subset of IODelay configuration requirements that need to > be dynamic and whose glitchy behavior will not cause functionality > failure for that interface. > > IMPORTANT NOTE: we take the approach of keeping LOCK_BITs cleared > to 0x0 at all times, even when configuring Manual IO Timing Modes. > This is done by eliminating the LOCK_BIT=1 setting from Step > of the Manual IO timing Mode configuration procedure. This option > leaves the CFG_* registers unprotected from unintended writes to the > CTRL_CORE_PAD_* registers while Manual IO Timing Modes are configured. > > This approach is taken to allow for a generic driver to exist in kernel > world that has to be used carefully in required usecases. > > Signed-off-by: Nishanth Menon <nm@ti.com> > Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com> > [tony@atomide.com: updated to use generic pinctrl functions, added > binding documentation, updated comments] > Acked-by: Rob Herring <robh@kernel.org> > Signed-off-by: Tony Lindgren <tony@atomide.com> Oh what a hardware mess. But very nicely isolated now. Patch applied! Yours, Linus Walleij ^ permalink raw reply [flat|nested] 74+ messages in thread
end of thread, other threads:[~2017-01-09 18:46 UTC | newest] Thread overview: 74+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2015-03-04 0:00 [PATCH 0/2] pinctrl: Introduce support for iodelay module in TI SoCs Nishanth Menon 2015-03-04 0:00 ` Nishanth Menon 2015-03-04 0:00 ` Nishanth Menon 2015-03-04 0:00 ` [PATCH 1/2] pinctrl: bindings: pinctrl: Add support for TI's IODelay configuration Nishanth Menon 2015-03-04 0:00 ` Nishanth Menon 2015-03-04 0:00 ` Nishanth Menon 2015-03-10 10:39 ` Linus Walleij 2015-03-10 10:39 ` Linus Walleij 2015-03-10 10:39 ` Linus Walleij 2015-03-10 15:06 ` Nishanth Menon 2015-03-10 15:06 ` Nishanth Menon 2015-03-10 15:06 ` Nishanth Menon 2015-03-10 15:33 ` Tony Lindgren 2015-03-10 15:33 ` Tony Lindgren 2015-03-10 15:33 ` Tony Lindgren 2015-03-10 17:25 ` Nishanth Menon 2015-03-10 17:25 ` Nishanth Menon 2015-03-10 17:25 ` Nishanth Menon 2015-03-10 17:31 ` Tony Lindgren 2015-03-10 17:31 ` Tony Lindgren 2015-03-10 17:31 ` Tony Lindgren 2015-03-10 18:33 ` Nishanth Menon 2015-03-10 18:33 ` Nishanth Menon 2015-03-10 18:33 ` Nishanth Menon 2015-03-10 19:20 ` Nishanth Menon 2015-03-10 19:20 ` Nishanth Menon 2015-03-10 19:20 ` Nishanth Menon 2015-03-18 1:30 ` Linus Walleij 2015-03-18 1:30 ` Linus Walleij 2015-03-18 1:30 ` Linus Walleij 2015-03-18 1:41 ` Tony Lindgren 2015-03-18 1:41 ` Tony Lindgren 2015-03-18 1:41 ` Tony Lindgren 2015-04-15 1:29 ` Lennart Sorensen 2015-04-15 1:29 ` Lennart Sorensen 2015-04-15 1:29 ` Lennart Sorensen [not found] ` <20150415012910.GA29560-1wCw9BSqJbv44Nm34jS7GywD8/FfD2ys@public.gmane.org> 2015-04-15 16:51 ` Nishanth Menon 2015-04-15 16:51 ` Nishanth Menon 2015-04-15 16:51 ` Nishanth Menon 2015-04-15 18:44 ` Lennart Sorensen 2015-04-15 18:44 ` Lennart Sorensen 2015-04-15 18:44 ` Lennart Sorensen 2015-04-15 18:53 ` Nishanth Menon 2015-04-15 18:53 ` Nishanth Menon 2015-04-15 18:53 ` Nishanth Menon 2015-03-04 0:00 ` [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver Nishanth Menon 2015-03-04 0:00 ` Nishanth Menon 2015-03-04 0:00 ` Nishanth Menon 2015-03-04 22:58 ` Paul Bolle 2015-03-04 22:58 ` Paul Bolle 2015-03-04 22:58 ` Tony Lindgren 2015-03-04 22:58 ` Tony Lindgren 2015-03-05 2:22 ` Nishanth Menon 2015-03-05 2:22 ` Nishanth Menon 2015-03-05 2:22 ` Nishanth Menon 2015-03-10 11:03 ` Linus Walleij 2015-03-10 11:03 ` Linus Walleij 2015-03-10 11:03 ` Linus Walleij 2015-03-11 12:39 ` Nishanth Menon 2015-03-11 12:39 ` Nishanth Menon 2015-03-11 12:39 ` Nishanth Menon 2016-12-30 18:37 [PATCH 0/2] Add TI iodelay driver using #pinctrl-cells Tony Lindgren 2016-12-30 18:37 ` [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver Tony Lindgren [not found] ` <20161230183732.5595-3-tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> 2016-12-31 22:52 ` kbuild test robot [not found] ` <201701010645.EBjk2p8Y%fengguang.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> 2017-01-02 18:04 ` Tony Lindgren 2017-01-01 2:02 ` kbuild test robot 2017-01-02 22:12 ` Tony Lindgren 2017-01-04 13:32 ` Rob Herring 2017-01-04 15:38 ` Nishanth Menon [not found] ` <63a8a1ec-343a-8c96-a0d2-21d81f7ad10e-l0cyMroinI0@public.gmane.org> 2017-01-04 16:05 ` Tony Lindgren [not found] ` <20170104160511.GF25222-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> 2017-01-04 16:51 ` Nishanth Menon [not found] ` <20170102221228.GH9325-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> 2017-01-04 17:57 ` Nishanth Menon 2017-01-05 18:50 ` Tony Lindgren 2017-01-05 18:54 [PATCHv3 0/2] Add TI iodelay driver using #pinctrl-cells Tony Lindgren [not found] ` <20170105185414.27247-1-tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> 2017-01-05 18:54 ` [PATCH 2/2] pinctrl: Introduce TI IOdelay configuration driver Tony Lindgren 2017-01-09 18:46 ` Linus Walleij
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.