xref: /netbsd-src/sys/arch/mips/alchemy/dev/ausmbus_psc.c (revision 4066ef2218b7d7a605ca8031f7897395da0a5bb8)
1*4066ef22Sandvar /* $NetBSD: ausmbus_psc.c,v 1.16 2022/06/18 22:11:00 andvar Exp $ */
24f9e38e9Sshige 
34f9e38e9Sshige /*-
44f9e38e9Sshige  * Copyright (c) 2006 Shigeyuki Fukushima.
54f9e38e9Sshige  * All rights reserved.
64f9e38e9Sshige  *
74f9e38e9Sshige  * Written by Shigeyuki Fukushima.
84f9e38e9Sshige  *
94f9e38e9Sshige  * Redistribution and use in source and binary forms, with or without
104f9e38e9Sshige  * modification, are permitted provided that the following conditions
114f9e38e9Sshige  * are met:
124f9e38e9Sshige  * 1. Redistributions of source code must retain the above copyright
134f9e38e9Sshige  *    notice, this list of conditions and the following disclaimer.
144f9e38e9Sshige  * 2. Redistributions in binary form must reproduce the above
154f9e38e9Sshige  *    copyright notice, this list of conditions and the following
164f9e38e9Sshige  *    disclaimer in the documentation and/or other materials provided
174f9e38e9Sshige  *    with the distribution.
184f9e38e9Sshige  * 3. The name of the author may not be used to endorse or promote
194f9e38e9Sshige  *    products derived from this software without specific prior
204f9e38e9Sshige  *    written permission.
214f9e38e9Sshige  *
224f9e38e9Sshige  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
234f9e38e9Sshige  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
244f9e38e9Sshige  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
254f9e38e9Sshige  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
264f9e38e9Sshige  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
274f9e38e9Sshige  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
284f9e38e9Sshige  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
294f9e38e9Sshige  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
304f9e38e9Sshige  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
314f9e38e9Sshige  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
324f9e38e9Sshige  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
334f9e38e9Sshige  */
344f9e38e9Sshige 
354f9e38e9Sshige #include <sys/cdefs.h>
36*4066ef22Sandvar __KERNEL_RCSID(0, "$NetBSD: ausmbus_psc.c,v 1.16 2022/06/18 22:11:00 andvar Exp $");
374f9e38e9Sshige 
384f9e38e9Sshige #include "locators.h"
394f9e38e9Sshige 
404f9e38e9Sshige #include <sys/param.h>
414f9e38e9Sshige #include <sys/systm.h>
424f9e38e9Sshige #include <sys/device.h>
434f9e38e9Sshige #include <sys/errno.h>
444f9e38e9Sshige 
45e265f67bSdyoung #include <sys/bus.h>
464f9e38e9Sshige #include <machine/cpu.h>
474f9e38e9Sshige 
484f9e38e9Sshige #include <mips/alchemy/dev/aupscreg.h>
494f9e38e9Sshige #include <mips/alchemy/dev/aupscvar.h>
5097e07f10Sshige #include <mips/alchemy/dev/ausmbus_pscreg.h>
514f9e38e9Sshige 
524f9e38e9Sshige #include <dev/i2c/i2cvar.h>
534f9e38e9Sshige #include <dev/i2c/i2c_bitbang.h>
544f9e38e9Sshige 
554f9e38e9Sshige struct ausmbus_softc {
56f58fcf6aSkiyohara 	device_t			sc_dev;
574f9e38e9Sshige 
584f9e38e9Sshige 	/* protocol comoon fields */
594f9e38e9Sshige 	struct aupsc_controller		sc_ctrl;
604f9e38e9Sshige 
614f9e38e9Sshige 	/* protocol specific fields */
624f9e38e9Sshige 	struct i2c_controller		sc_i2c;
634f9e38e9Sshige 	i2c_addr_t			sc_smbus_slave_addr;
644f9e38e9Sshige 	int				sc_smbus_timeout;
654f9e38e9Sshige };
664f9e38e9Sshige 
674f9e38e9Sshige #define	ausmbus_reg_read(sc, reg) \
684f9e38e9Sshige 	bus_space_read_4(sc->sc_ctrl.psc_bust, sc->sc_ctrl.psc_bush, reg)
694f9e38e9Sshige #define	ausmbus_reg_write(sc, reg, val) \
70ff41ffc5Sshige 	bus_space_write_4(sc->sc_ctrl.psc_bust, sc->sc_ctrl.psc_bush, reg, \
71ff41ffc5Sshige 		val); \
72ff41ffc5Sshige 	delay(100);
734f9e38e9Sshige 
74f58fcf6aSkiyohara static int	ausmbus_match(device_t, struct cfdata *, void *);
75f58fcf6aSkiyohara static void	ausmbus_attach(device_t, device_t, void *);
764f9e38e9Sshige 
77f58fcf6aSkiyohara CFATTACH_DECL_NEW(ausmbus, sizeof(struct ausmbus_softc),
784f9e38e9Sshige 	ausmbus_match, ausmbus_attach, NULL, NULL);
794f9e38e9Sshige 
80*4066ef22Sandvar /* functions for i2c_controller */
814f9e38e9Sshige static int	ausmbus_acquire_bus(void *, int);
824f9e38e9Sshige static void	ausmbus_release_bus(void *, int);
834f9e38e9Sshige static int	ausmbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
844f9e38e9Sshige 				const void *cmd, size_t cmdlen, void *vbuf,
854f9e38e9Sshige 				size_t buflen, int flags);
864f9e38e9Sshige 
874f9e38e9Sshige /* subroutine functions for i2c_controller */
889929afbcSpgoyette static int	ausmbus_quick_write(struct ausmbus_softc *);
899929afbcSpgoyette static int	ausmbus_quick_read(struct ausmbus_softc *);
904f9e38e9Sshige static int	ausmbus_receive_1(struct ausmbus_softc *, uint8_t *);
914f9e38e9Sshige static int	ausmbus_read_1(struct ausmbus_softc *, uint8_t, uint8_t *);
92383a50d6Skiyohara static int	ausmbus_read_2(struct ausmbus_softc *, uint8_t, uint16_t *);
934f9e38e9Sshige static int	ausmbus_send_1(struct ausmbus_softc *, uint8_t);
944f9e38e9Sshige static int	ausmbus_write_1(struct ausmbus_softc *, uint8_t, uint8_t);
95383a50d6Skiyohara static int	ausmbus_write_2(struct ausmbus_softc *, uint8_t, uint16_t);
964f9e38e9Sshige static int	ausmbus_wait_mastertx(struct ausmbus_softc *sc);
974f9e38e9Sshige static int	ausmbus_wait_masterrx(struct ausmbus_softc *sc);
984f9e38e9Sshige static int	ausmbus_initiate_xfer(void *, i2c_addr_t, int);
994f9e38e9Sshige static int	ausmbus_read_byte(void *arg, uint8_t *vp, int flags);
1004f9e38e9Sshige static int	ausmbus_write_byte(void *arg, uint8_t v, int flags);
1014f9e38e9Sshige 
1024f9e38e9Sshige 
1034f9e38e9Sshige static int
ausmbus_match(device_t parent,struct cfdata * cf,void * aux)104f58fcf6aSkiyohara ausmbus_match(device_t parent, struct cfdata *cf, void *aux)
1054f9e38e9Sshige {
1064f9e38e9Sshige 	struct aupsc_attach_args *aa = (struct aupsc_attach_args *)aux;
1074f9e38e9Sshige 
1084f9e38e9Sshige 	if (strcmp(aa->aupsc_name, cf->cf_name) != 0)
1094f9e38e9Sshige 		return 0;
1104f9e38e9Sshige 
1114f9e38e9Sshige 	return 1;
1124f9e38e9Sshige }
1134f9e38e9Sshige 
1144f9e38e9Sshige static void
ausmbus_attach(device_t parent,device_t self,void * aux)115f58fcf6aSkiyohara ausmbus_attach(device_t parent, device_t self, void *aux)
1164f9e38e9Sshige {
117f58fcf6aSkiyohara 	struct ausmbus_softc *sc = device_private(self);
1184f9e38e9Sshige 	struct aupsc_attach_args *aa = (struct aupsc_attach_args *)aux;
1194f9e38e9Sshige 	struct i2cbus_attach_args iba;
1204f9e38e9Sshige 
1214f9e38e9Sshige 	aprint_normal(": Alchemy PSC SMBus protocol\n");
1224f9e38e9Sshige 
123f58fcf6aSkiyohara 	sc->sc_dev = self;
124f58fcf6aSkiyohara 
1254f9e38e9Sshige 	/* Initialize PSC */
1264f9e38e9Sshige 	sc->sc_ctrl = aa->aupsc_ctrl;
1274f9e38e9Sshige 
1284f9e38e9Sshige 	/* Initialize i2c_controller for SMBus */
129601e1783Sthorpej 	iic_tag_init(&sc->sc_i2c);
1304f9e38e9Sshige 	sc->sc_i2c.ic_cookie = sc;
1314f9e38e9Sshige 	sc->sc_i2c.ic_acquire_bus = ausmbus_acquire_bus;
1324f9e38e9Sshige 	sc->sc_i2c.ic_release_bus = ausmbus_release_bus;
1334f9e38e9Sshige 	sc->sc_i2c.ic_exec = ausmbus_exec;
1344f9e38e9Sshige 	sc->sc_smbus_timeout = 10;
1354f9e38e9Sshige 
1362f02870fSchs 	memset(&iba, 0, sizeof(iba));
1374f9e38e9Sshige 	iba.iba_tag = &sc->sc_i2c;
138c7fb772bSthorpej 	config_found(self, &iba, iicbus_print, CFARGS_NONE);
1394f9e38e9Sshige }
1404f9e38e9Sshige 
1414f9e38e9Sshige static int
ausmbus_acquire_bus(void * arg,int flags)1424f9e38e9Sshige ausmbus_acquire_bus(void *arg, int flags)
1434f9e38e9Sshige {
1444f9e38e9Sshige 	struct ausmbus_softc *sc = arg;
1454f9e38e9Sshige 	uint32_t v;
1464f9e38e9Sshige 
1474f9e38e9Sshige 	/* Select SMBus Protocol & Enable PSC */
1484f9e38e9Sshige 	sc->sc_ctrl.psc_enable(sc, AUPSC_SEL_SMBUS);
1494f9e38e9Sshige 	v = ausmbus_reg_read(sc, AUPSC_SMBSTAT);
1504f9e38e9Sshige 	if ((v & SMBUS_STAT_SR) == 0) {
1514f9e38e9Sshige 		/* PSC is not ready */
1524f9e38e9Sshige 		return -1;
1534f9e38e9Sshige 	}
1544f9e38e9Sshige 
1554f9e38e9Sshige 	/* Setup SMBus Configuration register */
1564f9e38e9Sshige 	v = SMBUS_CFG_DD;				/* Disable DMA */
1574f9e38e9Sshige 	v |= SMBUS_CFG_RT_SET(SMBUS_CFG_RT_FIFO8);	/* Rx FIFO 8data */
1584f9e38e9Sshige 	v |= SMBUS_CFG_TT_SET(SMBUS_CFG_TT_FIFO8);	/* Tx FIFO 8data */
1594f9e38e9Sshige 	v |= SMBUS_CFG_DIV_SET(SMBUS_CFG_DIV8);		/* pscn_mainclk/8 */
1604f9e38e9Sshige 	v &= ~SMBUS_CFG_SFM;				/* Standard Mode */
1614f9e38e9Sshige 	ausmbus_reg_write(sc, AUPSC_SMBCFG, v);
1624f9e38e9Sshige 
1634f9e38e9Sshige 	/* Setup SMBus Protocol Timing register */
1644f9e38e9Sshige 	v = SMBUS_TMR_TH_SET(SMBUS_TMR_STD_TH)
1654f9e38e9Sshige 		| SMBUS_TMR_PS_SET(SMBUS_TMR_STD_PS)
1664f9e38e9Sshige 		| SMBUS_TMR_PU_SET(SMBUS_TMR_STD_PU)
1674f9e38e9Sshige 		| SMBUS_TMR_SH_SET(SMBUS_TMR_STD_SH)
1684f9e38e9Sshige 		| SMBUS_TMR_SU_SET(SMBUS_TMR_STD_SU)
1694f9e38e9Sshige 		| SMBUS_TMR_CL_SET(SMBUS_TMR_STD_CL)
1704f9e38e9Sshige 		| SMBUS_TMR_CH_SET(SMBUS_TMR_STD_CH);
1714f9e38e9Sshige 	ausmbus_reg_write(sc, AUPSC_SMBTMR, v);
1724f9e38e9Sshige 
1734f9e38e9Sshige 	/* Setup SMBus Mask register */
1744f9e38e9Sshige 	v = SMBUS_MSK_ALLMASK;
1754f9e38e9Sshige 	ausmbus_reg_write(sc, AUPSC_SMBMSK, v);
1764f9e38e9Sshige 
1774f9e38e9Sshige 	/* SMBus Enable */
1784f9e38e9Sshige 	v = ausmbus_reg_read(sc, AUPSC_SMBCFG);
1794f9e38e9Sshige 	v |= SMBUS_CFG_DE;
1804f9e38e9Sshige 	ausmbus_reg_write(sc, AUPSC_SMBCFG, v);
1814f9e38e9Sshige 	v = ausmbus_reg_read(sc, AUPSC_SMBSTAT);
1824f9e38e9Sshige 	if ((v & SMBUS_STAT_SR) == 0) {
1834f9e38e9Sshige 		/* SMBus is not ready */
1844f9e38e9Sshige 		return -1;
1854f9e38e9Sshige 	}
1864f9e38e9Sshige 
1874f9e38e9Sshige #ifdef AUSMBUS_PSC_DEBUG
1884f9e38e9Sshige 	aprint_normal("AuSMBus enabled.\n");
1894f9e38e9Sshige 	aprint_normal("AuSMBus smbconfig: 0x%08x\n",
1904f9e38e9Sshige 			ausmbus_reg_read(sc, AUPSC_SMBCFG));
1914f9e38e9Sshige 	aprint_normal("AuSMBus smbstatus: 0x%08x\n",
1924f9e38e9Sshige 			ausmbus_reg_read(sc, AUPSC_SMBSTAT));
1934f9e38e9Sshige 	aprint_normal("AuSMBus smbtmr   : 0x%08x\n",
1944f9e38e9Sshige 			ausmbus_reg_read(sc, AUPSC_SMBTMR));
1954f9e38e9Sshige 	aprint_normal("AuSMBus smbmask  : 0x%08x\n",
1964f9e38e9Sshige 			ausmbus_reg_read(sc, AUPSC_SMBMSK));
1974f9e38e9Sshige #endif
1984f9e38e9Sshige 
1994f9e38e9Sshige 	return 0;
2004f9e38e9Sshige }
2014f9e38e9Sshige 
2024f9e38e9Sshige static void
ausmbus_release_bus(void * arg,int flags)2034f9e38e9Sshige ausmbus_release_bus(void *arg, int flags)
2044f9e38e9Sshige {
2054f9e38e9Sshige 	struct ausmbus_softc *sc = arg;
2064f9e38e9Sshige 
2074f9e38e9Sshige 	ausmbus_reg_write(sc, AUPSC_SMBCFG, 0);
2084f9e38e9Sshige 	sc->sc_ctrl.psc_disable(sc);
2094f9e38e9Sshige 
2104f9e38e9Sshige 	return;
2114f9e38e9Sshige }
2124f9e38e9Sshige 
2134f9e38e9Sshige static int
ausmbus_exec(void * cookie,i2c_op_t op,i2c_addr_t addr,const void * vcmd,size_t cmdlen,void * vbuf,size_t buflen,int flags)2144f9e38e9Sshige ausmbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *vcmd,
2154f9e38e9Sshige 	size_t cmdlen, void *vbuf, size_t buflen, int flags)
2164f9e38e9Sshige {
2174f9e38e9Sshige 	struct ausmbus_softc *sc  = (struct ausmbus_softc *)cookie;
2184f9e38e9Sshige 	const uint8_t *cmd = vcmd;
2194f9e38e9Sshige 
2204f9e38e9Sshige 	sc->sc_smbus_slave_addr  = addr;
2214f9e38e9Sshige 
2224f9e38e9Sshige 	/* Receive byte */
2234f9e38e9Sshige 	if ((I2C_OP_READ_P(op)) && (cmdlen == 0) && (buflen == 1)) {
224383a50d6Skiyohara 		return ausmbus_receive_1(sc, (uint8_t *)vbuf);
2254f9e38e9Sshige 	}
2264f9e38e9Sshige 
2274f9e38e9Sshige 	/* Read byte */
2284f9e38e9Sshige 	if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 1)) {
229383a50d6Skiyohara 		return ausmbus_read_1(sc, *cmd, (uint8_t *)vbuf);
230383a50d6Skiyohara 	}
231383a50d6Skiyohara 
232383a50d6Skiyohara 	/* Read word */
233383a50d6Skiyohara 	if ((I2C_OP_READ_P(op)) && (cmdlen == 1) && (buflen == 2)) {
234383a50d6Skiyohara 		return ausmbus_read_2(sc, *cmd, (uint16_t *)vbuf);
2354f9e38e9Sshige 	}
2364f9e38e9Sshige 
2379929afbcSpgoyette 	/* Read quick */
2389929afbcSpgoyette 	if ((I2C_OP_READ_P(op)) && (cmdlen == 0) && (buflen == 0)) {
2399929afbcSpgoyette 		return ausmbus_quick_read(sc);
2409929afbcSpgoyette 	}
2419929afbcSpgoyette 
2424f9e38e9Sshige 	/* Send byte */
2434f9e38e9Sshige 	if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 1)) {
244383a50d6Skiyohara 		return ausmbus_send_1(sc, *((uint8_t *)vbuf));
2454f9e38e9Sshige 	}
2464f9e38e9Sshige 
2474f9e38e9Sshige 	/* Write byte */
2484f9e38e9Sshige 	if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 1)) {
249383a50d6Skiyohara 		return ausmbus_write_1(sc, *cmd, *((uint8_t *)vbuf));
250383a50d6Skiyohara 	}
251383a50d6Skiyohara 
252383a50d6Skiyohara 	/* Write word */
253383a50d6Skiyohara 	if ((I2C_OP_WRITE_P(op)) && (cmdlen == 1) && (buflen == 2)) {
254383a50d6Skiyohara 		return ausmbus_write_2(sc, *cmd, *((uint16_t *)vbuf));
2554f9e38e9Sshige 	}
2564f9e38e9Sshige 
2579929afbcSpgoyette 	/* Write quick */
2589929afbcSpgoyette 	if ((I2C_OP_WRITE_P(op)) && (cmdlen == 0) && (buflen == 0)) {
2599929afbcSpgoyette 		return ausmbus_quick_write(sc);
2609929afbcSpgoyette 	}
2619929afbcSpgoyette 
2624f9e38e9Sshige 	/*
2634f9e38e9Sshige 	 * XXX: TODO Please Support other protocols defined in SMBus 2.0
2644f9e38e9Sshige 	 * - Process call
2654f9e38e9Sshige 	 * - Block write/read
2664f9e38e9Sshige 	 * - Clock write-block read process cal
2674f9e38e9Sshige 	 * - SMBus host notify protocol
2689929afbcSpgoyette 	 *
2699929afbcSpgoyette 	 * - Read quick and write quick have not been tested!
2704f9e38e9Sshige 	 */
2714f9e38e9Sshige 
2724f9e38e9Sshige 	return -1;
2734f9e38e9Sshige }
2744f9e38e9Sshige 
2754f9e38e9Sshige static int
ausmbus_receive_1(struct ausmbus_softc * sc,uint8_t * vp)2764f9e38e9Sshige ausmbus_receive_1(struct ausmbus_softc *sc, uint8_t *vp)
2774f9e38e9Sshige {
2784f9e38e9Sshige 	int error;
2794f9e38e9Sshige 
2804f9e38e9Sshige 	error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_READ);
2814f9e38e9Sshige 	if (error != 0) {
2824f9e38e9Sshige 		return error;
2834f9e38e9Sshige 	}
2844f9e38e9Sshige 	error = ausmbus_read_byte(sc, vp, I2C_F_STOP);
2854f9e38e9Sshige 	if (error != 0) {
2864f9e38e9Sshige 		return error;
2874f9e38e9Sshige 	}
2884f9e38e9Sshige 
2894f9e38e9Sshige 	return 0;
2904f9e38e9Sshige }
2914f9e38e9Sshige 
2924f9e38e9Sshige static int
ausmbus_read_1(struct ausmbus_softc * sc,uint8_t cmd,uint8_t * vp)2934f9e38e9Sshige ausmbus_read_1(struct ausmbus_softc *sc, uint8_t cmd, uint8_t *vp)
2944f9e38e9Sshige {
2954f9e38e9Sshige 	int error;
2964f9e38e9Sshige 
2974f9e38e9Sshige 	error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_WRITE);
2984f9e38e9Sshige 	if (error != 0) {
2994f9e38e9Sshige 		return error;
3004f9e38e9Sshige 	}
3014f9e38e9Sshige 
3024f9e38e9Sshige 	error = ausmbus_write_byte(sc, cmd, I2C_F_READ);
3034f9e38e9Sshige 	if (error != 0) {
3044f9e38e9Sshige 		return error;
3054f9e38e9Sshige 	}
3064f9e38e9Sshige 
3074f9e38e9Sshige 	error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_READ);
3084f9e38e9Sshige 	if (error != 0) {
3094f9e38e9Sshige 		return error;
3104f9e38e9Sshige 	}
3114f9e38e9Sshige 
3124f9e38e9Sshige 	error = ausmbus_read_byte(sc, vp, I2C_F_STOP);
3134f9e38e9Sshige 	if (error != 0) {
3144f9e38e9Sshige 		return error;
3154f9e38e9Sshige 	}
3164f9e38e9Sshige 
3174f9e38e9Sshige 	return 0;
3184f9e38e9Sshige }
3194f9e38e9Sshige 
3204f9e38e9Sshige static int
ausmbus_read_2(struct ausmbus_softc * sc,uint8_t cmd,uint16_t * vp)321383a50d6Skiyohara ausmbus_read_2(struct ausmbus_softc *sc, uint8_t cmd, uint16_t *vp)
322383a50d6Skiyohara {
323383a50d6Skiyohara 	int error;
324383a50d6Skiyohara 	uint8_t high, low;
325383a50d6Skiyohara 
326383a50d6Skiyohara 	error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_WRITE);
327383a50d6Skiyohara 	if (error != 0) {
328383a50d6Skiyohara 		return error;
329383a50d6Skiyohara 	}
330383a50d6Skiyohara 
331383a50d6Skiyohara 	error = ausmbus_write_byte(sc, cmd, I2C_F_READ);
332383a50d6Skiyohara 	if (error != 0) {
333383a50d6Skiyohara 		return error;
334383a50d6Skiyohara 	}
335383a50d6Skiyohara 
336383a50d6Skiyohara 	error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_READ);
337383a50d6Skiyohara 	if (error != 0) {
338383a50d6Skiyohara 		return error;
339383a50d6Skiyohara 	}
340383a50d6Skiyohara 
341383a50d6Skiyohara 	error = ausmbus_read_byte(sc, &low, 0);
342383a50d6Skiyohara 	if (error != 0) {
343383a50d6Skiyohara 		return error;
344383a50d6Skiyohara 	}
345383a50d6Skiyohara 
346383a50d6Skiyohara 	error = ausmbus_read_byte(sc, &high, I2C_F_STOP);
347383a50d6Skiyohara 	if (error != 0) {
348383a50d6Skiyohara 		return error;
349383a50d6Skiyohara 	}
350383a50d6Skiyohara 
351383a50d6Skiyohara 	*vp = (high << 8) | low;
352383a50d6Skiyohara 
353383a50d6Skiyohara 	return 0;
354383a50d6Skiyohara }
355383a50d6Skiyohara 
356383a50d6Skiyohara static int
ausmbus_send_1(struct ausmbus_softc * sc,uint8_t val)3574f9e38e9Sshige ausmbus_send_1(struct ausmbus_softc *sc, uint8_t val)
3584f9e38e9Sshige {
3594f9e38e9Sshige 	int error;
3604f9e38e9Sshige 
3614f9e38e9Sshige 	error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_WRITE);
3624f9e38e9Sshige 	if (error != 0) {
3634f9e38e9Sshige 		return error;
3644f9e38e9Sshige 	}
3654f9e38e9Sshige 
3664f9e38e9Sshige 	error = ausmbus_write_byte(sc, val, I2C_F_STOP);
3674f9e38e9Sshige 	if (error != 0) {
3684f9e38e9Sshige 		return error;
3694f9e38e9Sshige 	}
3704f9e38e9Sshige 
3714f9e38e9Sshige 	return 0;
3724f9e38e9Sshige }
3734f9e38e9Sshige 
3744f9e38e9Sshige static int
ausmbus_write_1(struct ausmbus_softc * sc,uint8_t cmd,uint8_t val)3754f9e38e9Sshige ausmbus_write_1(struct ausmbus_softc *sc, uint8_t cmd, uint8_t val)
3764f9e38e9Sshige {
3774f9e38e9Sshige 	int error;
3784f9e38e9Sshige 
3794f9e38e9Sshige 	error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_WRITE);
3804f9e38e9Sshige 	if (error != 0) {
3814f9e38e9Sshige 		return error;
3824f9e38e9Sshige 	}
3834f9e38e9Sshige 
3844f9e38e9Sshige 	error = ausmbus_write_byte(sc, cmd, 0);
3854f9e38e9Sshige 	if (error != 0) {
3864f9e38e9Sshige 		return error;
3874f9e38e9Sshige 	}
3884f9e38e9Sshige 
3894f9e38e9Sshige 	error = ausmbus_write_byte(sc, val, I2C_F_STOP);
3904f9e38e9Sshige 	if (error != 0) {
3914f9e38e9Sshige 		return error;
3924f9e38e9Sshige 	}
3934f9e38e9Sshige 
3944f9e38e9Sshige 	return 0;
3954f9e38e9Sshige }
3964f9e38e9Sshige 
3974f9e38e9Sshige static int
ausmbus_write_2(struct ausmbus_softc * sc,uint8_t cmd,uint16_t val)398383a50d6Skiyohara ausmbus_write_2(struct ausmbus_softc *sc, uint8_t cmd, uint16_t val)
399383a50d6Skiyohara {
400383a50d6Skiyohara 	int error;
401383a50d6Skiyohara 	uint8_t high, low;
402383a50d6Skiyohara 
403383a50d6Skiyohara 	high = (val >> 8) & 0xff;
404383a50d6Skiyohara 	low = val & 0xff;
405383a50d6Skiyohara 
406383a50d6Skiyohara 	error = ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr, I2C_F_WRITE);
407383a50d6Skiyohara 	if (error != 0) {
408383a50d6Skiyohara 		return error;
409383a50d6Skiyohara 	}
410383a50d6Skiyohara 
411383a50d6Skiyohara 	error = ausmbus_write_byte(sc, cmd, 0);
412383a50d6Skiyohara 	if (error != 0) {
413383a50d6Skiyohara 		return error;
414383a50d6Skiyohara 	}
415383a50d6Skiyohara 
416383a50d6Skiyohara 	error = ausmbus_write_byte(sc, low, 0);
417383a50d6Skiyohara 	if (error != 0) {
418383a50d6Skiyohara 		return error;
419383a50d6Skiyohara 	}
420383a50d6Skiyohara 
421383a50d6Skiyohara 	error = ausmbus_write_byte(sc, high, I2C_F_STOP);
422383a50d6Skiyohara 	if (error != 0) {
423383a50d6Skiyohara 		return error;
424383a50d6Skiyohara 	}
425383a50d6Skiyohara 
426383a50d6Skiyohara 	return 0;
427383a50d6Skiyohara }
428383a50d6Skiyohara 
4299929afbcSpgoyette /*
4309929afbcSpgoyette  * XXX The quick_write() and quick_read() routines have not been tested!
4319929afbcSpgoyette  */
4329929afbcSpgoyette static int
ausmbus_quick_write(struct ausmbus_softc * sc)4339929afbcSpgoyette ausmbus_quick_write(struct ausmbus_softc *sc)
4349929afbcSpgoyette {
4359929afbcSpgoyette 	return ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr,
4369929afbcSpgoyette 			I2C_F_STOP | I2C_F_WRITE);
4379929afbcSpgoyette }
4389929afbcSpgoyette 
4399929afbcSpgoyette static int
ausmbus_quick_read(struct ausmbus_softc * sc)4409929afbcSpgoyette ausmbus_quick_read(struct ausmbus_softc *sc)
4419929afbcSpgoyette {
4429929afbcSpgoyette 	return ausmbus_initiate_xfer(sc, sc->sc_smbus_slave_addr,
4439929afbcSpgoyette 			I2C_F_STOP | I2C_F_READ);
4449929afbcSpgoyette }
4459929afbcSpgoyette 
446383a50d6Skiyohara static int
ausmbus_wait_mastertx(struct ausmbus_softc * sc)4474f9e38e9Sshige ausmbus_wait_mastertx(struct ausmbus_softc *sc)
4484f9e38e9Sshige {
4494f9e38e9Sshige 	uint32_t v;
4504f9e38e9Sshige 	int timeout;
4514f9e38e9Sshige 	int txerr = 0;
4524f9e38e9Sshige 
4534f9e38e9Sshige 	timeout = sc->sc_smbus_timeout;
4544f9e38e9Sshige 
4554f9e38e9Sshige 	do {
4564f9e38e9Sshige 		v = ausmbus_reg_read(sc, AUPSC_SMBEVNT);
4574f9e38e9Sshige #ifdef AUSMBUS_PSC_DEBUG
4584f9e38e9Sshige 		aprint_normal("AuSMBus: ausmbus_wait_mastertx(): psc_smbevnt=0x%08x\n", v);
4594f9e38e9Sshige #endif
4604f9e38e9Sshige 		if ((v & SMBUS_EVNT_TU) != 0)
4614f9e38e9Sshige 			break;
4624f9e38e9Sshige 		if ((v & SMBUS_EVNT_MD) != 0)
4634f9e38e9Sshige 			break;
4644f9e38e9Sshige 		if ((v & (SMBUS_EVNT_DN | SMBUS_EVNT_AN | SMBUS_EVNT_AL))
4654f9e38e9Sshige 			!= 0) {
4664f9e38e9Sshige 			txerr = 1;
4674f9e38e9Sshige 			break;
4684f9e38e9Sshige 		}
4694f9e38e9Sshige 		timeout--;
4704f9e38e9Sshige 		delay(1);
4714f9e38e9Sshige 	} while (timeout > 0);
4724f9e38e9Sshige 
4734f9e38e9Sshige 	if (txerr != 0) {
4744f9e38e9Sshige 		ausmbus_reg_write(sc, AUPSC_SMBEVNT,
4754f9e38e9Sshige 			SMBUS_EVNT_DN | SMBUS_EVNT_AN | SMBUS_EVNT_AL);
4764f9e38e9Sshige #ifdef AUSMBUS_PSC_DEBUG
4774f9e38e9Sshige 		aprint_normal("AuSMBus: ausmbus_wait_mastertx(): Tx error\n");
4784f9e38e9Sshige #endif
4794f9e38e9Sshige 		return -1;
4804f9e38e9Sshige 	}
4814f9e38e9Sshige 
4824f9e38e9Sshige 	/* Reset Event TU (Tx Underflow) */
4834f9e38e9Sshige 	ausmbus_reg_write(sc, AUPSC_SMBEVNT, SMBUS_EVNT_TU | SMBUS_EVNT_MD);
4844f9e38e9Sshige 
4854f9e38e9Sshige #ifdef AUSMBUS_PSC_DEBUG
4864f9e38e9Sshige 	v = ausmbus_reg_read(sc, AUPSC_SMBEVNT);
4874f9e38e9Sshige 	aprint_normal("AuSMBus: ausmbus_wait_mastertx(): psc_smbevnt=0x%08x (reset)\n", v);
4884f9e38e9Sshige #endif
4894f9e38e9Sshige 	return 0;
4904f9e38e9Sshige }
4914f9e38e9Sshige 
4924f9e38e9Sshige static int
ausmbus_wait_masterrx(struct ausmbus_softc * sc)4934f9e38e9Sshige ausmbus_wait_masterrx(struct ausmbus_softc *sc)
4944f9e38e9Sshige {
4954f9e38e9Sshige 	uint32_t v;
4964f9e38e9Sshige 	int timeout;
4974f9e38e9Sshige 	timeout = sc->sc_smbus_timeout;
4984f9e38e9Sshige 
4994f9e38e9Sshige 	if (ausmbus_wait_mastertx(sc) != 0)
5004f9e38e9Sshige 		return -1;
5014f9e38e9Sshige 
5024f9e38e9Sshige 	do {
5034f9e38e9Sshige 		v = ausmbus_reg_read(sc, AUPSC_SMBSTAT);
5044f9e38e9Sshige #ifdef AUSMBUS_PSC_DEBUG
5054f9e38e9Sshige 		aprint_normal("AuSMBus: ausmbus_wait_masterrx(): psc_smbstat=0x%08x\n", v);
5064f9e38e9Sshige #endif
5074f9e38e9Sshige 		if ((v & SMBUS_STAT_RE) == 0)
5084f9e38e9Sshige 			break;
5094f9e38e9Sshige 		timeout--;
5104f9e38e9Sshige 		delay(1);
5114f9e38e9Sshige 	} while (timeout > 0);
5124f9e38e9Sshige 
5134f9e38e9Sshige 	return 0;
5144f9e38e9Sshige }
5154f9e38e9Sshige 
5164f9e38e9Sshige static int
ausmbus_initiate_xfer(void * arg,i2c_addr_t addr,int flags)5174f9e38e9Sshige ausmbus_initiate_xfer(void *arg, i2c_addr_t addr, int flags)
5184f9e38e9Sshige {
5194f9e38e9Sshige 	struct ausmbus_softc *sc = arg;
5204f9e38e9Sshige 	uint32_t v;
5214f9e38e9Sshige 
5224f9e38e9Sshige 	/* Tx/Rx Slave Address */
5234f9e38e9Sshige 	v = (addr << 1) & SMBUS_TXRX_ADDRDATA;
5244f9e38e9Sshige 	if ((flags & I2C_F_READ) != 0)
5254f9e38e9Sshige 		v |= 1;
5269929afbcSpgoyette 	if ((flags & I2C_F_STOP) != 0)
5279929afbcSpgoyette 		v |= SMBUS_TXRX_STP;
5284f9e38e9Sshige 	ausmbus_reg_write(sc, AUPSC_SMBTXRX, v);
5294f9e38e9Sshige 
5304f9e38e9Sshige 	/* Master Start */
5314f9e38e9Sshige 	ausmbus_reg_write(sc, AUPSC_SMBPCR, SMBUS_PCR_MS);
5324f9e38e9Sshige 
5334f9e38e9Sshige 	if (ausmbus_wait_mastertx(sc) != 0)
5344f9e38e9Sshige 		return -1;
5354f9e38e9Sshige 
5364f9e38e9Sshige 	return 0;
5374f9e38e9Sshige }
5384f9e38e9Sshige 
5394f9e38e9Sshige static int
ausmbus_read_byte(void * arg,uint8_t * vp,int flags)5404f9e38e9Sshige ausmbus_read_byte(void *arg, uint8_t *vp, int flags)
5414f9e38e9Sshige {
5424f9e38e9Sshige 	struct ausmbus_softc *sc = arg;
5434f9e38e9Sshige 	uint32_t v;
5444f9e38e9Sshige 
5454f9e38e9Sshige 	if ((flags & I2C_F_STOP) != 0) {
5464f9e38e9Sshige 		ausmbus_reg_write(sc, AUPSC_SMBTXRX, SMBUS_TXRX_STP);
5474f9e38e9Sshige 	} else {
5484f9e38e9Sshige 		ausmbus_reg_write(sc, AUPSC_SMBTXRX, 0);
5494f9e38e9Sshige 	}
5504f9e38e9Sshige 
5514f9e38e9Sshige 	if (ausmbus_wait_masterrx(sc) != 0)
5524f9e38e9Sshige 		return -1;
5534f9e38e9Sshige 
5544f9e38e9Sshige 	v = ausmbus_reg_read(sc, AUPSC_SMBTXRX);
5554f9e38e9Sshige 	*vp = v & SMBUS_TXRX_ADDRDATA;
5564f9e38e9Sshige 
5574f9e38e9Sshige 	return 0;
5584f9e38e9Sshige }
5594f9e38e9Sshige 
5604f9e38e9Sshige static int
ausmbus_write_byte(void * arg,uint8_t v,int flags)5614f9e38e9Sshige ausmbus_write_byte(void *arg, uint8_t v, int flags)
5624f9e38e9Sshige {
5634f9e38e9Sshige 	struct ausmbus_softc *sc = arg;
5644f9e38e9Sshige 
5654f9e38e9Sshige 	if ((flags & I2C_F_STOP) != 0)  {
5664f9e38e9Sshige 		ausmbus_reg_write(sc, AUPSC_SMBTXRX, (v | SMBUS_TXRX_STP));
5674f9e38e9Sshige 	} else if ((flags & I2C_F_READ) != 0) {
5684f9e38e9Sshige 		ausmbus_reg_write(sc, AUPSC_SMBTXRX, (v | SMBUS_TXRX_RSR));
5694f9e38e9Sshige 	} else {
5704f9e38e9Sshige 		ausmbus_reg_write(sc, AUPSC_SMBTXRX, v);
5714f9e38e9Sshige 	}
5724f9e38e9Sshige 
5734f9e38e9Sshige 	if (ausmbus_wait_mastertx(sc) != 0)
5744f9e38e9Sshige 		return -1;
5754f9e38e9Sshige 
5764f9e38e9Sshige 	return 0;
5774f9e38e9Sshige }
578