u8 timer_val; /* content for the wd_time register */ char minutes_mode;
u8 pulse_val; /* pulse width flag */ char pulse_mode; /* enable pulse output mode? */
};
struct fintek_wdt_pdata { enum chips type;
};
/* Super I/O functions */ staticinlineint superio_inb(int base, int reg)
{
outb(reg, base); return inb(base + 1);
}
staticint superio_inw(int base, int reg)
{ int val;
val = superio_inb(base, reg) << 8;
val |= superio_inb(base, reg + 1); return val;
}
staticinlinevoid superio_outb(int base, int reg, u8 val)
{
outb(reg, base);
outb(val, base + 1);
}
staticinlinevoid superio_set_bit(int base, int reg, int bit)
{ unsignedlong val = superio_inb(base, reg);
__set_bit(bit, &val);
superio_outb(base, reg, val);
}
staticinlinevoid superio_clear_bit(int base, int reg, int bit)
{ unsignedlong val = superio_inb(base, reg);
__clear_bit(bit, &val);
superio_outb(base, reg, val);
}
staticinlineint superio_enter(int base)
{ /* Don't step on other drivers' I/O space by accident */ if (!request_muxed_region(base, 2, DRVNAME)) {
pr_err("I/O address 0x%04x already in use\n", (int)base); return -EBUSY;
}
/* according to the datasheet the key must be sent twice! */
outb(SIO_UNLOCK_KEY, base);
outb(SIO_UNLOCK_KEY, base);
return 0;
}
staticinlinevoid superio_select(int base, int ld)
{
outb(SIO_REG_LDSEL, base);
outb(ld, base + 1);
}
err = superio_enter(wd->sioaddr); if (err) return err;
superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
if (wd->minutes_mode) /* select minutes for timer units */
superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
F71808FG_FLAG_WD_UNIT); else /* select seconds for timer units */
superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
F71808FG_FLAG_WD_UNIT);
/* Set timer value */
superio_outb(wd->sioaddr, F71808FG_REG_WD_TIME,
wd->timer_val);
/* Make sure we don't die as soon as the watchdog is enabled below */
err = fintek_wdt_keepalive(wdd); if (err) return err;
err = superio_enter(wd->sioaddr); if (err) return err;
superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
/* Watchdog pin configuration */ switch (wd->type) { case f71808fg: /* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */
superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT2, 3);
superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 3); break;
case f71862fg: if (f71862fg_pin == 63) { /* SPI must be disabled first to use this pin! */
superio_clear_bit(wd->sioaddr, SIO_REG_ROM_ADDR_SEL, 6);
superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT3, 4);
} elseif (f71862fg_pin == 56) {
superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1);
} break;
case f71868: case f71869: /* GPIO14 --> WDTRST# */
superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT1, 4); break;
case f71882fg: /* Set pin 56 to WDTRST# */
superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1); break;
case f71889fg: /* set pin 40 to WDTRST# */
superio_outb(wd->sioaddr, SIO_REG_MFUNCT3,
superio_inb(wd->sioaddr, SIO_REG_MFUNCT3) & 0xcf); break;
case f81803: /* Enable TSI Level register bank */
superio_clear_bit(wd->sioaddr, SIO_REG_CLOCK_SEL, 3); /* Set pin 27 to WDTRST# */
superio_outb(wd->sioaddr, SIO_REG_TSI_LEVEL_SEL, 0x5f &
superio_inb(wd->sioaddr, SIO_REG_TSI_LEVEL_SEL)); break;
case f81865: /* Set pin 70 to WDTRST# */
superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 5); break;
case f81866: case f81966: /* * GPIO1 Control Register when 27h BIT3:2 = 01 & BIT0 = 0. * The PIN 70(GPIO15/WDTRST) is controlled by 2Ch: * BIT5: 0 -> WDTRST# * 1 -> GPIO15
*/
tmp = superio_inb(wd->sioaddr, SIO_F81866_REG_PORT_SEL);
tmp &= ~(BIT(3) | BIT(0));
tmp |= BIT(2);
superio_outb(wd->sioaddr, SIO_F81866_REG_PORT_SEL, tmp);
/* * We don't want WDTMOUT_STS to stick around till regular reboot. * Write 1 to the bit to clear it to zero.
*/
superio_outb(sioaddr, F71808FG_REG_WDT_CONF,
wdt_conf | BIT(F71808FG_FLAG_WDTMOUT_STS));
wdd = &wd->wdd;
if (fintek_wdt_is_running(wd, wdt_conf))
set_bit(WDOG_HW_RUNNING, &wdd->status);
if (wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS))
wdd->bootstatus = WDIOF_CARDRESET;
/* * WATCHDOG_HANDLE_BOOT_ENABLED can result in keepalive being directly * called without a set_timeout before, so it needs to be done here * unconditionally.
*/
fintek_wdt_set_timeout(wdd, wdd->timeout);
fintek_wdt_set_pulse_width(wd, pulse_width);
if (start_withtimeout) {
err = fintek_wdt_start(wdd); if (err) {
dev_err(dev, "cannot start watchdog timer\n"); return err;
}
set_bit(WDOG_HW_RUNNING, &wdd->status);
dev_info(dev, "watchdog started with initial timeout of %u sec\n",
start_withtimeout);
}
return devm_watchdog_register_device(dev, wdd);
}
staticint __init fintek_wdt_find(int sioaddr)
{ enum chips type;
u16 devid; int err = superio_enter(sioaddr); if (err) return err;
devid = superio_inw(sioaddr, SIO_REG_MANID); if (devid != SIO_FINTEK_ID) {
pr_debug("Not a Fintek device\n");
err = -ENODEV; gotoexit;
}
devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID); switch (devid) { case SIO_F71808_ID:
type = f71808fg; break; case SIO_F71862_ID:
type = f71862fg; break; case SIO_F71868_ID:
type = f71868; break; case SIO_F71869_ID: case SIO_F71869A_ID:
type = f71869; break; case SIO_F71882_ID:
type = f71882fg; break; case SIO_F71889_ID:
type = f71889fg; break; case SIO_F71858_ID: /* Confirmed (by datasheet) not to have a watchdog. */
err = -ENODEV; gotoexit; case SIO_F81803_ID:
type = f81803; break; case SIO_F81865_ID:
type = f81865; break; case SIO_F81866_ID:
type = f81866; break; case SIO_F81966_ID:
type = f81966; break; default:
pr_info("Unrecognized Fintek device: %04x\n",
(unsignedint)devid);
err = -ENODEV; gotoexit;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.