1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * s526.c 4 * Sensoray s526 Comedi driver 5 * 6 * COMEDI - Linux Control and Measurement Device Interface 7 * Copyright (C) 2000 David A. Schleef <ds@schleef.org> 8 */ 9 10 /* 11 * Driver: s526 12 * Description: Sensoray 526 driver 13 * Devices: [Sensoray] 526 (s526) 14 * Author: Richie 15 * Everett Wang <everett.wang@everteq.com> 16 * Updated: Thu, 14 Sep. 2006 17 * Status: experimental 18 * 19 * Encoder works 20 * Analog input works 21 * Analog output works 22 * PWM output works 23 * Commands are not supported yet. 24 * 25 * Configuration Options: 26 * [0] - I/O port base address 27 */ 28 29 #include <linux/module.h> 30 #include "../comedidev.h" 31 32 /* 33 * Register I/O map 34 */ 35 #define S526_TIMER_REG 0x00 36 #define S526_TIMER_LOAD(x) (((x) & 0xff) << 8) 37 #define S526_TIMER_MODE ((x) << 1) 38 #define S526_TIMER_MANUAL S526_TIMER_MODE(0) 39 #define S526_TIMER_AUTO S526_TIMER_MODE(1) 40 #define S526_TIMER_RESTART BIT(0) 41 #define S526_WDOG_REG 0x02 42 #define S526_WDOG_INVERTED BIT(4) 43 #define S526_WDOG_ENA BIT(3) 44 #define S526_WDOG_INTERVAL(x) (((x) & 0x7) << 0) 45 #define S526_AO_CTRL_REG 0x04 46 #define S526_AO_CTRL_RESET BIT(3) 47 #define S526_AO_CTRL_CHAN(x) (((x) & 0x3) << 1) 48 #define S526_AO_CTRL_START BIT(0) 49 #define S526_AI_CTRL_REG 0x06 50 #define S526_AI_CTRL_DELAY BIT(15) 51 #define S526_AI_CTRL_CONV(x) (1 << (5 + ((x) & 0x9))) 52 #define S526_AI_CTRL_READ(x) (((x) & 0xf) << 1) 53 #define S526_AI_CTRL_START BIT(0) 54 #define S526_AO_REG 0x08 55 #define S526_AI_REG 0x08 56 #define S526_DIO_CTRL_REG 0x0a 57 #define S526_DIO_CTRL_DIO3_NEG BIT(15) /* irq on DIO3 neg/pos edge */ 58 #define S526_DIO_CTRL_DIO2_NEG BIT(14) /* irq on DIO2 neg/pos edge */ 59 #define S526_DIO_CTRL_DIO1_NEG BIT(13) /* irq on DIO1 neg/pos edge */ 60 #define S526_DIO_CTRL_DIO0_NEG BIT(12) /* irq on DIO0 neg/pos edge */ 61 #define S526_DIO_CTRL_GRP2_OUT BIT(11) 62 #define S526_DIO_CTRL_GRP1_OUT BIT(10) 63 #define S526_DIO_CTRL_GRP2_NEG BIT(8) /* irq on DIO[4-7] neg/pos edge */ 64 #define S526_INT_ENA_REG 0x0c 65 #define S526_INT_STATUS_REG 0x0e 66 #define S526_INT_DIO(x) BIT(8 + ((x) & 0x7)) 67 #define S526_INT_EEPROM BIT(7) /* status only */ 68 #define S526_INT_CNTR(x) BIT(3 + (3 - ((x) & 0x3))) 69 #define S526_INT_AI BIT(2) 70 #define S526_INT_AO BIT(1) 71 #define S526_INT_TIMER BIT(0) 72 #define S526_MISC_REG 0x10 73 #define S526_MISC_LED_OFF BIT(0) 74 #define S526_GPCT_LSB_REG(x) (0x12 + ((x) * 8)) 75 #define S526_GPCT_MSB_REG(x) (0x14 + ((x) * 8)) 76 #define S526_GPCT_MODE_REG(x) (0x16 + ((x) * 8)) 77 #define S526_GPCT_MODE_COUT_SRC(x) ((x) << 0) 78 #define S526_GPCT_MODE_COUT_SRC_MASK S526_GPCT_MODE_COUT_SRC(0x1) 79 #define S526_GPCT_MODE_COUT_SRC_RCAP S526_GPCT_MODE_COUT_SRC(0) 80 #define S526_GPCT_MODE_COUT_SRC_RTGL S526_GPCT_MODE_COUT_SRC(1) 81 #define S526_GPCT_MODE_COUT_POL(x) ((x) << 1) 82 #define S526_GPCT_MODE_COUT_POL_MASK S526_GPCT_MODE_COUT_POL(0x1) 83 #define S526_GPCT_MODE_COUT_POL_NORM S526_GPCT_MODE_COUT_POL(0) 84 #define S526_GPCT_MODE_COUT_POL_INV S526_GPCT_MODE_COUT_POL(1) 85 #define S526_GPCT_MODE_AUTOLOAD(x) ((x) << 2) 86 #define S526_GPCT_MODE_AUTOLOAD_MASK S526_GPCT_MODE_AUTOLOAD(0x7) 87 #define S526_GPCT_MODE_AUTOLOAD_NONE S526_GPCT_MODE_AUTOLOAD(0) 88 /* these 3 bits can be OR'ed */ 89 #define S526_GPCT_MODE_AUTOLOAD_RO S526_GPCT_MODE_AUTOLOAD(0x1) 90 #define S526_GPCT_MODE_AUTOLOAD_IXFALL S526_GPCT_MODE_AUTOLOAD(0x2) 91 #define S526_GPCT_MODE_AUTOLOAD_IXRISE S526_GPCT_MODE_AUTOLOAD(0x4) 92 #define S526_GPCT_MODE_HWCTEN_SRC(x) ((x) << 5) 93 #define S526_GPCT_MODE_HWCTEN_SRC_MASK S526_GPCT_MODE_HWCTEN_SRC(0x3) 94 #define S526_GPCT_MODE_HWCTEN_SRC_CEN S526_GPCT_MODE_HWCTEN_SRC(0) 95 #define S526_GPCT_MODE_HWCTEN_SRC_IX S526_GPCT_MODE_HWCTEN_SRC(1) 96 #define S526_GPCT_MODE_HWCTEN_SRC_IXRF S526_GPCT_MODE_HWCTEN_SRC(2) 97 #define S526_GPCT_MODE_HWCTEN_SRC_NRCAP S526_GPCT_MODE_HWCTEN_SRC(3) 98 #define S526_GPCT_MODE_CTEN_CTRL(x) ((x) << 7) 99 #define S526_GPCT_MODE_CTEN_CTRL_MASK S526_GPCT_MODE_CTEN_CTRL(0x3) 100 #define S526_GPCT_MODE_CTEN_CTRL_DIS S526_GPCT_MODE_CTEN_CTRL(0) 101 #define S526_GPCT_MODE_CTEN_CTRL_ENA S526_GPCT_MODE_CTEN_CTRL(1) 102 #define S526_GPCT_MODE_CTEN_CTRL_HW S526_GPCT_MODE_CTEN_CTRL(2) 103 #define S526_GPCT_MODE_CTEN_CTRL_INVHW S526_GPCT_MODE_CTEN_CTRL(3) 104 #define S526_GPCT_MODE_CLK_SRC(x) ((x) << 9) 105 #define S526_GPCT_MODE_CLK_SRC_MASK S526_GPCT_MODE_CLK_SRC(0x3) 106 /* if count direction control set to quadrature */ 107 #define S526_GPCT_MODE_CLK_SRC_QUADX1 S526_GPCT_MODE_CLK_SRC(0) 108 #define S526_GPCT_MODE_CLK_SRC_QUADX2 S526_GPCT_MODE_CLK_SRC(1) 109 #define S526_GPCT_MODE_CLK_SRC_QUADX4 S526_GPCT_MODE_CLK_SRC(2) 110 #define S526_GPCT_MODE_CLK_SRC_QUADX4_ S526_GPCT_MODE_CLK_SRC(3) 111 /* if count direction control set to software control */ 112 #define S526_GPCT_MODE_CLK_SRC_ARISE S526_GPCT_MODE_CLK_SRC(0) 113 #define S526_GPCT_MODE_CLK_SRC_AFALL S526_GPCT_MODE_CLK_SRC(1) 114 #define S526_GPCT_MODE_CLK_SRC_INT S526_GPCT_MODE_CLK_SRC(2) 115 #define S526_GPCT_MODE_CLK_SRC_INTHALF S526_GPCT_MODE_CLK_SRC(3) 116 #define S526_GPCT_MODE_CT_DIR(x) ((x) << 11) 117 #define S526_GPCT_MODE_CT_DIR_MASK S526_GPCT_MODE_CT_DIR(0x1) 118 /* if count direction control set to software control */ 119 #define S526_GPCT_MODE_CT_DIR_UP S526_GPCT_MODE_CT_DIR(0) 120 #define S526_GPCT_MODE_CT_DIR_DOWN S526_GPCT_MODE_CT_DIR(1) 121 #define S526_GPCT_MODE_CTDIR_CTRL(x) ((x) << 12) 122 #define S526_GPCT_MODE_CTDIR_CTRL_MASK S526_GPCT_MODE_CTDIR_CTRL(0x1) 123 #define S526_GPCT_MODE_CTDIR_CTRL_QUAD S526_GPCT_MODE_CTDIR_CTRL(0) 124 #define S526_GPCT_MODE_CTDIR_CTRL_SOFT S526_GPCT_MODE_CTDIR_CTRL(1) 125 #define S526_GPCT_MODE_LATCH_CTRL(x) ((x) << 13) 126 #define S526_GPCT_MODE_LATCH_CTRL_MASK S526_GPCT_MODE_LATCH_CTRL(0x1) 127 #define S526_GPCT_MODE_LATCH_CTRL_READ S526_GPCT_MODE_LATCH_CTRL(0) 128 #define S526_GPCT_MODE_LATCH_CTRL_EVENT S526_GPCT_MODE_LATCH_CTRL(1) 129 #define S526_GPCT_MODE_PR_SELECT(x) ((x) << 14) 130 #define S526_GPCT_MODE_PR_SELECT_MASK S526_GPCT_MODE_PR_SELECT(0x1) 131 #define S526_GPCT_MODE_PR_SELECT_PR0 S526_GPCT_MODE_PR_SELECT(0) 132 #define S526_GPCT_MODE_PR_SELECT_PR1 S526_GPCT_MODE_PR_SELECT(1) 133 /* Control/Status - R = readable, W = writeable, C = write 1 to clear */ 134 #define S526_GPCT_CTRL_REG(x) (0x18 + ((x) * 8)) 135 #define S526_GPCT_CTRL_EV_STATUS(x) ((x) << 0) /* RC */ 136 #define S526_GPCT_CTRL_EV_STATUS_MASK S526_GPCT_EV_STATUS(0xf) 137 #define S526_GPCT_CTRL_EV_STATUS_NONE S526_GPCT_EV_STATUS(0) 138 /* these 4 bits can be OR'ed */ 139 #define S526_GPCT_CTRL_EV_STATUS_ECAP S526_GPCT_EV_STATUS(0x1) 140 #define S526_GPCT_CTRL_EV_STATUS_ICAPN S526_GPCT_EV_STATUS(0x2) 141 #define S526_GPCT_CTRL_EV_STATUS_ICAPP S526_GPCT_EV_STATUS(0x4) 142 #define S526_GPCT_CTRL_EV_STATUS_RCAP S526_GPCT_EV_STATUS(0x8) 143 #define S526_GPCT_CTRL_COUT_STATUS BIT(4) /* R */ 144 #define S526_GPCT_CTRL_INDEX_STATUS BIT(5) /* R */ 145 #define S525_GPCT_CTRL_INTEN(x) ((x) << 6) /* W */ 146 #define S525_GPCT_CTRL_INTEN_MASK S526_GPCT_CTRL_INTEN(0xf) 147 #define S525_GPCT_CTRL_INTEN_NONE S526_GPCT_CTRL_INTEN(0) 148 /* these 4 bits can be OR'ed */ 149 #define S525_GPCT_CTRL_INTEN_ERROR S526_GPCT_CTRL_INTEN(0x1) 150 #define S525_GPCT_CTRL_INTEN_IXFALL S526_GPCT_CTRL_INTEN(0x2) 151 #define S525_GPCT_CTRL_INTEN_IXRISE S526_GPCT_CTRL_INTEN(0x4) 152 #define S525_GPCT_CTRL_INTEN_RO S526_GPCT_CTRL_INTEN(0x8) 153 #define S525_GPCT_CTRL_LATCH_SEL(x) ((x) << 10) /* W */ 154 #define S525_GPCT_CTRL_LATCH_SEL_MASK S526_GPCT_CTRL_LATCH_SEL(0x7) 155 #define S525_GPCT_CTRL_LATCH_SEL_NONE S526_GPCT_CTRL_LATCH_SEL(0) 156 /* these 3 bits can be OR'ed */ 157 #define S525_GPCT_CTRL_LATCH_SEL_IXFALL S526_GPCT_CTRL_LATCH_SEL(0x1) 158 #define S525_GPCT_CTRL_LATCH_SEL_IXRISE S526_GPCT_CTRL_LATCH_SEL(0x2) 159 #define S525_GPCT_CTRL_LATCH_SEL_ITIMER S526_GPCT_CTRL_LATCH_SEL(0x4) 160 #define S525_GPCT_CTRL_CT_ARM BIT(13) /* W */ 161 #define S525_GPCT_CTRL_CT_LOAD BIT(14) /* W */ 162 #define S526_GPCT_CTRL_CT_RESET BIT(15) /* W */ 163 #define S526_EEPROM_DATA_REG 0x32 164 #define S526_EEPROM_CTRL_REG 0x34 165 #define S526_EEPROM_CTRL_ADDR(x) (((x) & 0x3f) << 3) 166 #define S526_EEPROM_CTRL(x) (((x) & 0x3) << 1) 167 #define S526_EEPROM_CTRL_READ S526_EEPROM_CTRL(2) 168 #define S526_EEPROM_CTRL_START BIT(0) 169 170 struct s526_private { 171 unsigned int gpct_config[4]; 172 unsigned short ai_ctrl; 173 }; 174 175 static void s526_gpct_write(struct comedi_device *dev, 176 unsigned int chan, unsigned int val) 177 { 178 /* write high word then low word */ 179 outw((val >> 16) & 0xffff, dev->iobase + S526_GPCT_MSB_REG(chan)); 180 outw(val & 0xffff, dev->iobase + S526_GPCT_LSB_REG(chan)); 181 } 182 183 static unsigned int s526_gpct_read(struct comedi_device *dev, 184 unsigned int chan) 185 { 186 unsigned int val; 187 188 /* read the low word then high word */ 189 val = inw(dev->iobase + S526_GPCT_LSB_REG(chan)) & 0xffff; 190 val |= (inw(dev->iobase + S526_GPCT_MSB_REG(chan)) & 0xff) << 16; 191 192 return val; 193 } 194 195 static int s526_gpct_rinsn(struct comedi_device *dev, 196 struct comedi_subdevice *s, 197 struct comedi_insn *insn, 198 unsigned int *data) 199 { 200 unsigned int chan = CR_CHAN(insn->chanspec); 201 int i; 202 203 for (i = 0; i < insn->n; i++) 204 data[i] = s526_gpct_read(dev, chan); 205 206 return insn->n; 207 } 208 209 static int s526_gpct_insn_config(struct comedi_device *dev, 210 struct comedi_subdevice *s, 211 struct comedi_insn *insn, 212 unsigned int *data) 213 { 214 struct s526_private *devpriv = dev->private; 215 unsigned int chan = CR_CHAN(insn->chanspec); 216 unsigned int val; 217 218 /* 219 * Check what type of Counter the user requested 220 * data[0] contains the Application type 221 */ 222 switch (data[0]) { 223 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER: 224 /* 225 * data[0]: Application Type 226 * data[1]: Counter Mode Register Value 227 * data[2]: Pre-load Register Value 228 * data[3]: Conter Control Register 229 */ 230 devpriv->gpct_config[chan] = data[0]; 231 232 #if 1 233 /* Set Counter Mode Register */ 234 val = data[1] & 0xffff; 235 outw(val, dev->iobase + S526_GPCT_MODE_REG(chan)); 236 237 /* Reset the counter if it is software preload */ 238 if ((val & S526_GPCT_MODE_AUTOLOAD_MASK) == 239 S526_GPCT_MODE_AUTOLOAD_NONE) { 240 /* Reset the counter */ 241 outw(S526_GPCT_CTRL_CT_RESET, 242 dev->iobase + S526_GPCT_CTRL_REG(chan)); 243 /* 244 * Load the counter from PR0 245 * outw(S526_GPCT_CTRL_CT_LOAD, 246 * dev->iobase + S526_GPCT_CTRL_REG(chan)); 247 */ 248 } 249 #else 250 val = S526_GPCT_MODE_CTDIR_CTRL_QUAD; 251 252 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */ 253 if (data[1] == GPCT_X2) 254 val |= S526_GPCT_MODE_CLK_SRC_QUADX2; 255 else if (data[1] == GPCT_X4) 256 val |= S526_GPCT_MODE_CLK_SRC_QUADX4; 257 else 258 val |= S526_GPCT_MODE_CLK_SRC_QUADX1; 259 260 /* When to take into account the indexpulse: */ 261 /* 262 * if (data[2] == GPCT_IndexPhaseLowLow) { 263 * } else if (data[2] == GPCT_IndexPhaseLowHigh) { 264 * } else if (data[2] == GPCT_IndexPhaseHighLow) { 265 * } else if (data[2] == GPCT_IndexPhaseHighHigh) { 266 * } 267 */ 268 /* Take into account the index pulse? */ 269 if (data[3] == GPCT_RESET_COUNTER_ON_INDEX) { 270 /* Auto load with INDEX^ */ 271 val |= S526_GPCT_MODE_AUTOLOAD_IXRISE; 272 } 273 274 /* Set Counter Mode Register */ 275 val = data[1] & 0xffff; 276 outw(val, dev->iobase + S526_GPCT_MODE_REG(chan)); 277 278 /* Load the pre-load register */ 279 s526_gpct_write(dev, chan, data[2]); 280 281 /* Write the Counter Control Register */ 282 if (data[3]) 283 outw(data[3] & 0xffff, 284 dev->iobase + S526_GPCT_CTRL_REG(chan)); 285 286 /* Reset the counter if it is software preload */ 287 if ((val & S526_GPCT_MODE_AUTOLOAD_MASK) == 288 S526_GPCT_MODE_AUTOLOAD_NONE) { 289 /* Reset the counter */ 290 outw(S526_GPCT_CTRL_CT_RESET, 291 dev->iobase + S526_GPCT_CTRL_REG(chan)); 292 /* Load the counter from PR0 */ 293 outw(S526_GPCT_CTRL_CT_LOAD, 294 dev->iobase + S526_GPCT_CTRL_REG(chan)); 295 } 296 #endif 297 break; 298 299 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR: 300 /* 301 * data[0]: Application Type 302 * data[1]: Counter Mode Register Value 303 * data[2]: Pre-load Register 0 Value 304 * data[3]: Pre-load Register 1 Value 305 * data[4]: Conter Control Register 306 */ 307 devpriv->gpct_config[chan] = data[0]; 308 309 /* Set Counter Mode Register */ 310 val = data[1] & 0xffff; 311 /* Select PR0 */ 312 val &= ~S526_GPCT_MODE_PR_SELECT_MASK; 313 val |= S526_GPCT_MODE_PR_SELECT_PR0; 314 outw(val, dev->iobase + S526_GPCT_MODE_REG(chan)); 315 316 /* Load the pre-load register 0 */ 317 s526_gpct_write(dev, chan, data[2]); 318 319 /* Set Counter Mode Register */ 320 val = data[1] & 0xffff; 321 /* Select PR1 */ 322 val &= ~S526_GPCT_MODE_PR_SELECT_MASK; 323 val |= S526_GPCT_MODE_PR_SELECT_PR1; 324 outw(val, dev->iobase + S526_GPCT_MODE_REG(chan)); 325 326 /* Load the pre-load register 1 */ 327 s526_gpct_write(dev, chan, data[3]); 328 329 /* Write the Counter Control Register */ 330 if (data[4]) { 331 val = data[4] & 0xffff; 332 outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan)); 333 } 334 break; 335 336 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR: 337 /* 338 * data[0]: Application Type 339 * data[1]: Counter Mode Register Value 340 * data[2]: Pre-load Register 0 Value 341 * data[3]: Pre-load Register 1 Value 342 * data[4]: Conter Control Register 343 */ 344 devpriv->gpct_config[chan] = data[0]; 345 346 /* Set Counter Mode Register */ 347 val = data[1] & 0xffff; 348 /* Select PR0 */ 349 val &= ~S526_GPCT_MODE_PR_SELECT_MASK; 350 val |= S526_GPCT_MODE_PR_SELECT_PR0; 351 outw(val, dev->iobase + S526_GPCT_MODE_REG(chan)); 352 353 /* Load the pre-load register 0 */ 354 s526_gpct_write(dev, chan, data[2]); 355 356 /* Set Counter Mode Register */ 357 val = data[1] & 0xffff; 358 /* Select PR1 */ 359 val &= ~S526_GPCT_MODE_PR_SELECT_MASK; 360 val |= S526_GPCT_MODE_PR_SELECT_PR1; 361 outw(val, dev->iobase + S526_GPCT_MODE_REG(chan)); 362 363 /* Load the pre-load register 1 */ 364 s526_gpct_write(dev, chan, data[3]); 365 366 /* Write the Counter Control Register */ 367 if (data[4]) { 368 val = data[4] & 0xffff; 369 outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan)); 370 } 371 break; 372 373 default: 374 return -EINVAL; 375 } 376 377 return insn->n; 378 } 379 380 static int s526_gpct_winsn(struct comedi_device *dev, 381 struct comedi_subdevice *s, 382 struct comedi_insn *insn, 383 unsigned int *data) 384 { 385 struct s526_private *devpriv = dev->private; 386 unsigned int chan = CR_CHAN(insn->chanspec); 387 388 inw(dev->iobase + S526_GPCT_MODE_REG(chan)); /* Is this required? */ 389 390 /* Check what Application of Counter this channel is configured for */ 391 switch (devpriv->gpct_config[chan]) { 392 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR: 393 /* 394 * data[0] contains the PULSE_WIDTH 395 * data[1] contains the PULSE_PERIOD 396 * @pre PULSE_PERIOD > PULSE_WIDTH > 0 397 * The above periods must be expressed as a multiple of the 398 * pulse frequency on the selected source 399 */ 400 if ((data[1] <= data[0]) || !data[0]) 401 return -EINVAL; 402 /* to write the PULSE_WIDTH */ 403 fallthrough; 404 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER: 405 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR: 406 s526_gpct_write(dev, chan, data[0]); 407 break; 408 409 default: 410 return -EINVAL; 411 } 412 413 return insn->n; 414 } 415 416 static int s526_eoc(struct comedi_device *dev, 417 struct comedi_subdevice *s, 418 struct comedi_insn *insn, 419 unsigned long context) 420 { 421 unsigned int status; 422 423 status = inw(dev->iobase + S526_INT_STATUS_REG); 424 if (status & context) { 425 /* we got our eoc event, clear it */ 426 outw(context, dev->iobase + S526_INT_STATUS_REG); 427 return 0; 428 } 429 return -EBUSY; 430 } 431 432 static int s526_ai_insn_read(struct comedi_device *dev, 433 struct comedi_subdevice *s, 434 struct comedi_insn *insn, 435 unsigned int *data) 436 { 437 struct s526_private *devpriv = dev->private; 438 unsigned int chan = CR_CHAN(insn->chanspec); 439 unsigned int ctrl; 440 unsigned int val; 441 int ret; 442 int i; 443 444 ctrl = S526_AI_CTRL_CONV(chan) | S526_AI_CTRL_READ(chan) | 445 S526_AI_CTRL_START; 446 if (ctrl != devpriv->ai_ctrl) { 447 /* 448 * The multiplexor needs to change, enable the 15us 449 * delay for the first sample. 450 */ 451 devpriv->ai_ctrl = ctrl; 452 ctrl |= S526_AI_CTRL_DELAY; 453 } 454 455 for (i = 0; i < insn->n; i++) { 456 /* trigger conversion */ 457 outw(ctrl, dev->iobase + S526_AI_CTRL_REG); 458 ctrl &= ~S526_AI_CTRL_DELAY; 459 460 /* wait for conversion to end */ 461 ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AI); 462 if (ret) 463 return ret; 464 465 val = inw(dev->iobase + S526_AI_REG); 466 data[i] = comedi_offset_munge(s, val); 467 } 468 469 return insn->n; 470 } 471 472 static int s526_ao_insn_write(struct comedi_device *dev, 473 struct comedi_subdevice *s, 474 struct comedi_insn *insn, 475 unsigned int *data) 476 { 477 unsigned int chan = CR_CHAN(insn->chanspec); 478 unsigned int ctrl = S526_AO_CTRL_CHAN(chan); 479 unsigned int val = s->readback[chan]; 480 int ret; 481 int i; 482 483 outw(ctrl, dev->iobase + S526_AO_CTRL_REG); 484 ctrl |= S526_AO_CTRL_START; 485 486 for (i = 0; i < insn->n; i++) { 487 val = data[i]; 488 outw(val, dev->iobase + S526_AO_REG); 489 outw(ctrl, dev->iobase + S526_AO_CTRL_REG); 490 491 /* wait for conversion to end */ 492 ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AO); 493 if (ret) 494 return ret; 495 } 496 s->readback[chan] = val; 497 498 return insn->n; 499 } 500 501 static int s526_dio_insn_bits(struct comedi_device *dev, 502 struct comedi_subdevice *s, 503 struct comedi_insn *insn, 504 unsigned int *data) 505 { 506 if (comedi_dio_update_state(s, data)) 507 outw(s->state, dev->iobase + S526_DIO_CTRL_REG); 508 509 data[1] = inw(dev->iobase + S526_DIO_CTRL_REG) & 0xff; 510 511 return insn->n; 512 } 513 514 static int s526_dio_insn_config(struct comedi_device *dev, 515 struct comedi_subdevice *s, 516 struct comedi_insn *insn, 517 unsigned int *data) 518 { 519 unsigned int chan = CR_CHAN(insn->chanspec); 520 unsigned int mask; 521 int ret; 522 523 /* 524 * Digital I/O can be configured as inputs or outputs in 525 * groups of 4; DIO group 1 (DIO0-3) and DIO group 2 (DIO4-7). 526 */ 527 if (chan < 4) 528 mask = 0x0f; 529 else 530 mask = 0xf0; 531 532 ret = comedi_dio_insn_config(dev, s, insn, data, mask); 533 if (ret) 534 return ret; 535 536 if (s->io_bits & 0x0f) 537 s->state |= S526_DIO_CTRL_GRP1_OUT; 538 else 539 s->state &= ~S526_DIO_CTRL_GRP1_OUT; 540 if (s->io_bits & 0xf0) 541 s->state |= S526_DIO_CTRL_GRP2_OUT; 542 else 543 s->state &= ~S526_DIO_CTRL_GRP2_OUT; 544 545 outw(s->state, dev->iobase + S526_DIO_CTRL_REG); 546 547 return insn->n; 548 } 549 550 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it) 551 { 552 struct s526_private *devpriv; 553 struct comedi_subdevice *s; 554 int ret; 555 556 ret = comedi_request_region(dev, it->options[0], 0x40); 557 if (ret) 558 return ret; 559 560 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 561 if (!devpriv) 562 return -ENOMEM; 563 564 ret = comedi_alloc_subdevices(dev, 4); 565 if (ret) 566 return ret; 567 568 /* General-Purpose Counter/Timer (GPCT) */ 569 s = &dev->subdevices[0]; 570 s->type = COMEDI_SUBD_COUNTER; 571 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL; 572 s->n_chan = 4; 573 s->maxdata = 0x00ffffff; 574 s->insn_read = s526_gpct_rinsn; 575 s->insn_config = s526_gpct_insn_config; 576 s->insn_write = s526_gpct_winsn; 577 578 /* 579 * Analog Input subdevice 580 * channels 0 to 7 are the regular differential inputs 581 * channel 8 is "reference 0" (+10V) 582 * channel 9 is "reference 1" (0V) 583 */ 584 s = &dev->subdevices[1]; 585 s->type = COMEDI_SUBD_AI; 586 s->subdev_flags = SDF_READABLE | SDF_DIFF; 587 s->n_chan = 10; 588 s->maxdata = 0xffff; 589 s->range_table = &range_bipolar10; 590 s->len_chanlist = 16; 591 s->insn_read = s526_ai_insn_read; 592 593 /* Analog Output subdevice */ 594 s = &dev->subdevices[2]; 595 s->type = COMEDI_SUBD_AO; 596 s->subdev_flags = SDF_WRITABLE; 597 s->n_chan = 4; 598 s->maxdata = 0xffff; 599 s->range_table = &range_bipolar10; 600 s->insn_write = s526_ao_insn_write; 601 602 ret = comedi_alloc_subdev_readback(s); 603 if (ret) 604 return ret; 605 606 /* Digital I/O subdevice */ 607 s = &dev->subdevices[3]; 608 s->type = COMEDI_SUBD_DIO; 609 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 610 s->n_chan = 8; 611 s->maxdata = 1; 612 s->range_table = &range_digital; 613 s->insn_bits = s526_dio_insn_bits; 614 s->insn_config = s526_dio_insn_config; 615 616 return 0; 617 } 618 619 static struct comedi_driver s526_driver = { 620 .driver_name = "s526", 621 .module = THIS_MODULE, 622 .attach = s526_attach, 623 .detach = comedi_legacy_detach, 624 }; 625 module_comedi_driver(s526_driver); 626 627 MODULE_AUTHOR("Comedi https://www.comedi.org"); 628 MODULE_DESCRIPTION("Comedi low-level driver"); 629 MODULE_LICENSE("GPL"); 630