1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * COMEDI driver for the watchdog subdevice found on some addi-data boards 4 * Copyright (c) 2013 H Hartley Sweeten <hsweeten@visionengravers.com> 5 * 6 * Based on implementations in various addi-data COMEDI drivers. 7 * 8 * COMEDI - Linux Control and Measurement Device Interface 9 * Copyright (C) 1998 David A. Schleef <ds@schleef.org> 10 */ 11 12#include <linux/module.h> 13#include <linux/comedi/comedidev.h> 14#include "addi_tcw.h" 15#include "addi_watchdog.h" 16 17struct addi_watchdog_private { 18 unsigned long iobase; 19 unsigned int wdog_ctrl; 20}; 21 22/* 23 * The watchdog subdevice is configured with two INSN_CONFIG instructions: 24 * 25 * Enable the watchdog and set the reload timeout: 26 * data[0] = INSN_CONFIG_ARM 27 * data[1] = timeout reload value 28 * 29 * Disable the watchdog: 30 * data[0] = INSN_CONFIG_DISARM 31 */ 32static int addi_watchdog_insn_config(struct comedi_device *dev, 33 struct comedi_subdevice *s, 34 struct comedi_insn *insn, 35 unsigned int *data) 36{ 37 struct addi_watchdog_private *spriv = s->private; 38 unsigned int reload; 39 40 switch (data[0]) { 41 case INSN_CONFIG_ARM: 42 spriv->wdog_ctrl = ADDI_TCW_CTRL_ENA; 43 reload = data[1] & s->maxdata; 44 outl(reload, spriv->iobase + ADDI_TCW_RELOAD_REG); 45 46 /* Time base is 20ms, let the user know the timeout */ 47 dev_info(dev->class_dev, "watchdog enabled, timeout:%dms\n", 48 20 * reload + 20); 49 break; 50 case INSN_CONFIG_DISARM: 51 spriv->wdog_ctrl = 0; 52 break; 53 default: 54 return -EINVAL; 55 } 56 57 outl(spriv->wdog_ctrl, spriv->iobase + ADDI_TCW_CTRL_REG); 58 59 return insn->n; 60} 61 62static int addi_watchdog_insn_read(struct comedi_device *dev, 63 struct comedi_subdevice *s, 64 struct comedi_insn *insn, 65 unsigned int *data) 66{ 67 struct addi_watchdog_private *spriv = s->private; 68 int i; 69 70 for (i = 0; i < insn->n; i++) 71 data[i] = inl(spriv->iobase + ADDI_TCW_STATUS_REG); 72 73 return insn->n; 74} 75 76static int addi_watchdog_insn_write(struct comedi_device *dev, 77 struct comedi_subdevice *s, 78 struct comedi_insn *insn, 79 unsigned int *data) 80{ 81 struct addi_watchdog_private *spriv = s->private; 82 int i; 83 84 if (spriv->wdog_ctrl == 0) { 85 dev_warn(dev->class_dev, "watchdog is disabled\n"); 86 return -EINVAL; 87 } 88 89 /* "ping" the watchdog */ 90 for (i = 0; i < insn->n; i++) { 91 outl(spriv->wdog_ctrl | ADDI_TCW_CTRL_TRIG, 92 spriv->iobase + ADDI_TCW_CTRL_REG); 93 } 94 95 return insn->n; 96} 97 98void addi_watchdog_reset(unsigned long iobase) 99{ 100 outl(0x0, iobase + ADDI_TCW_CTRL_REG); 101 outl(0x0, iobase + ADDI_TCW_RELOAD_REG); 102} 103EXPORT_SYMBOL_GPL(addi_watchdog_reset); 104 105int addi_watchdog_init(struct comedi_subdevice *s, unsigned long iobase) 106{ 107 struct addi_watchdog_private *spriv; 108 109 spriv = comedi_alloc_spriv(s, sizeof(*spriv)); 110 if (!spriv) 111 return -ENOMEM; 112 113 spriv->iobase = iobase; 114 115 s->type = COMEDI_SUBD_TIMER; 116 s->subdev_flags = SDF_WRITABLE; 117 s->n_chan = 1; 118 s->maxdata = 0xff; 119 s->insn_config = addi_watchdog_insn_config; 120 s->insn_read = addi_watchdog_insn_read; 121 s->insn_write = addi_watchdog_insn_write; 122 123 return 0; 124} 125EXPORT_SYMBOL_GPL(addi_watchdog_init); 126 127static int __init addi_watchdog_module_init(void) 128{ 129 return 0; 130} 131module_init(addi_watchdog_module_init); 132 133static void __exit addi_watchdog_module_exit(void) 134{ 135} 136module_exit(addi_watchdog_module_exit); 137 138MODULE_DESCRIPTION("ADDI-DATA Watchdog subdevice"); 139MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>"); 140MODULE_LICENSE("GPL"); 141