diff options
-rw-r--r-- | Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml | 31 | ||||
-rw-r--r-- | arch/arm64/boot/dts/freescale/imx8mm-verdin-wifi.dtsi | 1 | ||||
-rw-r--r-- | arch/arm64/boot/dts/freescale/imx8mm-verdin.dtsi | 6 | ||||
-rw-r--r-- | arch/arm64/boot/dts/freescale/imx8mp-verdin-dahlia.dtsi | 1 | ||||
-rw-r--r-- | arch/arm64/boot/dts/freescale/imx8mp-verdin-wifi.dtsi | 2 | ||||
-rw-r--r-- | arch/arm64/boot/dts/freescale/imx8mp-verdin.dtsi | 46 | ||||
-rw-r--r-- | arch/arm64/boot/dts/freescale/imx8mp.dtsi | 6 | ||||
-rw-r--r-- | arch/arm64/configs/toradex_defconfig | 10 | ||||
-rw-r--r-- | arch/arm64/configs/toradex_imx_v8.config | 42 | ||||
-rw-r--r-- | drivers/gpu/drm/panel/panel-simple.c | 2 | ||||
-rw-r--r-- | drivers/usb/dwc3/drd.c | 13 | ||||
-rw-r--r-- | drivers/usb/dwc3/dwc3-imx8mp.c | 77 |
12 files changed, 190 insertions, 47 deletions
diff --git a/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml b/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml index 974032b1fda0..01ab0f922ae8 100644 --- a/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml @@ -15,9 +15,9 @@ properties: const: fsl,imx8mp-dwc3 reg: - maxItems: 1 - description: Address and length of the register set for the wrapper of - dwc3 core on the SOC. + items: + - description: Address and length of the register set for HSIO Block Control + - description: Address and length of the register set for the wrapper of dwc3 core on the SOC. "#address-cells": enum: [ 1, 2 ] @@ -49,6 +49,28 @@ properties: - const: hsio - const: suspend + fsl,permanently-attached: + type: boolean + description: + Indicates if the device atached to a downstream port is + permanently attached. + + fsl,disable-port-power-control: + type: boolean + description: + Indicates whether the host controller implementation includes port + power control. Defines Bit 3 in capability register (HCCPARAMS). + + fsl,over-current-active-low: + type: boolean + description: + Over current signal polarity is active low. + + fsl,power-active-low: + type: boolean + description: + Power pad (PWR) polarity is active low. + # Required child node: patternProperties: @@ -74,7 +96,8 @@ examples: #include <dt-bindings/interrupt-controller/arm-gic.h> usb3_0: usb@32f10100 { compatible = "fsl,imx8mp-dwc3"; - reg = <0x32f10100 0x8>; + reg = <0x32f10100 0x8>, + <0x381f0000 0x20>; clocks = <&clk IMX8MP_CLK_HSIO_ROOT>, <&clk IMX8MP_CLK_USB_ROOT>; clock-names = "hsio", "suspend"; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-verdin-wifi.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-verdin-wifi.dtsi index 017db9eab256..51da18e8d221 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-verdin-wifi.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm-verdin-wifi.dtsi @@ -83,6 +83,7 @@ /* On-module Wi-Fi */ &usdhc3 { bus-width = <4>; + fsl,sdio-async-interrupt-enabled; keep-power-in-suspend; non-removable; pinctrl-names = "default", "state_100mhz", "state_200mhz"; diff --git a/arch/arm64/boot/dts/freescale/imx8mm-verdin.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-verdin.dtsi index 494b5de1a74a..98212e716ea4 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-verdin.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm-verdin.dtsi @@ -153,8 +153,10 @@ #size-cells = <2>; ranges; - /* Use the kernel configuration settings instead */ - /delete-node/ linux,cma; + linux,cma { + size = <0 0x20000000>; + /delete-node/ alloc-ranges; + }; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-verdin-dahlia.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-verdin-dahlia.dtsi index 48ef964eb621..7d4d2c234117 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-verdin-dahlia.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-verdin-dahlia.dtsi @@ -178,6 +178,7 @@ /* Verdin USB_2 */ &usb3_1 { + fsl,permanently-attached; status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-verdin-wifi.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-verdin-wifi.dtsi index 36289c175e6e..3509aeeb437f 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-verdin-wifi.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-verdin-wifi.dtsi @@ -70,8 +70,8 @@ /* On-module Wi-Fi */ &usdhc1 { bus-width = <4>; + fsl,sdio-async-interrupt-enabled; keep-power-in-suspend; - max-frequency = <100000000>; non-removable; pinctrl-names = "default", "state_100mhz", "state_200mhz"; pinctrl-0 = <&pinctrl_usdhc1>, <&pinctrl_wifi_ctrl>; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-verdin.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-verdin.dtsi index e0f9dc1543ce..4a9c02acebb4 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-verdin.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp-verdin.dtsi @@ -187,8 +187,10 @@ #size-cells = <2>; ranges; - /* Use the kernel configuration settings instead */ - /delete-node/ linux,cma; + linux,cma { + size = <0 0x20000000>; + /delete-node/ alloc-ranges; + }; }; }; @@ -908,28 +910,45 @@ }; /* Verdin USB_1 */ -&usb3_phy0 { - vbus-supply = <®_usb1_vbus>; +&usb3_0 { + fsl,disable-port-power-control; + fsl,over-current-active-low; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usb_1_oc_n>; }; &usb_dwc3_0 { + /* dual role only, not full featured OTG */ adp-disable; dr_mode = "otg"; hnp-disable; maximum-speed = "high-speed"; - over-current-active-low; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_usb_1_id>; + role-switch-default-mode = "peripheral"; srp-disable; + usb-role-switch; + + connector { + compatible = "gpio-usb-b-connector", "usb-b-connector"; + id-gpios = <&gpio2 10 GPIO_ACTIVE_HIGH>; + label = "Type-C"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usb_1_id>; + self-powered; + type = "micro"; + vbus-supply = <®_usb1_vbus>; + }; }; /* Verdin USB_2 */ +&usb3_1 { + fsl,disable-port-power-control; +}; + &usb3_phy1 { vbus-supply = <®_usb2_vbus>; }; &usb_dwc3_1 { - disable-over-current; dr_mode = "host"; }; @@ -1163,7 +1182,6 @@ pinctrl_gpio_hog3: gpiohog3grp { fsl,pins = - <MX8MP_IOMUXC_GPIO1_IO13__GPIO1_IO13 0x1c4>, /* SODIMM 157 */ /* CSI_1_MCLK */ <MX8MP_IOMUXC_GPIO1_IO15__GPIO1_IO15 0x1c4>; /* SODIMM 91 */ }; @@ -1338,7 +1356,7 @@ pinctrl_usb1_vbus: usb1vbusgrp { fsl,pins = - <MX8MP_IOMUXC_GPIO1_IO12__USB1_PWR 0x19>; /* SODIMM 155 */ + <MX8MP_IOMUXC_GPIO1_IO12__GPIO1_IO12 0x106>; /* SODIMM 155 */ }; /* USB_1_ID */ @@ -1347,9 +1365,15 @@ <MX8MP_IOMUXC_SD1_RESET_B__GPIO2_IO10 0x1c4>; /* SODIMM 161 */ }; + /* USB_1_OC# */ + pinctrl_usb_1_oc_n: usb1ocngrp { + fsl,pins = + <MX8MP_IOMUXC_GPIO1_IO13__USB1_OC 0x1c4>; /* SODIMM 157 */ + }; + pinctrl_usb2_vbus: usb2vbusgrp { fsl,pins = - <MX8MP_IOMUXC_GPIO1_IO14__USB2_PWR 0x19>; /* SODIMM 185 */ + <MX8MP_IOMUXC_GPIO1_IO14__GPIO1_IO14 0x106>; /* SODIMM 185 */ }; /* On-module Wi-Fi */ diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi index a9727c0a82db..d8bf3cf52693 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi @@ -2140,7 +2140,8 @@ usb3_0: usb@32f10100 { compatible = "fsl,imx8mp-dwc3"; - reg = <0x32f10100 0x8>; + reg = <0x32f10100 0x8>, + <0x381f0000 0x20>; clocks = <&clk IMX8MP_CLK_HSIO_ROOT>, <&clk IMX8MP_CLK_USB_ROOT>; clock-names = "hsio", "suspend"; @@ -2182,7 +2183,8 @@ usb3_1: usb@32f10108 { compatible = "fsl,imx8mp-dwc3"; - reg = <0x32f10108 0x8>; + reg = <0x32f10108 0x8>, + <0x382f0000 0x20>; clocks = <&clk IMX8MP_CLK_HSIO_ROOT>, <&clk IMX8MP_CLK_USB_ROOT>; clock-names = "hsio", "suspend"; diff --git a/arch/arm64/configs/toradex_defconfig b/arch/arm64/configs/toradex_defconfig index 1a09fce6f5f7..d49ffbbb16ae 100644 --- a/arch/arm64/configs/toradex_defconfig +++ b/arch/arm64/configs/toradex_defconfig @@ -14,6 +14,7 @@ CONFIG_IKCONFIG_PROC=y CONFIG_NUMA_BALANCING=y CONFIG_MEMCG=y CONFIG_BLK_CGROUP=y +CONFIG_CFS_BANDWIDTH=y CONFIG_CGROUP_PIDS=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_HUGETLB=y @@ -21,6 +22,7 @@ CONFIG_CPUSETS=y CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_BPF=y CONFIG_USER_NS=y CONFIG_SCHED_AUTOGROUP=y CONFIG_RELAY=y @@ -87,8 +89,6 @@ CONFIG_KSM=y CONFIG_MEMORY_FAILURE=y CONFIG_TRANSPARENT_HUGEPAGE=y CONFIG_CMA=y -CONFIG_CMA_SYSFS=y -CONFIG_CMA_AREAS=7 CONFIG_ZSMALLOC=y CONFIG_NET=y CONFIG_PACKET=y @@ -832,7 +832,6 @@ CONFIG_DMATEST=y CONFIG_DMABUF_HEAPS=y CONFIG_DMABUF_HEAPS_SYSTEM=y CONFIG_DMABUF_HEAPS_CMA=y -CONFIG_DMABUF_HEAPS_DSP=y CONFIG_UIO_PCI_GENERIC=y CONFIG_UIO_IVSHMEM=y CONFIG_VFIO=y @@ -882,7 +881,6 @@ CONFIG_ARM_SMMU=y CONFIG_ARM_SMMU_V3=y CONFIG_REMOTEPROC=y CONFIG_IMX_REMOTEPROC=y -CONFIG_IMX_DSP_REMOTEPROC=m CONFIG_RPMSG_CHAR=m CONFIG_RPMSG_QCOM_GLINK_RPM=y CONFIG_SOUNDWIRE=m @@ -1041,9 +1039,7 @@ CONFIG_INDIRECT_PIO=y CONFIG_CRC_CCITT=m CONFIG_CRC8=y CONFIG_DMA_CMA=y -CONFIG_CMA_SIZE_MBYTES=1376 -CONFIG_CMA_SIZE_PERCENTAGE=25 -CONFIG_CMA_SIZE_SEL_MIN=y +CONFIG_CMA_SIZE_MBYTES=32 CONFIG_PRINTK_TIME=y CONFIG_DYNAMIC_DEBUG=y CONFIG_DEBUG_INFO=y diff --git a/arch/arm64/configs/toradex_imx_v8.config b/arch/arm64/configs/toradex_imx_v8.config index e392ef72ccd4..a30bfc73da4d 100644 --- a/arch/arm64/configs/toradex_imx_v8.config +++ b/arch/arm64/configs/toradex_imx_v8.config @@ -14,6 +14,32 @@ CONFIG_CRYPTO_LZ4=y CONFIG_KERNEL_LZ4=y +# Systemd, https://github.com/systemd/systemd/blob/main/README +CONFIG_DEVTMPFS=y +CONFIG_CGROUPS=y +CONFIG_INOTIFY_USER=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EPOLL=y +CONFIG_UNIX=y +CONFIG_SYSFS=y +CONFIG_PROC_FS=y +CONFIG_FHANDLE=y +CONFIG_SYSFS_DEPRECATED=n +CONFIG_UEVENT_HELPER=n +CONFIG_FW_LOADER_USER_HELPER=n +CONFIG_NET_NS=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_BPF=y +CONFIG_BPF_SYSCALL=y +CONFIG_BPF_JIT=y +CONFIG_CGROUP_BPF=y +CONFIG_RT_GROUP_SCHED=n + # RTC widely used on Toradex eval-boards CONFIG_RTC_DRV_DS1307=y @@ -72,18 +98,6 @@ CONFIG_NTFS_RW=y # Ease debugging since this config is used to produce a reference image only CONFIG_DYNAMIC_DEBUG=y -# Setting up CMA memory to a value which works for most -CONFIG_CMA_AREAS=7 -CONFIG_CMA_SIZE_MBYTES=1376 -CONFIG_CMA_SIZE_PERCENTAGE=25 -CONFIG_CMA_SYSFS=y -# These four configs need to go together and only one can be enabled, as it is -# a choice field -CONFIG_CMA_SIZE_SEL_MIN=y -CONFIG_CMA_SIZE_SEL_MBYTES=n -CONFIG_CMA_SIZE_SEL_PERCENTAGE=n -CONFIG_CMA_SIZE_SEL_MAX=n - # Load SDMA as a module to prevent errors/timeouts on boot CONFIG_IMX_SDMA=m @@ -277,3 +291,7 @@ CONFIG_WLAN_VENDOR_RSI=n CONFIG_WLAN_VENDOR_ST=n CONFIG_WLAN_VENDOR_TI=n CONFIG_WLAN_VENDOR_ZYDAS=n + +# Disable DSP drivers +CONFIG_IMX_DSP_REMOTEPROC=n +CONFIG_DMABUF_HEAPS_DSP=n diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 7623fbd9f543..c161d23179e8 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -575,7 +575,7 @@ static int panel_dpi_probe(struct device *dev, struct panel_desc *desc; unsigned int bus_flags; struct videomode vm; - const char *mapping; + const char *mapping = ""; int ret; np = dev->of_node; diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 6c82493f7df9..cb737c276583 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -9,6 +9,7 @@ #include <linux/extcon.h> #include <linux/of_graph.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/property.h> @@ -568,6 +569,18 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc) if (IS_ERR(dwc->role_sw)) return PTR_ERR(dwc->role_sw); + if (IS_ENABLED(CONFIG_OF)) { + /* populate connector entry */ + int ret = devm_of_platform_populate(dwc->dev); + + if (ret) { + usb_role_switch_unregister(dwc->role_sw); + dwc->role_sw = NULL; + dev_err(dwc->dev, "DWC3 platform devices creation failed: %i\n", ret); + return ret; + } + } + dwc3_set_mode(dwc, mode); return 0; } diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index 503bf4a3fdef..cba3b79f7b40 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -37,9 +37,21 @@ #define USB_WAKEUP_EN_MASK GENMASK(5, 0) +/* USB glue registers */ +#define USB_CTRL0 0x00 +#define USB_CTRL1 0x04 + +#define USB_CTRL0_PORTPWR_EN BIT(12) /* 1 - PPC enabled (default) */ +#define USB_CTRL0_USB3_FIXED BIT(22) /* 1 - USB3 permanent attached */ +#define USB_CTRL0_USB2_FIXED BIT(23) /* 1 - USB2 permanent attached */ + +#define USB_CTRL1_OC_POLARITY BIT(16) /* 0 - HIGH / 1 - LOW */ +#define USB_CTRL1_PWR_POLARITY BIT(17) /* 0 - HIGH / 1 - LOW */ + struct dwc3_imx8mp { struct device *dev; struct platform_device *dwc3; + void __iomem *hsio_blk_base; void __iomem *glue_base; struct clk *hsio_clk; struct clk *suspend_clk; @@ -48,6 +60,42 @@ struct dwc3_imx8mp { bool wakeup_pending; }; +static void imx8mp_configure_glue(struct dwc3_imx8mp *dwc3_imx) +{ + struct device *dev = dwc3_imx->dev; + u32 value; + + if (!dwc3_imx->glue_base) + return; + + value = readl(dwc3_imx->glue_base + USB_CTRL0); + + if (device_property_read_bool(dev, "fsl,permanently-attached")) + value |= (USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED); + else + value &= ~(USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED); + + if (device_property_read_bool(dev, "fsl,disable-port-power-control")) + value &= ~(USB_CTRL0_PORTPWR_EN); + else + value |= USB_CTRL0_PORTPWR_EN; + + writel(value, dwc3_imx->glue_base + USB_CTRL0); + + value = readl(dwc3_imx->glue_base + USB_CTRL1); + if (device_property_read_bool(dev, "fsl,over-current-active-low")) + value |= USB_CTRL1_OC_POLARITY; + else + value &= ~USB_CTRL1_OC_POLARITY; + + if (device_property_read_bool(dev, "fsl,power-active-low")) + value |= USB_CTRL1_PWR_POLARITY; + else + value &= ~USB_CTRL1_PWR_POLARITY; + + writel(value, dwc3_imx->glue_base + USB_CTRL1); +} + static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx) { struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3); @@ -56,7 +104,7 @@ static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx) if (!dwc3) return; - val = readl(dwc3_imx->glue_base + USB_WAKEUP_CTRL); + val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL); if ((dwc3->current_dr_role == DWC3_GCTL_PRTCAP_HOST) && dwc3->xhci) val |= USB_WAKEUP_EN | USB_WAKEUP_SS_CONN | @@ -65,16 +113,16 @@ static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx) val |= USB_WAKEUP_EN | USB_WAKEUP_VBUS_EN | USB_WAKEUP_VBUS_SRC_SESS_VAL; - writel(val, dwc3_imx->glue_base + USB_WAKEUP_CTRL); + writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL); } static void dwc3_imx8mp_wakeup_disable(struct dwc3_imx8mp *dwc3_imx) { u32 val; - val = readl(dwc3_imx->glue_base + USB_WAKEUP_CTRL); + val = readl(dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL); val &= ~(USB_WAKEUP_EN | USB_WAKEUP_EN_MASK); - writel(val, dwc3_imx->glue_base + USB_WAKEUP_CTRL); + writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL); } static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx) @@ -141,6 +189,7 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *dwc3_np, *node = dev->of_node; struct dwc3_imx8mp *dwc3_imx; + struct resource *res; int err, irq; if (!node) { @@ -156,9 +205,18 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) dwc3_imx->dev = dev; - dwc3_imx->glue_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(dwc3_imx->glue_base)) - return PTR_ERR(dwc3_imx->glue_base); + dwc3_imx->hsio_blk_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dwc3_imx->hsio_blk_base)) + return PTR_ERR(dwc3_imx->hsio_blk_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_warn(dev, "Base address for glue layer missing. Continuing without, some features are missing though."); + } else { + dwc3_imx->glue_base = devm_ioremap_resource(dev, res); + if (IS_ERR(dwc3_imx->glue_base)) + return PTR_ERR(dwc3_imx->glue_base); + } request_bus_freq(BUS_FREQ_HIGH); dwc3_imx->hsio_clk = devm_clk_get(dev, "hsio"); @@ -194,6 +252,8 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) } dwc3_imx->irq = irq; + imx8mp_configure_glue(dwc3_imx); + pm_runtime_set_active(dev); pm_runtime_enable(dev); err = pm_runtime_get_sync(dev); @@ -298,6 +358,9 @@ static int __maybe_unused dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, dwc3_imx8mp_wakeup_disable(dwc3_imx); dwc3_imx->pm_suspended = false; + /* Upon power loss any previous configuration is lost, restore it */ + imx8mp_configure_glue(dwc3_imx); + if (dwc3_imx->wakeup_pending) { dwc3_imx->wakeup_pending = false; if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) { |