qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

npcm7xx_smbus-test.c (16038B)


      1 /*
      2  * QTests for Nuvoton NPCM7xx SMBus Modules.
      3  *
      4  * Copyright 2020 Google LLC
      5  *
      6  * This program is free software; you can redistribute it and/or modify it
      7  * under the terms of the GNU General Public License as published by the
      8  * Free Software Foundation; either version 2 of the License, or
      9  * (at your option) any later version.
     10  *
     11  * This program is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
     14  * for more details.
     15  */
     16 
     17 #include "qemu/osdep.h"
     18 #include "qemu/bitops.h"
     19 #include "libqos/i2c.h"
     20 #include "libqtest.h"
     21 #include "hw/sensor/tmp105_regs.h"
     22 
     23 #define NR_SMBUS_DEVICES    16
     24 #define SMBUS_ADDR(x)       (0xf0080000 + 0x1000 * (x))
     25 #define SMBUS_IRQ(x)        (64 + (x))
     26 
     27 #define EVB_DEVICE_ADDR     0x48
     28 #define INVALID_DEVICE_ADDR 0x01
     29 
     30 const int evb_bus_list[] = {0, 1, 2, 6};
     31 
     32 /* Offsets */
     33 enum CommonRegister {
     34     OFFSET_SDA     = 0x0,
     35     OFFSET_ST      = 0x2,
     36     OFFSET_CST     = 0x4,
     37     OFFSET_CTL1    = 0x6,
     38     OFFSET_ADDR1   = 0x8,
     39     OFFSET_CTL2    = 0xa,
     40     OFFSET_ADDR2   = 0xc,
     41     OFFSET_CTL3    = 0xe,
     42     OFFSET_CST2    = 0x18,
     43     OFFSET_CST3    = 0x19,
     44 };
     45 
     46 enum NPCM7xxSMBusBank0Register {
     47     OFFSET_ADDR3   = 0x10,
     48     OFFSET_ADDR7   = 0x11,
     49     OFFSET_ADDR4   = 0x12,
     50     OFFSET_ADDR8   = 0x13,
     51     OFFSET_ADDR5   = 0x14,
     52     OFFSET_ADDR9   = 0x15,
     53     OFFSET_ADDR6   = 0x16,
     54     OFFSET_ADDR10  = 0x17,
     55     OFFSET_CTL4    = 0x1a,
     56     OFFSET_CTL5    = 0x1b,
     57     OFFSET_SCLLT   = 0x1c,
     58     OFFSET_FIF_CTL = 0x1d,
     59     OFFSET_SCLHT   = 0x1e,
     60 };
     61 
     62 enum NPCM7xxSMBusBank1Register {
     63     OFFSET_FIF_CTS  = 0x10,
     64     OFFSET_FAIR_PER = 0x11,
     65     OFFSET_TXF_CTL  = 0x12,
     66     OFFSET_T_OUT    = 0x14,
     67     OFFSET_TXF_STS  = 0x1a,
     68     OFFSET_RXF_STS  = 0x1c,
     69     OFFSET_RXF_CTL  = 0x1e,
     70 };
     71 
     72 /* ST fields */
     73 #define ST_STP              BIT(7)
     74 #define ST_SDAST            BIT(6)
     75 #define ST_BER              BIT(5)
     76 #define ST_NEGACK           BIT(4)
     77 #define ST_STASTR           BIT(3)
     78 #define ST_NMATCH           BIT(2)
     79 #define ST_MODE             BIT(1)
     80 #define ST_XMIT             BIT(0)
     81 
     82 /* CST fields */
     83 #define CST_ARPMATCH        BIT(7)
     84 #define CST_MATCHAF         BIT(6)
     85 #define CST_TGSCL           BIT(5)
     86 #define CST_TSDA            BIT(4)
     87 #define CST_GCMATCH         BIT(3)
     88 #define CST_MATCH           BIT(2)
     89 #define CST_BB              BIT(1)
     90 #define CST_BUSY            BIT(0)
     91 
     92 /* CST2 fields */
     93 #define CST2_INSTTS         BIT(7)
     94 #define CST2_MATCH7F        BIT(6)
     95 #define CST2_MATCH6F        BIT(5)
     96 #define CST2_MATCH5F        BIT(4)
     97 #define CST2_MATCH4F        BIT(3)
     98 #define CST2_MATCH3F        BIT(2)
     99 #define CST2_MATCH2F        BIT(1)
    100 #define CST2_MATCH1F        BIT(0)
    101 
    102 /* CST3 fields */
    103 #define CST3_EO_BUSY        BIT(7)
    104 #define CST3_MATCH10F       BIT(2)
    105 #define CST3_MATCH9F        BIT(1)
    106 #define CST3_MATCH8F        BIT(0)
    107 
    108 /* CTL1 fields */
    109 #define CTL1_STASTRE        BIT(7)
    110 #define CTL1_NMINTE         BIT(6)
    111 #define CTL1_GCMEN          BIT(5)
    112 #define CTL1_ACK            BIT(4)
    113 #define CTL1_EOBINTE        BIT(3)
    114 #define CTL1_INTEN          BIT(2)
    115 #define CTL1_STOP           BIT(1)
    116 #define CTL1_START          BIT(0)
    117 
    118 /* CTL2 fields */
    119 #define CTL2_SCLFRQ(rv)     extract8((rv), 1, 6)
    120 #define CTL2_ENABLE         BIT(0)
    121 
    122 /* CTL3 fields */
    123 #define CTL3_SCL_LVL        BIT(7)
    124 #define CTL3_SDA_LVL        BIT(6)
    125 #define CTL3_BNK_SEL        BIT(5)
    126 #define CTL3_400K_MODE      BIT(4)
    127 #define CTL3_IDL_START      BIT(3)
    128 #define CTL3_ARPMEN         BIT(2)
    129 #define CTL3_SCLFRQ(rv)     extract8((rv), 0, 2)
    130 
    131 /* ADDR fields */
    132 #define ADDR_EN             BIT(7)
    133 #define ADDR_A(rv)          extract8((rv), 0, 6)
    134 
    135 /* FIF_CTL fields */
    136 #define FIF_CTL_FIFO_EN         BIT(4)
    137 
    138 /* FIF_CTS fields */
    139 #define FIF_CTS_CLR_FIFO        BIT(6)
    140 #define FIF_CTS_RFTE_IE         BIT(3)
    141 #define FIF_CTS_RXF_TXE         BIT(1)
    142 
    143 /* TXF_CTL fields */
    144 #define TXF_CTL_THR_TXIE        BIT(6)
    145 #define TXF_CTL_TX_THR(rv)      extract8((rv), 0, 5)
    146 
    147 /* TXF_STS fields */
    148 #define TXF_STS_TX_THST         BIT(6)
    149 #define TXF_STS_TX_BYTES(rv)    extract8((rv), 0, 5)
    150 
    151 /* RXF_CTL fields */
    152 #define RXF_CTL_THR_RXIE        BIT(6)
    153 #define RXF_CTL_LAST            BIT(5)
    154 #define RXF_CTL_RX_THR(rv)      extract8((rv), 0, 5)
    155 
    156 /* RXF_STS fields */
    157 #define RXF_STS_RX_THST         BIT(6)
    158 #define RXF_STS_RX_BYTES(rv)    extract8((rv), 0, 5)
    159 
    160 
    161 static void choose_bank(QTestState *qts, uint64_t base_addr, uint8_t bank)
    162 {
    163     uint8_t ctl3 = qtest_readb(qts, base_addr + OFFSET_CTL3);
    164 
    165     if (bank) {
    166         ctl3 |= CTL3_BNK_SEL;
    167     } else {
    168         ctl3 &= ~CTL3_BNK_SEL;
    169     }
    170 
    171     qtest_writeb(qts, base_addr + OFFSET_CTL3, ctl3);
    172 }
    173 
    174 static void check_running(QTestState *qts, uint64_t base_addr)
    175 {
    176     g_assert_true(qtest_readb(qts, base_addr + OFFSET_CST) & CST_BUSY);
    177     g_assert_true(qtest_readb(qts, base_addr + OFFSET_CST) & CST_BB);
    178 }
    179 
    180 static void check_stopped(QTestState *qts, uint64_t base_addr)
    181 {
    182     uint8_t cst3;
    183 
    184     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_ST), ==, 0);
    185     g_assert_false(qtest_readb(qts, base_addr + OFFSET_CST) & CST_BUSY);
    186     g_assert_false(qtest_readb(qts, base_addr + OFFSET_CST) & CST_BB);
    187 
    188     cst3 = qtest_readb(qts, base_addr + OFFSET_CST3);
    189     g_assert_true(cst3 & CST3_EO_BUSY);
    190     qtest_writeb(qts, base_addr + OFFSET_CST3, cst3);
    191     cst3 = qtest_readb(qts, base_addr + OFFSET_CST3);
    192     g_assert_false(cst3 & CST3_EO_BUSY);
    193 }
    194 
    195 static void enable_bus(QTestState *qts, uint64_t base_addr)
    196 {
    197     uint8_t ctl2 = qtest_readb(qts, base_addr + OFFSET_CTL2);
    198 
    199     ctl2 |= CTL2_ENABLE;
    200     qtest_writeb(qts, base_addr + OFFSET_CTL2, ctl2);
    201     g_assert_true(qtest_readb(qts, base_addr + OFFSET_CTL2) & CTL2_ENABLE);
    202 }
    203 
    204 static void disable_bus(QTestState *qts, uint64_t base_addr)
    205 {
    206     uint8_t ctl2 = qtest_readb(qts, base_addr + OFFSET_CTL2);
    207 
    208     ctl2 &= ~CTL2_ENABLE;
    209     qtest_writeb(qts, base_addr + OFFSET_CTL2, ctl2);
    210     g_assert_false(qtest_readb(qts, base_addr + OFFSET_CTL2) & CTL2_ENABLE);
    211 }
    212 
    213 static void start_transfer(QTestState *qts, uint64_t base_addr)
    214 {
    215     uint8_t ctl1;
    216 
    217     ctl1 = CTL1_START | CTL1_INTEN | CTL1_STASTRE;
    218     qtest_writeb(qts, base_addr + OFFSET_CTL1, ctl1);
    219     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_CTL1), ==,
    220                     CTL1_INTEN | CTL1_STASTRE | CTL1_INTEN);
    221     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_ST), ==,
    222                     ST_MODE | ST_XMIT | ST_SDAST);
    223     check_running(qts, base_addr);
    224 }
    225 
    226 static void stop_transfer(QTestState *qts, uint64_t base_addr)
    227 {
    228     uint8_t ctl1 = qtest_readb(qts, base_addr + OFFSET_CTL1);
    229 
    230     ctl1 &= ~(CTL1_START | CTL1_ACK);
    231     ctl1 |= CTL1_STOP | CTL1_INTEN | CTL1_EOBINTE;
    232     qtest_writeb(qts, base_addr + OFFSET_CTL1, ctl1);
    233     ctl1 = qtest_readb(qts, base_addr + OFFSET_CTL1);
    234     g_assert_false(ctl1 & CTL1_STOP);
    235 }
    236 
    237 static void send_byte(QTestState *qts, uint64_t base_addr, uint8_t byte)
    238 {
    239     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_ST), ==,
    240                     ST_MODE | ST_XMIT | ST_SDAST);
    241     qtest_writeb(qts, base_addr + OFFSET_SDA, byte);
    242 }
    243 
    244 static bool check_recv(QTestState *qts, uint64_t base_addr)
    245 {
    246     uint8_t st, fif_ctl, rxf_ctl, rxf_sts;
    247     bool fifo;
    248 
    249     st = qtest_readb(qts, base_addr + OFFSET_ST);
    250     choose_bank(qts, base_addr, 0);
    251     fif_ctl = qtest_readb(qts, base_addr + OFFSET_FIF_CTL);
    252     fifo = fif_ctl & FIF_CTL_FIFO_EN;
    253     if (!fifo) {
    254         return st == (ST_MODE | ST_SDAST);
    255     }
    256 
    257     choose_bank(qts, base_addr, 1);
    258     rxf_ctl = qtest_readb(qts, base_addr + OFFSET_RXF_CTL);
    259     rxf_sts = qtest_readb(qts, base_addr + OFFSET_RXF_STS);
    260 
    261     if ((rxf_ctl & RXF_CTL_THR_RXIE) && RXF_STS_RX_BYTES(rxf_sts) < 16) {
    262         return st == ST_MODE;
    263     } else {
    264         return st == (ST_MODE | ST_SDAST);
    265     }
    266 }
    267 
    268 static uint8_t recv_byte(QTestState *qts, uint64_t base_addr)
    269 {
    270     g_assert_true(check_recv(qts, base_addr));
    271     return qtest_readb(qts, base_addr + OFFSET_SDA);
    272 }
    273 
    274 static void send_address(QTestState *qts, uint64_t base_addr, uint8_t addr,
    275                          bool recv, bool valid)
    276 {
    277     uint8_t encoded_addr = (addr << 1) | (recv ? 1 : 0);
    278     uint8_t st;
    279 
    280     qtest_writeb(qts, base_addr + OFFSET_SDA, encoded_addr);
    281     st = qtest_readb(qts, base_addr + OFFSET_ST);
    282 
    283     if (valid) {
    284         if (recv) {
    285             g_assert_cmphex(st, ==, ST_MODE | ST_SDAST | ST_STASTR);
    286         } else {
    287             g_assert_cmphex(st, ==, ST_MODE | ST_XMIT | ST_SDAST | ST_STASTR);
    288         }
    289 
    290         qtest_writeb(qts, base_addr + OFFSET_ST, ST_STASTR);
    291         st = qtest_readb(qts, base_addr + OFFSET_ST);
    292         if (recv) {
    293             g_assert_true(check_recv(qts, base_addr));
    294         } else {
    295             g_assert_cmphex(st, ==, ST_MODE | ST_XMIT | ST_SDAST);
    296         }
    297     } else {
    298         if (recv) {
    299             g_assert_cmphex(st, ==, ST_MODE | ST_NEGACK);
    300         } else {
    301             g_assert_cmphex(st, ==, ST_MODE | ST_XMIT | ST_NEGACK);
    302         }
    303     }
    304 }
    305 
    306 static void send_nack(QTestState *qts, uint64_t base_addr)
    307 {
    308     uint8_t ctl1 = qtest_readb(qts, base_addr + OFFSET_CTL1);
    309 
    310     ctl1 &= ~(CTL1_START | CTL1_STOP);
    311     ctl1 |= CTL1_ACK | CTL1_INTEN;
    312     qtest_writeb(qts, base_addr + OFFSET_CTL1, ctl1);
    313 }
    314 
    315 static void start_fifo_mode(QTestState *qts, uint64_t base_addr)
    316 {
    317     choose_bank(qts, base_addr, 0);
    318     qtest_writeb(qts, base_addr + OFFSET_FIF_CTL, FIF_CTL_FIFO_EN);
    319     g_assert_true(qtest_readb(qts, base_addr + OFFSET_FIF_CTL) &
    320                   FIF_CTL_FIFO_EN);
    321     choose_bank(qts, base_addr, 1);
    322     qtest_writeb(qts, base_addr + OFFSET_FIF_CTS,
    323                  FIF_CTS_CLR_FIFO | FIF_CTS_RFTE_IE);
    324     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_FIF_CTS), ==,
    325                     FIF_CTS_RFTE_IE);
    326     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_TXF_STS), ==, 0);
    327     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_RXF_STS), ==, 0);
    328 }
    329 
    330 static void start_recv_fifo(QTestState *qts, uint64_t base_addr, uint8_t bytes)
    331 {
    332     choose_bank(qts, base_addr, 1);
    333     qtest_writeb(qts, base_addr + OFFSET_TXF_CTL, 0);
    334     qtest_writeb(qts, base_addr + OFFSET_RXF_CTL,
    335                  RXF_CTL_THR_RXIE | RXF_CTL_LAST | bytes);
    336 }
    337 
    338 /* Check the SMBus's status is set correctly when disabled. */
    339 static void test_disable_bus(gconstpointer data)
    340 {
    341     intptr_t index = (intptr_t)data;
    342     uint64_t base_addr = SMBUS_ADDR(index);
    343     QTestState *qts = qtest_init("-machine npcm750-evb");
    344 
    345     disable_bus(qts, base_addr);
    346     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_CTL1), ==, 0);
    347     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_ST), ==, 0);
    348     g_assert_false(qtest_readb(qts, base_addr + OFFSET_CST3) & CST3_EO_BUSY);
    349     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_CST), ==, 0);
    350     qtest_quit(qts);
    351 }
    352 
    353 /* Check the SMBus returns a NACK for an invalid address. */
    354 static void test_invalid_addr(gconstpointer data)
    355 {
    356     intptr_t index = (intptr_t)data;
    357     uint64_t base_addr = SMBUS_ADDR(index);
    358     int irq = SMBUS_IRQ(index);
    359     QTestState *qts = qtest_init("-machine npcm750-evb");
    360 
    361     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
    362     enable_bus(qts, base_addr);
    363     g_assert_false(qtest_get_irq(qts, irq));
    364     start_transfer(qts, base_addr);
    365     send_address(qts, base_addr, INVALID_DEVICE_ADDR, false, false);
    366     g_assert_true(qtest_get_irq(qts, irq));
    367     stop_transfer(qts, base_addr);
    368     check_running(qts, base_addr);
    369     qtest_writeb(qts, base_addr + OFFSET_ST, ST_NEGACK);
    370     g_assert_false(qtest_readb(qts, base_addr + OFFSET_ST) & ST_NEGACK);
    371     check_stopped(qts, base_addr);
    372     qtest_quit(qts);
    373 }
    374 
    375 /* Check the SMBus can send and receive bytes to a device in single mode. */
    376 static void test_single_mode(gconstpointer data)
    377 {
    378     intptr_t index = (intptr_t)data;
    379     uint64_t base_addr = SMBUS_ADDR(index);
    380     int irq = SMBUS_IRQ(index);
    381     uint8_t value = 0x60;
    382     QTestState *qts = qtest_init("-machine npcm750-evb");
    383 
    384     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
    385     enable_bus(qts, base_addr);
    386 
    387     /* Sending */
    388     g_assert_false(qtest_get_irq(qts, irq));
    389     start_transfer(qts, base_addr);
    390     g_assert_true(qtest_get_irq(qts, irq));
    391     send_address(qts, base_addr, EVB_DEVICE_ADDR, false, true);
    392     send_byte(qts, base_addr, TMP105_REG_CONFIG);
    393     send_byte(qts, base_addr, value);
    394     stop_transfer(qts, base_addr);
    395     check_stopped(qts, base_addr);
    396 
    397     /* Receiving */
    398     start_transfer(qts, base_addr);
    399     send_address(qts, base_addr, EVB_DEVICE_ADDR, false, true);
    400     send_byte(qts, base_addr, TMP105_REG_CONFIG);
    401     start_transfer(qts, base_addr);
    402     send_address(qts, base_addr, EVB_DEVICE_ADDR, true, true);
    403     send_nack(qts, base_addr);
    404     stop_transfer(qts, base_addr);
    405     check_running(qts, base_addr);
    406     g_assert_cmphex(recv_byte(qts, base_addr), ==, value);
    407     check_stopped(qts, base_addr);
    408     qtest_quit(qts);
    409 }
    410 
    411 /* Check the SMBus can send and receive bytes in FIFO mode. */
    412 static void test_fifo_mode(gconstpointer data)
    413 {
    414     intptr_t index = (intptr_t)data;
    415     uint64_t base_addr = SMBUS_ADDR(index);
    416     int irq = SMBUS_IRQ(index);
    417     uint8_t value = 0x60;
    418     QTestState *qts = qtest_init("-machine npcm750-evb");
    419 
    420     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
    421     enable_bus(qts, base_addr);
    422     start_fifo_mode(qts, base_addr);
    423     g_assert_false(qtest_get_irq(qts, irq));
    424 
    425     /* Sending */
    426     start_transfer(qts, base_addr);
    427     send_address(qts, base_addr, EVB_DEVICE_ADDR, false, true);
    428     choose_bank(qts, base_addr, 1);
    429     g_assert_true(qtest_readb(qts, base_addr + OFFSET_FIF_CTS) &
    430                   FIF_CTS_RXF_TXE);
    431     qtest_writeb(qts, base_addr + OFFSET_TXF_CTL, TXF_CTL_THR_TXIE);
    432     send_byte(qts, base_addr, TMP105_REG_CONFIG);
    433     send_byte(qts, base_addr, value);
    434     g_assert_true(qtest_readb(qts, base_addr + OFFSET_FIF_CTS) &
    435                   FIF_CTS_RXF_TXE);
    436     g_assert_true(qtest_readb(qts, base_addr + OFFSET_TXF_STS) &
    437                   TXF_STS_TX_THST);
    438     g_assert_cmpuint(TXF_STS_TX_BYTES(
    439                         qtest_readb(qts, base_addr + OFFSET_TXF_STS)), ==, 0);
    440     g_assert_true(qtest_get_irq(qts, irq));
    441     stop_transfer(qts, base_addr);
    442     check_stopped(qts, base_addr);
    443 
    444     /* Receiving */
    445     start_fifo_mode(qts, base_addr);
    446     start_transfer(qts, base_addr);
    447     send_address(qts, base_addr, EVB_DEVICE_ADDR, false, true);
    448     send_byte(qts, base_addr, TMP105_REG_CONFIG);
    449     start_transfer(qts, base_addr);
    450     qtest_writeb(qts, base_addr + OFFSET_FIF_CTS, FIF_CTS_RXF_TXE);
    451     start_recv_fifo(qts, base_addr, 1);
    452     send_address(qts, base_addr, EVB_DEVICE_ADDR, true, true);
    453     g_assert_false(qtest_readb(qts, base_addr + OFFSET_FIF_CTS) &
    454                    FIF_CTS_RXF_TXE);
    455     g_assert_true(qtest_readb(qts, base_addr + OFFSET_RXF_STS) &
    456                   RXF_STS_RX_THST);
    457     g_assert_cmpuint(RXF_STS_RX_BYTES(
    458                         qtest_readb(qts, base_addr + OFFSET_RXF_STS)), ==, 1);
    459     send_nack(qts, base_addr);
    460     stop_transfer(qts, base_addr);
    461     check_running(qts, base_addr);
    462     g_assert_cmphex(recv_byte(qts, base_addr), ==, value);
    463     g_assert_cmpuint(RXF_STS_RX_BYTES(
    464                         qtest_readb(qts, base_addr + OFFSET_RXF_STS)), ==, 0);
    465     check_stopped(qts, base_addr);
    466     qtest_quit(qts);
    467 }
    468 
    469 static void smbus_add_test(const char *name, int index, GTestDataFunc fn)
    470 {
    471     g_autofree char *full_name = g_strdup_printf(
    472             "npcm7xx_smbus[%d]/%s", index, name);
    473     qtest_add_data_func(full_name, (void *)(intptr_t)index, fn);
    474 }
    475 #define add_test(name, td) smbus_add_test(#name, td, test_##name)
    476 
    477 int main(int argc, char **argv)
    478 {
    479     int i;
    480 
    481     g_test_init(&argc, &argv, NULL);
    482     g_test_set_nonfatal_assertions();
    483 
    484     for (i = 0; i < NR_SMBUS_DEVICES; ++i) {
    485         add_test(disable_bus, i);
    486         add_test(invalid_addr, i);
    487     }
    488 
    489     for (i = 0; i < ARRAY_SIZE(evb_bus_list); ++i) {
    490         add_test(single_mode, evb_bus_list[i]);
    491         add_test(fifo_mode, evb_bus_list[i]);
    492     }
    493 
    494     return g_test_run();
    495 }