Home | History | Annotate | Line # | Download | only in ic
      1 /* $NetBSD: gcscpcib.c,v 1.5 2021/08/07 16:19:12 thorpej Exp $ */
      2 /* $OpenBSD: gcscpcib.c,v 1.6 2007/11/17 17:02:47 mbalmer Exp $	*/
      3 
      4 /*
      5  * Copyright (c) 2008 Yojiro UO <yuo (at) nui.org>
      6  * Copyright (c) 2007 Marc Balmer <mbalmer (at) openbsd.org>
      7  * Copyright (c) 2007 Michael Shalayeff
      8  * All rights reserved.
      9  *
     10  * Permission to use, copy, modify, and distribute this software for any
     11  * purpose with or without fee is hereby granted, provided that the above
     12  * copyright notice and this permission notice appear in all copies.
     13  *
     14  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     15  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     16  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     17  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     18  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
     19  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     20  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     21  */
     22 
     23 /*
     24  * AMD CS5535/CS5536 series LPC bridge also containing timer, watchdog and GPIO.
     25  */
     26 #include <sys/cdefs.h>
     27 __KERNEL_RCSID(0, "$NetBSD: gcscpcib.c,v 1.5 2021/08/07 16:19:12 thorpej Exp $");
     28 
     29 #include "gpio.h"
     30 
     31 #include <sys/param.h>
     32 #include <sys/systm.h>
     33 #include <sys/device.h>
     34 #include <sys/gpio.h>
     35 #include <sys/sysctl.h>
     36 #include <sys/timetc.h>
     37 #include <sys/wdog.h>
     38 
     39 #include <sys/bus.h>
     40 
     41 #include <dev/gpio/gpiovar.h>
     42 
     43 #include <dev/sysmon/sysmonvar.h>
     44 
     45 #include <dev/ic/gcscpcibreg.h>
     46 #include <dev/ic/gcscpcibvar.h>
     47 
     48 /* define if you need to select MFGPT for watchdog manually (0-5). */
     49 /* #define AMD553X_WDT_FORCEUSEMFGPT 	0 */
     50 /* select precision of watchdog timer (default value = 0.25sec (4Hz tick) */
     51 #define	AMD553X_MFGPT_PRESCALE	AMD553X_MFGPT_DIV_8K /* 32K/8K = 4Hz */
     52 
     53 /* #define GCSCPCIB_DEBUG */
     54 #ifdef GCSCPCIB_DEBUG
     55 #define DPRINTF(x)	printf x
     56 #else
     57 #define DPRINTF(x)
     58 #endif
     59 
     60 /* 1 bit replace (not support multiple bit)*/
     61 #define AMD553X_MFGPTx_NR_DISABLE(x, bit) \
     62 	( gcsc_wrmsr(AMD553X_MFGPT_NR, gcsc_rdmsr(AMD553X_MFGPT_NR) & ~((bit) << (x))) )
     63 #define AMD553X_MFGPTx_NR_ENABLE(x, bit) \
     64 	( gcsc_wrmsr(AMD553X_MFGPT_NR, gcsc_rdmsr(AMD553X_MFGPT_NR) | ((bit) << (x))) )
     65 
     66 /* caliculate watchdog timer setting */
     67 #define	AMD553X_WDT_TICK (1<<(AMD553X_MFGPT_DIV_32K - AMD553X_MFGPT_PRESCALE))
     68 #define AMD553X_WDT_COUNTMAX	(0xffff / AMD553X_WDT_TICK)
     69 
     70 static u_int	gcscpcib_get_timecount(struct timecounter *tc);
     71 static int	gscspcib_scan_mfgpt(struct gcscpcib_softc *sc);
     72 static void 	gscspcib_wdog_update(struct gcscpcib_softc *, uint16_t);
     73 static int	gcscpcib_wdog_setmode(struct sysmon_wdog *smw);
     74 static int	gcscpcib_wdog_tickle(struct sysmon_wdog *smw);
     75 static void	gcscpcib_wdog_enable(struct gcscpcib_softc *sc);
     76 static void	gcscpcib_wdog_disable(struct gcscpcib_softc *sc);
     77 static void	gcscpcib_wdog_reset(struct gcscpcib_softc *sc);
     78 
     79 #if NGPIO > 0
     80 static int	gcscpcib_gpio_pin_read(void *, int);
     81 static void	gcscpcib_gpio_pin_write(void *, int, int);
     82 static void	gcscpcib_gpio_pin_ctl(void *, int, int);
     83 #endif
     84 
     85 void
     86 gcscpcib_attach(device_t self, struct gcscpcib_softc *sc,
     87     bus_space_tag_t iot, int flags)
     88 {
     89 	struct timecounter *tc = &sc->sc_timecounter;
     90 	bus_addr_t wdtbase;
     91 	int wdt = 0, gpio = 0;
     92 #if NGPIO > 0
     93 	struct gpiobus_attach_args gba;
     94 	bus_addr_t gpiobase;
     95 	int i;
     96 #endif
     97 
     98 	sc->sc_iot = iot;
     99 #if NGPIO > 0
    100 	sc->sc_gpio_iot = iot;
    101 #endif
    102 
    103 	/* Attach the CS553[56] timer */
    104 	tc->tc_get_timecount = gcscpcib_get_timecount;
    105 	tc->tc_counter_mask = 0xffffffff;
    106 	tc->tc_frequency = 3579545;
    107 	tc->tc_name = device_xname(self);
    108 	tc->tc_quality = 1000;
    109 	tc->tc_priv = sc;
    110 	tc_init(tc);
    111 
    112 	if (flags & GCSCATTACH_NO_WDT)
    113 		goto gpio;
    114 
    115 	/* Attach the watchdog timer */
    116 	wdtbase = gcsc_rdmsr(MSR_LBAR_MFGPT) & 0xffff;
    117 	if (bus_space_map(sc->sc_iot, wdtbase, 64, 0, &sc->sc_ioh)) {
    118 		aprint_error_dev(self, "can't map memory space for WDT\n");
    119 	} else {
    120 		/* select a MFGPT timer for watchdog counter */
    121 		if (!gscspcib_scan_mfgpt(sc)) {
    122 			aprint_error_dev(self, "can't alloc an MFGPT for WDT\n");
    123 			goto gpio;
    124 		}
    125 		/*
    126 		 * Note: MFGPGx_SETUP register is write once register
    127  		 * except CNT_EN and CMP[12]EV bit.
    128 		 */
    129 		bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    130 			AMD553X_MFGPTX_SETUP(sc->sc_wdt_mfgpt),
    131 		    	AMD553X_MFGPT_CMP2EV | AMD553X_MFGPT_CMP2 |
    132 		    	AMD553X_MFGPT_PRESCALE);
    133 
    134 		/* disable watchdog action */
    135 		AMD553X_MFGPTx_NR_DISABLE(sc->sc_wdt_mfgpt,
    136 			AMD553X_MFGPT0_C2_NMIM);
    137 		AMD553X_MFGPTx_NR_DISABLE(sc->sc_wdt_mfgpt,
    138 			AMD553X_MFGPT0_C2_RSTEN);
    139 
    140 		sc->sc_smw.smw_name = device_xname(self);
    141 		sc->sc_smw.smw_cookie = sc;
    142 		sc->sc_smw.smw_setmode = gcscpcib_wdog_setmode;
    143 		sc->sc_smw.smw_tickle = gcscpcib_wdog_tickle;
    144 		sc->sc_smw.smw_period = 32;
    145 		aprint_normal_dev(self, "Watchdog Timer via MFGPT%d",
    146 			 sc->sc_wdt_mfgpt);
    147 		wdt = 1;
    148 	}
    149 
    150 gpio:
    151 #if NGPIO > 0
    152 	/* map GPIO I/O space */
    153 	gpiobase = gcsc_rdmsr(MSR_LBAR_GPIO) & 0xffff;
    154 	if (!bus_space_map(sc->sc_gpio_iot, gpiobase, 0xff, 0,
    155 	    &sc->sc_gpio_ioh)) {
    156 		if (wdt)
    157 			aprint_normal(", GPIO");
    158 		else
    159 			aprint_normal_dev(self, "GPIO");
    160 
    161 		/* initialize pin array */
    162 		for (i = 0; i < AMD553X_GPIO_NPINS; i++) {
    163 			sc->sc_gpio_pins[i].pin_num = i;
    164 			sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT |
    165 			    GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN |
    166 			    GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE |
    167 			    GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN |
    168 			    GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
    169 
    170 			/* read initial state */
    171 			sc->sc_gpio_pins[i].pin_state =
    172 			    gcscpcib_gpio_pin_read(sc, i);
    173 		}
    174 
    175 		/* create controller tag */
    176 		sc->sc_gpio_gc.gp_cookie = sc;
    177 		sc->sc_gpio_gc.gp_pin_read = gcscpcib_gpio_pin_read;
    178 		sc->sc_gpio_gc.gp_pin_write = gcscpcib_gpio_pin_write;
    179 		sc->sc_gpio_gc.gp_pin_ctl = gcscpcib_gpio_pin_ctl;
    180 
    181 		gba.gba_gc = &sc->sc_gpio_gc;
    182 		gba.gba_pins = sc->sc_gpio_pins;
    183 		gba.gba_npins = AMD553X_GPIO_NPINS;
    184 		gpio = 1;
    185 	}
    186 #endif
    187 	if (wdt || gpio)
    188 		aprint_normal("\n");
    189 
    190 #if NGPIO > 0
    191 	/* Attach GPIO framework */
    192 	if (gpio)
    193                 config_found(self, &gba, gpiobus_print,
    194 		    CFARGS(.iattr = "gpiobus"));
    195 #endif
    196 
    197 	/* Register Watchdog timer to SMW */
    198 	if (wdt) {
    199 		if (sysmon_wdog_register(&sc->sc_smw) != 0)
    200 			aprint_error_dev(self,
    201 			    "cannot register wdog with sysmon\n");
    202 	}
    203 }
    204 
    205 static u_int
    206 gcscpcib_get_timecount(struct timecounter *tc)
    207 {
    208         return gcsc_rdmsr(AMD553X_TMC);
    209 }
    210 
    211 /* Watchdog timer support functions */
    212 static int
    213 gscspcib_scan_mfgpt(struct gcscpcib_softc *sc)
    214 {
    215 	int i;
    216 
    217 #ifdef AMD553X_WDT_FORCEUSEMFGPT
    218 	if (AMD553X_WDT_FORCEUSEMFGPT >= AMD553X_MFGPT_MAX)
    219 		return 0;
    220 	sc->sc_wdt_mfgpt = AMD553X_WDT_FORCEUSEMFGPT;
    221 	return 1;
    222 #endif /* AMD553X_WDT_FORCEUSEMFGPT */
    223 
    224 	for (i = 0; i < AMD553X_MFGPT_MAX; i++){
    225 		if (bus_space_read_2(sc->sc_iot, sc->sc_ioh,
    226 		    AMD553X_MFGPTX_SETUP(i)) == 0) {
    227 			/* found unused MFGPT, use it. */
    228 			sc->sc_wdt_mfgpt = i;
    229 			return 1;
    230 		}
    231 	}
    232 	/* no MFGPT for WDT found */
    233 	return 0;
    234 }
    235 
    236 
    237 static void
    238 gscspcib_wdog_update(struct gcscpcib_softc *sc, uint16_t count)
    239 {
    240 #ifdef GCSCPCIB_DEBUG
    241 	uint16_t cnt;
    242 	cnt = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
    243 		AMD553X_MFGPTX_CNT(sc->sc_wdt_mfgpt));
    244 #endif
    245 	if (count > AMD553X_WDT_COUNTMAX)
    246 		count = AMD553X_WDT_COUNTMAX;
    247 	/*
    248 	 * CS553X databook recommend following sequence to re-initialize
    249 	 * the counter and compare value. (See p165 on CS5536 databook)
    250 	 * 1: suspend counter: clear counter enable bit to 0
    251 	 * 2: reset (and NMI, if need) enable bit in MSRs
    252 	 * 3: update counter & clear event flags
    253 	 * 4: resume (2) operation
    254 	 * 5: re-enable counter
    255 	 */
    256 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    257 		AMD553X_MFGPTX_SETUP(sc->sc_wdt_mfgpt), 0);
    258 	AMD553X_MFGPTx_NR_DISABLE(sc->sc_wdt_mfgpt, AMD553X_MFGPT0_C2_RSTEN);
    259 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    260 		AMD553X_MFGPTX_CNT(sc->sc_wdt_mfgpt), count);
    261 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    262 		AMD553X_MFGPTX_SETUP(sc->sc_wdt_mfgpt),
    263 			AMD553X_MFGPT_CMP1 | AMD553X_MFGPT_CMP2);
    264 	AMD553X_MFGPTx_NR_ENABLE(sc->sc_wdt_mfgpt, AMD553X_MFGPT0_C2_RSTEN);
    265 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    266 		AMD553X_MFGPTX_SETUP(sc->sc_wdt_mfgpt),
    267 	    	AMD553X_MFGPT_CNT_EN | AMD553X_MFGPT_CMP2);
    268 
    269 	DPRINTF(("%s: MFGPT%d_CNT= %d -> %d (expect: %d), MFGPT_NR=%#.8x\n",
    270 		__func__, sc->sc_wdt_mfgpt, cnt,
    271 		bus_space_read_2(sc->sc_iot, sc->sc_ioh,
    272 			 AMD553X_MFGPTX_CNT(sc->sc_wdt_mfgpt)), count,
    273 		(uint32_t)(gcsc_rdmsr(AMD553X_MFGPT_NR))));
    274 }
    275 
    276 static void
    277 gcscpcib_wdog_disable(struct gcscpcib_softc *sc)
    278 {
    279 	/*
    280 	 * stop counter and reset counter value
    281 	 * Note: as the MFGPTx_SETUP is write once register, the prescaler
    282 	 * setting, clock select and compare mode are kept till reset.
    283 	 */
    284 	gscspcib_wdog_update(sc, 0);
    285 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    286 		AMD553X_MFGPTX_SETUP(sc->sc_wdt_mfgpt), 0);
    287 
    288 	/* disable watchdog action */
    289 	DPRINTF(("%s: disable watchdog action\n", __func__));
    290 	AMD553X_MFGPTx_NR_DISABLE(sc->sc_wdt_mfgpt, AMD553X_MFGPT0_C2_RSTEN);
    291 }
    292 
    293 static void
    294 gcscpcib_wdog_enable(struct gcscpcib_softc *sc)
    295 {
    296 	int period = sc->sc_smw.smw_period;
    297 
    298 	/* clear recent event flag and counter value, and start counter */
    299 	gcscpcib_wdog_reset(sc);
    300 	/* set watchdog timer limit, counter tick is 0.5sec */
    301 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    302 		AMD553X_MFGPTX_CMP2(sc->sc_wdt_mfgpt),
    303 			period * AMD553X_WDT_TICK);
    304 
    305 	/* enable watchdog action */
    306 	DPRINTF(("%s: enable watchdog action. (MFGPT0_CMP2= %d)", __func__,
    307 	        bus_space_read_2(sc->sc_iot, sc->sc_ioh,
    308 			 AMD553X_MFGPTX_CMP2(sc->sc_wdt_mfgpt))));
    309 	AMD553X_MFGPTx_NR_ENABLE(sc->sc_wdt_mfgpt, AMD553X_MFGPT0_C2_RSTEN);
    310 	DPRINTF((" AMD553X_MFGPT_NR 0x%" PRIx64 "\n", gcsc_rdmsr(AMD553X_MFGPT_NR)));
    311 }
    312 
    313 static int
    314 gcscpcib_wdog_setmode(struct sysmon_wdog *smw)
    315 {
    316 	struct gcscpcib_softc *sc = smw->smw_cookie;
    317 
    318 	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
    319 		gcscpcib_wdog_disable(sc);
    320 		return 0;
    321 	}
    322 
    323 	if (smw->smw_period == WDOG_PERIOD_DEFAULT)
    324 		smw->smw_period = 32;
    325 	else if (smw->smw_period > AMD553X_WDT_COUNTMAX) /* too big */
    326 		return EINVAL;
    327 
    328 	gcscpcib_wdog_enable(sc);
    329 
    330 	return 0;
    331 }
    332 
    333 static void
    334 gcscpcib_wdog_reset(struct gcscpcib_softc *sc)
    335 {
    336 	/* reset counter value */
    337 	gscspcib_wdog_update(sc, 0);
    338 	/* start counter & clear recent event of CMP2 */
    339 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    340 		AMD553X_MFGPTX_SETUP(sc->sc_wdt_mfgpt),
    341 	   	AMD553X_MFGPT_CNT_EN | AMD553X_MFGPT_CMP2);
    342 }
    343 
    344 static int
    345 gcscpcib_wdog_tickle(struct sysmon_wdog *smw)
    346 {
    347 	struct gcscpcib_softc *sc = smw->smw_cookie;
    348 
    349 	DPRINTF(("%s: update watchdog timer\n", __func__));
    350 	gcscpcib_wdog_reset(sc);
    351 	return 0;
    352 }
    353 
    354 #if NGPIO > 0
    355 /* GPIO support functions */
    356 static int
    357 gcscpcib_gpio_pin_read(void *arg, int pin)
    358 {
    359 	struct gcscpcib_softc *sc = arg;
    360 	uint32_t data;
    361 	int reg;
    362 
    363 	reg = AMD553X_GPIO_OUT_VAL;
    364 	if (pin > 15) {
    365 		pin &= 0x0f;
    366 		reg += AMD553X_GPIOH_OFFSET;
    367 	}
    368 	data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
    369 
    370 	return data & 1 << pin ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
    371 }
    372 
    373 static void
    374 gcscpcib_gpio_pin_write(void *arg, int pin, int value)
    375 {
    376 	struct gcscpcib_softc *sc = arg;
    377 	uint32_t data;
    378 	int reg;
    379 
    380 	reg = AMD553X_GPIO_OUT_VAL;
    381 	if (pin > 15) {
    382 		pin &= 0x0f;
    383 		reg += AMD553X_GPIOH_OFFSET;
    384 	}
    385 	if (value == 1)
    386 		data = 1 << pin;
    387 	else
    388 		data = 1 << (pin + 16);
    389 
    390 	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data);
    391 }
    392 
    393 static void
    394 gcscpcib_gpio_pin_ctl(void *arg, int pin, int flags)
    395 {
    396 	struct gcscpcib_softc *sc = arg;
    397 	int n, reg[7], val[7], nreg = 0, off = 0;
    398 
    399 	if (pin > 15) {
    400 		pin &= 0x0f;
    401 		off = AMD553X_GPIOH_OFFSET;
    402 	}
    403 
    404 	reg[nreg] = AMD553X_GPIO_IN_EN + off;
    405 	if (flags & GPIO_PIN_INPUT)
    406 		val[nreg++] = 1 << pin;
    407 	else
    408 		val[nreg++] = 1 << (pin + 16);
    409 
    410 	reg[nreg] = AMD553X_GPIO_OUT_EN + off;
    411 	if (flags & GPIO_PIN_OUTPUT)
    412 		val[nreg++] = 1 << pin;
    413 	else
    414 		val[nreg++] = 1 << (pin + 16);
    415 
    416 	reg[nreg] = AMD553X_GPIO_OD_EN + off;
    417 	if (flags & GPIO_PIN_OPENDRAIN)
    418 		val[nreg++] = 1 << pin;
    419 	else
    420 		val[nreg++] = 1 << (pin + 16);
    421 
    422 	reg[nreg] = AMD553X_GPIO_PU_EN + off;
    423 	if (flags & GPIO_PIN_PULLUP)
    424 		val[nreg++] = 1 << pin;
    425 	else
    426 		val[nreg++] = 1 << (pin + 16);
    427 
    428 	reg[nreg] = AMD553X_GPIO_PD_EN + off;
    429 	if (flags & GPIO_PIN_PULLDOWN)
    430 		val[nreg++] = 1 << pin;
    431 	else
    432 		val[nreg++] = 1 << (pin + 16);
    433 
    434 	reg[nreg] = AMD553X_GPIO_IN_INVRT_EN + off;
    435 	if (flags & GPIO_PIN_INVIN)
    436 		val[nreg++] = 1 << pin;
    437 	else
    438 		val[nreg++] = 1 << (pin + 16);
    439 
    440 	reg[nreg] = AMD553X_GPIO_OUT_INVRT_EN + off;
    441 	if (flags & GPIO_PIN_INVOUT)
    442 		val[nreg++] = 1 << pin;
    443 	else
    444 		val[nreg++] = 1 << (pin + 16);
    445 
    446 	/* set flags */
    447 	for (n = 0; n < nreg; n++)
    448 		bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg[n],
    449 		    val[n]);
    450 }
    451 #endif /* NGPIO > 0 */
    452 
    453