summaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorAndreas Fenkart <afenkart@gmail.com>2014-05-29 04:28:00 -0400
committerUlf Hansson <ulf.hansson@linaro.org>2014-07-09 05:26:03 -0400
commit2cd3a2a54656f9c480b1c7272fc07635d575841b (patch)
tree488673f7a98aabf2882891400abe230b1c10bf32 /drivers/mmc
parentbe19c40577ee74f00c1242d8fd4447524f38c102 (diff)
mmc: omap_hsmmc: Enable SDIO interrupt
There have been various patches floating around for enabling the SDIO IRQ for hsmmc, but none of them ever got merged. Probably the reason for not merging the SDIO interrupt patches has been the lack of wake-up path for SDIO on some omaps that has also needed remuxing the SDIO DAT1 line to a GPIO making the patches complex. This patch adds the minimal SDIO IRQ support to hsmmc for omaps that do have the wake-up path. For those omaps, the DAT1 line need to have the wake-up enable bit set, and the wake-up interrupt is the same as for the MMC controller. This patch has been tested on am3730 es1.2 with mwifiex connected to MMC3 with mwifiex waking to Ethernet traffic from off-idle mode. Note that for omaps that do not have the SDIO wake-up path, this patch will not work for idle modes and further patches for remuxing DAT1 to GPIO are needed. Based on earlier patches [1][2] by David Vrabel <david.vrabel@csr.com>, Steve Sakoman <steve@sakoman.com> For now, only support SDIO interrupt if we are booted with a separate wake-irq configued via device tree. This is because omaps need the wake-irq for idle states, and some omaps need special quirks. And we don't want to add new legacy mux platform init code callbacks any longer as we are moving to DT based booting anyways. To use it, you need to specify the wake-irq using the interrupts-extended property. [1] http://www.sakoman.com/cgi-bin/gitweb.cgi?p=linux.git;a=commitdiff_plain;h=010810d22f6f49ac03da4ba384969432e0320453 [2] http://comments.gmane.org/gmane.linux.kernel.mmc/20446 Acked-by: Balaji T K <balajitk@ti.com> Signed-off-by: Andreas Fenkart <afenkart@gmail.com> Signed-off-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/omap_hsmmc.c201
1 files changed, 189 insertions, 12 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 6b7b75585926..9446010a5dd9 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -29,6 +29,7 @@
29#include <linux/timer.h> 29#include <linux/timer.h>
30#include <linux/clk.h> 30#include <linux/clk.h>
31#include <linux/of.h> 31#include <linux/of.h>
32#include <linux/of_irq.h>
32#include <linux/of_gpio.h> 33#include <linux/of_gpio.h>
33#include <linux/of_device.h> 34#include <linux/of_device.h>
34#include <linux/omap-dmaengine.h> 35#include <linux/omap-dmaengine.h>
@@ -36,6 +37,7 @@
36#include <linux/mmc/core.h> 37#include <linux/mmc/core.h>
37#include <linux/mmc/mmc.h> 38#include <linux/mmc/mmc.h>
38#include <linux/io.h> 39#include <linux/io.h>
40#include <linux/irq.h>
39#include <linux/gpio.h> 41#include <linux/gpio.h>
40#include <linux/regulator/consumer.h> 42#include <linux/regulator/consumer.h>
41#include <linux/pinctrl/consumer.h> 43#include <linux/pinctrl/consumer.h>
@@ -106,6 +108,7 @@
106#define TC_EN (1 << 1) 108#define TC_EN (1 << 1)
107#define BWR_EN (1 << 4) 109#define BWR_EN (1 << 4)
108#define BRR_EN (1 << 5) 110#define BRR_EN (1 << 5)
111#define CIRQ_EN (1 << 8)
109#define ERR_EN (1 << 15) 112#define ERR_EN (1 << 15)
110#define CTO_EN (1 << 16) 113#define CTO_EN (1 << 16)
111#define CCRC_EN (1 << 17) 114#define CCRC_EN (1 << 17)
@@ -140,7 +143,6 @@
140#define VDD_3V0 3000000 /* 300000 uV */ 143#define VDD_3V0 3000000 /* 300000 uV */
141#define VDD_165_195 (ffs(MMC_VDD_165_195) - 1) 144#define VDD_165_195 (ffs(MMC_VDD_165_195) - 1)
142 145
143#define AUTO_CMD23 (1 << 1) /* Auto CMD23 support */
144/* 146/*
145 * One controller can have multiple slots, like on some omap boards using 147 * One controller can have multiple slots, like on some omap boards using
146 * omap.c controller driver. Luckily this is not currently done on any known 148 * omap.c controller driver. Luckily this is not currently done on any known
@@ -194,6 +196,7 @@ struct omap_hsmmc_host {
194 u32 sysctl; 196 u32 sysctl;
195 u32 capa; 197 u32 capa;
196 int irq; 198 int irq;
199 int wake_irq;
197 int use_dma, dma_ch; 200 int use_dma, dma_ch;
198 struct dma_chan *tx_chan; 201 struct dma_chan *tx_chan;
199 struct dma_chan *rx_chan; 202 struct dma_chan *rx_chan;
@@ -206,6 +209,9 @@ struct omap_hsmmc_host {
206 int req_in_progress; 209 int req_in_progress;
207 unsigned long clk_rate; 210 unsigned long clk_rate;
208 unsigned int flags; 211 unsigned int flags;
212#define AUTO_CMD23 (1 << 0) /* Auto CMD23 support */
213#define HSMMC_SDIO_IRQ_ENABLED (1 << 1) /* SDIO irq enabled */
214#define HSMMC_WAKE_IRQ_ENABLED (1 << 2)
209 struct omap_hsmmc_next next_data; 215 struct omap_hsmmc_next next_data;
210 struct omap_mmc_platform_data *pdata; 216 struct omap_mmc_platform_data *pdata;
211}; 217};
@@ -510,27 +516,40 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
510static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host, 516static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
511 struct mmc_command *cmd) 517 struct mmc_command *cmd)
512{ 518{
513 unsigned int irq_mask; 519 u32 irq_mask = INT_EN_MASK;
520 unsigned long flags;
514 521
515 if (host->use_dma) 522 if (host->use_dma)
516 irq_mask = INT_EN_MASK & ~(BRR_EN | BWR_EN); 523 irq_mask &= ~(BRR_EN | BWR_EN);
517 else
518 irq_mask = INT_EN_MASK;
519 524
520 /* Disable timeout for erases */ 525 /* Disable timeout for erases */
521 if (cmd->opcode == MMC_ERASE) 526 if (cmd->opcode == MMC_ERASE)
522 irq_mask &= ~DTO_EN; 527 irq_mask &= ~DTO_EN;
523 528
529 spin_lock_irqsave(&host->irq_lock, flags);
524 OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); 530 OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
525 OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); 531 OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
532
533 /* latch pending CIRQ, but don't signal MMC core */
534 if (host->flags & HSMMC_SDIO_IRQ_ENABLED)
535 irq_mask |= CIRQ_EN;
526 OMAP_HSMMC_WRITE(host->base, IE, irq_mask); 536 OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
537 spin_unlock_irqrestore(&host->irq_lock, flags);
527} 538}
528 539
529static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host) 540static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
530{ 541{
531 OMAP_HSMMC_WRITE(host->base, ISE, 0); 542 u32 irq_mask = 0;
532 OMAP_HSMMC_WRITE(host->base, IE, 0); 543 unsigned long flags;
544
545 spin_lock_irqsave(&host->irq_lock, flags);
546 /* no transfer running but need to keep cirq if enabled */
547 if (host->flags & HSMMC_SDIO_IRQ_ENABLED)
548 irq_mask |= CIRQ_EN;
549 OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
550 OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
533 OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); 551 OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
552 spin_unlock_irqrestore(&host->irq_lock, flags);
534} 553}
535 554
536/* Calculate divisor for the given clock frequency */ 555/* Calculate divisor for the given clock frequency */
@@ -681,7 +700,9 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
681 && time_before(jiffies, timeout)) 700 && time_before(jiffies, timeout))
682 ; 701 ;
683 702
684 omap_hsmmc_disable_irq(host); 703 OMAP_HSMMC_WRITE(host->base, ISE, 0);
704 OMAP_HSMMC_WRITE(host->base, IE, 0);
705 OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
685 706
686 /* Do not initialize card-specific things if the power is off */ 707 /* Do not initialize card-specific things if the power is off */
687 if (host->power_mode == MMC_POWER_OFF) 708 if (host->power_mode == MMC_POWER_OFF)
@@ -1118,8 +1139,12 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
1118 int status; 1139 int status;
1119 1140
1120 status = OMAP_HSMMC_READ(host->base, STAT); 1141 status = OMAP_HSMMC_READ(host->base, STAT);
1121 while (status & INT_EN_MASK && host->req_in_progress) { 1142 while (status & (INT_EN_MASK | CIRQ_EN)) {
1122 omap_hsmmc_do_irq(host, status); 1143 if (host->req_in_progress)
1144 omap_hsmmc_do_irq(host, status);
1145
1146 if (status & CIRQ_EN)
1147 mmc_signal_sdio_irq(host->mmc);
1123 1148
1124 /* Flush posted write */ 1149 /* Flush posted write */
1125 status = OMAP_HSMMC_READ(host->base, STAT); 1150 status = OMAP_HSMMC_READ(host->base, STAT);
@@ -1128,6 +1153,22 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
1128 return IRQ_HANDLED; 1153 return IRQ_HANDLED;
1129} 1154}
1130 1155
1156static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id)
1157{
1158 struct omap_hsmmc_host *host = dev_id;
1159
1160 /* cirq is level triggered, disable to avoid infinite loop */
1161 spin_lock(&host->irq_lock);
1162 if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
1163 disable_irq_nosync(host->wake_irq);
1164 host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
1165 }
1166 spin_unlock(&host->irq_lock);
1167 pm_request_resume(host->dev); /* no use counter */
1168
1169 return IRQ_HANDLED;
1170}
1171
1131static void set_sd_bus_power(struct omap_hsmmc_host *host) 1172static void set_sd_bus_power(struct omap_hsmmc_host *host)
1132{ 1173{
1133 unsigned long i; 1174 unsigned long i;
@@ -1639,6 +1680,79 @@ static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
1639 mmc_slot(host).init_card(card); 1680 mmc_slot(host).init_card(card);
1640} 1681}
1641 1682
1683static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
1684{
1685 struct omap_hsmmc_host *host = mmc_priv(mmc);
1686 u32 irq_mask;
1687 unsigned long flags;
1688
1689 spin_lock_irqsave(&host->irq_lock, flags);
1690
1691 irq_mask = OMAP_HSMMC_READ(host->base, ISE);
1692 if (enable) {
1693 host->flags |= HSMMC_SDIO_IRQ_ENABLED;
1694 irq_mask |= CIRQ_EN;
1695 } else {
1696 host->flags &= ~HSMMC_SDIO_IRQ_ENABLED;
1697 irq_mask &= ~CIRQ_EN;
1698 }
1699 OMAP_HSMMC_WRITE(host->base, IE, irq_mask);
1700
1701 /*
1702 * if enable, piggy back detection on current request
1703 * but always disable immediately
1704 */
1705 if (!host->req_in_progress || !enable)
1706 OMAP_HSMMC_WRITE(host->base, ISE, irq_mask);
1707
1708 /* flush posted write */
1709 OMAP_HSMMC_READ(host->base, IE);
1710
1711 spin_unlock_irqrestore(&host->irq_lock, flags);
1712}
1713
1714static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
1715{
1716 struct mmc_host *mmc = host->mmc;
1717 int ret;
1718
1719 /*
1720 * For omaps with wake-up path, wakeirq will be irq from pinctrl and
1721 * for other omaps, wakeirq will be from GPIO (dat line remuxed to
1722 * gpio). wakeirq is needed to detect sdio irq in runtime suspend state
1723 * with functional clock disabled.
1724 */
1725 if (!host->dev->of_node || !host->wake_irq)
1726 return -ENODEV;
1727
1728 /* Prevent auto-enabling of IRQ */
1729 irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN);
1730 ret = devm_request_irq(host->dev, host->wake_irq, omap_hsmmc_wake_irq,
1731 IRQF_TRIGGER_LOW | IRQF_ONESHOT,
1732 mmc_hostname(mmc), host);
1733 if (ret) {
1734 dev_err(mmc_dev(host->mmc), "Unable to request wake IRQ\n");
1735 goto err;
1736 }
1737
1738 /*
1739 * Some omaps don't have wake-up path from deeper idle states
1740 * and need to remux SDIO DAT1 to GPIO for wake-up from idle.
1741 */
1742 if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) {
1743 ret = -ENODEV;
1744 devm_free_irq(host->dev, host->wake_irq, host);
1745 goto err;
1746 }
1747
1748 return 0;
1749
1750err:
1751 dev_warn(host->dev, "no SDIO IRQ support, falling back to polling\n");
1752 host->wake_irq = 0;
1753 return ret;
1754}
1755
1642static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host) 1756static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
1643{ 1757{
1644 u32 hctl, capa, value; 1758 u32 hctl, capa, value;
@@ -1691,7 +1805,7 @@ static const struct mmc_host_ops omap_hsmmc_ops = {
1691 .get_cd = omap_hsmmc_get_cd, 1805 .get_cd = omap_hsmmc_get_cd,
1692 .get_ro = omap_hsmmc_get_ro, 1806 .get_ro = omap_hsmmc_get_ro,
1693 .init_card = omap_hsmmc_init_card, 1807 .init_card = omap_hsmmc_init_card,
1694 /* NYET -- enable_sdio_irq */ 1808 .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
1695}; 1809};
1696 1810
1697#ifdef CONFIG_DEBUG_FS 1811#ifdef CONFIG_DEBUG_FS
@@ -1761,6 +1875,10 @@ static const struct omap_mmc_of_data omap3_pre_es3_mmc_of_data = {
1761static const struct omap_mmc_of_data omap4_mmc_of_data = { 1875static const struct omap_mmc_of_data omap4_mmc_of_data = {
1762 .reg_offset = 0x100, 1876 .reg_offset = 0x100,
1763}; 1877};
1878static const struct omap_mmc_of_data am33xx_mmc_of_data = {
1879 .reg_offset = 0x100,
1880 .controller_flags = OMAP_HSMMC_SWAKEUP_MISSING,
1881};
1764 1882
1765static const struct of_device_id omap_mmc_of_match[] = { 1883static const struct of_device_id omap_mmc_of_match[] = {
1766 { 1884 {
@@ -1777,6 +1895,10 @@ static const struct of_device_id omap_mmc_of_match[] = {
1777 .compatible = "ti,omap4-hsmmc", 1895 .compatible = "ti,omap4-hsmmc",
1778 .data = &omap4_mmc_of_data, 1896 .data = &omap4_mmc_of_data,
1779 }, 1897 },
1898 {
1899 .compatible = "ti,am33xx-hsmmc",
1900 .data = &am33xx_mmc_of_data,
1901 },
1780 {}, 1902 {},
1781}; 1903};
1782MODULE_DEVICE_TABLE(of, omap_mmc_of_match); 1904MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
@@ -1913,6 +2035,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
1913 2035
1914 platform_set_drvdata(pdev, host); 2036 platform_set_drvdata(pdev, host);
1915 2037
2038 if (pdev->dev.of_node)
2039 host->wake_irq = irq_of_parse_and_map(pdev->dev.of_node, 1);
2040
1916 mmc->ops = &omap_hsmmc_ops; 2041 mmc->ops = &omap_hsmmc_ops;
1917 2042
1918 mmc->f_min = OMAP_MMC_MIN_CLOCK; 2043 mmc->f_min = OMAP_MMC_MIN_CLOCK;
@@ -2066,6 +2191,18 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
2066 dev_warn(&pdev->dev, 2191 dev_warn(&pdev->dev,
2067 "pins are not configured from the driver\n"); 2192 "pins are not configured from the driver\n");
2068 2193
2194 /*
2195 * For now, only support SDIO interrupt if we have a separate
2196 * wake-up interrupt configured from device tree. This is because
2197 * the wake-up interrupt is needed for idle state and some
2198 * platforms need special quirks. And we don't want to add new
2199 * legacy mux platform init code callbacks any longer as we
2200 * are moving to DT based booting anyways.
2201 */
2202 ret = omap_hsmmc_configure_wake_irq(host);
2203 if (!ret)
2204 mmc->caps |= MMC_CAP_SDIO_IRQ;
2205
2069 omap_hsmmc_protect_card(host); 2206 omap_hsmmc_protect_card(host);
2070 2207
2071 mmc_add_host(mmc); 2208 mmc_add_host(mmc);
@@ -2170,11 +2307,18 @@ static int omap_hsmmc_suspend(struct device *dev)
2170 pm_runtime_get_sync(host->dev); 2307 pm_runtime_get_sync(host->dev);
2171 2308
2172 if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) { 2309 if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
2173 omap_hsmmc_disable_irq(host); 2310 OMAP_HSMMC_WRITE(host->base, ISE, 0);
2311 OMAP_HSMMC_WRITE(host->base, IE, 0);
2312 OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
2174 OMAP_HSMMC_WRITE(host->base, HCTL, 2313 OMAP_HSMMC_WRITE(host->base, HCTL,
2175 OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); 2314 OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
2176 } 2315 }
2177 2316
2317 /* do not wake up due to sdio irq */
2318 if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
2319 !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
2320 disable_irq(host->wake_irq);
2321
2178 if (host->dbclk) 2322 if (host->dbclk)
2179 clk_disable_unprepare(host->dbclk); 2323 clk_disable_unprepare(host->dbclk);
2180 2324
@@ -2200,6 +2344,10 @@ static int omap_hsmmc_resume(struct device *dev)
2200 2344
2201 omap_hsmmc_protect_card(host); 2345 omap_hsmmc_protect_card(host);
2202 2346
2347 if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
2348 !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ))
2349 enable_irq(host->wake_irq);
2350
2203 pm_runtime_mark_last_busy(host->dev); 2351 pm_runtime_mark_last_busy(host->dev);
2204 pm_runtime_put_autosuspend(host->dev); 2352 pm_runtime_put_autosuspend(host->dev);
2205 return 0; 2353 return 0;
@@ -2215,22 +2363,51 @@ static int omap_hsmmc_resume(struct device *dev)
2215static int omap_hsmmc_runtime_suspend(struct device *dev) 2363static int omap_hsmmc_runtime_suspend(struct device *dev)
2216{ 2364{
2217 struct omap_hsmmc_host *host; 2365 struct omap_hsmmc_host *host;
2366 unsigned long flags;
2218 2367
2219 host = platform_get_drvdata(to_platform_device(dev)); 2368 host = platform_get_drvdata(to_platform_device(dev));
2220 omap_hsmmc_context_save(host); 2369 omap_hsmmc_context_save(host);
2221 dev_dbg(dev, "disabled\n"); 2370 dev_dbg(dev, "disabled\n");
2222 2371
2372 spin_lock_irqsave(&host->irq_lock, flags);
2373 if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
2374 (host->flags & HSMMC_SDIO_IRQ_ENABLED)) {
2375 /* disable sdio irq handling to prevent race */
2376 OMAP_HSMMC_WRITE(host->base, ISE, 0);
2377 OMAP_HSMMC_WRITE(host->base, IE, 0);
2378 OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
2379
2380 WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED);
2381 enable_irq(host->wake_irq);
2382 host->flags |= HSMMC_WAKE_IRQ_ENABLED;
2383 }
2384 spin_unlock_irqrestore(&host->irq_lock, flags);
2223 return 0; 2385 return 0;
2224} 2386}
2225 2387
2226static int omap_hsmmc_runtime_resume(struct device *dev) 2388static int omap_hsmmc_runtime_resume(struct device *dev)
2227{ 2389{
2228 struct omap_hsmmc_host *host; 2390 struct omap_hsmmc_host *host;
2391 unsigned long flags;
2229 2392
2230 host = platform_get_drvdata(to_platform_device(dev)); 2393 host = platform_get_drvdata(to_platform_device(dev));
2231 omap_hsmmc_context_restore(host); 2394 omap_hsmmc_context_restore(host);
2232 dev_dbg(dev, "enabled\n"); 2395 dev_dbg(dev, "enabled\n");
2233 2396
2397 spin_lock_irqsave(&host->irq_lock, flags);
2398 if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
2399 (host->flags & HSMMC_SDIO_IRQ_ENABLED)) {
2400 /* sdio irq flag can't change while in runtime suspend */
2401 if (host->flags & HSMMC_WAKE_IRQ_ENABLED) {
2402 disable_irq_nosync(host->wake_irq);
2403 host->flags &= ~HSMMC_WAKE_IRQ_ENABLED;
2404 }
2405
2406 OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
2407 OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
2408 OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
2409 }
2410 spin_unlock_irqrestore(&host->irq_lock, flags);
2234 return 0; 2411 return 0;
2235} 2412}
2236 2413