1/* 2 comedi/drivers/pcm724.c 3 4 Drew Csillag <drew_csillag@yahoo.com> 5 6 hardware driver for Advantech card: 7 card: PCM-3724 8 driver: pcm3724 9 10 Options for PCM-3724 11 [0] - IO Base 12*/ 13/* 14Driver: pcm3724 15Description: Advantech PCM-3724 16Author: Drew Csillag <drew_csillag@yahoo.com> 17Devices: [Advantech] PCM-3724 (pcm724) 18Status: tested 19 20This is driver for digital I/O boards PCM-3724 with 48 DIO. 21It needs 8255.o for operations and only immediate mode is supported. 22See the source for configuration details. 23 24Copy/pasted/hacked from pcm724.c 25*/ 26/* 27 * check_driver overrides: 28 * struct comedi_insn 29 */ 30 31#include <linux/module.h> 32#include "../comedidev.h" 33 34#include "8255.h" 35 36#define BUF_C0 0x1 37#define BUF_B0 0x2 38#define BUF_A0 0x4 39#define BUF_C1 0x8 40#define BUF_B1 0x10 41#define BUF_A1 0x20 42 43#define GATE_A0 0x4 44#define GATE_B0 0x2 45#define GATE_C0 0x1 46#define GATE_A1 0x20 47#define GATE_B1 0x10 48#define GATE_C1 0x8 49 50/* used to track configured dios */ 51struct priv_pcm3724 { 52 int dio_1; 53 int dio_2; 54}; 55 56static int compute_buffer(int config, int devno, struct comedi_subdevice *s) 57{ 58 /* 1 in io_bits indicates output */ 59 if (s->io_bits & 0x0000ff) { 60 if (devno == 0) 61 config |= BUF_A0; 62 else 63 config |= BUF_A1; 64 } 65 if (s->io_bits & 0x00ff00) { 66 if (devno == 0) 67 config |= BUF_B0; 68 else 69 config |= BUF_B1; 70 } 71 if (s->io_bits & 0xff0000) { 72 if (devno == 0) 73 config |= BUF_C0; 74 else 75 config |= BUF_C1; 76 } 77 return config; 78} 79 80static void do_3724_config(struct comedi_device *dev, 81 struct comedi_subdevice *s, int chanspec) 82{ 83 struct comedi_subdevice *s_dio1 = &dev->subdevices[0]; 84 struct comedi_subdevice *s_dio2 = &dev->subdevices[1]; 85 int config; 86 int buffer_config; 87 unsigned long port_8255_cfg; 88 89 config = I8255_CTRL_CW; 90 buffer_config = 0; 91 92 /* 1 in io_bits indicates output, 1 in config indicates input */ 93 if (!(s->io_bits & 0x0000ff)) 94 config |= I8255_CTRL_A_IO; 95 96 if (!(s->io_bits & 0x00ff00)) 97 config |= I8255_CTRL_B_IO; 98 99 if (!(s->io_bits & 0xff0000)) 100 config |= I8255_CTRL_C_HI_IO | I8255_CTRL_C_LO_IO; 101 102 buffer_config = compute_buffer(0, 0, s_dio1); 103 buffer_config = compute_buffer(buffer_config, 1, s_dio2); 104 105 if (s == s_dio1) 106 port_8255_cfg = dev->iobase + I8255_CTRL_REG; 107 else 108 port_8255_cfg = dev->iobase + I8255_SIZE + I8255_CTRL_REG; 109 110 outb(buffer_config, dev->iobase + 8); /* update buffer register */ 111 112 outb(config, port_8255_cfg); 113} 114 115static void enable_chan(struct comedi_device *dev, struct comedi_subdevice *s, 116 int chanspec) 117{ 118 struct priv_pcm3724 *priv = dev->private; 119 struct comedi_subdevice *s_dio1 = &dev->subdevices[0]; 120 unsigned int mask; 121 int gatecfg; 122 123 gatecfg = 0; 124 125 mask = 1 << CR_CHAN(chanspec); 126 if (s == s_dio1) 127 priv->dio_1 |= mask; 128 else 129 priv->dio_2 |= mask; 130 131 if (priv->dio_1 & 0xff0000) 132 gatecfg |= GATE_C0; 133 134 if (priv->dio_1 & 0xff00) 135 gatecfg |= GATE_B0; 136 137 if (priv->dio_1 & 0xff) 138 gatecfg |= GATE_A0; 139 140 if (priv->dio_2 & 0xff0000) 141 gatecfg |= GATE_C1; 142 143 if (priv->dio_2 & 0xff00) 144 gatecfg |= GATE_B1; 145 146 if (priv->dio_2 & 0xff) 147 gatecfg |= GATE_A1; 148 149 outb(gatecfg, dev->iobase + 9); 150} 151 152/* overriding the 8255 insn config */ 153static int subdev_3724_insn_config(struct comedi_device *dev, 154 struct comedi_subdevice *s, 155 struct comedi_insn *insn, 156 unsigned int *data) 157{ 158 unsigned int chan = CR_CHAN(insn->chanspec); 159 unsigned int mask; 160 int ret; 161 162 if (chan < 8) 163 mask = 0x0000ff; 164 else if (chan < 16) 165 mask = 0x00ff00; 166 else if (chan < 20) 167 mask = 0x0f0000; 168 else 169 mask = 0xf00000; 170 171 ret = comedi_dio_insn_config(dev, s, insn, data, mask); 172 if (ret) 173 return ret; 174 175 do_3724_config(dev, s, insn->chanspec); 176 enable_chan(dev, s, insn->chanspec); 177 178 return insn->n; 179} 180 181static int pcm3724_attach(struct comedi_device *dev, 182 struct comedi_devconfig *it) 183{ 184 struct priv_pcm3724 *priv; 185 struct comedi_subdevice *s; 186 int ret, i; 187 188 priv = comedi_alloc_devpriv(dev, sizeof(*priv)); 189 if (!priv) 190 return -ENOMEM; 191 192 ret = comedi_request_region(dev, it->options[0], 0x10); 193 if (ret) 194 return ret; 195 196 ret = comedi_alloc_subdevices(dev, 2); 197 if (ret) 198 return ret; 199 200 for (i = 0; i < dev->n_subdevices; i++) { 201 s = &dev->subdevices[i]; 202 ret = subdev_8255_init(dev, s, NULL, i * I8255_SIZE); 203 if (ret) 204 return ret; 205 s->insn_config = subdev_3724_insn_config; 206 } 207 return 0; 208} 209 210static struct comedi_driver pcm3724_driver = { 211 .driver_name = "pcm3724", 212 .module = THIS_MODULE, 213 .attach = pcm3724_attach, 214 .detach = comedi_legacy_detach, 215}; 216module_comedi_driver(pcm3724_driver); 217 218MODULE_AUTHOR("Comedi http://www.comedi.org"); 219MODULE_DESCRIPTION("Comedi low-level driver"); 220MODULE_LICENSE("GPL"); 221