Home | History | Annotate | Line # | Download | only in ic
gcscpcib.c revision 1.3
      1 /* $NetBSD: gcscpcib.c,v 1.3 2012/03/02 12:56:51 nonaka 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.3 2012/03/02 12:56:51 nonaka 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_ia(self, "gpiobus", &gba, gpiobus_print);
    194 #endif
    195 
    196 	/* Register Watchdog timer to SMW */
    197 	if (wdt) {
    198 		if (sysmon_wdog_register(&sc->sc_smw) != 0)
    199 			aprint_error_dev(self,
    200 			    "cannot register wdog with sysmon\n");
    201 	}
    202 }
    203 
    204 static u_int
    205 gcscpcib_get_timecount(struct timecounter *tc)
    206 {
    207         return gcsc_rdmsr(AMD553X_TMC);
    208 }
    209 
    210 /* Watchdog timer support functions */
    211 static int
    212 gscspcib_scan_mfgpt(struct gcscpcib_softc *sc)
    213 {
    214 	int i;
    215 
    216 #ifdef AMD553X_WDT_FORCEUSEMFGPT
    217 	if (AMD553X_WDT_FORCEUSEMFGPT >= AMD553X_MFGPT_MAX)
    218 		return 0;
    219 	sc->sc_wdt_mfgpt = AMD553X_WDT_FORCEUSEMFGPT;
    220 	return 1;
    221 #endif /* AMD553X_WDT_FORCEUSEMFGPT */
    222 
    223 	for (i = 0; i < AMD553X_MFGPT_MAX; i++){
    224 		if (bus_space_read_2(sc->sc_iot, sc->sc_ioh,
    225 		    AMD553X_MFGPTX_SETUP(i)) == 0) {
    226 			/* found unused MFGPT, use it. */
    227 			sc->sc_wdt_mfgpt = i;
    228 			return 1;
    229 		}
    230 	}
    231 	/* no MFGPT for WDT found */
    232 	return 0;
    233 }
    234 
    235 
    236 static void
    237 gscspcib_wdog_update(struct gcscpcib_softc *sc, uint16_t count)
    238 {
    239 #ifdef GCSCPCIB_DEBUG
    240 	uint16_t cnt;
    241 	cnt = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
    242 		AMD553X_MFGPTX_CNT(sc->sc_wdt_mfgpt));
    243 #endif
    244 	if (count > AMD553X_WDT_COUNTMAX)
    245 		count = AMD553X_WDT_COUNTMAX;
    246 	/*
    247 	 * CS553X databook recommend following sequence to re-initialize
    248 	 * the counter and compare value. (See p165 on CS5536 databook)
    249 	 * 1: suspend counter: clear counter enable bit to 0
    250 	 * 2: reset (and NMI, if need) enable bit in MSRs
    251 	 * 3: update counter & clear event flags
    252 	 * 4: resume (2) operation
    253 	 * 5: re-enable counter
    254 	 */
    255 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    256 		AMD553X_MFGPTX_SETUP(sc->sc_wdt_mfgpt), 0);
    257 	AMD553X_MFGPTx_NR_DISABLE(sc->sc_wdt_mfgpt, AMD553X_MFGPT0_C2_RSTEN);
    258 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    259 		AMD553X_MFGPTX_CNT(sc->sc_wdt_mfgpt), count);
    260 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    261 		AMD553X_MFGPTX_SETUP(sc->sc_wdt_mfgpt),
    262 			AMD553X_MFGPT_CMP1 | AMD553X_MFGPT_CMP2);
    263 	AMD553X_MFGPTx_NR_ENABLE(sc->sc_wdt_mfgpt, AMD553X_MFGPT0_C2_RSTEN);
    264 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    265 		AMD553X_MFGPTX_SETUP(sc->sc_wdt_mfgpt),
    266 	    	AMD553X_MFGPT_CNT_EN | AMD553X_MFGPT_CMP2);
    267 
    268 	DPRINTF(("%s: MFGPT%d_CNT= %d -> %d (expect: %d), MFGPT_NR=%#.8x\n",
    269 		__func__, sc->sc_wdt_mfgpt, cnt,
    270 		bus_space_read_2(sc->sc_iot, sc->sc_ioh,
    271 			 AMD553X_MFGPTX_CNT(sc->sc_wdt_mfgpt)), count,
    272 		(uint32_t)(gcsc_rdmsr(AMD553X_MFGPT_NR))));
    273 }
    274 
    275 static void
    276 gcscpcib_wdog_disable(struct gcscpcib_softc *sc)
    277 {
    278 	/*
    279 	 * stop counter and reset counter value
    280 	 * Note: as the MFGPTx_SETUP is write once register, the prescaler
    281 	 * setting, clock select and compare mode are kept till reset.
    282 	 */
    283 	gscspcib_wdog_update(sc, 0);
    284 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    285 		AMD553X_MFGPTX_SETUP(sc->sc_wdt_mfgpt), 0);
    286 
    287 	/* disable watchdog action */
    288 	DPRINTF(("%s: disable watchdog action\n", __func__));
    289 	AMD553X_MFGPTx_NR_DISABLE(sc->sc_wdt_mfgpt, AMD553X_MFGPT0_C2_RSTEN);
    290 }
    291 
    292 static void
    293 gcscpcib_wdog_enable(struct gcscpcib_softc *sc)
    294 {
    295 	int period = sc->sc_smw.smw_period;
    296 
    297 	/* clear recent event flag and counter value, and start counter */
    298 	gcscpcib_wdog_reset(sc);
    299 	/* set watchdog timer limit, counter tick is 0.5sec */
    300 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    301 		AMD553X_MFGPTX_CMP2(sc->sc_wdt_mfgpt),
    302 			period * AMD553X_WDT_TICK);
    303 
    304 	/* enable watchdog action */
    305 	DPRINTF(("%s: enable watchdog action. (MFGPT0_CMP2= %d)", __func__,
    306 	        bus_space_read_2(sc->sc_iot, sc->sc_ioh,
    307 			 AMD553X_MFGPTX_CMP2(sc->sc_wdt_mfgpt))));
    308 	AMD553X_MFGPTx_NR_ENABLE(sc->sc_wdt_mfgpt, AMD553X_MFGPT0_C2_RSTEN);
    309 	DPRINTF((" AMD553X_MFGPT_NR 0x%" PRIx64 "\n", gcsc_rdmsr(AMD553X_MFGPT_NR)));
    310 }
    311 
    312 static int
    313 gcscpcib_wdog_setmode(struct sysmon_wdog *smw)
    314 {
    315 	struct gcscpcib_softc *sc = smw->smw_cookie;
    316 
    317 	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
    318 		gcscpcib_wdog_disable(sc);
    319 		return 0;
    320 	}
    321 
    322 	if (smw->smw_period == WDOG_PERIOD_DEFAULT)
    323 		smw->smw_period = 32;
    324 	else if (smw->smw_period > AMD553X_WDT_COUNTMAX) /* too big */
    325 		return EINVAL;
    326 
    327 	gcscpcib_wdog_enable(sc);
    328 
    329 	return 0;
    330 }
    331 
    332 static void
    333 gcscpcib_wdog_reset(struct gcscpcib_softc *sc)
    334 {
    335 	/* reset counter value */
    336 	gscspcib_wdog_update(sc, 0);
    337 	/* start counter & clear recent event of CMP2 */
    338 	bus_space_write_2(sc->sc_iot, sc->sc_ioh,
    339 		AMD553X_MFGPTX_SETUP(sc->sc_wdt_mfgpt),
    340 	   	AMD553X_MFGPT_CNT_EN | AMD553X_MFGPT_CMP2);
    341 }
    342 
    343 static int
    344 gcscpcib_wdog_tickle(struct sysmon_wdog *smw)
    345 {
    346 	struct gcscpcib_softc *sc = smw->smw_cookie;
    347 
    348 	DPRINTF(("%s: update watchdog timer\n", __func__));
    349 	gcscpcib_wdog_reset(sc);
    350 	return 0;
    351 }
    352 
    353 #if NGPIO > 0
    354 /* GPIO support functions */
    355 static int
    356 gcscpcib_gpio_pin_read(void *arg, int pin)
    357 {
    358 	struct gcscpcib_softc *sc = arg;
    359 	uint32_t data;
    360 	int reg;
    361 
    362 	reg = AMD553X_GPIO_OUT_VAL;
    363 	if (pin > 15) {
    364 		pin &= 0x0f;
    365 		reg += AMD553X_GPIOH_OFFSET;
    366 	}
    367 	data = bus_space_read_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg);
    368 
    369 	return data & 1 << pin ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
    370 }
    371 
    372 static void
    373 gcscpcib_gpio_pin_write(void *arg, int pin, int value)
    374 {
    375 	struct gcscpcib_softc *sc = arg;
    376 	uint32_t data;
    377 	int reg;
    378 
    379 	reg = AMD553X_GPIO_OUT_VAL;
    380 	if (pin > 15) {
    381 		pin &= 0x0f;
    382 		reg += AMD553X_GPIOH_OFFSET;
    383 	}
    384 	if (value == 1)
    385 		data = 1 << pin;
    386 	else
    387 		data = 1 << (pin + 16);
    388 
    389 	bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg, data);
    390 }
    391 
    392 static void
    393 gcscpcib_gpio_pin_ctl(void *arg, int pin, int flags)
    394 {
    395 	struct gcscpcib_softc *sc = arg;
    396 	int n, reg[7], val[7], nreg = 0, off = 0;
    397 
    398 	if (pin > 15) {
    399 		pin &= 0x0f;
    400 		off = AMD553X_GPIOH_OFFSET;
    401 	}
    402 
    403 	reg[nreg] = AMD553X_GPIO_IN_EN + off;
    404 	if (flags & GPIO_PIN_INPUT)
    405 		val[nreg++] = 1 << pin;
    406 	else
    407 		val[nreg++] = 1 << (pin + 16);
    408 
    409 	reg[nreg] = AMD553X_GPIO_OUT_EN + off;
    410 	if (flags & GPIO_PIN_OUTPUT)
    411 		val[nreg++] = 1 << pin;
    412 	else
    413 		val[nreg++] = 1 << (pin + 16);
    414 
    415 	reg[nreg] = AMD553X_GPIO_OD_EN + off;
    416 	if (flags & GPIO_PIN_OPENDRAIN)
    417 		val[nreg++] = 1 << pin;
    418 	else
    419 		val[nreg++] = 1 << (pin + 16);
    420 
    421 	reg[nreg] = AMD553X_GPIO_PU_EN + off;
    422 	if (flags & GPIO_PIN_PULLUP)
    423 		val[nreg++] = 1 << pin;
    424 	else
    425 		val[nreg++] = 1 << (pin + 16);
    426 
    427 	reg[nreg] = AMD553X_GPIO_PD_EN + off;
    428 	if (flags & GPIO_PIN_PULLDOWN)
    429 		val[nreg++] = 1 << pin;
    430 	else
    431 		val[nreg++] = 1 << (pin + 16);
    432 
    433 	reg[nreg] = AMD553X_GPIO_IN_INVRT_EN + off;
    434 	if (flags & GPIO_PIN_INVIN)
    435 		val[nreg++] = 1 << pin;
    436 	else
    437 		val[nreg++] = 1 << (pin + 16);
    438 
    439 	reg[nreg] = AMD553X_GPIO_OUT_INVRT_EN + off;
    440 	if (flags & GPIO_PIN_INVOUT)
    441 		val[nreg++] = 1 << pin;
    442 	else
    443 		val[nreg++] = 1 << (pin + 16);
    444 
    445 	/* set flags */
    446 	for (n = 0; n < nreg; n++)
    447 		bus_space_write_4(sc->sc_gpio_iot, sc->sc_gpio_ioh, reg[n],
    448 		    val[n]);
    449 }
    450 #endif /* NGPIO > 0 */
    451 
    452