diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-01-29 20:11:56 +0100 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-01-29 20:11:56 +0100 |
commit | 0fc7e74663447682c904fe375bb680b004ddaa14 (patch) | |
tree | 95f89c4a207995e57354c4f44bf7d83b5536370c /arch/arm | |
parent | Merge tag 'for-backlight-next-4.16' of git://git.kernel.org/pub/scm/linux/ker... (diff) | |
parent | Merge tag 'nand/for-4.16' of git://git.infradead.org/linux-mtd into mtd/next (diff) | |
download | linux-0fc7e74663447682c904fe375bb680b004ddaa14.tar.xz linux-0fc7e74663447682c904fe375bb680b004ddaa14.zip |
Merge tag 'mtd/for-4.16' of git://git.infradead.org/linux-mtd
Pull MTD updates from Boris Brezillon:
"MTD core changes:
- Rework core functions to avoid duplicating generic checks in
NAND/OneNAND sub-layers
- Update the MAINTAINERS entry to reflect the fact that MTD
maintainers now use a single git tree
MTD driver changes:
- CFI: use macros instead of inline functions to limit stack usage
and make KASAN happy
NAND core changes:
- Fix NAND_CMD_NONE handling in nand_command[_lp]() hooks
- Introduce the ->exec_op() infrastructure
- Rework NAND buffers handling
- Fix ECC requirements for K9F4G08U0D
- Fix nand_do_read_oob() to return the number of bitflips
- Mark K9F1G08U0E as not supporting subpage writes
NAND driver changes:
- MTK: Rework the driver to support new IP versions
- OMAP OneNAND: Full rework to use new APIs (libgpio, dmaengine) and
fix DT support
- Marvell: Add a new driver to replace the pxa3xx one
SPI NOR core changes:
- Add support to new ISSI and Cypress/Spansion memory parts.
- Fix support of Micron memories by checking error bits in the FSR.
- Fix update of block-protection bits by reading back the SR.
- Restore the internal state of the SPI flash memory when removing
the device.
SPI NOR driver changes:
- Maintenance for Freescale, Intel and Metiatek drivers.
- Add support of the direct access mode for the Cadence QSPI
controller"
* tag 'mtd/for-4.16' of git://git.infradead.org/linux-mtd: (93 commits)
mtd: nand: sunxi: Fix ECC strength choice
mtd: nand: gpmi: Fix subpage reads
mtd: nand: Fix build issues due to an anonymous union
mtd: nand: marvell: Fix missing memory allocation modifier
mtd: nand: marvell: remove redundant variable 'oob_len'
mtd: nand: marvell: fix spelling mistake: "suceed"-> "succeed"
mtd: onenand: omap2: Remove redundant dev_err call in omap2_onenand_probe()
mtd: Remove duplicate checks on mtd_oob_ops parameter
mtd: Fallback to ->_read/write_oob() when ->_read/write() is missing
mtd: mtdpart: Make ECC stat handling consistent
mtd: onenand: omap2: print resource using %pR format string
mtd: mtk-nor: modify functions' name more generally
mtd: onenand: samsung: remove incorrect __iomem annotation
MAINTAINERS: Add entry for Marvell NAND controller driver
ARM: OMAP2+: Remove gpmc-onenand
mtd: onenand: omap2: Configure driver from DT
mtd: onenand: omap2: Decouple DMA enabling from INT pin availability
mtd: onenand: omap2: Do not make delay for GPIO OMAP3 specific
mtd: onenand: omap2: Convert to use dmaengine for memcpy
mtd: onenand: omap2: Unify OMAP2 and OMAP3 DMA implementation
...
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/boot/dts/omap2420-n8x0-common.dtsi | 1 | ||||
-rw-r--r-- | arch/arm/boot/dts/omap3-igep.dtsi | 30 | ||||
-rw-r--r-- | arch/arm/boot/dts/omap3-n900.dts | 1 | ||||
-rw-r--r-- | arch/arm/boot/dts/omap3-n950-n9.dtsi | 1 | ||||
-rw-r--r-- | arch/arm/boot/dts/omap3430-sdp.dts | 1 | ||||
-rw-r--r-- | arch/arm/configs/mvebu_v7_defconfig | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap2/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-omap2/gpmc-onenand.c | 409 |
8 files changed, 20 insertions, 428 deletions
diff --git a/arch/arm/boot/dts/omap2420-n8x0-common.dtsi b/arch/arm/boot/dts/omap2420-n8x0-common.dtsi index 1df3ace3af92..63b0b4921e4e 100644 --- a/arch/arm/boot/dts/omap2420-n8x0-common.dtsi +++ b/arch/arm/boot/dts/omap2420-n8x0-common.dtsi @@ -52,6 +52,7 @@ onenand@0,0 { #address-cells = <1>; #size-cells = <1>; + compatible = "ti,omap2-onenand"; reg = <0 0 0x20000>; /* CS0, offset 0, IO size 128K */ gpmc,sync-read; diff --git a/arch/arm/boot/dts/omap3-igep.dtsi b/arch/arm/boot/dts/omap3-igep.dtsi index 4ad7d5565906..f33cc80c9dbc 100644 --- a/arch/arm/boot/dts/omap3-igep.dtsi +++ b/arch/arm/boot/dts/omap3-igep.dtsi @@ -147,32 +147,32 @@ gpmc,sync-read; gpmc,sync-write; gpmc,burst-length = <16>; - gpmc,burst-read; gpmc,burst-wrap; + gpmc,burst-read; gpmc,burst-write; gpmc,device-width = <2>; /* GPMC_DEVWIDTH_16BIT */ gpmc,mux-add-data = <2>; /* GPMC_MUX_AD */ gpmc,cs-on-ns = <0>; - gpmc,cs-rd-off-ns = <87>; - gpmc,cs-wr-off-ns = <87>; + gpmc,cs-rd-off-ns = <96>; + gpmc,cs-wr-off-ns = <96>; gpmc,adv-on-ns = <0>; - gpmc,adv-rd-off-ns = <10>; - gpmc,adv-wr-off-ns = <10>; - gpmc,oe-on-ns = <15>; - gpmc,oe-off-ns = <87>; + gpmc,adv-rd-off-ns = <12>; + gpmc,adv-wr-off-ns = <12>; + gpmc,oe-on-ns = <18>; + gpmc,oe-off-ns = <96>; gpmc,we-on-ns = <0>; - gpmc,we-off-ns = <87>; - gpmc,rd-cycle-ns = <112>; - gpmc,wr-cycle-ns = <112>; - gpmc,access-ns = <81>; - gpmc,page-burst-access-ns = <15>; + gpmc,we-off-ns = <96>; + gpmc,rd-cycle-ns = <114>; + gpmc,wr-cycle-ns = <114>; + gpmc,access-ns = <90>; + gpmc,page-burst-access-ns = <12>; gpmc,bus-turnaround-ns = <0>; gpmc,cycle2cycle-delay-ns = <0>; gpmc,wait-monitoring-ns = <0>; - gpmc,clk-activation-ns = <5>; + gpmc,clk-activation-ns = <6>; gpmc,wr-data-mux-bus-ns = <30>; - gpmc,wr-access-ns = <81>; - gpmc,sync-clk-ps = <15000>; + gpmc,wr-access-ns = <90>; + gpmc,sync-clk-ps = <12000>; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts index 669c51c00c00..e7c7b8e50703 100644 --- a/arch/arm/boot/dts/omap3-n900.dts +++ b/arch/arm/boot/dts/omap3-n900.dts @@ -838,6 +838,7 @@ onenand@0,0 { #address-cells = <1>; #size-cells = <1>; + compatible = "ti,omap2-onenand"; reg = <0 0 0x20000>; /* CS0, offset 0, IO size 128K */ gpmc,sync-read; diff --git a/arch/arm/boot/dts/omap3-n950-n9.dtsi b/arch/arm/boot/dts/omap3-n950-n9.dtsi index 12fbb3da5fce..0d9b85317529 100644 --- a/arch/arm/boot/dts/omap3-n950-n9.dtsi +++ b/arch/arm/boot/dts/omap3-n950-n9.dtsi @@ -367,6 +367,7 @@ onenand@0,0 { #address-cells = <1>; #size-cells = <1>; + compatible = "ti,omap2-onenand"; reg = <0 0 0x20000>; /* CS0, offset 0, IO size 128K */ gpmc,sync-read; diff --git a/arch/arm/boot/dts/omap3430-sdp.dts b/arch/arm/boot/dts/omap3430-sdp.dts index 908951eb5943..d652708f6bef 100644 --- a/arch/arm/boot/dts/omap3430-sdp.dts +++ b/arch/arm/boot/dts/omap3430-sdp.dts @@ -154,6 +154,7 @@ linux,mtd-name= "samsung,kfm2g16q2m-deb8"; #address-cells = <1>; #size-cells = <1>; + compatible = "ti,omap2-onenand"; reg = <2 0 0x20000>; /* CS2, offset 0, IO size 4 */ gpmc,device-width = <2>; diff --git a/arch/arm/configs/mvebu_v7_defconfig b/arch/arm/configs/mvebu_v7_defconfig index 69553704f2dc..4b6e4fd47e5d 100644 --- a/arch/arm/configs/mvebu_v7_defconfig +++ b/arch/arm/configs/mvebu_v7_defconfig @@ -57,7 +57,7 @@ CONFIG_MTD_CFI_STAA=y CONFIG_MTD_PHYSMAP_OF=y CONFIG_MTD_M25P80=y CONFIG_MTD_NAND=y -CONFIG_MTD_NAND_PXA3xx=y +CONFIG_MTD_NAND_MARVELL=y CONFIG_MTD_SPI_NOR=y CONFIG_SRAM=y CONFIG_MTD_UBI=y diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 2f722a805948..c15bbcad5f67 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -232,6 +232,3 @@ obj-y += $(omap-hsmmc-m) $(omap-hsmmc-y) obj-y += omap_phy_internal.o obj-$(CONFIG_MACH_OMAP2_TUSB6010) += usb-tusb6010.o - -onenand-$(CONFIG_MTD_ONENAND_OMAP2) := gpmc-onenand.o -obj-y += $(onenand-m) $(onenand-y) diff --git a/arch/arm/mach-omap2/gpmc-onenand.c b/arch/arm/mach-omap2/gpmc-onenand.c deleted file mode 100644 index 2944af820558..000000000000 --- a/arch/arm/mach-omap2/gpmc-onenand.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - * linux/arch/arm/mach-omap2/gpmc-onenand.c - * - * Copyright (C) 2006 - 2009 Nokia Corporation - * Contacts: Juha Yrjola - * Tony Lindgren - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/string.h> -#include <linux/kernel.h> -#include <linux/platform_device.h> -#include <linux/mtd/onenand_regs.h> -#include <linux/io.h> -#include <linux/omap-gpmc.h> -#include <linux/platform_data/mtd-onenand-omap2.h> -#include <linux/err.h> - -#include <asm/mach/flash.h> - -#include "soc.h" - -#define ONENAND_IO_SIZE SZ_128K - -#define ONENAND_FLAG_SYNCREAD (1 << 0) -#define ONENAND_FLAG_SYNCWRITE (1 << 1) -#define ONENAND_FLAG_HF (1 << 2) -#define ONENAND_FLAG_VHF (1 << 3) - -static unsigned onenand_flags; -static unsigned latency; - -static struct omap_onenand_platform_data *gpmc_onenand_data; - -static struct resource gpmc_onenand_resource = { - .flags = IORESOURCE_MEM, -}; - -static struct platform_device gpmc_onenand_device = { - .name = "omap2-onenand", - .id = -1, - .num_resources = 1, - .resource = &gpmc_onenand_resource, -}; - -static struct gpmc_settings onenand_async = { - .device_width = GPMC_DEVWIDTH_16BIT, - .mux_add_data = GPMC_MUX_AD, -}; - -static struct gpmc_settings onenand_sync = { - .burst_read = true, - .burst_wrap = true, - .burst_len = GPMC_BURST_16, - .device_width = GPMC_DEVWIDTH_16BIT, - .mux_add_data = GPMC_MUX_AD, - .wait_pin = 0, -}; - -static void omap2_onenand_calc_async_timings(struct gpmc_timings *t) -{ - struct gpmc_device_timings dev_t; - const int t_cer = 15; - const int t_avdp = 12; - const int t_aavdh = 7; - const int t_ce = 76; - const int t_aa = 76; - const int t_oe = 20; - const int t_cez = 20; /* max of t_cez, t_oez */ - const int t_wpl = 40; - const int t_wph = 30; - - memset(&dev_t, 0, sizeof(dev_t)); - - dev_t.t_avdp_r = max_t(int, t_avdp, t_cer) * 1000; - dev_t.t_avdp_w = dev_t.t_avdp_r; - dev_t.t_aavdh = t_aavdh * 1000; - dev_t.t_aa = t_aa * 1000; - dev_t.t_ce = t_ce * 1000; - dev_t.t_oe = t_oe * 1000; - dev_t.t_cez_r = t_cez * 1000; - dev_t.t_cez_w = dev_t.t_cez_r; - dev_t.t_wpl = t_wpl * 1000; - dev_t.t_wph = t_wph * 1000; - - gpmc_calc_timings(t, &onenand_async, &dev_t); -} - -static void omap2_onenand_set_async_mode(void __iomem *onenand_base) -{ - u32 reg; - - /* Ensure sync read and sync write are disabled */ - reg = readw(onenand_base + ONENAND_REG_SYS_CFG1); - reg &= ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE; - writew(reg, onenand_base + ONENAND_REG_SYS_CFG1); -} - -static void set_onenand_cfg(void __iomem *onenand_base) -{ - u32 reg = ONENAND_SYS_CFG1_RDY | ONENAND_SYS_CFG1_INT; - - reg |= (latency << ONENAND_SYS_CFG1_BRL_SHIFT) | - ONENAND_SYS_CFG1_BL_16; - if (onenand_flags & ONENAND_FLAG_SYNCREAD) - reg |= ONENAND_SYS_CFG1_SYNC_READ; - else - reg &= ~ONENAND_SYS_CFG1_SYNC_READ; - if (onenand_flags & ONENAND_FLAG_SYNCWRITE) - reg |= ONENAND_SYS_CFG1_SYNC_WRITE; - else - reg &= ~ONENAND_SYS_CFG1_SYNC_WRITE; - if (onenand_flags & ONENAND_FLAG_HF) - reg |= ONENAND_SYS_CFG1_HF; - else - reg &= ~ONENAND_SYS_CFG1_HF; - if (onenand_flags & ONENAND_FLAG_VHF) - reg |= ONENAND_SYS_CFG1_VHF; - else - reg &= ~ONENAND_SYS_CFG1_VHF; - - writew(reg, onenand_base + ONENAND_REG_SYS_CFG1); -} - -static int omap2_onenand_get_freq(struct omap_onenand_platform_data *cfg, - void __iomem *onenand_base) -{ - u16 ver = readw(onenand_base + ONENAND_REG_VERSION_ID); - int freq; - - switch ((ver >> 4) & 0xf) { - case 0: - freq = 40; - break; - case 1: - freq = 54; - break; - case 2: - freq = 66; - break; - case 3: - freq = 83; - break; - case 4: - freq = 104; - break; - default: - pr_err("onenand rate not detected, bad GPMC async timings?\n"); - freq = 0; - } - - return freq; -} - -static void omap2_onenand_calc_sync_timings(struct gpmc_timings *t, - unsigned int flags, - int freq) -{ - struct gpmc_device_timings dev_t; - const int t_cer = 15; - const int t_avdp = 12; - const int t_cez = 20; /* max of t_cez, t_oez */ - const int t_wpl = 40; - const int t_wph = 30; - int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo; - int div, gpmc_clk_ns; - - if (flags & ONENAND_SYNC_READ) - onenand_flags = ONENAND_FLAG_SYNCREAD; - else if (flags & ONENAND_SYNC_READWRITE) - onenand_flags = ONENAND_FLAG_SYNCREAD | ONENAND_FLAG_SYNCWRITE; - - switch (freq) { - case 104: - min_gpmc_clk_period = 9600; /* 104 MHz */ - t_ces = 3; - t_avds = 4; - t_avdh = 2; - t_ach = 3; - t_aavdh = 6; - t_rdyo = 6; - break; - case 83: - min_gpmc_clk_period = 12000; /* 83 MHz */ - t_ces = 5; - t_avds = 4; - t_avdh = 2; - t_ach = 6; - t_aavdh = 6; - t_rdyo = 9; - break; - case 66: - min_gpmc_clk_period = 15000; /* 66 MHz */ - t_ces = 6; - t_avds = 5; - t_avdh = 2; - t_ach = 6; - t_aavdh = 6; - t_rdyo = 11; - break; - default: - min_gpmc_clk_period = 18500; /* 54 MHz */ - t_ces = 7; - t_avds = 7; - t_avdh = 7; - t_ach = 9; - t_aavdh = 7; - t_rdyo = 15; - onenand_flags &= ~ONENAND_FLAG_SYNCWRITE; - break; - } - - div = gpmc_calc_divider(min_gpmc_clk_period); - gpmc_clk_ns = gpmc_ticks_to_ns(div); - if (gpmc_clk_ns < 15) /* >66MHz */ - onenand_flags |= ONENAND_FLAG_HF; - else - onenand_flags &= ~ONENAND_FLAG_HF; - if (gpmc_clk_ns < 12) /* >83MHz */ - onenand_flags |= ONENAND_FLAG_VHF; - else - onenand_flags &= ~ONENAND_FLAG_VHF; - if (onenand_flags & ONENAND_FLAG_VHF) - latency = 8; - else if (onenand_flags & ONENAND_FLAG_HF) - latency = 6; - else if (gpmc_clk_ns >= 25) /* 40 MHz*/ - latency = 3; - else - latency = 4; - - /* Set synchronous read timings */ - memset(&dev_t, 0, sizeof(dev_t)); - - if (onenand_flags & ONENAND_FLAG_SYNCREAD) - onenand_sync.sync_read = true; - if (onenand_flags & ONENAND_FLAG_SYNCWRITE) { - onenand_sync.sync_write = true; - onenand_sync.burst_write = true; - } else { - dev_t.t_avdp_w = max(t_avdp, t_cer) * 1000; - dev_t.t_wpl = t_wpl * 1000; - dev_t.t_wph = t_wph * 1000; - dev_t.t_aavdh = t_aavdh * 1000; - } - dev_t.ce_xdelay = true; - dev_t.avd_xdelay = true; - dev_t.oe_xdelay = true; - dev_t.we_xdelay = true; - dev_t.clk = min_gpmc_clk_period; - dev_t.t_bacc = dev_t.clk; - dev_t.t_ces = t_ces * 1000; - dev_t.t_avds = t_avds * 1000; - dev_t.t_avdh = t_avdh * 1000; - dev_t.t_ach = t_ach * 1000; - dev_t.cyc_iaa = (latency + 1); - dev_t.t_cez_r = t_cez * 1000; - dev_t.t_cez_w = dev_t.t_cez_r; - dev_t.cyc_aavdh_oe = 1; - dev_t.t_rdyo = t_rdyo * 1000 + min_gpmc_clk_period; - - gpmc_calc_timings(t, &onenand_sync, &dev_t); -} - -static int omap2_onenand_setup_async(void __iomem *onenand_base) -{ - struct gpmc_timings t; - int ret; - - /* - * Note that we need to keep sync_write set for the call to - * omap2_onenand_set_async_mode() to work to detect the onenand - * supported clock rate for the sync timings. - */ - if (gpmc_onenand_data->of_node) { - gpmc_read_settings_dt(gpmc_onenand_data->of_node, - &onenand_async); - if (onenand_async.sync_read || onenand_async.sync_write) { - if (onenand_async.sync_write) - gpmc_onenand_data->flags |= - ONENAND_SYNC_READWRITE; - else - gpmc_onenand_data->flags |= ONENAND_SYNC_READ; - onenand_async.sync_read = false; - } - } - - onenand_async.sync_write = true; - omap2_onenand_calc_async_timings(&t); - - ret = gpmc_cs_program_settings(gpmc_onenand_data->cs, &onenand_async); - if (ret < 0) - return ret; - - ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t, &onenand_async); - if (ret < 0) - return ret; - - omap2_onenand_set_async_mode(onenand_base); - - return 0; -} - -static int omap2_onenand_setup_sync(void __iomem *onenand_base, int *freq_ptr) -{ - int ret, freq = *freq_ptr; - struct gpmc_timings t; - - if (!freq) { - /* Very first call freq is not known */ - freq = omap2_onenand_get_freq(gpmc_onenand_data, onenand_base); - if (!freq) - return -ENODEV; - set_onenand_cfg(onenand_base); - } - - if (gpmc_onenand_data->of_node) { - gpmc_read_settings_dt(gpmc_onenand_data->of_node, - &onenand_sync); - } else { - /* - * FIXME: Appears to be legacy code from initial ONENAND commit. - * Unclear what boards this is for and if this can be removed. - */ - if (!cpu_is_omap34xx()) - onenand_sync.wait_on_read = true; - } - - omap2_onenand_calc_sync_timings(&t, gpmc_onenand_data->flags, freq); - - ret = gpmc_cs_program_settings(gpmc_onenand_data->cs, &onenand_sync); - if (ret < 0) - return ret; - - ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t, &onenand_sync); - if (ret < 0) - return ret; - - set_onenand_cfg(onenand_base); - - *freq_ptr = freq; - - return 0; -} - -static int gpmc_onenand_setup(void __iomem *onenand_base, int *freq_ptr) -{ - struct device *dev = &gpmc_onenand_device.dev; - unsigned l = ONENAND_SYNC_READ | ONENAND_SYNC_READWRITE; - int ret; - - ret = omap2_onenand_setup_async(onenand_base); - if (ret) { - dev_err(dev, "unable to set to async mode\n"); - return ret; - } - - if (!(gpmc_onenand_data->flags & l)) - return 0; - - ret = omap2_onenand_setup_sync(onenand_base, freq_ptr); - if (ret) - dev_err(dev, "unable to set to sync mode\n"); - return ret; -} - -int gpmc_onenand_init(struct omap_onenand_platform_data *_onenand_data) -{ - int err; - struct device *dev = &gpmc_onenand_device.dev; - - gpmc_onenand_data = _onenand_data; - gpmc_onenand_data->onenand_setup = gpmc_onenand_setup; - gpmc_onenand_device.dev.platform_data = gpmc_onenand_data; - - if (cpu_is_omap24xx() && - (gpmc_onenand_data->flags & ONENAND_SYNC_READWRITE)) { - dev_warn(dev, "OneNAND using only SYNC_READ on 24xx\n"); - gpmc_onenand_data->flags &= ~ONENAND_SYNC_READWRITE; - gpmc_onenand_data->flags |= ONENAND_SYNC_READ; - } - - if (cpu_is_omap34xx()) - gpmc_onenand_data->flags |= ONENAND_IN_OMAP34XX; - else - gpmc_onenand_data->flags &= ~ONENAND_IN_OMAP34XX; - - err = gpmc_cs_request(gpmc_onenand_data->cs, ONENAND_IO_SIZE, - (unsigned long *)&gpmc_onenand_resource.start); - if (err < 0) { - dev_err(dev, "Cannot request GPMC CS %d, error %d\n", - gpmc_onenand_data->cs, err); - return err; - } - - gpmc_onenand_resource.end = gpmc_onenand_resource.start + - ONENAND_IO_SIZE - 1; - - err = platform_device_register(&gpmc_onenand_device); - if (err) { - dev_err(dev, "Unable to register OneNAND device\n"); - gpmc_cs_free(gpmc_onenand_data->cs); - } - - return err; -} |