14d2cd2d8SGlenn Miles /*
24d2cd2d8SGlenn Miles  * QTest testcase for PowerNV 10 Host I2C Communications
34d2cd2d8SGlenn Miles  *
44d2cd2d8SGlenn Miles  * Copyright (c) 2023, IBM Corporation.
54d2cd2d8SGlenn Miles  *
64d2cd2d8SGlenn Miles  * This work is licensed under the terms of the GNU GPL, version 2 or
74d2cd2d8SGlenn Miles  * later. See the COPYING file in the top-level directory.
84d2cd2d8SGlenn Miles  */
94d2cd2d8SGlenn Miles #include "qemu/osdep.h"
104d2cd2d8SGlenn Miles #include "libqtest.h"
11*6328d8ffSCédric Le Goater #include "hw/gpio/pca9554_regs.h"
12*6328d8ffSCédric Le Goater #include "hw/gpio/pca9552_regs.h"
134d2cd2d8SGlenn Miles #include "pnv-xscom.h"
144d2cd2d8SGlenn Miles 
154d2cd2d8SGlenn Miles #define PPC_BIT(bit)            (0x8000000000000000ULL >> (bit))
164d2cd2d8SGlenn Miles #define PPC_BIT32(bit)          (0x80000000 >> (bit))
174d2cd2d8SGlenn Miles #define PPC_BIT8(bit)           (0x80 >> (bit))
184d2cd2d8SGlenn Miles #define PPC_BITMASK(bs, be)     ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs))
194d2cd2d8SGlenn Miles #define PPC_BITMASK32(bs, be)   ((PPC_BIT32(bs) - PPC_BIT32(be)) | \
204d2cd2d8SGlenn Miles                                  PPC_BIT32(bs))
214d2cd2d8SGlenn Miles 
224d2cd2d8SGlenn Miles #define MASK_TO_LSH(m)          (__builtin_ffsll(m) - 1)
234d2cd2d8SGlenn Miles #define GETFIELD(m, v)          (((v) & (m)) >> MASK_TO_LSH(m))
244d2cd2d8SGlenn Miles #define SETFIELD(m, v, val) \
254d2cd2d8SGlenn Miles         (((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) & (m)))
264d2cd2d8SGlenn Miles 
274d2cd2d8SGlenn Miles #define PNV10_XSCOM_I2CM_BASE   0xa0000
284d2cd2d8SGlenn Miles #define PNV10_XSCOM_I2CM_SIZE   0x1000
294d2cd2d8SGlenn Miles 
304d2cd2d8SGlenn Miles #include "hw/i2c/pnv_i2c_regs.h"
314d2cd2d8SGlenn Miles 
324d2cd2d8SGlenn Miles typedef struct {
334d2cd2d8SGlenn Miles     QTestState    *qts;
344d2cd2d8SGlenn Miles     const PnvChip *chip;
354d2cd2d8SGlenn Miles     int           engine;
364d2cd2d8SGlenn Miles } PnvI2cCtlr;
374d2cd2d8SGlenn Miles 
384d2cd2d8SGlenn Miles typedef struct {
394d2cd2d8SGlenn Miles     PnvI2cCtlr  *ctlr;
404d2cd2d8SGlenn Miles     int         port;
414d2cd2d8SGlenn Miles     uint8_t     addr;
424d2cd2d8SGlenn Miles } PnvI2cDev;
434d2cd2d8SGlenn Miles 
444d2cd2d8SGlenn Miles 
pnv_i2c_xscom_addr(PnvI2cCtlr * ctlr,uint32_t reg)454d2cd2d8SGlenn Miles static uint64_t pnv_i2c_xscom_addr(PnvI2cCtlr *ctlr, uint32_t reg)
464d2cd2d8SGlenn Miles {
474d2cd2d8SGlenn Miles     return pnv_xscom_addr(ctlr->chip, PNV10_XSCOM_I2CM_BASE +
484d2cd2d8SGlenn Miles                           (PNV10_XSCOM_I2CM_SIZE * ctlr->engine) + reg);
494d2cd2d8SGlenn Miles }
504d2cd2d8SGlenn Miles 
pnv_i2c_xscom_read(PnvI2cCtlr * ctlr,uint32_t reg)514d2cd2d8SGlenn Miles static uint64_t pnv_i2c_xscom_read(PnvI2cCtlr *ctlr, uint32_t reg)
524d2cd2d8SGlenn Miles {
534d2cd2d8SGlenn Miles     return qtest_readq(ctlr->qts, pnv_i2c_xscom_addr(ctlr, reg));
544d2cd2d8SGlenn Miles }
554d2cd2d8SGlenn Miles 
pnv_i2c_xscom_write(PnvI2cCtlr * ctlr,uint32_t reg,uint64_t val)564d2cd2d8SGlenn Miles static void pnv_i2c_xscom_write(PnvI2cCtlr *ctlr, uint32_t reg, uint64_t val)
574d2cd2d8SGlenn Miles {
584d2cd2d8SGlenn Miles     qtest_writeq(ctlr->qts, pnv_i2c_xscom_addr(ctlr, reg), val);
594d2cd2d8SGlenn Miles }
604d2cd2d8SGlenn Miles 
614d2cd2d8SGlenn Miles /* Write len bytes from buf to i2c device with given addr and port */
pnv_i2c_send(PnvI2cDev * dev,const uint8_t * buf,uint16_t len)624d2cd2d8SGlenn Miles static void pnv_i2c_send(PnvI2cDev *dev, const uint8_t *buf, uint16_t len)
634d2cd2d8SGlenn Miles {
644d2cd2d8SGlenn Miles     int byte_num;
654d2cd2d8SGlenn Miles     uint64_t reg64;
664d2cd2d8SGlenn Miles 
674d2cd2d8SGlenn Miles     /* select requested port */
684d2cd2d8SGlenn Miles     reg64 = SETFIELD(I2C_MODE_BIT_RATE_DIV, 0ull, 0x2be);
694d2cd2d8SGlenn Miles     reg64 = SETFIELD(I2C_MODE_PORT_NUM, reg64, dev->port);
704d2cd2d8SGlenn Miles     pnv_i2c_xscom_write(dev->ctlr, I2C_MODE_REG, reg64);
714d2cd2d8SGlenn Miles 
724d2cd2d8SGlenn Miles     /* check status for cmd complete and bus idle */
734d2cd2d8SGlenn Miles     reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG);
744d2cd2d8SGlenn Miles     g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0);
754d2cd2d8SGlenn Miles     reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG);
764d2cd2d8SGlenn Miles     g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==,
774d2cd2d8SGlenn Miles                     I2C_STAT_CMD_COMP);
784d2cd2d8SGlenn Miles 
794d2cd2d8SGlenn Miles     /* Send start, with stop, with address and len bytes of data */
804d2cd2d8SGlenn Miles     reg64 = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR | I2C_CMD_WITH_STOP;
814d2cd2d8SGlenn Miles     reg64 = SETFIELD(I2C_CMD_DEV_ADDR, reg64, dev->addr);
824d2cd2d8SGlenn Miles     reg64 = SETFIELD(I2C_CMD_LEN_BYTES, reg64, len);
834d2cd2d8SGlenn Miles     pnv_i2c_xscom_write(dev->ctlr, I2C_CMD_REG, reg64);
844d2cd2d8SGlenn Miles 
854d2cd2d8SGlenn Miles     /* check status for errors */
864d2cd2d8SGlenn Miles     reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG);
874d2cd2d8SGlenn Miles     g_assert_cmphex(reg64 & I2C_STAT_ANY_ERR, ==, 0);
884d2cd2d8SGlenn Miles 
894d2cd2d8SGlenn Miles     /* write data bytes to fifo register */
904d2cd2d8SGlenn Miles     for (byte_num = 0; byte_num < len; byte_num++) {
914d2cd2d8SGlenn Miles         reg64 = SETFIELD(I2C_FIFO, 0ull, buf[byte_num]);
924d2cd2d8SGlenn Miles         pnv_i2c_xscom_write(dev->ctlr, I2C_FIFO_REG, reg64);
934d2cd2d8SGlenn Miles     }
944d2cd2d8SGlenn Miles 
954d2cd2d8SGlenn Miles     /* check status for cmd complete and bus idle */
964d2cd2d8SGlenn Miles     reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG);
974d2cd2d8SGlenn Miles     g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0);
984d2cd2d8SGlenn Miles     reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG);
994d2cd2d8SGlenn Miles     g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==,
1004d2cd2d8SGlenn Miles                     I2C_STAT_CMD_COMP);
1014d2cd2d8SGlenn Miles }
1024d2cd2d8SGlenn Miles 
1034d2cd2d8SGlenn Miles /* Recieve len bytes into buf from i2c device with given addr and port */
pnv_i2c_recv(PnvI2cDev * dev,uint8_t * buf,uint16_t len)1044d2cd2d8SGlenn Miles static void pnv_i2c_recv(PnvI2cDev *dev, uint8_t *buf, uint16_t len)
1054d2cd2d8SGlenn Miles {
1064d2cd2d8SGlenn Miles     int byte_num;
1074d2cd2d8SGlenn Miles     uint64_t reg64;
1084d2cd2d8SGlenn Miles 
1094d2cd2d8SGlenn Miles     /* select requested port */
1104d2cd2d8SGlenn Miles     reg64 = SETFIELD(I2C_MODE_BIT_RATE_DIV, 0ull, 0x2be);
1114d2cd2d8SGlenn Miles     reg64 = SETFIELD(I2C_MODE_PORT_NUM, reg64, dev->port);
1124d2cd2d8SGlenn Miles     pnv_i2c_xscom_write(dev->ctlr, I2C_MODE_REG, reg64);
1134d2cd2d8SGlenn Miles 
1144d2cd2d8SGlenn Miles     /* check status for cmd complete and bus idle */
1154d2cd2d8SGlenn Miles     reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG);
1164d2cd2d8SGlenn Miles     g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0);
1174d2cd2d8SGlenn Miles     reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG);
1184d2cd2d8SGlenn Miles     g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==,
1194d2cd2d8SGlenn Miles                     I2C_STAT_CMD_COMP);
1204d2cd2d8SGlenn Miles 
1214d2cd2d8SGlenn Miles     /* Send start, with stop, with address and len bytes of data */
1224d2cd2d8SGlenn Miles     reg64 = I2C_CMD_WITH_START | I2C_CMD_WITH_ADDR |
1234d2cd2d8SGlenn Miles             I2C_CMD_WITH_STOP | I2C_CMD_READ_NOT_WRITE;
1244d2cd2d8SGlenn Miles     reg64 = SETFIELD(I2C_CMD_DEV_ADDR, reg64, dev->addr);
1254d2cd2d8SGlenn Miles     reg64 = SETFIELD(I2C_CMD_LEN_BYTES, reg64, len);
1264d2cd2d8SGlenn Miles     pnv_i2c_xscom_write(dev->ctlr, I2C_CMD_REG, reg64);
1274d2cd2d8SGlenn Miles 
1284d2cd2d8SGlenn Miles     /* check status for errors */
1294d2cd2d8SGlenn Miles     reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG);
1304d2cd2d8SGlenn Miles     g_assert_cmphex(reg64 & I2C_STAT_ANY_ERR, ==, 0);
1314d2cd2d8SGlenn Miles 
1324d2cd2d8SGlenn Miles     /* Read data bytes from fifo register */
1334d2cd2d8SGlenn Miles     for (byte_num = 0; byte_num < len; byte_num++) {
1344d2cd2d8SGlenn Miles         reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_FIFO_REG);
1354d2cd2d8SGlenn Miles         buf[byte_num] = GETFIELD(I2C_FIFO, reg64);
1364d2cd2d8SGlenn Miles     }
1374d2cd2d8SGlenn Miles 
1384d2cd2d8SGlenn Miles     /* check status for cmd complete and bus idle */
1394d2cd2d8SGlenn Miles     reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_EXTD_STAT_REG);
1404d2cd2d8SGlenn Miles     g_assert_cmphex(reg64 & I2C_EXTD_STAT_I2C_BUSY, ==, 0);
1414d2cd2d8SGlenn Miles     reg64 = pnv_i2c_xscom_read(dev->ctlr, I2C_STAT_REG);
1424d2cd2d8SGlenn Miles     g_assert_cmphex(reg64 & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP), ==,
1434d2cd2d8SGlenn Miles                     I2C_STAT_CMD_COMP);
1444d2cd2d8SGlenn Miles }
1454d2cd2d8SGlenn Miles 
pnv_i2c_pca9554_default_cfg(PnvI2cDev * dev)1464d2cd2d8SGlenn Miles static void pnv_i2c_pca9554_default_cfg(PnvI2cDev *dev)
1474d2cd2d8SGlenn Miles {
1484d2cd2d8SGlenn Miles     uint8_t buf[2];
1494d2cd2d8SGlenn Miles 
1504d2cd2d8SGlenn Miles     /* input register bits are not inverted */
1514d2cd2d8SGlenn Miles     buf[0] = PCA9554_POLARITY;
1524d2cd2d8SGlenn Miles     buf[1] = 0;
1534d2cd2d8SGlenn Miles     pnv_i2c_send(dev, buf, 2);
1544d2cd2d8SGlenn Miles 
1554d2cd2d8SGlenn Miles     /* All pins are inputs */
1564d2cd2d8SGlenn Miles     buf[0] = PCA9554_CONFIG;
1574d2cd2d8SGlenn Miles     buf[1] = 0xff;
1584d2cd2d8SGlenn Miles     pnv_i2c_send(dev, buf, 2);
1594d2cd2d8SGlenn Miles 
1604d2cd2d8SGlenn Miles     /* Output value for when pins are outputs */
1614d2cd2d8SGlenn Miles     buf[0] = PCA9554_OUTPUT;
1624d2cd2d8SGlenn Miles     buf[1] = 0xff;
1634d2cd2d8SGlenn Miles     pnv_i2c_send(dev, buf, 2);
1644d2cd2d8SGlenn Miles }
1654d2cd2d8SGlenn Miles 
pnv_i2c_pca9554_set_pin(PnvI2cDev * dev,int pin,bool high)1664d2cd2d8SGlenn Miles static void pnv_i2c_pca9554_set_pin(PnvI2cDev *dev, int pin, bool high)
1674d2cd2d8SGlenn Miles {
1684d2cd2d8SGlenn Miles     uint8_t send_buf[2];
1694d2cd2d8SGlenn Miles     uint8_t recv_buf[2];
1704d2cd2d8SGlenn Miles     uint8_t mask = 0x1 << pin;
1714d2cd2d8SGlenn Miles     uint8_t new_value = ((high) ? 1 : 0) << pin;
1724d2cd2d8SGlenn Miles 
1734d2cd2d8SGlenn Miles     /* read current OUTPUT value */
1744d2cd2d8SGlenn Miles     send_buf[0] = PCA9554_OUTPUT;
1754d2cd2d8SGlenn Miles     pnv_i2c_send(dev, send_buf, 1);
1764d2cd2d8SGlenn Miles     pnv_i2c_recv(dev, recv_buf, 1);
1774d2cd2d8SGlenn Miles 
1784d2cd2d8SGlenn Miles     /* write new OUTPUT value */
1794d2cd2d8SGlenn Miles     send_buf[1] = (recv_buf[0] & ~mask) | new_value;
1804d2cd2d8SGlenn Miles     pnv_i2c_send(dev, send_buf, 2);
1814d2cd2d8SGlenn Miles 
1824d2cd2d8SGlenn Miles     /* Update config bit for output */
1834d2cd2d8SGlenn Miles     send_buf[0] = PCA9554_CONFIG;
1844d2cd2d8SGlenn Miles     pnv_i2c_send(dev, send_buf, 1);
1854d2cd2d8SGlenn Miles     pnv_i2c_recv(dev, recv_buf, 1);
1864d2cd2d8SGlenn Miles     send_buf[1] = recv_buf[0] & ~mask;
1874d2cd2d8SGlenn Miles     pnv_i2c_send(dev, send_buf, 2);
1884d2cd2d8SGlenn Miles }
1894d2cd2d8SGlenn Miles 
pnv_i2c_pca9554_read_pins(PnvI2cDev * dev)1904d2cd2d8SGlenn Miles static uint8_t pnv_i2c_pca9554_read_pins(PnvI2cDev *dev)
1914d2cd2d8SGlenn Miles {
1924d2cd2d8SGlenn Miles     uint8_t send_buf[1];
1934d2cd2d8SGlenn Miles     uint8_t recv_buf[1];
1944d2cd2d8SGlenn Miles     uint8_t inputs;
1954d2cd2d8SGlenn Miles     send_buf[0] = PCA9554_INPUT;
1964d2cd2d8SGlenn Miles     pnv_i2c_send(dev, send_buf, 1);
1974d2cd2d8SGlenn Miles     pnv_i2c_recv(dev, recv_buf, 1);
1984d2cd2d8SGlenn Miles     inputs = recv_buf[0];
1994d2cd2d8SGlenn Miles     return inputs;
2004d2cd2d8SGlenn Miles }
2014d2cd2d8SGlenn Miles 
pnv_i2c_pca9554_flip_polarity(PnvI2cDev * dev)2024d2cd2d8SGlenn Miles static void pnv_i2c_pca9554_flip_polarity(PnvI2cDev *dev)
2034d2cd2d8SGlenn Miles {
2044d2cd2d8SGlenn Miles     uint8_t recv_buf[1];
2054d2cd2d8SGlenn Miles     uint8_t send_buf[2];
2064d2cd2d8SGlenn Miles 
2074d2cd2d8SGlenn Miles     send_buf[0] = PCA9554_POLARITY;
2084d2cd2d8SGlenn Miles     pnv_i2c_send(dev, send_buf, 1);
2094d2cd2d8SGlenn Miles     pnv_i2c_recv(dev, recv_buf, 1);
2104d2cd2d8SGlenn Miles     send_buf[1] = recv_buf[0] ^ 0xff;
2114d2cd2d8SGlenn Miles     pnv_i2c_send(dev, send_buf, 2);
2124d2cd2d8SGlenn Miles }
2134d2cd2d8SGlenn Miles 
pnv_i2c_pca9554_default_inputs(PnvI2cDev * dev)2144d2cd2d8SGlenn Miles static void pnv_i2c_pca9554_default_inputs(PnvI2cDev *dev)
2154d2cd2d8SGlenn Miles {
2164d2cd2d8SGlenn Miles     uint8_t pin_values = pnv_i2c_pca9554_read_pins(dev);
2174d2cd2d8SGlenn Miles     g_assert_cmphex(pin_values, ==, 0xff);
2184d2cd2d8SGlenn Miles }
2194d2cd2d8SGlenn Miles 
2204d2cd2d8SGlenn Miles /* Check that setting pin values and polarity changes inputs as expected */
pnv_i2c_pca554_set_pins(PnvI2cDev * dev)2214d2cd2d8SGlenn Miles static void pnv_i2c_pca554_set_pins(PnvI2cDev *dev)
2224d2cd2d8SGlenn Miles {
2234d2cd2d8SGlenn Miles     uint8_t pin_values;
2244d2cd2d8SGlenn Miles     pnv_i2c_pca9554_set_pin(dev, 0, 0);
2254d2cd2d8SGlenn Miles     pin_values = pnv_i2c_pca9554_read_pins(dev);
2264d2cd2d8SGlenn Miles     g_assert_cmphex(pin_values, ==, 0xfe);
2274d2cd2d8SGlenn Miles     pnv_i2c_pca9554_flip_polarity(dev);
2284d2cd2d8SGlenn Miles     pin_values = pnv_i2c_pca9554_read_pins(dev);
2294d2cd2d8SGlenn Miles     g_assert_cmphex(pin_values, ==, 0x01);
2304d2cd2d8SGlenn Miles     pnv_i2c_pca9554_set_pin(dev, 2, 0);
2314d2cd2d8SGlenn Miles     pin_values = pnv_i2c_pca9554_read_pins(dev);
2324d2cd2d8SGlenn Miles     g_assert_cmphex(pin_values, ==, 0x05);
2334d2cd2d8SGlenn Miles     pnv_i2c_pca9554_flip_polarity(dev);
2344d2cd2d8SGlenn Miles     pin_values = pnv_i2c_pca9554_read_pins(dev);
2354d2cd2d8SGlenn Miles     g_assert_cmphex(pin_values, ==, 0xfa);
2364d2cd2d8SGlenn Miles     pnv_i2c_pca9554_default_cfg(dev);
2374d2cd2d8SGlenn Miles     pin_values = pnv_i2c_pca9554_read_pins(dev);
2384d2cd2d8SGlenn Miles     g_assert_cmphex(pin_values, ==, 0xff);
2394d2cd2d8SGlenn Miles }
2404d2cd2d8SGlenn Miles 
pnv_i2c_pca9552_default_cfg(PnvI2cDev * dev)2414d2cd2d8SGlenn Miles static void pnv_i2c_pca9552_default_cfg(PnvI2cDev *dev)
2424d2cd2d8SGlenn Miles {
2434d2cd2d8SGlenn Miles     uint8_t buf[2];
2444d2cd2d8SGlenn Miles     /* configure pwm/psc regs */
2454d2cd2d8SGlenn Miles     buf[0] = PCA9552_PSC0;
2464d2cd2d8SGlenn Miles     buf[1] = 0xff;
2474d2cd2d8SGlenn Miles     pnv_i2c_send(dev, buf, 2);
2484d2cd2d8SGlenn Miles     buf[0] = PCA9552_PWM0;
2494d2cd2d8SGlenn Miles     buf[1] = 0x80;
2504d2cd2d8SGlenn Miles     pnv_i2c_send(dev, buf, 2);
2514d2cd2d8SGlenn Miles     buf[0] = PCA9552_PSC1;
2524d2cd2d8SGlenn Miles     buf[1] = 0xff;
2534d2cd2d8SGlenn Miles     pnv_i2c_send(dev, buf, 2);
2544d2cd2d8SGlenn Miles     buf[0] = PCA9552_PWM1;
2554d2cd2d8SGlenn Miles     buf[1] = 0x80;
2564d2cd2d8SGlenn Miles     pnv_i2c_send(dev, buf, 2);
2574d2cd2d8SGlenn Miles 
2584d2cd2d8SGlenn Miles     /* configure all pins as inputs */
2594d2cd2d8SGlenn Miles     buf[0] = PCA9552_LS0;
2604d2cd2d8SGlenn Miles     buf[1] = 0x55;
2614d2cd2d8SGlenn Miles     pnv_i2c_send(dev, buf, 2);
2624d2cd2d8SGlenn Miles     buf[0] = PCA9552_LS1;
2634d2cd2d8SGlenn Miles     buf[1] = 0x55;
2644d2cd2d8SGlenn Miles     pnv_i2c_send(dev, buf, 2);
2654d2cd2d8SGlenn Miles     buf[0] = PCA9552_LS2;
2664d2cd2d8SGlenn Miles     buf[1] = 0x55;
2674d2cd2d8SGlenn Miles     pnv_i2c_send(dev, buf, 2);
2684d2cd2d8SGlenn Miles     buf[0] = PCA9552_LS3;
2694d2cd2d8SGlenn Miles     buf[1] = 0x55;
2704d2cd2d8SGlenn Miles     pnv_i2c_send(dev, buf, 2);
2714d2cd2d8SGlenn Miles }
2724d2cd2d8SGlenn Miles 
pnv_i2c_pca9552_set_pin(PnvI2cDev * dev,int pin,bool high)2734d2cd2d8SGlenn Miles static void pnv_i2c_pca9552_set_pin(PnvI2cDev *dev, int pin, bool high)
2744d2cd2d8SGlenn Miles {
2754d2cd2d8SGlenn Miles     uint8_t send_buf[2];
2764d2cd2d8SGlenn Miles     uint8_t recv_buf[2];
2774d2cd2d8SGlenn Miles     uint8_t reg = PCA9552_LS0 + (pin / 4);
2784d2cd2d8SGlenn Miles     uint8_t shift = (pin % 4) * 2;
2794d2cd2d8SGlenn Miles     uint8_t mask = ~(0x3 << shift);
2804d2cd2d8SGlenn Miles     uint8_t new_value = ((high) ? 1 : 0) << shift;
2814d2cd2d8SGlenn Miles 
2824d2cd2d8SGlenn Miles     /* read current LSx value */
2834d2cd2d8SGlenn Miles     send_buf[0] = reg;
2844d2cd2d8SGlenn Miles     pnv_i2c_send(dev, send_buf, 1);
2854d2cd2d8SGlenn Miles     pnv_i2c_recv(dev, recv_buf, 1);
2864d2cd2d8SGlenn Miles 
2874d2cd2d8SGlenn Miles     /* write new value to LSx */
2884d2cd2d8SGlenn Miles     send_buf[1] = (recv_buf[0] & mask) | new_value;
2894d2cd2d8SGlenn Miles     pnv_i2c_send(dev, send_buf, 2);
2904d2cd2d8SGlenn Miles }
2914d2cd2d8SGlenn Miles 
pnv_i2c_pca9552_read_pins(PnvI2cDev * dev)2924d2cd2d8SGlenn Miles static uint16_t pnv_i2c_pca9552_read_pins(PnvI2cDev *dev)
2934d2cd2d8SGlenn Miles {
2944d2cd2d8SGlenn Miles     uint8_t send_buf[2];
2954d2cd2d8SGlenn Miles     uint8_t recv_buf[2];
2964d2cd2d8SGlenn Miles     uint16_t inputs;
2974d2cd2d8SGlenn Miles     send_buf[0] = PCA9552_INPUT0;
2984d2cd2d8SGlenn Miles     pnv_i2c_send(dev, send_buf, 1);
2994d2cd2d8SGlenn Miles     pnv_i2c_recv(dev, recv_buf, 1);
3004d2cd2d8SGlenn Miles     inputs = recv_buf[0];
3014d2cd2d8SGlenn Miles     send_buf[0] = PCA9552_INPUT1;
3024d2cd2d8SGlenn Miles     pnv_i2c_send(dev, send_buf, 1);
3034d2cd2d8SGlenn Miles     pnv_i2c_recv(dev, recv_buf, 1);
3044d2cd2d8SGlenn Miles     inputs |= recv_buf[0] << 8;
3054d2cd2d8SGlenn Miles     return inputs;
3064d2cd2d8SGlenn Miles }
3074d2cd2d8SGlenn Miles 
pnv_i2c_pca9552_default_inputs(PnvI2cDev * dev)3084d2cd2d8SGlenn Miles static void pnv_i2c_pca9552_default_inputs(PnvI2cDev *dev)
3094d2cd2d8SGlenn Miles {
3104d2cd2d8SGlenn Miles     uint16_t pin_values = pnv_i2c_pca9552_read_pins(dev);
3114d2cd2d8SGlenn Miles     g_assert_cmphex(pin_values, ==, 0xffff);
3124d2cd2d8SGlenn Miles }
3134d2cd2d8SGlenn Miles 
3144d2cd2d8SGlenn Miles /*
3154d2cd2d8SGlenn Miles  * Set pins 0-4 one at a time and verify that pins 5-9 are
3164d2cd2d8SGlenn Miles  * set to the same value
3174d2cd2d8SGlenn Miles  */
pnv_i2c_pca552_set_pins(PnvI2cDev * dev)3184d2cd2d8SGlenn Miles static void pnv_i2c_pca552_set_pins(PnvI2cDev *dev)
3194d2cd2d8SGlenn Miles {
3204d2cd2d8SGlenn Miles     uint16_t pin_values;
3214d2cd2d8SGlenn Miles 
3224d2cd2d8SGlenn Miles     /* set pin 0 low */
3234d2cd2d8SGlenn Miles     pnv_i2c_pca9552_set_pin(dev, 0, 0);
3244d2cd2d8SGlenn Miles     pin_values = pnv_i2c_pca9552_read_pins(dev);
3254d2cd2d8SGlenn Miles 
3264d2cd2d8SGlenn Miles     /* pins 0 and 5 should be low */
3274d2cd2d8SGlenn Miles     g_assert_cmphex(pin_values, ==, 0xffde);
3284d2cd2d8SGlenn Miles 
3294d2cd2d8SGlenn Miles     /* set pin 1 low */
3304d2cd2d8SGlenn Miles     pnv_i2c_pca9552_set_pin(dev, 1, 0);
3314d2cd2d8SGlenn Miles     pin_values = pnv_i2c_pca9552_read_pins(dev);
3324d2cd2d8SGlenn Miles 
3334d2cd2d8SGlenn Miles     /* pins 0, 1, 5 and 6 should be low */
3344d2cd2d8SGlenn Miles     g_assert_cmphex(pin_values, ==, 0xff9c);
3354d2cd2d8SGlenn Miles 
3364d2cd2d8SGlenn Miles     /* set pin 2 low */
3374d2cd2d8SGlenn Miles     pnv_i2c_pca9552_set_pin(dev, 2, 0);
3384d2cd2d8SGlenn Miles     pin_values = pnv_i2c_pca9552_read_pins(dev);
3394d2cd2d8SGlenn Miles 
3404d2cd2d8SGlenn Miles     /* pins 0, 1, 2, 5, 6 and 7 should be low */
3414d2cd2d8SGlenn Miles     g_assert_cmphex(pin_values, ==, 0xff18);
3424d2cd2d8SGlenn Miles 
3434d2cd2d8SGlenn Miles     /* set pin 3 low */
3444d2cd2d8SGlenn Miles     pnv_i2c_pca9552_set_pin(dev, 3, 0);
3454d2cd2d8SGlenn Miles     pin_values = pnv_i2c_pca9552_read_pins(dev);
3464d2cd2d8SGlenn Miles 
3474d2cd2d8SGlenn Miles     /* pins 0, 1, 2, 3, 5, 6, 7 and 8 should be low */
3484d2cd2d8SGlenn Miles     g_assert_cmphex(pin_values, ==, 0xfe10);
3494d2cd2d8SGlenn Miles 
3504d2cd2d8SGlenn Miles     /* set pin 4 low */
3514d2cd2d8SGlenn Miles     pnv_i2c_pca9552_set_pin(dev, 4, 0);
3524d2cd2d8SGlenn Miles     pin_values = pnv_i2c_pca9552_read_pins(dev);
3534d2cd2d8SGlenn Miles 
3544d2cd2d8SGlenn Miles     /* pins 0, 1, 2, 3, 5, 6, 7, 8 and 9 should be low */
3554d2cd2d8SGlenn Miles     g_assert_cmphex(pin_values, ==, 0xfc00);
3564d2cd2d8SGlenn Miles 
3574d2cd2d8SGlenn Miles     /* reset all pins to the high state */
3584d2cd2d8SGlenn Miles     pnv_i2c_pca9552_default_cfg(dev);
3594d2cd2d8SGlenn Miles     pin_values = pnv_i2c_pca9552_read_pins(dev);
3604d2cd2d8SGlenn Miles 
3614d2cd2d8SGlenn Miles     /* verify all pins went back to the high state */
3624d2cd2d8SGlenn Miles     g_assert_cmphex(pin_values, ==, 0xffff);
3634d2cd2d8SGlenn Miles }
3644d2cd2d8SGlenn Miles 
reset_engine(PnvI2cCtlr * ctlr)3654d2cd2d8SGlenn Miles static void reset_engine(PnvI2cCtlr *ctlr)
3664d2cd2d8SGlenn Miles {
3674d2cd2d8SGlenn Miles     pnv_i2c_xscom_write(ctlr, I2C_RESET_I2C_REG, 0);
3684d2cd2d8SGlenn Miles }
3694d2cd2d8SGlenn Miles 
check_i2cm_por_regs(QTestState * qts,const PnvChip * chip)3704d2cd2d8SGlenn Miles static void check_i2cm_por_regs(QTestState *qts, const PnvChip *chip)
3714d2cd2d8SGlenn Miles {
3724d2cd2d8SGlenn Miles     int engine;
3734d2cd2d8SGlenn Miles     for (engine = 0; engine < chip->num_i2c; engine++) {
3744d2cd2d8SGlenn Miles         PnvI2cCtlr ctlr;
3754d2cd2d8SGlenn Miles         ctlr.qts = qts;
3764d2cd2d8SGlenn Miles         ctlr.chip = chip;
3774d2cd2d8SGlenn Miles         ctlr.engine = engine;
3784d2cd2d8SGlenn Miles 
3794d2cd2d8SGlenn Miles         /* Check version in Extended Status Register */
3804d2cd2d8SGlenn Miles         uint64_t value = pnv_i2c_xscom_read(&ctlr, I2C_EXTD_STAT_REG);
3814d2cd2d8SGlenn Miles         g_assert_cmphex(value & I2C_EXTD_STAT_I2C_VERSION, ==, 0x1700000000);
3824d2cd2d8SGlenn Miles 
3834d2cd2d8SGlenn Miles         /* Check for command complete and bus idle in Status Register */
3844d2cd2d8SGlenn Miles         value = pnv_i2c_xscom_read(&ctlr, I2C_STAT_REG);
3854d2cd2d8SGlenn Miles         g_assert_cmphex(value & (I2C_STAT_ANY_ERR | I2C_STAT_CMD_COMP),
3864d2cd2d8SGlenn Miles                         ==,
3874d2cd2d8SGlenn Miles                         I2C_STAT_CMD_COMP);
3884d2cd2d8SGlenn Miles     }
3894d2cd2d8SGlenn Miles }
3904d2cd2d8SGlenn Miles 
reset_all(QTestState * qts,const PnvChip * chip)3914d2cd2d8SGlenn Miles static void reset_all(QTestState *qts, const PnvChip *chip)
3924d2cd2d8SGlenn Miles {
3934d2cd2d8SGlenn Miles     int engine;
3944d2cd2d8SGlenn Miles     for (engine = 0; engine < chip->num_i2c; engine++) {
3954d2cd2d8SGlenn Miles         PnvI2cCtlr ctlr;
3964d2cd2d8SGlenn Miles         ctlr.qts = qts;
3974d2cd2d8SGlenn Miles         ctlr.chip = chip;
3984d2cd2d8SGlenn Miles         ctlr.engine = engine;
3994d2cd2d8SGlenn Miles         reset_engine(&ctlr);
4004d2cd2d8SGlenn Miles         pnv_i2c_xscom_write(&ctlr, I2C_MODE_REG, 0x02be040000000000);
4014d2cd2d8SGlenn Miles     }
4024d2cd2d8SGlenn Miles }
4034d2cd2d8SGlenn Miles 
test_host_i2c(const void * data)4044d2cd2d8SGlenn Miles static void test_host_i2c(const void *data)
4054d2cd2d8SGlenn Miles {
4064d2cd2d8SGlenn Miles     const PnvChip *chip = data;
4074d2cd2d8SGlenn Miles     QTestState *qts;
4084d2cd2d8SGlenn Miles     const char *machine = "powernv8";
4094d2cd2d8SGlenn Miles     PnvI2cCtlr ctlr;
4104d2cd2d8SGlenn Miles     PnvI2cDev pca9552;
4114d2cd2d8SGlenn Miles     PnvI2cDev pca9554;
4124d2cd2d8SGlenn Miles 
4134d2cd2d8SGlenn Miles     if (chip->chip_type == PNV_CHIP_POWER9) {
4144d2cd2d8SGlenn Miles         machine = "powernv9";
4154d2cd2d8SGlenn Miles     } else if (chip->chip_type == PNV_CHIP_POWER10) {
4164d2cd2d8SGlenn Miles         machine = "powernv10-rainier";
4174d2cd2d8SGlenn Miles     }
4184d2cd2d8SGlenn Miles 
4194d2cd2d8SGlenn Miles     qts = qtest_initf("-M %s -smp %d,cores=1,threads=%d -nographic "
4204d2cd2d8SGlenn Miles                       "-nodefaults -serial mon:stdio -S "
4214d2cd2d8SGlenn Miles                       "-d guest_errors",
4224d2cd2d8SGlenn Miles                       machine, SMT, SMT);
4234d2cd2d8SGlenn Miles 
4244d2cd2d8SGlenn Miles     /* Check the I2C master status registers after POR */
4254d2cd2d8SGlenn Miles     check_i2cm_por_regs(qts, chip);
4264d2cd2d8SGlenn Miles 
4274d2cd2d8SGlenn Miles     /* Now do a forced "immediate" reset on all engines */
4284d2cd2d8SGlenn Miles     reset_all(qts, chip);
4294d2cd2d8SGlenn Miles 
4304d2cd2d8SGlenn Miles     /* Check that the status values are still good */
4314d2cd2d8SGlenn Miles     check_i2cm_por_regs(qts, chip);
4324d2cd2d8SGlenn Miles 
4334d2cd2d8SGlenn Miles     /* P9 doesn't have any i2c devices attached at this time */
4344d2cd2d8SGlenn Miles     if (chip->chip_type != PNV_CHIP_POWER10) {
4354d2cd2d8SGlenn Miles         qtest_quit(qts);
4364d2cd2d8SGlenn Miles         return;
4374d2cd2d8SGlenn Miles     }
4384d2cd2d8SGlenn Miles 
4394d2cd2d8SGlenn Miles     /* Initialize for a P10 pca9552 hotplug device */
4404d2cd2d8SGlenn Miles     ctlr.qts = qts;
4414d2cd2d8SGlenn Miles     ctlr.chip = chip;
4424d2cd2d8SGlenn Miles     ctlr.engine = 2;
4434d2cd2d8SGlenn Miles     pca9552.ctlr = &ctlr;
4444d2cd2d8SGlenn Miles     pca9552.port = 1;
4454d2cd2d8SGlenn Miles     pca9552.addr = 0x63;
4464d2cd2d8SGlenn Miles 
4474d2cd2d8SGlenn Miles     /* Set all pca9552 pins as inputs */
4484d2cd2d8SGlenn Miles     pnv_i2c_pca9552_default_cfg(&pca9552);
4494d2cd2d8SGlenn Miles 
4504d2cd2d8SGlenn Miles     /* Check that all pins of the pca9552 are high */
4514d2cd2d8SGlenn Miles     pnv_i2c_pca9552_default_inputs(&pca9552);
4524d2cd2d8SGlenn Miles 
4534d2cd2d8SGlenn Miles     /* perform individual pin tests */
4544d2cd2d8SGlenn Miles     pnv_i2c_pca552_set_pins(&pca9552);
4554d2cd2d8SGlenn Miles 
4564d2cd2d8SGlenn Miles     /* Initialize for a P10 pca9554 CableCard Presence detection device */
4574d2cd2d8SGlenn Miles     pca9554.ctlr = &ctlr;
4584d2cd2d8SGlenn Miles     pca9554.port = 1;
4594d2cd2d8SGlenn Miles     pca9554.addr = 0x25;
4604d2cd2d8SGlenn Miles 
4614d2cd2d8SGlenn Miles     /* Set all pca9554 pins as inputs */
4624d2cd2d8SGlenn Miles     pnv_i2c_pca9554_default_cfg(&pca9554);
4634d2cd2d8SGlenn Miles 
4644d2cd2d8SGlenn Miles     /* Check that all pins of the pca9554 are high */
4654d2cd2d8SGlenn Miles     pnv_i2c_pca9554_default_inputs(&pca9554);
4664d2cd2d8SGlenn Miles 
4674d2cd2d8SGlenn Miles     /* perform individual pin tests */
4684d2cd2d8SGlenn Miles     pnv_i2c_pca554_set_pins(&pca9554);
4694d2cd2d8SGlenn Miles 
4704d2cd2d8SGlenn Miles     qtest_quit(qts);
4714d2cd2d8SGlenn Miles }
4724d2cd2d8SGlenn Miles 
add_test(const char * name,void (* test)(const void * data))4734d2cd2d8SGlenn Miles static void add_test(const char *name, void (*test)(const void *data))
4744d2cd2d8SGlenn Miles {
4754d2cd2d8SGlenn Miles     int i;
4764d2cd2d8SGlenn Miles 
4774d2cd2d8SGlenn Miles     for (i = 0; i < ARRAY_SIZE(pnv_chips); i++) {
4784d2cd2d8SGlenn Miles         char *tname = g_strdup_printf("pnv-xscom/%s/%s", name,
4794d2cd2d8SGlenn Miles                                       pnv_chips[i].cpu_model);
4804d2cd2d8SGlenn Miles         qtest_add_data_func(tname, &pnv_chips[i], test);
4814d2cd2d8SGlenn Miles         g_free(tname);
4824d2cd2d8SGlenn Miles     }
4834d2cd2d8SGlenn Miles }
4844d2cd2d8SGlenn Miles 
main(int argc,char ** argv)4854d2cd2d8SGlenn Miles int main(int argc, char **argv)
4864d2cd2d8SGlenn Miles {
4874d2cd2d8SGlenn Miles     g_test_init(&argc, &argv, NULL);
4884d2cd2d8SGlenn Miles 
4894d2cd2d8SGlenn Miles     add_test("host-i2c", test_host_i2c);
4904d2cd2d8SGlenn Miles     return g_test_run();
4914d2cd2d8SGlenn Miles }
492