Staging: comedi: amplc_dio200: Protect counter subdevice with spinlock
[pandora-kernel.git] / drivers / staging / comedi / drivers / amplc_dio200.c
index 8eb6765..bf27617 100644 (file)
@@ -460,6 +460,7 @@ struct dio200_subdev_8254 {
        int has_clk_gat_sce;
        unsigned clock_src[3];  /* Current clock sources */
        unsigned gate_src[3];   /* Current gate sources */
+       spinlock_t spinlock;
 };
 
 struct dio200_subdev_intr {
@@ -1042,8 +1043,11 @@ dio200_subdev_8254_read(struct comedi_device *dev, struct comedi_subdevice *s,
 {
        struct dio200_subdev_8254 *subpriv = s->private;
        int chan = CR_CHAN(insn->chanspec);
+       unsigned long flags;
 
+       spin_lock_irqsave(&subpriv->spinlock, flags);
        data[0] = i8254_read(subpriv->iobase, 0, chan);
+       spin_unlock_irqrestore(&subpriv->spinlock, flags);
 
        return 1;
 }
@@ -1057,8 +1061,11 @@ dio200_subdev_8254_write(struct comedi_device *dev, struct comedi_subdevice *s,
 {
        struct dio200_subdev_8254 *subpriv = s->private;
        int chan = CR_CHAN(insn->chanspec);
+       unsigned long flags;
 
+       spin_lock_irqsave(&subpriv->spinlock, flags);
        i8254_write(subpriv->iobase, 0, chan, data[0]);
+       spin_unlock_irqrestore(&subpriv->spinlock, flags);
 
        return 1;
 }
@@ -1151,14 +1158,16 @@ dio200_subdev_8254_config(struct comedi_device *dev, struct comedi_subdevice *s,
                          struct comedi_insn *insn, unsigned int *data)
 {
        struct dio200_subdev_8254 *subpriv = s->private;
-       int ret;
+       int ret = 0;
        int chan = CR_CHAN(insn->chanspec);
+       unsigned long flags;
 
+       spin_lock_irqsave(&subpriv->spinlock, flags);
        switch (data[0]) {
        case INSN_CONFIG_SET_COUNTER_MODE:
                ret = i8254_set_mode(subpriv->iobase, 0, chan, data[1]);
                if (ret < 0)
-                       return -EINVAL;
+                       ret = -EINVAL;
                break;
        case INSN_CONFIG_8254_READ_STATUS:
                data[1] = i8254_status(subpriv->iobase, 0, chan);
@@ -1166,30 +1175,35 @@ dio200_subdev_8254_config(struct comedi_device *dev, struct comedi_subdevice *s,
        case INSN_CONFIG_SET_GATE_SRC:
                ret = dio200_set_gate_src(subpriv, chan, data[2]);
                if (ret < 0)
-                       return -EINVAL;
+                       ret = -EINVAL;
                break;
        case INSN_CONFIG_GET_GATE_SRC:
                ret = dio200_get_gate_src(subpriv, chan);
-               if (ret < 0)
-                       return -EINVAL;
+               if (ret < 0) {
+                       ret = -EINVAL;
+                       break;
+               }
                data[2] = ret;
                break;
        case INSN_CONFIG_SET_CLOCK_SRC:
                ret = dio200_set_clock_src(subpriv, chan, data[1]);
                if (ret < 0)
-                       return -EINVAL;
+                       ret = -EINVAL;
                break;
        case INSN_CONFIG_GET_CLOCK_SRC:
                ret = dio200_get_clock_src(subpriv, chan, &data[2]);
-               if (ret < 0)
-                       return -EINVAL;
+               if (ret < 0) {
+                       ret = -EINVAL;
+                       break;
+               }
                data[1] = ret;
                break;
        default:
-               return -EINVAL;
+               ret = -EINVAL;
                break;
        }
-       return insn->n;
+       spin_unlock_irqrestore(&subpriv->spinlock, flags);
+       return ret < 0 ? ret : insn->n;
 }
 
 /*
@@ -1222,6 +1236,7 @@ dio200_subdev_8254_init(struct comedi_device *dev, struct comedi_subdevice *s,
        s->insn_write = dio200_subdev_8254_write;
        s->insn_config = dio200_subdev_8254_config;
 
+       spin_lock_init(&subpriv->spinlock);
        subpriv->iobase = offset + iobase;
        subpriv->has_clk_gat_sce = has_clk_gat_sce;
        if (has_clk_gat_sce) {