1 /* $NetBSD: gcscpcib.c,v 1.7 2009/05/04 12:41:09 cegger Exp $ */
2 /* $OpenBSD: gcscpcib.c,v 1.6 2007/11/17 17:02:47 mbalmer Exp $ */
5 * Copyright (c) 2008 Yojiro UO <yuo@nui.org>
6 * Copyright (c) 2007 Marc Balmer <mbalmer@openbsd.org>
7 * Copyright (c) 2007 Michael Shalayeff
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.
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.
24 * AMD CS5535/CS5536 series LPC bridge also containing timer, watchdog and GPIO.
26 #include <sys/cdefs.h>
27 __KERNEL_RCSID(0, "$NetBSD: gcscpcib.c,v 1.7 2009/05/04 12:41:09 cegger Exp $");
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/device.h>
35 #include <sys/sysctl.h>
36 #include <sys/timetc.h>
40 #include <machine/cpufunc.h>
43 #include <dev/gpio/gpiovar.h>
45 #include <dev/pci/pcireg.h>
46 #include <dev/pci/pcivar.h>
47 #include <dev/pci/pcidevs.h>
49 #include <dev/sysmon/sysmonvar.h>
51 #include <arch/i386/pci/gcscpcibreg.h>
52 #include <arch/x86/pci/pcibvar.h>
54 /* define if you need to select MFGPT for watchdog manually (0-5). */
55 /* #define AMD553X_WDT_FORCEUSEMFGPT 0 */
56 /* select precision of watchdog timer (default value = 0.25sec (4Hz tick) */
57 #define AMD553X_MFGPT_PRESCALE AMD553X_MFGPT_DIV_8K /* 32K/8K = 4Hz */
59 /* #define GCSCPCIB_DEBUG */
61 #define DPRINTF(x) printf x
66 /* 1 bit replace (not support multiple bit)*/
67 #define AMD553X_MFGPTx_NR_DISABLE(x, bit) \
68 ( wrmsr(AMD553X_MFGPT_NR, rdmsr(AMD553X_MFGPT_NR) & ~((bit) << (x))) )
69 #define AMD553X_MFGPTx_NR_ENABLE(x, bit) \
70 ( wrmsr(AMD553X_MFGPT_NR, rdmsr(AMD553X_MFGPT_NR) | ((bit) << (x))) )
72 /* caliculate watchdog timer setting */
73 #define AMD553X_WDT_TICK (1<<(AMD553X_MFGPT_DIV_32K - AMD553X_MFGPT_PRESCALE))
74 #define AMD553X_WDT_COUNTMAX (0xffff / AMD553X_WDT_TICK)
76 struct gcscpcib_softc
{
77 /* we call pcibattach() which assumes softc starts like this: */
78 struct pcib_softc sc_pcib
;
82 struct timecounter sc_timecounter
;
83 bus_space_tag_t sc_iot
;
84 bus_space_handle_t sc_ioh
;
87 struct sysmon_wdog sc_smw
;
92 bus_space_tag_t sc_gpio_iot
;
93 bus_space_handle_t sc_gpio_ioh
;
94 struct gpio_chipset_tag sc_gpio_gc
;
95 gpio_pin_t sc_gpio_pins
[AMD553X_GPIO_NPINS
];
98 /* SMbus/i2c interface */
100 bus_space_tag_t sc_smbus_iot
;
101 bus_space_handle_t sc_smbus_ioh
;
102 i2c_addr_t sc_smbus_slaveaddr
; /* address of smbus slave */
103 struct i2c_controller sc_i2c
; /* i2c controller info */
104 krwlock_t sc_smbus_rwlock
;
108 static int gcscpcib_match(device_t
, cfdata_t
, void *);
109 static void gcscpcib_attach(device_t
, device_t
, void *);
111 CFATTACH_DECL_NEW(gcscpcib
, sizeof(struct gcscpcib_softc
),
112 gcscpcib_match
, gcscpcib_attach
, NULL
, NULL
);
114 static u_int
gcscpcib_get_timecount(struct timecounter
*tc
);
115 static int gscspcib_scan_mfgpt(struct gcscpcib_softc
*sc
);
116 static void gscspcib_wdog_update(struct gcscpcib_softc
*, uint16_t);
117 static int gcscpcib_wdog_setmode(struct sysmon_wdog
*smw
);
118 static int gcscpcib_wdog_tickle(struct sysmon_wdog
*smw
);
119 static void gcscpcib_wdog_enable(struct gcscpcib_softc
*sc
);
120 static void gcscpcib_wdog_disable(struct gcscpcib_softc
*sc
);
121 static void gcscpcib_wdog_reset(struct gcscpcib_softc
*sc
);
124 static int gcscpcib_gpio_pin_read(void *, int);
125 static void gcscpcib_gpio_pin_write(void *, int, int);
126 static void gcscpcib_gpio_pin_ctl(void *, int, int);
130 gcscpcib_match(device_t parent
, cfdata_t match
, void *aux
)
132 struct pci_attach_args
*pa
= aux
;
134 if (PCI_CLASS(pa
->pa_class
) != PCI_CLASS_BRIDGE
||
135 PCI_SUBCLASS(pa
->pa_class
) != PCI_SUBCLASS_BRIDGE_ISA
)
138 switch (PCI_PRODUCT(pa
->pa_id
)) {
139 case PCI_PRODUCT_NS_CS5535_ISA
:
140 case PCI_PRODUCT_AMD_CS5536_PCIB
:
141 return 2; /* supersede pcib(4) */
148 gcscpcib_attach(device_t parent
, device_t self
, void *aux
)
150 struct gcscpcib_softc
*sc
= device_private(self
);
151 struct pci_attach_args
*pa
= (struct pci_attach_args
*)aux
;
152 struct timecounter
*tc
= &sc
->sc_timecounter
;
156 struct gpiobus_attach_args gba
;
161 sc
->sc_pcib
.sc_pc
= pa
->pa_pc
;
162 sc
->sc_pcib
.sc_tag
= pa
->pa_tag
;
163 sc
->sc_iot
= pa
->pa_iot
;
165 sc
->sc_gpio_iot
= pa
->pa_iot
;
168 /* Attach the PCI-ISA bridge at first */
169 pcibattach(parent
, self
, aux
);
171 /* Attach the CS553[56] timer */
172 tc
->tc_get_timecount
= gcscpcib_get_timecount
;
173 tc
->tc_counter_mask
= 0xffffffff;
174 tc
->tc_frequency
= 3579545;
175 tc
->tc_name
= device_xname(self
);
176 tc
->tc_quality
= 1000;
180 /* Attach the watchdog timer */
181 wdtbase
= rdmsr(MSR_LBAR_MFGPT
) & 0xffff;
182 if (bus_space_map(sc
->sc_iot
, wdtbase
, 64, 0, &sc
->sc_ioh
)) {
183 aprint_error_dev(self
, "can't map memory space for WDT\n");
185 /* select a MFGPT timer for watchdog counter */
186 if (!gscspcib_scan_mfgpt(sc
)) {
187 aprint_error_dev(self
, "can't alloc an MFGPT for WDT\n");
191 * Note: MFGPGx_SETUP register is write once register
192 * except CNT_EN and CMP[12]EV bit.
194 bus_space_write_2(sc
->sc_iot
, sc
->sc_ioh
,
195 AMD553X_MFGPTX_SETUP(sc
->sc_wdt_mfgpt
),
196 AMD553X_MFGPT_CMP2EV
| AMD553X_MFGPT_CMP2
|
197 AMD553X_MFGPT_PRESCALE
);
199 /* disable watchdog action */
200 AMD553X_MFGPTx_NR_DISABLE(sc
->sc_wdt_mfgpt
,
201 AMD553X_MFGPT0_C2_NMIM
);
202 AMD553X_MFGPTx_NR_DISABLE(sc
->sc_wdt_mfgpt
,
203 AMD553X_MFGPT0_C2_RSTEN
);
205 sc
->sc_smw
.smw_name
= device_xname(self
);
206 sc
->sc_smw
.smw_cookie
= sc
;
207 sc
->sc_smw
.smw_setmode
= gcscpcib_wdog_setmode
;
208 sc
->sc_smw
.smw_tickle
= gcscpcib_wdog_tickle
;
209 sc
->sc_smw
.smw_period
= 32;
210 aprint_normal_dev(self
, "Watchdog Timer via MFGPT%d",
217 /* map GPIO I/O space */
218 gpiobase
= rdmsr(MSR_LBAR_GPIO
) & 0xffff;
219 if (!bus_space_map(sc
->sc_gpio_iot
, gpiobase
, 0xff, 0,
221 aprint_normal(", GPIO");
223 /* initialize pin array */
224 for (i
= 0; i
< AMD553X_GPIO_NPINS
; i
++) {
225 sc
->sc_gpio_pins
[i
].pin_num
= i
;
226 sc
->sc_gpio_pins
[i
].pin_caps
= GPIO_PIN_INPUT
|
227 GPIO_PIN_OUTPUT
| GPIO_PIN_OPENDRAIN
|
228 GPIO_PIN_PUSHPULL
| GPIO_PIN_TRISTATE
|
229 GPIO_PIN_PULLUP
| GPIO_PIN_PULLDOWN
|
230 GPIO_PIN_INVIN
| GPIO_PIN_INVOUT
;
232 /* read initial state */
233 sc
->sc_gpio_pins
[i
].pin_state
=
234 gcscpcib_gpio_pin_read(sc
, i
);
237 /* create controller tag */
238 sc
->sc_gpio_gc
.gp_cookie
= sc
;
239 sc
->sc_gpio_gc
.gp_pin_read
= gcscpcib_gpio_pin_read
;
240 sc
->sc_gpio_gc
.gp_pin_write
= gcscpcib_gpio_pin_write
;
241 sc
->sc_gpio_gc
.gp_pin_ctl
= gcscpcib_gpio_pin_ctl
;
243 gba
.gba_gc
= &sc
->sc_gpio_gc
;
244 gba
.gba_pins
= sc
->sc_gpio_pins
;
245 gba
.gba_npins
= AMD553X_GPIO_NPINS
;
252 /* Attach GPIO framework */
254 config_found_ia(self
, "gpiobus", &gba
, gpiobus_print
);
257 /* Register Watchdog timer to SMW */
259 if (sysmon_wdog_register(&sc
->sc_smw
) != 0)
260 aprint_error_dev(self
,
261 "cannot register wdog with sysmon\n");
266 gcscpcib_get_timecount(struct timecounter
*tc
)
268 return rdmsr(AMD553X_TMC
);
271 /* Watchdog timer support functions */
273 gscspcib_scan_mfgpt(struct gcscpcib_softc
*sc
)
277 #ifdef AMD553X_WDT_FORCEUSEMFGPT
278 if (AMD553X_WDT_FORCEUSEMFGPT
>= AMD553X_MFGPT_MAX
)
280 sc
->sc_wdt_mfgpt
= AMD553X_WDT_FORCEUSEMFGPT
;
282 #endif /* AMD553X_WDT_FORCEUSEMFGPT */
284 for (i
= 0; i
< AMD553X_MFGPT_MAX
; i
++){
285 if (bus_space_read_2(sc
->sc_iot
, sc
->sc_ioh
,
286 AMD553X_MFGPTX_SETUP(i
)) == 0) {
287 /* found unused MFGPT, use it. */
288 sc
->sc_wdt_mfgpt
= i
;
292 /* no MFGPT for WDT found */
298 gscspcib_wdog_update(struct gcscpcib_softc
*sc
, uint16_t count
)
300 #ifdef GCSCPCIB_DEBUG
302 cnt
= bus_space_read_2(sc
->sc_iot
, sc
->sc_ioh
,
303 AMD553X_MFGPTX_CNT(sc
->sc_wdt_mfgpt
));
305 if (count
> AMD553X_WDT_COUNTMAX
)
306 count
= AMD553X_WDT_COUNTMAX
;
308 * CS553X databook recommend following sequence to re-initialize
309 * the counter and compare value. (See p165 on CS5536 databook)
310 * 1: suspend counter: clear counter enable bit to 0
311 * 2: reset (and NMI, if need) enable bit in MSRs
312 * 3: update counter & clear event flags
313 * 4: resume (2) operation
314 * 5: re-enable counter
316 bus_space_write_2(sc
->sc_iot
, sc
->sc_ioh
,
317 AMD553X_MFGPTX_SETUP(sc
->sc_wdt_mfgpt
), 0);
318 AMD553X_MFGPTx_NR_DISABLE(sc
->sc_wdt_mfgpt
, AMD553X_MFGPT0_C2_RSTEN
);
319 bus_space_write_2(sc
->sc_iot
, sc
->sc_ioh
,
320 AMD553X_MFGPTX_CNT(sc
->sc_wdt_mfgpt
), count
);
321 bus_space_write_2(sc
->sc_iot
, sc
->sc_ioh
,
322 AMD553X_MFGPTX_SETUP(sc
->sc_wdt_mfgpt
),
323 AMD553X_MFGPT_CMP1
| AMD553X_MFGPT_CMP2
);
324 AMD553X_MFGPTx_NR_ENABLE(sc
->sc_wdt_mfgpt
, AMD553X_MFGPT0_C2_RSTEN
);
325 bus_space_write_2(sc
->sc_iot
, sc
->sc_ioh
,
326 AMD553X_MFGPTX_SETUP(sc
->sc_wdt_mfgpt
),
327 AMD553X_MFGPT_CNT_EN
| AMD553X_MFGPT_CMP2
);
329 DPRINTF(("%s: MFGPT%d_CNT= %d -> %d (expect: %d), MFGPT_NR=%#.8x\n",
330 __func__
, sc
->sc_wdt_mfgpt
, cnt
,
331 bus_space_read_2(sc
->sc_iot
, sc
->sc_ioh
,
332 AMD553X_MFGPTX_CNT(sc
->sc_wdt_mfgpt
)), count
,
333 (uint32_t)(rdmsr(AMD553X_MFGPT_NR
))));
337 gcscpcib_wdog_disable(struct gcscpcib_softc
*sc
)
340 * stop counter and reset counter value
341 * Note: as the MFGPTx_SETUP is write once register, the prescaler
342 * setting, clock select and compare mode are kept till reset.
344 gscspcib_wdog_update(sc
, 0);
345 bus_space_write_2(sc
->sc_iot
, sc
->sc_ioh
,
346 AMD553X_MFGPTX_SETUP(sc
->sc_wdt_mfgpt
), 0);
348 /* disable watchdog action */
349 DPRINTF(("%s: disable watchdog action\n", __func__
));
350 AMD553X_MFGPTx_NR_DISABLE(sc
->sc_wdt_mfgpt
, AMD553X_MFGPT0_C2_RSTEN
);
354 gcscpcib_wdog_enable(struct gcscpcib_softc
*sc
)
356 int period
= sc
->sc_smw
.smw_period
;
358 /* clear recent event flag and counter value, and start counter */
359 gcscpcib_wdog_reset(sc
);
360 /* set watchdog timer limit, counter tick is 0.5sec */
361 bus_space_write_2(sc
->sc_iot
, sc
->sc_ioh
,
362 AMD553X_MFGPTX_CMP2(sc
->sc_wdt_mfgpt
),
363 period
* AMD553X_WDT_TICK
);
365 /* enable watchdog action */
366 DPRINTF(("%s: enable watchdog action. (MFGPT0_CMP2= %d)\n", __func__
,
367 bus_space_read_2(sc
->sc_iot
, sc
->sc_ioh
,
368 AMD553X_MFGPTX_CMP2(sc
->sc_wdt_mfgpt
))));
369 AMD553X_MFGPTx_NR_ENABLE(sc
->sc_wdt_mfgpt
, AMD553X_MFGPT0_C2_RSTEN
);
373 gcscpcib_wdog_setmode(struct sysmon_wdog
*smw
)
375 struct gcscpcib_softc
*sc
= smw
->smw_cookie
;
377 if ((smw
->smw_mode
& WDOG_MODE_MASK
) == WDOG_MODE_DISARMED
) {
378 gcscpcib_wdog_disable(sc
);
382 if (smw
->smw_period
== WDOG_PERIOD_DEFAULT
)
383 smw
->smw_period
= 32;
384 else if (smw
->smw_period
> AMD553X_WDT_COUNTMAX
) /* too big */
387 gcscpcib_wdog_enable(sc
);
393 gcscpcib_wdog_reset(struct gcscpcib_softc
*sc
)
395 /* reset counter value */
396 gscspcib_wdog_update(sc
, 0);
397 /* start counter & clear recent event of CMP2 */
398 bus_space_write_2(sc
->sc_iot
, sc
->sc_ioh
,
399 AMD553X_MFGPTX_SETUP(sc
->sc_wdt_mfgpt
),
400 AMD553X_MFGPT_CNT_EN
| AMD553X_MFGPT_CMP2
);
404 gcscpcib_wdog_tickle(struct sysmon_wdog
*smw
)
406 struct gcscpcib_softc
*sc
= smw
->smw_cookie
;
408 DPRINTF(("%s: update watchdog timer\n", __func__
));
409 gcscpcib_wdog_reset(sc
);
414 /* GPIO support functions */
416 gcscpcib_gpio_pin_read(void *arg
, int pin
)
418 struct gcscpcib_softc
*sc
= arg
;
422 reg
= AMD553X_GPIO_OUT_VAL
;
425 reg
+= AMD553X_GPIOH_OFFSET
;
427 data
= bus_space_read_4(sc
->sc_gpio_iot
, sc
->sc_gpio_ioh
, reg
);
429 return data
& 1 << pin
? GPIO_PIN_HIGH
: GPIO_PIN_LOW
;
433 gcscpcib_gpio_pin_write(void *arg
, int pin
, int value
)
435 struct gcscpcib_softc
*sc
= arg
;
439 reg
= AMD553X_GPIO_OUT_VAL
;
442 reg
+= AMD553X_GPIOH_OFFSET
;
447 data
= 1 << (pin
+ 16);
449 bus_space_write_4(sc
->sc_gpio_iot
, sc
->sc_gpio_ioh
, reg
, data
);
453 gcscpcib_gpio_pin_ctl(void *arg
, int pin
, int flags
)
455 struct gcscpcib_softc
*sc
= arg
;
456 int n
, reg
[7], val
[7], nreg
= 0, off
= 0;
460 off
= AMD553X_GPIOH_OFFSET
;
463 reg
[nreg
] = AMD553X_GPIO_IN_EN
+ off
;
464 if (flags
& GPIO_PIN_INPUT
)
465 val
[nreg
++] = 1 << pin
;
467 val
[nreg
++] = 1 << (pin
+ 16);
469 reg
[nreg
] = AMD553X_GPIO_OUT_EN
+ off
;
470 if (flags
& GPIO_PIN_OUTPUT
)
471 val
[nreg
++] = 1 << pin
;
473 val
[nreg
++] = 1 << (pin
+ 16);
475 reg
[nreg
] = AMD553X_GPIO_OD_EN
+ off
;
476 if (flags
& GPIO_PIN_OPENDRAIN
)
477 val
[nreg
++] = 1 << pin
;
479 val
[nreg
++] = 1 << (pin
+ 16);
481 reg
[nreg
] = AMD553X_GPIO_PU_EN
+ off
;
482 if (flags
& GPIO_PIN_PULLUP
)
483 val
[nreg
++] = 1 << pin
;
485 val
[nreg
++] = 1 << (pin
+ 16);
487 reg
[nreg
] = AMD553X_GPIO_PD_EN
+ off
;
488 if (flags
& GPIO_PIN_PULLDOWN
)
489 val
[nreg
++] = 1 << pin
;
491 val
[nreg
++] = 1 << (pin
+ 16);
493 reg
[nreg
] = AMD553X_GPIO_IN_INVRT_EN
+ off
;
494 if (flags
& GPIO_PIN_INVIN
)
495 val
[nreg
++] = 1 << pin
;
497 val
[nreg
++] = 1 << (pin
+ 16);
499 reg
[nreg
] = AMD553X_GPIO_OUT_INVRT_EN
+ off
;
500 if (flags
& GPIO_PIN_INVOUT
)
501 val
[nreg
++] = 1 << pin
;
503 val
[nreg
++] = 1 << (pin
+ 16);
506 for (n
= 0; n
< nreg
; n
++)
507 bus_space_write_4(sc
->sc_gpio_iot
, sc
->sc_gpio_ioh
, reg
[n
],
510 #endif /* NGPIO > 0 */