static irqreturn_t mfgpt_tick(int irq, void *dev_id) { u16 val = geode_mfgpt_read(mfgpt_event_clock, MFGPT_REG_SETUP); /* See if the interrupt was for us */ if (!(val & (MFGPT_SETUP_SETUP | MFGPT_SETUP_CMP2 | MFGPT_SETUP_CMP1))) return IRQ_NONE; /* Turn off the clock (and clear the event) */ mfgpt_disable_timer(mfgpt_event_clock); if (mfgpt_tick_mode == CLOCK_EVT_MODE_SHUTDOWN) return IRQ_HANDLED; /* Clear the counter */ geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_COUNTER, 0); /* Restart the clock in periodic mode */ if (mfgpt_tick_mode == CLOCK_EVT_MODE_PERIODIC) { geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2); } mfgpt_clockevent.event_handler(&mfgpt_clockevent); return IRQ_HANDLED; }
static void mfgpt_start_timer(u16 delta) { geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_CMP2, (u16) delta); geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_COUNTER, 0); geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2); }
static void geodewdt_ping(void) { /* Stop the counter */ geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0); /* Reset the counter */ geode_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0); /* Enable the counter */ geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN); }
static int geodewdt_set_heartbeat(int val) { if (val < 1 || val > GEODEWDT_MAX_SECONDS) return -EINVAL; geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0); geode_mfgpt_write(wdt_timer, MFGPT_REG_CMP2, val * GEODEWDT_HZ); geode_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0); geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN); timeout = val; return 0; }
int __init mfgpt_timer_setup(void) { int timer, ret; u16 val; timer = geode_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING); if (timer < 0) { printk(KERN_ERR "mfgpt-timer: Could not allocate a MFPGT timer\n"); return -ENODEV; } mfgpt_event_clock = timer; /* Set up the IRQ on the MFGPT side */ if (geode_mfgpt_setup_irq(mfgpt_event_clock, MFGPT_CMP2, irq)) { printk(KERN_ERR "mfgpt-timer: Could not set up IRQ %d\n", irq); return -EIO; } /* And register it with the kernel */ ret = setup_irq(irq, &mfgptirq); if (ret) { printk(KERN_ERR "mfgpt-timer: Unable to set up the interrupt.\n"); goto err; } /* Set the clock scale and enable the event mode for CMP2 */ val = MFGPT_SCALE | (3 << 8); geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP, val); /* Set up the clock event */ mfgpt_clockevent.mult = div_sc(MFGPT_HZ, NSEC_PER_SEC, mfgpt_clockevent.shift); mfgpt_clockevent.min_delta_ns = clockevent_delta2ns(0xF, &mfgpt_clockevent); mfgpt_clockevent.max_delta_ns = clockevent_delta2ns(0xFFFE, &mfgpt_clockevent); printk(KERN_INFO "mfgpt-timer: registering the MFGPT timer as a clock event.\n"); clockevents_register_device(&mfgpt_clockevent); return 0; err: geode_mfgpt_release_irq(mfgpt_event_clock, MFGPT_CMP2, irq); printk(KERN_ERR "mfgpt-timer: Unable to set up the MFGPT clock source\n"); return -EIO; }
static void geodewdt_disable(void) { geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0); geode_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0); }
static long geodewdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; int __user *p = argp; int interval; static struct watchdog_info ident = { .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = WATCHDOG_NAME, }; switch (cmd) { case WDIOC_GETSUPPORT: return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; break; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0, p); case WDIOC_SETOPTIONS: { int options, ret = -EINVAL; if (get_user(options, p)) return -EFAULT; if (options & WDIOS_DISABLECARD) { geodewdt_disable(); ret = 0; } if (options & WDIOS_ENABLECARD) { geodewdt_ping(); ret = 0; } return ret; } case WDIOC_KEEPALIVE: geodewdt_ping(); return 0; case WDIOC_SETTIMEOUT: if (get_user(interval, p)) return -EFAULT; if (geodewdt_set_heartbeat(interval)) return -EINVAL; /* Fall through */ case WDIOC_GETTIMEOUT: return put_user(timeout, p); default: return -ENOTTY; } return 0; } static const struct file_operations geodewdt_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .write = geodewdt_write, .unlocked_ioctl = geodewdt_ioctl, .open = geodewdt_open, .release = geodewdt_release, }; static struct miscdevice geodewdt_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &geodewdt_fops, }; static int __devinit geodewdt_probe(struct platform_device *dev) { int ret, timer; timer = geode_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING); if (timer == -1) { printk(KERN_ERR "geodewdt: No timers were available\n"); return -ENODEV; } wdt_timer = timer; /* Set up the timer */ geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, GEODEWDT_SCALE | (3 << 8)); /* Set up comparator 2 to reset when the event fires */ geode_mfgpt_toggle_event(wdt_timer, MFGPT_CMP2, MFGPT_EVENT_RESET, 1); /* Set up the initial timeout */ geode_mfgpt_write(wdt_timer, MFGPT_REG_CMP2, timeout * GEODEWDT_HZ); ret = misc_register(&geodewdt_miscdev); return ret; } static int __devexit geodewdt_remove(struct platform_device *dev) { misc_deregister(&geodewdt_miscdev); return 0; }
static void mfgpt_disable_timer(u16 clock) { /* avoid races by clearing CMP1 and CMP2 unconditionally */ geode_mfgpt_write(clock, MFGPT_REG_SETUP, (u16) ~MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP1 | MFGPT_SETUP_CMP2); }