From 816d793003e93c1e5eec0a2e90fbd8b9dde9f7a5 Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Mon, 15 Apr 2019 13:42:44 -0700 Subject: Update 4-15-19 Signed-off-by: Ed Tanous --- .../0005-arm-dts-aspeed-g5-add-espi.patch | 12 +- .../0007-New-flash-map-for-intel.patch | 65 +- .../0008-Add-ASPEED-SGPIO-driver.patch | 7 +- .../0009-SGPIO-DT-and-pinctrl-fixup.patch | 20 +- ...-drivers-to-sync-with-linux-upstreaming-v.patch | 20 +- ...021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch | 2 +- .../0022-Add-AST2500-eSPI-driver.patch | 12 +- .../0028-Add-AST2500-JTAG-driver.patch | 12 +- ...ed-Add-Aspeed-UART-routing-control-driver.patch | 20 +- ...m-dts-adpeed-Swap-the-mac-nodes-numbering.patch | 85 - ...m-dts-aspeed-Swap-the-mac-nodes-numbering.patch | 85 + ...35-Implement-a-memory-driver-share-memory.patch | 23 +- .../0036-net-ncsi-backport-ncsi-patches.patch | 1425 --------------- .../0038-media-aspeed-backport-ikvm-patches.patch | 1853 +------------------- ...d-PWM-driver-which-uses-FTTMR010-timer-IP.patch | 65 +- .../0040-i2c-Add-mux-hold-unhold-msg-types.patch | 77 +- ...e-passthrough-based-gpio-character-device.patch | 48 +- ...eout-ms-and-retries-device-tree-propertie.patch | 10 +- ...-Suppress-excessive-HID-gadget-error-logs.patch | 43 + ...tform-Fix-a-kernel-warning-on-clk-control.patch | 177 ++ 20 files changed, 508 insertions(+), 3553 deletions(-) delete mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-adpeed-Swap-the-mac-nodes-numbering.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch delete mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0049-Suppress-excessive-HID-gadget-error-logs.patch create mode 100644 meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0050-media-platform-Fix-a-kernel-warning-on-clk-control.patch (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed') diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-arm-dts-aspeed-g5-add-espi.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-arm-dts-aspeed-g5-add-espi.patch index 2a94453b3..08498cd01 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-arm-dts-aspeed-g5-add-espi.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0005-arm-dts-aspeed-g5-add-espi.patch @@ -1,19 +1,19 @@ -From 2affc8ab570c9d1e6d6e5ecbdbeddbc5e3b15cc5 Mon Sep 17 00:00:00 2001 +From 536b09695117440ed428ff27023cd9167fcf4dfe Mon Sep 17 00:00:00 2001 From: Juston Li Date: Mon, 27 Mar 2017 11:16:00 -0700 Subject: [PATCH] arm: dts: aspeed-g5: add espi -Change-Id: I0b607657883619a3acefdbf344d39bf01790c4b1 Signed-off-by: Juston Li --- arch/arm/boot/dts/aspeed-g5.dtsi | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi -index e4c5de3208e0..a3c456ba3f34 100644 +index a79e01ffe9d4..0c74adf739d2 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi -@@ -260,13 +260,22 @@ +@@ -261,7 +261,7 @@ + #gpio-cells = <2>; gpio-controller; compatible = "aspeed,ast2500-gpio"; - reg = <0x1e780000 0x1000>; @@ -21,7 +21,7 @@ index e4c5de3208e0..a3c456ba3f34 100644 interrupts = <20>; gpio-ranges = <&pinctrl 0 0 220>; clocks = <&syscon ASPEED_CLK_APB>; - interrupt-controller; +@@ -269,6 +269,15 @@ #interrupt-cells = <2>; }; @@ -37,7 +37,7 @@ index e4c5de3208e0..a3c456ba3f34 100644 rtc: rtc@1e781000 { compatible = "aspeed,ast2500-rtc"; reg = <0x1e781000 0x18>; -@@ -342,6 +351,13 @@ +@@ -344,6 +353,13 @@ status = "disabled"; }; diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-New-flash-map-for-intel.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-New-flash-map-for-intel.patch index 2ac429a22..11663c503 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-New-flash-map-for-intel.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0007-New-flash-map-for-intel.patch @@ -1,22 +1,23 @@ -From 074f1c74fde88aac3a10059e4928919782cd40d6 Mon Sep 17 00:00:00 2001 +From 3eabb52efdecfc0da896476ac5567060a6b3788a Mon Sep 17 00:00:00 2001 From: Vernon Mauery Date: Mon, 4 Jun 2018 13:45:42 -0700 Subject: [PATCH] New flash map for Intel - =================================================================== +Signed-off-by: Vernon Mauery +Signed-off-by: Vikram Bodireddy --- - .../boot/dts/openbmc-flash-layout-intel-128MB.dtsi | 58 ++++++++++++++++++++++ - .../boot/dts/openbmc-flash-layout-intel-64MB.dtsi | 39 +++++++++++++++ - 2 files changed, 97 insertions(+) + .../boot/dts/openbmc-flash-layout-intel-128MB.dtsi | 52 ++++++++++++++++++++++ + .../boot/dts/openbmc-flash-layout-intel-64MB.dtsi | 39 ++++++++++++++++ + 2 files changed, 91 insertions(+) create mode 100644 arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi create mode 100644 arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi diff --git a/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi b/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi new file mode 100644 -index 0000000..23426ac +index 000000000000..23426acc30c7 --- /dev/null +++ b/arch/arm/boot/dts/openbmc-flash-layout-intel-128MB.dtsi -@@ -0,0 +1,58 @@ +@@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0+ +// 128MB flash layout: PFR (active + tmp1/tmp2 + extra) +// image with common RW partition @@ -31,53 +32,47 @@ index 0000000..23426ac + label = "u-boot"; + }; + -+ fit-image-a@80000 { -+ reg = <0x80000 0x1b80000>; -+ label = "image-a"; ++ pfm@80000 { ++ reg = <0x80000 0x20000>; ++ label = "pfm"; + }; + -+ sofs@1c00000 { -+ reg = <0x1c00000 0x200000>; -+ label = "sofs"; ++ u-boot-env@a0000 { ++ reg = <0xa0000 0x20000>; ++ label = "u-boot-env"; + }; + -+ rwfs@1e00000 { -+ reg = <0x1e00000 0x600000>; -+ label = "rwfs"; ++ sofs@c0000 { ++ reg = <0xc0000 0x200000>; ++ label = "sofs"; + }; + -+ u-boot-env@2400000 { -+ reg = <0x2400000 0x20000>; -+ label = "u-boot-env"; ++ rwfs@2c0000 { ++ reg = <0x2c0000 0xe40000>; ++ label = "rwfs"; + }; + -+ /* -+ pfr-resvd@1260000 { -+ reg = <0x2460000 0x20000>; -+ label = "pfr-resvd"; ++ fit-image-a@1100000 { ++ reg = <0x1100000 0x2500000>; ++ label = "image-a"; + }; -+ */ + -+ rc1@2480000 { -+ reg = <0x2480000 0x1b80000>; -+ label = "rc1"; ++ rc-image@3600000 { ++ reg = <0x3600000 0x2500000>; ++ label = "rc-image"; + }; + -+ rc2@4000000 { -+ reg = <0x4000000 0x1b80000>; -+ label = "rc2"; ++ image-staging@5b00000 { ++ reg = <0x5b00000 0x2500000>; ++ label = "image-stg"; + }; + -+ bios-staging@6000000 { -+ reg = <0x6000000 0x2000000>; -+ label = "bios-staging"; -+ }; +}; + + diff --git a/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi b/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi new file mode 100644 -index 0000000..6ae8e57 +index 000000000000..6ae8e57087e2 --- /dev/null +++ b/arch/arm/boot/dts/openbmc-flash-layout-intel-64MB.dtsi @@ -0,0 +1,39 @@ diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch index 78824dde7..beb5087f5 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-Add-ASPEED-SGPIO-driver.patch @@ -1,4 +1,4 @@ -From 42505ffb3c24b3e7f8182af520ab1c10a3b3f3c4 Mon Sep 17 00:00:00 2001 +From 58adbd18074fbf8005d5d7a5ec116c326252f606 Mon Sep 17 00:00:00 2001 From: "Feist, James" Date: Mon, 5 Jun 2017 11:13:52 -0700 Subject: [PATCH] Add ASPEED SGPIO driver. @@ -6,7 +6,6 @@ Subject: [PATCH] Add ASPEED SGPIO driver. Port aspeed sgpio driver to OBMC Kernel and enable it on Purley config. Based off AST sdk 4.0. -Change-Id: I8529c3fb001ea6f93e63b269cdcdde3887a84e40 Signed-off-by: James Feist Signed-off-by: Jae Hyun Yoo --- @@ -17,7 +16,7 @@ Signed-off-by: Jae Hyun Yoo create mode 100644 drivers/gpio/sgpio-aspeed.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig -index 71c0ab46f216..a0485be99db7 100644 +index b5a2845347ec..e3ce2b68a1fc 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -124,6 +124,14 @@ config GPIO_ASPEED @@ -36,7 +35,7 @@ index 71c0ab46f216..a0485be99db7 100644 tristate "Atheros AR71XX/AR724X/AR913X GPIO support" default y if ATH79 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile -index 1324c8f966a7..23b8d29bef70 100644 +index 37628f8dbf70..069155f1db9e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0009-SGPIO-DT-and-pinctrl-fixup.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0009-SGPIO-DT-and-pinctrl-fixup.patch index 346b9e3e3..1c5d9ab53 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0009-SGPIO-DT-and-pinctrl-fixup.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0009-SGPIO-DT-and-pinctrl-fixup.patch @@ -1,4 +1,4 @@ -From f4b91f5c6723e56e106a609cdbcc8da48c56499e Mon Sep 17 00:00:00 2001 +From 2f895fe17cd72124b2a04af306f9349e5da90a6c Mon Sep 17 00:00:00 2001 From: Vernon Mauery Date: Wed, 16 May 2018 10:03:14 -0700 Subject: [PATCH] SGPIO DT and pinctrl fixup @@ -15,11 +15,11 @@ Signed-off-by: Jae Hyun Yoo 4 files changed, 54 insertions(+), 60 deletions(-) diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi -index 6af12872ee74..9aed0f696a98 100644 +index 3990aed25ee6..19f721118b52 100644 --- a/arch/arm/boot/dts/aspeed-g4.dtsi +++ b/arch/arm/boot/dts/aspeed-g4.dtsi -@@ -201,6 +201,18 @@ - interrupt-controller; +@@ -203,6 +203,18 @@ + #interrupt-cells = <2>; }; + sgpio: sgpio@1e780200 { @@ -37,7 +37,7 @@ index 6af12872ee74..9aed0f696a98 100644 timer: timer@1e782000 { /* This timer is a Faraday FTTMR010 derivative */ compatible = "aspeed,ast2400-timer"; -@@ -1150,44 +1162,14 @@ +@@ -1183,44 +1195,14 @@ groups = "SD2"; }; @@ -89,10 +89,10 @@ index 6af12872ee74..9aed0f696a98 100644 pinctrl_sioonctrl_default: sioonctrl_default { diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi -index 01e901031bd4..36d72c91a2ad 100644 +index 0c74adf739d2..d4c99b82f7bd 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi -@@ -274,6 +274,9 @@ +@@ -276,6 +276,9 @@ reg = <0x1e780200 0x0100>; interrupts = <40>; interrupt-controller; @@ -101,8 +101,8 @@ index 01e901031bd4..36d72c91a2ad 100644 + status = "disabled"; }; - timer: timer@1e782000 { -@@ -1324,6 +1327,11 @@ + rtc: rtc@1e781000 { +@@ -1388,6 +1391,11 @@ groups = "SDA2"; }; @@ -207,7 +207,7 @@ index 05b153034517..353af05b8602 100644 ASPEED_PINCTRL_FUNC(SIOPBI), ASPEED_PINCTRL_FUNC(SIOPBO), diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c -index 187abd7693cf..0c89647f166f 100644 +index 4230e1038a88..13f749e35001 100644 --- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c +++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g5.c @@ -577,6 +577,8 @@ SS_PIN_DECL(N3, GPIOJ2, SGPMO); diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch index 3cd1d9e84..db21250bb 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch @@ -1,4 +1,4 @@ -From ce7a88017fb2124100c4e5481a205034f34da23c Mon Sep 17 00:00:00 2001 +From 63ccbbe64f7e6560233971b886f6166fc59d20ef Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Mon, 7 Jan 2019 09:56:10 -0800 Subject: [PATCH] Update PECI drivers to sync with linux upstreaming version @@ -89,10 +89,10 @@ index 821a9258f2e6..a3a3e465c888 100644 +temp[6-*]_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of the core. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index 9e118d700b48..efe67f7faed3 100644 +index 996e80590b5b..93945eb19261 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig -@@ -1319,7 +1319,7 @@ config SENSORS_PECI_CPUTEMP +@@ -1321,7 +1321,7 @@ config SENSORS_PECI_CPUTEMP the PECI Client Command Suite via the processor PECI client. Check Documentation/hwmon/peci-cputemp for details. @@ -101,7 +101,7 @@ index 9e118d700b48..efe67f7faed3 100644 will be called peci-cputemp. config SENSORS_PECI_DIMMTEMP -@@ -1333,7 +1333,7 @@ config SENSORS_PECI_DIMMTEMP +@@ -1335,7 +1335,7 @@ config SENSORS_PECI_DIMMTEMP Suite via the processor PECI client. Check Documentation/hwmon/peci-dimmtemp for details. @@ -109,7 +109,7 @@ index 9e118d700b48..efe67f7faed3 100644 + This driver can also be built as a module. If so, the module will be called peci-dimmtemp. - source drivers/hwmon/pmbus/Kconfig + source "drivers/hwmon/pmbus/Kconfig" diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c index 11880c86a854..30ba1638e358 100644 --- a/drivers/hwmon/peci-cputemp.c @@ -663,10 +663,10 @@ index 6ca1855a86bb..ce6b470eae63 100644 /** diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig -index 66b71a6122d6..28a83b354ea8 100644 +index 9af5730ad7ba..28087e9cd4da 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig -@@ -596,7 +596,7 @@ config MFD_INTEL_MSIC +@@ -606,7 +606,7 @@ config MFD_INTEL_MSIC devices used in Intel Medfield platforms. config MFD_INTEL_PECI_CLIENT @@ -675,7 +675,7 @@ index 66b71a6122d6..28a83b354ea8 100644 depends on (PECI || COMPILE_TEST) select MFD_CORE help -@@ -609,6 +609,9 @@ config MFD_INTEL_PECI_CLIENT +@@ -619,6 +619,9 @@ config MFD_INTEL_PECI_CLIENT Additional drivers must be enabled in order to use the functionality of the device. @@ -1940,7 +1940,7 @@ index 51cb2563ceb6..000000000000 -MODULE_DESCRIPTION("ASPEED PECI driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c -index fac8c72dcda8..e2ef013e5002 100644 +index 6f241469ec7e..e2ef013e5002 100644 --- a/drivers/peci/peci-core.c +++ b/drivers/peci/peci-core.c @@ -1,38 +1,31 @@ @@ -2851,7 +2851,7 @@ index fac8c72dcda8..e2ef013e5002 100644 - return -ENOTTY; - } - -- if (!access_ok(VERIFY_WRITE, argp, msg_len)) +- if (!access_ok(argp, msg_len)) - return -EFAULT; - - msg = memdup_user(argp, msg_len); diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch index 95302aae8..e6dd44cd7 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0021-Initial-Port-of-Aspeed-LPC-SIO-driver.patch @@ -111,7 +111,7 @@ index 768278b059c3..de2d5c6d186c 100644 +obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_OCXL) += ocxl/ - obj-$(CONFIG_MISC_RTSX) += cardreader/ + obj-y += cardreader/ diff --git a/drivers/misc/aspeed-lpc-sio.c b/drivers/misc/aspeed-lpc-sio.c new file mode 100644 index 000000000000..c717a3182320 diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch index 120adbbc8..216c750de 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0022-Add-AST2500-eSPI-driver.patch @@ -1,4 +1,4 @@ -From a01815b4bb983ede71993d6c761dedd22d148b6b Mon Sep 17 00:00:00 2001 +From 3437db37b2f39a69505338546d9f846338de6c88 Mon Sep 17 00:00:00 2001 From: Haiyue Wang Date: Sat, 24 Feb 2018 11:12:32 +0800 Subject: [PATCH] eSPI: add ASPEED AST2500 eSPI driver to boot a host with PCH @@ -181,10 +181,10 @@ index 000000000000..185acd71bd26 +`_ + diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi -index 4a302d745b09..165a2bddc6cd 100644 +index da9e903808bc..01d27e845982 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi -@@ -266,6 +266,7 @@ +@@ -267,6 +267,7 @@ clocks = <&syscon ASPEED_CLK_APB>; interrupt-controller; #interrupt-cells = <2>; @@ -192,7 +192,7 @@ index 4a302d745b09..165a2bddc6cd 100644 }; sgpio: sgpio@1e780200 { -@@ -360,6 +361,9 @@ +@@ -361,6 +362,9 @@ reg = <0x1e6ee000 0x100>; interrupts = <23>; status = "disabled"; @@ -203,7 +203,7 @@ index 4a302d745b09..165a2bddc6cd 100644 lpc: lpc@1e789000 { diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig -index fe1e2a4072a8..f2062546250c 100644 +index d4ed3777462a..8b1fcf741411 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -485,6 +485,14 @@ config VEXPRESS_SYSCFG @@ -222,7 +222,7 @@ index fe1e2a4072a8..f2062546250c 100644 depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile -index a2b85ec21d09..bb89694e6b4b 100644 +index 7b018962cad3..89b051f82391 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_GENWQE) += genwqe/ diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch index 860a1ba5d..89a667e95 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0028-Add-AST2500-JTAG-driver.patch @@ -1,4 +1,4 @@ -From 43470f186979483ba6c1e6374c7ea3a129622862 Mon Sep 17 00:00:00 2001 +From 409ea2cede8588a59badd5dd7cf8721879d4c68a Mon Sep 17 00:00:00 2001 From: "Hunt, Bryan" Date: Fri, 30 Mar 2018 10:48:01 -0700 Subject: [PATCH] Add AST2500d JTAG driver @@ -21,10 +21,10 @@ Signed-off-by: Hunt, Bryan create mode 100644 include/uapi/linux/jtag_drv.h diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi -index a7bbc2adecc9..b63003c2c0c7 100644 +index 01d27e845982..adde826ac1d9 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi -@@ -366,6 +366,15 @@ +@@ -367,6 +367,15 @@ pinctrl-0 = <&pinctrl_espi_default>; }; @@ -41,17 +41,17 @@ index a7bbc2adecc9..b63003c2c0c7 100644 compatible = "aspeed,ast2500-lpc", "simple-mfd"; reg = <0x1e789000 0x1000>; diff --git a/drivers/Kconfig b/drivers/Kconfig -index c633db2b41fb..2778a5c33ca5 100644 +index bbb66439a307..a1579d66f47d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig -@@ -221,4 +221,5 @@ source "drivers/slimbus/Kconfig" +@@ -230,4 +230,5 @@ source "drivers/slimbus/Kconfig" source "drivers/peci/Kconfig" +source "drivers/jtag/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile -index 63c9b425e6e1..714067945fd2 100644 +index 9ec44c032a42..69b201766154 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -187,3 +187,4 @@ obj-$(CONFIG_UNISYS_VISORBUS) += visorbus/ diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch index 539c976c7..e015f2fd9 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0032-misc-aspeed-Add-Aspeed-UART-routing-control-driver.patch @@ -1,4 +1,4 @@ -From e39e3a3e54cbe8e5a39b4148a9232f4570d009a6 Mon Sep 17 00:00:00 2001 +From 37b192b278d5ea5da62b2fcff4fce7cf372e4fe6 Mon Sep 17 00:00:00 2001 From: Oskar Senft Date: Wed, 8 Aug 2018 10:15:05 -0400 Subject: [PATCH] misc: aspeed: Add Aspeed UART routing control driver. @@ -102,10 +102,10 @@ index 000000000000..afaf17cb7eda +$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1 +io1 io2 io3 io4 uart2 [uart3] uart4 io6 diff --git a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts -index 655bb37e422f..eb05f5a2c480 100644 +index 8aba46cdce46..d184fdf6dda6 100644 --- a/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts +++ b/arch/arm/boot/dts/aspeed-bmc-intel-purley.dts -@@ -174,6 +174,10 @@ +@@ -227,6 +227,10 @@ status = "okay"; }; @@ -117,10 +117,10 @@ index 655bb37e422f..eb05f5a2c480 100644 status = "okay"; diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi -index 3bb31c1daf9d..92843cc1a8f4 100644 +index adde826ac1d9..5606ac1d96d5 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi -@@ -482,6 +482,12 @@ +@@ -479,6 +479,12 @@ status = "disabled"; }; }; @@ -134,12 +134,12 @@ index 3bb31c1daf9d..92843cc1a8f4 100644 peci: bus@1e78b000 { diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig -index f2062546250c..8e2fc51dcc44 100644 +index 8b1fcf741411..60f203c04b9b 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig -@@ -537,6 +537,12 @@ config MISC_RTSX - tristate - default MISC_RTSX_PCI || MISC_RTSX_USB +@@ -560,6 +560,12 @@ config NPCM7XX_PCI_MBOX + Expose the NPCM750/730/715/705 PCI MBOX registers found on + Nuvoton SOCs to userspace. +config ASPEED_UART_ROUTING + tristate "Aspeed ast2500 UART routing control" @@ -151,7 +151,7 @@ index f2062546250c..8e2fc51dcc44 100644 source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile -index bb89694e6b4b..0f00eb63556c 100644 +index 89b051f82391..8f70b888a9ca 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_CXL_BASE) += cxl/ diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-adpeed-Swap-the-mac-nodes-numbering.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-adpeed-Swap-the-mac-nodes-numbering.patch deleted file mode 100644 index eef3bee6f..000000000 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-adpeed-Swap-the-mac-nodes-numbering.patch +++ /dev/null @@ -1,85 +0,0 @@ -From 9c509b9450f641c169ee3aeb60e398c43810dcb2 Mon Sep 17 00:00:00 2001 -From: Jae Hyun Yoo -Date: Wed, 3 Oct 2018 10:17:58 -0700 -Subject: [PATCH] arm: dts: adpeed: Swap the mac nodes numbering - -This patch swaps the numbering of mac0 and mac1 to make a dedicated -nic get assigned the first ethernet device number. - -Signed-off-by: Jae Hyun Yoo ---- - arch/arm/boot/dts/aspeed-g4.dtsi | 16 ++++++++-------- - arch/arm/boot/dts/aspeed-g5.dtsi | 16 ++++++++-------- - 2 files changed, 16 insertions(+), 16 deletions(-) - -diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi -index 22eab8a952ed..004bbb08dd4a 100644 ---- a/arch/arm/boot/dts/aspeed-g4.dtsi -+++ b/arch/arm/boot/dts/aspeed-g4.dtsi -@@ -101,14 +101,6 @@ - reg = <0x1e6c2000 0x80>; - }; - -- mac0: ethernet@1e660000 { -- compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; -- reg = <0x1e660000 0x180>; -- interrupts = <2>; -- clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; -- status = "disabled"; -- }; -- - mac1: ethernet@1e680000 { - compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; - reg = <0x1e680000 0x180>; -@@ -117,6 +109,14 @@ - status = "disabled"; - }; - -+ mac0: ethernet@1e660000 { -+ compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; -+ reg = <0x1e660000 0x180>; -+ interrupts = <2>; -+ clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; -+ status = "disabled"; -+ }; -+ - ehci0: usb@1e6a1000 { - compatible = "aspeed,ast2400-ehci", "generic-ehci"; - reg = <0x1e6a1000 0x100>; -diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi -index 92843cc1a8f4..30a7f349feeb 100644 ---- a/arch/arm/boot/dts/aspeed-g5.dtsi -+++ b/arch/arm/boot/dts/aspeed-g5.dtsi -@@ -142,14 +142,6 @@ - reg = <0x1e6c2000 0x80>; - }; - -- mac0: ethernet@1e660000 { -- compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; -- reg = <0x1e660000 0x180>; -- interrupts = <2>; -- clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; -- status = "disabled"; -- }; -- - mac1: ethernet@1e680000 { - compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; - reg = <0x1e680000 0x180>; -@@ -158,6 +150,14 @@ - status = "disabled"; - }; - -+ mac0: ethernet@1e660000 { -+ compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; -+ reg = <0x1e660000 0x180>; -+ interrupts = <2>; -+ clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; -+ status = "disabled"; -+ }; -+ - ehci0: usb@1e6a1000 { - compatible = "aspeed,ast2500-ehci", "generic-ehci"; - reg = <0x1e6a1000 0x100>; --- -2.7.4 - diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch new file mode 100644 index 000000000..b819be69b --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0034-arm-dts-aspeed-Swap-the-mac-nodes-numbering.patch @@ -0,0 +1,85 @@ +From 9c509b9450f641c169ee3aeb60e398c43810dcb2 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo +Date: Wed, 3 Oct 2018 10:17:58 -0700 +Subject: [PATCH] arm: dts: aspeed: Swap the mac nodes numbering + +This patch swaps the numbering of mac0 and mac1 to make a dedicated +nic get assigned the first ethernet device number. + +Signed-off-by: Jae Hyun Yoo +--- + arch/arm/boot/dts/aspeed-g4.dtsi | 16 ++++++++-------- + arch/arm/boot/dts/aspeed-g5.dtsi | 16 ++++++++-------- + 2 files changed, 16 insertions(+), 16 deletions(-) + +diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi +index 22eab8a952ed..004bbb08dd4a 100644 +--- a/arch/arm/boot/dts/aspeed-g4.dtsi ++++ b/arch/arm/boot/dts/aspeed-g4.dtsi +@@ -101,14 +101,6 @@ + reg = <0x1e6c2000 0x80>; + }; + +- mac0: ethernet@1e660000 { +- compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; +- reg = <0x1e660000 0x180>; +- interrupts = <2>; +- clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; +- status = "disabled"; +- }; +- + mac1: ethernet@1e680000 { + compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; + reg = <0x1e680000 0x180>; +@@ -117,6 +109,14 @@ + status = "disabled"; + }; + ++ mac0: ethernet@1e660000 { ++ compatible = "aspeed,ast2400-mac", "faraday,ftgmac100"; ++ reg = <0x1e660000 0x180>; ++ interrupts = <2>; ++ clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; ++ status = "disabled"; ++ }; ++ + ehci0: usb@1e6a1000 { + compatible = "aspeed,ast2400-ehci", "generic-ehci"; + reg = <0x1e6a1000 0x100>; +diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi +index 92843cc1a8f4..30a7f349feeb 100644 +--- a/arch/arm/boot/dts/aspeed-g5.dtsi ++++ b/arch/arm/boot/dts/aspeed-g5.dtsi +@@ -142,14 +142,6 @@ + reg = <0x1e6c2000 0x80>; + }; + +- mac0: ethernet@1e660000 { +- compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; +- reg = <0x1e660000 0x180>; +- interrupts = <2>; +- clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; +- status = "disabled"; +- }; +- + mac1: ethernet@1e680000 { + compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; + reg = <0x1e680000 0x180>; +@@ -158,6 +150,14 @@ + status = "disabled"; + }; + ++ mac0: ethernet@1e660000 { ++ compatible = "aspeed,ast2500-mac", "faraday,ftgmac100"; ++ reg = <0x1e660000 0x180>; ++ interrupts = <2>; ++ clocks = <&syscon ASPEED_CLK_GATE_MAC1CLK>; ++ status = "disabled"; ++ }; ++ + ehci0: usb@1e6a1000 { + compatible = "aspeed,ast2500-ehci", "generic-ehci"; + reg = <0x1e6a1000 0x100>; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch index 51ddbb18e..3863ea8f6 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0035-Implement-a-memory-driver-share-memory.patch @@ -1,5 +1,5 @@ -From 1d459c15998c9a79ba7a758cef6129ed29f3b958 Mon Sep 17 00:00:00 2001 -From: cyang29 +From dae410353f8681b58907c61eb2eb056513d86f6d Mon Sep 17 00:00:00 2001 +From: Cheng C Yang Date: Fri, 9 Nov 2018 10:24:37 +0800 Subject: [PATCH] Implement a memory driver share memory @@ -8,7 +8,7 @@ The driver is used by MDRV2. In MDRV2 BIOS will send whole SMBIOS table to VGA memory and BMC can get the table from VGA memory through this driver. -Signed-off-by: cyang29 +Signed-off-by: Cheng C Yang --- .../devicetree/bindings/misc/vga-shared-memory.txt | 20 +++ drivers/misc/Kconfig | 10 ++ @@ -45,10 +45,10 @@ index 000000000000..03f57c53e844 + reg = <0x9ff00000 0x100000>; +}; diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig -index 8e2fc51dcc44..1279a9674537 100644 +index 60f203c04b9b..2d4c6ba87e70 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig -@@ -543,6 +543,16 @@ config ASPEED_UART_ROUTING +@@ -566,6 +566,16 @@ config ASPEED_UART_ROUTING If you want to configure UART routing on Aspeed BMC platforms, enable this option. @@ -66,14 +66,17 @@ index 8e2fc51dcc44..1279a9674537 100644 source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile -index 0f00eb63556c..f4951a6e435b 100644 +index 8f70b888a9ca..30ee065491ef 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile -@@ -62,3 +62,4 @@ obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o +@@ -59,6 +59,7 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o + obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o + obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o + obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o ++obj-$(CONFIG_ASPEED_VGA_SHAREDMEM) += aspeed-vga-sharedmem.o obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_OCXL) += ocxl/ - obj-$(CONFIG_MISC_RTSX) += cardreader/ -+obj-$(CONFIG_ASPEED_VGA_SHAREDMEM) += aspeed-vga-sharedmem.o + obj-y += cardreader/ diff --git a/drivers/misc/aspeed-vga-sharedmem.c b/drivers/misc/aspeed-vga-sharedmem.c new file mode 100644 index 000000000000..76f60cd67d3a @@ -245,5 +248,5 @@ index 000000000000..76f60cd67d3a +MODULE_DESCRIPTION("Shared VGA memory"); +MODULE_LICENSE("GPL v2"); -- -2.16.2 +2.7.4 diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch deleted file mode 100644 index 83717369c..000000000 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0036-net-ncsi-backport-ncsi-patches.patch +++ /dev/null @@ -1,1425 +0,0 @@ -From 58c3299017c5e6022fb2a2a74b662b2a4c0306f5 Mon Sep 17 00:00:00 2001 -From: Jae Hyun Yoo -Date: Tue, 20 Nov 2018 10:14:47 -0800 -Subject: [PATCH] net/ncsi: backport ncsi patches - -net/ncsi: Allow enabling multiple packages & channels - -This series extends the NCSI driver to configure multiple packages -and/or channels simultaneously. Since the RFC series this includes a few -extra changes to fix areas in the driver that either made this harder or -were roadblocks due to deviations from the NCSI specification. - -Patches 1 & 2 fix two issues where the driver made assumptions about the -capabilities of the NCSI topology. -Patches 3 & 4 change some internal semantics slightly to make multi-mode -easier. -Patch 5 introduces a cleaner way of reconfiguring the NCSI configuration -and keeping track of channel states. -Patch 6 implements the main multi-package/multi-channel configuration, -configured via the Netlink interface. - -Readers who have an interesting NCSI setup - especially multi-package -with HWA - please test! I think I've covered all permutations but I -don't have infinite hardware to test on. - -net/ncsi: Don't enable all channels when HWA available - -NCSI hardware arbitration allows multiple packages to be enabled at once -and share the same wiring. If the NCSI driver recognises that HWA is -available it unconditionally enables all packages and channels; but that -is a configuration decision rather than something required by HWA. -Additionally the current implementation will not failover on link events -which can cause connectivity to be lost unless the interface is manually -bounced. - -Retain basic HWA support but remove the separate configuration path to -enable all channels, leaving this to be handled by a later -implementation. - -net/ncsi: Probe single packages to avoid conflict - -Currently the NCSI driver sends a select-package command to all possible -packages simultaneously to discover what packages are available. However -at this stage in the probe process the driver does not know if -hardware arbitration is available: if it isn't then this process could -cause collisions on the RMII bus when packages try to respond. - -Update the probe loop to probe each package one by one, and once -complete check if HWA is universally supported. - -net/ncsi: Don't deselect package in suspend if active - -When a package is deselected all channels of that package cease -communication. If there are other channels active on the package of the -suspended channel this will disable them as well, so only send a -deselect-package command if no other channels are active. - -net/ncsi: Don't mark configured channels inactive - -The concepts of a channel being 'active' and it having link are slightly -muddled in the NCSI driver. Tweak this slightly so that -NCSI_CHANNEL_ACTIVE represents a channel that has been configured and -enabled, and NCSI_CHANNEL_INACTIVE represents a de-configured channel. -This distinction is important because a channel can be 'active' but have -its link down; in this case the channel may still need to be configured -so that it may receive AEN link-state-change packets. - -net/ncsi: Reset channel state in ncsi_start_dev() - -When the NCSI driver is stopped with ncsi_stop_dev() the channel -monitors are stopped and the state set to "inactive". However the -channels are still configured and active from the perspective of the -network controller. We should suspend each active channel but in the -context of ncsi_stop_dev() the transmit queue has been or is about to be -stopped so we won't have time to do so. - -Instead when ncsi_start_dev() is called if the NCSI topology has already -been probed then call ncsi_reset_dev() to suspend any channels that were -previously active. This resets the network controller to a known state, -provides an up to date view of channel link state, and makes sure that -mode flags such as NCSI_MODE_TX_ENABLE are properly reset. - -In addition to ncsi_start_dev() use ncsi_reset_dev() in ncsi-netlink.c -to update the channel configuration more cleanly. - -net/ncsi: Configure multi-package, multi-channel modes with failover - -This patch extends the ncsi-netlink interface with two new commands and -three new attributes to configure multiple packages and/or channels at -once, and configure specific failover modes. - -NCSI_CMD_SET_PACKAGE mask and NCSI_CMD_SET_CHANNEL_MASK set a whitelist -of packages or channels allowed to be configured with the -NCSI_ATTR_PACKAGE_MASK and NCSI_ATTR_CHANNEL_MASK attributes -respectively. If one of these whitelists is set only packages or -channels matching the whitelist are considered for the channel queue in -ncsi_choose_active_channel(). - -These commands may also use the NCSI_ATTR_MULTI_FLAG to signal that -multiple packages or channels may be configured simultaneously. NCSI -hardware arbitration (HWA) must be available in order to enable -multi-package mode. Multi-channel mode is always available. - -If the NCSI_ATTR_CHANNEL_ID attribute is present in the -NCSI_CMD_SET_CHANNEL_MASK command the it sets the preferred channel as -with the NCSI_CMD_SET_INTERFACE command. The combination of preferred -channel and channel whitelist defines a primary channel and the allowed -failover channels. -If the NCSI_ATTR_MULTI_FLAG attribute is also present then the preferred -channel is configured for Tx/Rx and the other channels are enabled only -for Rx. - -Signed-off-by: Samuel Mendoza-Jonas -Signed-off-by: Jae Hyun Yoo ---- - include/uapi/linux/ncsi.h | 15 ++ - net/ncsi/internal.h | 19 +- - net/ncsi/ncsi-aen.c | 75 +++++-- - net/ncsi/ncsi-manage.c | 522 ++++++++++++++++++++++++++++++++-------------- - net/ncsi/ncsi-netlink.c | 233 ++++++++++++++++++--- - net/ncsi/ncsi-rsp.c | 2 +- - 6 files changed, 660 insertions(+), 206 deletions(-) - -diff --git a/include/uapi/linux/ncsi.h b/include/uapi/linux/ncsi.h -index 0a26a5576645..a3f87c54fdb3 100644 ---- a/include/uapi/linux/ncsi.h -+++ b/include/uapi/linux/ncsi.h -@@ -26,6 +26,12 @@ - * @NCSI_CMD_SEND_CMD: send NC-SI command to network card. - * Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID - * and NCSI_ATTR_CHANNEL_ID. -+ * @NCSI_CMD_SET_PACKAGE_MASK: set a whitelist of allowed packages. -+ * Requires NCSI_ATTR_IFINDEX and NCSI_ATTR_PACKAGE_MASK. -+ * @NCSI_CMD_SET_CHANNEL_MASK: set a whitelist of allowed channels. -+ * Requires NCSI_ATTR_IFINDEX, NCSI_ATTR_PACKAGE_ID, and -+ * NCSI_ATTR_CHANNEL_MASK. If NCSI_ATTR_CHANNEL_ID is present it sets -+ * the primary channel. - * @NCSI_CMD_MAX: highest command number - */ - enum ncsi_nl_commands { -@@ -34,6 +40,8 @@ enum ncsi_nl_commands { - NCSI_CMD_SET_INTERFACE, - NCSI_CMD_CLEAR_INTERFACE, - NCSI_CMD_SEND_CMD, -+ NCSI_CMD_SET_PACKAGE_MASK, -+ NCSI_CMD_SET_CHANNEL_MASK, - - __NCSI_CMD_AFTER_LAST, - NCSI_CMD_MAX = __NCSI_CMD_AFTER_LAST - 1 -@@ -48,6 +56,10 @@ enum ncsi_nl_commands { - * @NCSI_ATTR_PACKAGE_ID: package ID - * @NCSI_ATTR_CHANNEL_ID: channel ID - * @NCSI_ATTR_DATA: command payload -+ * @NCSI_ATTR_MULTI_FLAG: flag to signal that multi-mode should be enabled with -+ * NCSI_CMD_SET_PACKAGE_MASK or NCSI_CMD_SET_CHANNEL_MASK. -+ * @NCSI_ATTR_PACKAGE_MASK: 32-bit mask of allowed packages. -+ * @NCSI_ATTR_CHANNEL_MASK: 32-bit mask of allowed channels. - * @NCSI_ATTR_MAX: highest attribute number - */ - enum ncsi_nl_attrs { -@@ -57,6 +69,9 @@ enum ncsi_nl_attrs { - NCSI_ATTR_PACKAGE_ID, - NCSI_ATTR_CHANNEL_ID, - NCSI_ATTR_DATA, -+ NCSI_ATTR_MULTI_FLAG, -+ NCSI_ATTR_PACKAGE_MASK, -+ NCSI_ATTR_CHANNEL_MASK, - - __NCSI_ATTR_AFTER_LAST, - NCSI_ATTR_MAX = __NCSI_ATTR_AFTER_LAST - 1 -diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h -index 1dae77c54009..9e3642b802c4 100644 ---- a/net/ncsi/internal.h -+++ b/net/ncsi/internal.h -@@ -222,6 +222,10 @@ struct ncsi_package { - unsigned int channel_num; /* Number of channels */ - struct list_head channels; /* List of chanels */ - struct list_head node; /* Form list of packages */ -+ -+ bool multi_channel; /* Enable multiple channels */ -+ u32 channel_whitelist; /* Channels to configure */ -+ struct ncsi_channel *preferred_channel; /* Primary channel */ - }; - - struct ncsi_request { -@@ -287,16 +291,16 @@ struct ncsi_dev_priv { - #define NCSI_DEV_PROBED 1 /* Finalized NCSI topology */ - #define NCSI_DEV_HWA 2 /* Enabled HW arbitration */ - #define NCSI_DEV_RESHUFFLE 4 -+#define NCSI_DEV_RESET 8 /* Reset state of NC */ - unsigned int gma_flag; /* OEM GMA flag */ - spinlock_t lock; /* Protect the NCSI device */ - #if IS_ENABLED(CONFIG_IPV6) - unsigned int inet6_addr_num; /* Number of IPv6 addresses */ - #endif -+ unsigned int package_probe_id;/* Current ID during probe */ - unsigned int package_num; /* Number of packages */ - struct list_head packages; /* List of packages */ - struct ncsi_channel *hot_channel; /* Channel was ever active */ -- struct ncsi_package *force_package; /* Force a specific package */ -- struct ncsi_channel *force_channel; /* Force a specific channel */ - struct ncsi_request requests[256]; /* Request table */ - unsigned int request_id; /* Last used request ID */ - #define NCSI_REQ_START_IDX 1 -@@ -309,6 +313,9 @@ struct ncsi_dev_priv { - struct list_head node; /* Form NCSI device list */ - #define NCSI_MAX_VLAN_VIDS 15 - struct list_head vlan_vids; /* List of active VLAN IDs */ -+ -+ bool multi_package; /* Enable multiple packages */ -+ u32 package_whitelist; /* Packages to configure */ - }; - - struct ncsi_cmd_arg { -@@ -341,6 +348,7 @@ extern spinlock_t ncsi_dev_lock; - list_for_each_entry_rcu(nc, &np->channels, node) - - /* Resources */ -+int ncsi_reset_dev(struct ncsi_dev *nd); - void ncsi_start_channel_monitor(struct ncsi_channel *nc); - void ncsi_stop_channel_monitor(struct ncsi_channel *nc); - struct ncsi_channel *ncsi_find_channel(struct ncsi_package *np, -@@ -361,6 +369,13 @@ struct ncsi_request *ncsi_alloc_request(struct ncsi_dev_priv *ndp, - void ncsi_free_request(struct ncsi_request *nr); - struct ncsi_dev *ncsi_find_dev(struct net_device *dev); - int ncsi_process_next_channel(struct ncsi_dev_priv *ndp); -+bool ncsi_channel_has_link(struct ncsi_channel *channel); -+bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp, -+ struct ncsi_channel *channel); -+int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp, -+ struct ncsi_package *np, -+ struct ncsi_channel *disable, -+ struct ncsi_channel *enable); - - /* Packet handlers */ - u32 ncsi_calculate_checksum(unsigned char *data, int len); -diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c -index 25e483e8278b..26d67e27551f 100644 ---- a/net/ncsi/ncsi-aen.c -+++ b/net/ncsi/ncsi-aen.c -@@ -50,13 +50,15 @@ static int ncsi_validate_aen_pkt(struct ncsi_aen_pkt_hdr *h, - static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, - struct ncsi_aen_pkt_hdr *h) - { -- struct ncsi_aen_lsc_pkt *lsc; -- struct ncsi_channel *nc; -+ struct ncsi_channel *nc, *tmp; - struct ncsi_channel_mode *ncm; -- bool chained; -- int state; - unsigned long old_data, data; -+ struct ncsi_aen_lsc_pkt *lsc; -+ struct ncsi_package *np; -+ bool had_link, has_link; - unsigned long flags; -+ bool chained; -+ int state; - - /* Find the NCSI channel */ - ncsi_find_package_and_channel(ndp, h->common.channel, NULL, &nc); -@@ -73,6 +75,9 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, - ncm->data[2] = data; - ncm->data[4] = ntohl(lsc->oem_status); - -+ had_link = !!(old_data & 0x1); -+ has_link = !!(data & 0x1); -+ - netdev_dbg(ndp->ndev.dev, "NCSI: LSC AEN - channel %u state %s\n", - nc->id, data & 0x1 ? "up" : "down"); - -@@ -80,22 +85,60 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, - state = nc->state; - spin_unlock_irqrestore(&nc->lock, flags); - -- if (!((old_data ^ data) & 0x1) || chained) -- return 0; -- if (!(state == NCSI_CHANNEL_INACTIVE && (data & 0x1)) && -- !(state == NCSI_CHANNEL_ACTIVE && !(data & 0x1))) -+ if (state == NCSI_CHANNEL_INACTIVE) -+ netdev_warn(ndp->ndev.dev, -+ "NCSI: Inactive channel %u received AEN!\n", -+ nc->id); -+ -+ if ((had_link == has_link) || chained) - return 0; - -- if (!(ndp->flags & NCSI_DEV_HWA) && -- state == NCSI_CHANNEL_ACTIVE) -- ndp->flags |= NCSI_DEV_RESHUFFLE; -+ if (!ndp->multi_package && !nc->package->multi_channel) { -+ if (had_link) { -+ ndp->flags |= NCSI_DEV_RESHUFFLE; -+ ncsi_stop_channel_monitor(nc); -+ spin_lock_irqsave(&ndp->lock, flags); -+ list_add_tail_rcu(&nc->link, &ndp->channel_queue); -+ spin_unlock_irqrestore(&ndp->lock, flags); -+ return ncsi_process_next_channel(ndp); -+ } -+ /* Configured channel came up */ -+ return 0; -+ } - -- ncsi_stop_channel_monitor(nc); -- spin_lock_irqsave(&ndp->lock, flags); -- list_add_tail_rcu(&nc->link, &ndp->channel_queue); -- spin_unlock_irqrestore(&ndp->lock, flags); -+ if (had_link) { -+ ncm = &nc->modes[NCSI_MODE_TX_ENABLE]; -+ if (ncsi_channel_is_last(ndp, nc)) { -+ /* No channels left, reconfigure */ -+ return ncsi_reset_dev(&ndp->ndev); -+ } else if (ncm->enable) { -+ /* Need to failover Tx channel */ -+ ncsi_update_tx_channel(ndp, nc->package, nc, NULL); -+ } -+ } else if (has_link && nc->package->preferred_channel == nc) { -+ /* Return Tx to preferred channel */ -+ ncsi_update_tx_channel(ndp, nc->package, NULL, nc); -+ } else if (has_link) { -+ NCSI_FOR_EACH_PACKAGE(ndp, np) { -+ NCSI_FOR_EACH_CHANNEL(np, tmp) { -+ /* Enable Tx on this channel if the current Tx -+ * channel is down. -+ */ -+ ncm = &tmp->modes[NCSI_MODE_TX_ENABLE]; -+ if (ncm->enable && -+ !ncsi_channel_has_link(tmp)) { -+ ncsi_update_tx_channel(ndp, nc->package, -+ tmp, nc); -+ break; -+ } -+ } -+ } -+ } - -- return ncsi_process_next_channel(ndp); -+ /* Leave configured channels active in a multi-channel scenario so -+ * AEN events are still received. -+ */ -+ return 0; - } - - static int ncsi_aen_handler_cr(struct ncsi_dev_priv *ndp, -diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c -index bfc43b28c7a6..92e59f07f9a7 100644 ---- a/net/ncsi/ncsi-manage.c -+++ b/net/ncsi/ncsi-manage.c -@@ -28,6 +28,29 @@ - LIST_HEAD(ncsi_dev_list); - DEFINE_SPINLOCK(ncsi_dev_lock); - -+bool ncsi_channel_has_link(struct ncsi_channel *channel) -+{ -+ return !!(channel->modes[NCSI_MODE_LINK].data[2] & 0x1); -+} -+ -+bool ncsi_channel_is_last(struct ncsi_dev_priv *ndp, -+ struct ncsi_channel *channel) -+{ -+ struct ncsi_package *np; -+ struct ncsi_channel *nc; -+ -+ NCSI_FOR_EACH_PACKAGE(ndp, np) -+ NCSI_FOR_EACH_CHANNEL(np, nc) { -+ if (nc == channel) -+ continue; -+ if (nc->state == NCSI_CHANNEL_ACTIVE && -+ ncsi_channel_has_link(nc)) -+ return false; -+ } -+ -+ return true; -+} -+ - static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down) - { - struct ncsi_dev *nd = &ndp->ndev; -@@ -52,7 +75,7 @@ static void ncsi_report_link(struct ncsi_dev_priv *ndp, bool force_down) - continue; - } - -- if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) { -+ if (ncsi_channel_has_link(nc)) { - spin_unlock_irqrestore(&nc->lock, flags); - nd->link_up = 1; - goto report; -@@ -113,10 +136,8 @@ static void ncsi_channel_monitor(struct timer_list *t) - default: - netdev_err(ndp->ndev.dev, "NCSI Channel %d timed out!\n", - nc->id); -- if (!(ndp->flags & NCSI_DEV_HWA)) { -- ncsi_report_link(ndp, true); -- ndp->flags |= NCSI_DEV_RESHUFFLE; -- } -+ ncsi_report_link(ndp, true); -+ ndp->flags |= NCSI_DEV_RESHUFFLE; - - ncsi_stop_channel_monitor(nc); - -@@ -269,6 +290,7 @@ struct ncsi_package *ncsi_add_package(struct ncsi_dev_priv *ndp, - np->ndp = ndp; - spin_lock_init(&np->lock); - INIT_LIST_HEAD(&np->channels); -+ np->channel_whitelist = UINT_MAX; - - spin_lock_irqsave(&ndp->lock, flags); - tmp = ncsi_find_package(ndp, id); -@@ -442,12 +464,14 @@ static void ncsi_request_timeout(struct timer_list *t) - static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) - { - struct ncsi_dev *nd = &ndp->ndev; -- struct ncsi_package *np = ndp->active_package; -- struct ncsi_channel *nc = ndp->active_channel; -+ struct ncsi_package *np; -+ struct ncsi_channel *nc, *tmp; - struct ncsi_cmd_arg nca; - unsigned long flags; - int ret; - -+ np = ndp->active_package; -+ nc = ndp->active_channel; - nca.ndp = ndp; - nca.req_flags = NCSI_REQ_FLAG_EVENT_DRIVEN; - switch (nd->state) { -@@ -523,6 +547,15 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) - if (ret) - goto error; - -+ NCSI_FOR_EACH_CHANNEL(np, tmp) { -+ /* If there is another channel active on this package -+ * do not deselect the package. -+ */ -+ if (tmp != nc && tmp->state == NCSI_CHANNEL_ACTIVE) { -+ nd->state = ncsi_dev_state_suspend_done; -+ break; -+ } -+ } - break; - case ncsi_dev_state_suspend_deselect: - ndp->pending_req_num = 1; -@@ -541,8 +574,10 @@ static void ncsi_suspend_channel(struct ncsi_dev_priv *ndp) - spin_lock_irqsave(&nc->lock, flags); - nc->state = NCSI_CHANNEL_INACTIVE; - spin_unlock_irqrestore(&nc->lock, flags); -- ncsi_process_next_channel(ndp); -- -+ if (ndp->flags & NCSI_DEV_RESET) -+ ncsi_reset_dev(nd); -+ else -+ ncsi_process_next_channel(ndp); - break; - default: - netdev_warn(nd->dev, "Wrong NCSI state 0x%x in suspend\n", -@@ -717,13 +752,144 @@ static int ncsi_gma_handler(struct ncsi_cmd_arg *nca, unsigned int mf_id) - - #endif /* CONFIG_NCSI_OEM_CMD_GET_MAC */ - -+/* Determine if a given channel from the channel_queue should be used for Tx */ -+static bool ncsi_channel_is_tx(struct ncsi_dev_priv *ndp, -+ struct ncsi_channel *nc) -+{ -+ struct ncsi_channel_mode *ncm; -+ struct ncsi_channel *channel; -+ struct ncsi_package *np; -+ -+ /* Check if any other channel has Tx enabled; a channel may have already -+ * been configured and removed from the channel queue. -+ */ -+ NCSI_FOR_EACH_PACKAGE(ndp, np) { -+ if (!ndp->multi_package && np != nc->package) -+ continue; -+ NCSI_FOR_EACH_CHANNEL(np, channel) { -+ ncm = &channel->modes[NCSI_MODE_TX_ENABLE]; -+ if (ncm->enable) -+ return false; -+ } -+ } -+ -+ /* This channel is the preferred channel and has link */ -+ list_for_each_entry_rcu(channel, &ndp->channel_queue, link) { -+ np = channel->package; -+ if (np->preferred_channel && -+ ncsi_channel_has_link(np->preferred_channel)) { -+ return np->preferred_channel == nc; -+ } -+ } -+ -+ /* This channel has link */ -+ if (ncsi_channel_has_link(nc)) -+ return true; -+ -+ list_for_each_entry_rcu(channel, &ndp->channel_queue, link) -+ if (ncsi_channel_has_link(channel)) -+ return false; -+ -+ /* No other channel has link; default to this one */ -+ return true; -+} -+ -+/* Change the active Tx channel in a multi-channel setup */ -+int ncsi_update_tx_channel(struct ncsi_dev_priv *ndp, -+ struct ncsi_package *package, -+ struct ncsi_channel *disable, -+ struct ncsi_channel *enable) -+{ -+ struct ncsi_cmd_arg nca; -+ struct ncsi_channel *nc; -+ struct ncsi_package *np; -+ int ret = 0; -+ -+ if (!package->multi_channel && !ndp->multi_package) -+ netdev_warn(ndp->ndev.dev, -+ "NCSI: Trying to update Tx channel in single-channel mode\n"); -+ nca.ndp = ndp; -+ nca.req_flags = 0; -+ -+ /* Find current channel with Tx enabled */ -+ NCSI_FOR_EACH_PACKAGE(ndp, np) { -+ if (disable) -+ break; -+ if (!ndp->multi_package && np != package) -+ continue; -+ -+ NCSI_FOR_EACH_CHANNEL(np, nc) -+ if (nc->modes[NCSI_MODE_TX_ENABLE].enable) { -+ disable = nc; -+ break; -+ } -+ } -+ -+ /* Find a suitable channel for Tx */ -+ NCSI_FOR_EACH_PACKAGE(ndp, np) { -+ if (enable) -+ break; -+ if (!ndp->multi_package && np != package) -+ continue; -+ if (!(ndp->package_whitelist & (0x1 << np->id))) -+ continue; -+ -+ if (np->preferred_channel && -+ ncsi_channel_has_link(np->preferred_channel)) { -+ enable = np->preferred_channel; -+ break; -+ } -+ -+ NCSI_FOR_EACH_CHANNEL(np, nc) { -+ if (!(np->channel_whitelist & 0x1 << nc->id)) -+ continue; -+ if (nc->state != NCSI_CHANNEL_ACTIVE) -+ continue; -+ if (ncsi_channel_has_link(nc)) { -+ enable = nc; -+ break; -+ } -+ } -+ } -+ -+ if (disable == enable) -+ return -1; -+ -+ if (!enable) -+ return -1; -+ -+ if (disable) { -+ nca.channel = disable->id; -+ nca.package = disable->package->id; -+ nca.type = NCSI_PKT_CMD_DCNT; -+ ret = ncsi_xmit_cmd(&nca); -+ if (ret) -+ netdev_err(ndp->ndev.dev, -+ "Error %d sending DCNT\n", -+ ret); -+ } -+ -+ netdev_info(ndp->ndev.dev, "NCSI: channel %u enables Tx\n", enable->id); -+ -+ nca.channel = enable->id; -+ nca.package = enable->package->id; -+ nca.type = NCSI_PKT_CMD_ECNT; -+ ret = ncsi_xmit_cmd(&nca); -+ if (ret) -+ netdev_err(ndp->ndev.dev, -+ "Error %d sending ECNT\n", -+ ret); -+ -+ return ret; -+} -+ - static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) - { -- struct ncsi_dev *nd = &ndp->ndev; -- struct net_device *dev = nd->dev; - struct ncsi_package *np = ndp->active_package; - struct ncsi_channel *nc = ndp->active_channel; - struct ncsi_channel *hot_nc = NULL; -+ struct ncsi_dev *nd = &ndp->ndev; -+ struct net_device *dev = nd->dev; - struct ncsi_cmd_arg nca; - unsigned char index; - unsigned long flags; -@@ -845,20 +1011,29 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) - } else if (nd->state == ncsi_dev_state_config_ebf) { - nca.type = NCSI_PKT_CMD_EBF; - nca.dwords[0] = nc->caps[NCSI_CAP_BC].cap; -- nd->state = ncsi_dev_state_config_ecnt; -+ if (ncsi_channel_is_tx(ndp, nc)) -+ nd->state = ncsi_dev_state_config_ecnt; -+ else -+ nd->state = ncsi_dev_state_config_ec; - #if IS_ENABLED(CONFIG_IPV6) - if (ndp->inet6_addr_num > 0 && - (nc->caps[NCSI_CAP_GENERIC].cap & - NCSI_CAP_GENERIC_MC)) - nd->state = ncsi_dev_state_config_egmf; -- else -- nd->state = ncsi_dev_state_config_ecnt; - } else if (nd->state == ncsi_dev_state_config_egmf) { - nca.type = NCSI_PKT_CMD_EGMF; - nca.dwords[0] = nc->caps[NCSI_CAP_MC].cap; -- nd->state = ncsi_dev_state_config_ecnt; -+ if (ncsi_channel_is_tx(ndp, nc)) -+ nd->state = ncsi_dev_state_config_ecnt; -+ else -+ nd->state = ncsi_dev_state_config_ec; - #endif /* CONFIG_IPV6 */ - } else if (nd->state == ncsi_dev_state_config_ecnt) { -+ if (np->preferred_channel && -+ nc != np->preferred_channel) -+ netdev_info(ndp->ndev.dev, -+ "NCSI: Tx failed over to channel %u\n", -+ nc->id); - nca.type = NCSI_PKT_CMD_ECNT; - nd->state = ncsi_dev_state_config_ec; - } else if (nd->state == ncsi_dev_state_config_ec) { -@@ -889,6 +1064,16 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) - netdev_dbg(ndp->ndev.dev, "NCSI: channel %u config done\n", - nc->id); - spin_lock_irqsave(&nc->lock, flags); -+ nc->state = NCSI_CHANNEL_ACTIVE; -+ -+ if (ndp->flags & NCSI_DEV_RESET) { -+ /* A reset event happened during config, start it now */ -+ nc->reconfigure_needed = false; -+ spin_unlock_irqrestore(&nc->lock, flags); -+ ncsi_reset_dev(nd); -+ break; -+ } -+ - if (nc->reconfigure_needed) { - /* This channel's configuration has been updated - * part-way during the config state - start the -@@ -909,10 +1094,8 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) - - if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) { - hot_nc = nc; -- nc->state = NCSI_CHANNEL_ACTIVE; - } else { - hot_nc = NULL; -- nc->state = NCSI_CHANNEL_INACTIVE; - netdev_dbg(ndp->ndev.dev, - "NCSI: channel %u link down after config\n", - nc->id); -@@ -940,43 +1123,35 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) - - static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) - { -- struct ncsi_package *np, *force_package; -- struct ncsi_channel *nc, *found, *hot_nc, *force_channel; -+ struct ncsi_channel *nc, *found, *hot_nc; - struct ncsi_channel_mode *ncm; -- unsigned long flags; -+ unsigned long flags, cflags; -+ struct ncsi_package *np; -+ bool with_link; - - spin_lock_irqsave(&ndp->lock, flags); - hot_nc = ndp->hot_channel; -- force_channel = ndp->force_channel; -- force_package = ndp->force_package; - spin_unlock_irqrestore(&ndp->lock, flags); - -- /* Force a specific channel whether or not it has link if we have been -- * configured to do so -- */ -- if (force_package && force_channel) { -- found = force_channel; -- ncm = &found->modes[NCSI_MODE_LINK]; -- if (!(ncm->data[2] & 0x1)) -- netdev_info(ndp->ndev.dev, -- "NCSI: Channel %u forced, but it is link down\n", -- found->id); -- goto out; -- } -- -- /* The search is done once an inactive channel with up -- * link is found. -+ /* By default the search is done once an inactive channel with up -+ * link is found, unless a preferred channel is set. -+ * If multi_package or multi_channel are configured all channels in the -+ * whitelist are added to the channel queue. - */ - found = NULL; -+ with_link = false; - NCSI_FOR_EACH_PACKAGE(ndp, np) { -- if (ndp->force_package && np != ndp->force_package) -+ if (!(ndp->package_whitelist & (0x1 << np->id))) - continue; - NCSI_FOR_EACH_CHANNEL(np, nc) { -- spin_lock_irqsave(&nc->lock, flags); -+ if (!(np->channel_whitelist & (0x1 << nc->id))) -+ continue; -+ -+ spin_lock_irqsave(&nc->lock, cflags); - - if (!list_empty(&nc->link) || - nc->state != NCSI_CHANNEL_INACTIVE) { -- spin_unlock_irqrestore(&nc->lock, flags); -+ spin_unlock_irqrestore(&nc->lock, cflags); - continue; - } - -@@ -988,32 +1163,49 @@ static int ncsi_choose_active_channel(struct ncsi_dev_priv *ndp) - - ncm = &nc->modes[NCSI_MODE_LINK]; - if (ncm->data[2] & 0x1) { -- spin_unlock_irqrestore(&nc->lock, flags); - found = nc; -- goto out; -+ with_link = true; - } - -- spin_unlock_irqrestore(&nc->lock, flags); -+ /* If multi_channel is enabled configure all valid -+ * channels whether or not they currently have link -+ * so they will have AENs enabled. -+ */ -+ if (with_link || np->multi_channel) { -+ spin_lock_irqsave(&ndp->lock, flags); -+ list_add_tail_rcu(&nc->link, -+ &ndp->channel_queue); -+ spin_unlock_irqrestore(&ndp->lock, flags); -+ -+ netdev_dbg(ndp->ndev.dev, -+ "NCSI: Channel %u added to queue (link %s)\n", -+ nc->id, -+ ncm->data[2] & 0x1 ? "up" : "down"); -+ } -+ -+ spin_unlock_irqrestore(&nc->lock, cflags); -+ -+ if (with_link && !np->multi_channel) -+ break; - } -+ if (with_link && !ndp->multi_package) -+ break; - } - -- if (!found) { -+ if (list_empty(&ndp->channel_queue) && found) { -+ netdev_info(ndp->ndev.dev, -+ "NCSI: No channel with link found, configuring channel %u\n", -+ found->id); -+ spin_lock_irqsave(&ndp->lock, flags); -+ list_add_tail_rcu(&found->link, &ndp->channel_queue); -+ spin_unlock_irqrestore(&ndp->lock, flags); -+ } else if (!found) { - netdev_warn(ndp->ndev.dev, -- "NCSI: No channel found with link\n"); -+ "NCSI: No channel found to configure!\n"); - ncsi_report_link(ndp, true); - return -ENODEV; - } - -- ncm = &found->modes[NCSI_MODE_LINK]; -- netdev_dbg(ndp->ndev.dev, -- "NCSI: Channel %u added to queue (link %s)\n", -- found->id, ncm->data[2] & 0x1 ? "up" : "down"); -- --out: -- spin_lock_irqsave(&ndp->lock, flags); -- list_add_tail_rcu(&found->link, &ndp->channel_queue); -- spin_unlock_irqrestore(&ndp->lock, flags); -- - return ncsi_process_next_channel(ndp); - } - -@@ -1050,35 +1242,6 @@ static bool ncsi_check_hwa(struct ncsi_dev_priv *ndp) - return false; - } - --static int ncsi_enable_hwa(struct ncsi_dev_priv *ndp) --{ -- struct ncsi_package *np; -- struct ncsi_channel *nc; -- unsigned long flags; -- -- /* Move all available channels to processing queue */ -- spin_lock_irqsave(&ndp->lock, flags); -- NCSI_FOR_EACH_PACKAGE(ndp, np) { -- NCSI_FOR_EACH_CHANNEL(np, nc) { -- WARN_ON_ONCE(nc->state != NCSI_CHANNEL_INACTIVE || -- !list_empty(&nc->link)); -- ncsi_stop_channel_monitor(nc); -- list_add_tail_rcu(&nc->link, &ndp->channel_queue); -- } -- } -- spin_unlock_irqrestore(&ndp->lock, flags); -- -- /* We can have no channels in extremely case */ -- if (list_empty(&ndp->channel_queue)) { -- netdev_err(ndp->ndev.dev, -- "NCSI: No available channels for HWA\n"); -- ncsi_report_link(ndp, false); -- return -ENOENT; -- } -- -- return ncsi_process_next_channel(ndp); --} -- - static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) - { - struct ncsi_dev *nd = &ndp->ndev; -@@ -1110,70 +1273,28 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) - nd->state = ncsi_dev_state_probe_package; - break; - case ncsi_dev_state_probe_package: -- ndp->pending_req_num = 16; -+ ndp->pending_req_num = 1; - -- /* Select all possible packages */ - nca.type = NCSI_PKT_CMD_SP; - nca.bytes[0] = 1; -+ nca.package = ndp->package_probe_id; - nca.channel = NCSI_RESERVED_CHANNEL; -- for (index = 0; index < 8; index++) { -- nca.package = index; -- ret = ncsi_xmit_cmd(&nca); -- if (ret) -- goto error; -- } -- -- /* Disable all possible packages */ -- nca.type = NCSI_PKT_CMD_DP; -- for (index = 0; index < 8; index++) { -- nca.package = index; -- ret = ncsi_xmit_cmd(&nca); -- if (ret) -- goto error; -- } -- -+ ret = ncsi_xmit_cmd(&nca); -+ if (ret) -+ goto error; - nd->state = ncsi_dev_state_probe_channel; - break; - case ncsi_dev_state_probe_channel: -- if (!ndp->active_package) -- ndp->active_package = list_first_or_null_rcu( -- &ndp->packages, struct ncsi_package, node); -- else if (list_is_last(&ndp->active_package->node, -- &ndp->packages)) -- ndp->active_package = NULL; -- else -- ndp->active_package = list_next_entry( -- ndp->active_package, node); -- -- /* All available packages and channels are enumerated. The -- * enumeration happens for once when the NCSI interface is -- * started. So we need continue to start the interface after -- * the enumeration. -- * -- * We have to choose an active channel before configuring it. -- * Note that we possibly don't have active channel in extreme -- * situation. -- */ -+ ndp->active_package = ncsi_find_package(ndp, -+ ndp->package_probe_id); - if (!ndp->active_package) { -- ndp->flags |= NCSI_DEV_PROBED; -- if (ncsi_check_hwa(ndp)) -- ncsi_enable_hwa(ndp); -- else -- ncsi_choose_active_channel(ndp); -- return; -+ /* No response */ -+ nd->state = ncsi_dev_state_probe_dp; -+ schedule_work(&ndp->work); -+ break; - } -- -- /* Select the active package */ -- ndp->pending_req_num = 1; -- nca.type = NCSI_PKT_CMD_SP; -- nca.bytes[0] = 1; -- nca.package = ndp->active_package->id; -- nca.channel = NCSI_RESERVED_CHANNEL; -- ret = ncsi_xmit_cmd(&nca); -- if (ret) -- goto error; -- - nd->state = ncsi_dev_state_probe_cis; -+ schedule_work(&ndp->work); - break; - case ncsi_dev_state_probe_cis: - ndp->pending_req_num = NCSI_RESERVED_CHANNEL; -@@ -1222,22 +1343,35 @@ static void ncsi_probe_channel(struct ncsi_dev_priv *ndp) - case ncsi_dev_state_probe_dp: - ndp->pending_req_num = 1; - -- /* Deselect the active package */ -+ /* Deselect the current package */ - nca.type = NCSI_PKT_CMD_DP; -- nca.package = ndp->active_package->id; -+ nca.package = ndp->package_probe_id; - nca.channel = NCSI_RESERVED_CHANNEL; - ret = ncsi_xmit_cmd(&nca); - if (ret) - goto error; - -- /* Scan channels in next package */ -- nd->state = ncsi_dev_state_probe_channel; -+ /* Probe next package */ -+ ndp->package_probe_id++; -+ if (ndp->package_probe_id >= 8) { -+ /* Probe finished */ -+ ndp->flags |= NCSI_DEV_PROBED; -+ break; -+ } -+ nd->state = ncsi_dev_state_probe_package; -+ ndp->active_package = NULL; - break; - default: - netdev_warn(nd->dev, "Wrong NCSI state 0x%0x in enumeration\n", - nd->state); - } - -+ if (ndp->flags & NCSI_DEV_PROBED) { -+ /* Check if all packages have HWA support */ -+ ncsi_check_hwa(ndp); -+ ncsi_choose_active_channel(ndp); -+ } -+ - return; - error: - netdev_err(ndp->ndev.dev, -@@ -1556,6 +1690,7 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev, - INIT_LIST_HEAD(&ndp->channel_queue); - INIT_LIST_HEAD(&ndp->vlan_vids); - INIT_WORK(&ndp->work, ncsi_dev_work); -+ ndp->package_whitelist = UINT_MAX; - - /* Initialize private NCSI device */ - spin_lock_init(&ndp->lock); -@@ -1592,26 +1727,19 @@ EXPORT_SYMBOL_GPL(ncsi_register_dev); - int ncsi_start_dev(struct ncsi_dev *nd) - { - struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd); -- int ret; - - if (nd->state != ncsi_dev_state_registered && - nd->state != ncsi_dev_state_functional) - return -ENOTTY; - - if (!(ndp->flags & NCSI_DEV_PROBED)) { -+ ndp->package_probe_id = 0; - nd->state = ncsi_dev_state_probe; - schedule_work(&ndp->work); - return 0; - } - -- if (ndp->flags & NCSI_DEV_HWA) { -- netdev_info(ndp->ndev.dev, "NCSI: Enabling HWA mode\n"); -- ret = ncsi_enable_hwa(ndp); -- } else { -- ret = ncsi_choose_active_channel(ndp); -- } -- -- return ret; -+ return ncsi_reset_dev(nd); - } - EXPORT_SYMBOL_GPL(ncsi_start_dev); - -@@ -1624,7 +1752,10 @@ void ncsi_stop_dev(struct ncsi_dev *nd) - int old_state; - unsigned long flags; - -- /* Stop the channel monitor and reset channel's state */ -+ /* Stop the channel monitor on any active channels. Don't reset the -+ * channel state so we know which were active when ncsi_start_dev() -+ * is next called. -+ */ - NCSI_FOR_EACH_PACKAGE(ndp, np) { - NCSI_FOR_EACH_CHANNEL(np, nc) { - ncsi_stop_channel_monitor(nc); -@@ -1632,7 +1763,6 @@ void ncsi_stop_dev(struct ncsi_dev *nd) - spin_lock_irqsave(&nc->lock, flags); - chained = !list_empty(&nc->link); - old_state = nc->state; -- nc->state = NCSI_CHANNEL_INACTIVE; - spin_unlock_irqrestore(&nc->lock, flags); - - WARN_ON_ONCE(chained || -@@ -1645,6 +1775,92 @@ void ncsi_stop_dev(struct ncsi_dev *nd) - } - EXPORT_SYMBOL_GPL(ncsi_stop_dev); - -+int ncsi_reset_dev(struct ncsi_dev *nd) -+{ -+ struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd); -+ struct ncsi_channel *nc, *active, *tmp; -+ struct ncsi_package *np; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&ndp->lock, flags); -+ -+ if (!(ndp->flags & NCSI_DEV_RESET)) { -+ /* Haven't been called yet, check states */ -+ switch (nd->state & ncsi_dev_state_major) { -+ case ncsi_dev_state_registered: -+ case ncsi_dev_state_probe: -+ /* Not even probed yet - do nothing */ -+ spin_unlock_irqrestore(&ndp->lock, flags); -+ return 0; -+ case ncsi_dev_state_suspend: -+ case ncsi_dev_state_config: -+ /* Wait for the channel to finish its suspend/config -+ * operation; once it finishes it will check for -+ * NCSI_DEV_RESET and reset the state. -+ */ -+ ndp->flags |= NCSI_DEV_RESET; -+ spin_unlock_irqrestore(&ndp->lock, flags); -+ return 0; -+ } -+ } else { -+ switch (nd->state) { -+ case ncsi_dev_state_suspend_done: -+ case ncsi_dev_state_config_done: -+ case ncsi_dev_state_functional: -+ /* Ok */ -+ break; -+ default: -+ /* Current reset operation happening */ -+ spin_unlock_irqrestore(&ndp->lock, flags); -+ return 0; -+ } -+ } -+ -+ if (!list_empty(&ndp->channel_queue)) { -+ /* Clear any channel queue we may have interrupted */ -+ list_for_each_entry_safe(nc, tmp, &ndp->channel_queue, link) -+ list_del_init(&nc->link); -+ } -+ spin_unlock_irqrestore(&ndp->lock, flags); -+ -+ active = NULL; -+ NCSI_FOR_EACH_PACKAGE(ndp, np) { -+ NCSI_FOR_EACH_CHANNEL(np, nc) { -+ spin_lock_irqsave(&nc->lock, flags); -+ -+ if (nc->state == NCSI_CHANNEL_ACTIVE) { -+ active = nc; -+ nc->state = NCSI_CHANNEL_INVISIBLE; -+ spin_unlock_irqrestore(&nc->lock, flags); -+ ncsi_stop_channel_monitor(nc); -+ break; -+ } -+ -+ spin_unlock_irqrestore(&nc->lock, flags); -+ } -+ if (active) -+ break; -+ } -+ -+ if (!active) { -+ /* Done */ -+ spin_lock_irqsave(&ndp->lock, flags); -+ ndp->flags &= ~NCSI_DEV_RESET; -+ spin_unlock_irqrestore(&ndp->lock, flags); -+ return ncsi_choose_active_channel(ndp); -+ } -+ -+ spin_lock_irqsave(&ndp->lock, flags); -+ ndp->flags |= NCSI_DEV_RESET; -+ ndp->active_channel = active; -+ ndp->active_package = active->package; -+ spin_unlock_irqrestore(&ndp->lock, flags); -+ -+ nd->state = ncsi_dev_state_suspend; -+ schedule_work(&ndp->work); -+ return 0; -+} -+ - void ncsi_unregister_dev(struct ncsi_dev *nd) - { - struct ncsi_dev_priv *ndp = TO_NCSI_DEV_PRIV(nd); -diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c -index 33314381b4f5..5d782445d2fc 100644 ---- a/net/ncsi/ncsi-netlink.c -+++ b/net/ncsi/ncsi-netlink.c -@@ -30,6 +30,9 @@ static const struct nla_policy ncsi_genl_policy[NCSI_ATTR_MAX + 1] = { - [NCSI_ATTR_PACKAGE_ID] = { .type = NLA_U32 }, - [NCSI_ATTR_CHANNEL_ID] = { .type = NLA_U32 }, - [NCSI_ATTR_DATA] = { .type = NLA_BINARY, .len = 2048 }, -+ [NCSI_ATTR_MULTI_FLAG] = { .type = NLA_FLAG }, -+ [NCSI_ATTR_PACKAGE_MASK] = { .type = NLA_U32 }, -+ [NCSI_ATTR_CHANNEL_MASK] = { .type = NLA_U32 }, - }; - - static struct ncsi_dev_priv *ndp_from_ifindex(struct net *net, u32 ifindex) -@@ -69,7 +72,7 @@ static int ncsi_write_channel_info(struct sk_buff *skb, - nla_put_u32(skb, NCSI_CHANNEL_ATTR_LINK_STATE, m->data[2]); - if (nc->state == NCSI_CHANNEL_ACTIVE) - nla_put_flag(skb, NCSI_CHANNEL_ATTR_ACTIVE); -- if (ndp->force_channel == nc) -+ if (nc == nc->package->preferred_channel) - nla_put_flag(skb, NCSI_CHANNEL_ATTR_FORCED); - - nla_put_u32(skb, NCSI_CHANNEL_ATTR_VERSION_MAJOR, nc->version.version); -@@ -114,7 +117,7 @@ static int ncsi_write_package_info(struct sk_buff *skb, - if (!pnest) - return -ENOMEM; - nla_put_u32(skb, NCSI_PKG_ATTR_ID, np->id); -- if (ndp->force_package == np) -+ if ((0x1 << np->id) == ndp->package_whitelist) - nla_put_flag(skb, NCSI_PKG_ATTR_FORCED); - cnest = nla_nest_start(skb, NCSI_PKG_ATTR_CHANNEL_LIST); - if (!cnest) { -@@ -290,49 +293,58 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) - package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); - package = NULL; - -- spin_lock_irqsave(&ndp->lock, flags); -- - NCSI_FOR_EACH_PACKAGE(ndp, np) - if (np->id == package_id) - package = np; - if (!package) { - /* The user has set a package that does not exist */ -- spin_unlock_irqrestore(&ndp->lock, flags); - return -ERANGE; - } - - channel = NULL; -- if (!info->attrs[NCSI_ATTR_CHANNEL_ID]) { -- /* Allow any channel */ -- channel_id = NCSI_RESERVED_CHANNEL; -- } else { -+ if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { - channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); - NCSI_FOR_EACH_CHANNEL(package, nc) -- if (nc->id == channel_id) -+ if (nc->id == channel_id) { - channel = nc; -+ break; -+ } -+ if (!channel) { -+ netdev_info(ndp->ndev.dev, -+ "NCSI: Channel %u does not exist!\n", -+ channel_id); -+ return -ERANGE; -+ } - } - -- if (channel_id != NCSI_RESERVED_CHANNEL && !channel) { -- /* The user has set a channel that does not exist on this -- * package -- */ -- spin_unlock_irqrestore(&ndp->lock, flags); -- netdev_info(ndp->ndev.dev, "NCSI: Channel %u does not exist!\n", -- channel_id); -- return -ERANGE; -- } -- -- ndp->force_package = package; -- ndp->force_channel = channel; -+ spin_lock_irqsave(&ndp->lock, flags); -+ ndp->package_whitelist = 0x1 << package->id; -+ ndp->multi_package = false; - spin_unlock_irqrestore(&ndp->lock, flags); - -- netdev_info(ndp->ndev.dev, "Set package 0x%x, channel 0x%x%s as preferred\n", -- package_id, channel_id, -- channel_id == NCSI_RESERVED_CHANNEL ? " (any)" : ""); -+ spin_lock_irqsave(&package->lock, flags); -+ package->multi_channel = false; -+ if (channel) { -+ package->channel_whitelist = 0x1 << channel->id; -+ package->preferred_channel = channel; -+ } else { -+ /* Allow any channel */ -+ package->channel_whitelist = UINT_MAX; -+ package->preferred_channel = NULL; -+ } -+ spin_unlock_irqrestore(&package->lock, flags); -+ -+ if (channel) -+ netdev_info(ndp->ndev.dev, -+ "Set package 0x%x, channel 0x%x as preferred\n", -+ package_id, channel_id); -+ else -+ netdev_info(ndp->ndev.dev, "Set package 0x%x as preferred\n", -+ package_id); - -- /* Bounce the NCSI channel to set changes */ -- ncsi_stop_dev(&ndp->ndev); -- ncsi_start_dev(&ndp->ndev); -+ /* Update channel configuration */ -+ if (!(ndp->flags & NCSI_DEV_RESET)) -+ ncsi_reset_dev(&ndp->ndev); - - return 0; - } -@@ -340,6 +352,7 @@ static int ncsi_set_interface_nl(struct sk_buff *msg, struct genl_info *info) - static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) - { - struct ncsi_dev_priv *ndp; -+ struct ncsi_package *np; - unsigned long flags; - - if (!info || !info->attrs) -@@ -353,16 +366,24 @@ static int ncsi_clear_interface_nl(struct sk_buff *msg, struct genl_info *info) - if (!ndp) - return -ENODEV; - -- /* Clear any override */ -+ /* Reset any whitelists and disable multi mode */ - spin_lock_irqsave(&ndp->lock, flags); -- ndp->force_package = NULL; -- ndp->force_channel = NULL; -+ ndp->package_whitelist = UINT_MAX; -+ ndp->multi_package = false; - spin_unlock_irqrestore(&ndp->lock, flags); -+ -+ NCSI_FOR_EACH_PACKAGE(ndp, np) { -+ spin_lock_irqsave(&np->lock, flags); -+ np->multi_channel = false; -+ np->channel_whitelist = UINT_MAX; -+ np->preferred_channel = NULL; -+ spin_unlock_irqrestore(&np->lock, flags); -+ } - netdev_info(ndp->ndev.dev, "NCSI: Cleared preferred package/channel\n"); - -- /* Bounce the NCSI channel to set changes */ -- ncsi_stop_dev(&ndp->ndev); -- ncsi_start_dev(&ndp->ndev); -+ /* Update channel configuration */ -+ if (!(ndp->flags & NCSI_DEV_RESET)) -+ ncsi_reset_dev(&ndp->ndev); - - return 0; - } -@@ -563,6 +584,138 @@ int ncsi_send_netlink_err(struct net_device *dev, - return nlmsg_unicast(net->genl_sock, skb, snd_portid); - } - -+static int ncsi_set_package_mask_nl(struct sk_buff *msg, -+ struct genl_info *info) -+{ -+ struct ncsi_dev_priv *ndp; -+ unsigned long flags; -+ int rc; -+ -+ if (!info || !info->attrs) -+ return -EINVAL; -+ -+ if (!info->attrs[NCSI_ATTR_IFINDEX]) -+ return -EINVAL; -+ -+ if (!info->attrs[NCSI_ATTR_PACKAGE_MASK]) -+ return -EINVAL; -+ -+ ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), -+ nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); -+ if (!ndp) -+ return -ENODEV; -+ -+ spin_lock_irqsave(&ndp->lock, flags); -+ if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { -+ if (ndp->flags & NCSI_DEV_HWA) { -+ ndp->multi_package = true; -+ rc = 0; -+ } else { -+ netdev_err(ndp->ndev.dev, -+ "NCSI: Can't use multiple packages without HWA\n"); -+ rc = -EPERM; -+ } -+ } else { -+ ndp->multi_package = false; -+ rc = 0; -+ } -+ -+ if (!rc) -+ ndp->package_whitelist = -+ nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_MASK]); -+ spin_unlock_irqrestore(&ndp->lock, flags); -+ -+ if (!rc) { -+ /* Update channel configuration */ -+ if (!(ndp->flags & NCSI_DEV_RESET)) -+ ncsi_reset_dev(&ndp->ndev); -+ } -+ -+ return rc; -+} -+ -+static int ncsi_set_channel_mask_nl(struct sk_buff *msg, -+ struct genl_info *info) -+{ -+ struct ncsi_package *np, *package; -+ struct ncsi_channel *nc, *channel; -+ u32 package_id, channel_id; -+ struct ncsi_dev_priv *ndp; -+ unsigned long flags; -+ -+ if (!info || !info->attrs) -+ return -EINVAL; -+ -+ if (!info->attrs[NCSI_ATTR_IFINDEX]) -+ return -EINVAL; -+ -+ if (!info->attrs[NCSI_ATTR_PACKAGE_ID]) -+ return -EINVAL; -+ -+ if (!info->attrs[NCSI_ATTR_CHANNEL_MASK]) -+ return -EINVAL; -+ -+ ndp = ndp_from_ifindex(get_net(sock_net(msg->sk)), -+ nla_get_u32(info->attrs[NCSI_ATTR_IFINDEX])); -+ if (!ndp) -+ return -ENODEV; -+ -+ package_id = nla_get_u32(info->attrs[NCSI_ATTR_PACKAGE_ID]); -+ package = NULL; -+ NCSI_FOR_EACH_PACKAGE(ndp, np) -+ if (np->id == package_id) { -+ package = np; -+ break; -+ } -+ if (!package) -+ return -ERANGE; -+ -+ spin_lock_irqsave(&package->lock, flags); -+ -+ channel = NULL; -+ if (info->attrs[NCSI_ATTR_CHANNEL_ID]) { -+ channel_id = nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_ID]); -+ NCSI_FOR_EACH_CHANNEL(np, nc) -+ if (nc->id == channel_id) { -+ channel = nc; -+ break; -+ } -+ if (!channel) { -+ spin_unlock_irqrestore(&package->lock, flags); -+ return -ERANGE; -+ } -+ netdev_dbg(ndp->ndev.dev, -+ "NCSI: Channel %u set as preferred channel\n", -+ channel->id); -+ } -+ -+ package->channel_whitelist = -+ nla_get_u32(info->attrs[NCSI_ATTR_CHANNEL_MASK]); -+ if (package->channel_whitelist == 0) -+ netdev_dbg(ndp->ndev.dev, -+ "NCSI: Package %u set to all channels disabled\n", -+ package->id); -+ -+ package->preferred_channel = channel; -+ -+ if (nla_get_flag(info->attrs[NCSI_ATTR_MULTI_FLAG])) { -+ package->multi_channel = true; -+ netdev_info(ndp->ndev.dev, -+ "NCSI: Multi-channel enabled on package %u\n", -+ package_id); -+ } else { -+ package->multi_channel = false; -+ } -+ -+ spin_unlock_irqrestore(&package->lock, flags); -+ -+ /* Update channel configuration */ -+ if (!(ndp->flags & NCSI_DEV_RESET)) -+ ncsi_reset_dev(&ndp->ndev); -+ -+ return 0; -+} -+ - static const struct genl_ops ncsi_ops[] = { - { - .cmd = NCSI_CMD_PKG_INFO, -@@ -589,6 +742,18 @@ static const struct genl_ops ncsi_ops[] = { - .doit = ncsi_send_cmd_nl, - .flags = GENL_ADMIN_PERM, - }, -+ { -+ .cmd = NCSI_CMD_SET_PACKAGE_MASK, -+ .policy = ncsi_genl_policy, -+ .doit = ncsi_set_package_mask_nl, -+ .flags = GENL_ADMIN_PERM, -+ }, -+ { -+ .cmd = NCSI_CMD_SET_CHANNEL_MASK, -+ .policy = ncsi_genl_policy, -+ .doit = ncsi_set_channel_mask_nl, -+ .flags = GENL_ADMIN_PERM, -+ }, - }; - - static struct genl_family ncsi_genl_family __ro_after_init = { -diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c -index 77e07ba3f493..de7737a27889 100644 ---- a/net/ncsi/ncsi-rsp.c -+++ b/net/ncsi/ncsi-rsp.c -@@ -256,7 +256,7 @@ static int ncsi_rsp_handler_dcnt(struct ncsi_request *nr) - if (!ncm->enable) - return 0; - -- ncm->enable = 1; -+ ncm->enable = 0; - return 0; - } - --- -2.7.4 - diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0038-media-aspeed-backport-ikvm-patches.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0038-media-aspeed-backport-ikvm-patches.patch index 02e423057..92d0a045d 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0038-media-aspeed-backport-ikvm-patches.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0038-media-aspeed-backport-ikvm-patches.patch @@ -1,16 +1,8 @@ -From feb75b023dfd0ebe3e8ca46f0e74603f07542c29 Mon Sep 17 00:00:00 2001 +From 13d6fd0f71b3d0d69370878613bf7eb78fefa18f Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Fri, 9 Nov 2018 11:32:27 -0800 Subject: [PATCH] Add Aspeed Video Engine Driver -The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs -can capture and compress video data from digital or analog sources. With -the Aspeed chip acting a service processor, the Video Engine can capture -the host processor graphics output. - -Add a V4L2 driver to capture video data and compress it to JPEG images. -Make the video frames available through the V4L2 streaming interface. - media: platform: Fix missing spin_lock_init() The driver allocates the spinlock but not initialize it. @@ -27,71 +19,14 @@ Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jae Hyun Yoo --- - .../devicetree/bindings/media/aspeed-video.txt | 26 + - MAINTAINERS | 8 + - arch/arm/boot/dts/aspeed-g5.dtsi | 11 + - drivers/clk/clk-aspeed.c | 41 +- - drivers/media/platform/Kconfig | 9 + - drivers/media/platform/Makefile | 1 + - drivers/media/platform/aspeed-video.c | 1730 ++++++++++++++++++++ - include/dt-bindings/clock/aspeed-clock.h | 1 + - 8 files changed, 1825 insertions(+), 2 deletions(-) - create mode 100644 Documentation/devicetree/bindings/media/aspeed-video.txt - create mode 100644 drivers/media/platform/aspeed-video.c + arch/arm/boot/dts/aspeed-g5.dtsi | 11 +++++++++ + drivers/clk/clk-aspeed.c | 41 ++++++++++++++++++++++++++++++-- + drivers/media/platform/aspeed-video.c | 1 + + include/dt-bindings/clock/aspeed-clock.h | 1 + + 4 files changed, 52 insertions(+), 2 deletions(-) -diff --git a/Documentation/devicetree/bindings/media/aspeed-video.txt b/Documentation/devicetree/bindings/media/aspeed-video.txt -new file mode 100644 -index 000000000000..78b464ae2672 ---- /dev/null -+++ b/Documentation/devicetree/bindings/media/aspeed-video.txt -@@ -0,0 +1,26 @@ -+* Device tree bindings for Aspeed Video Engine -+ -+The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs can -+capture and compress video data from digital or analog sources. -+ -+Required properties: -+ - compatible: "aspeed,ast2400-video-engine" or -+ "aspeed,ast2500-video-engine" -+ - reg: contains the offset and length of the VE memory region -+ - clocks: clock specifiers for the syscon clocks associated with -+ the VE (ordering must match the clock-names property) -+ - clock-names: "vclk" and "eclk" -+ - resets: reset specifier for the syscon reset associated with -+ the VE -+ - interrupts: the interrupt associated with the VE on this platform -+ -+Example: -+ -+video-engine@1e700000 { -+ compatible = "aspeed,ast2500-video-engine"; -+ reg = <0x1e700000 0x20000>; -+ clocks = <&syscon ASPEED_CLK_GATE_VCLK>, <&syscon ASPEED_CLK_GATE_ECLK>; -+ clock-names = "vclk", "eclk"; -+ resets = <&syscon ASPEED_RESET_VIDEO>; -+ interrupts = <7>; -+}; -diff --git a/MAINTAINERS b/MAINTAINERS -index b98a5763f6c1..cdd8f824b6da 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -2358,6 +2358,14 @@ S: Maintained - F: Documentation/hwmon/asc7621 - F: drivers/hwmon/asc7621.c - -+ASPEED VIDEO ENGINE DRIVER -+M: Eddie James -+L: linux-media@vger.kernel.org -+L: openbmc@lists.ozlabs.org (moderated for non-subscribers) -+S: Maintained -+F: drivers/media/platform/aspeed-video.c -+F: Documentation/devicetree/bindings/media/aspeed-video.txt -+ - ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS - M: Corentin Chary - L: acpi4asus-user@lists.sourceforge.net diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi -index 69330ae2efd3..946c13eaa1d4 100644 +index 6f26e0d323d6..d5783eaf30ae 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -243,6 +243,17 @@ @@ -211,1774 +146,18 @@ index 3bbb4fbf00c9..6cea55de485f 100644 */ /* Get the uart clock source configuration from SCU4C*/ -diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig -index 54fe90acb5b2..d6edf2d28f9b 100644 ---- a/drivers/media/platform/Kconfig -+++ b/drivers/media/platform/Kconfig -@@ -32,6 +32,15 @@ source "drivers/media/platform/davinci/Kconfig" - - source "drivers/media/platform/omap/Kconfig" - -+config VIDEO_ASPEED -+ tristate "Aspeed AST2400 and AST2500 Video Engine driver" -+ depends on VIDEO_V4L2 -+ select VIDEOBUF2_DMA_CONTIG -+ help -+ Support for the Aspeed Video Engine (VE) embedded in the Aspeed -+ AST2400 and AST2500 SOCs. The VE can capture and compress video data -+ from digital or analog sources. -+ - config VIDEO_SH_VOU - tristate "SuperH VOU video output driver" - depends on MEDIA_CAMERA_SUPPORT -diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile -index 41322ab65802..205c33a004fc 100644 ---- a/drivers/media/platform/Makefile -+++ b/drivers/media/platform/Makefile -@@ -3,6 +3,7 @@ - # Makefile for the video capture/playback device drivers. - # - -+obj-$(CONFIG_VIDEO_ASPEED) += aspeed-video.o - obj-$(CONFIG_VIDEO_CADENCE) += cadence/ - obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o - obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/ diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c -new file mode 100644 -index 000000000000..692e08ef38c0 ---- /dev/null +index dfec813f50a9..692e08ef38c0 100644 +--- a/drivers/media/platform/aspeed-video.c +++ b/drivers/media/platform/aspeed-video.c -@@ -0,0 +1,1730 @@ -+// SPDX-License-Identifier: GPL-2.0+ -+ -+#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 -+#include -+ -+#define DEVICE_NAME "aspeed-video" -+ -+#define ASPEED_VIDEO_JPEG_NUM_QUALITIES 12 -+#define ASPEED_VIDEO_JPEG_HEADER_SIZE 10 -+#define ASPEED_VIDEO_JPEG_QUANT_SIZE 116 -+#define ASPEED_VIDEO_JPEG_DCT_SIZE 34 -+ -+#define MAX_FRAME_RATE 60 -+#define MAX_HEIGHT 1200 -+#define MAX_WIDTH 1920 -+#define MIN_HEIGHT 480 -+#define MIN_WIDTH 640 -+ -+#define NUM_POLARITY_CHECKS 10 -+#define INVALID_RESOLUTION_RETRIES 2 -+#define INVALID_RESOLUTION_DELAY msecs_to_jiffies(250) -+#define RESOLUTION_CHANGE_DELAY msecs_to_jiffies(500) -+#define MODE_DETECT_TIMEOUT msecs_to_jiffies(500) -+#define STOP_TIMEOUT msecs_to_jiffies(1000) -+#define DIRECT_FETCH_THRESHOLD 0x0c0000 /* 1024 * 768 */ -+ -+#define VE_MAX_SRC_BUFFER_SIZE 0x8ca000 /* 1920 * 1200, 32bpp */ -+#define VE_JPEG_HEADER_SIZE 0x006000 /* 512 * 12 * 4 */ -+ -+#define VE_PROTECTION_KEY 0x000 -+#define VE_PROTECTION_KEY_UNLOCK 0x1a038aa8 -+ -+#define VE_SEQ_CTRL 0x004 -+#define VE_SEQ_CTRL_TRIG_MODE_DET BIT(0) -+#define VE_SEQ_CTRL_TRIG_CAPTURE BIT(1) -+#define VE_SEQ_CTRL_FORCE_IDLE BIT(2) -+#define VE_SEQ_CTRL_MULT_FRAME BIT(3) -+#define VE_SEQ_CTRL_TRIG_COMP BIT(4) -+#define VE_SEQ_CTRL_AUTO_COMP BIT(5) -+#define VE_SEQ_CTRL_EN_WATCHDOG BIT(7) -+#define VE_SEQ_CTRL_YUV420 BIT(10) -+#define VE_SEQ_CTRL_COMP_FMT GENMASK(11, 10) -+#define VE_SEQ_CTRL_HALT BIT(12) -+#define VE_SEQ_CTRL_EN_WATCHDOG_COMP BIT(14) -+#define VE_SEQ_CTRL_TRIG_JPG BIT(15) -+#define VE_SEQ_CTRL_CAP_BUSY BIT(16) -+#define VE_SEQ_CTRL_COMP_BUSY BIT(18) -+ -+#ifdef CONFIG_MACH_ASPEED_G5 -+#define VE_SEQ_CTRL_JPEG_MODE BIT(13) /* AST2500 */ -+#else -+#define VE_SEQ_CTRL_JPEG_MODE BIT(8) /* AST2400 */ -+#endif /* CONFIG_MACH_ASPEED_G5 */ -+ -+#define VE_CTRL 0x008 -+#define VE_CTRL_HSYNC_POL BIT(0) -+#define VE_CTRL_VSYNC_POL BIT(1) -+#define VE_CTRL_SOURCE BIT(2) -+#define VE_CTRL_INT_DE BIT(4) -+#define VE_CTRL_DIRECT_FETCH BIT(5) -+#define VE_CTRL_YUV BIT(6) -+#define VE_CTRL_RGB BIT(7) -+#define VE_CTRL_CAPTURE_FMT GENMASK(7, 6) -+#define VE_CTRL_AUTO_OR_CURSOR BIT(8) -+#define VE_CTRL_CLK_INVERSE BIT(11) -+#define VE_CTRL_CLK_DELAY GENMASK(11, 9) -+#define VE_CTRL_INTERLACE BIT(14) -+#define VE_CTRL_HSYNC_POL_CTRL BIT(15) -+#define VE_CTRL_FRC GENMASK(23, 16) -+ -+#define VE_TGS_0 0x00c -+#define VE_TGS_1 0x010 -+#define VE_TGS_FIRST GENMASK(28, 16) -+#define VE_TGS_LAST GENMASK(12, 0) -+ -+#define VE_SCALING_FACTOR 0x014 -+#define VE_SCALING_FILTER0 0x018 -+#define VE_SCALING_FILTER1 0x01c -+#define VE_SCALING_FILTER2 0x020 -+#define VE_SCALING_FILTER3 0x024 -+ -+#define VE_CAP_WINDOW 0x030 -+#define VE_COMP_WINDOW 0x034 -+#define VE_COMP_PROC_OFFSET 0x038 -+#define VE_COMP_OFFSET 0x03c -+#define VE_JPEG_ADDR 0x040 -+#define VE_SRC0_ADDR 0x044 -+#define VE_SRC_SCANLINE_OFFSET 0x048 -+#define VE_SRC1_ADDR 0x04c -+#define VE_COMP_ADDR 0x054 -+ -+#define VE_STREAM_BUF_SIZE 0x058 -+#define VE_STREAM_BUF_SIZE_N_PACKETS GENMASK(5, 3) -+#define VE_STREAM_BUF_SIZE_P_SIZE GENMASK(2, 0) -+ -+#define VE_COMP_CTRL 0x060 -+#define VE_COMP_CTRL_VQ_DCT_ONLY BIT(0) -+#define VE_COMP_CTRL_VQ_4COLOR BIT(1) -+#define VE_COMP_CTRL_QUANTIZE BIT(2) -+#define VE_COMP_CTRL_EN_BQ BIT(4) -+#define VE_COMP_CTRL_EN_CRYPTO BIT(5) -+#define VE_COMP_CTRL_DCT_CHR GENMASK(10, 6) -+#define VE_COMP_CTRL_DCT_LUM GENMASK(15, 11) -+#define VE_COMP_CTRL_EN_HQ BIT(16) -+#define VE_COMP_CTRL_RSVD BIT(19) -+#define VE_COMP_CTRL_ENCODE GENMASK(21, 20) -+#define VE_COMP_CTRL_HQ_DCT_CHR GENMASK(26, 22) -+#define VE_COMP_CTRL_HQ_DCT_LUM GENMASK(31, 27) -+ -+#define VE_OFFSET_COMP_STREAM 0x078 -+ -+#define VE_SRC_LR_EDGE_DET 0x090 -+#define VE_SRC_LR_EDGE_DET_LEFT GENMASK(11, 0) -+#define VE_SRC_LR_EDGE_DET_NO_V BIT(12) -+#define VE_SRC_LR_EDGE_DET_NO_H BIT(13) -+#define VE_SRC_LR_EDGE_DET_NO_DISP BIT(14) -+#define VE_SRC_LR_EDGE_DET_NO_CLK BIT(15) -+#define VE_SRC_LR_EDGE_DET_RT_SHF 16 -+#define VE_SRC_LR_EDGE_DET_RT GENMASK(27, VE_SRC_LR_EDGE_DET_RT_SHF) -+#define VE_SRC_LR_EDGE_DET_INTERLACE BIT(31) -+ -+#define VE_SRC_TB_EDGE_DET 0x094 -+#define VE_SRC_TB_EDGE_DET_TOP GENMASK(12, 0) -+#define VE_SRC_TB_EDGE_DET_BOT_SHF 16 -+#define VE_SRC_TB_EDGE_DET_BOT GENMASK(28, VE_SRC_TB_EDGE_DET_BOT_SHF) -+ -+#define VE_MODE_DETECT_STATUS 0x098 -+#define VE_MODE_DETECT_H_PIXELS GENMASK(11, 0) -+#define VE_MODE_DETECT_V_LINES_SHF 16 -+#define VE_MODE_DETECT_V_LINES GENMASK(27, VE_MODE_DETECT_V_LINES_SHF) -+#define VE_MODE_DETECT_STATUS_VSYNC BIT(28) -+#define VE_MODE_DETECT_STATUS_HSYNC BIT(29) -+ -+#define VE_SYNC_STATUS 0x09c -+#define VE_SYNC_STATUS_HSYNC GENMASK(11, 0) -+#define VE_SYNC_STATUS_VSYNC_SHF 16 -+#define VE_SYNC_STATUS_VSYNC GENMASK(27, VE_SYNC_STATUS_VSYNC_SHF) -+ -+#define VE_INTERRUPT_CTRL 0x304 -+#define VE_INTERRUPT_STATUS 0x308 -+#define VE_INTERRUPT_MODE_DETECT_WD BIT(0) -+#define VE_INTERRUPT_CAPTURE_COMPLETE BIT(1) -+#define VE_INTERRUPT_COMP_READY BIT(2) -+#define VE_INTERRUPT_COMP_COMPLETE BIT(3) -+#define VE_INTERRUPT_MODE_DETECT BIT(4) -+#define VE_INTERRUPT_FRAME_COMPLETE BIT(5) -+#define VE_INTERRUPT_DECODE_ERR BIT(6) -+#define VE_INTERRUPT_HALT_READY BIT(8) -+#define VE_INTERRUPT_HANG_WD BIT(9) -+#define VE_INTERRUPT_STREAM_DESC BIT(10) -+#define VE_INTERRUPT_VSYNC_DESC BIT(11) -+ -+#define VE_MODE_DETECT 0x30c -+#define VE_MEM_RESTRICT_START 0x310 -+#define VE_MEM_RESTRICT_END 0x314 -+ -+enum { -+ VIDEO_MODE_DETECT_DONE, -+ VIDEO_RES_CHANGE, -+ VIDEO_RES_DETECT, -+ VIDEO_STREAMING, -+ VIDEO_FRAME_INPRG, -+ VIDEO_STOPPED, -+}; -+ -+struct aspeed_video_addr { -+ unsigned int size; -+ dma_addr_t dma; -+ void *virt; -+}; -+ -+struct aspeed_video_buffer { -+ struct vb2_v4l2_buffer vb; -+ struct list_head link; -+}; -+ -+#define to_aspeed_video_buffer(x) \ -+ container_of((x), struct aspeed_video_buffer, vb) -+ -+struct aspeed_video { -+ void __iomem *base; -+ struct clk *eclk; -+ struct clk *vclk; -+ struct reset_control *rst; -+ -+ struct device *dev; -+ struct v4l2_ctrl_handler ctrl_handler; -+ 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; -+ struct mutex video_lock; /* v4l2 and videobuf2 lock */ -+ -+ wait_queue_head_t wait; -+ spinlock_t lock; /* buffer list lock */ -+ struct delayed_work res_work; -+ struct list_head buffers; -+ unsigned long flags; -+ unsigned int sequence; -+ -+ unsigned int max_compressed_size; -+ struct aspeed_video_addr srcs[2]; -+ struct aspeed_video_addr jpeg; -+ -+ bool yuv420; -+ unsigned int frame_rate; -+ unsigned int jpeg_quality; -+ -+ unsigned int frame_bottom; -+ unsigned int frame_left; -+ unsigned int frame_right; -+ unsigned int frame_top; -+}; -+ -+#define to_aspeed_video(x) container_of((x), struct aspeed_video, v4l2_dev) -+ -+static const u32 aspeed_video_jpeg_header[ASPEED_VIDEO_JPEG_HEADER_SIZE] = { -+ 0xe0ffd8ff, 0x464a1000, 0x01004649, 0x60000101, 0x00006000, 0x0f00feff, -+ 0x00002d05, 0x00000000, 0x00000000, 0x00dbff00 -+}; -+ -+static const u32 aspeed_video_jpeg_quant[ASPEED_VIDEO_JPEG_QUANT_SIZE] = { -+ 0x081100c0, 0x00000000, 0x00110103, 0x03011102, 0xc4ff0111, 0x00001f00, -+ 0x01010501, 0x01010101, 0x00000000, 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, 0x00011f00, -+ 0x01010103, 0x01010101, 0x00000101, 0x00000000, 0x04030201, 0x08070605, -+ 0xff0b0a09, 0x11b500c4, 0x02010200, 0x04030404, 0x04040507, 0x77020100, -+ 0x03020100, 0x21050411, 0x41120631, 0x71610751, 0x81322213, 0x91421408, -+ 0x09c1b1a1, 0xf0523323, 0xd1726215, 0x3424160a, 0x17f125e1, 0x261a1918, -+ 0x2a292827, 0x38373635, 0x44433a39, 0x48474645, 0x54534a49, 0x58575655, -+ 0x64635a59, 0x68676665, 0x74736a69, 0x78777675, 0x83827a79, 0x87868584, -+ 0x928a8988, 0x96959493, 0x9a999897, 0xa5a4a3a2, 0xa9a8a7a6, 0xb4b3b2aa, -+ 0xb8b7b6b5, 0xc3c2bab9, 0xc7c6c5c4, 0xd2cac9c8, 0xd6d5d4d3, 0xdad9d8d7, -+ 0xe5e4e3e2, 0xe9e8e7e6, 0xf4f3f2ea, 0xf8f7f6f5, 0xdafffaf9, 0x01030c00, -+ 0x03110200, 0x003f0011 -+}; -+ -+static const u32 aspeed_video_jpeg_dct[ASPEED_VIDEO_JPEG_NUM_QUALITIES] -+ [ASPEED_VIDEO_JPEG_DCT_SIZE] = { -+ { 0x0d140043, 0x0c0f110f, 0x11101114, 0x17141516, 0x1e20321e, -+ 0x3d1e1b1b, 0x32242e2b, 0x4b4c3f48, 0x44463f47, 0x61735a50, -+ 0x566c5550, 0x88644644, 0x7a766c65, 0x4d808280, 0x8c978d60, -+ 0x7e73967d, 0xdbff7b80, 0x1f014300, 0x272d2121, 0x3030582d, -+ 0x697bb958, 0xb8b9b97b, 0xb9b8a6a6, 0xb9b9b9b9, 0xb9b9b9b9, -+ 0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, -+ 0xb9b9b9b9, 0xb9b9b9b9, 0xb9b9b9b9, 0xffb9b9b9 }, -+ { 0x0c110043, 0x0a0d0f0d, 0x0f0e0f11, 0x14111213, 0x1a1c2b1a, -+ 0x351a1818, 0x2b1f2826, 0x4142373f, 0x3c3d373e, 0x55644e46, -+ 0x4b5f4a46, 0x77573d3c, 0x6b675f58, 0x43707170, 0x7a847b54, -+ 0x6e64836d, 0xdbff6c70, 0x1b014300, 0x22271d1d, 0x2a2a4c27, -+ 0x5b6ba04c, 0xa0a0a06b, 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, -+ 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, -+ 0xa0a0a0a0, 0xa0a0a0a0, 0xa0a0a0a0, 0xffa0a0a0 }, -+ { 0x090e0043, 0x090a0c0a, 0x0c0b0c0e, 0x110e0f10, 0x15172415, -+ 0x2c151313, 0x241a211f, 0x36372e34, 0x31322e33, 0x4653413a, -+ 0x3e4e3d3a, 0x62483231, 0x58564e49, 0x385d5e5d, 0x656d6645, -+ 0x5b536c5a, 0xdbff595d, 0x16014300, 0x1c201818, 0x22223f20, -+ 0x4b58853f, 0x85858558, 0x85858585, 0x85858585, 0x85858585, -+ 0x85858585, 0x85858585, 0x85858585, 0x85858585, 0x85858585, -+ 0x85858585, 0x85858585, 0x85858585, 0xff858585 }, -+ { 0x070b0043, 0x07080a08, 0x0a090a0b, 0x0d0b0c0c, 0x11121c11, -+ 0x23110f0f, 0x1c141a19, 0x2b2b2429, 0x27282428, 0x3842332e, -+ 0x313e302e, 0x4e392827, 0x46443e3a, 0x2c4a4a4a, 0x50565137, -+ 0x48425647, 0xdbff474a, 0x12014300, 0x161a1313, 0x1c1c331a, -+ 0x3d486c33, 0x6c6c6c48, 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, -+ 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, -+ 0x6c6c6c6c, 0x6c6c6c6c, 0x6c6c6c6c, 0xff6c6c6c }, -+ { 0x06090043, 0x05060706, 0x07070709, 0x0a09090a, 0x0d0e160d, -+ 0x1b0d0c0c, 0x16101413, 0x21221c20, 0x1e1f1c20, 0x2b332824, -+ 0x26302624, 0x3d2d1f1e, 0x3735302d, 0x22393a39, 0x3f443f2b, -+ 0x38334338, 0xdbff3739, 0x0d014300, 0x11130e0e, 0x15152613, -+ 0x2d355026, 0x50505035, 0x50505050, 0x50505050, 0x50505050, -+ 0x50505050, 0x50505050, 0x50505050, 0x50505050, 0x50505050, -+ 0x50505050, 0x50505050, 0x50505050, 0xff505050 }, -+ { 0x04060043, 0x03040504, 0x05040506, 0x07060606, 0x09090f09, -+ 0x12090808, 0x0f0a0d0d, 0x16161315, 0x14151315, 0x1d221b18, -+ 0x19201918, 0x281e1514, 0x2423201e, 0x17262726, 0x2a2d2a1c, -+ 0x25222d25, 0xdbff2526, 0x09014300, 0x0b0d0a0a, 0x0e0e1a0d, -+ 0x1f25371a, 0x37373725, 0x37373737, 0x37373737, 0x37373737, -+ 0x37373737, 0x37373737, 0x37373737, 0x37373737, 0x37373737, -+ 0x37373737, 0x37373737, 0x37373737, 0xff373737 }, -+ { 0x02030043, 0x01020202, 0x02020203, 0x03030303, 0x04040704, -+ 0x09040404, 0x07050606, 0x0b0b090a, 0x0a0a090a, 0x0e110d0c, -+ 0x0c100c0c, 0x140f0a0a, 0x1211100f, 0x0b131313, 0x1516150e, -+ 0x12111612, 0xdbff1213, 0x04014300, 0x05060505, 0x07070d06, -+ 0x0f121b0d, 0x1b1b1b12, 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, -+ 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, -+ 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b, 0xff1b1b1b }, -+ { 0x01020043, 0x01010101, 0x01010102, 0x02020202, 0x03030503, -+ 0x06030202, 0x05030404, 0x07070607, 0x06070607, 0x090b0908, -+ 0x080a0808, 0x0d0a0706, 0x0c0b0a0a, 0x070c0d0c, 0x0e0f0e09, -+ 0x0c0b0f0c, 0xdbff0c0c, 0x03014300, 0x03040303, 0x04040804, -+ 0x0a0c1208, 0x1212120c, 0x12121212, 0x12121212, 0x12121212, -+ 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212, -+ 0x12121212, 0x12121212, 0x12121212, 0xff121212 }, -+ { 0x01020043, 0x01010101, 0x01010102, 0x02020202, 0x03030503, -+ 0x06030202, 0x05030404, 0x07070607, 0x06070607, 0x090b0908, -+ 0x080a0808, 0x0d0a0706, 0x0c0b0a0a, 0x070c0d0c, 0x0e0f0e09, -+ 0x0c0b0f0c, 0xdbff0c0c, 0x02014300, 0x03030202, 0x04040703, -+ 0x080a0f07, 0x0f0f0f0a, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, -+ 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, -+ 0x0f0f0f0f, 0x0f0f0f0f, 0x0f0f0f0f, 0xff0f0f0f }, -+ { 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x02020302, -+ 0x04020202, 0x03020303, 0x05050405, 0x05050405, 0x07080606, -+ 0x06080606, 0x0a070505, 0x09080807, 0x05090909, 0x0a0b0a07, -+ 0x09080b09, 0xdbff0909, 0x02014300, 0x02030202, 0x03030503, -+ 0x07080c05, 0x0c0c0c08, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, -+ 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, -+ 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0xff0c0c0c }, -+ { 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x01010201, -+ 0x03010101, 0x02010202, 0x03030303, 0x03030303, 0x04050404, -+ 0x04050404, 0x06050303, 0x06050505, 0x03060606, 0x07070704, -+ 0x06050706, 0xdbff0606, 0x01014300, 0x01020101, 0x02020402, -+ 0x05060904, 0x09090906, 0x09090909, 0x09090909, 0x09090909, -+ 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909, -+ 0x09090909, 0x09090909, 0x09090909, 0xff090909 }, -+ { 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x01010101, -+ 0x01010101, 0x01010101, 0x01010101, 0x01010101, 0x02020202, -+ 0x02020202, 0x03020101, 0x03020202, 0x01030303, 0x03030302, -+ 0x03020303, 0xdbff0403, 0x01014300, 0x01010101, 0x01010201, -+ 0x03040602, 0x06060604, 0x06060606, 0x06060606, 0x06060606, -+ 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, -+ 0x06060606, 0x06060606, 0x06060606, 0xff060606 } -+}; -+ -+static const struct v4l2_dv_timings_cap aspeed_video_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 = 138240000, /* 1920 x 1200 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 void aspeed_video_init_jpeg_table(u32 *table, bool yuv420) -+{ -+ int i; -+ unsigned int base; -+ -+ for (i = 0; i < ASPEED_VIDEO_JPEG_NUM_QUALITIES; i++) { -+ base = 256 * i; /* AST HW requires this header spacing */ -+ memcpy(&table[base], aspeed_video_jpeg_header, -+ sizeof(aspeed_video_jpeg_header)); -+ -+ base += ASPEED_VIDEO_JPEG_HEADER_SIZE; -+ memcpy(&table[base], aspeed_video_jpeg_dct[i], -+ sizeof(aspeed_video_jpeg_dct[i])); -+ -+ base += ASPEED_VIDEO_JPEG_DCT_SIZE; -+ memcpy(&table[base], aspeed_video_jpeg_quant, -+ sizeof(aspeed_video_jpeg_quant)); -+ -+ if (yuv420) -+ table[base + 2] = 0x00220103; -+ } -+} -+ -+static void aspeed_video_update(struct aspeed_video *video, u32 reg, u32 clear, -+ u32 bits) -+{ -+ u32 t = readl(video->base + reg); -+ u32 before = t; -+ -+ t &= ~clear; -+ t |= bits; -+ writel(t, video->base + reg); -+ dev_dbg(video->dev, "update %03x[%08x -> %08x]\n", reg, before, -+ readl(video->base + reg)); -+} -+ -+static u32 aspeed_video_read(struct aspeed_video *video, u32 reg) -+{ -+ u32 t = readl(video->base + reg); -+ -+ dev_dbg(video->dev, "read %03x[%08x]\n", reg, t); -+ return t; -+} -+ -+static void aspeed_video_write(struct aspeed_video *video, u32 reg, u32 val) -+{ -+ writel(val, video->base + reg); -+ dev_dbg(video->dev, "write %03x[%08x]\n", reg, -+ readl(video->base + reg)); -+} -+ -+static int aspeed_video_start_frame(struct aspeed_video *video) -+{ -+ dma_addr_t addr; -+ unsigned long flags; -+ struct aspeed_video_buffer *buf; -+ u32 seq_ctrl = aspeed_video_read(video, VE_SEQ_CTRL); -+ -+ if (video->v4l2_input_status) { -+ dev_dbg(video->dev, "No signal; don't start frame\n"); -+ return 0; -+ } -+ -+ if (!(seq_ctrl & VE_SEQ_CTRL_COMP_BUSY) || -+ !(seq_ctrl & VE_SEQ_CTRL_CAP_BUSY)) { -+ dev_err(video->dev, "Engine busy; don't start frame\n"); -+ return -EBUSY; -+ } -+ -+ spin_lock_irqsave(&video->lock, flags); -+ buf = list_first_entry_or_null(&video->buffers, -+ struct aspeed_video_buffer, link); -+ if (!buf) { -+ spin_unlock_irqrestore(&video->lock, flags); -+ dev_dbg(video->dev, "No buffers; don't start frame\n"); -+ return -EPROTO; -+ } -+ -+ set_bit(VIDEO_FRAME_INPRG, &video->flags); -+ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); -+ spin_unlock_irqrestore(&video->lock, flags); -+ -+ aspeed_video_write(video, VE_COMP_PROC_OFFSET, 0); -+ aspeed_video_write(video, VE_COMP_OFFSET, 0); -+ aspeed_video_write(video, VE_COMP_ADDR, addr); -+ -+ aspeed_video_update(video, VE_INTERRUPT_CTRL, 0, -+ VE_INTERRUPT_COMP_COMPLETE | -+ VE_INTERRUPT_CAPTURE_COMPLETE); -+ -+ aspeed_video_update(video, VE_SEQ_CTRL, 0, -+ VE_SEQ_CTRL_TRIG_CAPTURE | VE_SEQ_CTRL_TRIG_COMP); -+ -+ return 0; -+} -+ -+static void aspeed_video_enable_mode_detect(struct aspeed_video *video) -+{ -+ /* Enable mode detect interrupts */ -+ aspeed_video_update(video, VE_INTERRUPT_CTRL, 0, -+ VE_INTERRUPT_MODE_DETECT); -+ -+ /* Trigger mode detect */ -+ aspeed_video_update(video, VE_SEQ_CTRL, 0, VE_SEQ_CTRL_TRIG_MODE_DET); -+} -+ -+static void aspeed_video_reset(struct aspeed_video *video) -+{ -+ /* Reset the engine */ -+ reset_control_assert(video->rst); -+ -+ /* Don't usleep here; function may be called in interrupt context */ -+ udelay(100); -+ reset_control_deassert(video->rst); -+} -+ -+static void aspeed_video_off(struct aspeed_video *video) -+{ -+ aspeed_video_reset(video); -+ -+ /* Turn off the relevant clocks */ -+ clk_disable_unprepare(video->vclk); -+ clk_disable_unprepare(video->eclk); -+} -+ -+static void aspeed_video_on(struct aspeed_video *video) -+{ -+ /* Turn on the relevant clocks */ -+ clk_prepare_enable(video->eclk); -+ clk_prepare_enable(video->vclk); -+ -+ aspeed_video_reset(video); -+} -+ -+static void aspeed_video_bufs_done(struct aspeed_video *video, -+ enum vb2_buffer_state state) -+{ -+ unsigned long flags; -+ struct aspeed_video_buffer *buf; -+ -+ spin_lock_irqsave(&video->lock, flags); -+ list_for_each_entry(buf, &video->buffers, link) -+ vb2_buffer_done(&buf->vb.vb2_buf, state); -+ INIT_LIST_HEAD(&video->buffers); -+ spin_unlock_irqrestore(&video->lock, flags); -+} -+ -+static void aspeed_video_irq_res_change(struct aspeed_video *video) -+{ -+ dev_dbg(video->dev, "Resolution changed; resetting\n"); -+ -+ set_bit(VIDEO_RES_CHANGE, &video->flags); -+ clear_bit(VIDEO_FRAME_INPRG, &video->flags); -+ -+ aspeed_video_off(video); -+ aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR); -+ -+ schedule_delayed_work(&video->res_work, RESOLUTION_CHANGE_DELAY); -+} -+ -+static irqreturn_t aspeed_video_irq(int irq, void *arg) -+{ -+ struct aspeed_video *video = arg; -+ u32 sts = aspeed_video_read(video, VE_INTERRUPT_STATUS); -+ -+ /* -+ * Resolution changed or signal was lost; reset the engine and -+ * re-initialize -+ */ -+ if (sts & VE_INTERRUPT_MODE_DETECT_WD) { -+ aspeed_video_irq_res_change(video); -+ return IRQ_HANDLED; -+ } -+ -+ if (sts & VE_INTERRUPT_MODE_DETECT) { -+ if (test_bit(VIDEO_RES_DETECT, &video->flags)) { -+ aspeed_video_update(video, VE_INTERRUPT_CTRL, -+ VE_INTERRUPT_MODE_DETECT, 0); -+ aspeed_video_write(video, VE_INTERRUPT_STATUS, -+ VE_INTERRUPT_MODE_DETECT); -+ -+ set_bit(VIDEO_MODE_DETECT_DONE, &video->flags); -+ wake_up_interruptible_all(&video->wait); -+ } else { -+ /* -+ * Signal acquired while NOT doing resolution -+ * detection; reset the engine and re-initialize -+ */ -+ aspeed_video_irq_res_change(video); -+ return IRQ_HANDLED; -+ } -+ } -+ -+ if ((sts & VE_INTERRUPT_COMP_COMPLETE) && -+ (sts & VE_INTERRUPT_CAPTURE_COMPLETE)) { -+ struct aspeed_video_buffer *buf; -+ u32 frame_size = aspeed_video_read(video, -+ VE_OFFSET_COMP_STREAM); -+ -+ spin_lock(&video->lock); -+ clear_bit(VIDEO_FRAME_INPRG, &video->flags); -+ buf = list_first_entry_or_null(&video->buffers, -+ struct aspeed_video_buffer, -+ link); -+ if (buf) { -+ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, frame_size); -+ -+ if (!list_is_last(&buf->link, &video->buffers)) { -+ buf->vb.vb2_buf.timestamp = ktime_get_ns(); -+ buf->vb.sequence = video->sequence++; -+ buf->vb.field = V4L2_FIELD_NONE; -+ vb2_buffer_done(&buf->vb.vb2_buf, -+ VB2_BUF_STATE_DONE); -+ list_del(&buf->link); -+ } -+ } -+ spin_unlock(&video->lock); -+ -+ aspeed_video_update(video, VE_SEQ_CTRL, -+ VE_SEQ_CTRL_TRIG_CAPTURE | -+ VE_SEQ_CTRL_FORCE_IDLE | -+ VE_SEQ_CTRL_TRIG_COMP, 0); -+ aspeed_video_update(video, VE_INTERRUPT_CTRL, -+ VE_INTERRUPT_COMP_COMPLETE | -+ VE_INTERRUPT_CAPTURE_COMPLETE, 0); -+ aspeed_video_write(video, VE_INTERRUPT_STATUS, -+ VE_INTERRUPT_COMP_COMPLETE | -+ VE_INTERRUPT_CAPTURE_COMPLETE); -+ -+ if (test_bit(VIDEO_STREAMING, &video->flags) && buf) -+ aspeed_video_start_frame(video); -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+static void aspeed_video_check_and_set_polarity(struct aspeed_video *video) -+{ -+ int i; -+ int hsync_counter = 0; -+ int vsync_counter = 0; -+ u32 sts; -+ -+ for (i = 0; i < NUM_POLARITY_CHECKS; ++i) { -+ sts = aspeed_video_read(video, VE_MODE_DETECT_STATUS); -+ if (sts & VE_MODE_DETECT_STATUS_VSYNC) -+ vsync_counter--; -+ else -+ vsync_counter++; -+ -+ if (sts & VE_MODE_DETECT_STATUS_HSYNC) -+ hsync_counter--; -+ else -+ hsync_counter++; -+ } -+ -+ if (hsync_counter < 0 || vsync_counter < 0) { -+ u32 ctrl; -+ -+ if (hsync_counter < 0) { -+ ctrl = VE_CTRL_HSYNC_POL; -+ video->detected_timings.polarities &= -+ ~V4L2_DV_HSYNC_POS_POL; -+ } else { -+ video->detected_timings.polarities |= -+ V4L2_DV_HSYNC_POS_POL; -+ } -+ -+ if (vsync_counter < 0) { -+ ctrl = VE_CTRL_VSYNC_POL; -+ video->detected_timings.polarities &= -+ ~V4L2_DV_VSYNC_POS_POL; -+ } else { -+ video->detected_timings.polarities |= -+ V4L2_DV_VSYNC_POS_POL; -+ } -+ -+ aspeed_video_update(video, VE_CTRL, 0, ctrl); -+ } -+} -+ -+static bool aspeed_video_alloc_buf(struct aspeed_video *video, -+ struct aspeed_video_addr *addr, -+ unsigned int size) -+{ -+ addr->virt = dma_alloc_coherent(video->dev, size, &addr->dma, -+ GFP_KERNEL); -+ if (!addr->virt) -+ return false; -+ -+ addr->size = size; -+ return true; -+} -+ -+static void aspeed_video_free_buf(struct aspeed_video *video, -+ struct aspeed_video_addr *addr) -+{ -+ dma_free_coherent(video->dev, addr->size, addr->virt, addr->dma); -+ addr->size = 0; -+ addr->dma = 0ULL; -+ addr->virt = NULL; -+} -+ -+/* -+ * Get the minimum HW-supported compression buffer size for the frame size. -+ * Assume worst-case JPEG compression size is 1/8 raw size. This should be -+ * plenty even for maximum quality; any worse and the engine will simply return -+ * incomplete JPEGs. -+ */ -+static void aspeed_video_calc_compressed_size(struct aspeed_video *video, -+ unsigned int frame_size) -+{ -+ int i, j; -+ u32 compression_buffer_size_reg = 0; -+ unsigned int size; -+ const unsigned int num_compression_packets = 4; -+ const unsigned int compression_packet_size = 1024; -+ const unsigned int max_compressed_size = frame_size / 2; /* 4bpp / 8 */ -+ -+ video->max_compressed_size = UINT_MAX; -+ -+ for (i = 0; i < 6; ++i) { -+ for (j = 0; j < 8; ++j) { -+ size = (num_compression_packets << i) * -+ (compression_packet_size << j); -+ if (size < max_compressed_size) -+ continue; -+ -+ if (size < video->max_compressed_size) { -+ compression_buffer_size_reg = (i << 3) | j; -+ video->max_compressed_size = size; -+ } -+ } -+ } -+ -+ aspeed_video_write(video, VE_STREAM_BUF_SIZE, -+ compression_buffer_size_reg); -+ -+ dev_dbg(video->dev, "Max compressed size: %x\n", -+ video->max_compressed_size); -+} -+ -+#define res_check(v) test_and_clear_bit(VIDEO_MODE_DETECT_DONE, &(v)->flags) -+ -+static void aspeed_video_get_resolution(struct aspeed_video *video) -+{ -+ bool invalid_resolution = true; -+ int rc; -+ int tries = 0; -+ u32 mds; -+ u32 src_lr_edge; -+ u32 src_tb_edge; -+ u32 sync; -+ struct v4l2_bt_timings *det = &video->detected_timings; -+ -+ det->width = MIN_WIDTH; -+ det->height = MIN_HEIGHT; -+ video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; -+ -+ /* -+ * Since we need max buffer size for detection, free the second source -+ * buffer first. -+ */ -+ if (video->srcs[1].size) -+ aspeed_video_free_buf(video, &video->srcs[1]); -+ -+ if (video->srcs[0].size < VE_MAX_SRC_BUFFER_SIZE) { -+ if (video->srcs[0].size) -+ aspeed_video_free_buf(video, &video->srcs[0]); -+ -+ if (!aspeed_video_alloc_buf(video, &video->srcs[0], -+ VE_MAX_SRC_BUFFER_SIZE)) { -+ dev_err(video->dev, -+ "Failed to allocate source buffers\n"); -+ return; -+ } -+ } -+ -+ aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma); -+ -+ do { -+ if (tries) { -+ set_current_state(TASK_INTERRUPTIBLE); -+ if (schedule_timeout(INVALID_RESOLUTION_DELAY)) -+ return; -+ } -+ -+ set_bit(VIDEO_RES_DETECT, &video->flags); -+ aspeed_video_enable_mode_detect(video); -+ -+ rc = wait_event_interruptible_timeout(video->wait, -+ res_check(video), -+ MODE_DETECT_TIMEOUT); -+ if (!rc) { -+ dev_err(video->dev, "Timed out; first mode detect\n"); -+ clear_bit(VIDEO_RES_DETECT, &video->flags); -+ return; -+ } -+ -+ /* Disable mode detect in order to re-trigger */ -+ aspeed_video_update(video, VE_SEQ_CTRL, -+ VE_SEQ_CTRL_TRIG_MODE_DET, 0); -+ -+ aspeed_video_check_and_set_polarity(video); -+ -+ aspeed_video_enable_mode_detect(video); -+ -+ rc = wait_event_interruptible_timeout(video->wait, -+ res_check(video), -+ MODE_DETECT_TIMEOUT); -+ clear_bit(VIDEO_RES_DETECT, &video->flags); -+ if (!rc) { -+ dev_err(video->dev, "Timed out; second mode detect\n"); -+ return; -+ } -+ -+ src_lr_edge = aspeed_video_read(video, VE_SRC_LR_EDGE_DET); -+ src_tb_edge = aspeed_video_read(video, VE_SRC_TB_EDGE_DET); -+ mds = aspeed_video_read(video, VE_MODE_DETECT_STATUS); -+ sync = aspeed_video_read(video, VE_SYNC_STATUS); -+ -+ video->frame_bottom = (src_tb_edge & VE_SRC_TB_EDGE_DET_BOT) >> -+ VE_SRC_TB_EDGE_DET_BOT_SHF; -+ video->frame_top = src_tb_edge & VE_SRC_TB_EDGE_DET_TOP; -+ det->vfrontporch = video->frame_top; -+ det->vbackporch = ((mds & VE_MODE_DETECT_V_LINES) >> -+ VE_MODE_DETECT_V_LINES_SHF) - video->frame_bottom; -+ det->vsync = (sync & VE_SYNC_STATUS_VSYNC) >> -+ VE_SYNC_STATUS_VSYNC_SHF; -+ if (video->frame_top > video->frame_bottom) -+ continue; -+ -+ video->frame_right = (src_lr_edge & VE_SRC_LR_EDGE_DET_RT) >> -+ VE_SRC_LR_EDGE_DET_RT_SHF; -+ video->frame_left = src_lr_edge & VE_SRC_LR_EDGE_DET_LEFT; -+ det->hfrontporch = video->frame_left; -+ det->hbackporch = (mds & VE_MODE_DETECT_H_PIXELS) - -+ video->frame_right; -+ det->hsync = sync & VE_SYNC_STATUS_HSYNC; -+ if (video->frame_left > video->frame_right) -+ continue; -+ -+ invalid_resolution = false; -+ } while (invalid_resolution && (tries++ < INVALID_RESOLUTION_RETRIES)); -+ -+ if (invalid_resolution) { -+ dev_err(video->dev, "Invalid resolution detected\n"); -+ return; -+ } -+ -+ det->height = (video->frame_bottom - video->frame_top) + 1; -+ det->width = (video->frame_right - video->frame_left) + 1; -+ video->v4l2_input_status = 0; -+ -+ /* -+ * Enable mode-detect watchdog, resolution-change watchdog and -+ * automatic compression after frame capture. -+ */ -+ aspeed_video_update(video, VE_INTERRUPT_CTRL, 0, -+ VE_INTERRUPT_MODE_DETECT_WD); -+ aspeed_video_update(video, VE_SEQ_CTRL, 0, -+ VE_SEQ_CTRL_AUTO_COMP | VE_SEQ_CTRL_EN_WATCHDOG); -+ -+ dev_dbg(video->dev, "Got resolution: %dx%d\n", det->width, -+ det->height); -+} -+ -+static void aspeed_video_set_resolution(struct aspeed_video *video) -+{ -+ struct v4l2_bt_timings *act = &video->active_timings; -+ unsigned int size = act->width * act->height; -+ -+ aspeed_video_calc_compressed_size(video, size); -+ -+ /* Don't use direct mode below 1024 x 768 (irqs don't fire) */ -+ if (size < DIRECT_FETCH_THRESHOLD) { -+ aspeed_video_write(video, VE_TGS_0, -+ FIELD_PREP(VE_TGS_FIRST, -+ video->frame_left - 1) | -+ FIELD_PREP(VE_TGS_LAST, -+ video->frame_right)); -+ aspeed_video_write(video, VE_TGS_1, -+ FIELD_PREP(VE_TGS_FIRST, video->frame_top) | -+ FIELD_PREP(VE_TGS_LAST, -+ video->frame_bottom + 1)); -+ aspeed_video_update(video, VE_CTRL, 0, VE_CTRL_INT_DE); -+ } else { -+ aspeed_video_update(video, VE_CTRL, 0, VE_CTRL_DIRECT_FETCH); -+ } -+ -+ /* Set capture/compression frame sizes */ -+ aspeed_video_write(video, VE_CAP_WINDOW, -+ act->width << 16 | act->height); -+ aspeed_video_write(video, VE_COMP_WINDOW, -+ act->width << 16 | act->height); -+ aspeed_video_write(video, VE_SRC_SCANLINE_OFFSET, act->width * 4); -+ -+ size *= 4; -+ -+ if (size == video->srcs[0].size / 2) { -+ aspeed_video_write(video, VE_SRC1_ADDR, -+ video->srcs[0].dma + size); -+ } else if (size == video->srcs[0].size) { -+ if (!aspeed_video_alloc_buf(video, &video->srcs[1], size)) -+ goto err_mem; -+ -+ aspeed_video_write(video, VE_SRC1_ADDR, video->srcs[1].dma); -+ } else { -+ aspeed_video_free_buf(video, &video->srcs[0]); -+ -+ if (!aspeed_video_alloc_buf(video, &video->srcs[0], size)) -+ goto err_mem; -+ -+ if (!aspeed_video_alloc_buf(video, &video->srcs[1], size)) -+ goto err_mem; -+ -+ aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma); -+ aspeed_video_write(video, VE_SRC1_ADDR, video->srcs[1].dma); -+ } -+ -+ return; -+ -+err_mem: -+ dev_err(video->dev, "Failed to allocate source buffers\n"); -+ -+ if (video->srcs[0].size) -+ aspeed_video_free_buf(video, &video->srcs[0]); -+} -+ -+static void aspeed_video_init_regs(struct aspeed_video *video) -+{ -+ u32 comp_ctrl = VE_COMP_CTRL_RSVD | -+ FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) | -+ FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10); -+ u32 ctrl = VE_CTRL_AUTO_OR_CURSOR; -+ u32 seq_ctrl = VE_SEQ_CTRL_JPEG_MODE; -+ -+ if (video->frame_rate) -+ ctrl |= FIELD_PREP(VE_CTRL_FRC, video->frame_rate); -+ -+ if (video->yuv420) -+ seq_ctrl |= VE_SEQ_CTRL_YUV420; -+ -+ /* Unlock VE registers */ -+ aspeed_video_write(video, VE_PROTECTION_KEY, VE_PROTECTION_KEY_UNLOCK); -+ -+ /* Disable interrupts */ -+ aspeed_video_write(video, VE_INTERRUPT_CTRL, 0); -+ aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xffffffff); -+ -+ /* Clear the offset */ -+ aspeed_video_write(video, VE_COMP_PROC_OFFSET, 0); -+ aspeed_video_write(video, VE_COMP_OFFSET, 0); -+ -+ aspeed_video_write(video, VE_JPEG_ADDR, video->jpeg.dma); -+ -+ /* Set control registers */ -+ aspeed_video_write(video, VE_SEQ_CTRL, seq_ctrl); -+ aspeed_video_write(video, VE_CTRL, ctrl); -+ aspeed_video_write(video, VE_COMP_CTRL, comp_ctrl); -+ -+ /* Don't downscale */ -+ aspeed_video_write(video, VE_SCALING_FACTOR, 0x10001000); -+ aspeed_video_write(video, VE_SCALING_FILTER0, 0x00200000); -+ aspeed_video_write(video, VE_SCALING_FILTER1, 0x00200000); -+ aspeed_video_write(video, VE_SCALING_FILTER2, 0x00200000); -+ aspeed_video_write(video, VE_SCALING_FILTER3, 0x00200000); -+ -+ /* Set mode detection defaults */ -+ aspeed_video_write(video, VE_MODE_DETECT, 0x22666500); -+} -+ -+static void aspeed_video_start(struct aspeed_video *video) -+{ -+ aspeed_video_on(video); -+ -+ aspeed_video_init_regs(video); -+ -+ /* Resolution set to 640x480 if no signal found */ -+ aspeed_video_get_resolution(video); -+ -+ /* Set timings since the device is being opened for the first time */ -+ video->active_timings = video->detected_timings; -+ aspeed_video_set_resolution(video); -+ -+ video->pix_fmt.width = video->active_timings.width; -+ video->pix_fmt.height = video->active_timings.height; -+ video->pix_fmt.sizeimage = video->max_compressed_size; -+} -+ -+static void aspeed_video_stop(struct aspeed_video *video) -+{ -+ set_bit(VIDEO_STOPPED, &video->flags); -+ cancel_delayed_work_sync(&video->res_work); -+ -+ aspeed_video_off(video); -+ -+ if (video->srcs[0].size) -+ aspeed_video_free_buf(video, &video->srcs[0]); -+ -+ if (video->srcs[1].size) -+ aspeed_video_free_buf(video, &video->srcs[1]); -+ -+ video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; -+ video->flags = 0; -+} -+ -+static int aspeed_video_querycap(struct file *file, void *fh, -+ struct v4l2_capability *cap) -+{ -+ strscpy(cap->driver, DEVICE_NAME, sizeof(cap->driver)); -+ strscpy(cap->card, "Aspeed Video Engine", sizeof(cap->card)); -+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", -+ DEVICE_NAME); -+ -+ return 0; -+} -+ -+static int aspeed_video_enum_format(struct file *file, void *fh, -+ struct v4l2_fmtdesc *f) -+{ -+ if (f->index) -+ return -EINVAL; -+ -+ f->pixelformat = V4L2_PIX_FMT_JPEG; -+ -+ return 0; -+} -+ -+static int aspeed_video_get_format(struct file *file, void *fh, -+ struct v4l2_format *f) -+{ -+ struct aspeed_video *video = video_drvdata(file); -+ -+ f->fmt.pix = video->pix_fmt; -+ -+ return 0; -+} -+ -+static int aspeed_video_enum_input(struct file *file, void *fh, -+ struct v4l2_input *inp) -+{ -+ struct aspeed_video *video = video_drvdata(file); -+ -+ if (inp->index) -+ return -EINVAL; -+ -+ strscpy(inp->name, "Host VGA capture", sizeof(inp->name)); -+ inp->type = V4L2_INPUT_TYPE_CAMERA; -+ inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; -+ inp->status = video->v4l2_input_status; -+ -+ return 0; -+} -+ -+static int aspeed_video_get_input(struct file *file, void *fh, unsigned int *i) -+{ -+ *i = 0; -+ -+ return 0; -+} -+ -+static int aspeed_video_set_input(struct file *file, void *fh, unsigned int i) -+{ -+ if (i) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+static int aspeed_video_get_parm(struct file *file, void *fh, -+ struct v4l2_streamparm *a) -+{ -+ struct aspeed_video *video = video_drvdata(file); -+ -+ a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; -+ a->parm.capture.readbuffers = 3; -+ a->parm.capture.timeperframe.numerator = 1; -+ if (!video->frame_rate) -+ a->parm.capture.timeperframe.denominator = MAX_FRAME_RATE; -+ else -+ a->parm.capture.timeperframe.denominator = video->frame_rate; -+ -+ return 0; -+} -+ -+static int aspeed_video_set_parm(struct file *file, void *fh, -+ struct v4l2_streamparm *a) -+{ -+ unsigned int frame_rate = 0; -+ struct aspeed_video *video = video_drvdata(file); -+ -+ a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; -+ a->parm.capture.readbuffers = 3; -+ -+ if (a->parm.capture.timeperframe.numerator) -+ frame_rate = a->parm.capture.timeperframe.denominator / -+ a->parm.capture.timeperframe.numerator; -+ -+ if (!frame_rate || frame_rate > MAX_FRAME_RATE) { -+ frame_rate = 0; -+ a->parm.capture.timeperframe.denominator = MAX_FRAME_RATE; -+ a->parm.capture.timeperframe.numerator = 1; -+ } -+ -+ if (video->frame_rate != frame_rate) { -+ video->frame_rate = frame_rate; -+ aspeed_video_update(video, VE_CTRL, VE_CTRL_FRC, -+ FIELD_PREP(VE_CTRL_FRC, frame_rate)); -+ } -+ -+ return 0; -+} -+ -+static int aspeed_video_enum_framesizes(struct file *file, void *fh, -+ struct v4l2_frmsizeenum *fsize) -+{ -+ struct aspeed_video *video = video_drvdata(file); -+ -+ if (fsize->index) -+ return -EINVAL; -+ -+ if (fsize->pixel_format != V4L2_PIX_FMT_JPEG) -+ return -EINVAL; -+ -+ fsize->discrete.width = video->pix_fmt.width; -+ fsize->discrete.height = video->pix_fmt.height; -+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; -+ -+ return 0; -+} -+ -+static int aspeed_video_enum_frameintervals(struct file *file, void *fh, -+ struct v4l2_frmivalenum *fival) -+{ -+ struct aspeed_video *video = video_drvdata(file); -+ -+ if (fival->index) -+ return -EINVAL; -+ -+ if (fival->width != video->detected_timings.width || -+ fival->height != video->detected_timings.height) -+ return -EINVAL; -+ -+ if (fival->pixel_format != V4L2_PIX_FMT_JPEG) -+ 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 aspeed_video_set_dv_timings(struct file *file, void *fh, -+ struct v4l2_dv_timings *timings) -+{ -+ struct aspeed_video *video = video_drvdata(file); -+ -+ if (timings->bt.width == video->active_timings.width && -+ timings->bt.height == video->active_timings.height) -+ return 0; -+ -+ if (vb2_is_busy(&video->queue)) -+ return -EBUSY; -+ -+ video->active_timings = timings->bt; -+ -+ aspeed_video_set_resolution(video); -+ -+ video->pix_fmt.width = timings->bt.width; -+ video->pix_fmt.height = timings->bt.height; -+ video->pix_fmt.sizeimage = video->max_compressed_size; -+ -+ timings->type = V4L2_DV_BT_656_1120; -+ -+ return 0; -+} -+ -+static int aspeed_video_get_dv_timings(struct file *file, void *fh, -+ struct v4l2_dv_timings *timings) -+{ -+ struct aspeed_video *video = video_drvdata(file); -+ -+ timings->type = V4L2_DV_BT_656_1120; -+ timings->bt = video->active_timings; -+ -+ return 0; -+} -+ -+static int aspeed_video_query_dv_timings(struct file *file, void *fh, -+ struct v4l2_dv_timings *timings) -+{ -+ int rc; -+ struct aspeed_video *video = video_drvdata(file); -+ -+ /* -+ * This blocks only if the driver is currently in the process of -+ * detecting a new resolution; in the event of no signal or timeout -+ * this function is woken up. -+ */ -+ if (file->f_flags & O_NONBLOCK) { -+ if (test_bit(VIDEO_RES_CHANGE, &video->flags)) -+ return -EAGAIN; -+ } else { -+ rc = wait_event_interruptible(video->wait, -+ !test_bit(VIDEO_RES_CHANGE, -+ &video->flags)); -+ if (rc) -+ return -EINTR; -+ } -+ -+ timings->type = V4L2_DV_BT_656_1120; -+ timings->bt = video->detected_timings; -+ -+ return video->v4l2_input_status ? -ENOLINK : 0; -+} -+ -+static int aspeed_video_enum_dv_timings(struct file *file, void *fh, -+ struct v4l2_enum_dv_timings *timings) -+{ -+ return v4l2_enum_dv_timings_cap(timings, &aspeed_video_timings_cap, -+ NULL, NULL); -+} -+ -+static int aspeed_video_dv_timings_cap(struct file *file, void *fh, -+ struct v4l2_dv_timings_cap *cap) -+{ -+ *cap = aspeed_video_timings_cap; -+ -+ return 0; -+} -+ -+static int aspeed_video_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); -+ } -+ -+ return v4l2_ctrl_subscribe_event(fh, sub); -+} -+ -+static const struct v4l2_ioctl_ops aspeed_video_ioctl_ops = { -+ .vidioc_querycap = aspeed_video_querycap, -+ -+ .vidioc_enum_fmt_vid_cap = aspeed_video_enum_format, -+ .vidioc_g_fmt_vid_cap = aspeed_video_get_format, -+ .vidioc_s_fmt_vid_cap = aspeed_video_get_format, -+ .vidioc_try_fmt_vid_cap = aspeed_video_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 = aspeed_video_enum_input, -+ .vidioc_g_input = aspeed_video_get_input, -+ .vidioc_s_input = aspeed_video_set_input, -+ -+ .vidioc_g_parm = aspeed_video_get_parm, -+ .vidioc_s_parm = aspeed_video_set_parm, -+ .vidioc_enum_framesizes = aspeed_video_enum_framesizes, -+ .vidioc_enum_frameintervals = aspeed_video_enum_frameintervals, -+ -+ .vidioc_s_dv_timings = aspeed_video_set_dv_timings, -+ .vidioc_g_dv_timings = aspeed_video_get_dv_timings, -+ .vidioc_query_dv_timings = aspeed_video_query_dv_timings, -+ .vidioc_enum_dv_timings = aspeed_video_enum_dv_timings, -+ .vidioc_dv_timings_cap = aspeed_video_dv_timings_cap, -+ -+ .vidioc_subscribe_event = aspeed_video_sub_event, -+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -+}; -+ -+static void aspeed_video_update_jpeg_quality(struct aspeed_video *video) -+{ -+ u32 comp_ctrl = FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) | -+ FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10); -+ -+ aspeed_video_update(video, VE_COMP_CTRL, -+ VE_COMP_CTRL_DCT_LUM | VE_COMP_CTRL_DCT_CHR, -+ comp_ctrl); -+} -+ -+static void aspeed_video_update_subsampling(struct aspeed_video *video) -+{ -+ if (video->jpeg.virt) -+ aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420); -+ -+ if (video->yuv420) -+ aspeed_video_update(video, VE_SEQ_CTRL, 0, VE_SEQ_CTRL_YUV420); -+ else -+ aspeed_video_update(video, VE_SEQ_CTRL, VE_SEQ_CTRL_YUV420, 0); -+} -+ -+static int aspeed_video_set_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct aspeed_video *video = container_of(ctrl->handler, -+ struct aspeed_video, -+ ctrl_handler); -+ -+ switch (ctrl->id) { -+ case V4L2_CID_JPEG_COMPRESSION_QUALITY: -+ video->jpeg_quality = ctrl->val; -+ aspeed_video_update_jpeg_quality(video); -+ break; -+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: -+ if (ctrl->val == V4L2_JPEG_CHROMA_SUBSAMPLING_420) { -+ video->yuv420 = true; -+ aspeed_video_update_subsampling(video); -+ } else { -+ video->yuv420 = false; -+ aspeed_video_update_subsampling(video); -+ } -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static const struct v4l2_ctrl_ops aspeed_video_ctrl_ops = { -+ .s_ctrl = aspeed_video_set_ctrl, -+}; -+ -+static void aspeed_video_resolution_work(struct work_struct *work) -+{ -+ struct delayed_work *dwork = to_delayed_work(work); -+ struct aspeed_video *video = container_of(dwork, struct aspeed_video, -+ res_work); -+ u32 input_status = video->v4l2_input_status; -+ -+ aspeed_video_on(video); -+ -+ /* Exit early in case no clients remain */ -+ if (test_bit(VIDEO_STOPPED, &video->flags)) -+ goto done; -+ -+ aspeed_video_init_regs(video); -+ -+ aspeed_video_get_resolution(video); -+ -+ if (video->detected_timings.width != video->active_timings.width || -+ video->detected_timings.height != video->active_timings.height || -+ input_status != video->v4l2_input_status) { -+ static const struct v4l2_event ev = { -+ .type = V4L2_EVENT_SOURCE_CHANGE, -+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, -+ }; -+ -+ v4l2_event_queue(&video->vdev, &ev); -+ } else if (test_bit(VIDEO_STREAMING, &video->flags)) { -+ /* No resolution change so just restart streaming */ -+ aspeed_video_start_frame(video); -+ } -+ -+done: -+ clear_bit(VIDEO_RES_CHANGE, &video->flags); -+ wake_up_interruptible_all(&video->wait); -+} -+ -+static int aspeed_video_open(struct file *file) -+{ -+ int rc; -+ struct aspeed_video *video = video_drvdata(file); -+ -+ mutex_lock(&video->video_lock); -+ -+ rc = v4l2_fh_open(file); -+ if (rc) { -+ mutex_unlock(&video->video_lock); -+ return rc; -+ } -+ -+ if (v4l2_fh_is_singular_file(file)) -+ aspeed_video_start(video); -+ -+ mutex_unlock(&video->video_lock); -+ -+ return 0; -+} -+ -+static int aspeed_video_release(struct file *file) -+{ -+ int rc; -+ struct aspeed_video *video = video_drvdata(file); -+ -+ mutex_lock(&video->video_lock); -+ -+ if (v4l2_fh_is_singular_file(file)) -+ aspeed_video_stop(video); -+ -+ rc = _vb2_fop_release(file, NULL); -+ -+ mutex_unlock(&video->video_lock); -+ -+ return rc; -+} -+ -+static const struct v4l2_file_operations aspeed_video_v4l2_fops = { -+ .owner = THIS_MODULE, -+ .read = vb2_fop_read, -+ .poll = vb2_fop_poll, -+ .unlocked_ioctl = video_ioctl2, -+ .mmap = vb2_fop_mmap, -+ .open = aspeed_video_open, -+ .release = aspeed_video_release, -+}; -+ -+static int aspeed_video_queue_setup(struct vb2_queue *q, -+ unsigned int *num_buffers, -+ unsigned int *num_planes, -+ unsigned int sizes[], -+ struct device *alloc_devs[]) -+{ -+ struct aspeed_video *video = vb2_get_drv_priv(q); -+ -+ if (*num_planes) { -+ if (sizes[0] < video->max_compressed_size) -+ return -EINVAL; -+ -+ return 0; -+ } -+ -+ *num_planes = 1; -+ sizes[0] = video->max_compressed_size; -+ -+ return 0; -+} -+ -+static int aspeed_video_buf_prepare(struct vb2_buffer *vb) -+{ -+ struct aspeed_video *video = vb2_get_drv_priv(vb->vb2_queue); -+ -+ if (vb2_plane_size(vb, 0) < video->max_compressed_size) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+static int aspeed_video_start_streaming(struct vb2_queue *q, -+ unsigned int count) -+{ -+ int rc; -+ struct aspeed_video *video = vb2_get_drv_priv(q); -+ -+ video->sequence = 0; -+ -+ rc = aspeed_video_start_frame(video); -+ if (rc) { -+ aspeed_video_bufs_done(video, VB2_BUF_STATE_QUEUED); -+ return rc; -+ } -+ -+ set_bit(VIDEO_STREAMING, &video->flags); -+ return 0; -+} -+ -+static void aspeed_video_stop_streaming(struct vb2_queue *q) -+{ -+ int rc; -+ struct aspeed_video *video = vb2_get_drv_priv(q); -+ -+ clear_bit(VIDEO_STREAMING, &video->flags); -+ -+ rc = wait_event_timeout(video->wait, -+ !test_bit(VIDEO_FRAME_INPRG, &video->flags), -+ STOP_TIMEOUT); -+ if (!rc) { -+ dev_err(video->dev, "Timed out when stopping streaming\n"); -+ -+ /* -+ * Need to force stop any DMA and try and get HW into a good -+ * state for future calls to start streaming again. -+ */ -+ aspeed_video_reset(video); -+ aspeed_video_init_regs(video); -+ -+ aspeed_video_get_resolution(video); -+ } -+ -+ aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR); -+} -+ -+static void aspeed_video_buf_queue(struct vb2_buffer *vb) -+{ -+ bool empty; -+ struct aspeed_video *video = vb2_get_drv_priv(vb->vb2_queue); -+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); -+ struct aspeed_video_buffer *avb = to_aspeed_video_buffer(vbuf); -+ unsigned long flags; -+ -+ spin_lock_irqsave(&video->lock, flags); -+ empty = list_empty(&video->buffers); -+ list_add_tail(&avb->link, &video->buffers); -+ spin_unlock_irqrestore(&video->lock, flags); -+ -+ if (test_bit(VIDEO_STREAMING, &video->flags) && -+ !test_bit(VIDEO_FRAME_INPRG, &video->flags) && empty) -+ aspeed_video_start_frame(video); -+} -+ -+static const struct vb2_ops aspeed_video_vb2_ops = { -+ .queue_setup = aspeed_video_queue_setup, -+ .wait_prepare = vb2_ops_wait_prepare, -+ .wait_finish = vb2_ops_wait_finish, -+ .buf_prepare = aspeed_video_buf_prepare, -+ .start_streaming = aspeed_video_start_streaming, -+ .stop_streaming = aspeed_video_stop_streaming, -+ .buf_queue = aspeed_video_buf_queue, -+}; -+ -+static int aspeed_video_setup_video(struct aspeed_video *video) -+{ -+ const u64 mask = ~(BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_444) | -+ BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_420)); -+ struct v4l2_device *v4l2_dev = &video->v4l2_dev; -+ struct vb2_queue *vbq = &video->queue; -+ struct video_device *vdev = &video->vdev; -+ int rc; -+ -+ video->pix_fmt.pixelformat = V4L2_PIX_FMT_JPEG; -+ video->pix_fmt.field = V4L2_FIELD_NONE; -+ video->pix_fmt.colorspace = V4L2_COLORSPACE_SRGB; -+ video->pix_fmt.quantization = V4L2_QUANTIZATION_FULL_RANGE; -+ video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; -+ -+ rc = v4l2_device_register(video->dev, v4l2_dev); -+ if (rc) { -+ dev_err(video->dev, "Failed to register v4l2 device\n"); -+ return rc; -+ } -+ -+ v4l2_ctrl_handler_init(&video->ctrl_handler, 2); -+ v4l2_ctrl_new_std(&video->ctrl_handler, &aspeed_video_ctrl_ops, -+ V4L2_CID_JPEG_COMPRESSION_QUALITY, 0, -+ ASPEED_VIDEO_JPEG_NUM_QUALITIES - 1, 1, 0); -+ v4l2_ctrl_new_std_menu(&video->ctrl_handler, &aspeed_video_ctrl_ops, -+ V4L2_CID_JPEG_CHROMA_SUBSAMPLING, -+ V4L2_JPEG_CHROMA_SUBSAMPLING_420, mask, -+ V4L2_JPEG_CHROMA_SUBSAMPLING_444); -+ -+ if (video->ctrl_handler.error) { -+ v4l2_ctrl_handler_free(&video->ctrl_handler); -+ v4l2_device_unregister(v4l2_dev); -+ -+ dev_err(video->dev, "Failed to init controls: %d\n", -+ video->ctrl_handler.error); -+ return rc; -+ } -+ -+ v4l2_dev->ctrl_handler = &video->ctrl_handler; -+ -+ vbq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -+ vbq->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; -+ vbq->dev = v4l2_dev->dev; -+ vbq->lock = &video->video_lock; -+ vbq->ops = &aspeed_video_vb2_ops; -+ vbq->mem_ops = &vb2_dma_contig_memops; -+ vbq->drv_priv = video; -+ vbq->buf_struct_size = sizeof(struct aspeed_video_buffer); -+ vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; -+ vbq->min_buffers_needed = 3; -+ -+ rc = vb2_queue_init(vbq); -+ if (rc) { -+ v4l2_ctrl_handler_free(&video->ctrl_handler); -+ v4l2_device_unregister(v4l2_dev); -+ -+ dev_err(video->dev, "Failed to init vb2 queue\n"); -+ return rc; -+ } -+ -+ vdev->queue = vbq; -+ vdev->fops = &aspeed_video_v4l2_fops; -+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | -+ V4L2_CAP_STREAMING; -+ vdev->v4l2_dev = v4l2_dev; -+ strscpy(vdev->name, DEVICE_NAME, sizeof(vdev->name)); -+ vdev->vfl_type = VFL_TYPE_GRABBER; -+ vdev->vfl_dir = VFL_DIR_RX; -+ vdev->release = video_device_release_empty; -+ vdev->ioctl_ops = &aspeed_video_ioctl_ops; -+ vdev->lock = &video->video_lock; -+ -+ video_set_drvdata(vdev, video); -+ rc = video_register_device(vdev, VFL_TYPE_GRABBER, 0); -+ if (rc) { -+ vb2_queue_release(vbq); -+ v4l2_ctrl_handler_free(&video->ctrl_handler); -+ v4l2_device_unregister(v4l2_dev); -+ -+ dev_err(video->dev, "Failed to register video device\n"); -+ return rc; -+ } -+ -+ return 0; -+} -+ -+static int aspeed_video_init(struct aspeed_video *video) -+{ -+ int irq; -+ int rc; -+ struct device *dev = video->dev; -+ -+ irq = irq_of_parse_and_map(dev->of_node, 0); -+ if (!irq) { -+ dev_err(dev, "Unable to find IRQ\n"); -+ return -ENODEV; -+ } -+ -+ rc = devm_request_irq(dev, irq, aspeed_video_irq, IRQF_SHARED, -+ DEVICE_NAME, video); -+ if (rc < 0) { -+ dev_err(dev, "Unable to request IRQ %d\n", irq); -+ return rc; -+ } -+ -+ video->eclk = devm_clk_get(dev, "eclk"); -+ if (IS_ERR(video->eclk)) { -+ dev_err(dev, "Unable to get ECLK\n"); -+ return PTR_ERR(video->eclk); -+ } -+ -+ video->vclk = devm_clk_get(dev, "vclk"); -+ if (IS_ERR(video->vclk)) { -+ dev_err(dev, "Unable to get VCLK\n"); -+ return PTR_ERR(video->vclk); -+ } -+ -+ video->rst = devm_reset_control_get_exclusive(dev, NULL); -+ if (IS_ERR(video->rst)) { -+ dev_err(dev, "Unable to get VE reset\n"); -+ return PTR_ERR(video->rst); -+ } -+ -+ rc = of_reserved_mem_device_init(dev); -+ if (rc) { -+ dev_err(dev, "Unable to reserve memory\n"); -+ return rc; -+ } -+ -+ rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); -+ if (rc) { -+ dev_err(dev, "Failed to set DMA mask\n"); -+ of_reserved_mem_device_release(dev); -+ return rc; -+ } -+ -+ if (!aspeed_video_alloc_buf(video, &video->jpeg, -+ VE_JPEG_HEADER_SIZE)) { -+ dev_err(dev, "Failed to allocate DMA for JPEG header\n"); -+ of_reserved_mem_device_release(dev); -+ return rc; -+ } -+ -+ aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420); -+ -+ return 0; -+} -+ -+static int aspeed_video_probe(struct platform_device *pdev) -+{ -+ int rc; -+ struct resource *res; -+ struct aspeed_video *video = kzalloc(sizeof(*video), GFP_KERNEL); -+ -+ if (!video) -+ return -ENOMEM; -+ -+ video->frame_rate = 30; -+ video->dev = &pdev->dev; +@@ -1661,6 +1661,7 @@ static int aspeed_video_probe(struct platform_device *pdev) + + video->frame_rate = 30; + video->dev = &pdev->dev; + spin_lock_init(&video->lock); -+ mutex_init(&video->video_lock); -+ init_waitqueue_head(&video->wait); -+ INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work); -+ INIT_LIST_HEAD(&video->buffers); -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ -+ video->base = devm_ioremap_resource(video->dev, res); -+ -+ if (IS_ERR(video->base)) -+ return PTR_ERR(video->base); -+ -+ rc = aspeed_video_init(video); -+ if (rc) -+ return rc; -+ -+ rc = aspeed_video_setup_video(video); -+ if (rc) -+ return rc; -+ -+ return 0; -+} -+ -+static int aspeed_video_remove(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); -+ struct aspeed_video *video = to_aspeed_video(v4l2_dev); -+ -+ video_unregister_device(&video->vdev); -+ -+ vb2_queue_release(&video->queue); -+ -+ v4l2_ctrl_handler_free(&video->ctrl_handler); -+ -+ v4l2_device_unregister(v4l2_dev); -+ -+ dma_free_coherent(video->dev, VE_JPEG_HEADER_SIZE, video->jpeg.virt, -+ video->jpeg.dma); -+ -+ of_reserved_mem_device_release(dev); -+ -+ return 0; -+} -+ -+static const struct of_device_id aspeed_video_of_match[] = { -+ { .compatible = "aspeed,ast2400-video-engine" }, -+ { .compatible = "aspeed,ast2500-video-engine" }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, aspeed_video_of_match); -+ -+static struct platform_driver aspeed_video_driver = { -+ .driver = { -+ .name = DEVICE_NAME, -+ .of_match_table = aspeed_video_of_match, -+ }, -+ .probe = aspeed_video_probe, -+ .remove = aspeed_video_remove, -+}; -+ -+module_platform_driver(aspeed_video_driver); -+ -+MODULE_DESCRIPTION("ASPEED Video Engine Driver"); -+MODULE_AUTHOR("Eddie James"); -+MODULE_LICENSE("GPL v2"); + mutex_init(&video->video_lock); + init_waitqueue_head(&video->wait); + INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work); diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/clock/aspeed-clock.h index 335879505a72..0b0f3a0ebe9b 100644 --- a/include/dt-bindings/clock/aspeed-clock.h diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch index 7400e2848..02ca65e9f 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0039-Add-Aspeed-PWM-driver-which-uses-FTTMR010-timer-IP.patch @@ -1,4 +1,4 @@ -From a771e5448ed259f768434d498daf8d8b292713de Mon Sep 17 00:00:00 2001 +From 95bae3d3051ee13627e5ef92bb9d60cfb5731118 Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Mon, 11 Feb 2019 17:02:35 -0800 Subject: [PATCH] Add Aspeed PWM driver which uses FTTMR010 timer IP @@ -12,17 +12,16 @@ Signed-off-by: Jae Hyun Yoo --- arch/arm/boot/dts/aspeed-g5.dtsi | 2 +- drivers/clocksource/timer-fttmr010.c | 25 ++ - drivers/input/misc/pwm-beeper.c | 8 +- drivers/pwm/Kconfig | 9 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-fttmr010.c | 465 +++++++++++++++++++++++++++++++++++ include/clocksource/timer-fttmr010.h | 17 ++ - 7 files changed, 522 insertions(+), 5 deletions(-) + 6 files changed, 514 insertions(+), 5 deletions(-) create mode 100644 drivers/pwm/pwm-fttmr010.c create mode 100644 include/clocksource/timer-fttmr010.h diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi -index 6686a13a5354..ccf2845cd788 100644 +index d5783eaf30ae..992de63d7a19 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -301,7 +301,7 @@ @@ -35,7 +34,7 @@ index 6686a13a5354..ccf2845cd788 100644 interrupts = <16 17 18 35 36 37 38 39>; clocks = <&syscon ASPEED_CLK_APB>; diff --git a/drivers/clocksource/timer-fttmr010.c b/drivers/clocksource/timer-fttmr010.c -index cf93f6419b51..8226ccf5cc2c 100644 +index fadff7915dd9..49a790924360 100644 --- a/drivers/clocksource/timer-fttmr010.c +++ b/drivers/clocksource/timer-fttmr010.c @@ -20,6 +20,8 @@ @@ -45,9 +44,9 @@ index cf93f6419b51..8226ccf5cc2c 100644 +#include + /* - * Register definitions for the timers + * Register definitions common for all the timer variants. */ -@@ -77,6 +79,9 @@ +@@ -91,6 +93,9 @@ #define TIMER_3_INT_OVERFLOW BIT(8) #define TIMER_INT_ALL_MASK 0x1ff @@ -57,7 +56,7 @@ index cf93f6419b51..8226ccf5cc2c 100644 struct fttmr010 { void __iomem *base; unsigned int tick_rate; -@@ -123,8 +128,11 @@ static int fttmr010_timer_set_next_event(unsigned long cycles, +@@ -137,8 +142,11 @@ static int fttmr010_timer_set_next_event(unsigned long cycles, struct clock_event_device *evt) { struct fttmr010 *fttmr010 = to_fttmr010(evt); @@ -69,7 +68,7 @@ index cf93f6419b51..8226ccf5cc2c 100644 /* Stop */ cr = readl(fttmr010->base + TIMER_CR); cr &= ~fttmr010->t1_enable_val; -@@ -147,27 +155,37 @@ static int fttmr010_timer_set_next_event(unsigned long cycles, +@@ -161,27 +169,37 @@ static int fttmr010_timer_set_next_event(unsigned long cycles, cr |= fttmr010->t1_enable_val; writel(cr, fttmr010->base + TIMER_CR); @@ -107,16 +106,16 @@ index cf93f6419b51..8226ccf5cc2c 100644 /* Stop */ cr = readl(fttmr010->base + TIMER_CR); cr &= ~fttmr010->t1_enable_val; -@@ -186,6 +204,8 @@ static int fttmr010_timer_set_oneshot(struct clock_event_device *evt) - cr |= TIMER_1_INT_MATCH1; - writel(cr, fttmr010->base + TIMER_INTR_MASK); +@@ -201,6 +219,8 @@ static int fttmr010_timer_set_oneshot(struct clock_event_device *evt) + writel(cr, fttmr010->base + TIMER_INTR_MASK); + } + spin_unlock_irqrestore(&timer_fttmr010_lock, flags); + return 0; } -@@ -193,8 +213,11 @@ static int fttmr010_timer_set_periodic(struct clock_event_device *evt) +@@ -208,8 +228,11 @@ static int fttmr010_timer_set_periodic(struct clock_event_device *evt) { struct fttmr010 *fttmr010 = to_fttmr010(evt); u32 period = DIV_ROUND_CLOSEST(fttmr010->tick_rate, HZ); @@ -128,7 +127,7 @@ index cf93f6419b51..8226ccf5cc2c 100644 /* Stop */ cr = readl(fttmr010->base + TIMER_CR); cr &= ~fttmr010->t1_enable_val; -@@ -221,6 +244,8 @@ static int fttmr010_timer_set_periodic(struct clock_event_device *evt) +@@ -235,6 +258,8 @@ static int fttmr010_timer_set_periodic(struct clock_event_device *evt) cr |= fttmr010->t1_enable_val; writel(cr, fttmr010->base + TIMER_CR); @@ -137,45 +136,11 @@ index cf93f6419b51..8226ccf5cc2c 100644 return 0; } -diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c -index edca0d737750..a3baa52f187f 100644 ---- a/drivers/input/misc/pwm-beeper.c -+++ b/drivers/input/misc/pwm-beeper.c -@@ -52,7 +52,7 @@ static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period) - if (error) - return error; - -- if (!beeper->amplifier_on) { -+ if (beeper->amplifier && !beeper->amplifier_on) { - error = regulator_enable(beeper->amplifier); - if (error) { - pwm_disable(beeper->pwm); -@@ -67,7 +67,7 @@ static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period) - - static void pwm_beeper_off(struct pwm_beeper *beeper) - { -- if (beeper->amplifier_on) { -+ if (beeper->amplifier && beeper->amplifier_on) { - regulator_disable(beeper->amplifier); - beeper->amplifier_on = false; - } -@@ -163,9 +163,9 @@ static int pwm_beeper_probe(struct platform_device *pdev) - if (IS_ERR(beeper->amplifier)) { - error = PTR_ERR(beeper->amplifier); - if (error != -EPROBE_DEFER) -- dev_err(dev, "Failed to get 'amp' regulator: %d\n", -+ dev_dbg(dev, "Failed to get 'amp' regulator: %d\n", - error); -- return error; -+ beeper->amplifier = NULL; - } - - INIT_WORK(&beeper->work, pwm_beeper_work); diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig -index 504d252716f2..9d4642c668c9 100644 +index a8f47df0655a..92a8fbebe2d9 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig -@@ -168,6 +168,15 @@ config PWM_FSL_FTM +@@ -170,6 +170,15 @@ config PWM_FSL_FTM To compile this driver as a module, choose M here: the module will be called pwm-fsl-ftm. diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch index 85b2f45a1..1b86e9c04 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0040-i2c-Add-mux-hold-unhold-msg-types.patch @@ -1,4 +1,4 @@ -From 38ba0a960fcd17f7b3480fe3025c261fd60fe979 Mon Sep 17 00:00:00 2001 +From 80ea6461d77e5b415d9f83fa2f4708fc21eab09b Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Fri, 15 Feb 2019 16:05:09 -0800 Subject: [PATCH] i2c: Add mux hold/unhold msg types @@ -39,19 +39,19 @@ in downstream only. Signed-off-by: Jae Hyun Yoo --- - drivers/i2c/i2c-core-base.c | 79 +++++++++++++++++++++++++++---- + drivers/i2c/i2c-core-base.c | 99 ++++++++++++++++++++++++++++++++++----- drivers/i2c/i2c-core-smbus.c | 17 ++++++- drivers/i2c/i2c-mux.c | 109 +++++++++++++++++++++++++++++++++++++++---- include/linux/i2c-mux.h | 3 ++ include/linux/i2c.h | 25 ++++++++++ include/uapi/linux/i2c.h | 1 + - 6 files changed, 215 insertions(+), 19 deletions(-) + 6 files changed, 233 insertions(+), 21 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c -index 9200e349f29e..728b818501b1 100644 +index 28460f6a60cc..009b0507768e 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c -@@ -1211,6 +1211,25 @@ int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr) +@@ -1210,6 +1210,25 @@ int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr) } EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify); @@ -77,7 +77,7 @@ index 9200e349f29e..728b818501b1 100644 static int i2c_register_adapter(struct i2c_adapter *adap) { int res = -EINVAL; -@@ -1292,6 +1311,9 @@ static int i2c_register_adapter(struct i2c_adapter *adap) +@@ -1291,6 +1310,9 @@ static int i2c_register_adapter(struct i2c_adapter *adap) bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); mutex_unlock(&core_lock); @@ -87,7 +87,7 @@ index 9200e349f29e..728b818501b1 100644 return 0; out_reg: -@@ -1512,6 +1534,8 @@ void i2c_del_adapter(struct i2c_adapter *adap) +@@ -1511,6 +1533,8 @@ void i2c_del_adapter(struct i2c_adapter *adap) idr_remove(&i2c_adapter_idr, adap->nr); mutex_unlock(&core_lock); @@ -96,7 +96,7 @@ index 9200e349f29e..728b818501b1 100644 /* Clear the device structure in case this adapter is ever going to be added again */ memset(&adap->dev, 0, sizeof(adap->dev)); -@@ -1861,7 +1885,9 @@ static int i2c_check_for_quirks(struct i2c_adapter *adap, struct i2c_msg *msgs, +@@ -1860,7 +1884,9 @@ static int i2c_check_for_quirks(struct i2c_adapter *adap, struct i2c_msg *msgs, */ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { @@ -106,7 +106,7 @@ index 9200e349f29e..728b818501b1 100644 int ret, try; if (WARN_ON(!msgs || num < 1)) -@@ -1870,6 +1896,25 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +@@ -1869,6 +1895,25 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) if (adap->quirks && i2c_check_for_quirks(adap, msgs, num)) return -EOPNOTSUPP; @@ -132,7 +132,7 @@ index 9200e349f29e..728b818501b1 100644 /* * i2c_trace_msg_key gets enabled when tracepoint i2c_transfer gets * enabled. This is an efficient way of keeping the for-loop from -@@ -1902,6 +1947,9 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +@@ -1901,6 +1946,9 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) trace_i2c_result(adap, num, ret); } @@ -142,26 +142,39 @@ index 9200e349f29e..728b818501b1 100644 return ret; } EXPORT_SYMBOL(__i2c_transfer); -@@ -1920,6 +1968,7 @@ EXPORT_SYMBOL(__i2c_transfer); +@@ -1919,6 +1967,7 @@ EXPORT_SYMBOL(__i2c_transfer); */ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { + bool do_bus_lock = true; int ret; - /* REVISIT the fault reporting model here is weak: -@@ -1949,18 +1998,30 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) - (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : ""); - } - #endif -- -- if (in_atomic() || irqs_disabled()) { -- ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT); -- if (!ret) -- /* I2C activity is ongoing. */ -- return -EAGAIN; -- } else { -- i2c_lock_bus(adap, I2C_LOCK_SEGMENT); + if (!adap->algo->master_xfer) { +@@ -1942,19 +1991,47 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + * one (discarding status on the second message) or errno + * (discarding status on the first one). + */ +- if (in_atomic() || irqs_disabled()) { +- ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT); +- if (!ret) +- /* I2C activity is ongoing. */ +- return -EAGAIN; +- } else { +- i2c_lock_bus(adap, I2C_LOCK_SEGMENT); +- } + +- ret = __i2c_transfer(adap, msgs, num); +- i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); ++ if (adap->algo->master_xfer) { ++#ifdef DEBUG ++ for (ret = 0; ret < num; ret++) { ++ dev_dbg(&adap->dev, ++ "master_xfer[%d] %c, addr=0x%02x, len=%d%s\n", ++ ret, (msgs[ret].flags & I2C_M_RD) ? 'R' : 'W', ++ msgs[ret].addr, msgs[ret].len, ++ (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : ""); ++ } ++#endif + /* + * Do not lock a bus for delivering an unhold msg to a mux + * adpater. This is just for a single length unhold msg case. @@ -181,15 +194,21 @@ index 9200e349f29e..728b818501b1 100644 + } else { + i2c_lock_bus(adap, I2C_LOCK_SEGMENT); + } - } ++ } - ret = __i2c_transfer(adap, msgs, num); -- i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); +- return ret; ++ ret = __i2c_transfer(adap, msgs, num); + if (do_bus_lock) + i2c_unlock_bus(adap, I2C_LOCK_SEGMENT); ++ ++ return ret; ++ } else { ++ dev_dbg(&adap->dev, "I2C level transfers not supported\n"); ++ return -EOPNOTSUPP; ++ } + } + EXPORT_SYMBOL(i2c_transfer); - return ret; - } else { diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index 9cd66cabb84f..64c58911bf21 100644 --- a/drivers/i2c/i2c-core-smbus.c diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch index 9e7757011..9aee6f0c0 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0041-Enable-passthrough-based-gpio-character-device.patch @@ -1,20 +1,20 @@ -From d34efc982a9206db87da49be3d9b1e20f59be56f Mon Sep 17 00:00:00 2001 +From 554bc7a7c7aa6e0c0ec49a24063102e17954d06c Mon Sep 17 00:00:00 2001 From: Kuiying Wang Date: Thu, 31 Jan 2019 17:47:39 +0800 Subject: [PATCH] Enable passthrough based gpio character device. Signed-off-by: Kuiying Wang --- - drivers/gpio/gpio-aspeed.c | 47 ++++++++++++++++++++++- - drivers/gpio/gpiolib.c | 51 +++++++++++++++++++++++-- + drivers/gpio/gpio-aspeed.c | 47 ++++++++++++++++++++++++++++-- + drivers/gpio/gpiolib.c | 51 +++++++++++++++++++++++++++++++-- drivers/gpio/gpiolib.h | 1 + - include/linux/gpio/consumer.h | 9 +++++ - include/linux/pinctrl/pinconf-generic.h | 2 + + include/linux/gpio/consumer.h | 9 ++++++ + include/linux/pinctrl/pinconf-generic.h | 2 ++ include/uapi/linux/gpio.h | 1 + 6 files changed, 106 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c -index 2342e154029b..06fd95197684 100644 +index 854bce4fb9e7..5f1bce3a9274 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -17,9 +17,11 @@ @@ -115,10 +115,10 @@ index 2342e154029b..06fd95197684 100644 gpio_id = of_match_node(aspeed_gpio_of_table, pdev->dev.of_node); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c -index a8e01d99919c..21eeca17583d 100644 +index d1adfdf50fb3..4f9fdd25c6d7 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c -@@ -419,6 +419,7 @@ struct linehandle_state { +@@ -428,6 +428,7 @@ struct linehandle_state { GPIOHANDLE_REQUEST_OUTPUT | \ GPIOHANDLE_REQUEST_ACTIVE_LOW | \ GPIOHANDLE_REQUEST_OPEN_DRAIN | \ @@ -126,7 +126,7 @@ index a8e01d99919c..21eeca17583d 100644 GPIOHANDLE_REQUEST_OPEN_SOURCE) static long linehandle_ioctl(struct file *filep, unsigned int cmd, -@@ -519,7 +520,6 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) +@@ -530,7 +531,6 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) return -EINVAL; lflags = handlereq.flags; @@ -134,7 +134,7 @@ index a8e01d99919c..21eeca17583d 100644 /* Return an error if an unknown flag is set */ if (lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) return -EINVAL; -@@ -579,6 +579,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) +@@ -590,6 +590,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) set_bit(FLAG_OPEN_DRAIN, &desc->flags); if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); @@ -143,7 +143,7 @@ index a8e01d99919c..21eeca17583d 100644 ret = gpiod_set_transitory(desc, false); if (ret < 0) -@@ -598,6 +600,11 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) +@@ -609,6 +611,11 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) ret = gpiod_direction_input(desc); if (ret) goto out_free_descs; @@ -155,7 +155,7 @@ index a8e01d99919c..21eeca17583d 100644 } dev_dbg(&gdev->dev, "registered chardev handle for line %d\n", offset); -@@ -1010,7 +1017,6 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +@@ -1027,7 +1034,6 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct gpio_device *gdev = filp->private_data; struct gpio_chip *chip = gdev->chip; void __user *ip = (void __user *)arg; @@ -163,7 +163,7 @@ index a8e01d99919c..21eeca17583d 100644 /* We fail any subsequent ioctl():s when the chip is gone */ if (!chip) return -ENODEV; -@@ -1018,7 +1024,6 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +@@ -1035,7 +1041,6 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) /* Fill in the struct and pass to userspace */ if (cmd == GPIO_GET_CHIPINFO_IOCTL) { struct gpiochip_info chipinfo; @@ -171,11 +171,10 @@ index a8e01d99919c..21eeca17583d 100644 memset(&chipinfo, 0, sizeof(chipinfo)); strncpy(chipinfo.name, dev_name(&gdev->dev), -@@ -2643,6 +2648,46 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) - } +@@ -2709,6 +2714,46 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) EXPORT_SYMBOL_GPL(gpiod_direction_output); -+/** + /** + * gpiod_direction_pass_through - set the GPIO direction to pass-through + * @desc: GPIO to set to pass-through + * @@ -215,14 +214,15 @@ index a8e01d99919c..21eeca17583d 100644 +} +EXPORT_SYMBOL_GPL(gpiod_direction_pass_through); + - /** ++/** * gpiod_set_debounce - sets @debounce time for a GPIO * @desc: descriptor of the GPIO for which to set debounce time + * @debounce: debounce time in microseconds diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h -index a7e49fef73d4..b143ee47870a 100644 +index bc57f0dc5953..a821a04fc04b 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h -@@ -210,6 +210,7 @@ struct gpio_desc { +@@ -212,6 +212,7 @@ struct gpio_desc { #define FLAG_IS_OUT 1 #define FLAG_EXPORT 2 /* protected by sysfs_lock */ #define FLAG_SYSFS 3 /* exported via /sys/class/gpio/control */ @@ -231,10 +231,10 @@ index a7e49fef73d4..b143ee47870a 100644 #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h -index 21ddbe440030..96551839c191 100644 +index 9ddcf50a3c59..f9775be5a46a 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h -@@ -99,6 +99,7 @@ void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs); +@@ -110,6 +110,7 @@ void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs); int gpiod_get_direction(struct gpio_desc *desc); int gpiod_direction_input(struct gpio_desc *desc); int gpiod_direction_output(struct gpio_desc *desc, int value); @@ -242,7 +242,7 @@ index 21ddbe440030..96551839c191 100644 int gpiod_direction_output_raw(struct gpio_desc *desc, int value); /* Value get/set from non-sleeping context */ -@@ -314,6 +315,14 @@ static inline int gpiod_direction_output(struct gpio_desc *desc, int value) +@@ -348,6 +349,14 @@ static inline int gpiod_direction_output(struct gpio_desc *desc, int value) WARN_ON(1); return -ENOSYS; } @@ -271,7 +271,7 @@ index 6c0680641108..59f0cbabb685 100644 PIN_CONFIG_MAX = 0xFF, }; diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h -index 1bf6e6df084b..384ced158412 100644 +index 4ebfe0ac6c5b..99864572b7d9 100644 --- a/include/uapi/linux/gpio.h +++ b/include/uapi/linux/gpio.h @@ -62,6 +62,7 @@ struct gpioline_info { @@ -283,5 +283,5 @@ index 1bf6e6df084b..384ced158412 100644 /** * struct gpiohandle_request - Information about a GPIO handle request -- -2.19.1 +2.7.4 diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch index 901d63645..3588d62d9 100644 --- a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0042-Add-bus-timeout-ms-and-retries-device-tree-propertie.patch @@ -1,4 +1,4 @@ -From ca4f6555620212ffccd943ea4c58e7ee7b1b0571 Mon Sep 17 00:00:00 2001 +From 6515a2134f90f33dbbea8ede5de598d17bb00c12 Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Thu, 7 Mar 2019 15:17:40 -0800 Subject: [PATCH] Add bus-timeout-ms and #retries device tree properties @@ -42,7 +42,7 @@ index 8fbd8633a387..7da7e813b2b0 100644 Example: diff --git a/Documentation/devicetree/bindings/i2c/i2c.txt b/Documentation/devicetree/bindings/i2c/i2c.txt -index 11263982470e..bdead91f82a4 100644 +index 44efafdfd7f5..e382931cf3d6 100644 --- a/Documentation/devicetree/bindings/i2c/i2c.txt +++ b/Documentation/devicetree/bindings/i2c/i2c.txt @@ -80,6 +80,12 @@ wants to support one of the below features, it should adapt the bindings below. @@ -71,10 +71,10 @@ index 506d867b43d9..84237c5d0aca 100644 bus->adap.dev.parent = &pdev->dev; bus->adap.dev.of_node = pdev->dev.of_node; diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c -index 728b818501b1..17cf0fb037ac 100644 +index 009b0507768e..386aa2dad908 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c -@@ -1232,6 +1232,7 @@ static void i2c_adapter_hold_timer_callback(struct timer_list *t) +@@ -1231,6 +1231,7 @@ static void i2c_adapter_hold_timer_callback(struct timer_list *t) static int i2c_register_adapter(struct i2c_adapter *adap) { @@ -82,7 +82,7 @@ index 728b818501b1..17cf0fb037ac 100644 int res = -EINVAL; /* Can't register until after driver model init */ -@@ -1258,8 +1259,15 @@ static int i2c_register_adapter(struct i2c_adapter *adap) +@@ -1257,8 +1258,15 @@ static int i2c_register_adapter(struct i2c_adapter *adap) INIT_LIST_HEAD(&adap->userspace_clients); /* Set default timeout to 1 second if not already set */ diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0049-Suppress-excessive-HID-gadget-error-logs.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0049-Suppress-excessive-HID-gadget-error-logs.patch new file mode 100644 index 000000000..d0f98b9c1 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0049-Suppress-excessive-HID-gadget-error-logs.patch @@ -0,0 +1,43 @@ +From 7dd0a7c62e5885bb726ef2bd5007e79a50932c38 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo +Date: Mon, 18 Mar 2019 14:06:36 -0700 +Subject: [PATCH] Suppress excessive HID gadget error logs + +HID events can be sent even when the host disconnects the HID +device according to the current graphic mode. For an example, if +KVM mouse events are sent when the host is in text mode, queueing +of end point messages will be dropped with this message: + +configfs-gadget gadget: usb_ep_queue error on int endpoint -108 + +This case is very usual case in BMC since BMC can control power +status of the host, so this commit suppress the error printing outs +with making HID gadget driver drop events quietly in the case. + +This should be a downstream only customization. Do not upstream it. + +Signed-off-by: Jae Hyun Yoo +--- + drivers/usb/gadget/function/f_hid.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c +index f3816a5c861e..3a94584a9dbc 100644 +--- a/drivers/usb/gadget/function/f_hid.c ++++ b/drivers/usb/gadget/function/f_hid.c +@@ -395,8 +395,10 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, + + status = usb_ep_queue(hidg->in_ep, req, GFP_ATOMIC); + if (status < 0) { +- ERROR(hidg->func.config->cdev, +- "usb_ep_queue error on int endpoint %zd\n", status); ++ if (status != -ESHUTDOWN) ++ ERROR(hidg->func.config->cdev, ++ "usb_ep_queue error on int endpoint %zd\n", ++ status); + goto release_write_pending; + } else { + status = count; +-- +2.7.4 + diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0050-media-platform-Fix-a-kernel-warning-on-clk-control.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0050-media-platform-Fix-a-kernel-warning-on-clk-control.patch new file mode 100644 index 000000000..a0168c889 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0050-media-platform-Fix-a-kernel-warning-on-clk-control.patch @@ -0,0 +1,177 @@ +From 1775e41d085b24a672dc271d08bfc83401288f0b Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo +Date: Fri, 22 Mar 2019 16:34:54 -0700 +Subject: [PATCH] media: platform: Fix a kernel warning on clk control + +Video engine clock control functions in the Aspeed video engine driver are +being called from multiple context without any protection so video clocks +can be disabled twice and eventually it causes a kernel warning with stack +dump printing out like below: + +[ 120.034729] WARNING: CPU: 0 PID: 1334 at drivers/clk/clk.c:684 clk_core_unprepare+0x13c/0x170 +[ 120.043252] eclk-gate already unprepared +[ 120.047283] CPU: 0 PID: 1334 Comm: obmc-ikvm Tainted: G W 5.0.3-b94b74e8b52db91fe4e99e0bb481ec8bf2b5b47c #1 +[ 120.058417] Hardware name: Generic DT based system +[ 120.063219] Backtrace: +[ 120.065787] [<80107cdc>] (dump_backtrace) from [<80107f10>] (show_stack+0x20/0x24) +[ 120.073371] r7:803a4ff0 r6:00000009 r5:00000000 r4:96197e1c +[ 120.079152] [<80107ef0>] (show_stack) from [<8068f7d8>] (dump_stack+0x20/0x28) +[ 120.086479] [<8068f7b8>] (dump_stack) from [<8011604c>] (__warn.part.3+0xb4/0xdc) +[ 120.094068] [<80115f98>] (__warn.part.3) from [<801160e0>] (warn_slowpath_fmt+0x6c/0x90) +[ 120.102164] r6:000002ac r5:8080c0b8 r4:80a07008 +[ 120.106893] [<80116078>] (warn_slowpath_fmt) from [<803a4ff0>] (clk_core_unprepare+0x13c/0x170) +[ 120.115686] r3:8080cf8c r2:8080c17c +[ 120.119276] r7:97d68e58 r6:9df23200 r5:9668c260 r4:96459260 +[ 120.125046] [<803a4eb4>] (clk_core_unprepare) from [<803a707c>] (clk_unprepare+0x34/0x3c) +[ 120.133226] r5:9668c260 r4:96459260 +[ 120.136932] [<803a7048>] (clk_unprepare) from [<804f34bc>] (aspeed_video_off+0x44/0x48) +[ 120.145031] r5:9668c260 r4:9668cbc0 +[ 120.148647] [<804f3478>] (aspeed_video_off) from [<804f3fd0>] (aspeed_video_release+0x94/0x118) +[ 120.157435] r5:966a0cb8 r4:966a0800 +[ 120.161049] [<804f3f3c>] (aspeed_video_release) from [<804d2c58>] (v4l2_release+0xd4/0xe8) +[ 120.169404] r7:97d68e58 r6:9d087810 r5:9df23200 r4:966a0b20 +[ 120.175168] [<804d2b84>] (v4l2_release) from [<80236224>] (__fput+0x98/0x1c4) +[ 120.182316] r5:96698e78 r4:9df23200 +[ 120.185994] [<8023618c>] (__fput) from [<802363b8>] (____fput+0x18/0x1c) +[ 120.192712] r9:80a0700c r8:801011e4 r7:00000000 r6:80a64bbc r5:961dd560 r4:961dd89c +[ 120.200562] [<802363a0>] (____fput) from [<80131c08>] (task_work_run+0x7c/0xa4) +[ 120.207994] [<80131b8c>] (task_work_run) from [<80106884>] (do_work_pending+0x4a8/0x578) +[ 120.216163] r7:801011e4 r6:80a07008 r5:96197fb0 r4:ffffe000 +[ 120.221856] [<801063dc>] (do_work_pending) from [<8010106c>] (slow_work_pending+0xc/0x20) +[ 120.230116] Exception stack(0x96197fb0 to 0x96197ff8) +[ 120.235254] 7fa0: 00000000 76ccf094 00000000 00000000 +[ 120.243438] 7fc0: 00000008 00a11978 7eab3c30 00000006 00000000 00000000 475b0fa4 00000000 +[ 120.251692] 7fe0: 00000002 7eab3a40 00000000 47720e38 80000010 00000008 +[ 120.258396] r10:00000000 r9:96196000 r8:801011e4 r7:00000006 r6:7eab3c30 r5:00a11978 +[ 120.266291] r4:00000008 + +To prevent this issue, this commit adds spinlock protection and clock +status checking logic into the Aspeed video engine driver. + +Fixes: d2b4387f3bdf ("media: platform: Add Aspeed Video Engine driver") +Signed-off-by: Jae Hyun Yoo +Cc: Eddie James +Cc: Mauro Carvalho Chehab +Cc: Joel Stanley +Cc: Andrew Jeffery +--- + drivers/media/platform/aspeed-video.c | 32 +++++++++++++++++++++++++++++--- + 1 file changed, 29 insertions(+), 3 deletions(-) + +diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c +index 8144fe36ad48..e70be8fdbde5 100644 +--- a/drivers/media/platform/aspeed-video.c ++++ b/drivers/media/platform/aspeed-video.c +@@ -187,6 +187,7 @@ enum { + VIDEO_STREAMING, + VIDEO_FRAME_INPRG, + VIDEO_STOPPED, ++ VIDEO_CLOCKS_ON, + }; + + struct aspeed_video_addr { +@@ -483,19 +484,29 @@ static void aspeed_video_enable_mode_detect(struct aspeed_video *video) + + static void aspeed_video_off(struct aspeed_video *video) + { ++ if (!test_bit(VIDEO_CLOCKS_ON, &video->flags)) ++ return; ++ + /* Disable interrupts */ + aspeed_video_write(video, VE_INTERRUPT_CTRL, 0); + + /* Turn off the relevant clocks */ + clk_disable_unprepare(video->vclk); + clk_disable_unprepare(video->eclk); ++ ++ clear_bit(VIDEO_CLOCKS_ON, &video->flags); + } + + static void aspeed_video_on(struct aspeed_video *video) + { ++ if (test_bit(VIDEO_CLOCKS_ON, &video->flags)) ++ return; ++ + /* Turn on the relevant clocks */ + clk_prepare_enable(video->eclk); + clk_prepare_enable(video->vclk); ++ ++ set_bit(VIDEO_CLOCKS_ON, &video->flags); + } + + static void aspeed_video_bufs_done(struct aspeed_video *video, +@@ -513,12 +524,14 @@ static void aspeed_video_bufs_done(struct aspeed_video *video, + + static void aspeed_video_irq_res_change(struct aspeed_video *video) + { ++ spin_lock(&video->lock); + dev_dbg(video->dev, "Resolution changed; resetting\n"); + + set_bit(VIDEO_RES_CHANGE, &video->flags); + clear_bit(VIDEO_FRAME_INPRG, &video->flags); + + aspeed_video_off(video); ++ spin_unlock(&video->lock); + aspeed_video_bufs_done(video, VB2_BUF_STATE_ERROR); + + schedule_delayed_work(&video->res_work, RESOLUTION_CHANGE_DELAY); +@@ -938,9 +951,13 @@ static void aspeed_video_init_regs(struct aspeed_video *video) + + static void aspeed_video_start(struct aspeed_video *video) + { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&video->lock, flags); + aspeed_video_on(video); + + aspeed_video_init_regs(video); ++ spin_unlock_irqrestore(&video->lock, flags); + + /* Resolution set to 640x480 if no signal found */ + aspeed_video_get_resolution(video); +@@ -956,6 +973,9 @@ static void aspeed_video_start(struct aspeed_video *video) + + static void aspeed_video_stop(struct aspeed_video *video) + { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&video->lock, flags); + set_bit(VIDEO_STOPPED, &video->flags); + cancel_delayed_work_sync(&video->res_work); + +@@ -969,6 +989,7 @@ static void aspeed_video_stop(struct aspeed_video *video) + + video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; + video->flags = 0; ++ spin_unlock_irqrestore(&video->lock, flags); + } + + static int aspeed_video_querycap(struct file *file, void *fh, +@@ -1306,16 +1327,21 @@ static void aspeed_video_resolution_work(struct work_struct *work) + struct delayed_work *dwork = to_delayed_work(work); + struct aspeed_video *video = container_of(dwork, struct aspeed_video, + res_work); +- u32 input_status = video->v4l2_input_status; ++ unsigned long flags; ++ u32 input_status; + ++ spin_lock_irqsave(&video->lock, flags); ++ input_status = video->v4l2_input_status; + aspeed_video_on(video); + + /* Exit early in case no clients remain */ +- if (test_bit(VIDEO_STOPPED, &video->flags)) ++ if (test_bit(VIDEO_STOPPED, &video->flags)) { ++ spin_unlock_irqrestore(&video->lock, flags); + goto done; ++ } + + aspeed_video_init_regs(video); +- ++ spin_unlock_irqrestore(&video->lock, flags); + aspeed_video_get_resolution(video); + + if (video->detected_timings.width != video->active_timings.width || +-- +2.7.4 + -- cgit v1.2.3