18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * i2c-au1550.c: SMBus (i2c) adapter for Alchemy PSC interface 48c2ecf20Sopenharmony_ci * Copyright (C) 2004 Embedded Edge, LLC <dan@embeddededge.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * 2.6 port by Matt Porter <mporter@kernel.crashing.org> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * The documentation describes this as an SMBus controller, but it doesn't 98c2ecf20Sopenharmony_ci * understand any of the SMBus protocol in hardware. It's really an I2C 108c2ecf20Sopenharmony_ci * controller that could emulate most of the SMBus in software. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This is just a skeleton adapter to use with the Au1550 PSC 138c2ecf20Sopenharmony_ci * algorithm. It was developed for the Pb1550, but will work with 148c2ecf20Sopenharmony_ci * any Au1550 board that has a similar PSC configuration. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci#include <linux/errno.h> 228c2ecf20Sopenharmony_ci#include <linux/i2c.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1000.h> 268c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1xxx_psc.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define PSC_SEL 0x00 298c2ecf20Sopenharmony_ci#define PSC_CTRL 0x04 308c2ecf20Sopenharmony_ci#define PSC_SMBCFG 0x08 318c2ecf20Sopenharmony_ci#define PSC_SMBMSK 0x0C 328c2ecf20Sopenharmony_ci#define PSC_SMBPCR 0x10 338c2ecf20Sopenharmony_ci#define PSC_SMBSTAT 0x14 348c2ecf20Sopenharmony_ci#define PSC_SMBEVNT 0x18 358c2ecf20Sopenharmony_ci#define PSC_SMBTXRX 0x1C 368c2ecf20Sopenharmony_ci#define PSC_SMBTMR 0x20 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct i2c_au1550_data { 398c2ecf20Sopenharmony_ci void __iomem *psc_base; 408c2ecf20Sopenharmony_ci int xfer_timeout; 418c2ecf20Sopenharmony_ci struct i2c_adapter adap; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic inline void WR(struct i2c_au1550_data *a, int r, unsigned long v) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci __raw_writel(v, a->psc_base + r); 478c2ecf20Sopenharmony_ci wmb(); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic inline unsigned long RD(struct i2c_au1550_data *a, int r) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci return __raw_readl(a->psc_base + r); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int wait_xfer_done(struct i2c_au1550_data *adap) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci int i; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* Wait for Tx Buffer Empty */ 608c2ecf20Sopenharmony_ci for (i = 0; i < adap->xfer_timeout; i++) { 618c2ecf20Sopenharmony_ci if (RD(adap, PSC_SMBSTAT) & PSC_SMBSTAT_TE) 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci udelay(1); 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return -ETIMEDOUT; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int wait_ack(struct i2c_au1550_data *adap) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci unsigned long stat; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (wait_xfer_done(adap)) 758c2ecf20Sopenharmony_ci return -ETIMEDOUT; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci stat = RD(adap, PSC_SMBEVNT); 788c2ecf20Sopenharmony_ci if ((stat & (PSC_SMBEVNT_DN | PSC_SMBEVNT_AN | PSC_SMBEVNT_AL)) != 0) 798c2ecf20Sopenharmony_ci return -ETIMEDOUT; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int wait_master_done(struct i2c_au1550_data *adap) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci int i; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* Wait for Master Done. */ 898c2ecf20Sopenharmony_ci for (i = 0; i < 2 * adap->xfer_timeout; i++) { 908c2ecf20Sopenharmony_ci if ((RD(adap, PSC_SMBEVNT) & PSC_SMBEVNT_MD) != 0) 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci udelay(1); 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return -ETIMEDOUT; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int 998c2ecf20Sopenharmony_cido_address(struct i2c_au1550_data *adap, unsigned int addr, int rd, int q) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci unsigned long stat; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* Reset the FIFOs, clear events. */ 1048c2ecf20Sopenharmony_ci stat = RD(adap, PSC_SMBSTAT); 1058c2ecf20Sopenharmony_ci WR(adap, PSC_SMBEVNT, PSC_SMBEVNT_ALLCLR); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (!(stat & PSC_SMBSTAT_TE) || !(stat & PSC_SMBSTAT_RE)) { 1088c2ecf20Sopenharmony_ci WR(adap, PSC_SMBPCR, PSC_SMBPCR_DC); 1098c2ecf20Sopenharmony_ci while ((RD(adap, PSC_SMBPCR) & PSC_SMBPCR_DC) != 0) 1108c2ecf20Sopenharmony_ci cpu_relax(); 1118c2ecf20Sopenharmony_ci udelay(50); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Write out the i2c chip address and specify operation */ 1158c2ecf20Sopenharmony_ci addr <<= 1; 1168c2ecf20Sopenharmony_ci if (rd) 1178c2ecf20Sopenharmony_ci addr |= 1; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* zero-byte xfers stop immediately */ 1208c2ecf20Sopenharmony_ci if (q) 1218c2ecf20Sopenharmony_ci addr |= PSC_SMBTXRX_STP; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Put byte into fifo, start up master. */ 1248c2ecf20Sopenharmony_ci WR(adap, PSC_SMBTXRX, addr); 1258c2ecf20Sopenharmony_ci WR(adap, PSC_SMBPCR, PSC_SMBPCR_MS); 1268c2ecf20Sopenharmony_ci if (wait_ack(adap)) 1278c2ecf20Sopenharmony_ci return -EIO; 1288c2ecf20Sopenharmony_ci return (q) ? wait_master_done(adap) : 0; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int wait_for_rx_byte(struct i2c_au1550_data *adap, unsigned char *out) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci int j; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (wait_xfer_done(adap)) 1368c2ecf20Sopenharmony_ci return -EIO; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci j = adap->xfer_timeout * 100; 1398c2ecf20Sopenharmony_ci do { 1408c2ecf20Sopenharmony_ci j--; 1418c2ecf20Sopenharmony_ci if (j <= 0) 1428c2ecf20Sopenharmony_ci return -EIO; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if ((RD(adap, PSC_SMBSTAT) & PSC_SMBSTAT_RE) == 0) 1458c2ecf20Sopenharmony_ci j = 0; 1468c2ecf20Sopenharmony_ci else 1478c2ecf20Sopenharmony_ci udelay(1); 1488c2ecf20Sopenharmony_ci } while (j > 0); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci *out = RD(adap, PSC_SMBTXRX); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int i2c_read(struct i2c_au1550_data *adap, unsigned char *buf, 1568c2ecf20Sopenharmony_ci unsigned int len) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci int i; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (len == 0) 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* A read is performed by stuffing the transmit fifo with 1648c2ecf20Sopenharmony_ci * zero bytes for timing, waiting for bytes to appear in the 1658c2ecf20Sopenharmony_ci * receive fifo, then reading the bytes. 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci i = 0; 1688c2ecf20Sopenharmony_ci while (i < (len - 1)) { 1698c2ecf20Sopenharmony_ci WR(adap, PSC_SMBTXRX, 0); 1708c2ecf20Sopenharmony_ci if (wait_for_rx_byte(adap, &buf[i])) 1718c2ecf20Sopenharmony_ci return -EIO; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci i++; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* The last byte has to indicate transfer done. */ 1778c2ecf20Sopenharmony_ci WR(adap, PSC_SMBTXRX, PSC_SMBTXRX_STP); 1788c2ecf20Sopenharmony_ci if (wait_master_done(adap)) 1798c2ecf20Sopenharmony_ci return -EIO; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci buf[i] = (unsigned char)(RD(adap, PSC_SMBTXRX) & 0xff); 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int i2c_write(struct i2c_au1550_data *adap, unsigned char *buf, 1868c2ecf20Sopenharmony_ci unsigned int len) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci int i; 1898c2ecf20Sopenharmony_ci unsigned long data; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (len == 0) 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci i = 0; 1958c2ecf20Sopenharmony_ci while (i < (len-1)) { 1968c2ecf20Sopenharmony_ci data = buf[i]; 1978c2ecf20Sopenharmony_ci WR(adap, PSC_SMBTXRX, data); 1988c2ecf20Sopenharmony_ci if (wait_ack(adap)) 1998c2ecf20Sopenharmony_ci return -EIO; 2008c2ecf20Sopenharmony_ci i++; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* The last byte has to indicate transfer done. */ 2048c2ecf20Sopenharmony_ci data = buf[i]; 2058c2ecf20Sopenharmony_ci data |= PSC_SMBTXRX_STP; 2068c2ecf20Sopenharmony_ci WR(adap, PSC_SMBTXRX, data); 2078c2ecf20Sopenharmony_ci if (wait_master_done(adap)) 2088c2ecf20Sopenharmony_ci return -EIO; 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int 2138c2ecf20Sopenharmony_ciau1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct i2c_au1550_data *adap = i2c_adap->algo_data; 2168c2ecf20Sopenharmony_ci struct i2c_msg *p; 2178c2ecf20Sopenharmony_ci int i, err = 0; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci WR(adap, PSC_CTRL, PSC_CTRL_ENABLE); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci for (i = 0; !err && i < num; i++) { 2228c2ecf20Sopenharmony_ci p = &msgs[i]; 2238c2ecf20Sopenharmony_ci err = do_address(adap, p->addr, p->flags & I2C_M_RD, 2248c2ecf20Sopenharmony_ci (p->len == 0)); 2258c2ecf20Sopenharmony_ci if (err || !p->len) 2268c2ecf20Sopenharmony_ci continue; 2278c2ecf20Sopenharmony_ci if (p->flags & I2C_M_RD) 2288c2ecf20Sopenharmony_ci err = i2c_read(adap, p->buf, p->len); 2298c2ecf20Sopenharmony_ci else 2308c2ecf20Sopenharmony_ci err = i2c_write(adap, p->buf, p->len); 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci /* Return the number of messages processed, or the error code. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_ci if (err == 0) 2368c2ecf20Sopenharmony_ci err = num; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci WR(adap, PSC_CTRL, PSC_CTRL_SUSPEND); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return err; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic u32 au1550_func(struct i2c_adapter *adap) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic const struct i2c_algorithm au1550_algo = { 2498c2ecf20Sopenharmony_ci .master_xfer = au1550_xfer, 2508c2ecf20Sopenharmony_ci .functionality = au1550_func, 2518c2ecf20Sopenharmony_ci}; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic void i2c_au1550_setup(struct i2c_au1550_data *priv) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci unsigned long cfg; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci WR(priv, PSC_CTRL, PSC_CTRL_DISABLE); 2588c2ecf20Sopenharmony_ci WR(priv, PSC_SEL, PSC_SEL_PS_SMBUSMODE); 2598c2ecf20Sopenharmony_ci WR(priv, PSC_SMBCFG, 0); 2608c2ecf20Sopenharmony_ci WR(priv, PSC_CTRL, PSC_CTRL_ENABLE); 2618c2ecf20Sopenharmony_ci while ((RD(priv, PSC_SMBSTAT) & PSC_SMBSTAT_SR) == 0) 2628c2ecf20Sopenharmony_ci cpu_relax(); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci cfg = PSC_SMBCFG_RT_FIFO8 | PSC_SMBCFG_TT_FIFO8 | PSC_SMBCFG_DD_DISABLE; 2658c2ecf20Sopenharmony_ci WR(priv, PSC_SMBCFG, cfg); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* Divide by 8 to get a 6.25 MHz clock. The later protocol 2688c2ecf20Sopenharmony_ci * timings are based on this clock. 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_ci cfg |= PSC_SMBCFG_SET_DIV(PSC_SMBCFG_DIV8); 2718c2ecf20Sopenharmony_ci WR(priv, PSC_SMBCFG, cfg); 2728c2ecf20Sopenharmony_ci WR(priv, PSC_SMBMSK, PSC_SMBMSK_ALLMASK); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* Set the protocol timer values. See Table 71 in the 2758c2ecf20Sopenharmony_ci * Au1550 Data Book for standard timing values. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci WR(priv, PSC_SMBTMR, PSC_SMBTMR_SET_TH(0) | PSC_SMBTMR_SET_PS(20) | \ 2788c2ecf20Sopenharmony_ci PSC_SMBTMR_SET_PU(20) | PSC_SMBTMR_SET_SH(20) | \ 2798c2ecf20Sopenharmony_ci PSC_SMBTMR_SET_SU(20) | PSC_SMBTMR_SET_CL(20) | \ 2808c2ecf20Sopenharmony_ci PSC_SMBTMR_SET_CH(20)); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci cfg |= PSC_SMBCFG_DE_ENABLE; 2838c2ecf20Sopenharmony_ci WR(priv, PSC_SMBCFG, cfg); 2848c2ecf20Sopenharmony_ci while ((RD(priv, PSC_SMBSTAT) & PSC_SMBSTAT_SR) == 0) 2858c2ecf20Sopenharmony_ci cpu_relax(); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci WR(priv, PSC_CTRL, PSC_CTRL_SUSPEND); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic void i2c_au1550_disable(struct i2c_au1550_data *priv) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci WR(priv, PSC_SMBCFG, 0); 2938c2ecf20Sopenharmony_ci WR(priv, PSC_CTRL, PSC_CTRL_DISABLE); 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci/* 2978c2ecf20Sopenharmony_ci * registering functions to load algorithms at runtime 2988c2ecf20Sopenharmony_ci * Prior to calling us, the 50MHz clock frequency and routing 2998c2ecf20Sopenharmony_ci * must have been set up for the PSC indicated by the adapter. 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_cistatic int 3028c2ecf20Sopenharmony_cii2c_au1550_probe(struct platform_device *pdev) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct i2c_au1550_data *priv; 3058c2ecf20Sopenharmony_ci struct resource *r; 3068c2ecf20Sopenharmony_ci int ret; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(struct i2c_au1550_data), 3098c2ecf20Sopenharmony_ci GFP_KERNEL); 3108c2ecf20Sopenharmony_ci if (!priv) 3118c2ecf20Sopenharmony_ci return -ENOMEM; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3148c2ecf20Sopenharmony_ci priv->psc_base = devm_ioremap_resource(&pdev->dev, r); 3158c2ecf20Sopenharmony_ci if (IS_ERR(priv->psc_base)) 3168c2ecf20Sopenharmony_ci return PTR_ERR(priv->psc_base); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci priv->xfer_timeout = 200; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci priv->adap.nr = pdev->id; 3218c2ecf20Sopenharmony_ci priv->adap.algo = &au1550_algo; 3228c2ecf20Sopenharmony_ci priv->adap.algo_data = priv; 3238c2ecf20Sopenharmony_ci priv->adap.dev.parent = &pdev->dev; 3248c2ecf20Sopenharmony_ci strlcpy(priv->adap.name, "Au1xxx PSC I2C", sizeof(priv->adap.name)); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* Now, set up the PSC for SMBus PIO mode. */ 3278c2ecf20Sopenharmony_ci i2c_au1550_setup(priv); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci ret = i2c_add_numbered_adapter(&priv->adap); 3308c2ecf20Sopenharmony_ci if (ret) { 3318c2ecf20Sopenharmony_ci i2c_au1550_disable(priv); 3328c2ecf20Sopenharmony_ci return ret; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 3368c2ecf20Sopenharmony_ci return 0; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int i2c_au1550_remove(struct platform_device *pdev) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct i2c_au1550_data *priv = platform_get_drvdata(pdev); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci i2c_del_adapter(&priv->adap); 3448c2ecf20Sopenharmony_ci i2c_au1550_disable(priv); 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3498c2ecf20Sopenharmony_cistatic int i2c_au1550_suspend(struct device *dev) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct i2c_au1550_data *priv = dev_get_drvdata(dev); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci i2c_au1550_disable(priv); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int i2c_au1550_resume(struct device *dev) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct i2c_au1550_data *priv = dev_get_drvdata(dev); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci i2c_au1550_setup(priv); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic const struct dev_pm_ops i2c_au1550_pmops = { 3688c2ecf20Sopenharmony_ci .suspend = i2c_au1550_suspend, 3698c2ecf20Sopenharmony_ci .resume = i2c_au1550_resume, 3708c2ecf20Sopenharmony_ci}; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci#define AU1XPSC_SMBUS_PMOPS (&i2c_au1550_pmops) 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci#else 3758c2ecf20Sopenharmony_ci#define AU1XPSC_SMBUS_PMOPS NULL 3768c2ecf20Sopenharmony_ci#endif 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic struct platform_driver au1xpsc_smbus_driver = { 3798c2ecf20Sopenharmony_ci .driver = { 3808c2ecf20Sopenharmony_ci .name = "au1xpsc_smbus", 3818c2ecf20Sopenharmony_ci .pm = AU1XPSC_SMBUS_PMOPS, 3828c2ecf20Sopenharmony_ci }, 3838c2ecf20Sopenharmony_ci .probe = i2c_au1550_probe, 3848c2ecf20Sopenharmony_ci .remove = i2c_au1550_remove, 3858c2ecf20Sopenharmony_ci}; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cimodule_platform_driver(au1xpsc_smbus_driver); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ciMODULE_AUTHOR("Dan Malek, Embedded Edge, LLC."); 3908c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SMBus adapter Alchemy pb1550"); 3918c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3928c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:au1xpsc_smbus"); 393