aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-pl022.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-03-21 13:32:00 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-03-21 13:32:00 -0400
commit5f0e685f316a1de6d3af8b23eaf46651faca32ab (patch)
treeaf1ed231b7fcfc65b146be59a0aee699aa9f6353 /drivers/spi/spi-pl022.c
parentf8974cb71310a05632aada76be6a27576d61e609 (diff)
parent87bf5ab82884c829366914aaa813cc8b07b9fe58 (diff)
Merge tag 'spi-for-linus' of git://git.secretlab.ca/git/linux-2.6
Pull SPI changes for v3.4 from Grant Likely: "Mostly a bunch of new drivers and driver bug fixes; but this also includes a few patches that create a core message queue infrastructure for the spi subsystem instead of making each driver open code it." * tag 'spi-for-linus' of git://git.secretlab.ca/git/linux-2.6: (34 commits) spi/fsl-espi: Make sure pm is within 2..32 spi/fsl-espi: make the clock computation easier to read spi: sh-hspi: modify write/read method spi: sh-hspi: control spi clock more correctly spi: sh-hspi: convert to using core message queue spi: s3c64xx: Fix build spi: s3c64xx: remove unnecessary callback msg->complete spi: remove redundant variable assignment spi: release lock on error path in spi_pump_messages() spi: Compatibility with direction which is used in samsung DMA operation spi-topcliff-pch: add recovery processing in case wait-event timeout spi-topcliff-pch: supports a spi mode setup and bit order setup by IO control spi-topcliff-pch: Fix issue for transmitting over 4KByte spi-topcliff-pch: Modify pci-bus number dynamically to get DMA device info spi/imx: simplify error handling to free gpios spi: Convert to DEFINE_PCI_DEVICE_TABLE spi: add Broadcom BCM63xx SPI controller driver SPI: add CSR SiRFprimaII SPI controller driver spi-topcliff-pch: fix -Wuninitialized warning spi: Mark spi_register_board_info() __devinit ...
Diffstat (limited to 'drivers/spi/spi-pl022.c')
-rw-r--r--drivers/spi/spi-pl022.c286
1 files changed, 57 insertions, 229 deletions
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index f37ad2271ad5..dc8485d1e883 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -29,7 +29,6 @@
29#include <linux/errno.h> 29#include <linux/errno.h>
30#include <linux/interrupt.h> 30#include <linux/interrupt.h>
31#include <linux/spi/spi.h> 31#include <linux/spi/spi.h>
32#include <linux/workqueue.h>
33#include <linux/delay.h> 32#include <linux/delay.h>
34#include <linux/clk.h> 33#include <linux/clk.h>
35#include <linux/err.h> 34#include <linux/err.h>
@@ -330,12 +329,13 @@ struct vendor_data {
330 * @clk: outgoing clock "SPICLK" for the SPI bus 329 * @clk: outgoing clock "SPICLK" for the SPI bus
331 * @master: SPI framework hookup 330 * @master: SPI framework hookup
332 * @master_info: controller-specific data from machine setup 331 * @master_info: controller-specific data from machine setup
333 * @workqueue: a workqueue on which any spi_message request is queued 332 * @kworker: thread struct for message pump
334 * @pump_messages: work struct for scheduling work to the workqueue 333 * @kworker_task: pointer to task for message pump kworker thread
334 * @pump_messages: work struct for scheduling work to the message pump
335 * @queue_lock: spinlock to syncronise access to message queue 335 * @queue_lock: spinlock to syncronise access to message queue
336 * @queue: message queue 336 * @queue: message queue
337 * @busy: workqueue is busy 337 * @busy: message pump is busy
338 * @running: workqueue is running 338 * @running: message pump is running
339 * @pump_transfers: Tasklet used in Interrupt Transfer mode 339 * @pump_transfers: Tasklet used in Interrupt Transfer mode
340 * @cur_msg: Pointer to current spi_message being processed 340 * @cur_msg: Pointer to current spi_message being processed
341 * @cur_transfer: Pointer to current spi_transfer 341 * @cur_transfer: Pointer to current spi_transfer
@@ -365,14 +365,7 @@ struct pl022 {
365 struct clk *clk; 365 struct clk *clk;
366 struct spi_master *master; 366 struct spi_master *master;
367 struct pl022_ssp_controller *master_info; 367 struct pl022_ssp_controller *master_info;
368 /* Driver message queue */ 368 /* Message per-transfer pump */
369 struct workqueue_struct *workqueue;
370 struct work_struct pump_messages;
371 spinlock_t queue_lock;
372 struct list_head queue;
373 bool busy;
374 bool running;
375 /* Message transfer pump */
376 struct tasklet_struct pump_transfers; 369 struct tasklet_struct pump_transfers;
377 struct spi_message *cur_msg; 370 struct spi_message *cur_msg;
378 struct spi_transfer *cur_transfer; 371 struct spi_transfer *cur_transfer;
@@ -394,6 +387,7 @@ struct pl022 {
394 struct sg_table sgt_rx; 387 struct sg_table sgt_rx;
395 struct sg_table sgt_tx; 388 struct sg_table sgt_tx;
396 char *dummypage; 389 char *dummypage;
390 bool dma_running;
397#endif 391#endif
398}; 392};
399 393
@@ -448,8 +442,6 @@ static void null_cs_control(u32 command)
448static void giveback(struct pl022 *pl022) 442static void giveback(struct pl022 *pl022)
449{ 443{
450 struct spi_transfer *last_transfer; 444 struct spi_transfer *last_transfer;
451 unsigned long flags;
452 struct spi_message *msg;
453 pl022->next_msg_cs_active = false; 445 pl022->next_msg_cs_active = false;
454 446
455 last_transfer = list_entry(pl022->cur_msg->transfers.prev, 447 last_transfer = list_entry(pl022->cur_msg->transfers.prev,
@@ -477,15 +469,8 @@ static void giveback(struct pl022 *pl022)
477 * sent the current message could be unloaded, which 469 * sent the current message could be unloaded, which
478 * could invalidate the cs_control() callback... 470 * could invalidate the cs_control() callback...
479 */ 471 */
480
481 /* get a pointer to the next message, if any */ 472 /* get a pointer to the next message, if any */
482 spin_lock_irqsave(&pl022->queue_lock, flags); 473 next_msg = spi_get_next_queued_message(pl022->master);
483 if (list_empty(&pl022->queue))
484 next_msg = NULL;
485 else
486 next_msg = list_entry(pl022->queue.next,
487 struct spi_message, queue);
488 spin_unlock_irqrestore(&pl022->queue_lock, flags);
489 474
490 /* 475 /*
491 * see if the next and current messages point 476 * see if the next and current messages point
@@ -497,19 +482,13 @@ static void giveback(struct pl022 *pl022)
497 pl022->cur_chip->cs_control(SSP_CHIP_DESELECT); 482 pl022->cur_chip->cs_control(SSP_CHIP_DESELECT);
498 else 483 else
499 pl022->next_msg_cs_active = true; 484 pl022->next_msg_cs_active = true;
485
500 } 486 }
501 487
502 spin_lock_irqsave(&pl022->queue_lock, flags);
503 msg = pl022->cur_msg;
504 pl022->cur_msg = NULL; 488 pl022->cur_msg = NULL;
505 pl022->cur_transfer = NULL; 489 pl022->cur_transfer = NULL;
506 pl022->cur_chip = NULL; 490 pl022->cur_chip = NULL;
507 queue_work(pl022->workqueue, &pl022->pump_messages); 491 spi_finalize_current_message(pl022->master);
508 spin_unlock_irqrestore(&pl022->queue_lock, flags);
509
510 msg->state = NULL;
511 if (msg->complete)
512 msg->complete(msg->context);
513} 492}
514 493
515/** 494/**
@@ -1063,6 +1042,7 @@ static int configure_dma(struct pl022 *pl022)
1063 dmaengine_submit(txdesc); 1042 dmaengine_submit(txdesc);
1064 dma_async_issue_pending(rxchan); 1043 dma_async_issue_pending(rxchan);
1065 dma_async_issue_pending(txchan); 1044 dma_async_issue_pending(txchan);
1045 pl022->dma_running = true;
1066 1046
1067 return 0; 1047 return 0;
1068 1048
@@ -1141,11 +1121,12 @@ static void terminate_dma(struct pl022 *pl022)
1141 dmaengine_terminate_all(rxchan); 1121 dmaengine_terminate_all(rxchan);
1142 dmaengine_terminate_all(txchan); 1122 dmaengine_terminate_all(txchan);
1143 unmap_free_dma_scatter(pl022); 1123 unmap_free_dma_scatter(pl022);
1124 pl022->dma_running = false;
1144} 1125}
1145 1126
1146static void pl022_dma_remove(struct pl022 *pl022) 1127static void pl022_dma_remove(struct pl022 *pl022)
1147{ 1128{
1148 if (pl022->busy) 1129 if (pl022->dma_running)
1149 terminate_dma(pl022); 1130 terminate_dma(pl022);
1150 if (pl022->dma_tx_channel) 1131 if (pl022->dma_tx_channel)
1151 dma_release_channel(pl022->dma_tx_channel); 1132 dma_release_channel(pl022->dma_tx_channel);
@@ -1493,73 +1474,20 @@ out:
1493 return; 1474 return;
1494} 1475}
1495 1476
1496/** 1477static int pl022_transfer_one_message(struct spi_master *master,
1497 * pump_messages - Workqueue function which processes spi message queue 1478 struct spi_message *msg)
1498 * @data: pointer to private data of SSP driver
1499 *
1500 * This function checks if there is any spi message in the queue that
1501 * needs processing and delegate control to appropriate function
1502 * do_polling_transfer()/do_interrupt_dma_transfer()
1503 * based on the kind of the transfer
1504 *
1505 */
1506static void pump_messages(struct work_struct *work)
1507{ 1479{
1508 struct pl022 *pl022 = 1480 struct pl022 *pl022 = spi_master_get_devdata(master);
1509 container_of(work, struct pl022, pump_messages);
1510 unsigned long flags;
1511 bool was_busy = false;
1512
1513 /* Lock queue and check for queue work */
1514 spin_lock_irqsave(&pl022->queue_lock, flags);
1515 if (list_empty(&pl022->queue) || !pl022->running) {
1516 if (pl022->busy) {
1517 /* nothing more to do - disable spi/ssp and power off */
1518 writew((readw(SSP_CR1(pl022->virtbase)) &
1519 (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
1520
1521 if (pl022->master_info->autosuspend_delay > 0) {
1522 pm_runtime_mark_last_busy(&pl022->adev->dev);
1523 pm_runtime_put_autosuspend(&pl022->adev->dev);
1524 } else {
1525 pm_runtime_put(&pl022->adev->dev);
1526 }
1527 }
1528 pl022->busy = false;
1529 spin_unlock_irqrestore(&pl022->queue_lock, flags);
1530 return;
1531 }
1532
1533 /* Make sure we are not already running a message */
1534 if (pl022->cur_msg) {
1535 spin_unlock_irqrestore(&pl022->queue_lock, flags);
1536 return;
1537 }
1538 /* Extract head of queue */
1539 pl022->cur_msg =
1540 list_entry(pl022->queue.next, struct spi_message, queue);
1541
1542 list_del_init(&pl022->cur_msg->queue);
1543 if (pl022->busy)
1544 was_busy = true;
1545 else
1546 pl022->busy = true;
1547 spin_unlock_irqrestore(&pl022->queue_lock, flags);
1548 1481
1549 /* Initial message state */ 1482 /* Initial message state */
1550 pl022->cur_msg->state = STATE_START; 1483 pl022->cur_msg = msg;
1551 pl022->cur_transfer = list_entry(pl022->cur_msg->transfers.next, 1484 msg->state = STATE_START;
1552 struct spi_transfer, transfer_list); 1485
1486 pl022->cur_transfer = list_entry(msg->transfers.next,
1487 struct spi_transfer, transfer_list);
1553 1488
1554 /* Setup the SPI using the per chip configuration */ 1489 /* Setup the SPI using the per chip configuration */
1555 pl022->cur_chip = spi_get_ctldata(pl022->cur_msg->spi); 1490 pl022->cur_chip = spi_get_ctldata(msg->spi);
1556 if (!was_busy)
1557 /*
1558 * We enable the core voltage and clocks here, then the clocks
1559 * and core will be disabled when this workqueue is run again
1560 * and there is no more work to be done.
1561 */
1562 pm_runtime_get_sync(&pl022->adev->dev);
1563 1491
1564 restore_state(pl022); 1492 restore_state(pl022);
1565 flush(pl022); 1493 flush(pl022);
@@ -1568,95 +1496,37 @@ static void pump_messages(struct work_struct *work)
1568 do_polling_transfer(pl022); 1496 do_polling_transfer(pl022);
1569 else 1497 else
1570 do_interrupt_dma_transfer(pl022); 1498 do_interrupt_dma_transfer(pl022);
1571}
1572
1573static int __init init_queue(struct pl022 *pl022)
1574{
1575 INIT_LIST_HEAD(&pl022->queue);
1576 spin_lock_init(&pl022->queue_lock);
1577
1578 pl022->running = false;
1579 pl022->busy = false;
1580
1581 tasklet_init(&pl022->pump_transfers, pump_transfers,
1582 (unsigned long)pl022);
1583
1584 INIT_WORK(&pl022->pump_messages, pump_messages);
1585 pl022->workqueue = create_singlethread_workqueue(
1586 dev_name(pl022->master->dev.parent));
1587 if (pl022->workqueue == NULL)
1588 return -EBUSY;
1589 1499
1590 return 0; 1500 return 0;
1591} 1501}
1592 1502
1593static int start_queue(struct pl022 *pl022) 1503static int pl022_prepare_transfer_hardware(struct spi_master *master)
1594{ 1504{
1595 unsigned long flags; 1505 struct pl022 *pl022 = spi_master_get_devdata(master);
1596
1597 spin_lock_irqsave(&pl022->queue_lock, flags);
1598
1599 if (pl022->running || pl022->busy) {
1600 spin_unlock_irqrestore(&pl022->queue_lock, flags);
1601 return -EBUSY;
1602 }
1603
1604 pl022->running = true;
1605 pl022->cur_msg = NULL;
1606 pl022->cur_transfer = NULL;
1607 pl022->cur_chip = NULL;
1608 pl022->next_msg_cs_active = false;
1609 spin_unlock_irqrestore(&pl022->queue_lock, flags);
1610
1611 queue_work(pl022->workqueue, &pl022->pump_messages);
1612 1506
1507 /*
1508 * Just make sure we have all we need to run the transfer by syncing
1509 * with the runtime PM framework.
1510 */
1511 pm_runtime_get_sync(&pl022->adev->dev);
1613 return 0; 1512 return 0;
1614} 1513}
1615 1514
1616static int stop_queue(struct pl022 *pl022) 1515static int pl022_unprepare_transfer_hardware(struct spi_master *master)
1617{ 1516{
1618 unsigned long flags; 1517 struct pl022 *pl022 = spi_master_get_devdata(master);
1619 unsigned limit = 500;
1620 int status = 0;
1621 1518
1622 spin_lock_irqsave(&pl022->queue_lock, flags); 1519 /* nothing more to do - disable spi/ssp and power off */
1520 writew((readw(SSP_CR1(pl022->virtbase)) &
1521 (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
1623 1522
1624 /* This is a bit lame, but is optimized for the common execution path. 1523 if (pl022->master_info->autosuspend_delay > 0) {
1625 * A wait_queue on the pl022->busy could be used, but then the common 1524 pm_runtime_mark_last_busy(&pl022->adev->dev);
1626 * execution path (pump_messages) would be required to call wake_up or 1525 pm_runtime_put_autosuspend(&pl022->adev->dev);
1627 * friends on every SPI message. Do this instead */ 1526 } else {
1628 while ((!list_empty(&pl022->queue) || pl022->busy) && limit--) { 1527 pm_runtime_put(&pl022->adev->dev);
1629 spin_unlock_irqrestore(&pl022->queue_lock, flags);
1630 msleep(10);
1631 spin_lock_irqsave(&pl022->queue_lock, flags);
1632 } 1528 }
1633 1529
1634 if (!list_empty(&pl022->queue) || pl022->busy)
1635 status = -EBUSY;
1636 else
1637 pl022->running = false;
1638
1639 spin_unlock_irqrestore(&pl022->queue_lock, flags);
1640
1641 return status;
1642}
1643
1644static int destroy_queue(struct pl022 *pl022)
1645{
1646 int status;
1647
1648 status = stop_queue(pl022);
1649 /* we are unloading the module or failing to load (only two calls
1650 * to this routine), and neither call can handle a return value.
1651 * However, destroy_workqueue calls flush_workqueue, and that will
1652 * block until all work is done. If the reason that stop_queue
1653 * timed out is that the work will never finish, then it does no
1654 * good to call destroy_workqueue, so return anyway. */
1655 if (status != 0)
1656 return status;
1657
1658 destroy_workqueue(pl022->workqueue);
1659
1660 return 0; 1530 return 0;
1661} 1531}
1662 1532
@@ -1776,38 +1646,6 @@ static int verify_controller_parameters(struct pl022 *pl022,
1776 return 0; 1646 return 0;
1777} 1647}
1778 1648
1779/**
1780 * pl022_transfer - transfer function registered to SPI master framework
1781 * @spi: spi device which is requesting transfer
1782 * @msg: spi message which is to handled is queued to driver queue
1783 *
1784 * This function is registered to the SPI framework for this SPI master
1785 * controller. It will queue the spi_message in the queue of driver if
1786 * the queue is not stopped and return.
1787 */
1788static int pl022_transfer(struct spi_device *spi, struct spi_message *msg)
1789{
1790 struct pl022 *pl022 = spi_master_get_devdata(spi->master);
1791 unsigned long flags;
1792
1793 spin_lock_irqsave(&pl022->queue_lock, flags);
1794
1795 if (!pl022->running) {
1796 spin_unlock_irqrestore(&pl022->queue_lock, flags);
1797 return -ESHUTDOWN;
1798 }
1799 msg->actual_length = 0;
1800 msg->status = -EINPROGRESS;
1801 msg->state = STATE_START;
1802
1803 list_add_tail(&msg->queue, &pl022->queue);
1804 if (pl022->running && !pl022->busy)
1805 queue_work(pl022->workqueue, &pl022->pump_messages);
1806
1807 spin_unlock_irqrestore(&pl022->queue_lock, flags);
1808 return 0;
1809}
1810
1811static inline u32 spi_rate(u32 rate, u16 cpsdvsr, u16 scr) 1649static inline u32 spi_rate(u32 rate, u16 cpsdvsr, u16 scr)
1812{ 1650{
1813 return rate / (cpsdvsr * (1 + scr)); 1651 return rate / (cpsdvsr * (1 + scr));
@@ -2170,7 +2008,10 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
2170 master->num_chipselect = platform_info->num_chipselect; 2008 master->num_chipselect = platform_info->num_chipselect;
2171 master->cleanup = pl022_cleanup; 2009 master->cleanup = pl022_cleanup;
2172 master->setup = pl022_setup; 2010 master->setup = pl022_setup;
2173 master->transfer = pl022_transfer; 2011 master->prepare_transfer_hardware = pl022_prepare_transfer_hardware;
2012 master->transfer_one_message = pl022_transfer_one_message;
2013 master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
2014 master->rt = platform_info->rt;
2174 2015
2175 /* 2016 /*
2176 * Supports mode 0-3, loopback, and active low CS. Transfers are 2017 * Supports mode 0-3, loopback, and active low CS. Transfers are
@@ -2214,6 +2055,10 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
2214 goto err_no_clk_en; 2055 goto err_no_clk_en;
2215 } 2056 }
2216 2057
2058 /* Initialize transfer pump */
2059 tasklet_init(&pl022->pump_transfers, pump_transfers,
2060 (unsigned long)pl022);
2061
2217 /* Disable SSP */ 2062 /* Disable SSP */
2218 writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)), 2063 writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)),
2219 SSP_CR1(pl022->virtbase)); 2064 SSP_CR1(pl022->virtbase));
@@ -2233,17 +2078,6 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
2233 platform_info->enable_dma = 0; 2078 platform_info->enable_dma = 0;
2234 } 2079 }
2235 2080
2236 /* Initialize and start queue */
2237 status = init_queue(pl022);
2238 if (status != 0) {
2239 dev_err(&adev->dev, "probe - problem initializing queue\n");
2240 goto err_init_queue;
2241 }
2242 status = start_queue(pl022);
2243 if (status != 0) {
2244 dev_err(&adev->dev, "probe - problem starting queue\n");
2245 goto err_start_queue;
2246 }
2247 /* Register with the SPI framework */ 2081 /* Register with the SPI framework */
2248 amba_set_drvdata(adev, pl022); 2082 amba_set_drvdata(adev, pl022);
2249 status = spi_register_master(master); 2083 status = spi_register_master(master);
@@ -2269,9 +2103,6 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
2269 return 0; 2103 return 0;
2270 2104
2271 err_spi_register: 2105 err_spi_register:
2272 err_start_queue:
2273 err_init_queue:
2274 destroy_queue(pl022);
2275 if (platform_info->enable_dma) 2106 if (platform_info->enable_dma)
2276 pl022_dma_remove(pl022); 2107 pl022_dma_remove(pl022);
2277 2108
@@ -2307,9 +2138,6 @@ pl022_remove(struct amba_device *adev)
2307 */ 2138 */
2308 pm_runtime_get_noresume(&adev->dev); 2139 pm_runtime_get_noresume(&adev->dev);
2309 2140
2310 /* Remove the queue */
2311 if (destroy_queue(pl022) != 0)
2312 dev_err(&adev->dev, "queue remove failed\n");
2313 load_ssp_default_config(pl022); 2141 load_ssp_default_config(pl022);
2314 if (pl022->master_info->enable_dma) 2142 if (pl022->master_info->enable_dma)
2315 pl022_dma_remove(pl022); 2143 pl022_dma_remove(pl022);
@@ -2331,12 +2159,12 @@ pl022_remove(struct amba_device *adev)
2331static int pl022_suspend(struct device *dev) 2159static int pl022_suspend(struct device *dev)
2332{ 2160{
2333 struct pl022 *pl022 = dev_get_drvdata(dev); 2161 struct pl022 *pl022 = dev_get_drvdata(dev);
2334 int status = 0; 2162 int ret;
2335 2163
2336 status = stop_queue(pl022); 2164 ret = spi_master_suspend(pl022->master);
2337 if (status) { 2165 if (ret) {
2338 dev_warn(dev, "suspend cannot stop queue\n"); 2166 dev_warn(dev, "cannot suspend master\n");
2339 return status; 2167 return ret;
2340 } 2168 }
2341 2169
2342 dev_dbg(dev, "suspended\n"); 2170 dev_dbg(dev, "suspended\n");
@@ -2346,16 +2174,16 @@ static int pl022_suspend(struct device *dev)
2346static int pl022_resume(struct device *dev) 2174static int pl022_resume(struct device *dev)
2347{ 2175{
2348 struct pl022 *pl022 = dev_get_drvdata(dev); 2176 struct pl022 *pl022 = dev_get_drvdata(dev);
2349 int status = 0; 2177 int ret;
2350 2178
2351 /* Start the queue running */ 2179 /* Start the queue running */
2352 status = start_queue(pl022); 2180 ret = spi_master_resume(pl022->master);
2353 if (status) 2181 if (ret)
2354 dev_err(dev, "problem starting queue (%d)\n", status); 2182 dev_err(dev, "problem starting queue (%d)\n", ret);
2355 else 2183 else
2356 dev_dbg(dev, "resumed\n"); 2184 dev_dbg(dev, "resumed\n");
2357 2185
2358 return status; 2186 return ret;
2359} 2187}
2360#endif /* CONFIG_PM */ 2188#endif /* CONFIG_PM */
2361 2189