summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/spi/spi-dw-mmio.c22
-rw-r--r--drivers/spi/spi-dw.c197
-rw-r--r--drivers/spi/spi-dw.h24
-rw-r--r--drivers/spi/spi-fsl-dspi.c2
-rw-r--r--drivers/spi/spi-fsl-espi.c40
-rw-r--r--drivers/spi/spi-fsl-lib.c6
-rw-r--r--drivers/spi/spi-fsl-lib.h1
-rw-r--r--drivers/spi/spi-fsl-spi.c2
-rw-r--r--drivers/spi/spi-gpio.c2
-rw-r--r--drivers/spi/spi-qup.c2
-rw-r--r--drivers/spi/spi-tegra114.c2
-rw-r--r--drivers/spi/spi-tegra20-sflash.c2
-rw-r--r--drivers/spi/spi-tegra20-slink.c2
13 files changed, 95 insertions, 209 deletions
diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c
index 1492f5ee9aaa..a5cba14ac3d2 100644
--- a/drivers/spi/spi-dw-mmio.c
+++ b/drivers/spi/spi-dw-mmio.c
@@ -16,6 +16,7 @@
#include <linux/spi/spi.h>
#include <linux/scatterlist.h>
#include <linux/module.h>
+#include <linux/of_gpio.h>
#include "spi-dw.h"
@@ -70,6 +71,27 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
dws->num_cs = 4;
dws->max_freq = clk_get_rate(dwsmmio->clk);
+ if (pdev->dev.of_node) {
+ int i;
+
+ for (i = 0; i < dws->num_cs; i++) {
+ int cs_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "cs-gpios", i);
+
+ if (cs_gpio == -EPROBE_DEFER) {
+ ret = cs_gpio;
+ goto out;
+ }
+
+ if (gpio_is_valid(cs_gpio)) {
+ ret = devm_gpio_request(&pdev->dev, cs_gpio,
+ dev_name(&pdev->dev));
+ if (ret)
+ goto out;
+ }
+ }
+ }
+
ret = dw_spi_add_host(&pdev->dev, dws);
if (ret)
goto out;
diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index 712ac5629cd4..29f33143b795 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -24,6 +24,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
+#include <linux/gpio.h>
#include "spi-dw.h"
@@ -36,12 +37,6 @@
#define DONE_STATE ((void *)2)
#define ERROR_STATE ((void *)-1)
-#define QUEUE_RUNNING 0
-#define QUEUE_STOPPED 1
-
-#define MRST_SPI_DEASSERT 0
-#define MRST_SPI_ASSERT 1
-
/* Slave spi_dev related */
struct chip_data {
u16 cr0;
@@ -263,28 +258,22 @@ static int map_dma_buffers(struct dw_spi *dws)
static void giveback(struct dw_spi *dws)
{
struct spi_transfer *last_transfer;
- unsigned long flags;
struct spi_message *msg;
- spin_lock_irqsave(&dws->lock, flags);
msg = dws->cur_msg;
dws->cur_msg = NULL;
dws->cur_transfer = NULL;
dws->prev_chip = dws->cur_chip;
dws->cur_chip = NULL;
dws->dma_mapped = 0;
- queue_work(dws->workqueue, &dws->pump_messages);
- spin_unlock_irqrestore(&dws->lock, flags);
last_transfer = list_last_entry(&msg->transfers, struct spi_transfer,
transfer_list);
- if (!last_transfer->cs_change && dws->cs_control)
- dws->cs_control(MRST_SPI_DEASSERT);
+ if (!last_transfer->cs_change)
+ spi_chip_sel(dws, dws->cur_msg->spi, 0);
- msg->state = NULL;
- if (msg->complete)
- msg->complete(msg->context);
+ spi_finalize_current_message(dws->master);
}
static void int_error_stop(struct dw_spi *dws, const char *msg)
@@ -502,7 +491,7 @@ static void pump_transfers(unsigned long data)
dw_writew(dws, DW_SPI_CTRL0, cr0);
spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);
- spi_chip_sel(dws, spi->chip_select);
+ spi_chip_sel(dws, spi, 1);
/* Set the interrupt mask, for poll mode just disable all int */
spi_mask_intr(dws, 0xff);
@@ -529,30 +518,12 @@ early_exit:
return;
}
-static void pump_messages(struct work_struct *work)
+static int dw_spi_transfer_one_message(struct spi_master *master,
+ struct spi_message *msg)
{
- struct dw_spi *dws =
- container_of(work, struct dw_spi, pump_messages);
- unsigned long flags;
-
- /* Lock queue and check for queue work */
- spin_lock_irqsave(&dws->lock, flags);
- if (list_empty(&dws->queue) || dws->run == QUEUE_STOPPED) {
- dws->busy = 0;
- spin_unlock_irqrestore(&dws->lock, flags);
- return;
- }
-
- /* Make sure we are not already running a message */
- if (dws->cur_msg) {
- spin_unlock_irqrestore(&dws->lock, flags);
- return;
- }
-
- /* Extract head of queue */
- dws->cur_msg = list_entry(dws->queue.next, struct spi_message, queue);
- list_del_init(&dws->cur_msg->queue);
+ struct dw_spi *dws = spi_master_get_devdata(master);
+ dws->cur_msg = msg;
/* Initial message state*/
dws->cur_msg->state = START_STATE;
dws->cur_transfer = list_entry(dws->cur_msg->transfers.next,
@@ -560,46 +531,9 @@ static void pump_messages(struct work_struct *work)
transfer_list);
dws->cur_chip = spi_get_ctldata(dws->cur_msg->spi);
- /* Mark as busy and launch transfers */
+ /* Launch transfers */
tasklet_schedule(&dws->pump_transfers);
- dws->busy = 1;
- spin_unlock_irqrestore(&dws->lock, flags);
-}
-
-/* spi_device use this to queue in their spi_msg */
-static int dw_spi_transfer(struct spi_device *spi, struct spi_message *msg)
-{
- struct dw_spi *dws = spi_master_get_devdata(spi->master);
- unsigned long flags;
-
- spin_lock_irqsave(&dws->lock, flags);
-
- if (dws->run == QUEUE_STOPPED) {
- spin_unlock_irqrestore(&dws->lock, flags);
- return -ESHUTDOWN;
- }
-
- msg->actual_length = 0;
- msg->status = -EINPROGRESS;
- msg->state = START_STATE;
-
- list_add_tail(&msg->queue, &dws->queue);
-
- if (dws->run == QUEUE_RUNNING && !dws->busy) {
-
- if (dws->cur_transfer || dws->cur_msg)
- queue_work(dws->workqueue,
- &dws->pump_messages);
- else {
- /* If no other data transaction in air, just go */
- spin_unlock_irqrestore(&dws->lock, flags);
- pump_messages(&dws->pump_messages);
- return 0;
- }
- }
-
- spin_unlock_irqrestore(&dws->lock, flags);
return 0;
}
@@ -608,6 +542,7 @@ static int dw_spi_setup(struct spi_device *spi)
{
struct dw_spi_chip *chip_info = NULL;
struct chip_data *chip;
+ int ret;
/* Only alloc on first setup */
chip = spi_get_ctldata(spi);
@@ -661,81 +596,13 @@ static int dw_spi_setup(struct spi_device *spi)
| (spi->mode << SPI_MODE_OFFSET)
| (chip->tmode << SPI_TMOD_OFFSET);
- return 0;
-}
-
-static int init_queue(struct dw_spi *dws)
-{
- INIT_LIST_HEAD(&dws->queue);
- spin_lock_init(&dws->lock);
-
- dws->run = QUEUE_STOPPED;
- dws->busy = 0;
-
- tasklet_init(&dws->pump_transfers,
- pump_transfers, (unsigned long)dws);
-
- INIT_WORK(&dws->pump_messages, pump_messages);
- dws->workqueue = create_singlethread_workqueue(
- dev_name(dws->master->dev.parent));
- if (dws->workqueue == NULL)
- return -EBUSY;
-
- return 0;
-}
-
-static int start_queue(struct dw_spi *dws)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dws->lock, flags);
-
- if (dws->run == QUEUE_RUNNING || dws->busy) {
- spin_unlock_irqrestore(&dws->lock, flags);
- return -EBUSY;
+ if (gpio_is_valid(spi->cs_gpio)) {
+ ret = gpio_direction_output(spi->cs_gpio,
+ !(spi->mode & SPI_CS_HIGH));
+ if (ret)
+ return ret;
}
- dws->run = QUEUE_RUNNING;
- dws->cur_msg = NULL;
- dws->cur_transfer = NULL;
- dws->cur_chip = NULL;
- dws->prev_chip = NULL;
- spin_unlock_irqrestore(&dws->lock, flags);
-
- queue_work(dws->workqueue, &dws->pump_messages);
-
- return 0;
-}
-
-static int stop_queue(struct dw_spi *dws)
-{
- unsigned long flags;
- unsigned limit = 50;
- int status = 0;
-
- spin_lock_irqsave(&dws->lock, flags);
- dws->run = QUEUE_STOPPED;
- while ((!list_empty(&dws->queue) || dws->busy) && limit--) {
- spin_unlock_irqrestore(&dws->lock, flags);
- msleep(10);
- spin_lock_irqsave(&dws->lock, flags);
- }
-
- if (!list_empty(&dws->queue) || dws->busy)
- status = -EBUSY;
- spin_unlock_irqrestore(&dws->lock, flags);
-
- return status;
-}
-
-static int destroy_queue(struct dw_spi *dws)
-{
- int status;
-
- status = stop_queue(dws);
- if (status != 0)
- return status;
- destroy_workqueue(dws->workqueue);
return 0;
}
@@ -794,7 +661,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
master->bus_num = dws->bus_num;
master->num_chipselect = dws->num_cs;
master->setup = dw_spi_setup;
- master->transfer = dw_spi_transfer;
+ master->transfer_one_message = dw_spi_transfer_one_message;
master->max_speed_hz = dws->max_freq;
/* Basic HW init */
@@ -808,33 +675,21 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
}
}
- /* Initial and start queue */
- ret = init_queue(dws);
- if (ret) {
- dev_err(&master->dev, "problem initializing queue\n");
- goto err_diable_hw;
- }
- ret = start_queue(dws);
- if (ret) {
- dev_err(&master->dev, "problem starting queue\n");
- goto err_diable_hw;
- }
+ tasklet_init(&dws->pump_transfers, pump_transfers, (unsigned long)dws);
spi_master_set_devdata(master, dws);
ret = devm_spi_register_master(dev, master);
if (ret) {
dev_err(&master->dev, "problem registering spi master\n");
- goto err_queue_alloc;
+ goto err_dma_exit;
}
mrst_spi_debugfs_init(dws);
return 0;
-err_queue_alloc:
- destroy_queue(dws);
+err_dma_exit:
if (dws->dma_ops && dws->dma_ops->dma_exit)
dws->dma_ops->dma_exit(dws);
-err_diable_hw:
spi_enable_chip(dws, 0);
err_free_master:
spi_master_put(master);
@@ -844,18 +699,10 @@ EXPORT_SYMBOL_GPL(dw_spi_add_host);
void dw_spi_remove_host(struct dw_spi *dws)
{
- int status = 0;
-
if (!dws)
return;
mrst_spi_debugfs_remove(dws);
- /* Remove the queue */
- status = destroy_queue(dws);
- if (status != 0)
- dev_err(&dws->master->dev,
- "dw_spi_remove: workqueue will not complete, message memory not freed\n");
-
if (dws->dma_ops && dws->dma_ops->dma_exit)
dws->dma_ops->dma_exit(dws);
spi_enable_chip(dws, 0);
@@ -868,7 +715,7 @@ int dw_spi_suspend_host(struct dw_spi *dws)
{
int ret = 0;
- ret = stop_queue(dws);
+ ret = spi_master_suspend(dws->master);
if (ret)
return ret;
spi_enable_chip(dws, 0);
@@ -882,7 +729,7 @@ int dw_spi_resume_host(struct dw_spi *dws)
int ret;
spi_hw_init(dws);
- ret = start_queue(dws);
+ ret = spi_master_resume(dws->master);
if (ret)
dev_err(&dws->master->dev, "fail to start queue (%d)\n", ret);
return ret;
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 587643dae11e..6d2acad34f64 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -3,6 +3,7 @@
#include <linux/io.h>
#include <linux/scatterlist.h>
+#include <linux/gpio.h>
/* Register offsets */
#define DW_SPI_CTRL0 0x00
@@ -104,14 +105,6 @@ struct dw_spi {
u16 bus_num;
u16 num_cs; /* supported slave numbers */
- /* Driver message queue */
- struct workqueue_struct *workqueue;
- struct work_struct pump_messages;
- spinlock_t lock;
- struct list_head queue;
- int busy;
- int run;
-
/* Message Transfer pump */
struct tasklet_struct pump_transfers;
@@ -186,15 +179,20 @@ static inline void spi_set_clk(struct dw_spi *dws, u16 div)
dw_writel(dws, DW_SPI_BAUDR, div);
}
-static inline void spi_chip_sel(struct dw_spi *dws, u16 cs)
+static inline void spi_chip_sel(struct dw_spi *dws, struct spi_device *spi,
+ int active)
{
- if (cs > dws->num_cs)
- return;
+ u16 cs = spi->chip_select;
+ int gpio_val = active ? (spi->mode & SPI_CS_HIGH) :
+ !(spi->mode & SPI_CS_HIGH);
if (dws->cs_control)
- dws->cs_control(1);
+ dws->cs_control(active);
+ if (gpio_is_valid(spi->cs_gpio))
+ gpio_set_value(spi->cs_gpio, gpio_val);
- dw_writel(dws, DW_SPI_SER, 1 << cs);
+ if (active)
+ dw_writel(dws, DW_SPI_SER, 1 << cs);
}
/* Disable IRQ bits */
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index d565eeee3bd8..5021ddf03f60 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -406,7 +406,7 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static struct of_device_id fsl_dspi_dt_ids[] = {
+static const struct of_device_id fsl_dspi_dt_ids[] = {
{ .compatible = "fsl,vf610-dspi", .data = NULL, },
{ /* sentinel */ }
};
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index e767f5831b9c..8ebd724e4c59 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -348,7 +348,7 @@ static void fsl_espi_cmd_trans(struct spi_message *m,
}
espi_trans->tx_buf = local_buf;
- espi_trans->rx_buf = local_buf + espi_trans->n_tx;
+ espi_trans->rx_buf = local_buf;
fsl_espi_do_trans(m, espi_trans);
espi_trans->actual_length = espi_trans->len;
@@ -397,7 +397,7 @@ static void fsl_espi_rw_trans(struct spi_message *m,
espi_trans->n_rx = trans_len;
espi_trans->len = trans_len + n_tx;
espi_trans->tx_buf = local_buf;
- espi_trans->rx_buf = local_buf + n_tx;
+ espi_trans->rx_buf = local_buf;
fsl_espi_do_trans(m, espi_trans);
memcpy(rx_buf + pos, espi_trans->rx_buf + n_tx, trans_len);
@@ -458,7 +458,7 @@ static int fsl_espi_setup(struct spi_device *spi)
return -EINVAL;
if (!cs) {
- cs = kzalloc(sizeof *cs, GFP_KERNEL);
+ cs = devm_kzalloc(&spi->dev, sizeof(*cs), GFP_KERNEL);
if (!cs)
return -ENOMEM;
spi->controller_state = cs;
@@ -586,8 +586,10 @@ static struct spi_master * fsl_espi_probe(struct device *dev,
struct spi_master *master;
struct mpc8xxx_spi *mpc8xxx_spi;
struct fsl_espi_reg *reg_base;
- u32 regval;
- int i, ret = 0;
+ struct device_node *nc;
+ const __be32 *prop;
+ u32 regval, csmode;
+ int i, len, ret = 0;
master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
if (!master) {
@@ -634,8 +636,32 @@ static struct spi_master * fsl_espi_probe(struct device *dev,
mpc8xxx_spi_write_reg(&reg_base->event, 0xffffffff);
/* Init eSPI CS mode register */
- for (i = 0; i < pdata->max_chipselect; i++)
- mpc8xxx_spi_write_reg(&reg_base->csmode[i], CSMODE_INIT_VAL);
+ for_each_available_child_of_node(master->dev.of_node, nc) {
+ /* get chip select */
+ prop = of_get_property(nc, "reg", &len);
+ if (!prop || len < sizeof(*prop))
+ continue;
+ i = be32_to_cpup(prop);
+ if (i < 0 || i >= pdata->max_chipselect)
+ continue;
+
+ csmode = CSMODE_INIT_VAL;
+ /* check if CSBEF is set in device tree */
+ prop = of_get_property(nc, "fsl,csbef", &len);
+ if (prop && len >= sizeof(*prop)) {
+ csmode &= ~(CSMODE_BEF(0xf));
+ csmode |= CSMODE_BEF(be32_to_cpup(prop));
+ }
+ /* check if CSAFT is set in device tree */
+ prop = of_get_property(nc, "fsl,csaft", &len);
+ if (prop && len >= sizeof(*prop)) {
+ csmode &= ~(CSMODE_AFT(0xf));
+ csmode |= CSMODE_AFT(be32_to_cpup(prop));
+ }
+ mpc8xxx_spi_write_reg(&reg_base->csmode[i], csmode);
+
+ dev_info(dev, "cs=%d, init_csmode=0x%x\n", i, csmode);
+ }
/* Enable SPI interface */
regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c
index e5d45fca3551..95212ea96c8d 100644
--- a/drivers/spi/spi-fsl-lib.c
+++ b/drivers/spi/spi-fsl-lib.c
@@ -99,11 +99,6 @@ int mpc8xxx_spi_transfer(struct spi_device *spi,
return 0;
}
-void mpc8xxx_spi_cleanup(struct spi_device *spi)
-{
- kfree(spi->controller_state);
-}
-
const char *mpc8xxx_spi_strmode(unsigned int flags)
{
if (flags & SPI_QE_CPU_MODE) {
@@ -134,7 +129,6 @@ int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
| SPI_LSB_FIRST | SPI_LOOP;
master->transfer = mpc8xxx_spi_transfer;
- master->cleanup = mpc8xxx_spi_cleanup;
master->dev.of_node = dev->of_node;
mpc8xxx_spi = spi_master_get_devdata(master);
diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h
index 52db6936778e..2fcbfd01d109 100644
--- a/drivers/spi/spi-fsl-lib.h
+++ b/drivers/spi/spi-fsl-lib.h
@@ -124,7 +124,6 @@ extern struct mpc8xxx_spi_probe_info *to_of_pinfo(
extern int mpc8xxx_spi_bufs(struct mpc8xxx_spi *mspi,
struct spi_transfer *t, unsigned int len);
extern int mpc8xxx_spi_transfer(struct spi_device *spi, struct spi_message *m);
-extern void mpc8xxx_spi_cleanup(struct spi_device *spi);
extern const char *mpc8xxx_spi_strmode(unsigned int flags);
extern int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
unsigned int irq);
diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c
index b3e7775034db..98ccd231bf00 100644
--- a/drivers/spi/spi-fsl-spi.c
+++ b/drivers/spi/spi-fsl-spi.c
@@ -431,7 +431,7 @@ static int fsl_spi_setup(struct spi_device *spi)
return -EINVAL;
if (!cs) {
- cs = kzalloc(sizeof *cs, GFP_KERNEL);
+ cs = devm_kzalloc(&spi->dev, sizeof(*cs), GFP_KERNEL);
if (!cs)
return -ENOMEM;
spi->controller_state = cs;
diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c
index 09823076df88..9f595535cf27 100644
--- a/drivers/spi/spi-gpio.c
+++ b/drivers/spi/spi-gpio.c
@@ -340,7 +340,7 @@ done:
}
#ifdef CONFIG_OF
-static struct of_device_id spi_gpio_dt_ids[] = {
+static const struct of_device_id spi_gpio_dt_ids[] = {
{ .compatible = "spi-gpio" },
{}
};
diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index 943f973a59aa..fc1de86d3c8a 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -749,7 +749,7 @@ static int spi_qup_remove(struct platform_device *pdev)
return 0;
}
-static struct of_device_id spi_qup_dt_match[] = {
+static const struct of_device_id spi_qup_dt_match[] = {
{ .compatible = "qcom,spi-qup-v2.1.1", },
{ .compatible = "qcom,spi-qup-v2.2.1", },
{ }
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index 400649595505..e4a85ada861d 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -1012,7 +1012,7 @@ static irqreturn_t tegra_spi_isr(int irq, void *context_data)
return IRQ_WAKE_THREAD;
}
-static struct of_device_id tegra_spi_of_match[] = {
+static const struct of_device_id tegra_spi_of_match[] = {
{ .compatible = "nvidia,tegra114-spi", },
{}
};
diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c
index 47869ea636e1..3548ce25c08f 100644
--- a/drivers/spi/spi-tegra20-sflash.c
+++ b/drivers/spi/spi-tegra20-sflash.c
@@ -419,7 +419,7 @@ static irqreturn_t tegra_sflash_isr(int irq, void *context_data)
return handle_cpu_based_xfer(tsd);
}
-static struct of_device_id tegra_sflash_of_match[] = {
+static const struct of_device_id tegra_sflash_of_match[] = {
{ .compatible = "nvidia,tegra20-sflash", },
{}
};
diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c
index e3c1b93e45d1..0b9e32e9f493 100644
--- a/drivers/spi/spi-tegra20-slink.c
+++ b/drivers/spi/spi-tegra20-slink.c
@@ -1001,7 +1001,7 @@ static const struct tegra_slink_chip_data tegra20_spi_cdata = {
.cs_hold_time = false,
};
-static struct of_device_id tegra_slink_of_match[] = {
+static const struct of_device_id tegra_slink_of_match[] = {
{ .compatible = "nvidia,tegra30-slink", .data = &tegra30_spi_cdata, },
{ .compatible = "nvidia,tegra20-slink", .data = &tegra20_spi_cdata, },
{}