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