diff --git a/Documentation/devicetree/bindings/media/phytium-jpeg-vram.txt b/Documentation/devicetree/bindings/media/phytium-jpeg-vram.txt new file mode 100644 index 0000000000000000000000000000000000000000..787381e9e438cad2b54192e720838b51fcdbea00 --- /dev/null +++ b/Documentation/devicetree/bindings/media/phytium-jpeg-vram.txt @@ -0,0 +1,27 @@ +* Device tree bindings for Phytium VRAM Engine + +The VRAM Engine embedded in the Phytium SOCs can capture +and compress video data from digital or analog sources. + +Required properties: + - compatible: "phytium,jpeg-vram" + - reg: contains the offset and length of the + VRAM Engine memory region + - interrupts: the interrupt associated with the VE on this platform + - ddr-mode: storage the ddr-mode related register information + +Example: + +jpeg_vram0: jpeg_vram@32b32000 { + compatible = "phytium,jpeg-vram"; + reg = <0x0 0x32b32000 0 0x1000>, + <0x0 0x32000000 0 0x8000>; + interrupts = ; + status = "disabled"; + ddr-mode { + phytium,dc-reg = <0x32000000 0x2000>; + phytium,dc-vram = <0xf4000000 0x4000000>; + phytium,jpeg-vram = <0xf9000000 0x1000000>; + phytium,trans-reg = <0x32b3a000 0x1000>; + }; +}; diff --git a/Documentation/devicetree/bindings/net/macb.txt b/Documentation/devicetree/bindings/net/macb.txt index 27168b116916324695cfe57a9cec224a09ff63c8..9f6ecd98443a601c989abd1ca3693d3967444998 100644 --- a/Documentation/devicetree/bindings/net/macb.txt +++ b/Documentation/devicetree/bindings/net/macb.txt @@ -15,7 +15,8 @@ Required properties: Use "atmel,sama5d4-gem" for the GEM IP (10/100) available on Atmel sama5d4 SoCs. Use "cdns,zynq-gem" Xilinx Zynq-7xxx SoC. Use "cdns,zynqmp-gem" for Zynq Ultrascale+ MPSoC. - Use "cdns,phytium-gem" for Phytium SoCs. + Use "cdns,phytium-gem-1.0" for Phytium SoCs. + Use "cdns,phytium-gem-2.0" for Phytium SoCs. Or the generic form: "cdns,emac". - reg: Address and length of the register set for the device - interrupts: Should contain macb interrupt diff --git a/Documentation/devicetree/bindings/pwm/pwm-phytium.txt b/Documentation/devicetree/bindings/pwm/pwm-phytium.txt index 0285e95a3730905651c5cc564edd9b981d8d2a22..6b5bec35245a169540b61697d53627979da1928a 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-phytium.txt +++ b/Documentation/devicetree/bindings/pwm/pwm-phytium.txt @@ -5,9 +5,10 @@ Required properties: - reg: physical base address and length of the controller's registers - interrupts: interrupt for the pwm controller - clocks : clock specifiers for both ipg and per clocks. -- phytium,db: One or two to describe dead-band configurations. +- phytium,db: One or two to describe dead-band configurations. "cntmod" indicates the counter mode (0 for modulo, 1 for up-and-down). "dutymod" indicdates which duty to compare with (0 for PMW_CCR, 1 for FIFO). + "cmpmod" indicdates compare operating configure, from 0 to 7. "div" selects the clock divider value, from 0 to 1023. "updbcly" selects the rising edge delay cycles. "dbpolarity" selects the polarity for dead-band. @@ -19,5 +20,5 @@ pwm0: pwm@2804a000 { reg= <0x0 0x2804a000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_48mhz>; - phytium,db = <0 0 0 1000 0>; + phytium,db = <0 0 3 0 1000 0>; }; diff --git a/MAINTAINERS b/MAINTAINERS index 2564021626446cebfe53f489d54f3318ea35a705..6be91a3855c1dbc4f2a5d30323a1c09b932cc713 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1866,6 +1866,7 @@ F: Documentation/devicetree/bindings/ipmi/phytium,bt-bmc.txt F: Documentation/devicetree/bindings/ipmi/phytium,kcs-bmc.txt F: Documentation/devicetree/bindings/mailbox/phytium-mailbox.txt F: Documentation/devicetree/bindings/media/phytium-jpeg.txt +F: Documentation/devicetree/bindings/media/phytium-jpeg-vram.txt F: Documentation/devicetree/bindings/mmc/phytium-mci.txt F: Documentation/devicetree/bindings/net/can/phytium-can.txt F: Documentation/devicetree/bindings/pci/phytium,phytium-pcie-ep.txt @@ -1892,6 +1893,7 @@ F: drivers/input/serio/phytium-ps2.c F: drivers/irqchip/irq-phytium-ixic.c F: drivers/mailbox/phytium_mailbox.c F: drivers/media/platform/phytium-jpeg/phytium_jpeg* +F: drivers/media/platform/phytium-jpeg/phytium_vram* F: drivers/mfd/phytium_px210_i2s_lsd.c F: drivers/mfd/phytium_px210_i2s_mmd.c F: drivers/mmc/host/phytium-mci* diff --git a/arch/arm64/boot/dts/phytium/pe2201.dtsi b/arch/arm64/boot/dts/phytium/pe2201.dtsi index c89961985b300c613a8448f4bf046deaf4a025bb..b880aeff86dccf9f19f3a957e157d038c6e62a5b 100644 --- a/arch/arm64/boot/dts/phytium/pe2201.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2201.dtsi @@ -13,6 +13,7 @@ / { aliases { ethernet0 = &macb0; ethernet1 = &macb1; + ethernet2 = &macb2; }; }; @@ -27,12 +28,20 @@ cpu0: cpu@0 { }; &soc { + reset: reset@2807e000 { + compatible = "phytium,reset"; + reg = <0x0 0x2807e000 0x0 0x10>; + #reset-cells = <1>; + status = "okay"; + }; + i2c0: i2c@28011000 { compatible = "phytium,i2c"; reg = <0x0 0x28011000 0x0 0x1000>; interrupts = ; interrupt-names = "smbus_alert"; clocks = <&sysclk_50mhz>; + resets = <&reset 0>; status = "disabled"; }; @@ -42,6 +51,7 @@ i2c1: i2c@28012000 { interrupts = ; interrupt-names = "smbus_alert"; clocks = <&sysclk_50mhz>; + resets = <&reset 1>; status = "disabled"; }; @@ -50,6 +60,183 @@ i2c2: i2c@28013000 { reg = <0x0 0x28013000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + resets = <&reset 2>; + status = "disabled"; + }; + + mio0: i2c@28014000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28014000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 3>; + status = "disabled"; + }; + + mio1: i2c@28016000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28016000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 4>; + status = "disabled"; + }; + + mio2: i2c@28018000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28018000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 5>; + status = "disabled"; + }; + + mio3: i2c@2801a000 { + compatible = "phytium,i2c"; + reg = <0x0 0x2801a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 6>; + status = "disabled"; + }; + + mio4: i2c@2801c000 { + compatible = "phytium,i2c"; + reg = <0x0 0x2801c000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 7>; + status = "disabled"; + }; + + mio5: i2c@2801e000 { + compatible = "phytium,i2c"; + reg = <0x0 0x2801e000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 8>; + status = "disabled"; + }; + + mio6: i2c@28020000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28020000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 9>; + status = "disabled"; + }; + + mio7: i2c@28022000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28022000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 10>; + status = "disabled"; + }; + + mio8: i2c@28024000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28024000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 11>; + status = "disabled"; + }; + + mio9: i2c@28026000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28026000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 12>; + status = "disabled"; + }; + + mio10: i2c@28028000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28028000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 13>; + status = "disabled"; + }; + + mio11: i2c@2802a000 { + compatible = "phytium,i2c"; + reg = <0x0 0x2802a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 14>; + status = "disabled"; + }; + + mio12: i2c@2802c000 { + compatible = "phytium,i2c"; + reg = <0x0 0x2802c000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 15>; + status = "disabled"; + }; + + mio13: i2c@2802e000 { + compatible = "phytium,i2c"; + reg = <0x0 0x2802e000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 16>; + status = "disabled"; + }; + + mio14: i2c@28030000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28030000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 17>; + status = "disabled"; + }; + + mio15: i2c@28032000 { + compatible = "phytium,i2c"; + reg = <0x0 0x28032000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_50mhz>; + #address-cells = <1>; + #size-cells = <0>; + resets = <&reset 18>; status = "disabled"; }; @@ -117,6 +304,7 @@ pwm4: pwm@2804e000 { reg = <0x0 0x2804e000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; @@ -125,6 +313,7 @@ pwm5: pwm@2804f000 { reg = <0x0 0x2804f000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; @@ -133,6 +322,7 @@ pwm6: pwm@28050000 { reg = <0x0 0x28050000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; @@ -141,6 +331,7 @@ pwm7: pwm@28051000 { reg = <0x0 0x28051000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; @@ -180,42 +371,6 @@ channel@7 { }; }; - adc1: adc@2807c000 { - compatible = "phytium,adc"; - reg = <0x0 0x2807c000 0x0 0x1000>; - interrupts = ; - clocks = <&sysclk_50mhz>; - status = "disabled"; - - #address-cells = <1>; - #size-cells = <0>; - - channel@0 { - reg = <0>; - }; - channel@1 { - reg = <1>; - }; - channel@2 { - reg = <2>; - }; - channel@3 { - reg = <3>; - }; - channel@4 { - reg = <4>; - }; - channel@5 { - reg = <5>; - }; - channel@6 { - reg = <6>; - }; - channel@7 { - reg = <7>; - }; - }; - sgpio: sgpio@2807d000 { compatible = "phytium,sgpio"; reg = <0x0 0x2807d000 0x0 0x1000>; @@ -228,8 +383,21 @@ sgpio: sgpio@2807d000 { status = "disabled"; }; - macb0: ethernet@32010000 { - compatible = "cdns,phytium-gem"; + macb0: ethernet@3200e000 { + compatible = "cdns,phytium-gem-1.0"; + reg = <0x0 0x3200e000 0x0 0x2000>; + interrupts = , + , + , + ; + clock-names = "pclk", "hclk", "tx_clk", "tsu_clk"; + clocks = <&sysclk_250mhz>, <&sysclk_48mhz>, <&sysclk_48mhz>, <&sysclk_250mhz>; + magic-packet; + status = "disabled"; + }; + + macb1: ethernet@32010000 { + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32010000 0x0 0x2000>; interrupts = , , @@ -241,8 +409,8 @@ macb0: ethernet@32010000 { status = "disabled"; }; - macb1: ethernet@32012000 { - compatible = "cdns,phytium-gem"; + macb2: ethernet@32012000 { + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32012000 0x0 0x2000>; interrupts = , , @@ -265,4 +433,27 @@ jpeg0: jpeg@32b32000 { phytium,ocm-buf-addr = <0x30c40000 0x30c60000>; status = "disabled"; }; + + jpeg_vram0: jpeg_vram@32b32000 { + compatible = "phytium,jpeg-vram"; + reg = <0x0 0x32b32000 0 0x1000>, + <0x0 0x32000000 0 0x8000>; + interrupts = ; + status = "disabled"; + ddr-mode { + phytium,dc-reg = <0x32000000 0x2000>; + phytium,dc-vram = <0xf4000000 0x4000000>; + phytium,jpeg-vram = <0xf9000000 0x1000000>; + phytium,trans-reg = <0x32b3a000 0x1000>; + }; + }; + + jtag: jtag@28044000 { + compatible = "phytium,jtag-master"; + reg = <0x0 0x28044000 0x0 0x1000>; + interrupts = <0x0 0xa7 0x4>; + clocks = <0x8>; + clock-names = "phytium_jtag_clk"; + status = "disabled"; + }; }; diff --git a/arch/arm64/boot/dts/phytium/pe2202.dtsi b/arch/arm64/boot/dts/phytium/pe2202.dtsi index 3493153b3906c3ee5d42ac0692e628d51d37818a..786a7f0a2e4ccbf1f2d291d60b0ed14376e5322b 100644 --- a/arch/arm64/boot/dts/phytium/pe2202.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2202.dtsi @@ -125,7 +125,7 @@ sata1: sata@32014000 { }; macb0: ethernet@3200c000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x3200c000 0x0 0x2000>; interrupts = , , @@ -142,7 +142,7 @@ macb0: ethernet@3200c000 { }; macb1: ethernet@3200e000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x3200e000 0x0 0x2000>; interrupts = , , @@ -155,7 +155,7 @@ macb1: ethernet@3200e000 { }; macb2: ethernet@32010000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32010000 0x0 0x2000>; interrupts = , , @@ -169,7 +169,7 @@ macb2: ethernet@32010000 { }; macb3: ethernet@32012000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32012000 0x0 0x2000>; interrupts = , , diff --git a/arch/arm64/boot/dts/phytium/pe2204.dtsi b/arch/arm64/boot/dts/phytium/pe2204.dtsi index fa1e9a8488c674b3f49b1223342ec42889fd84d3..153ad7ba5fd1c47d598bd8c5c2986394b268db91 100644 --- a/arch/arm64/boot/dts/phytium/pe2204.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2204.dtsi @@ -153,7 +153,7 @@ sata1: sata@32014000 { }; macb0: ethernet@3200c000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x3200c000 0x0 0x2000>; interrupts = , , @@ -171,7 +171,7 @@ macb0: ethernet@3200c000 { }; macb1: ethernet@3200e000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x3200e000 0x0 0x2000>; interrupts = , , @@ -184,7 +184,7 @@ macb1: ethernet@3200e000 { }; macb2: ethernet@32010000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32010000 0x0 0x2000>; interrupts = , , @@ -197,7 +197,7 @@ macb2: ethernet@32010000 { }; macb3: ethernet@32012000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32012000 0x0 0x2000>; interrupts = , , diff --git a/arch/arm64/boot/dts/phytium/pe220x.dtsi b/arch/arm64/boot/dts/phytium/pe220x.dtsi index 3f9df3b19c27995af8fc6ffac7d91497e4230f2e..fca212bf276c313408404ad86f253f51f06dcc26 100644 --- a/arch/arm64/boot/dts/phytium/pe220x.dtsi +++ b/arch/arm64/boot/dts/phytium/pe220x.dtsi @@ -312,6 +312,12 @@ bt: bt@48 { interrupts = ; status = "disabled"; }; + lpc_snoop: lpc-snoop@150 { + compatible = "phytium,pe2201-lpc-snoop"; + reg = <0x150 0x10>; + interrupts = ; + status = "disabled"; + }; }; /* MIO */ @@ -510,7 +516,8 @@ spi3: spi@2803d000 { watchdog0: watchdog@28040000 { compatible = "arm,sbsa-gwdt"; reg = <0x0 0x28041000 0x0 0x1000>, - <0x0 0x28040000 0x0 0x1000>; + <0x0 0x28040000 0x0 0x1000>, + <0x0 0x32a11810 0x0 0x0002>; interrupts = ; timeout-sec = <30>; status = "disabled"; @@ -519,7 +526,8 @@ watchdog0: watchdog@28040000 { watchdog1: watchdog@28042000 { compatible = "arm,sbsa-gwdt"; reg = <0x0 0x28043000 0x0 0x1000>, - <0x0 0x28042000 0x0 0x1000>; + <0x0 0x28042000 0x0 0x1000>, + <0x0 0x32a11812 0x0 0x0002>; interrupts = ; timeout-sec = <30>; status = "disabled"; @@ -530,6 +538,7 @@ pwm0: pwm@2804a000 { reg = <0x0 0x2804a000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; @@ -538,6 +547,7 @@ pwm1: pwm@2804b000 { reg = <0x0 0x2804b000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; @@ -546,6 +556,7 @@ pwm2: pwm@2804c000 { reg = <0x0 0x2804c000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; @@ -554,6 +565,7 @@ pwm3: pwm@2804d000 { reg = <0x0 0x2804d000 0x0 0x1000>; interrupts = ; clocks = <&sysclk_50mhz>; + #pwm-cells = <3>; status = "disabled"; }; diff --git a/drivers/Kconfig b/drivers/Kconfig index 8395bc515996f18982e89bc393c4edb458997873..d73b80b468e1150cb9f51a56a1231400463b3859 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -221,4 +221,6 @@ source "drivers/siox/Kconfig" source "drivers/slimbus/Kconfig" +source "drivers/jtag/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index e1ce029d28fdba01b73752d311d072ec6b81fab5..0bb54bfed5b4814608b2c85d1e2c0d6c1f21fbea 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -186,3 +186,4 @@ obj-$(CONFIG_MULTIPLEXER) += mux/ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ obj-$(CONFIG_SIOX) += siox/ obj-$(CONFIG_GNSS) += gnss/ +obj-$(CONFIG_JTAG) += jtag/ diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 9f9910799499f112cbfa75e23eb8f3d9304321da..5a01c66341fbdf5c3c01f2fa72efd7492451b56b 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -147,3 +147,12 @@ config ASPEED_BT_IPMI_BMC Provides a driver for the BT (Block Transfer) IPMI interface found on Aspeed SOCs (AST2400 and AST2500). The driver implements the BMC side of the BT interface. + +config IPMB_DEVICE_INTERFACE + tristate "IPMB Interface handler" + depends on I2C + depends on I2C_SLAVE + help + Provides a dirver for a device (Satellite MC) to + receive requests and send responses back to the BMC via + the IPMB interface. This module requires I2C support. diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index e94565313102241a85730b6edc31358fb8afbf74..f3903a95ae0d0e52a324bc78f4b0003d9a609660 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o obj-$(CONFIG_PHYTIUM_KCS_IPMI_BMC) += kcs_bmc_phytium.o obj-$(CONFIG_PHYTIUM_BT_IPMI_BMC) += bt_bmc_phytium.o +obj-$(CONFIG_IPMB_DEVICE_INTERFACE) += ipmb_dev_int.o diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c new file mode 100755 index 0000000000000000000000000000000000000000..10765262f6adf6e1d3b0ebbaa42822b0614ea0ca --- /dev/null +++ b/drivers/char/ipmi/ipmb_dev_int.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * IPMB driver to receive a request and send a response + * + * Copyright (C) 2019 Mellanox Techologies, Ltd. + * + * This was inspired by Brendan Higgins' ipmi-bmc-bt-i2c driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_MSG_LEN 240 +#define IPMB_REQUEST_LEN_MIN 7 +#define NETFN_RSP_BIT_MASK 0x4 +#define REQUEST_QUEUE_MAX_LEN 256 + +#define IPMB_MSG_LEN_IDX 0 +#define RQ_SA_8BIT_IDX 1 +#define NETFN_LUN_IDX 2 + +#define GET_7BIT_ADDR(addr_8bit) (addr_8bit >> 1) +#define GET_8BIT_ADDR(addr_7bit) ((addr_7bit << 1) & 0xff) + +#define IPMB_MSG_PAYLOAD_LEN_MAX (MAX_MSG_LEN - IPMB_REQUEST_LEN_MIN - 1) + +#define SMBUS_MSG_HEADER_LENGTH 2 +#define SMBUS_MSG_IDX_OFFSET (SMBUS_MSG_HEADER_LENGTH + 1) + +struct ipmb_msg { + u8 len; + u8 rs_sa; + u8 netfn_rs_lun; + u8 checksum1; + u8 rq_sa; + u8 rq_seq_rq_lun; + u8 cmd; + u8 payload[IPMB_MSG_PAYLOAD_LEN_MAX]; + /* checksum2 is included in payload */ +} __packed; + +struct ipmb_request_elem { + struct list_head list; + struct ipmb_msg request; +}; + +struct ipmb_dev { + struct i2c_client *client; + struct miscdevice miscdev; + struct ipmb_msg request; + struct list_head request_queue; + atomic_t request_queue_len; + size_t msg_idx; + spinlock_t lock; + wait_queue_head_t wait_queue; + struct mutex file_mutex; + bool is_i2c_protocol; +}; + +static inline struct ipmb_dev *to_ipmb_dev(struct file *file) +{ + return container_of(file->private_data, struct ipmb_dev, miscdev); +} + +static ssize_t ipmb_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + struct ipmb_dev *ipmb_dev = to_ipmb_dev(file); + struct ipmb_request_elem *queue_elem; + struct ipmb_msg msg; + ssize_t ret = 0; + + memset(&msg, 0, sizeof(msg)); + + spin_lock_irq(&ipmb_dev->lock); + + while (list_empty(&ipmb_dev->request_queue)) { + spin_unlock_irq(&ipmb_dev->lock); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(ipmb_dev->wait_queue, + !list_empty(&ipmb_dev->request_queue)); + if (ret) + return ret; + + spin_lock_irq(&ipmb_dev->lock); + } + + queue_elem = list_first_entry(&ipmb_dev->request_queue, + struct ipmb_request_elem, list); + memcpy(&msg, &queue_elem->request, sizeof(msg)); + list_del(&queue_elem->list); + kfree(queue_elem); + atomic_dec(&ipmb_dev->request_queue_len); + + spin_unlock_irq(&ipmb_dev->lock); + + count = min_t(size_t, count, msg.len + 1); + if (copy_to_user(buf, &msg, count)) + ret = -EFAULT; + + return ret < 0 ? ret : count; +} + +static int ipmb_i2c_write(struct i2c_client *client, u8 *msg, u8 addr) +{ + struct i2c_msg i2c_msg; + + /* + * subtract 1 byte (rq_sa) from the length of the msg passed to + * raw i2c_transfer + */ + i2c_msg.len = msg[IPMB_MSG_LEN_IDX] - 1; + + /* Assign message to buffer except first 2 bytes (length and address) */ + i2c_msg.buf = msg + 2; + + i2c_msg.addr = addr; + i2c_msg.flags = client->flags & I2C_CLIENT_PEC; + + return i2c_transfer(client->adapter, &i2c_msg, 1); +} + +static ssize_t ipmb_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ipmb_dev *ipmb_dev = to_ipmb_dev(file); + u8 rq_sa, netf_rq_lun, msg_len; + struct i2c_client *temp_client; + u8 msg[MAX_MSG_LEN]; + ssize_t ret; + + if (count > sizeof(msg)) + return -EINVAL; + + if (copy_from_user(&msg, buf, count)) + return -EFAULT; + + if (count < msg[0]) + return -EINVAL; + + rq_sa = GET_7BIT_ADDR(msg[RQ_SA_8BIT_IDX]); + netf_rq_lun = msg[NETFN_LUN_IDX]; + + /* Check i2c block transfer vs smbus */ + if (ipmb_dev->is_i2c_protocol) { + ret = ipmb_i2c_write(ipmb_dev->client, msg, rq_sa); + return (ret == 1) ? count : ret; + } + + /* + * subtract rq_sa and netf_rq_lun from the length of the msg. Fill the + * temporary client. Note that its use is an exception for IPMI. + */ + msg_len = msg[IPMB_MSG_LEN_IDX] - SMBUS_MSG_HEADER_LENGTH; + temp_client = kmemdup(ipmb_dev->client, sizeof(*temp_client), GFP_KERNEL); + if (!temp_client) + return -ENOMEM; + + temp_client->addr = rq_sa; + + ret = i2c_smbus_write_block_data(temp_client, netf_rq_lun, msg_len, + msg + SMBUS_MSG_IDX_OFFSET); + kfree(temp_client); + + return ret < 0 ? ret : count; +} + +static __poll_t ipmb_poll(struct file *file, poll_table *wait) +{ + struct ipmb_dev *ipmb_dev = to_ipmb_dev(file); + __poll_t mask = EPOLLOUT; + + mutex_lock(&ipmb_dev->file_mutex); + poll_wait(file, &ipmb_dev->wait_queue, wait); + + if (atomic_read(&ipmb_dev->request_queue_len)) + mask |= EPOLLIN; + mutex_unlock(&ipmb_dev->file_mutex); + + return mask; +} + +static const struct file_operations ipmb_fops = { + .owner = THIS_MODULE, + .read = ipmb_read, + .write = ipmb_write, + .poll = ipmb_poll, +}; + +/* Called with ipmb_dev->lock held. */ +static void ipmb_handle_request(struct ipmb_dev *ipmb_dev) +{ + struct ipmb_request_elem *queue_elem; + + if (atomic_read(&ipmb_dev->request_queue_len) >= + REQUEST_QUEUE_MAX_LEN) + return; + + queue_elem = kmalloc(sizeof(*queue_elem), GFP_ATOMIC); + if (!queue_elem) + return; + + memcpy(&queue_elem->request, &ipmb_dev->request, + sizeof(struct ipmb_msg)); + memset(&ipmb_dev->request, 0, sizeof(ipmb_dev->request)); + list_add(&queue_elem->list, &ipmb_dev->request_queue); + atomic_inc(&ipmb_dev->request_queue_len); + wake_up_all(&ipmb_dev->wait_queue); +} + +static u8 ipmb_verify_checksum1(struct ipmb_dev *ipmb_dev, u8 rs_sa) +{ + /* The 8 lsb of the sum is 0 when the checksum is valid */ + return (rs_sa + ipmb_dev->request.netfn_rs_lun + + ipmb_dev->request.checksum1); +} + +/* + * Verify if message has proper ipmb header with minimum length + * and correct checksum byte. + */ +static bool is_ipmb_msg(struct ipmb_dev *ipmb_dev, u8 rs_sa) +{ + if ((ipmb_dev->msg_idx >= IPMB_REQUEST_LEN_MIN) && + (!ipmb_verify_checksum1(ipmb_dev, rs_sa))) + return true; + + return false; +} + +/* + * The IPMB protocol only supports I2C Writes so there is no need + * to support I2C_SLAVE_READ* events. + * This i2c callback function only monitors IPMB request messages + * and adds them in a queue, so that they can be handled by + * receive_ipmb_request. + */ +static int ipmb_slave_cb(struct i2c_client *client, + enum i2c_slave_event event, u8 *val) +{ + struct ipmb_dev *ipmb_dev = i2c_get_clientdata(client); + u8 *buf = (u8 *)&ipmb_dev->request; + unsigned long flags; + + spin_lock_irqsave(&ipmb_dev->lock, flags); + switch (event) { + case I2C_SLAVE_WRITE_REQUESTED: + memset(&ipmb_dev->request, 0, sizeof(ipmb_dev->request)); + ipmb_dev->msg_idx = 0; + + /* + * At index 0, ipmb_msg stores the length of msg, + * skip it for now. + * The len will be populated once the whole + * buf is populated. + * + * The I2C bus driver's responsibility is to pass the + * data bytes to the backend driver; it does not + * forward the i2c slave address. + * Since the first byte in the IPMB message is the + * address of the responder, it is the responsibility + * of the IPMB driver to format the message properly. + * So this driver prepends the address of the responder + * to the received i2c data before the request message + * is handled in userland. + */ + buf[++ipmb_dev->msg_idx] = GET_8BIT_ADDR(client->addr); + break; + + case I2C_SLAVE_WRITE_RECEIVED: + if (ipmb_dev->msg_idx >= sizeof(struct ipmb_msg) - 1){ + break; + } + buf[++ipmb_dev->msg_idx] = *val; + + break; + + case I2C_SLAVE_STOP: + ipmb_dev->request.len = ipmb_dev->msg_idx; + if (is_ipmb_msg(ipmb_dev, GET_8BIT_ADDR(client->addr))){ + ipmb_handle_request(ipmb_dev); + } + break; + + default: + break; + } + spin_unlock_irqrestore(&ipmb_dev->lock, flags); + + return 0; +} + +static int ipmb_probe(struct i2c_client *client) +{ + struct ipmb_dev *ipmb_dev; + int ret; + + ipmb_dev = devm_kzalloc(&client->dev, sizeof(*ipmb_dev), + GFP_KERNEL); + if (!ipmb_dev) + return -ENOMEM; + + spin_lock_init(&ipmb_dev->lock); + init_waitqueue_head(&ipmb_dev->wait_queue); + atomic_set(&ipmb_dev->request_queue_len, 0); + INIT_LIST_HEAD(&ipmb_dev->request_queue); + + mutex_init(&ipmb_dev->file_mutex); + + ipmb_dev->miscdev.minor = MISC_DYNAMIC_MINOR; + + ipmb_dev->miscdev.name = devm_kasprintf(&client->dev, GFP_KERNEL, + "%s%d", "ipmb-", + client->adapter->nr); + ipmb_dev->miscdev.fops = &ipmb_fops; + ipmb_dev->miscdev.parent = &client->dev; + ret = misc_register(&ipmb_dev->miscdev); + if (ret) + return ret; + + ipmb_dev->is_i2c_protocol + = device_property_read_bool(&client->dev, "i2c-protocol"); + + ipmb_dev->client = client; + i2c_set_clientdata(client, ipmb_dev); + ret = i2c_slave_register(client, ipmb_slave_cb); + if (ret) { + misc_deregister(&ipmb_dev->miscdev); + return ret; + } + + return 0; +} + +static int ipmb_remove(struct i2c_client *client) +{ + struct ipmb_dev *ipmb_dev = i2c_get_clientdata(client); + + i2c_slave_unregister(client); + misc_deregister(&ipmb_dev->miscdev); + + return 0; +} + +static const struct i2c_device_id ipmb_id[] = { + { "ipmb-dev", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, ipmb_id); + +static const struct acpi_device_id acpi_ipmb_id[] = { + { "IPMB0001", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, acpi_ipmb_id); +#ifdef CONFIG_OF +static const struct of_device_id phytium_ipmb_of_match[] = { + { .compatible = "ipmb-dev", }, + {}, +}; +MODULE_DEVICE_TABLE(of, phytium_ipmb_of_match); +#endif +static struct i2c_driver ipmb_driver = { + .driver = { + .name = "ipmb-dev", + .of_match_table = of_match_ptr(phytium_ipmb_of_match), + .acpi_match_table = ACPI_PTR(acpi_ipmb_id), + }, + .probe_new = ipmb_probe, + .remove = ipmb_remove, + .id_table = ipmb_id, +}; +module_i2c_driver(ipmb_driver); + +MODULE_AUTHOR("Mellanox Technologies"); +MODULE_DESCRIPTION("IPMB driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/ipmi/kcs_bmc_phytium.c b/drivers/char/ipmi/kcs_bmc_phytium.c index 38729de065badf01240538cc4278b136488d2fbd..8b4c817083a2c4bc1c87008a9673c2213487777e 100644 --- a/drivers/char/ipmi/kcs_bmc_phytium.c +++ b/drivers/char/ipmi/kcs_bmc_phytium.c @@ -95,6 +95,10 @@ static u8 phytium_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg) rc = regmap_read(priv->map, reg, &val); WARN(rc != 0, "regmap_read() failed: %d\n", rc); + if (reg == LPC_IDR1 || reg == LPC_IDR2 || + reg == LPC_IDR3 || reg == LPC_IDR4) + rc = regmap_read(priv->map, reg, &val); + return rc == 0 ? (u8) val : 0; } @@ -103,6 +107,10 @@ static void phytium_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data) struct phytium_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc); int rc; + if (reg == LPC_ODR1 || reg == LPC_ODR2 || + reg == LPC_ODR3 || reg == LPC_ODR4) + regmap_write(priv->map, reg, data); + rc = regmap_write(priv->map, reg, data); WARN(rc != 0, "regmap_write() failed: %d\n", rc); } diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index a6649370b90690483394354b5c2926a0df988c07..03b0b78a01be83116168fb75dc52e5c7bb725a43 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -81,7 +81,7 @@ config GPIO_GENERIC # This symbol is selected by both MMIO and PCI expanders config GPIO_PHYTIUM_CORE - tristate + bool # This symbol is selected by both I2C and SPI expanders config GPIO_MAX730X @@ -409,7 +409,7 @@ config GPIO_OMAP Say yes here to enable GPIO support for TI OMAP SoCs. config GPIO_PHYTIUM_PLAT - tristate "Phytium GPIO Platform support" + bool "Phytium GPIO Platform support" default y if ARCH_PHYTIUM depends on ARM64 select GPIO_PHYTIUM_CORE @@ -1332,7 +1332,7 @@ config GPIO_PCIE_IDIO_24 filters are deactivated by this driver. config GPIO_PHYTIUM_PCI - tristate "Phytium GPIO PCI support" + bool "Phytium GPIO PCI support" select GPIO_PHYTIUM_CORE select IRQ_DOMAIN select GENERIC_IRQ_CHIP diff --git a/drivers/gpio/gpio-phytium-core.c b/drivers/gpio/gpio-phytium-core.c index d73298ea42476747222a6874479ffa19b5f9daf5..7f26de002ec7aeb2f3ff76d0260d9f3bf12ca35b 100644 --- a/drivers/gpio/gpio-phytium-core.c +++ b/drivers/gpio/gpio-phytium-core.c @@ -128,14 +128,14 @@ int phytium_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, return -EINVAL; ddr = gpio->regs + GPIO_SWPORTA_DDR + (loc.port * GPIO_PORT_STRIDE); + phytium_gpio_set(gc, offset, value); + raw_spin_lock_irqsave(&gpio->lock, flags); writel(readl(ddr) | BIT(loc.offset), ddr); raw_spin_unlock_irqrestore(&gpio->lock, flags); - phytium_gpio_set(gc, offset, value); - return 0; } EXPORT_SYMBOL_GPL(phytium_gpio_direction_output); diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c index 91976b6ca30002b985ef6594f82fcf10aabf91db..6e07b44aff695de7b382da36e0cba171f4ed3bc0 100644 --- a/drivers/hwmon/scmi-hwmon.c +++ b/drivers/hwmon/scmi-hwmon.c @@ -30,7 +30,7 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, sensor = *(scmi_sensors->info[type] + channel); ret = h->sensor_ops->reading_get(h, sensor->id, false, &value); if (!ret) - *val = value; + *val = value * 1000; return ret; } diff --git a/drivers/hwmon/tacho-phytium.c b/drivers/hwmon/tacho-phytium.c index 4f5d671a296008fbee1a4a7d51e47fb22bf22a29..ce1bfc86979b715825128faab407d1c7a11f1652 100644 --- a/drivers/hwmon/tacho-phytium.c +++ b/drivers/hwmon/tacho-phytium.c @@ -18,6 +18,7 @@ #include #include #include +#include #define TIMER_CTRL_REG 0x00 #define TIMER_CTRL_MODE_SHIFT 0//0:1 @@ -55,6 +56,8 @@ #define TIMER_INT_CLR_MASK GENMASK(5, 0) +#define MAX_PWM 255 + enum tacho_modes { tacho_mode = 1, capture_mode, @@ -66,9 +69,17 @@ falling_edge, double_edge, }; +struct pwm_data { + struct pwm_device *pwm; + struct mutex pwm_lock; + u8 pwm_value; + bool pwm_present; +}; + struct phytium_tacho { struct device *dev; struct device *hwmon; + struct pwm_data pwmdata; void __iomem *base; struct clk *clk; u32 freq; @@ -76,6 +87,7 @@ struct phytium_tacho { u8 work_mode; u8 edge_mode; u32 debounce; + u32 per_edges; }; static u16 capture_count; @@ -140,7 +152,7 @@ static int phytium_get_fan_tach_rpm(struct phytium_tacho *priv) if (raw_data == 0) return 0; - return (clk_source * 60 * raw_data) / 0x2faf080 / tach_div; + return (clk_source * 60 * raw_data) / 0x2faf080 / tach_div / priv->per_edges; } static ssize_t show_rpm(struct device *dev, struct device_attribute *attr, @@ -155,6 +167,77 @@ static ssize_t show_rpm(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", rpm); } +static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct phytium_tacho *priv = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", priv->pwmdata.pwm_value); +} + +static int __set_pwm(struct phytium_tacho *priv, unsigned long pwm) +{ + unsigned long period; + int ret = 0; + struct pwm_state state = {}; + mutex_lock(&priv->pwmdata.pwm_lock); + + pwm_init_state(priv->pwmdata.pwm, &state); + period = priv->pwmdata.pwm->args.period; + state.duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); + state.enabled = pwm ? true : false; + ret = pwm_apply_state(priv->pwmdata.pwm, &state); + mutex_unlock(&priv->pwmdata.pwm_lock); + return ret; +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct phytium_tacho *priv = dev_get_drvdata(dev); + long fan_ctrl; + + ret = kstrtol(buf, 10, &fan_ctrl); + if (ret != 0) + return ret; + + if (fan_ctrl < 0 || fan_ctrl > MAX_PWM) + return -EINVAL; + + if (priv->pwmdata.pwm_value == fan_ctrl) + return count; + + ret = __set_pwm(priv, fan_ctrl); + if (!ret) + priv->pwmdata.pwm_value = fan_ctrl; + + return count; +} + +static SENSOR_DEVICE_ATTR(pwm1, 0644, show_pwm, set_pwm, 0); + +static struct attribute *pwm_attrs[] = { + &sensor_dev_attr_pwm1.dev_attr.attr, + NULL, +}; + +static umode_t pwm_is_visible(struct kobject *kobj, struct attribute *a, + int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct phytium_tacho *priv = dev_get_drvdata(dev); + + if (!priv->pwmdata.pwm_present) + return 0; + return a->mode; +} + +static const struct attribute_group pwm_group = { + .attrs = pwm_attrs, + .is_visible = pwm_is_visible, +}; + static SENSOR_DEVICE_ATTR(fan_input, 0444, show_rpm, NULL, 0); @@ -176,6 +259,7 @@ static const struct attribute_group tacho_group = { }; static const struct attribute_group *tacho_groups[] = { + &pwm_group, &tacho_group, NULL }; @@ -267,11 +351,62 @@ static int phytium_tacho_get_debounce(struct phytium_tacho *tacho) return 0; } +static int phytium_tacho_create_pwm_port(struct phytium_tacho *tacho) +{ + struct pwm_state state = {}; + int ret; + tacho->pwmdata.pwm_present = true; + tacho->pwmdata.pwm_value = MAX_PWM; + + /* Set duty cycle to maximum allowed and enable PWM output */ + pwm_init_state(tacho->pwmdata.pwm, &state); + state.duty_cycle = tacho->pwmdata.pwm->args.period - 1; + state.enabled = true; + + ret = pwm_apply_state(tacho->pwmdata.pwm, &state); + if (ret) + dev_err(tacho->dev, "Failed to configure PWM\n"); + return ret; +} + +static void phytium_tacho_create_pwm(struct phytium_tacho *tacho) +{ + u32 ret,len; + struct device_node *nc = tacho->dev->of_node; + + /* support without pwms property */ + if (of_find_property(nc, "pwms", &len)) { + tacho->pwmdata.pwm = devm_of_pwm_get(tacho->dev, nc, NULL); + if (IS_ERR(tacho->pwmdata.pwm)) { + dev_warn(tacho->dev, "Could not get PWM\n"); + } else { + ret = phytium_tacho_create_pwm_port(tacho); + if (ret) + dev_warn(tacho->dev, + "Could not creat PWM (%x)\n", ret); + } + } else { + dev_info(tacho->dev, "No PWM config\n"); + } +} + +static int phytium_tacho_get_edges_per_revolution(struct phytium_tacho *tacho) +{ + u32 value; + struct device_node *nc = tacho->dev->of_node; + + if (!of_property_read_u32(nc, "edges-per-revolution", &value)) + return value; + else + return 1; +} + static void phytium_tacho_get_of_data(struct phytium_tacho *tacho) { tacho->work_mode = phytium_tacho_get_work_mode(tacho); tacho->edge_mode = phytium_tacho_get_edge_mode(tacho); tacho->debounce = phytium_tacho_get_debounce(tacho); + tacho->per_edges = phytium_tacho_get_edges_per_revolution(tacho); } static int phytium_tacho_probe(struct platform_device *pdev) @@ -319,6 +454,8 @@ static int phytium_tacho_probe(struct platform_device *pdev) return ret; } + phytium_tacho_create_pwm(tacho); + phytium_tacho_get_of_data(tacho); phytium_tacho_init(tacho); diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 0aea0dd31c9188dad8ec55f385d09e399e07c542..40b0e2bd1504711bb501ed848b7a954c124599b0 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -54,9 +54,9 @@ obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o i2c-designware-pci-objs := i2c-designware-pcidrv.o obj-$(CONFIG_I2C_DIGICOLOR) += i2c-digicolor.o obj-$(CONFIG_I2C_PHYTIUM_CORE) += i2c-phytium-core.o -i2c-phytium-core-objs := i2c-phytium-common.o i2c-phytium-master.o i2c-phytium-slave.o +i2c-phytium-core-objs := i2c-phytium-common.o i2c-phytium-mix.o obj-$(CONFIG_I2C_PHYTIUM_PCI) += i2c-phytium-pci.o -obj-$(CONFIG_I2C_PHYTIUM_PLATFORM) += i2c-phytium-platform.o +obj-$(CONFIG_I2C_PHYTIUM_PLATFORM) += i2c-phytium-chmd-platform.o obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o obj-$(CONFIG_I2C_EMEV2) += i2c-emev2.o diff --git a/drivers/i2c/busses/i2c-phytium-chmd-platform.c b/drivers/i2c/busses/i2c-phytium-chmd-platform.c new file mode 100644 index 0000000000000000000000000000000000000000..03356a73996221ec7b0561310f41d8fabae8f13a --- /dev/null +++ b/drivers/i2c/busses/i2c-phytium-chmd-platform.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Phytium I2C adapter driver. + * + * Derived from Synopysys I2C driver. + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd. + * + * Copyright (C) 2021-2023, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-phytium-core.h" + +#define DRV_NAME "i2c-phytium-platform" + +static u32 i2c_phytium_get_clk_rate_khz(struct phytium_i2c_dev *dev) +{ + return clk_get_rate(dev->clk) / 1000; +} + +#ifdef CONFIG_ACPI +static void phytium_i2c_acpi_params(struct platform_device *pdev, char method[], + u16 *hcnt, u16 *lcnt, u32 *sda_hold) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + acpi_handle handle = ACPI_HANDLE(&pdev->dev); + union acpi_object *obj; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, method, NULL, &buf))) + return; + + obj = (union acpi_object *)buf.pointer; + if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 3) { + const union acpi_object *objs = obj->package.elements; + + *hcnt = (u16)objs[0].integer.value; + *lcnt = (u16)objs[1].integer.value; + *sda_hold = (u32)objs[2].integer.value; + } + + kfree(buf.pointer); +} + +static int phytium_i2c_acpi_configure(struct platform_device *pdev) +{ + struct phytium_i2c_dev *dev = platform_get_drvdata(pdev); + struct i2c_timings *t = &dev->timings; + u32 ss_ht = 0, fp_ht = 0, hs_ht = 0, fs_ht = 0; + acpi_handle handle = ACPI_HANDLE(&pdev->dev); + const struct acpi_device_id *id; + + dev->adapter.nr = -1; + dev->tx_fifo_depth = 32; + dev->rx_fifo_depth = 32; + + /* + * Try to get SDA hold time and *CNT values from an ACPI method for + * selected speed modes. + */ + phytium_i2c_acpi_params(pdev, "SSCN", &dev->ss_hcnt, &dev->ss_lcnt, + &ss_ht); + phytium_i2c_acpi_params(pdev, "FPCN", &dev->fp_hcnt, &dev->fp_lcnt, + &fp_ht); + phytium_i2c_acpi_params(pdev, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, + &hs_ht); + phytium_i2c_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, + &fs_ht); + + switch (t->bus_freq_hz) { + case 100000: + dev->sda_hold_time = ss_ht; + break; + case 1000000: + dev->sda_hold_time = fp_ht; + break; + case 3400000: + dev->sda_hold_time = hs_ht; + break; + case 400000: + default: + dev->sda_hold_time = fs_ht; + break; + } + + id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); + if (id && id->driver_data) + dev->flags |= (u32)id->driver_data; + + return 0; +} + +static const struct acpi_device_id phytium_i2c_acpi_match[] = { { "PHYT0038", + 0 }, + {} }; +MODULE_DEVICE_TABLE(acpi, phytium_i2c_acpi_match); +#else +static inline int phytium_i2c_acpi_configure(struct platform_device *pdev) +{ + return -ENODEV; +} +#endif + +static void i2c_phytium_configure_master(struct phytium_i2c_dev *dev) +{ + struct i2c_timings *t = &dev->timings; + + dev->functionality = + I2C_FUNC_10BIT_ADDR | IC_DEFAULT_FUNCTIONALITY | I2C_FUNC_SLAVE; + + dev->master_cfg = + IC_CON_MASTER | IC_CON_SLAVE_DISABLE | IC_CON_RESTART_EN; + + dev->mode = PHYTIUM_IC_MASTER; + + switch (t->bus_freq_hz) { + case 100000: + dev->master_cfg |= IC_CON_SPEED_STD; + break; + case 3400000: + dev->master_cfg |= IC_CON_SPEED_HIGH; + break; + default: + dev->master_cfg |= IC_CON_SPEED_FAST; + } +} + +static void i2c_phytium_configure_slave(struct phytium_i2c_dev *dev) +{ + dev->functionality = I2C_FUNC_SLAVE | IC_DEFAULT_FUNCTIONALITY; + + dev->slave_cfg = IC_CON_RX_FIFO_FULL_HLD_CTRL | IC_CON_RESTART_EN | + IC_CON_STOP_DET_IFADDRESSED; + + dev->mode = PHYTIUM_IC_SLAVE; +} + +static int phytium_i2c_plat_probe(struct platform_device *pdev) +{ + struct i2c_adapter *adap; + struct phytium_i2c_dev *dev; + struct i2c_timings *t; + u32 acpi_speed; + struct resource *mem; + int irq, ret, i; + static const int supported_speeds[] = { 0, 100000, 400000, 1000000, + 3400000 }; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + dev = devm_kzalloc(&pdev->dev, sizeof(struct phytium_i2c_dev), + GFP_KERNEL); + if (!dev) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev->i2c_resource_addr = mem->start; + dev->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(dev->base)) + return PTR_ERR(dev->base); + + dev->dev = &pdev->dev; + dev->irq = irq; + dev->first_time_init_master = false; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + dev->use_ipmb = false; + dev->slave_state = SLAVE_STATE_IDLE; +#endif + spin_lock_init(&dev->i2c_lock); + platform_set_drvdata(pdev, dev); + + dev->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(dev->rst)) { + if (PTR_ERR(dev->rst) == -EPROBE_DEFER) + return -EPROBE_DEFER; + } else { + reset_control_deassert(dev->rst); + } + + t = &dev->timings; + i2c_parse_fw_timings(&pdev->dev, t, false); + + acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev); + /* + * Some DSTDs use a non standard speed, round down to the lowest + * standard speed. + */ + for (i = 1; i < ARRAY_SIZE(supported_speeds); i++) { + if (acpi_speed < supported_speeds[i]) + break; + } + acpi_speed = supported_speeds[i - 1]; + + /* + * Find bus speed from the "clock-frequency" device property, ACPI + * or by using fast mode if neither is set. + */ + if (acpi_speed && t->bus_freq_hz) + t->bus_freq_hz = min(t->bus_freq_hz, acpi_speed); + else if (acpi_speed || t->bus_freq_hz) + t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed); + else + t->bus_freq_hz = 400000; + + if (has_acpi_companion(&pdev->dev)) + phytium_i2c_acpi_configure(pdev); + + /* + * Only standard mode at 100kHz, fast mode at 400kHz, + * fast mode plus at 1MHz and high speed mode at 3.4MHz are supported. + */ + if (t->bus_freq_hz != 100000 && t->bus_freq_hz != 400000 && + t->bus_freq_hz != 1000000 && t->bus_freq_hz != 3400000) { + dev_err(&pdev->dev, + "%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are " + "supported\n", + t->bus_freq_hz); + ret = -EINVAL; + goto exit_reset; + } + + if (i2c_detect_slave_mode(&pdev->dev)) { + i2c_phytium_configure_slave(dev); + } else { + dev->first_time_init_master = true; + i2c_phytium_configure_master(dev); + } + dev->clk = devm_clk_get(&pdev->dev, NULL); + if (!i2c_phytium_prepare_clk(dev, true)) { + u64 clk_khz; + + dev->get_clk_rate_khz = i2c_phytium_get_clk_rate_khz; + clk_khz = dev->get_clk_rate_khz(dev); + + if (!dev->sda_hold_time && t->sda_hold_ns) + dev->sda_hold_time = div_u64( + clk_khz * t->sda_hold_ns + 500000, 1000000); + } + + dev->tx_fifo_depth = 7; + dev->rx_fifo_depth = 7; + dev->adapter.nr = pdev->id; + + adap = &dev->adapter; + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_DEPRECATED; + ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); + adap->dev.of_node = pdev->dev.of_node; + + dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_SMART_PREPARE | + DPM_FLAG_SMART_SUSPEND | + DPM_FLAG_LEAVE_SUSPENDED); + + /* The code below assumes runtime PM to be disabled. */ + WARN_ON(pm_runtime_enabled(&pdev->dev)); + + pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + + pm_runtime_enable(&pdev->dev); + + ret = i2c_phytium_probe(dev); + if (ret) + goto exit_probe; + + return ret; + +exit_probe: + pm_runtime_disable(dev->dev); +exit_reset: + if (!IS_ERR_OR_NULL(dev->rst)) + reset_control_assert(dev->rst); + return ret; +} + +static int phytium_i2c_plat_remove(struct platform_device *pdev) +{ + struct phytium_i2c_dev *dev = platform_get_drvdata(pdev); + + pm_runtime_get_sync(&pdev->dev); + + i2c_del_adapter(&dev->adapter); + + dev->disable(dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(dev->dev); + + if (!IS_ERR_OR_NULL(dev->rst)) + reset_control_assert(dev->rst); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id phytium_i2c_of_match[] = { + { + .compatible = "phytium,i2c", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, phytium_i2c_of_match); +#endif + +static int __maybe_unused phytium_i2c_plat_suspend(struct device *dev) +{ + struct phytium_i2c_dev *idev = dev_get_drvdata(dev); + + idev->disable(idev); + i2c_phytium_prepare_clk(idev, false); + + return 0; +} + +static int __maybe_unused phytium_i2c_plat_resume(struct device *dev) +{ + struct phytium_i2c_dev *idev = dev_get_drvdata(dev); + + i2c_phytium_prepare_clk(idev, true); + + idev->init(idev); + + return 0; +} + +static const struct dev_pm_ops phytium_i2c_dev_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(phytium_i2c_plat_suspend, + phytium_i2c_plat_resume) + SET_RUNTIME_PM_OPS(phytium_i2c_plat_suspend, + phytium_i2c_plat_resume, NULL) +}; + +static struct platform_driver phytium_i2c_driver = { + .probe = phytium_i2c_plat_probe, + .remove = phytium_i2c_plat_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(phytium_i2c_of_match), + .acpi_match_table = ACPI_PTR(phytium_i2c_acpi_match), + .pm = &phytium_i2c_dev_pm_ops, + }, +}; +module_platform_driver(phytium_i2c_driver); + +MODULE_ALIAS("platform:i2c-phytium"); +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Phytium I2C bus adapter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-phytium-common.c b/drivers/i2c/busses/i2c-phytium-common.c index a35bab2ffabcaa5f5406cf707f418d53db176f7c..07ca96a7c37ce637375126d17608f3c2b291d3ba 100644 --- a/drivers/i2c/busses/i2c-phytium-common.c +++ b/drivers/i2c/busses/i2c-phytium-common.c @@ -9,49 +9,81 @@ * * Copyright (C) 2021-2023, Phytium Technology Co., Ltd. */ + #include #include -#include -#include #include +#include +#include #include #include #include #include #include #include +#include #include "i2c-phytium-core.h" +extern int i2c_phytium_init_master(struct phytium_i2c_dev *dev); +extern int i2c_phytium_init_slave(struct phytium_i2c_dev *dev); + +int i2c_recover_controller(struct phytium_i2c_dev *dev) +{ + int index; + unsigned long flags; + + spin_lock_irqsave(&dev->i2c_lock, flags); + reset_control_reset(dev->rst); + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + dev->slave_state = SLAVE_STATE_IDLE; + dev->status = STATUS_IDLE; + if (dev->slave) + phytium_writel(dev, dev->slave->addr, IC_SAR); +#endif + + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return 0; +} + +#if IS_ENABLED(CONFIG_I2C_SLAVE) +int do_change_mode(int target_mode, struct phytium_i2c_dev *dev) +{ + if (target_mode == PHYTIUM_IC_MASTER) { + dev->disable_int(dev); + dev->disable(dev); + i2c_phytium_init_master(dev); + } else if (target_mode == PHYTIUM_IC_SLAVE) { + i2c_phytium_init_slave(dev); + __i2c_phytium_enable(dev); + } + + dev->mode = target_mode; + return 0; +} +#endif static char *abort_sources[] = { - [ABRT_7B_ADDR_NOACK] = - "slave address not acknowledged (7bit mode)", + [ABRT_7B_ADDR_NOACK] = "slave address not acknowledged (7bit mode)", [ABRT_10ADDR1_NOACK] = "first address byte not acknowledged (10bit mode)", [ABRT_10ADDR2_NOACK] = "second address byte not acknowledged (10bit mode)", - [ABRT_TXDATA_NOACK] = - "data not acknowledged", - [ABRT_GCALL_NOACK] = - "no acknowledgment for a general call", - [ABRT_GCALL_READ] = - "read after general call", - [ABRT_SBYTE_ACKDET] = - "start byte acknowledged", + [ABRT_TXDATA_NOACK] = "data not acknowledged", + [ABRT_GCALL_NOACK] = "no acknowledgement for a general call", + [ABRT_GCALL_READ] = "read after general call", + [ABRT_SBYTE_ACKDET] = "start byte acknowledged", [ABRT_SBYTE_NORSTRT] = "trying to send start byte when restart is disabled", [ABRT_10B_RD_NORSTRT] = "trying to read when restart is disabled (10bit mode)", - [ABRT_MASTER_DIS] = - "trying to use disabled adapter", - [ARB_LOST] = - "lost arbitration", + [ABRT_MASTER_DIS] = "trying to use disabled adapter", + [ARB_LOST] = "lost arbitration", [ABRT_SLAVE_FLUSH_TXFIFO] = "read command so flush old data in the TX FIFO", [ABRT_SLAVE_ARBLOST] = "slave lost the bus while transmitting data to a remote master", - [ABRT_SLAVE_RD_INTX] = - "incorrect slave-transmitter mode configuration", + [ABRT_SLAVE_RD_INTX] = "incorrect slave-transmitter mode configuration", }; u32 phytium_readl(struct phytium_i2c_dev *dev, int offset) @@ -69,7 +101,8 @@ u32 i2c_phytium_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) if (cond) return (ic_clk * tSYMBOL + 500000) / 1000000 - 8 + offset; else - return (ic_clk * (tSYMBOL + tf) + 500000) / 1000000 - 3 + offset; + return (ic_clk * (tSYMBOL + tf) + 500000) / 1000000 - 3 + + offset; } u32 i2c_phytium_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset) @@ -96,22 +129,7 @@ int i2c_phytium_set_sda_hold(struct phytium_i2c_dev *dev) void __i2c_phytium_disable(struct phytium_i2c_dev *dev) { - int timeout = 100; - - do { - __i2c_phytium_disable_nowait(dev); - if ((phytium_readl(dev, IC_ENABLE_STATUS) & 1) == 0) - return; - - /* - * Wait 10 times the signaling period of the highest I2C - * transfer supported by the driver (for 400KHz this is - * 25us). - */ - usleep_range(25, 250); - } while (timeout--); - - dev_warn(dev->dev, "timeout in disabling adapter\n"); + __i2c_phytium_disable_nowait(dev); } unsigned long i2c_phytium_clk_rate(struct phytium_i2c_dev *dev) @@ -136,19 +154,10 @@ EXPORT_SYMBOL_GPL(i2c_phytium_prepare_clk); int i2c_phytium_wait_bus_not_busy(struct phytium_i2c_dev *dev) { - int timeout = 20; /* 20 ms */ - - while (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) { - if (timeout <= 0) { - dev_warn(dev->dev, "timeout waiting for bus ready\n"); - i2c_recover_bus(&dev->adapter); - - if (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) - return -ETIMEDOUT; - return 0; - } - timeout--; - usleep_range(1000, 1100); + if (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) { + if (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) + return -ETIMEDOUT; + return 0; } return 0; @@ -160,22 +169,19 @@ int i2c_phytium_handle_tx_abort(struct phytium_i2c_dev *dev) int i; if (abort_source & IC_TX_ABRT_NOACK) { - for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) - dev_dbg(dev->dev, - "%s: %s\n", __func__, abort_sources[i]); + for_each_set_bit (i, &abort_source, ARRAY_SIZE(abort_sources)); return -EREMOTEIO; } - for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) - dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]); + for_each_set_bit (i, &abort_source, ARRAY_SIZE(abort_sources)); - if (abort_source & IC_TX_ARB_LOST) + if (abort_source & IC_TX_ARB_LOST) { return -EAGAIN; - else if (abort_source & IC_TX_ABRT_GCALL_READ) + } else if (abort_source & IC_TX_ABRT_GCALL_READ) { return -EINVAL; - else + } else { return -EIO; - + } return 0; } diff --git a/drivers/i2c/busses/i2c-phytium-core.h b/drivers/i2c/busses/i2c-phytium-core.h index 2081a252724a466f6dc3b60dd02379f5f1bf5837..16f14be19866c41695ced168e2acca9d3fa35cc1 100644 --- a/drivers/i2c/busses/i2c-phytium-core.h +++ b/drivers/i2c/busses/i2c-phytium-core.h @@ -10,225 +10,244 @@ * Copyright (C) 2021-2023, Phytium Technology Co., Ltd. */ +#include #include #include -#include -#define IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \ - I2C_FUNC_SMBUS_BYTE | \ - I2C_FUNC_SMBUS_BYTE_DATA | \ - I2C_FUNC_SMBUS_WORD_DATA | \ - I2C_FUNC_SMBUS_BLOCK_DATA | \ - I2C_FUNC_SMBUS_I2C_BLOCK) - -#define IC_CON_MASTER 0x1 -#define IC_CON_SPEED_STD 0x2 -#define IC_CON_SPEED_FAST 0x4 -#define IC_CON_SPEED_HIGH 0x6 -#define IC_CON_SPEED_MASK 0x6 -#define IC_CON_10BITADDR_SLAVE 0x8 -#define IC_CON_10BITADDR_MASTER 0x10 -#define IC_CON_RESTART_EN 0x20 -#define IC_CON_SLAVE_DISABLE 0x40 -#define IC_CON_STOP_DET_IFADDRESSED 0x80 -#define IC_CON_TX_EMPTY_CTRL 0x100 -#define IC_CON_RX_FIFO_FULL_HLD_CTRL 0x200 - -#define IC_CON 0x0 -#define IC_TAR 0x4 -#define IC_SAR 0x8 -#define IC_DATA_CMD 0x10 -#define IC_SS_SCL_HCNT 0x14 -#define IC_SS_SCL_LCNT 0x18 -#define IC_FS_SCL_HCNT 0x1c -#define IC_FS_SCL_LCNT 0x20 -#define IC_HS_SCL_HCNT 0x24 -#define IC_HS_SCL_LCNT 0x28 -#define IC_INTR_STAT 0x2c -#define IC_INTR_MASK 0x30 -#define IC_RAW_INTR_STAT 0x34 -#define IC_RX_TL 0x38 -#define IC_TX_TL 0x3c -#define IC_CLR_INTR 0x40 -#define IC_CLR_RX_UNDER 0x44 -#define IC_CLR_RX_OVER 0x48 -#define IC_CLR_TX_OVER 0x4c -#define IC_CLR_RD_REQ 0x50 -#define IC_CLR_TX_ABRT 0x54 -#define IC_CLR_RX_DONE 0x58 -#define IC_CLR_ACTIVITY 0x5c -#define IC_CLR_STOP_DET 0x60 -#define IC_CLR_START_DET 0x64 -#define IC_CLR_GEN_CALL 0x68 -#define IC_ENABLE 0x6c -#define IC_STATUS 0x70 -#define IC_TXFLR 0x74 -#define IC_RXFLR 0x78 -#define IC_SDA_HOLD 0x7c -#define IC_TX_ABRT_SOURCE 0x80 -#define IC_ENABLE_STATUS 0x9c -#define IC_SMBCLK_LOW_MEXT 0xa8 -#define IC_SMBCLK_LOW_TIMEOUT 0xac -#define IC_SMBDAT_STUCK_TIMEOUT 0xb4 -#define IC_CLR_SMBCLK_EXT_LOW_TIMEOUT 0xbc -#define IC_CLR_SMBCLK_TMO_LOW_TIMEOUT 0xc0 -#define IC_CLR_SMBDAT_LOW_TIMEOUT 0xc4 -#define IC_CLR_SMBALERT_IN_N 0xd0 - -#define IC_INTR_RX_UNDER 0x001 -#define IC_INTR_RX_OVER 0x002 -#define IC_INTR_RX_FULL 0x004 -#define IC_INTR_TX_OVER 0x008 -#define IC_INTR_TX_EMPTY 0x010 -#define IC_INTR_RD_REQ 0x020 -#define IC_INTR_TX_ABRT 0x040 -#define IC_INTR_RX_DONE 0x080 -#define IC_INTR_ACTIVITY 0x100 -#define IC_INTR_STOP_DET 0x200 -#define IC_INTR_START_DET 0x400 -#define IC_INTR_GEN_CALL 0x800 -#define IC_INTR_SMBCLK_EXT_LOW_TIMEOUT 0x1000 -#define IC_INTR_SMBCLK_TMO_LOW_TIMEOUT 0x2000 -#define IC_INTR_SMBSDA_LOW_TIMEOUT 0x4000 -#define IC_INTR_SMBALERT_IN_N 0x20000 - -#define IC_INTR_DEFAULT_MASK (IC_INTR_RX_FULL | \ - IC_INTR_TX_ABRT | \ - IC_INTR_STOP_DET) -#define IC_INTR_MASTER_MASK (IC_INTR_DEFAULT_MASK | \ - IC_INTR_TX_EMPTY) -#define IC_INTR_SLAVE_MASK (IC_INTR_DEFAULT_MASK | \ - IC_INTR_RX_DONE | \ - IC_INTR_RX_UNDER | \ - IC_INTR_RD_REQ) -#define IC_INTR_SMBUS_MASK (IC_INTR_MASTER_MASK | \ - IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | \ - IC_INTR_SMBCLK_TMO_LOW_TIMEOUT | \ - IC_INTR_SMBSDA_LOW_TIMEOUT) - -#define IC_STATUS_ACTIVITY 0x1 -#define IC_STATUS_TFE BIT(2) -#define IC_STATUS_MASTER_ACTIVITY BIT(5) -#define IC_STATUS_SLAVE_ACTIVITY BIT(6) - -#define IC_SDA_HOLD_RX_SHIFT 16 -#define IC_SDA_HOLD_RX_MASK GENMASK(23, IC_SDA_HOLD_RX_SHIFT) - -#define IC_ERR_TX_ABRT 0x1 - -#define IC_TAR_10BITADDR_MASTER BIT(12) - -#define IC_COMP_PARAM_1_SPEED_MODE_HIGH (BIT(2) | BIT(3)) -#define IC_COMP_PARAM_1_SPEED_MODE_MASK GENMASK(3, 2) - -#define STATUS_IDLE 0x0 -#define STATUS_WRITE_IN_PROGRESS 0x1 -#define STATUS_READ_IN_PROGRESS 0x2 +#include + +#define IC_DEFAULT_FUNCTIONALITY \ + (I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | \ + I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_I2C_BLOCK) + +#define IC_CON_MASTER 0x1 +#define IC_CON_SPEED_STD 0x2 +#define IC_CON_SPEED_FAST 0x4 +#define IC_CON_SPEED_HIGH 0x6 +#define IC_CON_SPEED_MASK 0x6 +#define IC_CON_10BITADDR_SLAVE 0x8 +#define IC_CON_10BITADDR_MASTER 0x10 +#define IC_CON_RESTART_EN 0x20 +#define IC_CON_SLAVE_DISABLE 0x40 +#define IC_CON_STOP_DET_IFADDRESSED 0x80 +#define IC_CON_TX_EMPTY_CTRL 0x100 +#define IC_CON_RX_FIFO_FULL_HLD_CTRL 0x200 + +#define IC_CON 0x0 +#define IC_TAR 0x4 +#define IC_SAR 0x8 +#define IC_DATA_CMD 0x10 +#define IC_SS_SCL_HCNT 0x14 +#define IC_SS_SCL_LCNT 0x18 +#define IC_FS_SCL_HCNT 0x1c +#define IC_FS_SCL_LCNT 0x20 +#define IC_HS_SCL_HCNT 0x24 +#define IC_HS_SCL_LCNT 0x28 +#define IC_INTR_STAT 0x2c +#define IC_INTR_MASK 0x30 +#define IC_RAW_INTR_STAT 0x34 +#define IC_RX_TL 0x38 +#define IC_TX_TL 0x3c +#define IC_CLR_INTR 0x40 +#define IC_CLR_RX_UNDER 0x44 +#define IC_CLR_RX_OVER 0x48 +#define IC_CLR_TX_OVER 0x4c +#define IC_CLR_RD_REQ 0x50 +#define IC_CLR_TX_ABRT 0x54 +#define IC_CLR_RX_DONE 0x58 +#define IC_CLR_ACTIVITY 0x5c +#define IC_CLR_STOP_DET 0x60 +#define IC_CLR_START_DET 0x64 +#define IC_CLR_GEN_CALL 0x68 +#define IC_ENABLE 0x6c +#define IC_STATUS 0x70 +#define IC_TXFLR 0x74 +#define IC_RXFLR 0x78 +#define IC_SDA_HOLD 0x7c +#define IC_TX_ABRT_SOURCE 0x80 +#define IC_ENABLE_STATUS 0x9c +#define IC_SMBCLK_LOW_MEXT 0xa8 +#define IC_SMBCLK_LOW_TIMEOUT 0xac +#define IC_SMBDAT_STUCK_TIMEOUT 0xb4 +#define IC_CLR_SMBCLK_EXT_LOW_TIMEOUT 0xbc +#define IC_CLR_SMBCLK_TMO_LOW_TIMEOUT 0xc0 +#define IC_CLR_SMBDAT_LOW_TIMEOUT 0xc4 +#define IC_CLR_SMBALERT_IN_N 0xd0 + +#define IC_INTR_RX_UNDER 0x001 +#define IC_INTR_RX_OVER 0x002 +#define IC_INTR_RX_FULL 0x004 +#define IC_INTR_TX_OVER 0x008 +#define IC_INTR_TX_EMPTY 0x010 +#define IC_INTR_RD_REQ 0x020 +#define IC_INTR_TX_ABRT 0x040 +#define IC_INTR_RX_DONE 0x080 +#define IC_INTR_ACTIVITY 0x100 +#define IC_INTR_STOP_DET 0x200 +#define IC_INTR_START_DET 0x400 +#define IC_INTR_GEN_CALL 0x800 +#define IC_INTR_SMBCLK_EXT_LOW_TIMEOUT 0x1000 +#define IC_INTR_SMBCLK_TMO_LOW_TIMEOUT 0x2000 +#define IC_INTR_SMBSDA_LOW_TIMEOUT 0x4000 +#define IC_INTR_SMBALERT_IN_N 0x20000 + +#define IC_INTR_DEFAULT_MASK \ + (IC_INTR_RX_FULL | IC_INTR_TX_ABRT | IC_INTR_STOP_DET) +#define IC_INTR_MASTER_MASK (IC_INTR_DEFAULT_MASK | IC_INTR_TX_EMPTY) +#define IC_INTR_SLAVE_MASK \ + (IC_INTR_DEFAULT_MASK | IC_INTR_RX_DONE | IC_INTR_RX_UNDER | \ + IC_INTR_RD_REQ) +#define IC_INTR_SMBUS_MASK \ + (IC_INTR_MASTER_MASK | IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | \ + IC_INTR_SMBCLK_TMO_LOW_TIMEOUT | IC_INTR_SMBSDA_LOW_TIMEOUT) + +#define IC_STATUS_ACTIVITY 0x1 +#define IC_STATUS_TFE BIT(2) +#define IC_STATUS_MASTER_ACTIVITY BIT(5) +#define IC_STATUS_SLAVE_ACTIVITY BIT(6) + +#define IC_SDA_HOLD_RX_SHIFT 16 +#define IC_SDA_HOLD_RX_MASK GENMASK(23, IC_SDA_HOLD_RX_SHIFT) + +#define IC_ERR_TX_ABRT 0x1 + +#define IC_TAR_10BITADDR_MASTER BIT(12) + +#define IC_COMP_PARAM_1_SPEED_MODE_HIGH (BIT(2) | BIT(3)) +#define IC_COMP_PARAM_1_SPEED_MODE_MASK GENMASK(3, 2) + +#define STATUS_IDLE 0x0 +#define STATUS_WRITE_IN_PROGRESS 0x1 +#define STATUS_READ_IN_PROGRESS 0x2 /* * operation modes */ -#define PHYTIUM_IC_MASTER 0 -#define PHYTIUM_IC_SLAVE 1 - -#define ABRT_7B_ADDR_NOACK 0 -#define ABRT_10ADDR1_NOACK 1 -#define ABRT_10ADDR2_NOACK 2 -#define ABRT_TXDATA_NOACK 3 -#define ABRT_GCALL_NOACK 4 -#define ABRT_GCALL_READ 5 -#define ABRT_SBYTE_ACKDET 7 -#define ABRT_SBYTE_NORSTRT 9 -#define ABRT_10B_RD_NORSTRT 10 -#define ABRT_MASTER_DIS 11 -#define ARB_LOST 12 -#define ABRT_SLAVE_FLUSH_TXFIFO 13 -#define ABRT_SLAVE_ARBLOST 14 -#define ABRT_SLAVE_RD_INTX 15 - -#define IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK) -#define IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK) -#define IC_TX_ABRT_10ADDR2_NOACK (1UL << ABRT_10ADDR2_NOACK) -#define IC_TX_ABRT_TXDATA_NOACK (1UL << ABRT_TXDATA_NOACK) -#define IC_TX_ABRT_GCALL_NOACK (1UL << ABRT_GCALL_NOACK) -#define IC_TX_ABRT_GCALL_READ (1UL << ABRT_GCALL_READ) -#define IC_TX_ABRT_SBYTE_ACKDET (1UL << ABRT_SBYTE_ACKDET) -#define IC_TX_ABRT_SBYTE_NORSTRT (1UL << ABRT_SBYTE_NORSTRT) -#define IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT) -#define IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS) -#define IC_TX_ARB_LOST (1UL << ARB_LOST) -#define IC_RX_ABRT_SLAVE_RD_INTX (1UL << ABRT_SLAVE_RD_INTX) -#define IC_RX_ABRT_SLAVE_ARBLOST (1UL << ABRT_SLAVE_ARBLOST) -#define IC_RX_ABRT_SLAVE_FLUSH_TXFIFO (1UL << ABRT_SLAVE_FLUSH_TXFIFO) - -#define IC_TX_ABRT_NOACK (IC_TX_ABRT_7B_ADDR_NOACK | \ - IC_TX_ABRT_10ADDR1_NOACK | \ - IC_TX_ABRT_10ADDR2_NOACK | \ - IC_TX_ABRT_TXDATA_NOACK | \ - IC_TX_ABRT_GCALL_NOACK) -#define CONTROLLER_TYPE_IIC 0 -#define CONTROLLER_TYPE_SMBUS 1 +#define PHYTIUM_IC_MASTER 0 +#define PHYTIUM_IC_SLAVE 1 +#if IS_ENABLED(CONFIG_I2C_SLAVE) +#define IPMB_MIX_LEN 6 +enum phytium_i2c_slave_state { + SLAVE_STATE_IDLE, + SLAVE_STATE_RECV, + SLAVE_STATE_SEND, + SLAVE_STATE_REQUEST, + SLAVE_STATE_RESPONSE +}; +#endif +#define ABRT_7B_ADDR_NOACK 0 +#define ABRT_10ADDR1_NOACK 1 +#define ABRT_10ADDR2_NOACK 2 +#define ABRT_TXDATA_NOACK 3 +#define ABRT_GCALL_NOACK 4 +#define ABRT_GCALL_READ 5 +#define ABRT_SBYTE_ACKDET 7 +#define ABRT_SBYTE_NORSTRT 9 +#define ABRT_10B_RD_NORSTRT 10 +#define ABRT_MASTER_DIS 11 +#define ARB_LOST 12 +#define ABRT_SLAVE_FLUSH_TXFIFO 13 +#define ABRT_SLAVE_ARBLOST 14 +#define ABRT_SLAVE_RD_INTX 15 + +#define IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK) +#define IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK) +#define IC_TX_ABRT_10ADDR2_NOACK (1UL << ABRT_10ADDR2_NOACK) +#define IC_TX_ABRT_TXDATA_NOACK (1UL << ABRT_TXDATA_NOACK) +#define IC_TX_ABRT_GCALL_NOACK (1UL << ABRT_GCALL_NOACK) +#define IC_TX_ABRT_GCALL_READ (1UL << ABRT_GCALL_READ) +#define IC_TX_ABRT_SBYTE_ACKDET (1UL << ABRT_SBYTE_ACKDET) +#define IC_TX_ABRT_SBYTE_NORSTRT (1UL << ABRT_SBYTE_NORSTRT) +#define IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT) +#define IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS) +#define IC_TX_ARB_LOST (1UL << ARB_LOST) +#define IC_RX_ABRT_SLAVE_RD_INTX (1UL << ABRT_SLAVE_RD_INTX) +#define IC_RX_ABRT_SLAVE_ARBLOST (1UL << ABRT_SLAVE_ARBLOST) +#define IC_RX_ABRT_SLAVE_FLUSH_TXFIFO (1UL << ABRT_SLAVE_FLUSH_TXFIFO) + +#define IC_TX_ABRT_NOACK \ + (IC_TX_ABRT_7B_ADDR_NOACK | IC_TX_ABRT_10ADDR1_NOACK | \ + IC_TX_ABRT_10ADDR2_NOACK | IC_TX_ABRT_TXDATA_NOACK | \ + IC_TX_ABRT_GCALL_NOACK) +#define CONTROLLER_TYPE_IIC 0 +#define CONTROLLER_TYPE_SMBUS 1 struct phytium_i2c_dev { - struct device *dev; - void __iomem *base; - int irq; - u32 flags; - struct completion cmd_complete; - struct clk *clk; - struct reset_control *rst; - int mode; - struct i2c_client *slave; - u32 (*get_clk_rate_khz)(struct phytium_i2c_dev *dev); - - struct i2c_adapter adapter; - struct i2c_client *ara; + struct device *dev; + void __iomem *base; + int irq; + u32 flags; + struct completion cmd_complete; + struct clk *clk; + struct reset_control *rst; + int mode; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + bool use_ipmb; + enum phytium_i2c_slave_state slave_state; +#endif + spinlock_t i2c_lock; + struct i2c_client *slave; + u32 (*get_clk_rate_khz)(struct phytium_i2c_dev *dev); + + struct i2c_adapter adapter; + struct i2c_client *ara; struct i2c_smbus_alert_setup alert_data; struct phytium_pci_i2c *controller; - unsigned int status; - int cmd_err; - u32 abort_source; - - struct i2c_msg *msgs; - int msgs_num; - int msg_write_idx; - int msg_read_idx; - int msg_err; - u32 tx_buf_len; - u8 *tx_buf; - u32 rx_buf_len; - u8 *rx_buf; - - u32 master_cfg; - u32 slave_cfg; - u32 functionality; - unsigned int tx_fifo_depth; - unsigned int rx_fifo_depth; - int rx_outstanding; - - struct i2c_timings timings; - u32 sda_hold_time; - u16 ss_hcnt; - u16 ss_lcnt; - u16 fs_hcnt; - u16 fs_lcnt; - u16 fp_hcnt; - u16 fp_lcnt; - u16 hs_hcnt; - u16 hs_lcnt; - - bool pm_disabled; - void (*disable)(struct phytium_i2c_dev *dev); - void (*disable_int)(struct phytium_i2c_dev *dev); - int (*init)(struct phytium_i2c_dev *dev); + unsigned int status; + int cmd_err; + u32 abort_source; + + struct i2c_msg *msgs; + int msgs_num; + int msg_write_idx; + int msg_read_idx; + int msg_err; + u32 tx_buf_len; + u8 *tx_buf; + u32 rx_buf_len; + u8 *rx_buf; + + u32 master_cfg; + u32 slave_cfg; + u32 functionality; + unsigned int tx_fifo_depth; + unsigned int rx_fifo_depth; + int rx_outstanding; + + struct i2c_timings timings; + u32 sda_hold_time; + u16 ss_hcnt; + u16 ss_lcnt; + u16 fs_hcnt; + u16 fs_lcnt; + u16 fp_hcnt; + u16 fp_lcnt; + u16 hs_hcnt; + u16 hs_lcnt; + u64 i2c_resource_addr; + bool pm_disabled; + bool first_time_init_master; + void (*disable)(struct phytium_i2c_dev *dev); + void (*disable_int)(struct phytium_i2c_dev *dev); + int (*init)(struct phytium_i2c_dev *dev); }; -#define ACCESS_INTR_MASK 0x00000004 +#define ACCESS_INTR_MASK 0x00000004 + +#define DEFAULT_CLOCK_FREQUENCY 48000000 -#define DEFAULT_CLOCK_FREQUENCY 48000000 +#define I2C_RESET_BASE_ADDR 0x2807e000 +#define I2C_RESET_OFFSET 0x4 +#define REG_LEN 0x100 +#define SHIFT_BIT 16 +int i2c_recover_controller(struct phytium_i2c_dev *dev); +#if IS_ENABLED(CONFIG_I2C_SLAVE) +int do_change_mode(int target_mode, struct phytium_i2c_dev *dev); +#endif u32 phytium_readl(struct phytium_i2c_dev *dev, int offset); void phytium_writel(struct phytium_i2c_dev *dev, u32 b, int offset); unsigned long i2c_phytium_clk_rate(struct phytium_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-phytium-master.c b/drivers/i2c/busses/i2c-phytium-master.c index 8d8a843df4c566e0154638ceaaac4bf1c71e3621..18607c491dc9057c0f46985cf4473b98828786f5 100644 --- a/drivers/i2c/busses/i2c-phytium-master.c +++ b/drivers/i2c/busses/i2c-phytium-master.c @@ -116,6 +116,9 @@ static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) break; } + if (flags & I2C_M_RECV_LEN) + msgs[dev->msg_write_idx].len = I2C_SMBUS_BLOCK_MAX + 1; + if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { /* new i2c_msg */ buf = msgs[dev->msg_write_idx].buf; @@ -132,8 +135,8 @@ static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) { u32 cmd = 0; - if (dev->msg_write_idx == dev->msgs_num - 1 && - buf_len == 1 && !(flags & I2C_M_RECV_LEN)) + if (dev->msg_write_idx == dev->msgs_num - 1 + && buf_len == 1) cmd |= BIT(9); if (need_restart) { cmd |= BIT(10); @@ -163,7 +166,7 @@ static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) * I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop * the transaction here. */ - if (buf_len > 0 || flags & I2C_M_RECV_LEN) { + if (buf_len > 0) { /* more bytes to be written */ dev->status |= STATUS_WRITE_IN_PROGRESS; break; @@ -190,11 +193,14 @@ static u8 i2c_phytium_recv_len(struct phytium_i2c_dev *dev, u8 len) * Adjust the buffer length and mask the flag * after receiving the first byte. */ + if (len > I2C_SMBUS_BLOCK_MAX) + len = 0; len += (flags & I2C_CLIENT_PEC) ? 2 : 1; dev->tx_buf_len = len - min_t(u8, len, dev->rx_outstanding); - msgs[dev->msg_read_idx].len = len; - msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN; - + if (dev->tx_buf_len == 0) { + dev->tx_buf_len = 1; + len = dev->rx_outstanding + 1; + } return len; } @@ -225,9 +231,17 @@ static void i2c_phytium_read(struct phytium_i2c_dev *dev) *buf = phytium_readl(dev, IC_DATA_CMD); /* Ensure length byte is a valid value */ - if (flags & I2C_M_RECV_LEN && - *buf <= I2C_SMBUS_BLOCK_MAX && *buf > 0) { + if (flags & I2C_M_RECV_LEN) { + msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN; len = i2c_phytium_recv_len(dev, *buf); + if (*buf > I2C_SMBUS_BLOCK_MAX || *buf == 0) { + dev_err(dev->dev, "Value %d is out of range[1~32]\n", *buf); + *buf = 0; + dev->msg_err = -EINVAL; + } else { + msgs[dev->msg_read_idx].len = *buf + + ((flags & I2C_CLIENT_PEC) ? 2 : 1); + } } buf++; dev->rx_outstanding--; diff --git a/drivers/i2c/busses/i2c-phytium-mix.c b/drivers/i2c/busses/i2c-phytium-mix.c new file mode 100644 index 0000000000000000000000000000000000000000..50d2c0f3ad76cac18ad7165914f3b483af9d3956 --- /dev/null +++ b/drivers/i2c/busses/i2c-phytium-mix.c @@ -0,0 +1,894 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium I2C adapter driver. + * + * Derived from Synopysys I2C driver. + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd.:w + * + * Copyright (C) 2021-2023, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-phytium-core.h" + +static int i2c_phytium_unreg_slave(struct i2c_client *slave); +static int i2c_phytium_reg_slave(struct i2c_client *slave); + +static void i2c_phytium_configure_fifo_slave(struct phytium_i2c_dev *dev) +{ + /* Configure Tx/Rx FIFO threshold levels. */ + phytium_writel(dev, 0, IC_TX_TL); + phytium_writel(dev, 0, IC_RX_TL); + dev->mode = PHYTIUM_IC_SLAVE; + /* Configure the I2C slave. */ + dev->slave_cfg = IC_CON_RX_FIFO_FULL_HLD_CTRL | IC_CON_RESTART_EN | + IC_CON_STOP_DET_IFADDRESSED; + phytium_writel(dev, dev->slave_cfg, IC_CON); + phytium_writel(dev, IC_INTR_SLAVE_MASK, IC_INTR_MASK); +} + +int i2c_phytium_init_slave(struct phytium_i2c_dev *dev) +{ + /* Disable the adapter. */ + __i2c_phytium_disable(dev); + + /* Write SDA hold time if supported */ + if (dev->sda_hold_time) + phytium_writel(dev, dev->sda_hold_time, IC_SDA_HOLD); + + i2c_phytium_configure_fifo_slave(dev); + + return 0; +} + +static void i2c_phytium_configure_master_ex(struct phytium_i2c_dev *dev) +{ + struct i2c_timings *t = &dev->timings; + + dev->functionality = I2C_FUNC_10BIT_ADDR | IC_DEFAULT_FUNCTIONALITY; + + dev->master_cfg = + IC_CON_MASTER | IC_CON_SLAVE_DISABLE | IC_CON_RESTART_EN; + + dev->mode = PHYTIUM_IC_MASTER; + + switch (t->bus_freq_hz) { + case 100000: + dev->master_cfg |= IC_CON_SPEED_STD; + break; + case 3400000: + dev->master_cfg |= IC_CON_SPEED_HIGH; + break; + default: + dev->master_cfg |= IC_CON_SPEED_FAST; + } +} +int i2c_phytium_init_master(struct phytium_i2c_dev *dev) +{ + /* Disable the adapter */ + __i2c_phytium_disable(dev); + + /* Write standard speed timing parameters */ + phytium_writel(dev, dev->ss_hcnt, IC_SS_SCL_HCNT); + phytium_writel(dev, dev->ss_lcnt, IC_SS_SCL_LCNT); + + /* Write fast mode/fast mode plus timing parameters */ + phytium_writel(dev, dev->fs_hcnt, IC_FS_SCL_HCNT); + phytium_writel(dev, dev->fs_lcnt, IC_FS_SCL_LCNT); + + /* Write high speed timing parameters if supported */ + if (dev->hs_hcnt && dev->hs_hcnt) { + phytium_writel(dev, dev->hs_hcnt, IC_HS_SCL_HCNT); + phytium_writel(dev, dev->hs_lcnt, IC_HS_SCL_LCNT); + } + dev->mode = PHYTIUM_IC_MASTER; + /* Write SDA hold time if supported */ + if (dev->sda_hold_time) + phytium_writel(dev, dev->sda_hold_time, IC_SDA_HOLD); + + /* Configure Tx/Rx FIFO threshold levels */ + phytium_writel(dev, dev->tx_fifo_depth >> 1, IC_TX_TL); + phytium_writel(dev, 0, IC_RX_TL); + + /* Configure the I2C master */ + if (dev->first_time_init_master == false) { + i2c_phytium_configure_master_ex(dev); + dev->first_time_init_master = true; + } + phytium_writel(dev, dev->master_cfg, IC_CON); + + return 0; +} + +static void i2c_phytium_xfer_init(struct phytium_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + u32 ic_con, ic_tar = 0; + + /* Disable the adapter */ + __i2c_phytium_disable(dev); + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->slave_state == SLAVE_STATE_IDLE) + dev->slave_state = SLAVE_STATE_REQUEST; +#endif + + /* If the slave address is 10-bit address, enable 10BITADDR */ + ic_con = phytium_readl(dev, IC_CON); + if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) { + ic_con |= IC_CON_10BITADDR_MASTER; + ic_tar = IC_TAR_10BITADDR_MASTER; + } else { + ic_con &= ~IC_CON_10BITADDR_MASTER; + } + + phytium_writel(dev, ic_con, IC_CON); + + /* + * Set the slave (target) address and enable 10-bit addressing mode + * if applicable. + */ + phytium_writel(dev, msgs[dev->msg_write_idx].addr | ic_tar, IC_TAR); + + /* Enforce disabled interrupts */ + i2c_phytium_disable_int(dev); + + /* Enable the adapter */ + __i2c_phytium_enable(dev); + + /* Dummy read */ + phytium_readl(dev, IC_ENABLE_STATUS); + + /* Clear and enable interrupts */ + phytium_readl(dev, IC_CLR_INTR); + phytium_writel(dev, IC_INTR_SMBUS_MASK, IC_INTR_MASK); +} + +static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + u32 intr_mask; + int tx_limit, rx_limit; + u32 addr = msgs[dev->msg_write_idx].addr; + u32 buf_len = dev->tx_buf_len; + u8 *buf = dev->tx_buf; + bool need_restart = false; + + intr_mask = IC_INTR_MASTER_MASK; + + for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { + u32 flags = msgs[dev->msg_write_idx].flags; + + if (msgs[dev->msg_write_idx].addr != addr) { + dev->msg_err = -EINVAL; + break; + } + + if (flags & I2C_M_RECV_LEN) { + msgs[dev->msg_write_idx].len = 1 + I2C_SMBUS_BLOCK_MAX; + } + + if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { + /* new i2c_msg */ + buf = msgs[dev->msg_write_idx].buf; + buf_len = msgs[dev->msg_write_idx].len; + if ((dev->master_cfg & IC_CON_RESTART_EN) && + (dev->msg_write_idx > 0)) + need_restart = true; + } + + tx_limit = dev->tx_fifo_depth - phytium_readl(dev, IC_TXFLR); + rx_limit = dev->tx_fifo_depth - phytium_readl(dev, IC_RXFLR); + while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) { + u32 cmd = 0; + + if (dev->msg_write_idx == dev->msgs_num - 1 && buf_len == 1) { + cmd |= BIT(9); + } + if (need_restart) { + cmd |= BIT(10); + need_restart = false; + } + + if (msgs[dev->msg_write_idx].flags & I2C_M_RD) { + /* avoid rx buffer overrun */ + if (dev->rx_outstanding >= dev->rx_fifo_depth) + break; + + phytium_writel(dev, cmd | 0x100, IC_DATA_CMD); + rx_limit--; + dev->rx_outstanding++; + } else { + phytium_writel(dev, cmd | *buf++, IC_DATA_CMD); + } + tx_limit--; + buf_len--; + } + + dev->tx_buf = buf; + dev->tx_buf_len = buf_len; + + /* + * Because we don't know the buffer length in the + * I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop + * the transaction here. + */ + if (buf_len > 0) { + /* more bytes to be written */ + dev->status |= STATUS_WRITE_IN_PROGRESS; + break; + } else { + dev->status &= ~STATUS_WRITE_IN_PROGRESS; + } + } + + if (dev->msg_write_idx == dev->msgs_num) + intr_mask &= ~IC_INTR_TX_EMPTY; + + if (dev->msg_err) + intr_mask = 0; + + phytium_writel(dev, intr_mask, IC_INTR_MASK); +} + +static u8 i2c_phytium_recv_len(struct phytium_i2c_dev *dev, u8 len) +{ + struct i2c_msg *msgs = dev->msgs; + u32 flags = msgs[dev->msg_read_idx].flags; + + /* + * Adjust the buffer length and mask the flag + * after receiving the first byte. + */ + if (len > I2C_SMBUS_BLOCK_MAX) { + len = 0; + } + + len += (flags & I2C_CLIENT_PEC) ? 2 : 1; + dev->tx_buf_len = len - min_t(u8, len, dev->rx_outstanding); + + /* require adding one byte to trigger the i2c_phytium_xfer finishing the transaction. */ + if (dev->tx_buf_len == 0) { + dev->tx_buf_len = 1; + len = dev->rx_outstanding + 1; + } + + return len; +} + +static void i2c_phytium_read(struct phytium_i2c_dev *dev) +{ + struct i2c_msg *msgs = dev->msgs; + int rx_valid; + + for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) { + u32 len; + u8 *buf; + + if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD)) + continue; + + if (!(dev->status & STATUS_READ_IN_PROGRESS)) { + len = msgs[dev->msg_read_idx].len; + buf = msgs[dev->msg_read_idx].buf; + } else { + len = dev->rx_buf_len; + buf = dev->rx_buf; + } + + rx_valid = phytium_readl(dev, IC_RXFLR); + + for (; len > 0 && rx_valid > 0; len--, rx_valid--) { + u32 flags = msgs[dev->msg_read_idx].flags; + + *buf = phytium_readl(dev, IC_DATA_CMD); + /* Ensure length byte is a valid value */ + if (flags & I2C_M_RECV_LEN) { + msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN; + len = i2c_phytium_recv_len(dev, *buf); + if (*buf > I2C_SMBUS_BLOCK_MAX || *buf == 0) { + dev_err(dev->dev, "The value %d is out of the SMBus range [1~32]\n", *buf); + *buf = 0; + dev->msg_err = -EINVAL; + } else { + msgs[dev->msg_read_idx].len = *buf + + ((flags & I2C_CLIENT_PEC) ? 2 : 1); + } + } + buf++; + dev->rx_outstanding--; + } + + if (len > 0) { + dev->status |= STATUS_READ_IN_PROGRESS; + dev->rx_buf_len = len; + dev->rx_buf = buf; + return; + } else + dev->status &= ~STATUS_READ_IN_PROGRESS; + } +} + +int i2c_phytium_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], + int num) +{ + struct phytium_i2c_dev *dev = i2c_get_adapdata(adapter); + int ret; + unsigned long flags; + bool change_mode = false; + + dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); + + ret = pm_runtime_get_sync(dev->dev); + if (ret < 0) { + dev_err(dev->dev, "pm runtime get sync err.\n"); + goto pm_exit; + } + + spin_lock_irqsave(&dev->i2c_lock, flags); + reinit_completion(&dev->cmd_complete); + + dev->msgs = msgs; + dev->msgs_num = num; + dev->cmd_err = 0; + dev->msg_write_idx = 0; + dev->msg_read_idx = 0; + dev->msg_err = 0; + dev->status = STATUS_IDLE; + dev->abort_source = 0; + dev->rx_outstanding = 0; + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->slave_state != SLAVE_STATE_RESPONSE && + dev->slave_state != SLAVE_STATE_IDLE) { + ret = -EAGAIN; + goto done; + } +#endif + ret = i2c_phytium_wait_bus_not_busy(dev); + if (ret < 0) + goto done; + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->mode == PHYTIUM_IC_SLAVE) { + if (do_change_mode(PHYTIUM_IC_MASTER, dev) == 0) + change_mode = true; + } +#endif + + /* Start the transfers */ + i2c_phytium_xfer_init(dev); + spin_unlock_irqrestore(&dev->i2c_lock, flags); + /* Wait for tx to complete */ + if (!wait_for_completion_timeout(&dev->cmd_complete, + adapter->timeout)) { + dev_err(dev->dev, "controller timed out\n"); + i2c_recover_controller(dev); + spin_lock_irqsave(&dev->i2c_lock, flags); + ret = -ETIMEDOUT; + goto done; + } + spin_lock_irqsave(&dev->i2c_lock, flags); + __i2c_phytium_disable_nowait(dev); + + if (dev->msg_err) { + ret = dev->msg_err; + goto done; + } + + if (likely(!dev->cmd_err && !dev->status)) { + ret = num; + goto done; + } + + /* We have got an error */ + if (dev->cmd_err == IC_ERR_TX_ABRT) { + spin_unlock_irqrestore(&dev->i2c_lock, flags); + ret = i2c_phytium_handle_tx_abort(dev); + spin_lock_irqsave(&dev->i2c_lock, flags); + goto done; + } + + ret = -EIO; + +done: + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (change_mode == true) { + do_change_mode(PHYTIUM_IC_SLAVE, dev); + } + dev->slave_state = SLAVE_STATE_IDLE; +#endif + spin_unlock_irqrestore(&dev->i2c_lock, flags); + +pm_exit: + + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + + return ret; +} + +#if IS_ENABLED(CONFIG_I2C_SLAVE) +static int i2c_phytium_reg_slave(struct i2c_client *slave) +{ + unsigned long flags; + struct phytium_i2c_dev *dev = i2c_get_adapdata(slave->adapter); + spin_lock_irqsave(&dev->i2c_lock, flags); + + if (dev->slave) { + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return -EBUSY; + } + if (slave->flags & I2C_CLIENT_TEN) { + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return -EAFNOSUPPORT; + } + pm_runtime_get_sync(dev->dev); + + /* + * Set slave address in the IC_SAR register, + * the address to which the i2c responds. + */ + __i2c_phytium_disable_nowait(dev); + i2c_phytium_init_slave(dev); + phytium_writel(dev, slave->addr, IC_SAR); + dev->slave = slave; + dev->mode = PHYTIUM_IC_SLAVE; + __i2c_phytium_enable(dev); + + dev->cmd_err = 0; + dev->msg_write_idx = 0; + dev->msg_read_idx = 0; + dev->msg_err = 0; + dev->status = STATUS_IDLE; + dev->abort_source = 0; + dev->rx_outstanding = 0; + dev->use_ipmb = true; + + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return 0; +} + +static int i2c_phytium_unreg_slave(struct i2c_client *slave) +{ + unsigned long flags; + struct phytium_i2c_dev *dev = i2c_get_adapdata(slave->adapter); + spin_lock_irqsave(&dev->i2c_lock, flags); + dev->disable_int(dev); + dev->disable(dev); + dev->slave = NULL; + pm_runtime_put(dev->dev); + + dev->mode = PHYTIUM_IC_MASTER; + i2c_phytium_init_master(dev); + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return 0; +} +#endif + +static const struct i2c_algorithm i2c_phytium_algo = { + .master_xfer = i2c_phytium_xfer, + .functionality = i2c_phytium_func, +#if IS_ENABLED(CONFIG_I2C_SLAVE) + .reg_slave = i2c_phytium_reg_slave, + .unreg_slave = i2c_phytium_unreg_slave, +#endif +}; + +static const struct i2c_adapter_quirks i2c_phytium_quirks = { + .flags = I2C_AQ_NO_ZERO_LEN, +}; + +static u32 i2c_phytium_read_clear_intrbits(struct phytium_i2c_dev *dev) +{ + u32 stat; + + stat = phytium_readl(dev, IC_INTR_STAT); + if (stat & IC_INTR_RX_UNDER) + phytium_readl(dev, IC_CLR_RX_UNDER); + if (stat & IC_INTR_RX_OVER) + phytium_readl(dev, IC_CLR_RX_OVER); + if (stat & IC_INTR_TX_OVER) + phytium_readl(dev, IC_CLR_TX_OVER); + if (stat & IC_INTR_RD_REQ) + phytium_readl(dev, IC_CLR_RD_REQ); + if (stat & IC_INTR_TX_ABRT) { + dev->abort_source = phytium_readl(dev, IC_TX_ABRT_SOURCE); + phytium_readl(dev, IC_CLR_TX_ABRT); + } + if (stat & IC_INTR_RX_DONE) + phytium_readl(dev, IC_CLR_RX_DONE); + if (stat & IC_INTR_ACTIVITY) + phytium_readl(dev, IC_CLR_ACTIVITY); + if (stat & IC_INTR_STOP_DET) + phytium_readl(dev, IC_CLR_STOP_DET); + if (stat & IC_INTR_START_DET) + phytium_readl(dev, IC_CLR_START_DET); + if (stat & IC_INTR_GEN_CALL) + phytium_readl(dev, IC_CLR_GEN_CALL); + if (stat & IC_INTR_SMBCLK_EXT_LOW_TIMEOUT) + phytium_readl(dev, IC_CLR_SMBCLK_EXT_LOW_TIMEOUT); + if (stat & IC_INTR_SMBCLK_TMO_LOW_TIMEOUT) + phytium_readl(dev, IC_CLR_SMBCLK_TMO_LOW_TIMEOUT); + if (stat & IC_INTR_SMBSDA_LOW_TIMEOUT) + phytium_readl(dev, IC_CLR_SMBDAT_LOW_TIMEOUT); + if (stat & IC_INTR_SMBALERT_IN_N) + phytium_readl(dev, IC_CLR_SMBALERT_IN_N); + + return stat; +} + +static u32 i2c_phytium_read_clear_intrbits_slave(struct phytium_i2c_dev *dev) +{ + u32 stat; + + /* + * The IC_INTR_STAT register just indicates "enabled" interrupts. + * Ths unmasked raw version of interrupt status bits are available + * in the IC_RAW_INTR_STAT register. + * + * That is, + * stat = phytium_readl(IC_INTR_STAT); + * equals to, + * stat = phytium_readl(IC_RAW_INTR_STAT) & phytium_readl(IC_INTR_MASK); + * + * The raw version might be useful for debugging purposes. + */ + stat = phytium_readl(dev, IC_INTR_STAT); + /* + * Do not use the IC_CLR_INTR register to clear interrupts, or + * you'll miss some interrupts, triggered during the period from + * phytium_readl(IC_INTR_STAT) to phytium_readl(IC_CLR_INTR). + * + * Instead, use the separately-prepared IC_CLR_* registers. + */ + if (stat & IC_INTR_TX_ABRT) + phytium_readl(dev, IC_CLR_TX_ABRT); + if (stat & IC_INTR_RX_UNDER) + phytium_readl(dev, IC_CLR_RX_UNDER); + if (stat & IC_INTR_RX_OVER) + phytium_readl(dev, IC_CLR_RX_OVER); + if (stat & IC_INTR_TX_OVER) + phytium_readl(dev, IC_CLR_TX_OVER); + if (stat & IC_INTR_RX_DONE) + phytium_readl(dev, IC_CLR_RX_DONE); + if (stat & IC_INTR_ACTIVITY) + phytium_readl(dev, IC_CLR_ACTIVITY); + if (stat & IC_INTR_STOP_DET) + phytium_readl(dev, IC_CLR_STOP_DET); + if (stat & IC_INTR_START_DET) + phytium_readl(dev, IC_CLR_START_DET); + if (stat & IC_INTR_GEN_CALL) + phytium_readl(dev, IC_CLR_GEN_CALL); + + return stat; +} + +static int i2c_phytium_irq_handler_master(struct phytium_i2c_dev *dev) +{ + u32 stat; + + stat = i2c_phytium_read_clear_intrbits(dev); + + /* SMBus interrupt */ + if (stat & + (IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | IC_INTR_SMBCLK_TMO_LOW_TIMEOUT)) { + phytium_writel(dev, phytium_readl(dev, IC_ENABLE) & (~BIT(6)), + IC_ENABLE); + phytium_writel(dev, phytium_readl(dev, IC_ENABLE) | BIT(4), + IC_ENABLE); + goto abort; + } + + if (stat & IC_INTR_SMBSDA_LOW_TIMEOUT) { + phytium_writel(dev, phytium_readl(dev, IC_ENABLE) | BIT(6), + IC_ENABLE); + goto abort; + } + + if (stat & IC_INTR_SMBALERT_IN_N && dev->ara) + i2c_handle_smbus_alert(dev->ara); + + if (stat & IC_INTR_TX_ABRT) { + dev->cmd_err |= IC_ERR_TX_ABRT; + dev->status = STATUS_IDLE; + + /* + * Anytime TX_ABRT is set, the contents of the tx/rx + * buffers are flushed. Make sure to skip them. + */ + phytium_writel(dev, 0, IC_INTR_MASK); + goto abort; + } + + if (stat & IC_INTR_RX_FULL) + i2c_phytium_read(dev); + + if (stat & IC_INTR_TX_EMPTY) + i2c_phytium_xfer_msg(dev); + +abort: + if ((stat & IC_INTR_TX_ABRT) || dev->msg_err){ + complete(&dev->cmd_complete); + } + else if (stat & IC_INTR_STOP_DET){ + if (dev->use_ipmb && dev->msgs->len >= IPMB_MIX_LEN && + dev->slave_state == SLAVE_STATE_REQUEST) + do_change_mode(PHYTIUM_IC_SLAVE, dev); + else + complete(&dev->cmd_complete); + } + else if (unlikely(dev->flags & ACCESS_INTR_MASK)) { + /* Workaround to trigger pending interrupt */ + stat = phytium_readl(dev, IC_INTR_MASK); + i2c_phytium_disable_int(dev); + phytium_writel(dev, stat, IC_INTR_MASK); + } + + return 0; +} + +static int i2c_phytium_irq_handler_slave(struct phytium_i2c_dev *dev) +{ + u32 raw_stat, stat, enabled; + u8 val, slave_activity; + + enabled = phytium_readl(dev, IC_ENABLE); + raw_stat = phytium_readl(dev, IC_RAW_INTR_STAT); + slave_activity = + ((phytium_readl(dev, IC_STATUS) & IC_STATUS_SLAVE_ACTIVITY) >> + 6); + + if (!enabled || !(raw_stat & ~IC_INTR_ACTIVITY) || !dev->slave) { + return 0; + } + stat = i2c_phytium_read_clear_intrbits_slave(dev); + + if (stat & IC_INTR_RX_FULL) { + if (dev->status != STATUS_WRITE_IN_PROGRESS) { + dev->status = STATUS_WRITE_IN_PROGRESS; + i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, + &val); + dev->slave_state = SLAVE_STATE_RECV; + } + do { + val = phytium_readl(dev, IC_DATA_CMD); + i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, + &val); + val = phytium_readl(dev, IC_STATUS); + } while (val & BIT(3)); + } + + if (stat & IC_INTR_RD_REQ) { + if (slave_activity) { + phytium_readl(dev, IC_CLR_RD_REQ); + + if (!(dev->status & STATUS_READ_IN_PROGRESS)) { + i2c_slave_event(dev->slave, + I2C_SLAVE_READ_REQUESTED, &val); + dev->status |= STATUS_READ_IN_PROGRESS; + dev->status &= ~STATUS_WRITE_IN_PROGRESS; + dev->slave_state = SLAVE_STATE_SEND; + + } else { + i2c_slave_event(dev->slave, + I2C_SLAVE_READ_PROCESSED, &val); + } + phytium_writel(dev, val, IC_DATA_CMD); + } + } + + if (stat & IC_INTR_STOP_DET) { + i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->slave_state == SLAVE_STATE_RECV || + dev->slave_state == SLAVE_STATE_SEND) { + dev->status = STATUS_IDLE; + dev->slave_state = SLAVE_STATE_RESPONSE; + complete(&dev->cmd_complete); + } +#endif + } + + return 1; +} + +static irqreturn_t i2c_phytium_isr(int this_irq, void *dev_id) +{ + struct phytium_i2c_dev *dev = dev_id; + u32 stat, enabled; + spin_lock(&dev->i2c_lock); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->mode == PHYTIUM_IC_SLAVE) { + i2c_phytium_irq_handler_slave(dev); + spin_unlock(&dev->i2c_lock); + return IRQ_HANDLED; + } +#endif + enabled = phytium_readl(dev, IC_ENABLE); + stat = phytium_readl(dev, IC_RAW_INTR_STAT); + if (!enabled || !(stat & ~IC_INTR_ACTIVITY)) { + spin_unlock(&dev->i2c_lock); + return IRQ_NONE; + } + + i2c_phytium_irq_handler_master(dev); + spin_unlock(&dev->i2c_lock); + return IRQ_HANDLED; +} + +static int i2c_phytium_set_timings_master(struct phytium_i2c_dev *dev) +{ + const char *mode_str, *fp_str = ""; + u32 sda_falling_time, scl_falling_time; + struct i2c_timings *t = &dev->timings; + u32 ic_clk; + int ret; + + /* Set standard and fast speed dividers for high/low periods */ + sda_falling_time = t->sda_fall_ns ?: 300; /* ns */ + scl_falling_time = t->scl_fall_ns ?: 300; /* ns */ + + /* Calculate SCL timing parameters for standard mode if not set */ + if (!dev->ss_hcnt || !dev->ss_lcnt) { + ic_clk = i2c_phytium_clk_rate(dev); + dev->ss_hcnt = i2c_phytium_scl_hcnt( + ic_clk, 4000, /* tHD;STA = tHIGH = 4.0 us */ + sda_falling_time, 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + dev->ss_lcnt = + i2c_phytium_scl_lcnt(ic_clk, 4700, /* tLOW = 4.7 us */ + scl_falling_time, + 0); /* No offset */ + } + dev_dbg(dev->dev, "Standard Mode HCNT:LCNT = %d:%d\n", dev->ss_hcnt, + dev->ss_lcnt); + /* + * Set SCL timing parameters for fast mode or fast mode plus. Only + * difference is the timing parameter values since the registers + * are the same. + */ + if (t->bus_freq_hz == 1000000) { + /* + * Check are fast mode plus parameters available and use + * fast mode if not. + */ + if (dev->fp_hcnt && dev->fp_lcnt) { + dev->fs_hcnt = dev->fp_hcnt; + dev->fs_lcnt = dev->fp_lcnt; + fp_str = " Plus"; + } + } + /* + * Calculate SCL timing parameters for fast mode if not set. They are + * needed also in high speed mode. + */ + if (!dev->fs_hcnt || !dev->fs_lcnt) { + ic_clk = i2c_phytium_clk_rate(dev); + dev->fs_hcnt = i2c_phytium_scl_hcnt( + ic_clk, 600, /* tHD;STA = tHIGH = 0.6 us */ + sda_falling_time, 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + dev->fs_lcnt = + i2c_phytium_scl_lcnt(ic_clk, 1300, /* tLOW = 1.3 us */ + scl_falling_time, + 0); /* No offset */ + } + dev_dbg(dev->dev, "Fast Mode%s HCNT:LCNT = %d:%d\n", fp_str, + dev->fs_hcnt, dev->fs_lcnt); + + if (dev->hs_hcnt && dev->hs_lcnt) + dev_dbg(dev->dev, "High Speed Mode HCNT:LCNT = %d:%d\n", + dev->hs_hcnt, dev->hs_lcnt); + + ret = i2c_phytium_set_sda_hold(dev); + if (ret) + goto out; + + switch (dev->master_cfg & IC_CON_SPEED_MASK) { + case IC_CON_SPEED_STD: + mode_str = "Standard Mode"; + break; + case IC_CON_SPEED_HIGH: + mode_str = "High Speed Mode"; + break; + default: + mode_str = "Fast Mode"; + } + dev_dbg(dev->dev, "Bus speed: %s%s\n", mode_str, fp_str); + +out: + return ret; +} + +int i2c_phytium_probe(struct phytium_i2c_dev *dev) +{ + struct i2c_adapter *adapter = &dev->adapter; + unsigned long irq_flags; + int ret; + const char *mode_str; + + init_completion(&dev->cmd_complete); + if (dev->mode == PHYTIUM_IC_MASTER) + dev->init = i2c_phytium_init_master; + else if (dev->mode == PHYTIUM_IC_SLAVE) + dev->init = i2c_phytium_init_slave; + + dev->disable = i2c_phytium_disable; + dev->disable_int = i2c_phytium_disable_int; + + ret = i2c_phytium_set_timings_master(dev); + if (ret) + return ret; + + ret = dev->init(dev); + if (ret) + return ret; + if (dev->mode == PHYTIUM_IC_MASTER) { + switch (dev->master_cfg & IC_CON_SPEED_MASK) { + case IC_CON_SPEED_STD: + mode_str = "Standard Mode"; + break; + case IC_CON_SPEED_HIGH: + mode_str = "High Speed Mode"; + break; + default: + mode_str = "Fast Mode"; + } + dev_err(dev->dev, "Bus speed: %s\n", mode_str); +/* XXX: should be initialized in firmware, remove it in future */ +#define DEFAULT_TIMEOUT (DEFAULT_CLOCK_FREQUENCY / 1000 * 35) + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBCLK_LOW_MEXT); + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBCLK_LOW_TIMEOUT); + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBDAT_STUCK_TIMEOUT); + } + + snprintf(adapter->name, sizeof(adapter->name), "Phytium I2C adapter"); + adapter->retries = 3; + adapter->algo = &i2c_phytium_algo; + adapter->quirks = &i2c_phytium_quirks; + adapter->dev.parent = dev->dev; + i2c_set_adapdata(adapter, dev); + + irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND; + + i2c_phytium_disable_int(dev); + ret = devm_request_irq(dev->dev, dev->irq, i2c_phytium_isr, irq_flags, + dev_name(dev->dev), dev); + if (ret) { + dev_err(dev->dev, "failed to request irq %i: %d\n", dev->irq, + ret); + return ret; + } + + /* + * Increment PM usage count during adapter registration in order to + * avoid possible spurious runtime suspend when adapter device is + * registered to the device core and immediate resume in case bus has + * registered I2C slaves that do I2C transfers in their probe. + */ + pm_runtime_get_noresume(dev->dev); + ret = i2c_add_numbered_adapter(adapter); + if (ret) + dev_err(dev->dev, "fail to add adapter: %d\n", ret); + pm_runtime_put_noidle(dev->dev); + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_phytium_probe); + +MODULE_DESCRIPTION("Phytium I2C bus controller adapter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/phytium-adc.c b/drivers/iio/adc/phytium-adc.c index 093a47fd3d03005deca418ef9adff5f4fd45b0e4..fbadb523978d06c7f8d56ae7ad15683a58450308 100644 --- a/drivers/iio/adc/phytium-adc.c +++ b/drivers/iio/adc/phytium-adc.c @@ -579,7 +579,6 @@ static int phytium_adc_probe(struct platform_device *pdev) indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); if (!indio_dev) return -ENOMEM; - platform_set_drvdata(pdev, indio_dev); adc = iio_priv(indio_dev); adc->dev = dev; @@ -609,6 +608,8 @@ static int phytium_adc_probe(struct platform_device *pdev) indio_dev->channels = adc->data->channels; indio_dev->num_channels = adc->data->num_channels; + platform_set_drvdata(pdev, indio_dev); + ret = devm_request_threaded_irq(adc->dev, platform_get_irq(pdev, 0), NULL, phytium_adc_threaded_irq, IRQF_ONESHOT, dev_name(dev), adc); @@ -628,7 +629,7 @@ static int phytium_adc_probe(struct platform_device *pdev) return ret; } - return devm_iio_device_register(dev, indio_dev); + return iio_device_register(indio_dev); } static int phytium_adc_remove(struct platform_device *pdev) diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..9b3fcd1fc7897286cb994e23a49b8366c087c5e1 --- /dev/null +++ b/drivers/jtag/Kconfig @@ -0,0 +1,24 @@ +menuconfig JTAG + tristate "JTAG support" + default Y + help + This provides basic core functionality support for JTAG class devices. + Hardware that is equipped with a JTAG microcontroller can be supported + by using this driver's interfaces. This driver exposes a set of IOCTLs + to the user space for the following commands: + + DR: Performs an IEEE 1149.1 Data Register scan. + IR: Performs an IEEE 1149.1 Instruction Register scan. + +menuconfig JTAG_PHYTIUM + tristate "Phytium SoC JTAG Master controller support" + depends on JTAG + help + This provides a support for Phytium JTAG device, equipped on + Phytium SoC PE2201. Drivers allows programming of hardware devices, + connected to SoC through the JTAG interface. + + If you want this support, you should say Y here. + + To compile this driver as a module, choose M here: the module will + be called jtag-phytium. diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..77e7cbe6dbcf26ae459f95c66d47a3829f26b585 --- /dev/null +++ b/drivers/jtag/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_JTAG) += jtag.o +obj-$(CONFIG_JTAG_PHYTIUM) += jtag-phytium.o diff --git a/drivers/jtag/jtag-phytium.c b/drivers/jtag/jtag-phytium.c new file mode 100644 index 0000000000000000000000000000000000000000..529e11043641d29818f59c76d2214e837161a6f0 --- /dev/null +++ b/drivers/jtag/jtag-phytium.c @@ -0,0 +1,603 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * JTAG driver for the Phytium SoC + * + * Copyright (C) 2021 Phytium Technology Inc. + * Xu Wenkang + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_JTGM_CTL 0x000 +#define TMS_RST_SET BIT(1) +#define NTRST_SET BIT(0) + +#define REG_TCK_CTL 0x004 +#define REG_FSM_CTL 0x008 +#define REG_INT_CFG 0x00c +#define REG_DR_GEN_CTL 0x010 +#define REG_INT_CTL 0x014 + +#define INFIFO_CNT GENMASK(12, 8) + +#define REG_DR_CONFIG 0x018 + +#define SB_SET BIT(12) +#define DR_VALID BIT(8) +#define DR_RSP BIT(7) + +enum jtag_dr_state { + DR_NORSP_WITHEXIT = 0x2, + DR_NORSP_WITHOUTEXIT = 0x3, + SJTAG_DR_READ_REG_WITHEXIT = 0x4, + DR_RSP_WITHEXIT = 0x6, + DR_RSP_WITHOUTEXIT = 0x7, +}; + +#define REG_DR_OUT 0x01c +#define REG_DR_IN 0x020 +#define REG_IR_CONFIG 0x024 + +enum jtag_ir_state { + IR_WITHEXIT = 0x0, + IR_WITHOUTEXIT = 0x1, +}; + +#define REG_IR_OUT 0x028 +#define REG_IR_16_CONFIG 0x02c + +#define TCK_FREQ 5000000 +#define PHYTIUM_JTAG_TIMEOUT msecs_to_jiffies(1000) +#define JTAG_TCK_MIN_DIVISOR_MASK 2 +#define JTAG_TCK_MAX_DIVISOR_MASK 0xffff +#define JTAG_GET_TCK_DIVISOR(x) (x & 0xffff) + +#undef PHYTIUM_JTAG_DEBUG 1 + +#ifdef PHYTIUM_JTAG_DEBUG +#define JTAG_DBUG(fmt, args...) pr_info("%s() " fmt, __func__, ##args) +#else +#define JTAG_DBUG(fmt, args...) +#endif + +struct phytium_jtag_config { + u32 jtag_buff_len; +}; + +struct phytium_jtag_info { + void __iomem *reg_base; + struct device *dev; + struct phytium_jtag_config *config; + int irq; + + struct clk *clk; + u32 clkin; + + struct completion completion; + struct completion outfifo_empty_completion; + struct completion count_completion; + + int count_out; + volatile int count_in; + int expect_num; + + u32 infifo[15]; + u32 infifo_full[16]; +}; + +static inline u32 +phytium_jtag_read(struct phytium_jtag_info *phytium_jtag, u32 reg) +{ + int val; + + val = readl(phytium_jtag->reg_base + reg); + JTAG_DBUG("%x, val: %x\n", reg, val); + return val; +} + +static inline void +phytium_jtag_write(struct phytium_jtag_info *phytium_jtag, u32 val, u32 reg) +{ + JTAG_DBUG("%x, val: %x\n", reg, val); + writel(val, phytium_jtag->reg_base + reg); +} + +static void phytium_jtag_disable_irq(struct phytium_jtag_info *phytium_jtag) +{ + u32 reg; + + reg = phytium_jtag_read(phytium_jtag, REG_INT_CTL); + reg |= 0x200ca; + phytium_jtag_write(phytium_jtag, reg, REG_INT_CTL); +} + +static void phytium_jtag_enable_irq(struct phytium_jtag_info *phytium_jtag) +{ + u32 reg; + + reg = phytium_jtag_read(phytium_jtag, REG_INT_CTL); + reg &= ~0xca; + phytium_jtag_write(phytium_jtag, reg, REG_INT_CTL); +} + +static int phytium_jtag_set_freq(struct jtag *jtag, u32 freq) +{ + struct phytium_jtag_info *phytium_jtag = jtag_priv(jtag); + u32 div; + + div = DIV_ROUND_UP(phytium_jtag->clkin, freq); + + if (div < JTAG_TCK_MIN_DIVISOR_MASK) { + pr_warn("The actual frequency will slower than required\n"); + div = JTAG_TCK_MIN_DIVISOR_MASK; + } + + if (div > JTAG_TCK_MAX_DIVISOR_MASK) { + pr_warn("The actual frequency will faster than required\n"); + div = JTAG_TCK_MAX_DIVISOR_MASK; + } + + phytium_jtag_write(phytium_jtag, div, REG_TCK_CTL); + JTAG_DBUG("Operation freq = %d / %d\n", phytium_jtag->clkin, div); + return 0; +} + +static int phytium_jtag_get_freq(struct jtag *jtag, u32 *freq) +{ + struct phytium_jtag_info *phytium_jtag = jtag_priv(jtag); + + *freq = phytium_jtag->clkin / + (JTAG_GET_TCK_DIVISOR(phytium_jtag_read(phytium_jtag, REG_TCK_CTL))); + + return 0; +} + +static int phytium_jtag_status_set(struct jtag *jtag, + struct jtag_end_tap_state *endstate) +{ + return 0; +} + +static int phytium_jtag_status_get(struct jtag *jtag, u32 *status) +{ + struct phytium_jtag_info *phytium_jtag = jtag_priv(jtag); + + *status = phytium_jtag_read(phytium_jtag, REG_FSM_CTL) && 0x70000; + return 0; +} + +static void phytium_hw_ir_16_scan_withexit(struct phytium_jtag_info *phytium_jtag, u32 shift_bits, u32 ir) +{ + u32 ir_16_config; + + ir_16_config = (0x1 << 24) + (IR_WITHEXIT << 21) + (0x1 << 20) + ((shift_bits - 0x1) << 16) + ir; + phytium_jtag_write(phytium_jtag, ir_16_config, REG_IR_16_CONFIG); +} + +static void phytium_hw_ir_scan_withoutexit(struct phytium_jtag_info *phytium_jtag, u32 shift_bits) +{ + u32 ir_config; + u32 reg; + + reg = phytium_jtag_read(phytium_jtag, REG_IR_16_CONFIG); + phytium_jtag_write(phytium_jtag, reg | 0x0 << 24, REG_IR_16_CONFIG); + + ir_config = (0x1 << 8) + (IR_WITHOUTEXIT << 5) + (shift_bits - 0x1); + phytium_jtag_write(phytium_jtag, ir_config, REG_IR_CONFIG); +} + +static void phytium_hw_ir_scan_withexit(struct phytium_jtag_info *phytium_jtag, u32 shift_bits) +{ + u32 ir_config; + u32 reg; + + reg = phytium_jtag_read(phytium_jtag, REG_IR_16_CONFIG); + phytium_jtag_write(phytium_jtag, reg | 0x0 << 24, REG_IR_16_CONFIG); + + ir_config = (0x1 << 8) + (IR_WITHEXIT << 5) + (shift_bits - 0x1); + phytium_jtag_write(phytium_jtag, ir_config, REG_IR_CONFIG); +} + +static void phytium_hw_dr_scan_withexit(struct phytium_jtag_info *phytium_jtag, u32 shift_bits) +{ + u32 dr_config; + + dr_config = (0x0 << 12) + (0x1 << 8) + (DR_RSP_WITHEXIT << 5) + (shift_bits - 0x1); + phytium_jtag_write(phytium_jtag, dr_config, REG_DR_CONFIG); +} + +static void phytium_hw_dr_scan_withoutexit(struct phytium_jtag_info *phytium_jtag, u32 shift_bits) +{ + u32 dr_config; + + dr_config = (0x0 << 12) + (0x1 << 8) + (DR_RSP_WITHOUTEXIT << 5) + (shift_bits - 0x1); + phytium_jtag_write(phytium_jtag, dr_config, REG_DR_CONFIG); +} + +static int phytium_hw_jtag_xfer(struct phytium_jtag_info *phytium_jtag, + struct jtag_xfer *xfer, u8 *xfer_data) +{ + unsigned int index = 0; + u32 shift_bits = 0; + u32 remain_xfer = xfer->length; + int i, tmp_idx = 0; + u32 fifo_reg = xfer->type ? REG_DR_OUT : REG_IR_OUT; + u32 *tdo; + u32 bytes; + u32 *xfer_data_32; + u32 outfifo_status; + int num; + + if (remain_xfer == 0) + return 0; + + bytes = DIV_ROUND_UP(xfer->length, BITS_PER_BYTE); + + tdo = kzalloc(bytes, GFP_KERNEL); + if (!tdo) + return -ENOMEM; + + xfer_data_32 = kzalloc(bytes, GFP_KERNEL); + if (!xfer_data_32) + return -ENOMEM; + + memcpy(xfer_data_32, xfer_data, bytes); + + while (remain_xfer) { + phytium_jtag->count_in = 0; + phytium_jtag_disable_irq(phytium_jtag); + if (remain_xfer > phytium_jtag->config->jtag_buff_len ) { + shift_bits = phytium_jtag->config->jtag_buff_len; + tmp_idx = shift_bits / 32; + for (i = 0; i < tmp_idx - 1; i++) { + phytium_jtag_write(phytium_jtag, xfer_data_32[index + i], fifo_reg); + if (xfer->type == JTAG_SIR_XFER) + phytium_hw_ir_scan_withoutexit(phytium_jtag, 32); + else + phytium_hw_dr_scan_withoutexit(phytium_jtag, 32); + } + phytium_jtag_write(phytium_jtag, xfer_data_32[index + tmp_idx - 1], fifo_reg); + if (xfer->type == JTAG_SIR_XFER) + phytium_hw_ir_scan_withexit(phytium_jtag, 32); + else + phytium_hw_dr_scan_withexit(phytium_jtag, 32); + } else { + shift_bits = remain_xfer; + if ((shift_bits > 32) && (shift_bits % 32 != 0)) { + tmp_idx = shift_bits / 32; + for (i = 0; i < tmp_idx; i++) { + phytium_jtag_write(phytium_jtag, xfer_data_32[index + i], fifo_reg); + if (xfer->type == JTAG_SIR_XFER) + phytium_hw_ir_scan_withoutexit(phytium_jtag, 32); + else + phytium_hw_dr_scan_withoutexit(phytium_jtag, 32); + } + if (xfer->type == JTAG_SIR_XFER) { + if (shift_bits <= 16) { + phytium_hw_ir_16_scan_withexit(phytium_jtag, shift_bits, xfer_data_32[index+i]); + } else { + phytium_jtag_write(phytium_jtag, xfer_data_32[index+i], fifo_reg); + phytium_hw_ir_scan_withexit(phytium_jtag, shift_bits % 32); + } + } else { + phytium_jtag_write(phytium_jtag, ((unsigned int *)xfer_data_32)[index+i], fifo_reg); + phytium_hw_dr_scan_withexit(phytium_jtag, shift_bits % 32); + } + } + + if (0 < shift_bits && shift_bits <= 32) { + if (xfer->type == JTAG_SIR_XFER) { + if (shift_bits <= 16) { + phytium_hw_ir_16_scan_withexit(phytium_jtag, shift_bits, xfer_data_32[index]); + } else { + phytium_jtag_write(phytium_jtag, xfer_data_32[index], fifo_reg); + phytium_hw_ir_scan_withexit(phytium_jtag, shift_bits); + } + } else { + phytium_jtag_write(phytium_jtag, xfer_data_32[index], fifo_reg); + phytium_hw_dr_scan_withexit(phytium_jtag, shift_bits); + } + } + + if ((shift_bits > 32) && (shift_bits % 32 == 0)) { + tmp_idx = shift_bits / 32; + for (i = 0; i < tmp_idx - 1; i++) { + phytium_jtag_write(phytium_jtag, xfer_data_32[index + i], fifo_reg); + if (xfer->type == JTAG_SDR_XFER) + phytium_hw_dr_scan_withoutexit(phytium_jtag, 32); + else + phytium_hw_ir_scan_withoutexit(phytium_jtag, 32); + } + phytium_jtag_write(phytium_jtag, xfer_data_32[index + i], fifo_reg); + if (xfer->type == JTAG_SDR_XFER) + phytium_hw_dr_scan_withexit(phytium_jtag, 32); + else + phytium_hw_ir_scan_withexit(phytium_jtag, 32); + } + } + + remain_xfer = remain_xfer - shift_bits; + + phytium_jtag_enable_irq(phytium_jtag); + + if (xfer->direction & JTAG_READ_XFER) { + tmp_idx = shift_bits / 32; + if (shift_bits % 32) + tmp_idx += 1; + phytium_jtag->expect_num = tmp_idx; + + if (phytium_jtag->count_in < tmp_idx) + wait_for_completion_interruptible_timeout(&phytium_jtag->completion, PHYTIUM_JTAG_TIMEOUT); + if (phytium_jtag->count_in == tmp_idx) + JTAG_DBUG("done %d : %d\n", phytium_jtag->count_in, tmp_idx); + + for (i = 0; i < tmp_idx; i++) { + if (shift_bits < 32) + tdo[index + i] = phytium_jtag->infifo[i]; + else + tdo[index + i] = phytium_jtag->infifo[i]; + shift_bits -= 32; + } + } + phytium_jtag_disable_irq(phytium_jtag); + index += tmp_idx; + + if (bytes % 4) + bytes = (bytes / 4 + 1) * 4; + memcpy(xfer_data_32, tdo, bytes); + memcpy(xfer_data, xfer_data_32, bytes); + } + + kfree(tdo); + kfree(xfer_data_32); + + return 0; +} + +static int phytium_jtag_xfer(struct jtag *jtag, struct jtag_xfer *xfer, + u8 *xfer_data) +{ + int ret; + struct phytium_jtag_info *phytium_jtag = jtag_priv(jtag); + + ret = phytium_hw_jtag_xfer(phytium_jtag, xfer, xfer_data); + return ret; +} + +static irqreturn_t phytium_jtag_isr(int irq, void *dev_id) +{ + u32 status; + struct phytium_jtag_info *phytium_jtag = dev_id; + int i = 0; + int infifo_num, outfifo_num; + + status = phytium_jtag_read(phytium_jtag, REG_INT_CTL); + + infifo_num = (status & 0x1f00) >> 8; + outfifo_num = (status & 0x3f00000) >> 20; + + //JTAG_DBUG("irq:%x in:%d out:%d", status, infifo_num, outfifo_num); + /* The infifo is full and reach 16 */ + if (infifo_num == 16) { + for (i = 0; i < 16; i++) + phytium_jtag->infifo_full[i] = phytium_jtag_read(phytium_jtag, REG_DR_IN); + phytium_jtag->count_in += 16; + return IRQ_HANDLED; + } + + /* The infifo reached threshold BUF_SIZE:15 */ + if (status & 0x20) { + for (i = 0; i < 15; i++) + phytium_jtag->infifo[i] = phytium_jtag_read(phytium_jtag, REG_DR_IN); + phytium_jtag->count_in += 15; + complete(&phytium_jtag->completion); + return IRQ_HANDLED; + } + + /* The infifo timeout interrupt */ + if (status & 0x10) { + for(i = 0; i < infifo_num; i++) + phytium_jtag->infifo[i + phytium_jtag->count_in] = phytium_jtag_read(phytium_jtag, REG_DR_IN); + + phytium_jtag->count_in += infifo_num; + + if (phytium_jtag->count_in == phytium_jtag->expect_num) { + complete(&phytium_jtag->completion); + //printk("irq timeout comp\n"); + } + return IRQ_HANDLED; + } + + if (((status & 0x10000) == 0x10000) && ((status & 0x20000) != 0x20000)) { + phytium_jtag_write(phytium_jtag, status | 0x20000, REG_INT_CTL); + return IRQ_HANDLED; + } + + pr_err("TODO Check JTAG's interrupt %x\n", + phytium_jtag_read(phytium_jtag, REG_INT_CTL)); + return IRQ_NONE; +} + + +static struct phytium_jtag_config jtag_config = { + .jtag_buff_len = 15 * 32, +}; + +static const struct of_device_id phytium_jtag_of_matches[] = { + { + .compatible = "phytium,jtag-master", + .data = &jtag_config, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, phytium_jtag_of_matches); + +static int phytium_jtag_bitbang(struct jtag *jtag, + struct tck_bitbang *tck_bitbang) +{ + return 0; +} + +static int phytium_jtag_mode_set(struct jtag *jtag, struct jtag_mode *jtag_mode) +{ + return 0; +} + +static int phytium_jtag_enable(struct jtag *jtag) +{ + return 0; +} + +static int phytium_jtag_disable(struct jtag *jtag) +{ + return 0; +} + +static const struct jtag_ops phytium_jtag_ops = { + .freq_get = phytium_jtag_get_freq, + .freq_set = phytium_jtag_set_freq, + .status_get = phytium_jtag_status_get, + .status_set = phytium_jtag_status_set, + .xfer = phytium_jtag_xfer, + .mode_set = phytium_jtag_mode_set, + .bitbang = phytium_jtag_bitbang, + .enable = phytium_jtag_enable, + .disable = phytium_jtag_disable, +}; + +static int phytium_jtag_probe(struct platform_device *pdev) +{ + struct phytium_jtag_info *phytium_jtag; + struct jtag *jtag; + const struct of_device_id *jtag_dev_id; + struct resource *res; + int ret = 0; + + jtag = jtag_alloc(&pdev->dev, sizeof(*phytium_jtag), + &phytium_jtag_ops); + if (!jtag) + return -ENOMEM; + + platform_set_drvdata(pdev, jtag); + phytium_jtag = jtag_priv(jtag); + phytium_jtag->dev = &pdev->dev; + + jtag_dev_id = of_match_device(phytium_jtag_of_matches, &pdev->dev); + if (!jtag_dev_id) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto out; + } + + phytium_jtag->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (!phytium_jtag->reg_base) { + ret = -EIO; + goto out; + } + + phytium_jtag->irq = platform_get_irq(pdev, 0); + if (phytium_jtag->irq < 0) { + dev_err(&pdev->dev, "no irq specified\n"); + ret = -ENOENT; + goto out; + } + + phytium_jtag->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(phytium_jtag->clk)) { + dev_err(&pdev->dev, "no clock defined\n"); + return -ENODEV; + } + + phytium_jtag->clk = devm_clk_get(&pdev->dev, NULL); + phytium_jtag->clkin = clk_get_rate(phytium_jtag->clk); + dev_info(&pdev->dev, "phytium_jtag->clkin %d\n", phytium_jtag->clkin); + + phytium_jtag->config = (struct phytium_jtag_config *)jtag_dev_id->data; + + init_completion(&phytium_jtag->completion); + init_completion(&phytium_jtag->count_completion); + + phytium_jtag->count_in = 0; + phytium_jtag->expect_num = 0; + + //Jtag Master init + phytium_jtag_write(phytium_jtag, 0x3, REG_JTGM_CTL); + udelay(3); + + ret = devm_request_irq(&pdev->dev, phytium_jtag->irq, phytium_jtag_isr, + 0, dev_name(&pdev->dev), phytium_jtag); + if (ret) { + dev_dbg(&pdev->dev, "JTAG Unable to get IRQ"); + goto out; + } + + phytium_jtag_set_freq(jtag, TCK_FREQ); + + phytium_jtag_write(phytium_jtag, 0x81032, REG_INT_CFG); + + // Register a misc device + ret = devm_jtag_register(phytium_jtag->dev, jtag); + if (ret) + { + dev_err(&pdev->dev, "failed to create device\n"); + goto out; + } + + dev_info(&pdev->dev, "phytium_jtag master: driver successfully loaded.\n"); + + return 0; + +out: + phytium_jtag_write(phytium_jtag, 0x3, REG_JTGM_CTL); + kfree(jtag); + dev_warn(&pdev->dev, "phytium_jtag: driver init failed (ret=%d)!\n", + ret); + return ret; +} + +static int phytium_jtag_remove(struct platform_device *pdev) +{ + struct jtag *jtag = platform_get_drvdata(pdev); + struct phytium_jtag_info *phytium_jtag; + + if (!jtag) + return 0; + + phytium_jtag = jtag_priv(jtag); + phytium_jtag_write(phytium_jtag, 0x3, REG_JTGM_CTL); + jtag_free(jtag); + dev_info(&pdev->dev, "phytium-jtag-master remove"); + return 0; +} + +static struct platform_driver phytium_jtag_driver = { + .probe = phytium_jtag_probe, + .remove = phytium_jtag_remove, + .driver = { + .name = "phytium-jtag", + .of_match_table = phytium_jtag_of_matches, + }, +}; + +module_platform_driver(phytium_jtag_driver); + +MODULE_AUTHOR("Xu Wenkang "); +MODULE_DESCRIPTION("Phytium JTAG Master Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/jtag/jtag.c b/drivers/jtag/jtag.c new file mode 100644 index 0000000000000000000000000000000000000000..876f759b33f04fe957e63947bf321f878b4b017c --- /dev/null +++ b/drivers/jtag/jtag.c @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018 Mellanox Technologies. All rights reserved. +// Copyright (c) 2018 Oleksandr Shamray +// Copyright (c) 2019 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct jtag { + struct miscdevice miscdev; + const struct jtag_ops *ops; + int id; + unsigned long priv[0]; +}; + +static DEFINE_IDA(jtag_ida); + +void *jtag_priv(struct jtag *jtag) +{ + return jtag->priv; +} +EXPORT_SYMBOL_GPL(jtag_priv); + +static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct jtag *jtag = file->private_data; + struct jtag_end_tap_state endstate; + struct jtag_xfer xfer; + struct tck_bitbang bitbang; + struct jtag_mode mode; + + u8 *xfer_data; + u32 data_size; + u32 value; + int err; + + if (!arg) + return -EINVAL; + + switch (cmd) { + case JTAG_GIOCFREQ: + if (!jtag->ops->freq_get) + return -EOPNOTSUPP; + + err = jtag->ops->freq_get(jtag, &value); + if (err) + break; + + if (put_user(value, (__u32 __user *)arg)) + err = -EFAULT; + break; + + case JTAG_SIOCFREQ: + if (!jtag->ops->freq_set) + return -EOPNOTSUPP; + + if (get_user(value, (__u32 __user *)arg)) + return -EFAULT; + if (value == 0) + return -EINVAL; + + err = jtag->ops->freq_set(jtag, value); + break; + + case JTAG_SIOCSTATE: + if (copy_from_user(&endstate, (const void __user *)arg, sizeof(struct jtag_end_tap_state))) + return -EFAULT; + if (endstate.endstate > JTAG_STATE_UPDATEIR) + return -EFAULT; + if (endstate.reset > JTAG_FORCE_RESET) + return -EINVAL; + err = jtag->ops->status_set(jtag, &endstate); + break; + + case JTAG_IOCXFER: + if (copy_from_user(&xfer, (const void __user *)arg, sizeof(struct jtag_xfer))) + return -EFAULT; + if (xfer.length >= JTAG_MAX_XFER_DATA_LEN) + return -EINVAL; + if (xfer.type > JTAG_SDR_XFER) + return -EINVAL; + if (xfer.direction > JTAG_READ_WRITE_XFER) + return -EINVAL; + + data_size = DIV_ROUND_UP(xfer.length, BITS_PER_BYTE); + xfer_data = memdup_user(u64_to_user_ptr(xfer.tdio), data_size); + + if (IS_ERR(xfer_data)) + return -EFAULT; + + err = jtag->ops->xfer(jtag, &xfer, xfer_data); + if (err) { + kfree(xfer_data); + return err; + } + + err = copy_to_user(u64_to_user_ptr(xfer.tdio), (void *)xfer_data, data_size); + kfree(xfer_data); + if (err) + return -EFAULT; + + if (copy_to_user((void __user *)arg, (void *)&xfer, sizeof(struct jtag_xfer))) + return -EFAULT; + + break; + + case JTAG_GIOCSTATUS: + err = jtag->ops->status_get(jtag, &value); + if (err) + break; + + err = put_user(value, (__u32 __user *)arg); + break; + + case JTAG_IOCBITBANG: + if (copy_from_user(&bitbang, (const void __user *)arg, sizeof(struct tck_bitbang))) + return -EFAULT; + + err = jtag->ops->bitbang(jtag, &bitbang); + if (err) + break; + + if (copy_to_user((void __user *)arg, (void *)&bitbang, sizeof(struct tck_bitbang))) + return -EFAULT; + break; + + case JTAG_SIOCMODE: + if (!jtag->ops->mode_set) + return -EOPNOTSUPP; + + if (copy_from_user(&mode, (const void __user *)arg, sizeof(struct jtag_mode))) + return -EFAULT; + err = jtag->ops->mode_set(jtag, &mode); + break; + + default: + return -EINVAL; + } + return err; +} + +static int jtag_open(struct inode *inode, struct file *file) +{ + struct jtag *jtag = container_of(file->private_data, struct jtag, miscdev); + + file->private_data = jtag; + if (jtag->ops->enable(jtag)) + return -EBUSY; + return nonseekable_open(inode, file); +} + +static int jtag_release(struct inode *inode, struct file *file) +{ + struct jtag *jtag = file->private_data; + + if (jtag->ops->disable(jtag)) + return -EBUSY; + + return 0; +} + +static const struct file_operations jtag_fops = { + .owner = THIS_MODULE, + .open = jtag_open, + .llseek = noop_llseek, + .unlocked_ioctl = jtag_ioctl, + .release = jtag_release, +}; + +struct jtag *jtag_alloc(struct device *host, size_t priv_size, + const struct jtag_ops *ops) +{ + struct jtag *jtag; + + if (!host) + return NULL; + + if (!ops) + return NULL; + + if (!ops->status_set ||!ops->status_get || !ops->xfer) + return NULL; + + jtag = kzalloc(sizeof(*jtag) + priv_size, GFP_KERNEL); + if (!jtag) + return NULL; + + jtag->ops = ops; + jtag->miscdev.parent = host; + + return jtag; +} +EXPORT_SYMBOL_GPL(jtag_alloc); + +void jtag_free(struct jtag *jtag) +{ + kfree(jtag); +} +EXPORT_SYMBOL_GPL(jtag_free); + +static int jtag_register(struct jtag *jtag) +{ + struct device *dev = jtag->miscdev.parent; + int err; + int id; + + if (!dev) + return -ENODEV; + + id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL); + if (id < 0) + return id; + + jtag->id = id; + + jtag->miscdev.fops = &jtag_fops; + jtag->miscdev.minor = MISC_DYNAMIC_MINOR; + + jtag->miscdev.name = kasprintf(GFP_KERNEL, "jtag%d", id); + if (!jtag->miscdev.name) { + err = -ENOMEM; + goto err_jtag_alloc; + } + + err = misc_register(&jtag->miscdev); + if (err) { + dev_err(jtag->miscdev.parent, "Unable to register device\n"); + goto err_jtag_name; + } + return 0; + +err_jtag_name: + kfree(jtag->miscdev.name); +err_jtag_alloc: + ida_simple_remove(&jtag_ida, id); + return err; +} + +static void jtag_unregister(struct jtag *jtag) +{ + misc_deregister(&jtag->miscdev); + kfree(jtag->miscdev.name); + ida_simple_remove(&jtag_ida, jtag->id); +} + +static void devm_jtag_unregister(struct device *dev, void *res) +{ + jtag_unregister(*(struct jtag **)res); +} + +int devm_jtag_register(struct device *dev, struct jtag *jtag) +{ + struct jtag **ptr; + int ret; + + ptr = devres_alloc(devm_jtag_unregister, sizeof(struct jtag *), + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = jtag_register(jtag); + if (!ret) { + *ptr = jtag; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + return ret; +} +EXPORT_SYMBOL_GPL(devm_jtag_register); + +static void __exit jtag_exit(void) +{ + ida_destroy(&jtag_ida); +} + +module_exit(jtag_exit); + +MODULE_AUTHOR("Xu Wenkang "); +MODULE_DESCRIPTION("Generic jtag support"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 6c224071bc83fcad826f24ad583b8399a0bb837f..a3ffc680e9caef6d32c9b4a721ced66b932f15b1 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -151,6 +151,12 @@ config VIDEO_TI_CAL In TI Technical Reference Manual this module is referred as Camera Interface Subsystem (CAMSS). +choice + prompt "Phytium jpeg mode" + default VIDEO_PHYTIUM_JPEG_VRAM + help + phytium jpeg mode configuration. + config VIDEO_PHYTIUM_JPEG tristate "Phytium JPEG Encoder driver" depends on VIDEO_V4L2 @@ -161,6 +167,10 @@ config VIDEO_PHYTIUM_JPEG The engine can capture and compress video data from digital or analog sources. +source "drivers/media/platform/phytium-vram/Kconfig" + +endchoice + endif # V4L_PLATFORM_DRIVERS menuconfig V4L_MEM2MEM_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index f89634e3239ad5a995a909c33d88a4fe3d714d50..1999b6efbd76ea323b562ea4d0c98a68770b31c9 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -95,6 +95,8 @@ obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/ obj-$(CONFIG_VIDEO_PHYTIUM_JPEG) += phytium-jpeg/ +obj-$(CONFIG_VIDEO_PHYTIUM_JPEG_VRAM) += phytium-vram/ + obj-y += meson/ obj-y += cros-ec-cec/ diff --git a/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c b/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c index dde1929ba612c7f43544e6ba9deb69ac375d185c..1bda4a5d8f982c1f72a6b0ff59855ea8a83e7b9e 100644 --- a/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c +++ b/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.c @@ -2,7 +2,7 @@ /* * Driver for Phytium JPEG Encoder Engine * - * Copyright (c) 2021-2023, Phytium Technology Co., Ltd. + * Copyright (c) 2021, Phytium Technology Co., Ltd. */ #include "phytium_jpeg_reg.h" @@ -71,7 +71,13 @@ static u32 phytium_jpeg_header[PHYTIUM_JPEG_HEADER_SIZE] = { static char yuv_mode_str[YUV_MODE_STR_LEN] = { "yuv444" }; module_param_string(yuv_mode, yuv_mode_str, sizeof(yuv_mode_str), 0444); -MODULE_PARM_DESC(yuv_mode, "Users select one mode from such modes as 'yuv444', or 'yuv422', or 'yuv420'. If no mode is set, the driver adapts defaults mode 'yuv444'."); +MODULE_PARM_DESC(yuv_mode, "Users select one mode from such modes as" + " 'yuv444', or 'yuv422', or 'yuv420'. If no mode is set," + " the driver adapts defaults mode 'yuv444'."); + +/* The below global variables are used to fiter same log-print lines */ +static bool first_invalid = true; +static bool cur_non_zero = true; static u32 phytium_jpeg_read(struct phytium_jpeg_dev *jpeg_dev, u32 reg) { @@ -166,11 +172,6 @@ static void phytium_jpeg_off(struct phytium_jpeg_dev *jpeg_dev) u32 clear_all_interrupt = INT_FIFO_OVERFLOW | INT_OCM_BUF_OVERFLOW | INT_JPEG_ENCODE_COMPLETE | INT_VIDEO_FORMAT_CHANGE; - if (!test_bit(VIDEO_CLOCKS_ON, &jpeg_dev->status)) { - dev_info(jpeg_dev->dev, "JPEG Engine is already off.\n"); - return; - } - /* disable all interrupt */ phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, disable_all_interrupt); /* clear all interrupt */ @@ -178,6 +179,11 @@ static void phytium_jpeg_off(struct phytium_jpeg_dev *jpeg_dev) /* disable JPEG engine */ phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, TRANSINFO_ENABLE_ENGINE, 0); + if (!test_bit(VIDEO_CLOCKS_ON, &jpeg_dev->status)) { + dev_info(jpeg_dev->dev, "JPEG Engine is already off.\n"); + return; + } + clear_bit(VIDEO_CLOCKS_ON, &jpeg_dev->status); /* wait 50 ms */ mdelay(50); @@ -218,21 +224,28 @@ static void phytium_jpeg_get_resolution(struct phytium_jpeg_dev *jpeg_dev) if (width * height != 0) { detected_timings->width = width; detected_timings->height = height; + jpeg_dev->v4l2_input_status = 0; + cur_non_zero = true; + } else { + /* filter some repeated log-print lines */ + first_invalid = cur_non_zero; + cur_non_zero = false; } - jpeg_dev->v4l2_input_status = 0; - /* * Resolution is changed will trigger an interrupt, resolution detecting * also is disable during process interrupt. So re-enable. */ phytium_jpeg_enable_source_detecting(jpeg_dev); - dev_info(jpeg_dev->dev, "Change resolution: %uX%u\n", width, height); + + if (cur_non_zero == true || first_invalid == true) { + dev_info(jpeg_dev->dev, "Change resolution: %uX%u\n", width, height); + } } static void phytium_jpeg_set_resolution(struct phytium_jpeg_dev *jpeg_dev) { - struct v4l2_bt_timings *active_timings = &jpeg_dev->active_timings; + struct v4l2_bt_timings *active_timings = &jpeg_dev->active_timings; int i; int src_addrs[OCM_BUF_NUM]; /* @@ -479,6 +492,9 @@ static int phytium_jpeg_query_dv_timings(struct file *file, void *priv, struct v4l2_dv_timings *timings) { int ret; + u32 source_info; + u32 width; + u32 height; struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); /* @@ -500,6 +516,16 @@ static int phytium_jpeg_query_dv_timings(struct file *file, void *priv, timings->type = V4L2_DV_BT_656_1120; timings->bt = jpeg_dev->detected_timings; + /* Get resolution from SRC_VGA_INFO_REG */ + source_info = phytium_jpeg_read(jpeg_dev, SRC_VGA_INFO_REG); + width = (source_info & SRC_HOR_PIXELS) >> SRC_WIDTH_SHIFT; + height = (source_info & SRC_VER_PIXELS) >> SRC_HEIGHT_SHIFT; + + /* Check if that the current resolution is zero. */ + if (width == 0 || height == 0) { + jpeg_dev->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; + } + return jpeg_dev->v4l2_input_status ? -ENOLINK : 0; } @@ -738,8 +764,10 @@ static int phytium_jpeg_start_frame(struct phytium_jpeg_dev *jpeg_dev) unsigned long status; struct phytium_jpeg_buffer *jpeg_buf; + /* JPEG Engine shouldn't be enable to compress in the case no signal is input JPEG Engine. + * V4L2_IN_ST_NO_SIGNAL + */ if (jpeg_dev->v4l2_input_status) { - dev_err(jpeg_dev->dev, "No signal; needn't start frame\n"); return 0; } @@ -855,7 +883,7 @@ static int phytium_jpeg_buf_prepare(struct vb2_buffer *vb) static inline struct phytium_jpeg_buffer * phytium_vb2buf_to_dstbuf(struct vb2_v4l2_buffer *buf) { - return container_of(buf, struct phytium_jpeg_buffer, vb); + return container_of(buf, struct phytium_jpeg_buffer, vb); } static void phytium_jpeg_buf_queue(struct vb2_buffer *vb) @@ -900,7 +928,6 @@ static void phytium_jpeg_irq_res_change(struct phytium_jpeg_dev *jpeg_dev, ulong delay) { dev_info(jpeg_dev->dev, "Source resolution is changed, resetting\n"); - set_bit(VIDEO_RES_CHANGE, &jpeg_dev->status); phytium_jpeg_off(jpeg_dev); @@ -915,7 +942,12 @@ static irqreturn_t phytium_jpeg_irq(int irq, void *arg) u32 frame_size; if (test_bit(VIDEO_POWEROFF, &jpeg_dev->status)) { - dev_info(jpeg_dev->dev, "jpeg engine is requested to poweroff\n"); + dev_info(jpeg_dev->dev, "jpeg engine is requested to poweroff 0x%x\n", + phytium_jpeg_read(jpeg_dev, INT_STATUS_CTRL_REG)); + /* Disable interruption */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, STS_VE_JPEG_CODE_COMP_EN, 0); + /* clear all interruption of the hardware's buffers */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, INT_JPEG_ENCODE_COMPLETE, 1); return IRQ_HANDLED; } @@ -1066,7 +1098,7 @@ static irqreturn_t phytium_jpeg_timer31_irq(int irq, void *arg) /* clear timer interrupt status */ writel(0x8, jpeg_dev->timer31_addr + 0x2c); - /* clear JPEG Engine's poweroff status */ + /* clear JPEG Engine's poweroff status */ clear_bit(VIDEO_POWEROFF, &jpeg_dev->status); dev_info(jpeg_dev->dev, "timer31 set jpeg status 0x%lx\n", jpeg_dev->status); @@ -1106,17 +1138,27 @@ static irqreturn_t phytium_jpeg_timer30_irq(int irq, void *arg) struct phytium_jpeg_dev *jpeg_dev = arg; struct arm_smccc_res res; + u32 disable_all_interrupt = 0; + u32 clear_all_interrupt = INT_FIFO_OVERFLOW | INT_OCM_BUF_OVERFLOW | + INT_JPEG_ENCODE_COMPLETE | INT_VIDEO_FORMAT_CHANGE; + /* disable timer interrupt */ writel(0, jpeg_dev->timer30_addr); /* clear timer interrupt status */ writel(0x8, jpeg_dev->timer30_addr + 0x2c); - /* Disable interruption */ - phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, STS_VE_JPEG_CODE_COMP_EN, 0); + /* disable all interrupts */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, disable_all_interrupt); + udelay(5); + /* clear all interrupts */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, clear_all_interrupt); + /* disable JPEG engine */ + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, 0, 0); /* call SE to poweroff JPEG Engine */ arm_smccc_smc(0xc300fff4, 0x9, 0x2, 0x80000020, 0, 0, 0, 0, &res); + set_bit(VIDEO_RES_CHANGE, &jpeg_dev->status); /* set JPEG Engine's status is poweroff */ set_bit(VIDEO_POWEROFF, &jpeg_dev->status); dev_info(jpeg_dev->dev, "timer30 set jpeg status 0x%lx\n", jpeg_dev->status); @@ -1201,12 +1243,45 @@ static int phytium_jpeg_init(struct phytium_jpeg_dev *jpeg_dev) } +/* The function is provided for user space adjusts the sampling mode. */ +static int phytium_jpeg_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct phytium_jpeg_dev *jpeg_dev = container_of(ctrl->handler, + struct phytium_jpeg_dev, + ctrl_handler); + if (ctrl->id != V4L2_CID_JPEG_CHROMA_SUBSAMPLING) { + return -EINVAL; + } + + switch (ctrl->val) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + strncpy(yuv_mode_str, "yuv420", sizeof(yuv_mode_str)); + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + strncpy(yuv_mode_str, "yuv422", sizeof(yuv_mode_str)); + break; + default: + strncpy(yuv_mode_str, "yuv444", sizeof(yuv_mode_str)); + } + phytium_jpeg_set_yuv_mode(jpeg_dev); + dev_info(jpeg_dev->dev, "current sample mode is %s\n", yuv_mode_str); + return 0; +} + +static const struct v4l2_ctrl_ops phytium_jpeg_ctrl_ops = { + .s_ctrl = phytium_jpeg_set_ctrl, +}; + + static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) { struct v4l2_device *v4l2_dev = &jpeg_dev->v4l2_dev; struct vb2_queue *dst_vq = &jpeg_dev->queue; struct video_device *vdev = &jpeg_dev->vdev; + const u64 mask = ~(BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_444) | + BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_422) | + BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_420)); int ret; jpeg_dev->pix_fmt.pixelformat = V4L2_PIX_FMT_JPEG; @@ -1220,8 +1295,19 @@ static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) dev_err(jpeg_dev->dev, "Failed to register v4l2 device\n"); return ret; } - /* Register how many v4l2 controls to a handler */ + v4l2_ctrl_handler_init(&jpeg_dev->ctrl_handler, 1); + v4l2_ctrl_new_std_menu(&jpeg_dev->ctrl_handler, &phytium_jpeg_ctrl_ops, + V4L2_CID_JPEG_CHROMA_SUBSAMPLING, + V4L2_JPEG_CHROMA_SUBSAMPLING_420, mask, + V4L2_JPEG_CHROMA_SUBSAMPLING_444); + if (jpeg_dev->ctrl_handler.error) { + v4l2_ctrl_handler_free(&jpeg_dev->ctrl_handler); + dev_err(jpeg_dev->dev, "Failed to init v4l2 controls:%d\n", + jpeg_dev->ctrl_handler.error); + goto err_v4l2_register; + } + v4l2_dev->ctrl_handler = &jpeg_dev->ctrl_handler; dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; dst_vq->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; dst_vq->dev = v4l2_dev->dev; @@ -1234,6 +1320,7 @@ static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) dst_vq->min_buffers_needed = CAPTURE_BUF_NUMBER; ret = vb2_queue_init(dst_vq); if (ret) { + v4l2_ctrl_handler_free(&jpeg_dev->ctrl_handler); dev_err(jpeg_dev->dev, "Failed to init vb2 queue\n"); goto err_v4l2_register; } @@ -1263,7 +1350,7 @@ static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) err_video_register: vb2_queue_release(dst_vq); - + v4l2_ctrl_handler_free(&jpeg_dev->ctrl_handler); err_v4l2_register: v4l2_device_unregister(v4l2_dev); return ret; @@ -1352,10 +1439,14 @@ static int phytium_jpeg_remove(struct platform_device *pdev) phytium_jpeg_off(jpeg_dev); + phytium_jpeg_write(jpeg_dev, TRANSFORM_INFO_REG, 0); + video_unregister_device(&jpeg_dev->vdev); vb2_queue_release(&jpeg_dev->queue); + v4l2_ctrl_handler_free(&jpeg_dev->ctrl_handler); + v4l2_device_unregister(v4l2_dev); of_reserved_mem_device_release(dev); diff --git a/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.h b/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.h index 5cb98e08469389527e327f7b8bccfc37d9bb51a7..7c9d088aa13a2a5386e3a7b8fb34db83524e134a 100644 --- a/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.h +++ b/drivers/media/platform/phytium-jpeg/phytium_jpeg_core.h @@ -45,7 +45,7 @@ #define MAX_PIXEL_CLOCK (1920 * 1080 * 60) /* 1920 x 1080 x 60Hz */ #define SOURCE_RESOLUTION_DETECT_TIMEOUT msecs_to_jiffies(500) -#define RESOLUTION_CHANGE_DELAY msecs_to_jiffies(0) +#define RESOLUTION_CHANGE_DELAY msecs_to_jiffies(250) #define INVALID_RESOLUTION_DELAY msecs_to_jiffies(250) #define STOP_TIMEOUT msecs_to_jiffies(1000) @@ -128,6 +128,7 @@ struct phytium_jpeg_dev { unsigned int frame_rate; void __iomem *timer30_addr; void __iomem *timer31_addr; + struct v4l2_ctrl_handler ctrl_handler; }; struct phytium_jpeg_config { diff --git a/drivers/media/platform/phytium-vram/Kconfig b/drivers/media/platform/phytium-vram/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..48c760482c986d22a64cd70315a8214dd2deaa51 --- /dev/null +++ b/drivers/media/platform/phytium-vram/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config VIDEO_PHYTIUM_JPEG_VRAM + tristate "Phytium JPEG VRAM Encoder driver" + depends on VIDEO_V4L2 + select VIDEOBUF2_DMA_CONTIG + help + Cannot be enabled together with VIDEO_PHYTIUM_JPEG ! + Support for the Phytium VRAM Encoder Engine embedded + in the Phytium SOCs. + The engine can capture and compress video data from + digital or analog sources. diff --git a/drivers/media/platform/phytium-vram/Makefile b/drivers/media/platform/phytium-vram/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..57281b9d046ad3f244f4b8ecdbc7c2ad8576ed58 --- /dev/null +++ b/drivers/media/platform/phytium-vram/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +phytium_vram-objs := phytium_vram_core.o +obj-$(CONFIG_VIDEO_PHYTIUM_JPEG_VRAM) += phytium_vram.o diff --git a/drivers/media/platform/phytium-vram/phytium_vram_core.c b/drivers/media/platform/phytium-vram/phytium_vram_core.c new file mode 100644 index 0000000000000000000000000000000000000000..b913553e3f24c4db760da5aa1ecd7745baf7f318 --- /dev/null +++ b/drivers/media/platform/phytium-vram/phytium_vram_core.c @@ -0,0 +1,1425 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Phytium VRAM Encoder Engine + * + * Copyright (c) 2021-2023, Phytium Technology Co., Ltd. + */ + +#include "phytium_vram_reg.h" +#include "phytium_vram_core.h" +#include + +#define BMC_MODE + +/* h_index(40) indicates high 8 bits of the height + * w_index(41) contains the low 8 bits of the height, + * and the width. For example, height 480(0x01e0) + * locates at 0x<01> 081100 and 0x038002 . + * width 640 (0x0280) locates at 0x03 <80> <02> e0. + */ + +/* color_mode(42) 0x0200 <11> 01 is a field marks + * YUV mode. + */ +static u32 phytium_jpeg_header[PHYTIUM_JPEG_HEADER_SIZE] = { + 0xe0ffd8ff, 0x464a0100, 0x01004649, 0x01000001, + 0x00000100, 0x4300dbff, 0x0c0b1000, 0x100a0c0e, + 0x120e0d0e, 0x18131011, 0x16181a28, 0x23311816, + 0x3a281d25, 0x393c3d33, 0x40373833, 0x404e5c48, + 0x37455744, 0x516d5038, 0x67625f57, 0x4d3e6768, + 0x64707971, 0x67655c78, 0x00dbff63, 0x12110143, + 0x18151812, 0x2f1a1a2f, 0x42384263, 0x63636363, + 0x63636363, 0x63636363, 0x63636363, 0x63636363, + 0x63636363, 0x63636363, 0x63636363, 0x63636363, + 0x63636363, 0x63636363, 0x63636363, 0xc0ff6363, + 0x01081100, 0x038002e0, 0x02001101, 0x11030111, + 0x00c4ff01, 0x0100001f, 0x01010105, 0x00010101, + 0x00000000, 0x01000000, 0x05040302, 0x09080706, + 0xc4ff0b0a, 0x00011f00, 0x01010103, 0x01010101, + 0x00000101, 0x00000000, 0x04030201, 0x08070605, + 0xff0b0a09, 0x10b500c4, 0x03010200, 0x03040203, + 0x04040505, 0x7d010000, 0x00030201, 0x12051104, + 0x06413121, 0x07615113, 0x32147122, 0x08a19181, + 0xc1b14223, 0xf0d15215, 0x72623324, 0x160a0982, + 0x1a191817, 0x28272625, 0x35342a29, 0x39383736, + 0x4544433a, 0x49484746, 0x5554534a, 0x59585756, + 0x6564635a, 0x69686766, 0x7574736a, 0x79787776, + 0x8584837a, 0x89888786, 0x9493928a, 0x98979695, + 0xa3a29a99, 0xa7a6a5a4, 0xb2aaa9a8, 0xb6b5b4b3, + 0xbab9b8b7, 0xc5c4c3c2, 0xc9c8c7c6, 0xd4d3d2ca, + 0xd8d7d6d5, 0xe2e1dad9, 0xe6e5e4e3, 0xeae9e8e7, + 0xf4f3f2f1, 0xf8f7f6f5, 0xc4fffaf9, 0x0011b500, + 0x04020102, 0x07040304, 0x00040405, 0x00770201, + 0x11030201, 0x31210504, 0x51411206, 0x13716107, + 0x08813222, 0xa1914214, 0x2309c1b1, 0x15f05233, + 0x0ad17262, 0xe1342416, 0x1817f125, 0x27261a19, + 0x352a2928, 0x39383736, 0x4544433a, 0x49484746, + 0x5554534a, 0x59585756, 0x6564635a, 0x69686766, + 0x7574736a, 0x79787776, 0x8483827a, 0x88878685, + 0x93928a89, 0x97969594, 0xa29a9998, 0xa6a5a4a3, + 0xaaa9a8a7, 0xb5b4b3b2, 0xb9b8b7b6, 0xc4c3c2ba, + 0xc8c7c6c5, 0xd3d2cac9, 0xd7d6d5d4, 0xe2dad9d8, + 0xe6e5e4e3, 0xeae9e8e7, 0xf5f4f3f2, 0xf9f8f7f6, + 0x00fefffa, 0x0000008f, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdaff0000, 0x01030c00, 0x03110200, 0x003f0011 +}; + +static char yuv_mode_str[YUV_MODE_STR_LEN] = { "yuv444" }; +static int dc_addr; + + +module_param_string(yuv_mode, yuv_mode_str, sizeof(yuv_mode_str), 0444); +module_param(dc_addr, int, 0644); + + +MODULE_PARM_DESC(yuv_mode, "Users select one mode from such modes as\n" + " \t\t'yuv444', or 'yuv422', or 'yuv420'. If no mode is set,\n" + " \t\tthe driver adapts defaults mode 'yuv444'."); + +static void phytium_jpeg_init_jpeg_quant(struct phytium_jpeg_dev *jpeg_dev); +static void phytium_jpeg_set_yuv_mode(struct phytium_jpeg_dev *jpeg_dev); + +static u32 phytium_jpeg_read(struct phytium_jpeg_dev *jpeg_dev, u32 reg) +{ + u32 reg_val = readl(jpeg_dev->base_addr + reg); + + dev_dbg(jpeg_dev->dev, "read 0x%p + 0x%x -->val[0x%x]\n", + jpeg_dev->base_addr, reg, reg_val); + + return reg_val; +} + +static void phytium_jpeg_write(struct phytium_jpeg_dev *jpeg_dev, + u32 reg, u32 val) +{ + writel(val, jpeg_dev->base_addr + reg); + dev_dbg(jpeg_dev->dev, "write 0x%x to addr 0x%p + 0x%x\n", + val, jpeg_dev->base_addr, reg); +} + +static void phytium_jpeg_update(struct phytium_jpeg_dev *jpeg_dev, u32 reg, + u32 clear, u32 bits) +{ + u32 reg_val = readl(jpeg_dev->base_addr + reg); + u32 tmp = reg_val; + + reg_val &= ~clear; + reg_val |= bits; + writel(reg_val, jpeg_dev->base_addr + reg); + + dev_dbg(jpeg_dev->dev, "the val of addr 0x%p + 0x%x, from 0x%x to 0x%x\n", + jpeg_dev->base_addr, reg, tmp, readl(jpeg_dev->base_addr + reg)); +} + +static void phytium_jpeg_init_regs(struct phytium_jpeg_dev *jpeg_dev) +{ + u32 transform_info = 0; + u32 disable_all_interrupt = 0; + u32 clear_all_interrupt = INT_FIFO_OVERFLOW | INT_OCM_BUF_OVERFLOW | + INT_JPEG_ENCODE_COMPLETE | INT_VIDEO_FORMAT_CHANGE; + u32 rate_to_reg = 0; + + spin_lock_irq(&jpeg_dev->reset_lock); + /* First, disable the JPEG engine, set bit0 = 0*/ + phytium_jpeg_write(jpeg_dev, TRANSFORM_INFO_REG, transform_info); + + /* Second, set VGAvideo_source_information. bit1 = 0 marks VGA */ + transform_info |= 0; + + /* Third, set AXI burst length bit[16:22]= 0xf , default value*/ + transform_info |= (0xF << TRANS_AXI_LEN_SHIFT) & TRANSINFO_AXI_LEN; + + /* Fourth, the default sampling format is YUV422, set bit13 to 0 */ + /* ignore setting sampling interval */ + phytium_jpeg_write(jpeg_dev, TRANSFORM_INFO_REG, transform_info); + udelay(5); + + /* Fifth, setting frame rate. + * Linux driver prohibit float point operations. So use the + * format: reg_val = (1 second * 10^8 / frame_rate / 134 *100) + * write reg_val to register. then enable Highest bit31 = 1 + */ + if (jpeg_dev->frame_rate) { + rate_to_reg = 100000000 / jpeg_dev->frame_rate / 134 * 100; + rate_to_reg |= FRAME_SAMPLE_CTRL_EN; + phytium_jpeg_write(jpeg_dev, FRAME_SAMPLE_CTRL, rate_to_reg); + } + /* Sixth, HUFF_MODE, driver needn't to configure, ignore */ + + /* disable all interrupts and then clear all interrupts */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, + disable_all_interrupt); + udelay(5); + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, clear_all_interrupt); + spin_unlock_irq(&jpeg_dev->reset_lock); + + /* Seventh, Sample_mode, hardware default is yuv444 */ + jpeg_dev->yuv420 = false; +} + +static void phytium_jpeg_reset_init(struct phytium_jpeg_dev *jpeg_dev) +{ + struct arm_smccc_res res; + + spin_lock_irq(&jpeg_dev->reset_lock); + /* call SE to poweroff JPEG Engine */ + arm_smccc_smc(PHYTIUM_SET_OCN_JPEG_OFF, 0x9, 0x3, 0x80000020, 0, 0, 0, 0, &res); + spin_unlock_irq(&jpeg_dev->reset_lock); + + phytium_jpeg_init_jpeg_quant(jpeg_dev); + + /* Select YUV mode */ + phytium_jpeg_set_yuv_mode(jpeg_dev); + phytium_jpeg_init_regs(jpeg_dev); +} + +/* Disable the jpeg engine */ +static void phytium_jpeg_off(struct phytium_jpeg_dev *jpeg_dev) +{ + u32 disable_all_interrupt = 0; + u32 clear_all_interrupt = INT_FIFO_OVERFLOW | INT_OCM_BUF_OVERFLOW | + INT_JPEG_ENCODE_COMPLETE | INT_VIDEO_FORMAT_CHANGE; + + spin_lock_irq(&jpeg_dev->reset_lock); + /* disable all interrupt */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, disable_all_interrupt); + /* clear all interrupt */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, clear_all_interrupt); + /* disable JPEG engine */ + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, TRANSINFO_ENABLE_ENGINE, 0); + spin_unlock_irq(&jpeg_dev->reset_lock); + + /* wait 50 ms */ + mdelay(50); + /* C08 bit7 1:busy */ +} + + +#define res_check(val) \ + test_and_clear_bit(VIDEO_MODE_DETECT_DONE, &(val)->status) + +/* The below functions is implemented for various v4l2 ioctl operations */ +static int phytium_jpeg_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + strscpy(cap->driver, PHYTIUM_JPEG_NAME, sizeof(cap->driver)); + strscpy(cap->card, "Phytium JPEG Engine", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", dev_name(jpeg_dev->dev)); + + return 0; +} + +static int phytium_jpeg_enum_format(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + dev_dbg(jpeg_dev->dev, "1\n"); + + if (f->index) { + dev_err(jpeg_dev->dev, "Failed to enum format\n"); + return -EINVAL; + } + + f->pixelformat = V4L2_PIX_FMT_JPEG; + + return 0; +} + +static int phytium_jpeg_get_format(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + f->fmt.pix = jpeg_dev->pix_fmt; + + dev_dbg(jpeg_dev->dev, "2\n"); + + return 0; +} + +static int phytium_jpeg_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + dev_dbg(jpeg_dev->dev, "3\n"); + + if (input->index) { + dev_err(jpeg_dev->dev, "failed to enum input\n"); + return -EINVAL; + } + + strscpy(input->name, "Host DC Capture", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + input->capabilities = V4L2_IN_CAP_DV_TIMINGS; + input->status = jpeg_dev->v4l2_input_status; + + return 0; +} + +static int phytium_jpeg_get_input(struct file *file, void *priv, + unsigned int *i) +{ + *i = 0; + + return 0; +} + +static int phytium_jpeg_set_input(struct file *file, void *priv, + unsigned int i) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + if (i != 0) { + dev_err(jpeg_dev->dev, "Failed to set input\n"); + return -EINVAL; + } + + return 0; +} + +static int phytium_jpeg_get_parm(struct file *file, void *priv, + struct v4l2_streamparm *stream) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + /* Readbuffers num is 3 */ + stream->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + stream->parm.capture.readbuffers = CAPTURE_BUF_NUMBER; + stream->parm.capture.timeperframe.denominator = 1; + + if (jpeg_dev->frame_rate == 0) + stream->parm.capture.timeperframe.denominator = MAX_FRAME_RATE; + else + stream->parm.capture.timeperframe.denominator = jpeg_dev->frame_rate; + + return 0; +} + +static int phytium_jpeg_set_parm(struct file *file, void *priv, + struct v4l2_streamparm *stream) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + unsigned int frame_rate = 0; + + stream->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + stream->parm.capture.readbuffers = CAPTURE_BUF_NUMBER; + + if (stream->parm.capture.timeperframe.numerator) + frame_rate = stream->parm.capture.timeperframe.denominator / + stream->parm.capture.timeperframe.numerator; + + if (frame_rate == 0 || frame_rate > MAX_FRAME_RATE) { + frame_rate = MAX_FRAME_RATE; + stream->parm.capture.timeperframe.denominator = MAX_FRAME_RATE; + stream->parm.capture.timeperframe.numerator = 1; + } + + jpeg_dev->frame_rate = frame_rate; + + return 0; +} + +static int phytium_jpeg_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) + +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + dev_dbg(jpeg_dev->dev, "4\n"); + + if (fsize->index != 0) { + dev_err(jpeg_dev->dev, "Failed to enum framesize.\n"); + return -EINVAL; + } + + if (fsize->pixel_format != V4L2_PIX_FMT_JPEG) { + dev_err(jpeg_dev->dev, "enum framesize pixel_format is not JPEG"); + return -EINVAL; + } + + fsize->discrete.width = jpeg_dev->pix_fmt.width; + fsize->discrete.height = jpeg_dev->pix_fmt.height; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + + return 0; + +} + +static int phytium_jpeg_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + dev_dbg(jpeg_dev->dev, "5\n"); + + if (fival->index != 0) { + dev_err(jpeg_dev->dev, "enum frame intervals failed\n"); + return -EINVAL; + } + + if (fival->width != jpeg_dev->detected_timings.width || + fival->height != jpeg_dev->detected_timings.height) { + dev_err(jpeg_dev->dev, "interval isn't same with the detected_timings.\n"); + return -EINVAL; + } + + if (fival->pixel_format != V4L2_PIX_FMT_JPEG) { + dev_err(jpeg_dev->dev, "enum frame interval pixel fomat is incorrect.\n"); + return -EINVAL; + } + + fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; + fival->stepwise.min.denominator = MAX_FRAME_RATE; + fival->stepwise.min.numerator = 1; + fival->stepwise.max.denominator = 1; + fival->stepwise.max.numerator = 1; + fival->stepwise.step = fival->stepwise.max; + + return 0; +} + +static int phytium_jpeg_set_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + dev_dbg(jpeg_dev->dev, "6\n"); + + /* the params are passed from user space are same with hardware's params */ + if (timings->bt.width == jpeg_dev->active_timings.width && + timings->bt.height == jpeg_dev->active_timings.height) + return 0; + + if (vb2_is_busy(&jpeg_dev->queue)) { + dev_err(jpeg_dev->dev, "queue is busy during setting dv timings.\n"); + return -EBUSY; + } + + jpeg_dev->active_timings = timings->bt; + jpeg_dev->max_compressed_size = + jpeg_dev->active_timings.width * jpeg_dev->active_timings.height; + jpeg_dev->pix_fmt.width = timings->bt.width; + jpeg_dev->pix_fmt.height = timings->bt.height; + jpeg_dev->pix_fmt.sizeimage = jpeg_dev->max_compressed_size; + timings->type = V4L2_DV_BT_656_1120; + + return 0; +} + +static int phytium_jpeg_get_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + timings->type = V4L2_DV_BT_656_1120; + timings->bt = jpeg_dev->active_timings; + dev_dbg(jpeg_dev->dev, "7 %d %d\n", jpeg_dev->detected_timings.width, + jpeg_dev->detected_timings.height); + + return 0; +} + +static int phytium_jpeg_query_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + if (test_bit(VIDEO_RES_CHANGE, &jpeg_dev->status)) + msleep(100); + + timings->type = V4L2_DV_BT_656_1120; + timings->bt = jpeg_dev->detected_timings; + dev_dbg(jpeg_dev->dev, "8 %d %d\n", jpeg_dev->detected_timings.width, + jpeg_dev->detected_timings.height); + + return jpeg_dev->v4l2_input_status ? -ENOLINK : 0; +} + +static const struct v4l2_dv_timings_cap phytium_jpeg_timings_cap = { + .type = V4L2_DV_BT_656_1120, + .bt = { + .min_width = MIN_WIDTH, + .max_width = MAX_WIDTH, + .min_height = MIN_HEIGHT, + .max_height = MAX_HEIGHT, + .min_pixelclock = 6574080, /* 640 x 480 x 24Hz */ + .max_pixelclock = 1244160000, /* 1920 x 1080 x 60Hz */ + .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF, + .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_REDUCED_BLANKING | + V4L2_DV_BT_CAP_CUSTOM, + }, +}; + +static int phytium_jpeg_enum_dv_timings(struct file *file, void *priv, + struct v4l2_enum_dv_timings *timings) +{ + return v4l2_enum_dv_timings_cap(timings, &phytium_jpeg_timings_cap, + NULL, NULL); +} + +static int phytium_jpeg_dv_timings_cap(struct file *file, void *priv, + struct v4l2_dv_timings_cap *cap) +{ + *cap = phytium_jpeg_timings_cap; + + return 0; +} + +/* The function is used to notify DV that video resolution is altered */ +static int phytium_jpeg_sub_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + default: + break; + } + + return v4l2_ctrl_subscribe_event(fh, sub); +} + +static const struct v4l2_ioctl_ops phytium_jpeg_ioctl_ops = { + .vidioc_querycap = phytium_jpeg_querycap, + .vidioc_enum_fmt_vid_cap = phytium_jpeg_enum_format, + .vidioc_g_fmt_vid_cap = phytium_jpeg_get_format, + .vidioc_s_fmt_vid_cap = phytium_jpeg_get_format, + .vidioc_try_fmt_vid_cap = phytium_jpeg_get_format, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_input = phytium_jpeg_enum_input, + .vidioc_g_input = phytium_jpeg_get_input, + .vidioc_s_input = phytium_jpeg_set_input, + .vidioc_g_parm = phytium_jpeg_get_parm, + .vidioc_s_parm = phytium_jpeg_set_parm, + .vidioc_enum_framesizes = phytium_jpeg_enum_framesizes, + .vidioc_enum_frameintervals = phytium_jpeg_enum_frameintervals, + .vidioc_s_dv_timings = phytium_jpeg_set_dv_timings, + .vidioc_g_dv_timings = phytium_jpeg_get_dv_timings, + .vidioc_query_dv_timings = phytium_jpeg_query_dv_timings, + .vidioc_enum_dv_timings = phytium_jpeg_enum_dv_timings, + .vidioc_dv_timings_cap = phytium_jpeg_dv_timings_cap, + .vidioc_subscribe_event = phytium_jpeg_sub_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static void phytium_jpeg_init_jpeg_quant(struct phytium_jpeg_dev *jpeg_dev) +{ + const u32 y_quant_table[QUANT_REG_NUM] = { + 0x08000000, 0x0ba2e8ba, 0x0aaaaaab, 0x09249249, 0x0aaaaaab, + 0x0ccccccc, 0x08000000, 0x09249249, 0x09d89d8a, 0x09249249, + 0x071c71c7, 0x07878788, 0x08000000, 0x06bca1af, 0x05555555, + 0x03333333, 0x04ec4ec5, 0x05555555, 0x05d1745d, 0x05d1745d, + 0x05555555, 0x029cbc15, 0x03a83a84, 0x03759f23, 0x0469ee58, + 0x03333333, 0x0234f72c, 0x02828283, 0x02192e2a, 0x02222222, + 0x023ee090, 0x02828283, 0x02492492, 0x0253c825, 0x02000000, + 0x01c71c72, 0x01642c86, 0x01a41a42, 0x02000000, 0x01e1e1e2, + 0x0178a4c8, 0x01dae607, 0x0253c825, 0x02492492, 0x0199999a, + 0x012c9fb5, 0x01948b10, 0x0178a4c8, 0x0158ed23, 0x014e5e0a, + 0x013e22cc, 0x013b13b1, 0x013e22cc, 0x02108421, 0x01a98ef6, + 0x0121fb78, 0x010ecf57, 0x01249249, 0x013e22cc, 0x01111111, + 0x01642c86, 0x01446f86, 0x013e22cc, 0x014afd6a + }; + + const u32 c_quant_table[QUANT_REG_NUM] = { + 0x07878788, 0x071c71c7, 0x071c71c7, 0x05555555, 0x06186186, + 0x05555555, 0x02b93105, 0x04ec4ec5, 0x04ec4ec5, 0x02b93105, + 0x014afd6a, 0x01f07c1f, 0x02492492, 0x01f07c1f, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a, + 0x014afd6a, 0x014afd6a, 0x014afd6a, 0x014afd6a + }; + int i; + + spin_lock_irq(&jpeg_dev->reset_lock); + for (i = 0; i < QUANT_REG_NUM; i++) { + phytium_jpeg_write(jpeg_dev, Y_QUANT_INDEX_ADDR_REG(i), y_quant_table[i]); + phytium_jpeg_write(jpeg_dev, C_QUANT_INDEX_ADDR_REG(i), c_quant_table[i]); + } + spin_unlock_irq(&jpeg_dev->reset_lock); +} + +static void phytium_jpeg_start(struct phytium_jpeg_dev *jpeg_dev) +{ + int i, value; + char *dst, *src; + unsigned long trans_src; + unsigned long dma_addr; + struct phytium_dc_param dc_param; + void __iomem *dc_reg = jpeg_dev->dc_reg; + + jpeg_dev->v4l2_input_status = 0; + + value = readl(dc_reg + PHYTIUM_DC_FRAMEBUFFER_CONFIG); + if ((value & FRAMEBUFFER_OUTPUT) && (!(value & FRAMEBUFFER_CLEAR))) { + dc_param.width = readl(dc_reg + PHYTIUM_DC_HDISPLAY) & HDISPLAY_END_MASK; + dc_param.height = readl(dc_reg + PHYTIUM_DC_VDISPLAY) & VDISPLAY_END_MASK; + dc_param.stride = readl(dc_reg + PHYTIUM_DC_FRAMEBUFFER_Y_STRIDE); + dma_addr = (readl(dc_reg + PE220X_DC_FRAMEBUFFER_Y_HI_ADDRESS) & PREFIX_MASK); + dma_addr = (dma_addr << PREFIX_SHIFT); + dma_addr |= readl(dc_reg + PHYTIUM_DC_FRAMEBUFFER_Y_ADDRESS); + dc_param.dma_addr = dma_addr; + jpeg_dev->dc_param = dc_param; +#ifdef BMC_MODE + trans_src = readl(jpeg_dev->trans_reg + PE220X_DC_ADDR_TRANS_SRC_ADDR); + trans_src = trans_src << ADDR_OFFSET; +#else + trans_src = jpeg_dev->dc_vram_info.dma_addr; +#endif + if (jpeg_dev->dc_param.dma_addr < trans_src) + src = jpeg_dev->dc_vram_info.virt_addr; + else + src = jpeg_dev->dc_vram_info.virt_addr + + (jpeg_dev->dc_param.dma_addr - trans_src); + + dst = jpeg_dev->src_addrs[0].virt_addr; + dev_dbg(jpeg_dev->dev, "23: dma_addr: 0x%lx trans_src:0x%lx\n", + jpeg_dev->dc_param.dma_addr, trans_src); + for (i = 0; i < jpeg_dev->dc_param.height; i++) { + memcpy(dst, src, jpeg_dev->dc_param.width * 4); + src += jpeg_dev->dc_param.stride; + dst += jpeg_dev->dc_param.width * 4; + } + } else { + jpeg_dev->dc_param.width = MIN_WIDTH; + jpeg_dev->dc_param.height = MIN_HEIGHT; + jpeg_dev->dc_param.stride = MIN_WIDTH * 4; + jpeg_dev->dc_param.dma_addr = 0; + dst = jpeg_dev->src_addrs[0].virt_addr; + for (i = 0; i < jpeg_dev->dc_param.height; i++) { + memset(dst, 0, jpeg_dev->dc_param.width * 4); + dst += jpeg_dev->dc_param.width * 4; + } + } + + jpeg_dev->detected_timings.width = jpeg_dev->dc_param.width; + jpeg_dev->detected_timings.height = jpeg_dev->dc_param.height; + jpeg_dev->active_timings = jpeg_dev->detected_timings; + jpeg_dev->max_compressed_size = jpeg_dev->active_timings.width + * jpeg_dev->active_timings.height; + jpeg_dev->pix_fmt.width = jpeg_dev->active_timings.width; + jpeg_dev->pix_fmt.height = jpeg_dev->active_timings.height; + jpeg_dev->pix_fmt.sizeimage = jpeg_dev->max_compressed_size; + +} + +static void phytium_jpeg_stop(struct phytium_jpeg_dev *jpeg_dev) +{ + phytium_jpeg_off(jpeg_dev); + + jpeg_dev->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; + jpeg_dev->status = 0; +} + +static int phytium_jpeg_open(struct file *file) +{ + int ret; + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + mutex_lock(&jpeg_dev->video_lock); + ret = v4l2_fh_open(file); + if (ret != 0) { + mutex_unlock(&jpeg_dev->video_lock); + dev_err(jpeg_dev->dev, "Failed to open the phytium jpeg device.\n"); + return ret; + } + + if (v4l2_fh_is_singular_file(file)) + phytium_jpeg_start(jpeg_dev); + + mutex_unlock(&jpeg_dev->video_lock); + + return 0; +} + +static int phytium_jpeg_release(struct file *file) +{ + int ret; + struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); + + mutex_lock(&jpeg_dev->video_lock); + + if (v4l2_fh_is_singular_file(file)) + phytium_jpeg_stop(jpeg_dev); + + ret = _vb2_fop_release(file, NULL); + mutex_unlock(&jpeg_dev->video_lock); + + return ret; +} + + +static const struct v4l2_file_operations phytium_jpeg_fops = { + .owner = THIS_MODULE, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, + .open = phytium_jpeg_open, + .release = phytium_jpeg_release, +}; + +static void phytium_jpeg_update_jpeg_header(u32 width, u32 height) +{ + const int h_index = PHYTIUM_JPEG_HEADER_H_INDEX; + const int w_index = PHYTIUM_JPEG_HEADER_W_INDEX; + + /* the high 8 bits of the height locates at bit24~bit31 */ + phytium_jpeg_header[h_index] = phytium_jpeg_header[h_index] & 0x00FFFFFF; + phytium_jpeg_header[h_index] |= ((height >> 8) & 0xFF) << 24; + + /* the low 8 bits of the height locates at bit0~bit7 */ + phytium_jpeg_header[w_index] = phytium_jpeg_header[w_index] & 0xFF000000; + phytium_jpeg_header[w_index] |= height & 0xFF; + + /* the high 8 bits of the width locates at bit8~bit15 */ + phytium_jpeg_header[w_index] |= ((width >> 8) & 0xFF) << 8; + /* the low 8 bits of the width locates at bit16~bit24 */ + phytium_jpeg_header[w_index] |= (width & 0xFF) << 16; +} + +static void phytium_jpeg_fill_header(struct phytium_jpeg_dev *jpeg_dev, + struct phytium_jpeg_buffer *jpeg_buf) +{ + void *vbuf = vb2_plane_vaddr(&jpeg_buf->vb.vb2_buf, 0); + u32 width = jpeg_dev->active_timings.width; + u32 height = jpeg_dev->active_timings.height; + + /* update the contents of the phytium jpeg header according to the resolution */ + phytium_jpeg_update_jpeg_header(width, height); + + /* replenish the contents of the JPEG header */ + memcpy(vbuf, phytium_jpeg_header, PHYTIUM_JPEG_HEADER_LEN); +} + +static int phytium_jpeg_start_frame(struct phytium_jpeg_dev *jpeg_dev) +{ + dma_addr_t dst_addr; + unsigned long status; + struct phytium_jpeg_buffer *jpeg_buf; + int src_addrs[OCM_BUF_NUM], i, value; + + if (jpeg_dev->v4l2_input_status) { + dev_err(jpeg_dev->dev, "No signal; needn't start frame\n"); + return 0; + } + + spin_lock_irqsave(&jpeg_dev->hw_lock, status); + jpeg_buf = list_first_entry_or_null(&jpeg_dev->buffers, + struct phytium_jpeg_buffer, link); + if (jpeg_buf == NULL) { + spin_unlock_irqrestore(&jpeg_dev->hw_lock, status); + dev_err(jpeg_dev->dev, "No buffers; doesn't start frame\n"); + return -EPROTO; + } + + set_bit(VIDEO_FRAME_INPRG, &jpeg_dev->status); + dst_addr = vb2_dma_contig_plane_dma_addr(&jpeg_buf->vb.vb2_buf, 0); + spin_unlock_irqrestore(&jpeg_dev->hw_lock, status); + + /* + * Because the JPEG Engine is unable to add a JPEG header, the phytium + * jpeg driver is required to fill the contents of a JPEG header before + * the jpeg engine write datas to the dma address. + */ + phytium_jpeg_fill_header(jpeg_dev, jpeg_buf); + dst_addr += PHYTIUM_JPEG_HEADER_LEN; + /* + * The ikvm application only using the last frame, so the driver replenish + * one output register with a dma address. + */ + dst_addr >>= JPEG_DST_ADDR_SHIFT; + + for (i = 0; i < OCM_BUF_NUM; i++) + src_addrs[i] = jpeg_dev->src_addrs[i].dma_addr >> OCM_BUF_SHIFT; + + value = (((jpeg_dev->active_timings.width - 1) & CFG_H_MASK) << CFG_H_SHIFT); + value |= (((jpeg_dev->active_timings.height - 1) & CFG_V_MASK) << CFG_V_SHIFT); + value |= (CFG_IMG | CFG_OCM_VALID); + + spin_lock_irq(&jpeg_dev->reset_lock); + phytium_jpeg_write(jpeg_dev, BUF_LIST_INDEX_ADDR(VB_BUF_NO), dst_addr); + /* Enable the validilty of the buffer marked with index */ + phytium_jpeg_write(jpeg_dev, BUF_LIST_INDEX_CTRL_STS_ADDR(VB_BUF_NO), + STS_JPEG_BUF_HIGH_LEVEL_VALID); + phytium_jpeg_write(jpeg_dev, OCM_BUF0_ADDR, src_addrs[0]); + phytium_jpeg_write(jpeg_dev, OCM_BUF1_ADDR, src_addrs[0]); + + /* configure ddr mode w*h */ + phytium_jpeg_write(jpeg_dev, SRC_DDR_INFO_REG, value); + + /* Enable the interruption which is used to identify an image was compressed */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, 0, STS_VE_JPEG_CODE_COMP_EN); + + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, + TRANSINFO_IMAGE_STORE, TRANSINFO_IMAGE_STORE); + + /* Enable JPEG, start to capture and compress */ + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, TRANSINFO_ENABLE_ENGINE, 1); + spin_unlock_irq(&jpeg_dev->reset_lock); + + return 0; +} + +static int phytium_jpeg_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(q); + + if (*num_planes) { + if (sizes[0] < jpeg_dev->max_compressed_size) { + v4l2_err(&jpeg_dev->v4l2_dev, "queue v4l2_buf's size is invalid\n"); + return -EINVAL; + } + } + + *num_planes = 1; + sizes[0] = jpeg_dev->max_compressed_size; + return 0; +} + +static int phytium_jpeg_buf_prepare(struct vb2_buffer *vb) +{ + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(vb->vb2_queue); + + if (vb2_plane_size(vb, 0) < jpeg_dev->max_compressed_size) { + v4l2_err(&jpeg_dev->v4l2_dev, "failed to prepare buffer\n"); + return -EINVAL; + } + + return 0; +} + +static inline struct phytium_jpeg_buffer * +phytium_vb2buf_to_dstbuf(struct vb2_v4l2_buffer *buf) +{ + return container_of(buf, struct phytium_jpeg_buffer, vb); +} + +static void phytium_jpeg_buf_queue(struct vb2_buffer *vb) +{ + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct phytium_jpeg_buffer *jpeg_buf = phytium_vb2buf_to_dstbuf(vbuf); + unsigned long status; + + spin_lock_irqsave(&jpeg_dev->hw_lock, status); + list_add_tail(&jpeg_buf->link, &jpeg_dev->buffers); + spin_unlock_irqrestore(&jpeg_dev->hw_lock, status); +} + +static void phytium_jpeg_bufs_done(struct phytium_jpeg_dev *jpeg_dev, + enum vb2_buffer_state state) +{ + unsigned long flags; + struct phytium_jpeg_buffer *buf; + + spin_lock_irqsave(&jpeg_dev->hw_lock, flags); + + list_for_each_entry(buf, &jpeg_dev->buffers, link) + vb2_buffer_done(&buf->vb.vb2_buf, state); + + INIT_LIST_HEAD(&jpeg_dev->buffers); + + spin_unlock_irqrestore(&jpeg_dev->hw_lock, flags); +} + +static irqreturn_t phytium_jpeg_irq(int irq, void *arg) +{ + struct phytium_jpeg_dev *jpeg_dev = arg; + u32 status; + struct phytium_jpeg_buffer *buf; + u32 frame_size; + + spin_lock_irq(&jpeg_dev->reset_lock); + status = phytium_jpeg_read(jpeg_dev, INT_STATUS_CTRL_REG); + spin_unlock_irq(&jpeg_dev->reset_lock); + + /* + * JPEG engine finish compressing a image JPEG encoding to trigger + * a interruption. the status identifies the buffer number. Currently, + * the driver uses one buffer. + * + * Note: Because the JPEG doesn't support adding a JPEG header, and + * driver is also unable to add a JPEG header to vb2_buffers. One + * solution is that a JPEG header is added by an application. + */ + if (status & INT_JPEG_COMP_BUF_LIST_NO) { + frame_size = phytium_jpeg_read(jpeg_dev, jpeg_dev->comp_size_read); + frame_size &= JPEG_BUF_CAPACITY_SIZE; + frame_size >>= JPEG_BUF_CAPACITY_SIZE_SHIFT; + spin_lock(&jpeg_dev->hw_lock); + if (test_bit(VIDEO_RES_CHANGE, &jpeg_dev->status)) + clear_bit(VIDEO_RES_CHANGE, &jpeg_dev->status); + + clear_bit(VIDEO_FRAME_INPRG, &jpeg_dev->status); + /* Delete first node from the queue */ + buf = list_first_entry_or_null(&jpeg_dev->buffers, + struct phytium_jpeg_buffer, link); + if (buf != NULL) { + frame_size += PHYTIUM_JPEG_HEADER_LEN; + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, frame_size); + if (!list_is_last(&buf->link, &jpeg_dev->buffers)) { + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + buf->vb.sequence = jpeg_dev->sequence++; + buf->vb.field = V4L2_FIELD_NONE; + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + list_del(&buf->link); + } + } + + spin_unlock(&jpeg_dev->hw_lock); + + spin_lock_irq(&jpeg_dev->reset_lock); + /* Disable JPEG engine */ + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, TRANSINFO_ENABLE_ENGINE, 0); + /* Disable interruption */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, STS_VE_JPEG_CODE_COMP_EN, 0); + /* clear all interruption of the hardware's buffers */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, INT_JPEG_ENCODE_COMPLETE, 1); + spin_unlock_irq(&jpeg_dev->reset_lock); + wake_up_interruptible_all(&jpeg_dev->wait_update_ddr); + } + + return IRQ_HANDLED; +} + +/* VIDIOC_STREAMON, all vb2_v4l2_buf' states are queue */ +static int phytium_jpeg_start_streaming(struct vb2_queue *q, unsigned int count) +{ + int ret; + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(q); + + dev_dbg(jpeg_dev->dev, "9\n"); + + jpeg_dev->sequence = 0; + jpeg_dev->timeout_jiffies = 0; + phytium_jpeg_reset_init(jpeg_dev); + dev_dbg(jpeg_dev->dev, "15\n"); + ret = phytium_jpeg_start_frame(jpeg_dev); + if (ret != 0) { + phytium_jpeg_bufs_done(jpeg_dev, VB2_BUF_STATE_QUEUED); + return ret; + } + + /* set the states of the jpeg engine */ + set_bit(VIDEO_STREAMING, &jpeg_dev->status); + dev_dbg(jpeg_dev->dev, "16\n"); + return ret; +} + +static void phytium_jpeg_stop_streaming(struct vb2_queue *q) +{ + struct phytium_jpeg_dev *jpeg_dev = vb2_get_drv_priv(q); + + dev_dbg(jpeg_dev->dev, "10\n"); + + clear_bit(VIDEO_STREAMING, &jpeg_dev->status); + phytium_jpeg_off(jpeg_dev); + /* first stop jpeg, wait, the free buffer */ + phytium_jpeg_bufs_done(jpeg_dev, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops phytium_jpeg_vb2_ops = { + .queue_setup = phytium_jpeg_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_prepare = phytium_jpeg_buf_prepare, + .buf_queue = phytium_jpeg_buf_queue, + .start_streaming = phytium_jpeg_start_streaming, + .stop_streaming = phytium_jpeg_stop_streaming, +}; + +static void phytium_jpeg_get_yuv_mode(struct phytium_jpeg_dev *jpeg_dev) +{ + const char *mode = yuv_mode_str; + + if (strstr(mode, "yuv422") != NULL) + jpeg_dev->yuv_mode = YUV422; + else if (strstr(mode, "yuv420") != NULL) + jpeg_dev->yuv_mode = YUV420; + else + jpeg_dev->yuv_mode = YUV444; + + /* update the field which indicates YUV mode locates in the JPEG header. */ + phytium_jpeg_header[YUVID] &= 0xFFFF00FF; + if (jpeg_dev->yuv_mode == YUV422) + phytium_jpeg_header[YUVID] |= 0x2100; + else if (jpeg_dev->yuv_mode == YUV420) + phytium_jpeg_header[YUVID] |= 0x2200; + else + phytium_jpeg_header[YUVID] |= 0x1100; +} + +static void phytium_jpeg_set_yuv_mode(struct phytium_jpeg_dev *jpeg_dev) +{ + spin_lock_irq(&jpeg_dev->reset_lock); + /* set the yuv mode register */ + phytium_jpeg_write(jpeg_dev, SAMPLE_MODE_REG, jpeg_dev->yuv_mode); + spin_unlock_irq(&jpeg_dev->reset_lock); +} + +int phytium_jpeg_ddr_mode_thread(void *data) +{ + struct phytium_jpeg_dev *jpeg_dev = data; + char *src = NULL, *dst; + int i, ret, value; + unsigned long trans_src; + unsigned long dma_addr; + struct phytium_dc_param dc_param; + void __iomem *dc_reg; + struct v4l2_bt_timings *detected_timings = &jpeg_dev->detected_timings; + static const struct v4l2_event event = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + + dc_reg = jpeg_dev->dc_reg; + + for (;;) { + dev_dbg(jpeg_dev->dev, "11 0x%lx\n", jpeg_dev->status); + ret = wait_event_interruptible(jpeg_dev->wait_update_ddr, + (((!test_bit(VIDEO_FRAME_INPRG, &jpeg_dev->status)) && + (!test_bit(VIDEO_RES_CHANGE, &jpeg_dev->status)) && + test_bit(VIDEO_STREAMING, &jpeg_dev->status)) + || kthread_should_stop())); + if (ret) + dev_err(jpeg_dev->dev, "Failed to get ddr update event\n"); + + dev_dbg(jpeg_dev->dev, "12 0x%lx\n", jpeg_dev->status); + if (kthread_should_stop()) + break; + + dev_dbg(jpeg_dev->dev, "20\n"); + + value = readl(dc_reg + PHYTIUM_DC_FRAMEBUFFER_CONFIG); + if ((value & FRAMEBUFFER_OUTPUT) && (!(value & FRAMEBUFFER_CLEAR))) { + dc_param.width = readl(dc_reg + PHYTIUM_DC_HDISPLAY) & HDISPLAY_END_MASK; + dc_param.height = readl(dc_reg + PHYTIUM_DC_VDISPLAY) & VDISPLAY_END_MASK; + dc_param.stride = readl(dc_reg + PHYTIUM_DC_FRAMEBUFFER_Y_STRIDE); + dma_addr = (readl(dc_reg + PE220X_DC_FRAMEBUFFER_Y_HI_ADDRESS) + & PREFIX_MASK); + dma_addr = (dma_addr << PREFIX_SHIFT); + dma_addr |= readl(dc_reg + PHYTIUM_DC_FRAMEBUFFER_Y_ADDRESS); + dc_param.dma_addr = dma_addr; + jpeg_dev->dc_param = dc_param; +#ifdef BMC_MODE + + trans_src = readl(jpeg_dev->trans_reg + PE220X_DC_ADDR_TRANS_SRC_ADDR); + trans_src = trans_src << ADDR_OFFSET; +#else + trans_src = jpeg_dev->dc_vram_info.dma_addr; +#endif + if (jpeg_dev->dc_param.width * jpeg_dev->dc_param.height != 0) { + detected_timings->width = jpeg_dev->dc_param.width; + detected_timings->height = jpeg_dev->dc_param.height; + } + + dev_dbg(jpeg_dev->dev, "13 %d %d\n", jpeg_dev->detected_timings.width, + jpeg_dev->active_timings.width); + if (jpeg_dev->detected_timings.width != jpeg_dev->active_timings.width || + jpeg_dev->detected_timings.height != jpeg_dev->active_timings.height) { + phytium_jpeg_off(jpeg_dev); + + v4l2_event_queue(&jpeg_dev->vdev, &event); + set_bit(VIDEO_RES_CHANGE, &jpeg_dev->status); + } + + if (jpeg_dev->dc_param.dma_addr < trans_src) + src = jpeg_dev->dc_vram_info.virt_addr; + else + src = jpeg_dev->dc_vram_info.virt_addr + + (jpeg_dev->dc_param.dma_addr - trans_src); + + dst = jpeg_dev->src_addrs[0].virt_addr; + for (i = 0; i < jpeg_dev->dc_param.height; i++) { + memcpy(dst, src, jpeg_dev->dc_param.width * 4); + src += jpeg_dev->dc_param.stride; + dst += jpeg_dev->dc_param.width * 4; + } + } else { + dst = jpeg_dev->src_addrs[0].virt_addr; + dev_dbg(jpeg_dev->dev, "21\n"); + for (i = 0; i < jpeg_dev->dc_param.height; i++) { + memset(dst, 0, jpeg_dev->dc_param.width * 4); + dst += jpeg_dev->dc_param.width * 4; + } + dev_dbg(jpeg_dev->dev, "22\n"); + } + + while (time_before(jiffies, jpeg_dev->timeout_jiffies)) { + usleep_range(1000, 3000); + }; + + if (!jpeg_dev->frame_rate) { + jpeg_dev->frame_rate = 30; + dev_err(jpeg_dev->dev, "jpeg frame rate is zero\n"); + } + jpeg_dev->timeout_jiffies = jiffies + msecs_to_jiffies(1000/jpeg_dev->frame_rate); + + dev_dbg(jpeg_dev->dev, "23\n"); + if (test_bit(VIDEO_STREAMING, &jpeg_dev->status) && + (!test_bit(VIDEO_RES_CHANGE, &jpeg_dev->status))) { + dev_dbg(jpeg_dev->dev, "14\n"); + phytium_jpeg_reset_init(jpeg_dev); + dev_dbg(jpeg_dev->dev, "17\n"); + phytium_jpeg_start_frame(jpeg_dev); + } + } + + return 0; +} + +static int phytium_jpeg_init(struct phytium_jpeg_dev *jpeg_dev) +{ + int irq; + int ret; + struct device *dev = jpeg_dev->dev; + + irq = irq_of_parse_and_map(dev->of_node, 0); + if (!irq) { + dev_err(dev, "Failed to get IRQ\n"); + return -ENODEV; + } + + ret = devm_request_threaded_irq(dev, irq, NULL, phytium_jpeg_irq, + IRQF_ONESHOT, PHYTIUM_JPEG_NAME, jpeg_dev); + if (ret < 0) { + dev_err(dev, "Failed to request IRQ %d\n", irq); + return ret; + } + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret != 0) { + dev_err(dev, "Failed to set DMA mask\n"); + return ret; + } + + phytium_jpeg_get_yuv_mode(jpeg_dev); + + return 0; + +} + +int phytium_jpeg_ddr_mode_init(struct phytium_jpeg_dev *jpeg_dev) +{ + struct device *dev = jpeg_dev->dev; + struct device_node *ddr_mode_node; + unsigned int dc_reg[2], dc_vram[2], jpeg_vram[2], trans_reg[2]; + int ret; + + ddr_mode_node = of_get_child_by_name(jpeg_dev->dev->of_node, "ddr-mode"); + if (!ddr_mode_node) { + dev_err(jpeg_dev->dev, "could not find ddr-mode node\n"); + goto out; + } + + ret = of_property_read_u32_array(ddr_mode_node, "phytium,dc-reg", + dc_reg, 2); + if (ret != 0) { + dev_err(dev, "Failed to get the dc-reg info from device tree node.\n"); + goto out; + } + + ret = of_property_read_u32_array(ddr_mode_node, "phytium,dc-vram", + dc_vram, 2); + if (ret != 0) { + dev_err(dev, "Failed to get the dc-vram info from device tree node.\n"); + goto out; + } + + ret = of_property_read_u32_array(ddr_mode_node, "phytium,jpeg-vram", + jpeg_vram, 2); + if (ret != 0) { + dev_err(dev, "Failed to get the jpeg-vram info from device tree node.\n"); + goto out; + } + + ret = of_property_read_u32_array(ddr_mode_node, "phytium,trans-reg", + trans_reg, 2); + if (ret != 0) { + dev_err(dev, "Failed to get the trans-reg info from device tree node.\n"); + goto out; + } + +#ifdef BMC_MODE + jpeg_dev->dc_reg = ioremap(dc_reg[0], dc_reg[1]); + if (jpeg_dev->dc_reg == NULL) { + dev_err(dev, "failed to remap dc reg(0x%x)\n", dc_reg[0]); + goto out; + } + + jpeg_dev->trans_reg = ioremap(trans_reg[0], trans_reg[1]); + if (jpeg_dev->trans_reg == NULL) { + dev_err(dev, "failed to remap trans reg(0x%x)\n", trans_reg[0]); + goto failed_remap_trans_reg; + } +#else + unsigned long dc_addr_h = PHYTIUM_DC_ADDR_H; + + jpeg_dev->dc_reg = ((dc_addr_h << 32) | dc_addr); + dev_dbg(jpeg_dev->dev, "dc_addr: 0x%p\n", jpeg_dev->dc_reg); +#endif + + jpeg_dev->dc_vram_info.virt_addr = ioremap_cache(dc_vram[0], dc_vram[1]); + if (jpeg_dev->dc_vram_info.virt_addr == NULL) { + dev_err(dev, "failed to remap dc vram(0x%x)\n", dc_vram[0]); + goto failed_remap_dc_vram; + } + jpeg_dev->dc_vram_info.dma_addr = dc_vram[0]; + jpeg_dev->dc_vram_info.size = dc_vram[1]; + dev_dbg(jpeg_dev->dev, "dc_vram_info.virt_addr:0x%p dma:0x%llx\n", + jpeg_dev->dc_vram_info.virt_addr, jpeg_dev->dc_vram_info.dma_addr); + + jpeg_dev->src_addrs[0].virt_addr = ioremap_cache(jpeg_vram[0], jpeg_vram[1]); + if (jpeg_dev->src_addrs[0].virt_addr == NULL) { + dev_err(dev, "failed to remap jpeg vram(0x%x)\n", jpeg_vram[0]); + goto failed_remap_jpeg_vram; + } + + jpeg_dev->src_addrs[0].dma_addr = jpeg_vram[0]; + jpeg_dev->src_addrs[0].size = jpeg_vram[1]; + dev_dbg(jpeg_dev->dev, "src_addrs[0]:0x%p dma:0x%x\n", jpeg_dev->src_addrs[0].virt_addr, + jpeg_dev->src_addrs[0].dma_addr); + memset(jpeg_dev->src_addrs[0].virt_addr, 0, jpeg_dev->src_addrs[0].size); + + spin_lock_init(&jpeg_dev->reset_lock); + jpeg_dev->kthread = kthread_run(phytium_jpeg_ddr_mode_thread, jpeg_dev, "%s", "jpeg"); + if (IS_ERR(jpeg_dev->kthread)) { + dev_err(dev, "failed to create update vram thread (%ld)\n", + PTR_ERR(jpeg_dev->kthread)); + goto failed_create_kthread; + } + + jpeg_dev->ddr_mode = true; + + return 0; + +failed_create_kthread: + iounmap(jpeg_dev->src_addrs[0].virt_addr); +failed_remap_jpeg_vram: + iounmap(jpeg_dev->dc_vram_info.virt_addr); +failed_remap_dc_vram: + iounmap(jpeg_dev->trans_reg); +failed_remap_trans_reg: + iounmap(jpeg_dev->dc_reg); +out: + jpeg_dev->ddr_mode = false; + + return -1; +} + +void phytium_jpeg_ddr_mode_release(struct phytium_jpeg_dev *jpeg_dev) +{ + if (jpeg_dev->ddr_mode) { + if (jpeg_dev->kthread) + kthread_stop(jpeg_dev->kthread); + + iounmap(jpeg_dev->src_addrs[0].virt_addr); + iounmap(jpeg_dev->dc_vram_info.virt_addr); +#ifdef BMC_MODE + iounmap(jpeg_dev->trans_reg); + iounmap(jpeg_dev->dc_reg); +#endif + jpeg_dev->ddr_mode = false; + } +} + +static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) +{ + struct v4l2_device *v4l2_dev = &jpeg_dev->v4l2_dev; + struct vb2_queue *dst_vq = &jpeg_dev->queue; + struct video_device *vdev = &jpeg_dev->vdev; + int ret; + + jpeg_dev->pix_fmt.pixelformat = V4L2_PIX_FMT_JPEG; + jpeg_dev->pix_fmt.field = V4L2_FIELD_NONE; + jpeg_dev->pix_fmt.colorspace = V4L2_COLORSPACE_SRGB; /* maybe ARGB */ + jpeg_dev->pix_fmt.quantization = V4L2_QUANTIZATION_FULL_RANGE; + jpeg_dev->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; + + ret = v4l2_device_register(jpeg_dev->dev, v4l2_dev); + if (ret != 0) { + dev_err(jpeg_dev->dev, "Failed to register v4l2 device\n"); + return ret; + } + + /* Register how many v4l2 controls to a handler */ + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dst_vq->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; + dst_vq->dev = v4l2_dev->dev; + dst_vq->lock = &jpeg_dev->video_lock; + dst_vq->ops = &phytium_jpeg_vb2_ops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->drv_priv = jpeg_dev; + dst_vq->buf_struct_size = sizeof(struct phytium_jpeg_buffer); + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + dst_vq->min_buffers_needed = CAPTURE_BUF_NUMBER; + ret = vb2_queue_init(dst_vq); + if (ret) { + dev_err(jpeg_dev->dev, "Failed to init vb2 queue\n"); + goto err_v4l2_register; + } + + vdev->queue = dst_vq; + vdev->fops = &phytium_jpeg_fops; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + vdev->v4l2_dev = v4l2_dev; + strscpy(vdev->name, PHYTIUM_JPEG_NAME, sizeof(vdev->name)); + vdev->vfl_type = VFL_TYPE_GRABBER; /* The newest kernel using VFL_TYPE_VIDEO */ + vdev->vfl_dir = VFL_DIR_RX; + vdev->release = video_device_release_empty; + vdev->ioctl_ops = &phytium_jpeg_ioctl_ops; + vdev->lock = &jpeg_dev->video_lock; + + video_set_drvdata(vdev, jpeg_dev); + ret = video_register_device(vdev, VFL_TYPE_GRABBER, 0); + if (ret != 0) { + dev_err(jpeg_dev->dev, "Failed to register video device\n"); + goto err_video_register; + } + + v4l2_info(v4l2_dev, "phytium JPEG registered as /dev/video%d (%d, %d)\n", + jpeg_dev->vdev.num, VIDEO_MAJOR, jpeg_dev->vdev.minor); + return ret; + +err_video_register: + vb2_queue_release(dst_vq); + +err_v4l2_register: + v4l2_device_unregister(v4l2_dev); + return ret; +} + +static const struct phytium_jpeg_config phytium_jpeg_config = { + .comp_size_read = BUF_LIST_INDEX_CTRL_STS_ADDR(VB_BUF_NO), +}; + +static const struct of_device_id phytium_jpeg_match[] = { + { + .compatible = "phytium,jpeg-vram", + .data = &phytium_jpeg_config, + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, phytium_jpeg_match); + +static int phytium_jpeg_probe(struct platform_device *pdev) +{ + struct phytium_jpeg_dev *jpeg_dev; + const struct of_device_id *match; + const struct phytium_jpeg_config *config; + struct resource *res; + int ret; + + jpeg_dev = devm_kzalloc(&pdev->dev, sizeof(*jpeg_dev), GFP_KERNEL); + if (jpeg_dev == NULL) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + jpeg_dev->base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(jpeg_dev->base_addr)) { + dev_err(jpeg_dev->dev, "Failed to ioremap.\n"); + return PTR_ERR(jpeg_dev->base_addr); + } + + match = of_match_node(phytium_jpeg_match, pdev->dev.of_node); + if (match == NULL) { + dev_err(jpeg_dev->dev, "Failed to match.\n"); + return -EINVAL; + } + + config = match->data; + jpeg_dev->comp_size_read = config->comp_size_read; + + jpeg_dev->frame_rate = 30; + jpeg_dev->dev = &pdev->dev; + spin_lock_init(&jpeg_dev->hw_lock); + mutex_init(&jpeg_dev->video_lock); + init_waitqueue_head(&jpeg_dev->wait_update_ddr); + INIT_LIST_HEAD(&jpeg_dev->buffers); + + ret = phytium_jpeg_ddr_mode_init(jpeg_dev); + if (ret < 0) + dev_info(jpeg_dev->dev, "JPEG engine ddr mode init failed\n"); + + ret = phytium_jpeg_init(jpeg_dev); + if (ret != 0) { + dev_err(jpeg_dev->dev, "Failed to initialize the JPEG engine.\n"); + return ret; + } + + ret = phytium_jpeg_setup_video(jpeg_dev); + + return ret; +} + +#define to_phytium_jpeg(x) container_of((x), struct phytium_jpeg_dev, v4l2_dev) +static int phytium_jpeg_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct phytium_jpeg_dev *jpeg_dev = to_phytium_jpeg(v4l2_dev); + + phytium_jpeg_off(jpeg_dev); + + phytium_jpeg_ddr_mode_release(jpeg_dev); + + video_unregister_device(&jpeg_dev->vdev); + + vb2_queue_release(&jpeg_dev->queue); + + v4l2_device_unregister(v4l2_dev); + + of_reserved_mem_device_release(dev); + + return 0; +} + +static struct platform_driver phytium_jpeg_driver = { + .probe = phytium_jpeg_probe, + .remove = phytium_jpeg_remove, + .driver = { + .name = PHYTIUM_JPEG_NAME, + .of_match_table = phytium_jpeg_match, + }, +}; + +module_platform_driver(phytium_jpeg_driver); + +MODULE_DESCRIPTION("Phytium JPEG-VRAM Encoder driver"); +MODULE_AUTHOR("Wang Hao "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/phytium-vram/phytium_vram_core.h b/drivers/media/platform/phytium-vram/phytium_vram_core.h new file mode 100644 index 0000000000000000000000000000000000000000..5559c3dccd1766d67d58f39b407077a2a4385119 --- /dev/null +++ b/drivers/media/platform/phytium-vram/phytium_vram_core.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021-2023, Phytium Technology Co., Ltd. + */ + +#ifndef _PHYTIUM_VRAM_CORE_H +#define _PHYTIUM_VRAM_CORE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PHYTIUM_JPEG_NAME "phytium-jpeg" +#define MAX_FRAME_RATE 60 +#define MAX_HEIGHT 1080 +#define MAX_WIDTH 1920 +#define MIN_HEIGHT 480 +#define MIN_WIDTH 640 +#define MIN_PIXEL_CLOCK (640 * 480 * 60) /* 640 x 480 x 60Hz */ +#define MAX_PIXEL_CLOCK (1920 * 1080 * 60) /* 1920 x 1080 x 60Hz */ + +#define SOURCE_RESOLUTION_DETECT_TIMEOUT msecs_to_jiffies(500) +#define RESOLUTION_CHANGE_DELAY msecs_to_jiffies(0) +#define INVALID_RESOLUTION_DELAY msecs_to_jiffies(250) +#define STOP_TIMEOUT msecs_to_jiffies(1000) + +#define INVALID_RESOLUTION_RETRIES 2 +#define CAPTURE_BUF_NUMBER 3 /* using how many buffers */ +#define VB_BUF_NO 0 /* there are 16 buffer, use which one */ + +/* The below macros are defined for the JPEG header of the phytium JPEG Engine */ +#define PHYTIUM_JPEG_HEADER_LEN (256 * 3) +#define PHYTIUM_JPEG_HEADER_SIZE (PHYTIUM_JPEG_HEADER_LEN / sizeof(u32)) +#define PHYTIUM_JPEG_HEADER_H_INDEX 40 +#define PHYTIUM_JPEG_HEADER_W_INDEX 41 + +/* There are two ocm buffers that are used for storaging the inputing video data */ +#define OCM_BUF_NUM 2 + +enum phytium_jpeg_status { + VIDEO_MODE_DETECT_DONE, + VIDEO_RES_CHANGE, + VIDEO_RES_DETECT, + VIDEO_STREAMING, + VIDEO_FRAME_INPRG, + VIDEO_STOPPED, + VIDEO_CLOCKS_ON, + VIDEO_POWEROFF, +}; + +struct phytium_dc_vram_info { + unsigned int size; + dma_addr_t dma_addr; + void *virt_addr; +}; + +struct phytium_jpeg_addr { + unsigned int size; + unsigned int dma_addr; + void *virt_addr; +}; + +struct phytium_jpeg_buffer { + struct vb2_v4l2_buffer vb; + struct list_head link; +}; + +enum jpeg_yuv_mode { + YUV444 = 0x0, + YUV422 = 0x1, + YUV420 = 0x2 +}; + +struct phytium_dc_param { + unsigned int width; + unsigned int height; + unsigned int stride; + unsigned int enable; + unsigned long dma_addr; +}; + +/** + * struct phytium_jpeg - JPEG IP abstraction + * @lock: the mutex protecting this structure + * @hw_lock: spinlock protecting the hw device resource + * @workqueue: decode work queue + * @dev: JPEG device + * @v4l2_dev: v4l2 device for mem2mem mode + * @m2m_dev: v4l2 mem2mem device data + * @alloc_ctx: videobuf2 memory allocator's context + * @dec_vdev: video device node for decoder mem2mem mode + * @dec_reg_base: JPEG registers mapping + * @clk_jdec: JPEG hw working clock + * @clk_jdec_smi: JPEG SMI bus clock + * @larb: SMI device + */ +struct phytium_jpeg_dev { + void __iomem *base_addr; + struct device *dev; + struct v4l2_device v4l2_dev; + struct v4l2_pix_format pix_fmt; + struct v4l2_bt_timings active_timings; + struct v4l2_bt_timings detected_timings; + u32 v4l2_input_status; + struct vb2_queue queue; + struct video_device vdev; + /* v4l2 and videobuf2 lock, protect the structure*/ + struct mutex video_lock; + u32 jpeg_mode; + u32 comp_size_read; + /* buffer list lock, protecting the hw device resource */ + spinlock_t hw_lock; + struct list_head buffers; + unsigned long status; + unsigned int sequence; + unsigned int max_compressed_size; + struct phytium_jpeg_addr src_addrs[OCM_BUF_NUM]; + struct phytium_jpeg_addr dst_addrs[16]; + + bool yuv420; + unsigned int frame_rate; + + void __iomem *dc_reg; + void __iomem *trans_reg; + struct phytium_dc_param dc_param; + struct phytium_dc_vram_info dc_vram_info; + struct task_struct *kthread; + wait_queue_head_t wait_update_ddr; + spinlock_t reset_lock; + bool ddr_mode; + enum jpeg_yuv_mode yuv_mode; + unsigned long timeout_jiffies; +}; + +struct phytium_jpeg_config { + u32 jpeg_mode; + u32 comp_size_read; +}; + +#define YUV_MODE_STR_LEN 8 +#define YUVID 42 + +#endif /* _PHYTIUM_VRAM_CORE_H */ diff --git a/drivers/media/platform/phytium-vram/phytium_vram_reg.h b/drivers/media/platform/phytium-vram/phytium_vram_reg.h new file mode 100644 index 0000000000000000000000000000000000000000..cf9a0a58a05a2504b83eeda6cfe48062747a4b01 --- /dev/null +++ b/drivers/media/platform/phytium-vram/phytium_vram_reg.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021-2023, Phytium Technology Co., Ltd. + */ + +#ifndef _PHYTIUM_VRAM_REG_H +#define _PHYTIUM_VRAM_REG_H + +#include +/* define the all kinds of control registers in a JPEG soc */ + +/* The service id call se to turn off ocn jpeg */ +#define PHYTIUM_SET_OCN_JPEG_OFF 0xc300fff4 + +/* The register is used to set the information of the video comes from main memory */ +#define SRC_DDR_INFO_REG 0x00000800 + +#define CFG_V_MASK 0x7ff +#define CFG_V_SHIFT 16 +#define CFG_H_MASK 0xfff +#define CFG_H_SHIFT 4 +#define CFG_IMG BIT(27) +#define CFG_OCM_VALID BIT(28) + + +/* The register is used to get the information of the video comes from external VGA */ +#define SRC_VGA_INFO_REG 0x00000894 + +#define SRC_FORMAT BIT(0) /* 0:RGB888, 1:RGB565 */ +#define SRC_DE_POLARITY BIT(1) /* 0:low level is effect, other */ +#define SRC_HS_POLARITY BIT(2) /* 0:low level is effect, other */ +#define SRC_VS_POLARITY BIT(3) /* 0:low level is effect, other */ +#define SRC_HOR_PIXELS GENMASK(15, 4) /* the number of the horizontal pixels */ +#define SRC_WIDTH_SHIFT 4 /* shift right to get width */ +#define SRC_VER_PIXELS GENMASK(26, 16) /* the number of the vertical pixels */ +#define SRC_HEIGHT_SHIFT 16 /* shift right to get height */ +/* The below bit fields is only used by image comes from main memory */ +#define SRC_COMP_DDR_IMG_EN BIT(27) /* 0: disable to JPEG compression, others */ + +/* marks which ocm buffer is occupied to store an image */ +#define SRC_DDR_IMG2OCM_VALID GENMASK(29, 28) + +/* The register controls starting work of the JPEG engine */ +#define TRANSFORM_INFO_REG 0x00000804 +#define TRANSINFO_ENABLE_ENGINE BIT(0) /* 1: enable the JPEG engine */ +/* 1: video comes from external VGA, 0: video comes from DDR */ +#define TRANSINFO_SRC_SELECT BIT(1) +/* 0: video comes from external VGA is cached to OCM, 1: DDR */ +#define TRANSINFO_IMAGE_STORE BIT(2) +#define TRANSINFO_FRAME_RATE GENMASK(9, 4) /* the value notes frame rate */ +#define TRANSINFO_BLOCK_SIZE BIT(12) /* 0: 8x8, 1: 16x16 */ +#define TRANSINFO_ENABLE_YUV422 BIT(13) /* 1: JPEG block is populated YUV422 */ +/* support burst with the values such as 1, 2, 4, 8, 16, 32, 64. using default value 0xf */ +#define TRANSINFO_AXI_LEN GENMASK(22, 16) +#define TRANS_AXI_LEN_SHIFT 16 + +/* The interrupt and status register */ +#define INT_STATUS_CTRL_REG 0x00000808 +#define INT_FIFO_OVERFLOW BIT(0) /* video fifo overflow, write 1 to clear */ +#define INT_OCM_BUF_OVERFLOW BIT(1) /* ocm buffer overflow, write 1 to clear */ +/* JPEG engine complete compression, write 1 to clear */ +#define INT_JPEG_ENCODE_COMPLETE BIT(2) +/* in VGA mode, video's format is changed */ +#define INT_VIDEO_FORMAT_CHANGE BIT(3) +/* enable the interrupt of th video fifo overflow and source resolution */ +#define DETECT_RESOLUTION_CHANGE_EN BIT(4) +/* enable the interrupt of the ocm buffer overflow */ +#define STS_VE_OCM_BUF_OVERFLOW_EN BIT(5) +/* enable the interrupt of the JPEG complete compression */ +#define STS_VE_JPEG_CODE_COMP_EN BIT(6) +/* in VGA mode, the bit notes ocm buff is busy */ +#define STS_VE_OCM_BUF_BUSY BIT(7) +/* in VGA mode, the bit notes sequence number of the frame */ +#define STS_VE_CUR_FRAME_NUMBER GENMASK(9, 8) +/* in VGA mode, the bit notes sequence number of the cached frame */ +#define STS_VE_BUF_CACHE_NUMBER GENMASK(11, 10) +/* in VGA mode, the buffer number in buffer list */ +#define STS_JPEG_COMP_BUF_NO GENMASK(15, 12) +#define INT_JPEG_COMP_BUF_LIST_NO GENMASK(31, 16) /* the interrupt number of the buffer */ + +#define OCM_BUF0_ADDR 0x0000080C +#define OCM_BUF1_ADDR 0x00000810 +#define OCM_BUF_SHIFT 8 + +#define BUF_LIST_BASE_ADDR 0x00000814 + +#define PHYTIUM_BUF_LIST_ACTRL_AND_STS_BASE_ADDR_REG 0x00000818 +#define STS_JPEG_BUF_HIGH_LEVEL_VALID BIT(0) /* Hight levle is validity */ +#define JPEG_BUF_CAPACITY_SIZE GENMASK(29, 8) /* the capacity of the buffer */ +#define JPEG_BUF_CAPACITY_SIZE_SHIFT 8 + +/* There are 16 buffers in the buffer list, the width between each other' address is 8 bytes */ +#define BUF_LIST_ADDR_OFFSET 0x8 +#define BUF_LIST_CTRL_AND_STS_OFFSET 0x8 + +/* Get the address of the specific index buffer */ +#define BUF_LIST_INDEX_ADDR(index) \ + (BUF_LIST_BASE_ADDR + (index) * BUF_LIST_ADDR_OFFSET) + +#define JPEG_DST_ADDR_SHIFT 8 + +#define BUF_LIST_INDEX_CTRL_STS_ADDR(index) \ + (PHYTIUM_BUF_LIST_ACTRL_AND_STS_BASE_ADDR_REG + (index) * BUF_LIST_CTRL_AND_STS_OFFSET) + +#define FRAME_SAMPLE_CTRL 0x00000898 +#define FRAME_SAMPLE_CTRL_EN BIT(31) +#define FRAME_SAMPLE_INTERVAL GENMASK(30, 0) + +/* The below registers are all related to quantilize */ +#define HUFF_MODE_REG 0x300 +#define SAMPLE_MODE_REG 0x304 + +#define Y_QUANT_BASE_ADDR_REG 0x400 +#define C_QUANT_BASE_ADDR_REG 0x500 + +#define QUANT_REG_NUM 64 + +#define Y_QUANT_INDEX_ADDR_REG(index) \ + (Y_QUANT_BASE_ADDR_REG + 4 * (index)) + +#define C_QUANT_INDEX_ADDR_REG(index) \ + (C_QUANT_BASE_ADDR_REG + 4 * (index)) + +#define PHYTIUM_DC_ADDR_H 0xffffff80UL + +#define PHYTIUM_DC_FRAMEBUFFER_Y_ADDRESS 0x1400 +#define PE220X_DC_FRAMEBUFFER_Y_HI_ADDRESS 0x1404 +#define PREFIX_MASK 0xff +#define PREFIX_SHIFT 32 + +#define PHYTIUM_DC_FRAMEBUFFER_Y_STRIDE 0x1408 + +#define PHYTIUM_DC_HDISPLAY 0x1430 + #define HDISPLAY_END_SHIFT 0 + #define HDISPLAY_END_MASK 0x7fff + #define HDISPLAY_TOTAL_SHIFT 16 + #define HDISPLAY_TOTAL_MASK 0x7fff + +#define PHYTIUM_DC_VDISPLAY 0x1440 + #define VDISPLAY_END_SHIFT 0 + #define VDISPLAY_END_MASK 0x7fff + #define VDISPLAY_TOTAL_SHIFT 16 + #define VDISPLAY_TOTAL_MASK 0x7fff + +#define PHYTIUM_DC_FRAMEBUFFER_CONFIG 0x1518 + #define FRAMEBUFFER_OUTPUT BIT(0) + #define FRAMEBUFFER_CLEAR BIT(8) + +#define PE220X_DC_ADDR_TRANS_SRC_ADDR 0x0 +#define ADDR_OFFSET 22 +#define ADDR_MASK 0xffffffffff + +#define PE220X_DC_ADDRESS_TRANSFORM_SIZE 0x4 +#define ADDRESS_TRANSFORM_ENABLE (0x1 << 31) +#define SIZE_OFFSET 22 + +#endif /* _PHYTIUM_VRAM_REG_H */ diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 3726eacdf65de2470ba83c6a91dc3351f69ea713..1f401d5e06c9e30df135cd32373d003417203b3f 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -501,6 +501,14 @@ config ASPEED_LPC_SNOOP allows the BMC to listen on and save the data written by the host to an arbitrary LPC I/O port. +config PHYTIUM_LPC_SNOOP + tristate "Phytium PE2201 HOST LPC snoop support" + depends on ARCH_PHYTIUM && REGMAP && MFD_SYSCON + help + Provides a driver to control the LPC snoop interface which + allows the BMC to listen on and save the data written by + the host to an arbitrary LPC I/O port. + config PCI_ENDPOINT_TEST depends on PCI select CRC32 diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index af22bbc3d00cbcd248c4e10247b87bbb46ecab3f..9b5daeae77d6f03de40329e725bd8875ef6dd5db 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o +obj-$(CONFIG_PHYTIUM_LPC_SNOOP) += phytium-lpc-snoop.o obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_OCXL) += ocxl/ obj-$(CONFIG_MISC_RTSX) += cardreader/ diff --git a/drivers/misc/phytium-lpc-snoop.c b/drivers/misc/phytium-lpc-snoop.c new file mode 100644 index 0000000000000000000000000000000000000000..491e73375db65ea1408b03a033a11cf2f56ca8e7 --- /dev/null +++ b/drivers/misc/phytium-lpc-snoop.c @@ -0,0 +1,322 @@ +/* + * Copyright 2020-2021 Phytium Technology Co.,Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Provides a simple driver to control the PHYTIUM PE2201 LPC snoop interface which + * allows the BMC to listen on and save the data written by + * the host to an arbitrary LPC I/O port. + * + * Typically used by the BMC to "watch" host boot progress via port + * 0x80 writes made by the BIOS during the boot process. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "phytium-lpc-snoop" + +#define NUM_SNOOP_CHANNELS 2 +#define SNOOP_FIFO_SIZE 2048 + +#define snp_enable_reg 0x150 +#define snp_enable_reg_snp1_en BIT(0) +#define snp_enable_reg_snp1_int_en BIT(1) +#define snp_enable_reg_snp2_en BIT(2) +#define snp_enable_reg_snp2_int_en BIT(3) + +#define snp_status_reg 0x154 +#define snp_status_reg_snp1_int BIT(0) +#define snp_status_reg_snp2_int BIT(1) + +#define snp_addr_reg 0x158 +#define snp_addr_reg_snp1_addr GENMASK(15, 0) +#define snp_addr_reg_snp1_shift 0 +#define snp_addr_reg_snp2_addr GENMASK(31, 16) +#define snp_addr_reg_snp2_shift 16 + +#define snp_data_reg 0x15c +#define snp_data_reg_snp1_data_reg GENMASK(7, 0) +#define snp_data_reg_snp1_shift 0 +#define snp_data_reg_snp2_data_reg GENMASK(15, 8) +#define snp_data_reg_snp2_shift 8 + +struct phytium_lpc_snoop_channel { + struct kfifo fifo; + wait_queue_head_t wq; + struct miscdevice miscdev; +}; + +struct phytium_lpc_snoop { + struct regmap *regmap; + int irq; + struct phytium_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS]; +}; + +static struct phytium_lpc_snoop_channel *snoop_file_to_chan(struct file *file) +{ + return container_of(file->private_data, + struct phytium_lpc_snoop_channel, + miscdev); +} + +static ssize_t snoop_file_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct phytium_lpc_snoop_channel *chan = snoop_file_to_chan(file); + unsigned int copied; + int ret = 0; + + if (kfifo_is_empty(&chan->fifo)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + ret = wait_event_interruptible(chan->wq, + !kfifo_is_empty(&chan->fifo)); + if (ret == -ERESTARTSYS) + return -EINTR; + } + ret = kfifo_to_user(&chan->fifo, buffer, count, &copied); + + return ret ? ret : copied; +} + +static unsigned int snoop_file_poll(struct file *file, + struct poll_table_struct *pt) +{ + struct phytium_lpc_snoop_channel *chan = snoop_file_to_chan(file); + + poll_wait(file, &chan->wq, pt); + return !kfifo_is_empty(&chan->fifo) ? POLLIN : 0; +} + +static const struct file_operations snoop_fops = { + .owner = THIS_MODULE, + .read = snoop_file_read, + .poll = snoop_file_poll, + .llseek = noop_llseek, +}; + +/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */ +static void put_fifo_with_discard(struct phytium_lpc_snoop_channel *chan, u8 val) +{ + if (!kfifo_initialized(&chan->fifo)) + return; + if (kfifo_is_full(&chan->fifo)) + kfifo_skip(&chan->fifo); + kfifo_put(&chan->fifo, val); + wake_up_interruptible(&chan->wq); +} + +static irqreturn_t phytium_lpc_snoop_irq(int irq, void *arg) +{ + struct phytium_lpc_snoop *lpc_snoop = arg; + u32 reg, data; + + if (regmap_read(lpc_snoop->regmap, snp_status_reg, ®)) + return IRQ_NONE; + + /* Check if one of the snoop channels is interrupting */ + reg &= (snp_status_reg_snp1_int | snp_status_reg_snp2_int); + if (!reg) + return IRQ_NONE; + + /* Ack pending IRQs */ + regmap_write(lpc_snoop->regmap, snp_status_reg, reg); + + /* Read and save most recent snoop'ed data byte to FIFO */ + regmap_read(lpc_snoop->regmap, snp_data_reg, &data); + + if (reg & snp_status_reg_snp1_int) { + u8 val = (data & snp_data_reg_snp1_data_reg) >> snp_data_reg_snp1_shift; + + put_fifo_with_discard(&lpc_snoop->chan[0], val); + } + if (reg & snp_status_reg_snp2_int) { + u8 val = (data & snp_data_reg_snp2_data_reg) >> snp_data_reg_snp2_shift; + + put_fifo_with_discard(&lpc_snoop->chan[1], val); + } + + return IRQ_HANDLED; +} + +static int phytium_lpc_snoop_config_irq(struct phytium_lpc_snoop *lpc_snoop, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc; + + lpc_snoop->irq = platform_get_irq(pdev, 0); + if (!lpc_snoop->irq) + return -ENODEV; + + rc = devm_request_irq(dev, lpc_snoop->irq, + phytium_lpc_snoop_irq, IRQF_SHARED, + DEVICE_NAME, lpc_snoop); + if (rc < 0) { + dev_warn(dev, "Unable to request IRQ %d\n", lpc_snoop->irq); + lpc_snoop->irq = 0; + return rc; + } + + return 0; +} + +static int phytium_lpc_enable_snoop(struct phytium_lpc_snoop *lpc_snoop, + struct device *dev, + int channel, u16 lpc_port) +{ + int rc = 0; + u32 snp_enable_reg_en, snp_addr_reg_mask, snp_addr_reg_shift; + init_waitqueue_head(&lpc_snoop->chan[channel].wq); + /* Create FIFO datastructure */ + rc = kfifo_alloc(&lpc_snoop->chan[channel].fifo, + SNOOP_FIFO_SIZE, GFP_KERNEL); + if (rc) + return rc; + + lpc_snoop->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR; + lpc_snoop->chan[channel].miscdev.name = + devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel); + lpc_snoop->chan[channel].miscdev.fops = &snoop_fops; + lpc_snoop->chan[channel].miscdev.parent = dev; + rc = misc_register(&lpc_snoop->chan[channel].miscdev); + if (rc) + return rc; + + /* Enable LPC snoop channel at requested port */ + switch (channel) { + case 0: + snp_enable_reg_en = snp_enable_reg_snp1_en | snp_enable_reg_snp1_int_en; + snp_addr_reg_mask = snp_addr_reg_snp1_addr; + snp_addr_reg_shift = snp_addr_reg_snp1_shift; + break; + case 1: + snp_enable_reg_en = snp_enable_reg_snp2_en | snp_enable_reg_snp2_int_en; + snp_addr_reg_mask = snp_addr_reg_snp2_addr; + snp_addr_reg_shift = snp_addr_reg_snp2_shift; + break; + default: + return -EINVAL; + } + + regmap_update_bits(lpc_snoop->regmap, snp_enable_reg, snp_enable_reg_en, snp_enable_reg_en); + regmap_update_bits(lpc_snoop->regmap, snp_addr_reg, snp_addr_reg_mask, + lpc_port << snp_addr_reg_shift); + return rc; +} + +static void phytium_lpc_disable_snoop(struct phytium_lpc_snoop *lpc_snoop, + int channel) +{ + switch (channel) { + case 0: + regmap_update_bits(lpc_snoop->regmap, snp_enable_reg, + snp_enable_reg_snp1_en | snp_enable_reg_snp1_int_en, + 0); + break; + case 1: + regmap_update_bits(lpc_snoop->regmap, snp_enable_reg, + snp_enable_reg_snp2_en | snp_enable_reg_snp2_int_en, + 0); + break; + default: + return; + } + + kfifo_free(&lpc_snoop->chan[channel].fifo); + misc_deregister(&lpc_snoop->chan[channel].miscdev); +} + +static int phytium_lpc_snoop_probe(struct platform_device *pdev) +{ + struct phytium_lpc_snoop *lpc_snoop; + struct device *dev; + u32 port; + int rc; + + dev = &pdev->dev; + + lpc_snoop = devm_kzalloc(dev, sizeof(*lpc_snoop), GFP_KERNEL); + if (!lpc_snoop) + return -ENOMEM; + + lpc_snoop->regmap = syscon_node_to_regmap( + pdev->dev.parent->of_node); + if (IS_ERR(lpc_snoop->regmap)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + + dev_set_drvdata(&pdev->dev, lpc_snoop); + + rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port); + if (rc) { + dev_err(dev, "no snoop ports configured\n"); + return -ENODEV; + } + + rc = phytium_lpc_snoop_config_irq(lpc_snoop, pdev); + if (rc) + return rc; + + rc = phytium_lpc_enable_snoop(lpc_snoop, dev, 0, port); + if (rc) + return rc; + + /* Configuration of 2nd snoop channel port is optional */ + if (of_property_read_u32_index(dev->of_node, "snoop-ports", + 1, &port) == 0) { + rc = phytium_lpc_enable_snoop(lpc_snoop, dev, 1, port); + if (rc) + phytium_lpc_disable_snoop(lpc_snoop, 0); + } + + return rc; +} + +static int phytium_lpc_snoop_remove(struct platform_device *pdev) +{ + struct phytium_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev); + + /* Disable both snoop channels */ + phytium_lpc_disable_snoop(lpc_snoop, 0); + phytium_lpc_disable_snoop(lpc_snoop, 1); + + return 0; +} + + +static const struct of_device_id phytium_lpc_snoop_match[] = { + { .compatible = "phytium,pe2201-lpc-snoop"}, + { }, + }; + +static struct platform_driver phytium_lpc_snoop_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = phytium_lpc_snoop_match, + }, + .probe = phytium_lpc_snoop_probe, + .remove = phytium_lpc_snoop_remove, +}; + +module_platform_driver(phytium_lpc_snoop_driver); + +MODULE_DEVICE_TABLE(of, phytium_lpc_snoop_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lan Hengyu lanhengyu1395@phytium.com.cn"); +MODULE_DESCRIPTION("PE2201 driver to control Phytium LPC snoop functionality"); diff --git a/drivers/mtd/spi-nor/phytium-quadspi.c b/drivers/mtd/spi-nor/phytium-quadspi.c index 6a415c2ed1ef736656a5f6ba1751a09af78f8273..847c263780d141caca7464e5a090a7578663aff2 100644 --- a/drivers/mtd/spi-nor/phytium-quadspi.c +++ b/drivers/mtd/spi-nor/phytium-quadspi.c @@ -25,15 +25,15 @@ #define QSPI_FLASH_CAP_REG 0x000 #define QSPI_RD_CFG_REG 0x004 #define QSPI_WR_CFG_REG 0x008 -#define QSPI_FLUSH_REG 0x00C +#define QSPI_FLUSH_REG 0x00C #define QSPI_CMD_PORT_REG 0x010 #define QSPI_ADDR_PORT_REG 0x014 #define QSPI_HD_PORT_REG 0x018 #define QSPI_LD_PORT_REG 0x01C #define QSPI_FUN_SET_REG 0x020 -#define QSPI_WIP_REG 0x024 -#define QSPI_WP_REG 0x028 -#define QSPI_MODE_REG 0x02C +#define QSPI_WIP_REG 0x024 +#define QSPI_WP_REG 0x028 +#define QSPI_MODE_REG 0x02C #define QSPI_FLASH_CAP_NUM_SHIFT 3 #define QSPI_FLASH_CAP_NUM_MASK (0x3 << QSPI_FLASH_CAP_NUM_SHIFT) @@ -142,12 +142,15 @@ #define PHYTIUM_QSPI_FIFO_TIMEOUT_US 50000 #define PHYTIUM_QSPI_BUSY_TIMEOUT_US 100000 -#define PHYTIUM_SCK_SEL 0x05 +#define PHYTIUM_SCK_SEL_MIN 0x03 /*Max frequency 37.5MHZ i.e. 8 div*/ +#define PHYTIUM_SCK_SEL 0x05 /*Default frequency 9.385MHZ i.e. 32 div*/ #define PHYTIUM_CMD_SCK_SEL 0x07 #define PHYTIUM_FMODE_MM 0x01 #define PHYTIUM_FMODE_IN 0x02 +#define WR_CFG_NODIRMAP_VALUE 0x5000000 + /* * the codes of the different commands */ @@ -156,28 +159,28 @@ #define CMD_RDSR 0x05 #define CMD_WREN 0x06 #define CMD_RDAR 0x65 -#define CMD_P4E 0x20 -#define CMD_4P4E 0x21 +#define CMD_P4E 0x20 +#define CMD_4P4E 0x21 #define CMD_BE 0x60 -#define CMD_4BE 0xC7 -#define CMD_READ 0x03 -#define CMD_FAST_READ 0x0B -#define CMD_QOR 0x6B -#define CMD_QIOR 0xEB -#define CMD_DDRFR 0x0D -#define CMD_DDRQIOQ 0xED -#define CMD_PP 0x02 -#define CMD_QPP 0x32 -#define CMD_SE 0xD8 -#define CMD_4FAST_READ 0x0C -#define CMD_4READ 0x13 -#define CMD_4QOR 0x6C -#define CMD_4QIOR 0xEC -#define CMD_4DDRFR 0x0E -#define CMD_4DDRQIOR 0xEE -#define CMD_4PP 0x12 -#define CMD_4QPP 0x34 -#define CMD_4SE 0xDC +#define CMD_4BE 0xC7 +#define CMD_READ 0x03 +#define CMD_FAST_READ 0x0B +#define CMD_QOR 0x6B +#define CMD_QIOR 0xEB +#define CMD_DDRFR 0x0D +#define CMD_DDRQIOQ 0xED +#define CMD_PP 0x02 +#define CMD_QPP 0x32 +#define CMD_SE 0xD8 +#define CMD_4FAST_READ 0x0C +#define CMD_4READ 0x13 +#define CMD_4QOR 0x6C +#define CMD_4QIOR 0xEC +#define CMD_4DDRFR 0x0E +#define CMD_4DDRQIOR 0xEE +#define CMD_4PP 0x12 +#define CMD_4QPP 0x34 +#define CMD_4SE 0xDC #define PHYTIUM_QSPI_1_1_1 0 #define PHYTIUM_QSPI_1_1_2 1 @@ -308,7 +311,7 @@ static int phytium_qspi_write_enable(struct phytium_qspi *qspi, u32 cmd = 0; cmd = CMD_WREN << QSPI_CMD_PORT_CMD_SHIFT; - cmd |= PHYTIUM_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->clk_div << QSPI_CMD_PORT_SCK_SEL_SHIFT; cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); @@ -323,7 +326,7 @@ static int phytium_qspi_write_disable(struct phytium_qspi *qspi, u32 cmd = 0; cmd = CMD_WRDI << QSPI_CMD_PORT_CMD_SHIFT; - cmd |= PHYTIUM_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->clk_div << QSPI_CMD_PORT_SCK_SEL_SHIFT; cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); @@ -365,7 +368,7 @@ static int phytium_qspi_read_flash_sfdp(struct phytium_qspi *qspi, cmd |= BIT(QSPI_CMD_PORT_DATA_TRANSFER_SHIFT); cmd |= BIT(QSPI_CMD_PORT_P_BUFFER_SHIFT); cmd |= BIT(QSPI_CMD_PORT_CMD_ADDR_SHIFT); - cmd |= PHYTIUM_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->clk_div << QSPI_CMD_PORT_SCK_SEL_SHIFT; cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); @@ -480,6 +483,46 @@ static int phytium_qspi_write_reg(struct spi_nor *nor, u8 opcode, return 0; } +static int phytium_qspi_get_transfer(enum spi_nor_protocol proto) +{ + int transfer = 0; + /* QSPI protocol */ + switch (proto) { + case SNOR_PROTO_1_1_1: + transfer = PHYTIUM_QSPI_1_1_1; + break; + + case SNOR_PROTO_1_1_2: + transfer = PHYTIUM_QSPI_1_1_2; + break; + + case SNOR_PROTO_1_1_4: + transfer = PHYTIUM_QSPI_1_1_4; + break; + + case SNOR_PROTO_1_2_2: + transfer = PHYTIUM_QSPI_1_2_2; + break; + + case SNOR_PROTO_1_4_4: + transfer = PHYTIUM_QSPI_1_4_4; + break; + + case SNOR_PROTO_2_2_2: + transfer = PHYTIUM_QSPI_2_2_2; + break; + + case SNOR_PROTO_4_4_4: + transfer = PHYTIUM_QSPI_4_4_4; + break; + + default: + break; + } + + return transfer; +} + static ssize_t phytium_qspi_read_tmp(struct phytium_qspi *qspi, u32 read_cmd, loff_t from, size_t len, u_char *buf) { @@ -507,6 +550,7 @@ static ssize_t phytium_qspi_read(struct spi_nor *nor, loff_t from, size_t len, struct phytium_qspi *qspi = flash->qspi; u32 cmd = nor->read_opcode; u32 addr = (u32)from; + u32 transfer = PHYTIUM_QSPI_1_1_1; addr = addr + flash->cs * flash->fsize; dev_dbg(qspi->dev, "read(%#.2x): buf:%pK from:%#.8x len:%#zx\n", @@ -517,7 +561,8 @@ static ssize_t phytium_qspi_read(struct spi_nor *nor, loff_t from, size_t len, cmd |= flash->clk_div << QSPI_CMD_PORT_SCK_SEL_SHIFT; cmd &= ~QSPI_RD_CFG_RD_TRANSFER_MASK; - cmd |= (flash->addr_width << QSPI_RD_CFG_RD_TRANSFER_SHIFT); + transfer = phytium_qspi_get_transfer(nor->read_proto); + cmd |= transfer << QSPI_RD_CFG_RD_TRANSFER_SHIFT; switch (nor->read_opcode) { case CMD_READ: @@ -559,7 +604,7 @@ static ssize_t phytium_qspi_read(struct spi_nor *nor, loff_t from, size_t len, return len; } -static ssize_t phytium_qspi_write(struct spi_nor *nor, loff_t to, size_t len, +static ssize_t phytium_qspi_dir_write(struct spi_nor *nor, loff_t to, size_t len, const u_char *buf) { struct phytium_qspi_flash *flash = nor->priv; @@ -567,6 +612,7 @@ static ssize_t phytium_qspi_write(struct spi_nor *nor, loff_t to, size_t len, struct phytium_qspi *qspi = flash->qspi; u32 cmd = nor->program_opcode; u32 addr = (u32)to; + u32 transfer = PHYTIUM_QSPI_1_1_1; int i; u_char tmp[8] = {0}; size_t mask = 0x03; @@ -584,6 +630,10 @@ static ssize_t phytium_qspi_write(struct spi_nor *nor, loff_t to, size_t len, cmd |= BIT(QSPI_WR_CFG_WR_MODE_SHIFT); cmd |= PHYTIUM_CMD_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd &= ~QSPI_WR_CFG_WR_TRANSFER_MASK; + transfer = phytium_qspi_get_transfer(nor->write_proto); + cmd |= transfer << QSPI_WR_CFG_WR_TRANSFER_SHIFT; + switch (nor->program_opcode) { case CMD_PP: case CMD_QPP: @@ -619,6 +669,93 @@ static ssize_t phytium_qspi_write(struct spi_nor *nor, loff_t to, size_t len, return len; } +static ssize_t phytium_qspi_nodir_write(struct spi_nor *nor, loff_t to, size_t len, + const u_char *buf) +{ + struct phytium_qspi_flash *flash = nor->priv; + struct device *dev = flash->qspi->dev; + struct phytium_qspi *qspi = flash->qspi; + u32 cmd = nor->program_opcode; + u32 addr = (u32)to; + u32 transfer = PHYTIUM_QSPI_1_1_1; + int i; + u_char tmp[8] = {0}; + size_t mask = 0x03; + size_t mask_p = 0x07; + u8 len_p; + u32 cnt = 0; + u32 val = 0; + + if (addr & 0x03) { + dev_err(dev, "Addr not four-byte aligned!\n"); + return -EINVAL; + } + + cmd = 0; + cmd |= nor->program_opcode << QSPI_CMD_PORT_CMD_SHIFT; + cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; + cmd |= BIT(QSPI_CMD_PORT_CMD_ADDR_SHIFT); + cmd |= BIT(QSPI_CMD_PORT_DATA_TRANSFER_SHIFT); + + cmd &= ~QSPI_CMD_PORT_TRANSFER_MASK; + transfer = phytium_qspi_get_transfer(nor->write_proto); + cmd |= transfer << QSPI_CMD_PORT_TRANSFER_SHIFT; + + switch (nor->addr_width) { + case 3: + cmd &= ~QSPI_CMD_PORT_SEL_MASK; + break; + case 4: + cmd |= BIT(QSPI_CMD_PORT_SEL_SHIFT); + break; + default: + dev_err(qspi->dev, "Not support addr_width:%#x\n", + nor->addr_width); + return -EINVAL; + } + + cmd |= flash->clk_div & QSPI_CMD_PORT_SCK_SEL_MASK; + cmd |= 0x07 << QSPI_CMD_PORT_RW_NUM_SHIFT; + + for (i = 0; i < len / 8; i++) { + val = readl_relaxed(qspi->io_base + QSPI_WR_CFG_REG); + phytium_qspi_write_enable(qspi,flash); + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + writel_relaxed(addr, qspi->io_base + QSPI_ADDR_PORT_REG); + writel_relaxed(*(u32 *)(buf + 4 + 8 * i),qspi->io_base + QSPI_HD_PORT_REG); + writel_relaxed(*(u32 *)(buf + 8 * i),qspi->io_base + QSPI_LD_PORT_REG); + phytium_qspi_wait_cmd(qspi, flash); + phytium_qspi_write_disable(qspi,flash); + addr += 8; + } + + len_p = (u8)(len & mask_p); + if (len_p) { + val = readl_relaxed(qspi->io_base + QSPI_WR_CFG_REG); + phytium_qspi_write_enable(qspi,flash); + cmd &= ~(0x07 << QSPI_CMD_PORT_RW_NUM_SHIFT); + cmd |= (len_p - 1) << QSPI_CMD_PORT_RW_NUM_SHIFT; + writel_relaxed(cmd, qspi->io_base + QSPI_CMD_PORT_REG); + writel_relaxed(addr, qspi->io_base + QSPI_ADDR_PORT_REG); + if ((len_p - 4) > 0) { + memcpy(tmp, buf + 4 + (len / 8) * 8, len_p - 4); + writel_relaxed(*(u32 *)(tmp),qspi->io_base + QSPI_HD_PORT_REG); + memcpy(tmp, buf + (len / 8) * 8, 4); + writel_relaxed(*(u32 *)(tmp),qspi->io_base + QSPI_LD_PORT_REG); + } else if (len_p == 4) { + memcpy(tmp, buf + (len / 8) * 8, 4); + writel_relaxed(*(u32 *)(tmp),qspi->io_base + QSPI_LD_PORT_REG); + } else { + memcpy(tmp, buf + (len / 8) * 8, len & mask); + writel_relaxed(*(u32 *)(tmp),qspi->io_base + QSPI_LD_PORT_REG); + } + } + writel_relaxed(0x05000000, qspi->io_base + QSPI_WR_CFG_REG); + + phytium_qspi_wait_cmd(qspi, flash); + return len; +} + static int phytium_qspi_erase(struct spi_nor *nor, loff_t offs) { struct phytium_qspi_flash *flash = nor->priv; @@ -631,7 +768,7 @@ static int phytium_qspi_erase(struct spi_nor *nor, loff_t offs) phytium_qspi_write_enable(qspi, flash); cmd = cmd << QSPI_CMD_PORT_CMD_SHIFT; - cmd |= PHYTIUM_SCK_SEL << QSPI_CMD_PORT_SCK_SEL_SHIFT; + cmd |= flash->clk_div << QSPI_CMD_PORT_SCK_SEL_SHIFT; cmd |= flash->cs << QSPI_CMD_PORT_CS_SHIFT; /* s25fl256s1 not supoort D8, DC, 20, 21 */ @@ -748,6 +885,7 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, struct phytium_qspi_flash *flash; struct mtd_info *mtd; int ret; + bool dirmap_write = false; of_property_read_u32(np, "reg", &cs_num); if (cs_num >= PHYTIUM_MAX_NORCHIP) @@ -761,8 +899,12 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, if (!clk_div) clk_div = PHYTIUM_SCK_SEL; - if (clk_div < 4) - return -EINVAL; + if (clk_div < PHYTIUM_SCK_SEL_MIN) { + clk_div = PHYTIUM_SCK_SEL_MIN; + dev_warn(qspi->dev, + "spi-clk-div too small, use default value: %d .\n", + clk_div); + } presc = DIV_ROUND_UP(qspi->clk_rate, max_rate) - 1; @@ -779,6 +921,21 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, } else if (width != 1) return -EINVAL; + // add tx hwcaps + of_property_read_u32(np, "spi-tx-bus-width", &width); + if (!width) + width = 1; + + if (width == 4) { + hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4 | SNOR_HWCAPS_PP_1_4_4 | + SNOR_HWCAPS_PP_4_4_4; + } + + if (of_property_read_bool(np, "dirmap-write")) { + dev_warn(qspi->dev, "direct write mode!"); + dirmap_write = true; + } + flash = &qspi->flash[cs_num]; flash->qspi = qspi; flash->cs = cs_num; @@ -792,7 +949,10 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, mtd = &flash->nor.mtd; flash->nor.read = phytium_qspi_read; - flash->nor.write = phytium_qspi_write; + if (dirmap_write) + flash->nor.write = phytium_qspi_dir_write; + else + flash->nor.write = phytium_qspi_nodir_write; flash->nor.erase = phytium_qspi_erase; flash->nor.read_reg = phytium_qspi_read_reg; flash->nor.write_reg = phytium_qspi_write_reg; @@ -816,7 +976,10 @@ static int phytium_qspi_flash_setup(struct phytium_qspi *qspi, flash_cap = cs_num << QSPI_FLASH_CAP_NUM_SHIFT; flash_cap |= ret; + writel_relaxed(flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG); + if (!dirmap_write) + writel_relaxed(WR_CFG_NODIRMAP_VALUE, qspi->io_base + QSPI_WR_CFG_REG); flash->read_mode = PHYTIUM_FMODE_MM; diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 590d5212cc72e4b8e4349283ee57408a0972145e..a6e1339f477defb36f7586519eba28b9f6c799a1 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -89,6 +89,7 @@ struct flash_info { #define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */ #define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */ #define USE_CLSR BIT(14) /* use CLSR command */ +#define SPI_NOR_QUAD_WRITE BIT(15) /* Flash supports Quad Write */ int (*quad_enable)(struct spi_nor *nor); }; @@ -1054,6 +1055,11 @@ static const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { + "gd25lq128d", INFO(0xc86018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, { "gd25q256", INFO(0xc84019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | @@ -1063,25 +1069,29 @@ static const struct flash_info spi_nor_ids[] = { { "gd25q512", INFO(0xc84020, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | + SPI_NOR_QUAD_WRITE) .quad_enable = macronix_quad_enable, }, { "gd25b512me", INFO(0xc8471a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | + SPI_NOR_QUAD_WRITE) .quad_enable = macronix_quad_enable, }, { "gd55b01ge", INFO(0xc8471b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | + SPI_NOR_QUAD_WRITE) .quad_enable = macronix_quad_enable, }, { "gd25lb512me", INFO(0xc8671a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | - SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + SPI_NOR_4B_OPCODES | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | + SPI_NOR_QUAD_WRITE) .quad_enable = macronix_quad_enable, }, { @@ -1278,18 +1288,26 @@ static const struct flash_info spi_nor_ids[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, + { + "w25q128jv", INFO(0xef7018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "w25m512jvq", INFO(0xef4020, 0, 64 * 1024, 1024, - SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ | SPI_NOR_4B_OPCODES) + SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ | SPI_NOR_4B_OPCODES | + SPI_NOR_QUAD_WRITE) }, { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024, - SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ | SPI_NOR_4B_OPCODES) + SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ | SPI_NOR_4B_OPCODES | + SPI_NOR_QUAD_WRITE) }, { "w25m512jvsfiq", INFO(0xef4021, 0, 64 * 1024, 2048, - SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ | SPI_NOR_4B_OPCODES) + SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ | SPI_NOR_4B_OPCODES | + SPI_NOR_QUAD_WRITE) }, /* Catalyst / On Semiconductor -- non-JEDEC */ @@ -2542,6 +2560,12 @@ static int spi_nor_init_params(struct spi_nor *nor, spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP], SPINOR_OP_PP, SNOR_PROTO_1_1_1); + if (info->flags & SPI_NOR_QUAD_WRITE) { + params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4; + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_1_1_4], + SPINOR_OP_PP_1_1_4, SNOR_PROTO_1_1_4); + } + /* Select the procedure to set the Quad Enable bit. */ if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD | SNOR_HWCAPS_PP_QUAD)) { diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 570d2ddf47dcc39477a6663cf950fb2e1bc36822..6e24eaaca6c76885255bbc63deadf096023bd43a 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -14,6 +14,7 @@ #include #include #include +#include #if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) || defined(CONFIG_MACB_USE_HWSTAMP) #define MACB_EXT_DESC @@ -85,6 +86,7 @@ #define GEM_DMACFG 0x0010 /* DMA Configuration */ #define GEM_JML 0x0048 /* Jumbo Max Length */ #define GEM_HS_MAC_CONFIG 0x0050 /* GEM high speed config */ +#define GEM_AXI_PIPE 0x0054 /* Axi max pipeline register*/ #define GEM_HRB 0x0080 /* Hash Bottom */ #define GEM_HRT 0x0084 /* Hash Top */ #define GEM_SA1B 0x0088 /* Specific1 Bottom */ @@ -160,6 +162,7 @@ #define GEM_PEFTN 0x01f4 /* PTP Peer Event Frame Tx Ns */ #define GEM_PEFRSL 0x01f8 /* PTP Peer Event Frame Rx Sec Low */ #define GEM_PEFRN 0x01fc /* PTP Peer Event Frame Rx Ns */ +#define GEM_PCSCTRL 0x0200 /* PCS control register*/ #define GEM_DCFG1 0x0280 /* Design Config 1 */ #define GEM_DCFG2 0x0284 /* Design Config 2 */ #define GEM_DCFG3 0x0288 /* Design Config 3 */ @@ -264,10 +267,6 @@ #define MACB_2PT5G_OFFSET 29 /* 2.5G operation selected */ #define MACB_2PT5G_SIZE 1 -/* GEM specific NCR bitfields. */ -#define GEM_ENABLE_HS_MAC_OFFSET 31 -#define GEM_ENABLE_HS_MAC_SIZE 1 - /* Bitfields in NCFGR */ #define MACB_SPD_OFFSET 0 /* Speed */ #define MACB_SPD_SIZE 1 @@ -308,6 +307,10 @@ #define MACB_IRXFCS_OFFSET 19 #define MACB_IRXFCS_SIZE 1 +/* GEM specific NCR bitfields. */ +#define GEM_ENABLE_HS_MAC_OFFSET 31 /* Use high speed MAC */ +#define GEM_ENABLE_HS_MAC_SIZE 1 + /* GEM specific NCFGR bitfields. */ #define GEM_GBE_OFFSET 10 /* Gigabit mode enable */ #define GEM_GBE_SIZE 1 @@ -497,6 +500,12 @@ #define GEM_HS_MAC_SPEED_OFFSET 0 #define GEM_HS_MAC_SPEED_SIZE 3 +/* Bitfields in PCSCNTRL */ +#define GEM_PCSAUTONEG_OFFSET 12 +#define GEM_PCSAUTONEG_SIZE 1 +#define GEM_PCS_RESET_OFFSET 15 +#define GEM_PCS_RESET_SIZE 1 + /* Bitfields in DCFG1. */ #define GEM_IRQCOR_OFFSET 23 #define GEM_IRQCOR_SIZE 1 @@ -705,11 +714,11 @@ #define MACB_CAPS_GEM_HAS_PTP 0x00000040 #define MACB_CAPS_BD_RD_PREFETCH 0x00000080 #define MACB_CAPS_NEEDS_RSTONUBR 0x00000100 +#define MACB_CAPS_SEL_CLK 0x00000800 #define MACB_CAPS_FIFO_MODE 0x10000000 #define MACB_CAPS_GIGABIT_MODE_AVAILABLE 0x20000000 #define MACB_CAPS_SG_DISABLED 0x40000000 #define MACB_CAPS_MACB_IS_GEM 0x80000000 -#define MACB_CAPS_SEL_CLK_HW 0x00001000 /* LSO settings */ #define MACB_LSO_UFO_ENABLE 0x01 @@ -1140,6 +1149,11 @@ struct macb_ptp_info { struct ifreq *ifr, int cmd); }; +struct macb_pm_data { + u32 scrt2; + u32 usrio; +}; + struct macb_config { u32 caps; unsigned int dma_burst_length; @@ -1148,6 +1162,7 @@ struct macb_config { struct clk **rx_clk, struct clk **tsu_clk); int (*init)(struct platform_device *pdev); int jumbo_max_len; + void (*sel_clk_hw)(struct macb *bp); }; struct tsu_incr { @@ -1282,6 +1297,17 @@ struct macb { int tx_bd_rd_prefetch; u32 rx_intr_mask; + struct macb_pm_data pm_data; + + /* phytium sel clk */ + void (*sel_clk_hw)(struct macb *bp); + + void *phy_vbase_addr; + unsigned long crc_detect_period; /* default period: 0 ms; disable crc detect function */ + unsigned long nb_rx; /* CRC detection starts when more than nb_rx packets are received */ + unsigned long crc_err_percent; /* PHY RESET when crc error more than crc_err_percent */ + unsigned long phy_reset_times; + struct task_struct *poll_task; }; #ifdef CONFIG_MACB_USE_HWSTAMP diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 0f18b5771ca0674635d9a301bbe6f50b560a47df..ac789926264a76bedaf36bff7fd4df6deb670ebf 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -38,13 +38,19 @@ #include #include #include +#include +#include +#include #include "macb.h" +#define MAX_RING_ADDR_ALLOC_TIMES 3 +#define RING_ADDR_INTERVAL 128 + #define MACB_RX_BUFFER_SIZE 128 #define RX_BUFFER_MULTIPLE 64 /* bytes */ #define DEFAULT_RX_RING_SIZE 512 /* must be power of 2 */ -#define MIN_RX_RING_SIZE 64 +#define MIN_RX_RING_SIZE 128 #define MAX_RX_RING_SIZE 8192 #define RX_RING_BYTES(bp) (macb_dma_desc_get_size(bp) \ * (bp)->rx_ring_size) @@ -93,6 +99,193 @@ */ #define MACB_HALT_TIMEOUT 1230 +#define DEFAULT_SLEEP_S 1 +#define DEFAULT_CHECK_FCS_PERIOD_MS 2000 +#define DEFAULT_CHECK_FCS_RX_THRESHOLD 2 +#define DEFAULT_CHECK_FCS_PERCENT_THRESHOLD 1 + +#define PHY0_ADDR_BASE 0x32140000 +#define PHY0_ADDR_OFFSET 0x254 +#define PHY1_ADDR_BASE 0x32240000 +#define PHY1_ADDR_OFFSET 0x08c +#define PHY2_ADDR_BASE 0x32340000 +#define PHY2_ADDR_OFFSET 0x254 +#define PHY3_ADDR_BASE 0x32440000 +#define PHY3_ADDR_OFFSET 0x254 + +#define MACB0_ADDR_BASE 0x3200c000 +#define MACB1_ADDR_BASE 0x3200e000 +#define MACB2_ADDR_BASE 0x32010000 +#define MACB3_ADDR_BASE 0x32012000 + +#define MAP_SIZE 4096 +#define MACB_POLL_VERSION "1.0" + +static DEFINE_MUTEX(stats_mutex); +static int crc_detect_poll(void *args); +static int __maybe_unused macb_suspend(struct device *dev); +static int __maybe_unused macb_resume(struct device *dev); + +static ssize_t +check_fcs_period_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + + return snprintf(buf, 10, "%lu\n", bp->crc_detect_period); +} + +static ssize_t +check_fcs_period_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = 0; + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + unsigned long crc_detect_period; + + if (kstrtoul(buf, 0, &crc_detect_period) != 0) + return -EINVAL; + + if (crc_detect_period < 0) + return -EINVAL; + bp->crc_detect_period = crc_detect_period; + + return err ? err : count; +} +static DEVICE_ATTR_RW(check_fcs_period); + +static ssize_t +check_fcs_rx_threshold_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + + return snprintf(buf, 10, "%lu\n", bp->nb_rx); +} + +static ssize_t +check_fcs_rx_threshold_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = 0; + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + unsigned long nb_rx; + + if (kstrtoul(buf, 0, &nb_rx) != 0) + return -EINVAL; + + if (nb_rx < 0) + return -EINVAL; + bp->nb_rx = nb_rx; + + return err ? err : count; +} +static DEVICE_ATTR_RW(check_fcs_rx_threshold); + +static ssize_t +check_fcs_percent_threshold_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + + return snprintf(buf, 10, "%lu\n", bp->crc_err_percent); +} + +static ssize_t +check_fcs_percent_threshold_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = 0; + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + unsigned long crc_err_percent; + + if (kstrtoul(buf, 0, &crc_err_percent) != 0) + return -EINVAL; + + if (crc_err_percent > 100 || crc_err_percent < 0) + return -EINVAL; + bp->crc_err_percent = crc_err_percent; + + return err ? err : count; +} +static DEVICE_ATTR_RW(check_fcs_percent_threshold); + +static ssize_t +phy_reset_times_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *netdev = (struct net_device *)dev_get_drvdata(dev); + struct macb *bp = netdev_priv(netdev); + + return snprintf(buf, 10, "%lu\n", bp->phy_reset_times); +} +static DEVICE_ATTR_RO(phy_reset_times); + +static struct attribute *dev_attrs[] = { + &dev_attr_check_fcs_period.attr, + &dev_attr_check_fcs_rx_threshold.attr, + &dev_attr_check_fcs_percent_threshold.attr, + &dev_attr_phy_reset_times.attr, + NULL, +}; + +static const struct attribute_group dev_attr_grp = { + .attrs = dev_attrs, +}; + +static void *get_mmap_addr(unsigned long phy_addr) +{ + void *virt_addr; + + virt_addr = ioremap(phy_addr, MAP_SIZE); + + if (!virt_addr) { + pr_err("Failed to map physical address to virtual address\n"); + return NULL; + } + + return virt_addr; +} + +static void phy_base_mmap(struct net_device *dev) +{ + struct macb *bp = netdev_priv(dev); + void *base = NULL; + + switch (dev->base_addr) { + case MACB0_ADDR_BASE: + base = get_mmap_addr(PHY0_ADDR_BASE); + break; + case MACB1_ADDR_BASE: + base = get_mmap_addr(PHY1_ADDR_BASE); + break; + case MACB2_ADDR_BASE: + base = get_mmap_addr(PHY2_ADDR_BASE); + break; + case MACB3_ADDR_BASE: + base = get_mmap_addr(PHY3_ADDR_BASE); + break; + default: + break; + } + + bp->phy_vbase_addr = base; +} + +static void phy_base_unmap(struct net_device *dev) +{ + struct macb *bp = netdev_priv(dev); + + if (bp->phy_vbase_addr) + iounmap(bp->phy_vbase_addr); +} + /* DMA buffer descriptor might be different size * depends on hardware configuration: * @@ -415,7 +608,84 @@ static void macb_set_tx_clk(struct clk *clk, int speed, struct net_device *dev) netdev_err(dev, "adjusting tx_clk failed.\n"); } -static int phytium_gem_sel_clk(struct macb *bp) +static int phytium_mac_config(struct macb *bp) +{ + u32 old_ctrl, ctrl; + u32 old_ncr, ncr; + u32 pcsctrl; + + netdev_dbg(bp->dev, "phytium mac config"); + + old_ncr = macb_readl(bp, NCR); + ncr = old_ncr; + old_ctrl = macb_readl(bp, NCFGR); + ctrl = old_ctrl; + + ncr &= ~(GEM_BIT(ENABLE_HS_MAC) | MACB_BIT(2PT5G)); + ctrl &= ~(GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL) | MACB_BIT(SPD) | MACB_BIT(FD)); + + if (macb_is_gem(bp)) + ctrl &= ~GEM_BIT(GBE); + + if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + ctrl |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL) | GEM_BIT(GBE); + ncr |= MACB_BIT(2PT5G); + } else if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII) { + ctrl |= GEM_BIT(PCSSEL); + ncr |= GEM_BIT(ENABLE_HS_MAC); + } + + if (bp->duplex) + ctrl |= MACB_BIT(FD); + + /* Apply the new configuration, if any */ + if (old_ctrl ^ ctrl) + macb_or_gem_writel(bp, NCFGR, ctrl); + + if (old_ncr ^ ncr) + macb_or_gem_writel(bp, NCR, ncr); + + if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + pcsctrl = gem_readl(bp, PCSCTRL); + pcsctrl &= ~GEM_BIT(PCSAUTONEG); + gem_writel(bp, PCSCTRL, pcsctrl); + } + + return 0; +} + +static void phytium_usx_pcs_link_up(struct macb *bp) +{ + u32 config; + + if (bp->phy_interface != PHY_INTERFACE_MODE_USXGMII) + return; + + netdev_dbg(bp->dev, "phytium usx pcs link up"); + + config = gem_readl(bp, USX_CONTROL); + if (bp->speed == SPEED_10000) { + config = GEM_BFINS(SERDES_RATE, MACB_SERDES_RATE_10G, config); + config = GEM_BFINS(USX_CTRL_SPEED, HS_SPEED_10000M, config); + } else if (bp->speed == SPEED_5000) { + config = GEM_BFINS(SERDES_RATE, MACB_SERDES_RATE_5G, config); + config = GEM_BFINS(USX_CTRL_SPEED, HS_SPEED_5000M, config); + } + + /* reset */ + config &= ~(GEM_BIT(SIGNAL_OK) | GEM_BIT(TX_EN)); + config |= GEM_BIT(RX_SYNC_RESET); + + gem_writel(bp, USX_CONTROL, config); + + /* enable rx and tx */ + config &= ~(GEM_BIT(RX_SYNC_RESET)); + config |= GEM_BIT(SIGNAL_OK) | GEM_BIT(TX_EN); + + gem_writel(bp, USX_CONTROL, config); +} + +static void phytium_gem1p0_sel_clk(struct macb *bp) { int speed = 0; @@ -427,16 +697,9 @@ static int phytium_gem_sel_clk(struct macb *bp) gem_writel(bp, PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ speed = HS_SPEED_10000M; } - } else if (bp->phy_interface == PHY_INTERFACE_MODE_5GBASER) { - if (bp->speed == SPEED_5000) { - gem_writel(bp, SRC_SEL_LN, 0x1); /*0x1c04*/ - gem_writel(bp, DIV_SEL0_LN, 0x8); /*0x1c08*/ - gem_writel(bp, DIV_SEL1_LN, 0x2); /*0x1c0c*/ - gem_writel(bp, PMA_XCVR_POWER_STATE, 0x0); /*0x1c10*/ - speed = HS_SPEED_5000M; - } } else if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { if (bp->speed == SPEED_2500) { + gem_writel(bp, SRC_SEL_LN, 0x1); /*0x1c04*/ gem_writel(bp, DIV_SEL0_LN, 0x1); /*0x1c08*/ gem_writel(bp, DIV_SEL1_LN, 0x2); /*0x1c0c*/ gem_writel(bp, PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ @@ -453,7 +716,22 @@ static int phytium_gem_sel_clk(struct macb *bp) speed = HS_SPEED_2500M; } } else if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { - if (bp->speed == SPEED_1000) { + if (bp->speed == SPEED_2500) { + gem_writel(bp, DIV_SEL0_LN, 0x1); /*0x1c08*/ + gem_writel(bp, DIV_SEL1_LN, 0x2); /*0x1c0c*/ + gem_writel(bp, PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ + gem_writel(bp, TX_CLK_SEL0, 0x0); /*0x1c20*/ + gem_writel(bp, TX_CLK_SEL1, 0x1); /*0x1c24*/ + gem_writel(bp, TX_CLK_SEL2, 0x1); /*0x1c28*/ + gem_writel(bp, TX_CLK_SEL3, 0x1); /*0x1c2c*/ + gem_writel(bp, RX_CLK_SEL0, 0x1); /*0x1c30*/ + gem_writel(bp, RX_CLK_SEL1, 0x0); /*0x1c34*/ + gem_writel(bp, TX_CLK_SEL3_0, 0x0); /*0x1c70*/ + gem_writel(bp, TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + gem_writel(bp, RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + gem_writel(bp, RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ + speed = HS_SPEED_2500M; + } else if (bp->speed == SPEED_1000) { gem_writel(bp, DIV_SEL0_LN, 0x4); /*0x1c08*/ gem_writel(bp, DIV_SEL1_LN, 0x8); /*0x1c0c*/ gem_writel(bp, PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ @@ -536,8 +814,32 @@ static int phytium_gem_sel_clk(struct macb *bp) /*HS_MAC_CONFIG(0x0050) provide rate to the external*/ gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, speed, gem_readl(bp, HS_MAC_CONFIG))); +} - return 0; +static void phytium_gem2p0_sel_clk(struct macb *bp) +{ + if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { + if (bp->speed == SPEED_100 || bp->speed == SPEED_10) { + gem_writel(bp, SRC_SEL_LN, 0x1); /*0x1c04*/ + gem_writel(bp, DIV_SEL1_LN, 0x1); /*0x1c0c*/ + } + } + + if (bp->speed == SPEED_100 || bp->speed == SPEED_10) + gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_100M, + gem_readl(bp, HS_MAC_CONFIG))); + else if (bp->speed == SPEED_1000) + gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_1000M, + gem_readl(bp, HS_MAC_CONFIG))); + else if (bp->speed == SPEED_2500) + gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_2500M, + gem_readl(bp, HS_MAC_CONFIG))); + else if (bp->speed == SPEED_5000) + gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_5000M, + gem_readl(bp, HS_MAC_CONFIG))); + else if (bp->speed == SPEED_10000) + gem_writel(bp, HS_MAC_CONFIG, GEM_BFINS(HS_MAC_SPEED, HS_SPEED_10000M, + gem_readl(bp, HS_MAC_CONFIG))); } static void macb_handle_link_change(struct net_device *dev) @@ -546,6 +848,7 @@ static void macb_handle_link_change(struct net_device *dev) struct phy_device *phydev = dev->phydev; unsigned long flags; int err, status_change = 0; + u32 network_ctrl, pcsctrl, old_pcsctrl; spin_lock_irqsave(&bp->lock, flags); @@ -563,7 +866,7 @@ static void macb_handle_link_change(struct net_device *dev) reg |= MACB_BIT(FD); if (phydev->speed == SPEED_100) reg |= MACB_BIT(SPD); - if (phydev->speed == SPEED_1000 && + if ((phydev->speed == SPEED_1000 || phydev->speed == SPEED_2500) && bp->caps & MACB_CAPS_GIGABIT_MODE_AVAILABLE) reg |= GEM_BIT(GBE); @@ -572,6 +875,17 @@ static void macb_handle_link_change(struct net_device *dev) bp->speed = phydev->speed; bp->duplex = phydev->duplex; status_change = 1; + + if (bp->speed == SPEED_2500) { + network_ctrl = macb_readl(bp, NCR); + network_ctrl |= MACB_BIT(2PT5G); + macb_writel(bp, NCR, network_ctrl); + + old_pcsctrl = gem_readl(bp, PCSCTRL); + pcsctrl = old_pcsctrl & ~GEM_BIT(PCSAUTONEG); + if (old_pcsctrl != pcsctrl) + gem_writel(bp, PCSCTRL, pcsctrl); + } } } @@ -595,8 +909,8 @@ static void macb_handle_link_change(struct net_device *dev) macb_set_tx_clk(bp->tx_clk, phydev->speed, dev); /* phytium need hwclock */ - if (bp->caps & MACB_CAPS_SEL_CLK_HW) - phytium_gem_sel_clk(bp); + if ((bp->caps & MACB_CAPS_SEL_CLK) && bp->sel_clk_hw) + bp->sel_clk_hw(bp); netif_carrier_on(dev); netdev_info(dev, "link up (%d/%s)\n", @@ -1063,7 +1377,6 @@ static void gem_rx_refill(struct macb_queue *queue) /* Make hw descriptor updates visible to CPU */ rmb(); - queue->rx_prepared_head++; desc = macb_rx_desc(queue, entry); if (!queue->rx_skbuff[entry]) { @@ -1102,6 +1415,7 @@ static void gem_rx_refill(struct macb_queue *queue) dma_wmb(); desc->addr &= ~MACB_BIT(RX_USED); } + queue->rx_prepared_head++; } /* Make descriptor updates visible to hardware */ @@ -1173,6 +1487,15 @@ static int gem_rx(struct macb_queue *queue, int budget) queue->stats.rx_dropped++; break; } + + len = ctrl & bp->rx_frm_len_mask; + if (unlikely(len <= 0 || len > bp->rx_buffer_size)) { + netdev_err(bp->dev, "illegal skb len: %d\n", len); + bp->dev->stats.rx_dropped++; + queue->stats.rx_dropped++; + break; + } + skb = queue->rx_skbuff[entry]; if (unlikely(!skb)) { netdev_err(bp->dev, @@ -1183,7 +1506,6 @@ static int gem_rx(struct macb_queue *queue, int budget) } /* now everything is ready for receiving packet */ queue->rx_skbuff[entry] = NULL; - len = ctrl & bp->rx_frm_len_mask; netdev_vdbg(bp->dev, "gem_rx %u (len %u)\n", entry, len); @@ -2078,27 +2400,48 @@ static void macb_free_rx_buffers(struct macb *bp) static void macb_free_consistent(struct macb *bp) { + struct macb_dma_desc *tx_ring_base = NULL; + struct macb_dma_desc *rx_ring_base = NULL; + dma_addr_t tx_ring_base_addr; + dma_addr_t rx_ring_base_addr; struct macb_queue *queue; unsigned int q; int size; bp->macbgem_ops.mog_free_rx_buffers(bp); + queue = bp->queues; + if (queue->tx_ring) { + tx_ring_base = queue->tx_ring; + tx_ring_base_addr = queue->tx_ring_dma; + } + if (queue->rx_ring) { + rx_ring_base = queue->rx_ring; + rx_ring_base_addr = queue->rx_ring_dma; + } + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { kfree(queue->tx_skb); queue->tx_skb = NULL; - if (queue->tx_ring) { - size = TX_RING_BYTES(bp) + bp->tx_bd_rd_prefetch; - dma_free_coherent(&bp->pdev->dev, size, - queue->tx_ring, queue->tx_ring_dma); + if (queue->tx_ring) queue->tx_ring = NULL; - } - if (queue->rx_ring) { - size = RX_RING_BYTES(bp) + bp->rx_bd_rd_prefetch; - dma_free_coherent(&bp->pdev->dev, size, - queue->rx_ring, queue->rx_ring_dma); + if (queue->rx_ring) queue->rx_ring = NULL; - } + } + + if (tx_ring_base) { + size = bp->num_queues * (TX_RING_BYTES(bp) + + bp->tx_bd_rd_prefetch + + RING_ADDR_INTERVAL); + dma_free_coherent(&bp->pdev->dev, size, tx_ring_base, + tx_ring_base_addr); + } + if (rx_ring_base) { + size = bp->num_queues * (RX_RING_BYTES(bp) + + bp->rx_bd_rd_prefetch + + RING_ADDR_INTERVAL); + dma_free_coherent(&bp->pdev->dev, size, rx_ring_base, + rx_ring_base_addr); } } @@ -2138,17 +2481,87 @@ static int macb_alloc_rx_buffers(struct macb *bp) return 0; } +static int macb_queue_phyaddr_check(struct macb *bp, dma_addr_t ring_base_addr, + int offset) +{ + u32 bus_addr_high; + int i; + + bus_addr_high = upper_32_bits(ring_base_addr); + for (i = 1; i < bp->num_queues; i++) { + ring_base_addr += offset; + if (bus_addr_high != upper_32_bits(ring_base_addr)) + return -1; + } + + return 0; +} + static int macb_alloc_consistent(struct macb *bp) { + struct macb_dma_desc *tx_ring_base, *rx_ring_base; + dma_addr_t tx_ring_base_addr, rx_ring_base_addr; struct macb_queue *queue; + int tx_offset, rx_offset; + int tx_size, rx_size; unsigned int q; - int size; + int ret, i; + int size = 0; + + tx_offset = TX_RING_BYTES(bp) + bp->tx_bd_rd_prefetch + + RING_ADDR_INTERVAL; + tx_size = bp->num_queues * tx_offset; + for (i = 0; i < MAX_RING_ADDR_ALLOC_TIMES + 1; i++) { + if (i == MAX_RING_ADDR_ALLOC_TIMES) + return -ENOMEM; + + tx_ring_base = dma_alloc_coherent(&bp->pdev->dev, tx_size, + &tx_ring_base_addr, + GFP_KERNEL); + if (!tx_ring_base) + continue; + + ret = macb_queue_phyaddr_check(bp, tx_ring_base_addr, + tx_offset); + if (ret) { + dma_free_coherent(&bp->pdev->dev, tx_size, tx_ring_base, + tx_ring_base_addr); + continue; + } else { + break; + } + } + + rx_offset = RX_RING_BYTES(bp) + bp->rx_bd_rd_prefetch + + RING_ADDR_INTERVAL; + rx_size = bp->num_queues * rx_offset; + for (i = 0; i < MAX_RING_ADDR_ALLOC_TIMES + 1; i++) { + if (i == MAX_RING_ADDR_ALLOC_TIMES) { + dma_free_coherent(&bp->pdev->dev, tx_size, tx_ring_base, + tx_ring_base_addr); + return -ENOMEM; + } + + rx_ring_base = dma_alloc_coherent(&bp->pdev->dev, rx_size, + &rx_ring_base_addr, + GFP_KERNEL); + if (!rx_ring_base) + continue; + + ret = macb_queue_phyaddr_check(bp, rx_ring_base_addr, + rx_offset); + if (ret) { + dma_free_coherent(&bp->pdev->dev, rx_size, rx_ring_base, + rx_ring_base_addr); + continue; + } else { + break; + } + } for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { - size = TX_RING_BYTES(bp) + bp->tx_bd_rd_prefetch; - queue->tx_ring = dma_alloc_coherent(&bp->pdev->dev, size, - &queue->tx_ring_dma, - GFP_KERNEL); + queue->tx_ring = (void *)tx_ring_base + q * tx_offset; + queue->tx_ring_dma = tx_ring_base_addr + q * tx_offset; if (!queue->tx_ring) goto out_err; netdev_dbg(bp->dev, @@ -2157,13 +2570,12 @@ static int macb_alloc_consistent(struct macb *bp) queue->tx_ring); size = bp->tx_ring_size * sizeof(struct macb_tx_skb); - queue->tx_skb = kmalloc(size, GFP_KERNEL); + queue->tx_skb = kzalloc(size, GFP_KERNEL); if (!queue->tx_skb) goto out_err; - size = RX_RING_BYTES(bp) + bp->rx_bd_rd_prefetch; - queue->rx_ring = dma_alloc_coherent(&bp->pdev->dev, size, - &queue->rx_ring_dma, GFP_KERNEL); + queue->rx_ring = (void *)rx_ring_base + q * rx_offset; + queue->rx_ring_dma = rx_ring_base_addr + q * rx_offset; if (!queue->rx_ring) goto out_err; netdev_dbg(bp->dev, @@ -2197,6 +2609,15 @@ static void gem_init_rings(struct macb *bp) queue->tx_head = 0; queue->tx_tail = 0; + for (i = 0; i < bp->rx_ring_size; i++) { + desc = macb_rx_desc(queue, i); + desc->ctrl = 0; + /* make sure ctrl is cleared first, + * and bit RX_USED is set to avoid a race. + */ + dma_wmb(); + desc->addr |= MACB_BIT(RX_USED); + } queue->rx_tail = 0; queue->rx_prepared_head = 0; @@ -2369,76 +2790,6 @@ static void macb_configure_dma(struct macb *bp) } } -static int macb_usx_pcs_config(struct macb *bp) -{ - u32 old_ctrl, ctrl; - u32 old_ncr, ncr; - - netdev_dbg(bp->dev, "macb usx pcs config"); - - ncr = macb_readl(bp, NCR); - ctrl = macb_or_gem_readl(bp, NCFGR); - old_ncr = ncr; - old_ctrl = ctrl; - - ncr &= ~(GEM_BIT(ENABLE_HS_MAC) | MACB_BIT(2PT5G)); - ctrl &= ~(GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL) | MACB_BIT(SPD) | MACB_BIT(FD)); - if (macb_is_gem(bp)) - ctrl &= ~GEM_BIT(GBE); - - if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { - ctrl |= GEM_BIT(PCSSEL) | GEM_BIT(SGMIIEN); - ncr |= MACB_BIT(2PT5G); - } else if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII || - bp->phy_interface == PHY_INTERFACE_MODE_5GBASER) { - ctrl |= GEM_BIT(PCSSEL); - ncr |= GEM_BIT(ENABLE_HS_MAC); - } - - if (bp->duplex) - ctrl |= MACB_BIT(FD); - - /* Apply the new configuration, if any */ - if (old_ctrl ^ ctrl) - macb_or_gem_writel(bp, NCFGR, ctrl); - - if (old_ncr ^ ncr) - macb_or_gem_writel(bp, NCR, ncr); - - return 0; -} - -static void macb_usx_pcs_link_up(struct macb *bp) -{ - u32 config; - - if (bp->phy_interface != PHY_INTERFACE_MODE_USXGMII && - bp->phy_interface != PHY_INTERFACE_MODE_5GBASER) - return; - - netdev_dbg(bp->dev, "macb usx pcs link up"); - - config = gem_readl(bp, USX_CONTROL); - if (bp->speed == SPEED_10000) { - config = GEM_BFINS(SERDES_RATE, MACB_SERDES_RATE_10G, config); - config = GEM_BFINS(USX_CTRL_SPEED, HS_SPEED_10000M, config); - } else if (bp->speed == SPEED_5000) { - config = GEM_BFINS(SERDES_RATE, MACB_SERDES_RATE_5G, config); - config = GEM_BFINS(USX_CTRL_SPEED, HS_SPEED_5000M, config); - } - - config &= ~(GEM_BIT(TX_SCR_BYPASS) | GEM_BIT(RX_SCR_BYPASS)); - config &= ~GEM_BIT(SIGNAL_OK) | GEM_BIT(TX_EN); - config |= GEM_BIT(RX_SYNC_RESET); - - gem_writel(bp, USX_CONTROL, config); - - config &= ~(GEM_BIT(RX_SYNC_RESET)); - config |= GEM_BIT(SIGNAL_OK) | GEM_BIT(TX_EN); - - gem_writel(bp, USX_CONTROL, config); -} - static void macb_init_hw(struct macb *bp) { struct macb_queue *queue; @@ -2449,6 +2800,10 @@ static void macb_init_hw(struct macb *bp) macb_reset_hw(bp); macb_set_hwaddr(bp); + config = macb_readl(bp, NCR); + config |= MACB_BIT(MPE); + macb_writel(bp, NCR, config); + config = macb_mdc_clk_div(bp); if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) config |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL); @@ -2470,14 +2825,15 @@ static void macb_init_hw(struct macb *bp) if ((bp->caps & MACB_CAPS_JUMBO) && bp->jumbo_max_len) gem_writel(bp, JML, bp->jumbo_max_len); + gem_writel(bp, AXI_PIPE, 0x1010); if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII || - bp->phy_interface == PHY_INTERFACE_MODE_5GBASER || bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { - if (bp->caps & MACB_CAPS_SEL_CLK_HW) - phytium_gem_sel_clk(bp); - macb_usx_pcs_config(bp); + /* phytium need hwclock */ + if (bp->caps & MACB_CAPS_SEL_CLK) + bp->sel_clk_hw(bp); + phytium_mac_config(bp); if (bp->link) - macb_usx_pcs_link_up(bp); + phytium_usx_pcs_link_up(bp); } else { bp->speed = SPEED_10; bp->duplex = DUPLEX_HALF; @@ -2673,6 +3029,14 @@ static int macb_open(struct net_device *dev) if (bp->ptp_info) bp->ptp_info->ptp_init(dev); + bp->phy_reset_times = 0; + if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { + bp->poll_task = kthread_create(crc_detect_poll, dev, + "crc_poll_%lx", dev->base_addr); + kthread_bind(bp->poll_task, 0); + wake_up_process(bp->poll_task); + } + return 0; } @@ -2683,6 +3047,9 @@ static int macb_close(struct net_device *dev) unsigned long flags; unsigned int q; + if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) + kthread_stop(bp->poll_task); + netif_tx_stop_all_queues(dev); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) @@ -2748,6 +3115,7 @@ static struct net_device_stats *gem_get_stats(struct macb *bp) struct gem_stats *hwstat = &bp->hw_stats.gem; struct net_device_stats *nstat = &bp->dev->stats; + mutex_lock(&stats_mutex); gem_update_stats(bp); nstat->rx_errors = (hwstat->rx_frame_check_sequence_errors + @@ -2777,6 +3145,7 @@ static struct net_device_stats *gem_get_stats(struct macb *bp) nstat->tx_aborted_errors = hwstat->tx_excessive_collisions; nstat->tx_carrier_errors = hwstat->tx_carrier_sense_errors; nstat->tx_fifo_errors = hwstat->tx_underrun; + mutex_unlock(&stats_mutex); return nstat; } @@ -2787,9 +3156,11 @@ static void gem_get_ethtool_stats(struct net_device *dev, struct macb *bp; bp = netdev_priv(dev); + mutex_lock(&stats_mutex); gem_update_stats(bp); memcpy(data, &bp->ethtool_stats, sizeof(u64) * (GEM_STATS_LEN + QUEUE_STATS_LEN * MACB_MAX_QUEUES)); + mutex_unlock(&stats_mutex); } static int gem_get_sset_count(struct net_device *dev, int sset) @@ -3376,6 +3747,79 @@ static int gem_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd) return ret; } +static int macb_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *kset) +{ + int ret = 0; + struct macb *bp = netdev_priv(ndev); + u32 supported = 0; + u32 advertising = 0; + + if (!ndev->phydev) { + if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII || + bp->phy_interface == PHY_INTERFACE_MODE_XGMII) { + supported = SUPPORTED_10000baseT_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_10000baseT_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + } else if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + supported = SUPPORTED_2500baseX_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_2500baseX_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + } else if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { + supported = SUPPORTED_2500baseX_Full | SUPPORTED_1000baseT_Full + | SUPPORTED_100baseT_Full | SUPPORTED_10baseT_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_2500baseX_Full | ADVERTISED_1000baseT_Full + | ADVERTISED_100baseT_Full | ADVERTISED_10baseT_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + } else if (bp->phy_interface == PHY_INTERFACE_MODE_RGMII) { + supported = SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full + | SUPPORTED_10baseT_Full | SUPPORTED_TP; + advertising = ADVERTISED_1000baseT_Full | ADVERTISED_100baseT_Full + | ADVERTISED_10baseT_Full | ADVERTISED_TP; + } else if (bp->phy_interface == PHY_INTERFACE_MODE_RMII) { + supported = SUPPORTED_100baseT_Full + | SUPPORTED_10baseT_Full | SUPPORTED_TP; + advertising = ADVERTISED_100baseT_Full + | ADVERTISED_10baseT_Full | ADVERTISED_TP; + } + + ethtool_convert_legacy_u32_to_link_mode(kset->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(kset->link_modes.advertising, + advertising); + kset->base.speed = bp->speed; + kset->base.duplex = bp->duplex; + } else { + phy_ethtool_get_link_ksettings(ndev, kset); + } + + return ret; +} + +static int macb_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *kset) +{ + int ret = 0; + + if (!ndev->phydev) { + netdev_err(ndev, "fixed link interface not supported set link\n"); + ret = -EOPNOTSUPP; + } else { + phy_ethtool_set_link_ksettings(ndev, kset); + } + + return ret; +} + static const struct ethtool_ops macb_ethtool_ops = { .get_regs_len = macb_get_regs_len, .get_regs = macb_get_regs, @@ -3397,8 +3841,8 @@ static const struct ethtool_ops gem_ethtool_ops = { .get_ethtool_stats = gem_get_ethtool_stats, .get_strings = gem_get_ethtool_strings, .get_sset_count = gem_get_sset_count, - .get_link_ksettings = phy_ethtool_get_link_ksettings, - .set_link_ksettings = phy_ethtool_set_link_ksettings, + .get_link_ksettings = macb_get_link_ksettings, + .set_link_ksettings = macb_set_link_ksettings, .get_ringparam = macb_get_ringparam, .set_ringparam = macb_set_ringparam, .get_rxnfc = gem_get_rxnfc, @@ -3523,6 +3967,214 @@ static void macb_configure_caps(struct macb *bp, dev_dbg(&bp->pdev->dev, "Cadence caps 0x%08x\n", bp->caps); } +/* only reset phy whose mode is sgmii */ +static void phy_reset(struct net_device *dev) +{ + struct macb *bp = netdev_priv(dev); + + if (bp->phy_vbase_addr) { + switch (dev->base_addr) { + case MACB0_ADDR_BASE: + writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY0_ADDR_OFFSET)); + writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY0_ADDR_OFFSET)); + break; + case MACB1_ADDR_BASE: + writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY1_ADDR_OFFSET)); + writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY1_ADDR_OFFSET)); + break; + case MACB2_ADDR_BASE: + writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY2_ADDR_OFFSET)); + writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY2_ADDR_OFFSET)); + break; + case MACB3_ADDR_BASE: + writel_relaxed(0x0, (void *)(bp->phy_vbase_addr + PHY3_ADDR_OFFSET)); + writel_relaxed(0x1, (void *)(bp->phy_vbase_addr + PHY3_ADDR_OFFSET)); + break; + default: + break; + } + } +} + +static int macb_pcs_software_reset(struct macb *bp, bool reset) +{ + u32 value = gem_readl(bp, PCSCTRL); + + if (reset) + value |= GEM_BIT(PCS_RESET); + else + value &= ~GEM_BIT(PCS_RESET); + + gem_writel(bp, PCSCTRL, value); + msleep(20); + + return 0; +} + +static int macb_check_link_speed_duplex(struct net_device *dev) +{ + struct macb *bp = netdev_priv(dev); + struct phy_device *phydev = dev->phydev; + struct device *device = &bp->pdev->dev; + unsigned int reg_data = 0; + unsigned int pcs_select_mode = 0; + unsigned int duplex = 0; + unsigned int speed = 0; + unsigned int need_serdes_reset = 0; + static unsigned int link_error_count; + + reg_data = gem_readl(bp, NCFGR); + pcs_select_mode = (reg_data >> MACB_CLK_OFFSET) & 3; + duplex = (reg_data >> MACB_FD_OFFSET) & 1; + speed = (reg_data >> MACB_SPD_OFFSET) & 1; + + if (phydev->duplex == DUPLEX_FULL && phydev->speed == SPEED_1000) { + if (pcs_select_mode != 3 || duplex != 1 || speed != 0) + need_serdes_reset = 1; + } else if (phydev->duplex == DUPLEX_FULL && phydev->speed == SPEED_100) { + if (pcs_select_mode != 2 || duplex != 1 || speed != 1) + need_serdes_reset = 1; + } else if (phydev->duplex == DUPLEX_FULL && phydev->speed == SPEED_10) { + if (pcs_select_mode != 2 || duplex != 1 || speed != 0) + need_serdes_reset = 1; + } else if (phydev->duplex == DUPLEX_HALF && phydev->speed == SPEED_100) { + if (pcs_select_mode != 2 || duplex != 0 || speed != 1) + need_serdes_reset = 1; + } else if (phydev->duplex == DUPLEX_HALF && phydev->speed == SPEED_10) { + if (pcs_select_mode != 2 || duplex != 0 || speed != 0) + need_serdes_reset = 1; + } + + if (!need_serdes_reset) { + link_error_count = 0; + return 0; + } + + if (link_error_count == 3) { + netdev_crit(dev, "%s failed, so reset sgmii serdes and reset device\n", + __func__); + phy_reset(dev); + bp->phy_reset_times++; + + if (!rtnl_trylock()) { + netdev_crit(dev, "%s rtnl_trylock failed\n", __func__); + return -EINVAL; + } + macb_suspend(device); + macb_resume(device); + rtnl_unlock(); + link_error_count = 0; + } else { + link_error_count++; + } + + return -EINVAL; +} + +static int crc_detect_poll(void *args) +{ + struct net_device *dev = (struct net_device *)args; + struct macb *bp = netdev_priv(dev); + struct net_device_stats *macb_stats; + struct device *device = &bp->pdev->dev; + unsigned long rx_pkts; + unsigned long rx_pkts_prev = 0; + unsigned long num_crc_err; + unsigned long num_crc_err_prev = 0; + unsigned long percent; + unsigned long rx_pkts_total; + unsigned long num_err_drop; + unsigned long num_err_drop_total = 0; + int flag = false; + int reset_flag; + int pcs_link; + int rx_error_count = 0; + + phy_base_mmap(dev); + netdev_info(bp->dev, "%s version %s.\n", __func__, MACB_POLL_VERSION); + + while (!kthread_should_stop()) { + if (!bp->link || bp->crc_detect_period == 0) { + ssleep(DEFAULT_SLEEP_S); + continue; + } + + pcs_link = macb_readl(bp, NSR) & MACB_BIT(NSR_LINK); + if (!pcs_link) { + netdev_info(bp->dev, "pcs reset.\n"); + macb_pcs_software_reset(bp, true); + } + + /* check speed and duplex of mac and phy */ + if (macb_check_link_speed_duplex(dev)) { + msleep(bp->crc_detect_period); + continue; + } + + reset_flag = false; + /* 1. detect num of detected rx_pkts + * 2. detect percent of crc error + * 3. phy reset + */ + macb_stats = macb_get_stats(dev); + if (!flag) { + rx_pkts_prev = macb_stats->rx_packets; + num_crc_err_prev = macb_stats->rx_crc_errors; + num_err_drop_total = macb_stats->rx_errors + macb_stats->rx_dropped; + flag = true; + } + + rx_pkts = macb_stats->rx_packets - rx_pkts_prev; + num_crc_err = macb_stats->rx_crc_errors - num_crc_err_prev; + rx_pkts_total = rx_pkts + num_crc_err; + if (rx_pkts_total >= bp->nb_rx && rx_pkts_total != 0) { + percent = (num_crc_err * 100) / rx_pkts_total; + if (percent >= bp->crc_err_percent) { + phy_reset(dev); + reset_flag = true; + bp->phy_reset_times++; + } + } + + rx_pkts_prev += rx_pkts; + num_crc_err_prev += num_crc_err; + + num_err_drop = macb_stats->rx_errors + macb_stats->rx_dropped - num_err_drop_total; + if (num_err_drop > rx_pkts) { + if (rx_error_count == 3) { + netdev_crit(dev, "reset serdes and device, num_err_drop:[%lu], rx_pkts:[%lu]\n", + num_err_drop, rx_pkts); + if (!reset_flag) { + phy_reset(dev); + bp->phy_reset_times++; + } + + if (!rtnl_trylock()) { + netdev_crit(dev, "%s rtnl_trylock failed\n", __func__); + num_err_drop_total += num_err_drop; + msleep(bp->crc_detect_period); + continue; + } + macb_suspend(device); + macb_resume(device); + rtnl_unlock(); + rx_error_count = 0; + } else { + rx_error_count++; + } + } else { + rx_error_count = 0; + } + + num_err_drop_total += num_err_drop; + + msleep(bp->crc_detect_period); + } + + phy_base_unmap(dev); + return 0; +} + static void macb_probe_queues(void __iomem *mem, bool native_io, unsigned int *queue_mask, @@ -4204,16 +4856,30 @@ static const struct macb_config zynq_config = { .init = macb_init, }; -static const struct macb_config phytium_config = { +static const struct macb_config phytium_gem1p0_config = { .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | - MACB_CAPS_JUMBO | - MACB_CAPS_GEM_HAS_PTP | - MACB_CAPS_BD_RD_PREFETCH | - MACB_CAPS_SEL_CLK_HW, + MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP | + MACB_CAPS_BD_RD_PREFETCH | + MACB_CAPS_SEL_CLK, .dma_burst_length = 16, .clk_init = macb_clk_init, .init = macb_init, .jumbo_max_len = 10240, + .sel_clk_hw = phytium_gem1p0_sel_clk, +}; + +static const struct macb_config phytium_gem2p0_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | + MACB_CAPS_JUMBO | + MACB_CAPS_GEM_HAS_PTP | + MACB_CAPS_BD_RD_PREFETCH | + MACB_CAPS_SEL_CLK, + .dma_burst_length = 16, + .clk_init = macb_clk_init, + .init = macb_init, + .jumbo_max_len = 10240, + .sel_clk_hw = phytium_gem2p0_sel_clk, }; static const struct of_device_id macb_dt_ids[] = { @@ -4231,7 +4897,8 @@ static const struct of_device_id macb_dt_ids[] = { { .compatible = "cdns,emac", .data = &emac_config }, { .compatible = "cdns,zynqmp-gem", .data = &zynqmp_config}, { .compatible = "cdns,zynq-gem", .data = &zynq_config }, - { .compatible = "cdns,phytium-gem", .data = &phytium_config }, + { .compatible = "cdns,phytium-gem-1.0", .data = &phytium_gem1p0_config }, + { .compatible = "cdns,phytium-gem-2.0", .data = &phytium_gem2p0_config }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, macb_dt_ids); @@ -4239,7 +4906,7 @@ MODULE_DEVICE_TABLE(of, macb_dt_ids); #ifdef CONFIG_ACPI static const struct acpi_device_id macb_acpi_ids[] = { - { .id = "PHYT0036", .driver_data = (kernel_ulong_t)&phytium_config }, + { .id = "PHYT0036", .driver_data = (kernel_ulong_t)&phytium_gem1p0_config }, { } }; @@ -4348,6 +5015,9 @@ static int macb_probe(struct platform_device *pdev) SET_NETDEV_DEV(dev, &pdev->dev); bp = netdev_priv(dev); + bp->crc_detect_period = DEFAULT_CHECK_FCS_PERIOD_MS; + bp->nb_rx = DEFAULT_CHECK_FCS_RX_THRESHOLD; + bp->crc_err_percent = DEFAULT_CHECK_FCS_PERCENT_THRESHOLD; bp->pdev = pdev; bp->dev = dev; bp->regs = mem; @@ -4371,11 +5041,18 @@ static int macb_probe(struct platform_device *pdev) if (macb_config) bp->jumbo_max_len = macb_config->jumbo_max_len; + if (macb_config) + bp->sel_clk_hw = macb_config->sel_clk_hw; + bp->wol = 0; if (device_property_read_bool(&pdev->dev, "magic-packet")) bp->wol |= MACB_WOL_HAS_MAGIC_PACKET; device_init_wakeup(&pdev->dev, bp->wol & MACB_WOL_HAS_MAGIC_PACKET); + err = sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp); + if (err) + return err; + spin_lock_init(&bp->lock); /* setup capabilities */ @@ -4445,11 +5122,9 @@ static int macb_probe(struct platform_device *pdev) bp->link = 1; bp->duplex = 1; bp->speed = SPEED_10000; - } else if (bp->phy_interface == PHY_INTERFACE_MODE_5GBASER) { - bp->link = 1; - bp->duplex = 1; - bp->speed = SPEED_5000; - } else if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + } + + if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { bp->link = 1; bp->duplex = 1; bp->speed = SPEED_2500; @@ -4528,6 +5203,8 @@ static int macb_remove(struct platform_device *pdev) struct macb *bp; struct device_node *np = pdev->dev.of_node; + sysfs_remove_group(&pdev->dev.kobj, &dev_attr_grp); + dev = platform_get_drvdata(pdev); if (dev) { @@ -4563,6 +5240,12 @@ static int __maybe_unused macb_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct net_device *netdev = platform_get_drvdata(pdev); struct macb *bp = netdev_priv(netdev); + struct macb_queue *queue = bp->queues; + unsigned long flags; + unsigned int q; + + if (!netif_running(netdev)) + return 0; netif_carrier_off(netdev); netif_device_detach(netdev); @@ -4572,12 +5255,35 @@ static int __maybe_unused macb_suspend(struct device *dev) macb_writel(bp, WOL, MACB_BIT(MAG)); enable_irq_wake(bp->queues[0].irq); } else { + for (q = 0, queue = bp->queues; q < bp->num_queues; + ++q, ++queue) + napi_disable(&queue->napi); + if (netdev->phydev) { + phy_stop(netdev->phydev); + phy_suspend(netdev->phydev); + } + + spin_lock_irqsave(&bp->lock, flags); + macb_reset_hw(bp); + spin_unlock_irqrestore(&bp->lock, flags); + } + + if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) + bp->pm_data.usrio = macb_or_gem_readl(bp, USRIO); + + if (netdev->hw_features & NETIF_F_NTUPLE) + bp->pm_data.scrt2 = gem_readl_n(bp, ETHT, SCRT2_ETHT); + + if (bp->ptp_info) + bp->ptp_info->ptp_remove(netdev); + + if (!(device_may_wakeup(&bp->dev->dev))) { clk_disable_unprepare(bp->tx_clk); clk_disable_unprepare(bp->hclk); clk_disable_unprepare(bp->pclk); clk_disable_unprepare(bp->rx_clk); - clk_disable_unprepare(bp->tsu_clk); } + clk_disable_unprepare(bp->tsu_clk); return 0; } @@ -4587,12 +5293,13 @@ static int __maybe_unused macb_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct net_device *netdev = platform_get_drvdata(pdev); struct macb *bp = netdev_priv(netdev); + struct macb_queue *queue = bp->queues; + unsigned int q; - if (bp->wol & MACB_WOL_ENABLED) { - macb_writel(bp, IDR, MACB_BIT(WOL)); - macb_writel(bp, WOL, 0); - disable_irq_wake(bp->queues[0].irq); - } else { + if (!netif_running(netdev)) + return 0; + + if (!(device_may_wakeup(&bp->dev->dev))) { clk_prepare_enable(bp->pclk); clk_prepare_enable(bp->hclk); clk_prepare_enable(bp->tx_clk); @@ -4600,7 +5307,33 @@ static int __maybe_unused macb_resume(struct device *dev) } clk_prepare_enable(bp->tsu_clk); + if (bp->wol & MACB_WOL_ENABLED) { + macb_writel(bp, IDR, MACB_BIT(WOL)); + macb_writel(bp, WOL, 0); + disable_irq_wake(bp->queues[0].irq); + } else { + for (q = 0, queue = bp->queues; q < bp->num_queues; + ++q, ++queue) + napi_enable(&queue->napi); + if (netdev->phydev) { + phy_resume(netdev->phydev); + phy_start(netdev->phydev); + } + } + + if (netdev->hw_features & NETIF_F_NTUPLE) + gem_writel_n(bp, ETHT, SCRT2_ETHT, bp->pm_data.scrt2); + + if (!(bp->caps & MACB_CAPS_USRIO_DISABLED)) + macb_or_gem_writel(bp, USRIO, bp->pm_data.usrio); + + bp->macbgem_ops.mog_init_rings(bp); + macb_init_hw(bp); + macb_set_rx_mode(netdev); netif_device_attach(netdev); + netif_carrier_on(netdev); + if (bp->ptp_info) + bp->ptp_info->ptp_init(netdev); return 0; } diff --git a/drivers/pwm/pwm-phytium.c b/drivers/pwm/pwm-phytium.c index 1bc02ff97d1ec0e2d6f872724a041d0099c6a756..23d347d4cab7d01477794c7fb7edeb0ed026d309 100644 --- a/drivers/pwm/pwm-phytium.c +++ b/drivers/pwm/pwm-phytium.c @@ -42,6 +42,8 @@ #define PWM_UPDBCLY_MASK 0x3ff #define PWM_DWDBCLY_MASK 0xffc00 #define PWM_DB_POLARITY_MASK 0xc +#define PWM_CMPMOD_SHIFT 0x4 +#define PWM_CMPMOD_MASK 0x7 #define PWM_N(x) ((0x400)*(x)) #define MAX_PARAMETER 2 @@ -50,6 +52,7 @@ struct phytium_pwm_state { int rst; int cntmod; int dutymod; + unsigned int cmpmod; unsigned int div; int db_rst; unsigned int updbcly; @@ -60,6 +63,7 @@ struct phytium_pwm_state { struct phytium_pwm_param { int cntmod; int dutymod; + unsigned int cmpmod; unsigned int div; unsigned int updbcly; unsigned int dwdbcly; @@ -151,6 +155,18 @@ static void pwm_phytium_dutymod(struct pwm_chip *chip, int dutymod, int n) writel(reg, our_chip->base + PWM_N(n) + REG_PWMCTRL); } +static void pwm_phytium_set_cmpmod(struct pwm_chip *chip, unsigned int cmpmod, int n) +{ + struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); + u32 reg; + + reg = readl(our_chip->base + PWM_N(n) + REG_PWMCTRL); + cmpmod = cmpmod & PWM_CMPMOD_MASK; + reg &= ~ (PWM_CMPMOD_MASK << PWM_CMPMOD_SHIFT); + reg |= cmpmod << PWM_CMPMOD_SHIFT; + writel(reg, our_chip->base + PWM_N(n) + REG_PWMCTRL); +} + static void pwm_phytium_set_div(struct pwm_chip *chip, unsigned int div, int n) { struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); @@ -179,15 +195,13 @@ static void pwm_phytium_set_tmode(struct pwm_chip *chip, int tmode, int n) static void pwm_phytium_set_periodns(struct pwm_chip *chip, unsigned int periodns, int n) { struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); - u32 reg; - int div = our_chip->state.div; + int div = our_chip->parameter[n].div; u64 cycles; cycles = clk_get_rate(our_chip->base_clk); cycles *= (periodns / (div + 1)); do_div(cycles, NSEC_PER_SEC); - reg = readl(our_chip->base + PWM_N(n) + REG_TPERIOD); cycles = cycles & PWM_PERIOD_MASK; our_chip->state_pm[n].period = cycles; @@ -197,8 +211,7 @@ static void pwm_phytium_set_periodns(struct pwm_chip *chip, unsigned int periodn static void pwm_phytium_set_duty(struct pwm_chip *chip, unsigned int duty, int n) { struct phytium_pwm_chip *our_chip = to_phytium_pwm_chip(chip); - u32 reg; - int div = our_chip->state.div; + int div = our_chip->parameter[n].div; u64 max_duty = our_chip->state_pm[n].period; u64 cycles; @@ -206,7 +219,6 @@ static void pwm_phytium_set_duty(struct pwm_chip *chip, unsigned int duty, int n cycles *= (duty / (div + 1)); do_div(cycles, NSEC_PER_SEC); - reg = readl(our_chip->base + PWM_N(n) + REG_PWMCCR); cycles = (max_duty - cycles) & PWM_DUTY_MASK; our_chip->state_pm[n].duty_cycle = cycles; @@ -268,9 +280,10 @@ static int pwm_phytium_init(struct pwm_chip *chip, struct pwm_device *pwm, int n writel(PWM_CTRL_INIT, our_chip->base + PWM_N(n) + REG_PWMCTRL); - pwm_phytium_dutymod(chip, our_chip->state.dutymod, n); - pwm_phytium_set_div(chip, our_chip->state.div, n); - pwm_phytium_set_tmode(chip, our_chip->state.cntmod, n); + pwm_phytium_dutymod(chip, our_chip->parameter[n].dutymod, n); + pwm_phytium_set_div(chip, our_chip->parameter[n].div, n); + pwm_phytium_set_tmode(chip, our_chip->parameter[n].cntmod, n); + pwm_phytium_set_cmpmod(chip, our_chip->parameter[n].cmpmod, n); return 0; } @@ -301,10 +314,11 @@ static int pwm_phytium_set_polarity(struct pwm_chip *chip, enum pwm_polarity pol if (polarity == PWM_POLARITY_INVERSED) { value &= 0xffffff0f; - value |= 0x30; + value |= (our_chip->parameter[n].cmpmod - 1) + << PWM_CMPMOD_SHIFT; } else if (polarity == PWM_POLARITY_NORMAL) { value &= 0xffffff0f; - value |= 0x40; + value |= our_chip->parameter[n].cmpmod << PWM_CMPMOD_SHIFT; } our_chip->state_pm[n].polarity = polarity; @@ -387,6 +401,7 @@ static int phytium_pwm_set_parameter(struct phytium_pwm_chip *priv) priv->state.cntmod = priv->parameter[i].cntmod; priv->state.dutymod = priv->parameter[i].dutymod; + priv->state.cmpmod = priv->parameter[i].cmpmod; priv->state.div = priv->parameter[i].div; priv->state.updbcly = priv->parameter[i].updbcly; priv->state.dwdbcly = priv->parameter[i].dwdbcly; @@ -418,6 +433,7 @@ static int pwm_phytium_probe_parameter(struct phytium_pwm_chip *priv, for (i = 0; i < priv->num_parameters; i++) { if (priv->parameter[i].cntmod > 1 || priv->parameter[i].dutymod > 1 || + priv->parameter[i].cmpmod > 7 || priv->parameter[i].div > 4096 || priv->parameter[i].dbpolarity > 3) return -EINVAL; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 13d28fdbdbb535cdf665b39c8b52ad5127d0b9b9..1aa460db76132eb2365244a2fe5fe5ded79c2daf 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -144,6 +144,15 @@ config RESET_TI_SYSCON you wish to use the reset framework for such memory-mapped devices, say Y here. Otherwise, say N. +config RESET_PHYTIUM + bool "PHYTIUM Reset Driver" + default ARCH_PHYTIUM + help + This enables the reset driver support for phytium devices with + memory-mapped reset registers as part of a syscon device node. If + you wish to use the reset framework for such memory-mapped devices, + say Y here. Otherwise, say N. + config RESET_UNIPHIER tristate "Reset controller driver for UniPhier SoCs" depends on ARCH_UNIPHIER || COMPILE_TEST diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 4243c38228e284478048c42cfbf0f900b7d7aed4..8417671c0ef430934197db36ea318fa273249e8c 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_RESET_OXNAS) += reset-oxnas.o obj-$(CONFIG_RESET_PISTACHIO) += reset-pistachio.o obj-$(CONFIG_RESET_QCOM_AOSS) += reset-qcom-aoss.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o +obj-$(CONFIG_RESET_PHYTIUM) += reset-phytium.o obj-$(CONFIG_RESET_STM32MP157) += reset-stm32mp1.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o diff --git a/drivers/reset/reset-phytium.c b/drivers/reset/reset-phytium.c new file mode 100644 index 0000000000000000000000000000000000000000..fbefd6bc939294a2564936faca8e6491e740dfe3 --- /dev/null +++ b/drivers/reset/reset-phytium.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Phytium Reset Controller Driver + * + * Copyright (C) 2021-2023, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PHYTIUM_RESET_MAX_CNTS (19) +#define PHYTIUM_I2C_RESET_ID0 (3) +#define PHYTIUM_RESET_OFFSET_0 (0) +#define PHYTIUM_RESET_OFFSET_1 (4) + +struct reset_phytium_dev { + void __iomem *base; + struct device *dev; + struct reset_controller_dev rcdev; +}; + +struct reset_reg_info { + u32 reg_offset; + u32 data; +}; +static struct reset_reg_info reset_mng[PHYTIUM_RESET_MAX_CNTS] = { + {PHYTIUM_RESET_OFFSET_0, (u32)BIT(28)}, + {PHYTIUM_RESET_OFFSET_0, (u32)BIT(29)}, + {PHYTIUM_RESET_OFFSET_0, (u32)BIT(30)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(16)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(17)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(18)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(19)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(20)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(21)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(22)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(23)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(24)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(25)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(26)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(27)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(28)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(29)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(30)}, + {PHYTIUM_RESET_OFFSET_1, (u32)BIT(31)} + }; + +static inline struct reset_phytium_dev * +to_reset_phytium_data(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct reset_phytium_dev, rcdev); +} + +static int reset_phytium_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_phytium_dev *rdev = to_reset_phytium_data(rcdev); + u32 reg_val, reset_reg_val; + + if (id >= PHYTIUM_RESET_MAX_CNTS) { + dev_err(rdev->dev, "The reset id is out of range %ld\n",id); + return -EINVAL; + } + + reg_val = readl_relaxed(rdev->base + reset_mng[id].reg_offset); + reset_reg_val = reg_val; + reg_val &= ~reset_mng[id].data; + writel_relaxed(reg_val, rdev->base + reset_mng[id].reg_offset); + writel_relaxed(reset_reg_val, rdev->base + reset_mng[id].reg_offset); + + return 0; +} + +const struct reset_control_ops reset_phytium_ops = { + .reset = reset_phytium_reset, +}; +EXPORT_SYMBOL_GPL(reset_phytium_ops); + +static const struct of_device_id reset_phytium_dt_ids[] = { + { .compatible = "phytium,reset", }, + { /* sentinel */ }, +}; + +static int reset_phytium_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct reset_phytium_dev *rdev; + struct resource *res; + + rdev = devm_kzalloc(dev, sizeof(*rdev), GFP_KERNEL); + if (!rdev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rdev->base = devm_ioremap_resource(dev, res); + if (IS_ERR(rdev->base)) + return PTR_ERR(rdev->base); + + rdev->rcdev.owner = THIS_MODULE; + rdev->rcdev.nr_resets = PHYTIUM_RESET_MAX_CNTS; + rdev->rcdev.ops = &reset_phytium_ops; + rdev->rcdev.of_node = dev->of_node; + rdev->dev = &pdev->dev; + + return devm_reset_controller_register(dev, &rdev->rcdev); +} + +static struct platform_driver reset_phytium_driver = { + .probe = reset_phytium_probe, + .driver = { + .name = "phytium-reset", + .of_match_table = reset_phytium_dt_ids, + }, +}; +builtin_platform_driver(reset_phytium_driver); +MODULE_DESCRIPTION("phytium reset"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-phytium.c b/drivers/spi/spi-phytium.c index f948a9cc175757827e5a884dcbc2243468050749..d29eaa19ccda66374d25c23903aac487d9c68f4d 100644 --- a/drivers/spi/spi-phytium.c +++ b/drivers/spi/spi-phytium.c @@ -413,6 +413,8 @@ int phytium_spi_add_host(struct device *dev, struct phytium_spi *fts) fts->dma_addr = (dma_addr_t)(fts->paddr + DR); snprintf(fts->name, sizeof(fts->name), "phytium_spi%d", fts->bus_num); + spi_hw_init(dev, fts); + ret = request_irq(fts->irq, phytium_spi_irq, IRQF_SHARED, fts->name, master); if (ret < 0) { dev_err(dev, "can not get IRQ\n"); @@ -434,8 +436,6 @@ int phytium_spi_add_host(struct device *dev, struct phytium_spi *fts) master->flags = SPI_MASTER_GPIO_SS; master->cs_gpios = fts->cs; - spi_hw_init(dev, fts); - if (fts->dma_ops && fts->dma_ops->dma_init) { ret = fts->dma_ops->dma_init(dev, fts); if (ret) { @@ -447,6 +447,7 @@ int phytium_spi_add_host(struct device *dev, struct phytium_spi *fts) } spi_master_set_devdata(master, fts); + ret = devm_spi_register_master(dev, master); if (ret) { dev_err(&master->dev, "problem registering spi master\n"); diff --git a/drivers/usb/phytium/host.c b/drivers/usb/phytium/host.c index d1b82adc91b8b7e108be6ea7f29000d4ff495566..8868e15cfc9548383d33eea56826adc3c1f94d4c 100644 --- a/drivers/usb/phytium/host.c +++ b/drivers/usb/phytium/host.c @@ -23,6 +23,71 @@ #define HOST_EP_NUM 16 +static int get_epnum_from_pool(struct HOST_CTRL *priv, int real_epNum, bool dirIn) +{ + int index, dir = 0; + int ret = 0; + + if (!priv) + return 0; + + if (!dirIn) + dir = 1; + + if (real_epNum <= MAX_INSTANCE_EP_NUM) { + if (!priv->ep_remap_pool[dir][real_epNum]) { + priv->ep_remap_pool[dir][real_epNum] = real_epNum; + ret = real_epNum; + goto out; + } + + if (priv->ep_remap_pool[dir][real_epNum] == real_epNum) { + ret = real_epNum; + goto out; + } + } else { + for (index = 1; index <= MAX_INSTANCE_EP_NUM; index++) { + if (priv->ep_remap_pool[dir][index] == real_epNum) { + ret = index; + goto out; + } + } + + for (index = 1; index <= MAX_INSTANCE_EP_NUM; index++) { + if (!priv->ep_remap_pool[dir][index]) { + priv->ep_remap_pool[dir][index] = real_epNum; + ret = index; + goto out; + } + } + } + +out: + return ret; +} + +static int release_epnum_from_pool(struct HOST_CTRL *priv, int real_epNum, bool dirIn) +{ + int index = 0; + int dir = 0; + + if (!priv) + return 0; + + if (!dirIn) + dir = 1; + + for (index = 1; index <= MAX_INSTANCE_EP_NUM; index++) { + if (priv->ep_remap_pool[dir][index] == real_epNum) { + priv->ep_remap_pool[dir][index] = 0; + + return 0; + } + } + + return 0; +} + static inline struct HOST_REQ *getUsbRequestEntry(struct list_head *list) { return (struct HOST_REQ *)((uintptr_t)list - (uintptr_t)&(((struct HOST_REQ *)0)->list)); @@ -85,6 +150,7 @@ static inline void disconnectHostDetect(struct HOST_CTRL *priv) if (!priv) return; + memset(priv->ep_remap_pool, 0, sizeof(priv->ep_remap_pool)); otgctrl = phytium_read8(&priv->regs->otgctrl); if ((otgctrl & OTGCTRL_ASETBHNPEN) && priv->otgState == HOST_OTG_STATE_A_SUSPEND) pr_info("Device no Response\n"); @@ -571,7 +637,7 @@ static void hostEpProgram(struct HOST_CTRL *priv, struct HostEp *hwEp, phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].txcon, regCon); if (usbEpPriv->type != USB_ENDPOINT_XFER_ISOC) { retval = priv->hostCallbacks.getEpToggle(priv, - usbReq->usbDev, usbEpPriv->epNum, 0); + usbReq->usbDev, usbHEp->device_epNum, 0); if (retval) { phytium_write8(&priv->regs->endprst, hwEp->hwEpNum | ENDPRST_IO_TX); @@ -592,7 +658,7 @@ static void hostEpProgram(struct HOST_CTRL *priv, struct HostEp *hwEp, usbEpPriv->maxPacketSize); phytium_write8(&priv->regs->epExt[hwEp->hwEpNum - 1].txctrl, - usbEpPriv->epNum); + usbHEp->device_epNum); phytium_write8(&priv->regs->fnaddr, usbEpPriv->faddress); @@ -629,7 +695,7 @@ static void hostEpProgram(struct HOST_CTRL *priv, struct HostEp *hwEp, if (usbEpPriv->type != USB_ENDPOINT_XFER_ISOC) { if (priv->hostCallbacks.getEpToggle) { retval = priv->hostCallbacks.getEpToggle(priv, - usbReq->usbDev, usbEpPriv->epNum, 1); + usbReq->usbDev, usbHEp->device_epNum, 1); if (retval) { phytium_write8(&priv->regs->endprst, hwEp->hwEpNum); phytium_write8(&priv->regs->endprst, hwEp->hwEpNum | @@ -646,7 +712,7 @@ static void hostEpProgram(struct HOST_CTRL *priv, struct HostEp *hwEp, usbEpPriv->maxPacketSize); phytium_write8(&priv->regs->epExt[hwEp->hwEpNum - 1].rxctrl, - usbEpPriv->epNum); + usbHEp->device_epNum); phytium_write8(&priv->regs->fnaddr, usbEpPriv->faddress); @@ -778,6 +844,38 @@ static void hostStartReq(struct HOST_CTRL *priv, struct HOST_REQ *req) } } +static void abortTransfer(struct HOST_CTRL *priv, + struct HOST_REQ *usbReq, struct HostEp *hwEp) +{ + struct HOST_EP *usbEp; + struct HOST_EP_PRIV *usbHEpPriv; + uint32_t status; + + if (!priv || !usbReq || !hwEp || !hwEp->scheduledUsbHEp) + return; + + usbEp = hwEp->scheduledUsbHEp; + usbHEpPriv = (struct HOST_EP_PRIV *)usbEp->hcPriv; + if (!usbHEpPriv) + return; + + status = (usbReq->status == EINPROGRESS) ? 0 : usbReq->status; + givebackRequest(priv, usbReq, status); + + if (list_empty(&usbEp->reqList)) { + usbHEpPriv->epIsReady = 0; + usbHEpPriv->currentHwEp = NULL; + hwEp->scheduledUsbHEp = NULL; + + if (hwEp->channel) { + priv->dmaDrv->dma_channelRelease(priv->dmaController, hwEp->channel); + hwEp->channel = NULL; + } + + if (usb_endpoint_xfer_int(&usbEp->desc)) + list_del(&usbHEpPriv->node); + } +} static void scheduleNextTransfer(struct HOST_CTRL *priv, struct HOST_REQ *usbReq, struct HostEp *hwEp) @@ -805,7 +903,7 @@ static void scheduleNextTransfer(struct HOST_CTRL *priv, endprst = (phytium_read8(&priv->regs->endprst) & ENDPRST_TOGSETQ) ? 1 : 0; if (priv->hostCallbacks.setEpToggle) priv->hostCallbacks.setEpToggle(priv, usbReq->usbDev, - usbHEpPriv->epNum, usbHEpPriv->isIn, endprst); + usbEp->device_epNum, usbHEpPriv->isIn, endprst); } else { if (waitForBusyBit(priv, hwEp) > 0) { usbReq->status = HOST_ESHUTDOWN; @@ -817,7 +915,7 @@ static void scheduleNextTransfer(struct HOST_CTRL *priv, endprst = (phytium_read8(&priv->regs->endprst) & ENDPRST_TOGSETQ) ? 1 : 0; if (priv->hostCallbacks.setEpToggle) priv->hostCallbacks.setEpToggle(priv, usbReq->usbDev, - usbHEpPriv->epNum, usbHEpPriv->isIn, endprst); + usbEp->device_epNum, usbHEpPriv->isIn, endprst); } break; } @@ -1137,9 +1235,8 @@ static void host_endpoint_update(struct phytium_cusb *config, if (!config || !udev || !ep) return; - epnum = usb_endpoint_num(&ep->desc); - if (epnum > MAX_INSTANCE_EP_NUM) - epnum = MAX_INSTANCE_EP_NUM; + epnum = get_epnum_from_pool(config->host_priv, usb_endpoint_num(&ep->desc), + usb_endpoint_dir_in(&ep->desc)); if (usb_endpoint_dir_out(&ep->desc)) { if (udev->out_ep[epnum] == NULL) { @@ -1211,9 +1308,8 @@ static int hc_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) req->isoFramesDesc = NULL; req->isoFramesNumber = urb->number_of_packets; - req->epNum = usb_endpoint_num(host_ep_desc); - if (req->epNum > MAX_INSTANCE_EP_NUM) - req->epNum = MAX_INSTANCE_EP_NUM; + req->epNum = get_epnum_from_pool(config->host_priv, usb_endpoint_num(host_ep_desc), + usb_endpoint_dir_in(host_ep_desc)); if (usb_endpoint_dir_in(host_ep_desc)) { if (!usbDev->in_ep[req->epNum]) @@ -1248,6 +1344,7 @@ static int hc_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) req->status = EINPROGRESS; req->usbDev = &usbDev->udev; req->usbEp = req->epIsIn ? usbDev->in_ep[req->epNum] : usbDev->out_ep[req->epNum]; + req->usbEp->device_epNum = usb_endpoint_num(host_ep_desc); if (!req->epNum) usbDev->ep0_hep.desc.wMaxPacketSize = urb->dev->ep0.desc.wMaxPacketSize; @@ -1289,6 +1386,10 @@ static int hc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) goto err_giveback; ret = config->host_obj->host_reqDequeue(priv, urb->hcpriv, status); + + release_epnum_from_pool(config->host_priv, usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe)); + kfree(urb->hcpriv); urb->hcpriv = NULL; done: @@ -1306,10 +1407,15 @@ static int hc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) static void hc_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ld_ep) { struct HOST_USB_DEVICE *usbDev; - int ep_num = usb_endpoint_num(&ld_ep->desc); + int ep_num; + struct phytium_cusb *config; - if (ep_num > MAX_INSTANCE_EP_NUM) - ep_num = MAX_INSTANCE_EP_NUM; + config = *(struct phytium_cusb **)hcd->hcd_priv; + if (!config) + return; + + ep_num = get_epnum_from_pool(config->host_priv, usb_endpoint_num(&ld_ep->desc), + usb_endpoint_dir_in(&ld_ep->desc)); usbDev = (struct HOST_USB_DEVICE *)ld_ep->hcpriv; if (!usbDev) @@ -1317,14 +1423,14 @@ static void hc_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *l if (ld_ep->desc.bEndpointAddress) { if (usb_endpoint_dir_in(&ld_ep->desc)) { - if (!usbDev->in_ep[ep_num]) { + if (usbDev->in_ep[ep_num]) { usbDev->in_ep[ep_num]->userExt = NULL; INIT_LIST_HEAD(&usbDev->in_ep[ep_num]->reqList); kfree(usbDev->in_ep[ep_num]); usbDev->in_ep[ep_num] = NULL; } } else { - if (!usbDev->out_ep[ep_num]) { + if (usbDev->out_ep[ep_num]) { usbDev->out_ep[ep_num]->userExt = NULL; INIT_LIST_HEAD(&usbDev->out_ep[ep_num]->reqList); kfree(usbDev->out_ep[ep_num]); @@ -1667,6 +1773,7 @@ static uint32_t initEndpoints(struct HOST_CTRL *priv) priv->hwEpOutCount = 0; phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | 0); phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | FIFOCTRL_IO_TX | 0); + memset(priv->ep_remap_pool, 0, sizeof(priv->ep_remap_pool)); for (epNum = 0; epNum < HOST_EP_NUM; epNum++) { priv->in[epNum].isInEp = 1; @@ -1917,8 +2024,7 @@ int32_t hostEpDisable(struct HOST_CTRL *priv, struct HOST_EP *ep) return 0; } -unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, - int speed) +unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, int speed) { unsigned int interval = 0; @@ -1932,7 +2038,7 @@ unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, interval = 1 << interval; if ((1 << interval) != desc.bInterval) pr_info("rounding to %d microframes, desc %d microframes\n", - 1 << interval, desc.bInterval); + 1 << interval, desc.bInterval); break; } @@ -1941,16 +2047,15 @@ unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, interval = 1 << interval; if (interval != desc.bInterval - 1) pr_info("rounding to %d %sframes\n", 1 << interval, - speed == USB_SPEED_FULL ? "" : "micro"); + speed == USB_SPEED_FULL ? "" : "micro"); } break; case USB_SPEED_FULL: if (usb_endpoint_xfer_isoc(&desc)) { - interval = clamp_val(desc.bInterval, 1, 16) - 1; - if (interval != desc.bInterval - 1) + interval = clamp_val(desc.bInterval, 1, 16); + if (interval != desc.bInterval) pr_info("rounding to %d %sframes\n", 1 << interval, - speed == USB_SPEED_FULL ? "" : "micro"); - interval += 3; + speed == USB_SPEED_FULL ? "" : "micro"); break; } /* fall through */ @@ -1960,7 +2065,7 @@ unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, interval = clamp_val(interval, 3, 10); if ((1 << interval) != desc.bInterval * 8) pr_info("rounding to %d microframes, desc %d microframes\n", - 1 << interval, desc.bInterval); + 1 << interval, desc.bInterval); } } @@ -2053,8 +2158,6 @@ static int abortActuallyUsbRequest(struct HOST_CTRL *priv, { struct HOST_EP_PRIV *usbEpPriv; struct HostEp *hostEp; - uint16_t rxerrien = 0; - uint16_t txerrien = 0; uint8_t rxcon, txcon; if (!priv || !req || !usbEp) @@ -2064,31 +2167,20 @@ static int abortActuallyUsbRequest(struct HOST_CTRL *priv, hostEp = usbEpPriv->currentHwEp; usbEpPriv->transferFinished = 1; - if (hostEp->isInEp) { if (hostEp->hwEpNum) { rxcon = phytium_read8(&priv->regs->ep[hostEp->hwEpNum - 1].rxcon); rxcon = rxcon & (~BIT(7)); phytium_write8(&priv->regs->ep[hostEp->hwEpNum - 1].rxcon, rxcon); } - rxerrien = phytium_read16(&priv->regs->rxerrien); - rxerrien &= ~(1 << hostEp->hwEpNum); - phytium_write16(&priv->regs->rxerrien, rxerrien); - phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | - ENDPRST_IO_TX | hostEp->hwEpNum); } else { if (hostEp->hwEpNum) { txcon = phytium_read8(&priv->regs->ep[hostEp->hwEpNum - 1].txcon); txcon = txcon & (~BIT(7)); phytium_write8(&priv->regs->ep[hostEp->hwEpNum - 1].txcon, txcon); } - txerrien = phytium_read16(&priv->regs->txerrien); - txerrien &= ~(1 << hostEp->hwEpNum); - phytium_write16(&priv->regs->txerrien, txerrien); - phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | hostEp->hwEpNum); } - - scheduleNextTransfer(priv, req, hostEp); + abortTransfer(priv, req, hostEp); return 0; } diff --git a/drivers/usb/phytium/host_api.h b/drivers/usb/phytium/host_api.h index 3d45258278c475fdd4ef15810499e41c73d27948..b99d2b4980fbe09ab31f8591b0cd528f6516d6f5 100644 --- a/drivers/usb/phytium/host_api.h +++ b/drivers/usb/phytium/host_api.h @@ -10,6 +10,7 @@ #define MAX_SUPPORTED_DEVICES 16 #define USB_PORT_STAT_RESUME (1 << 31) #define MAX_INSTANCE_EP_NUM 6 +#define ENDPOINT_DIR 2 enum HOST_OtgState { HOST_OTG_STATE_A_IDLE, @@ -63,6 +64,7 @@ struct HOST_EP { struct list_head reqList; void *userExt; uint8_t *hcPriv; + uint8_t device_epNum; }; struct HOST_USB_DEVICE { @@ -241,6 +243,7 @@ struct HOST_CTRL { struct HOST_USB_DEVICE *host_devices_table[MAX_SUPPORTED_DEVICES]; struct CUSTOM_REGS *custom_regs; struct VHUB_REGS *vhub_regs; + int ep_remap_pool[ENDPOINT_DIR][MAX_INSTANCE_EP_NUM + 1]; }; struct HOST_OBJ *HOST_GetInstance(void); diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c index e8bd9887c56638aaf81659c45d7505c424266b55..4c3b991f28b4d34afc689fc20df3ee3bf93bd9e3 100644 --- a/drivers/watchdog/sbsa_gwdt.c +++ b/drivers/watchdog/sbsa_gwdt.c @@ -82,6 +82,11 @@ #define SBSA_GWDT_WCS_WS0 BIT(1) #define SBSA_GWDT_WCS_WS1 BIT(2) +/* Watchdog Reset type */ +#define SBSA_GWDT_RESET 0x0 +#define SBSA_GWDT_CORE_RESET 0x1 +#define SBSA_GWDT_NONE 0x2 + /** * struct sbsa_gwdt - Internal representation of the SBSA GWDT * @wdd: kernel watchdog_device structure @@ -234,6 +239,9 @@ static int sbsa_gwdt_probe(struct platform_device *pdev) struct resource *res; int ret, irq; u32 status; + struct device_node *np; + const char *reset_type; + void __iomem *shared_base; gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL); if (!gwdt) @@ -313,6 +321,26 @@ static int sbsa_gwdt_probe(struct platform_device *pdev) */ sbsa_gwdt_set_timeout(wdd, wdd->timeout); + + np = pdev->dev.of_node; + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + + shared_base = devm_ioremap_resource(dev, res); + if (IS_ERR(shared_base)) + return PTR_ERR(shared_base); + + ret = of_property_read_string(np, "reset-type", &reset_type); + if (ret) + writew(SBSA_GWDT_CORE_RESET, shared_base); + else { + if (!strcmp(reset_type, "wdt reset")) + writew(SBSA_GWDT_RESET, shared_base); + else if (!strcmp(reset_type, "core reset")) + writew(SBSA_GWDT_CORE_RESET, shared_base); + else if (!strcmp(reset_type, "none")) + writew(SBSA_GWDT_NONE, shared_base); + } + ret = watchdog_register_device(wdd); if (ret) return ret; diff --git a/include/linux/jtag.h b/include/linux/jtag.h new file mode 100644 index 0000000000000000000000000000000000000000..d9775711423d3114bc8269554d75de15c3a40411 --- /dev/null +++ b/include/linux/jtag.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2018 Mellanox Technologies. All rights reserved. */ +/* Copyright (c) 2018 Oleksandr Shamray */ +/* Copyright (c) 2019 Intel Corporation */ +/* Copyright (c) 2021 Phytium Corporation */ + +#ifndef __LINUX_JTAG_H +#define __LINUX_JTAG_H + +#include +#include + +#define JTAG_MAX_XFER_DATA_LEN 65535 + +struct jtag; + +/** + * struct jtag_ops - callbacks for JTAG control functions: + * + * @freq_get: get frequency function. Filled by dev driver + * @freq_set: set frequency function. Filled by dev driver + * @status_get: get JTAG TAPC state function. Mandatory, Filled by dev driver + * @status_set: set JTAG TAPC state function. Mandatory, Filled by dev driver + * @xfer: send JTAG xfer function. Mandatory func. Filled by dev driver + * @mode_set: set + */ +struct jtag_ops { + int (*freq_get)(struct jtag *jtag, u32 *freq); + int (*freq_set)(struct jtag *jtag, u32 freq); + int (*status_get)(struct jtag *jtag, u32 *state); + int (*status_set)(struct jtag *jtag, struct jtag_end_tap_state *endst); + int (*xfer)(struct jtag *jtag, struct jtag_xfer *xfer, u8 *xfer_data); + int (*mode_set)(struct jtag *jtag, struct jtag_mode *jtag_mode); + int (*bitbang)(struct jtag *jtag, struct tck_bitbang *tck_bitbang); + int (*enable)(struct jtag *jtag); + int (*disable)(struct jtag *jtag); +}; + +extern void *jtag_priv(struct jtag *jtag); +extern int devm_jtag_register(struct device *dev, struct jtag *jtag); +extern struct jtag *jtag_alloc(struct device *host, size_t priv_size, + const struct jtag_ops *ops); +extern void jtag_free(struct jtag *jtag); + +#endif /* __LINUX_JTAG_H */ diff --git a/include/uapi/linux/jtag.h b/include/uapi/linux/jtag.h new file mode 100644 index 0000000000000000000000000000000000000000..96334bbcc626577e571c99c37040fe6a0188bab7 --- /dev/null +++ b/include/uapi/linux/jtag.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (c) 2018 Mellanox Technologies. All rights reserved. */ +/* Copyright (c) 2018 Oleksandr Shamray */ +/* Copyright (c) 2019 Intel Corporation */ + +#ifndef __UAPI_LINUX_JTAG_H +#define __UAPI_LINUX_JTAG_H + +/* + * JTAG_XFER_MODE: JTAG transfer mode. Used to set JTAG controller transfer mode + * This is bitmask for feature param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_XFER_MODE 0 +/* + * JTAG_CONTROL_MODE: JTAG controller mode. Used to set JTAG controller mode + * This is bitmask for feature param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_CONTROL_MODE 1 +/* + * JTAG_SLAVE_MODE: JTAG master mode output disable, it is used to + * enable other devices to own the JTAG bus. + * This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_SLAVE_MODE 0 +/* + * JTAG_MASTER_MODE: JTAG master mode. Used to set JTAG controller master mode + * This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_MASTER_MODE 1 +/* + * JTAG_XFER_HW_MODE: JTAG hardware mode. Used to set HW drived or bitbang + * mode. This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_XFER_HW_MODE 1 +/* + * JTAG_XFER_SW_MODE: JTAG software mode. Used to set SW drived or bitbang + * mode. This is bitmask for mode param in jtag_mode for ioctl JTAG_SIOCMODE + */ +#define JTAG_XFER_SW_MODE 0 + +/** + * enum jtag_endstate: + * + * @JTAG_STATE_TLRESET: JTAG state machine Test Logic Reset state + * @JTAG_STATE_IDLE: JTAG state machine IDLE state + * @JTAG_STATE_SELECTDR: JTAG state machine SELECT_DR state + * @JTAG_STATE_CAPTUREDR: JTAG state machine CAPTURE_DR state + * @JTAG_STATE_SHIFTDR: JTAG state machine SHIFT_DR state + * @JTAG_STATE_EXIT1DR: JTAG state machine EXIT-1 DR state + * @JTAG_STATE_PAUSEDR: JTAG state machine PAUSE_DR state + * @JTAG_STATE_EXIT2DR: JTAG state machine EXIT-2 DR state + * @JTAG_STATE_UPDATEDR: JTAG state machine UPDATE DR state + * @JTAG_STATE_SELECTIR: JTAG state machine SELECT_IR state + * @JTAG_STATE_CAPTUREIR: JTAG state machine CAPTURE_IR state + * @JTAG_STATE_SHIFTIR: JTAG state machine SHIFT_IR state + * @JTAG_STATE_EXIT1IR: JTAG state machine EXIT-1 IR state + * @JTAG_STATE_PAUSEIR: JTAG state machine PAUSE_IR state + * @JTAG_STATE_EXIT2IR: JTAG state machine EXIT-2 IR state + * @JTAG_STATE_UPDATEIR: JTAG state machine UPDATE IR state + */ +enum jtag_endstate { + JTAG_STATE_RESET, + JTAG_STATE_IDLE, + JTAG_STATE_SELECTDR, + JTAG_STATE_CAPTUREDR, + JTAG_STATE_SHIFTDR, + JTAG_STATE_EXIT1DR, + JTAG_STATE_PAUSEDR, + JTAG_STATE_EXIT2DR, + JTAG_STATE_UPDATEDR, + JTAG_STATE_SELECTIR, + JTAG_STATE_CAPTUREIR, + JTAG_STATE_SHIFTIR, + JTAG_STATE_EXIT1IR, + JTAG_STATE_PAUSEIR, + JTAG_STATE_EXIT2IR, + JTAG_STATE_UPDATEIR +}; + +/** + * enum jtag_reset: + * + * @JTAG_NO_RESET: JTAG run TAP from current state + * @JTAG_FORCE_RESET: JTAG force TAP to reset state + */ +enum jtag_reset { + JTAG_NO_RESET = 0, + JTAG_FORCE_RESET = 1, +}; + +/** + * enum jtag_xfer_type: + * + * @JTAG_SIR_XFER: SIR transfer + * @JTAG_SDR_XFER: SDR transfer + */ +enum jtag_xfer_type { + JTAG_SIR_XFER = 0, + JTAG_SDR_XFER = 1, +}; + +/** + * enum jtag_xfer_direction: + * + * @JTAG_READ_XFER: read transfer + * @JTAG_WRITE_XFER: write transfer + * @JTAG_READ_WRITE_XFER: read & write transfer + */ +enum jtag_xfer_direction { + JTAG_READ_XFER = 1, + JTAG_WRITE_XFER = 2, + JTAG_READ_WRITE_XFER = 3, +}; + +/** + * struct jtag_end_tap_state - forces JTAG state machine to go into a TAPC + * state + * + * @reset: 0 - run IDLE/PAUSE from current state + * 1 - go through TEST_LOGIC/RESET state before IDLE/PAUSE + * @end: completion flag + * @tck: clock counter + * + * Structure provide interface to JTAG device for JTAG set state execution. + */ +struct jtag_end_tap_state { + __u8 reset; + __u8 endstate; + __u8 tck; +}; + +/** + * struct jtag_xfer - jtag xfer: + * + * @type: transfer type + * @direction: xfer direction + * @length: xfer bits length + * @tdio : xfer data array + * @endir: xfer end state + * + * Structure provide interface to JTAG device for JTAG SDR/SIR xfer execution. + */ +struct jtag_xfer { + __u8 type; + __u8 direction; + __u8 endstate; + __u8 padding; + __u32 length; + __u64 tdio; +}; + +/** + * struct jtag_bitbang - jtag bitbang: + * + * @tms: JTAG TMS + * @tdi: JTAG TDI (input) + * @tdo: JTAG TDO (output) + * + * Structure provide interface to JTAG device for JTAG bitbang execution. + */ + +struct tck_bitbang { + __u8 tms; + __u8 tdi; + __u8 tdo; +} __attribute__((__packed__)); + +/** + * struct jtag_mode - jtag mode: + * + * @feature: 0 - JTAG feature setting selector for JTAG controller HW/SW + * 1 - JTAG feature setting selector for controller bus master + * mode output (enable / disable). + * @mode: (0 - SW / 1 - HW) for JTAG_XFER_MODE feature(0) + * (0 - output disable / 1 - output enable) for JTAG_CONTROL_MODE + * feature(1) + * + * Structure provide configuration modes to JTAG device. + */ +struct jtag_mode { + __u32 feature; + __u32 mode; +}; + +/* ioctl interface */ +#define __JTAG_IOCTL_MAGIC 0xb2 + +#define JTAG_SIOCSTATE _IOW(__JTAG_IOCTL_MAGIC, 0, struct jtag_end_tap_state) +#define JTAG_SIOCFREQ _IOW(__JTAG_IOCTL_MAGIC, 1, unsigned int) +#define JTAG_GIOCFREQ _IOR(__JTAG_IOCTL_MAGIC, 2, unsigned int) +#define JTAG_IOCXFER _IOWR(__JTAG_IOCTL_MAGIC, 3, struct jtag_xfer) +#define JTAG_GIOCSTATUS _IOWR(__JTAG_IOCTL_MAGIC, 4, enum jtag_endstate) +#define JTAG_SIOCMODE _IOW(__JTAG_IOCTL_MAGIC, 5, unsigned int) +#define JTAG_IOCBITBANG _IOW(__JTAG_IOCTL_MAGIC, 6, unsigned int) + +#endif /* __UAPI_LINUX_JTAG_H */