/** * @file me8254.c * * @brief 8254 subdevice instance. * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de) * @author Guenter Gebhardt */ /* * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de) * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __KERNEL__ # define __KERNEL__ #endif /* * Includes */ #include #include #include #include #include #include "medefines.h" #include "meinternal.h" #include "meerror.h" #include "medebug.h" #include "me8254_reg.h" #include "me8254.h" /* * Defines */ #define ME8254_NUMBER_CHANNELS 1 /**< One channel per counter. */ #define ME8254_CTR_WIDTH 16 /**< One counter has 16 bits. */ /* * Functions */ static int me8254_io_reset_subdevice(struct me_subdevice *subdevice, struct file *filep, int flags) { me8254_subdevice_t *instance; uint8_t clk_src; int err = ME_ERRNO_SUCCESS; PDEBUG("executed.\n"); instance = (me8254_subdevice_t *) subdevice; if (flags) { PERROR("Invalid flag specified.\n"); return ME_ERRNO_INVALID_FLAGS; } ME_SUBDEVICE_ENTER; spin_lock(&instance->subdevice_lock); spin_lock(instance->ctrl_reg_lock); if (instance->ctr_idx == 0) outb(ME8254_CTRL_SC0 | ME8254_CTRL_LM | ME8254_CTRL_M0 | ME8254_CTRL_BIN, instance->ctrl_reg); else if (instance->ctr_idx == 1) outb(ME8254_CTRL_SC1 | ME8254_CTRL_LM | ME8254_CTRL_M0 | ME8254_CTRL_BIN, instance->ctrl_reg); else outb(ME8254_CTRL_SC2 | ME8254_CTRL_LM | ME8254_CTRL_M0 | ME8254_CTRL_BIN, instance->ctrl_reg); spin_unlock(instance->ctrl_reg_lock); outb(0x00, instance->val_reg); outb(0x00, instance->val_reg); spin_lock(instance->clk_src_reg_lock); clk_src = inb(instance->clk_src_reg); switch (instance->device_id) { case PCI_DEVICE_ID_MEILHAUS_ME1400: case PCI_DEVICE_ID_MEILHAUS_ME140A: case PCI_DEVICE_ID_MEILHAUS_ME140B: case PCI_DEVICE_ID_MEILHAUS_ME14E0: case PCI_DEVICE_ID_MEILHAUS_ME14EA: case PCI_DEVICE_ID_MEILHAUS_ME14EB: if (instance->me8254_idx == 0) { if (instance->ctr_idx == 0) clk_src &= ~(ME1400AB_8254_A_0_CLK_SRC_10MHZ | ME1400AB_8254_A_0_CLK_SRC_QUARZ); else if (instance->ctr_idx == 1) clk_src &= ~(ME1400AB_8254_A_1_CLK_SRC_PREV); else clk_src &= ~(ME1400AB_8254_A_2_CLK_SRC_PREV); } else { if (instance->ctr_idx == 0) clk_src &= ~(ME1400AB_8254_B_0_CLK_SRC_10MHZ | ME1400AB_8254_B_0_CLK_SRC_QUARZ); else if (instance->ctr_idx == 1) clk_src &= ~(ME1400AB_8254_B_1_CLK_SRC_PREV); else clk_src &= ~(ME1400AB_8254_B_2_CLK_SRC_PREV); } break; case PCI_DEVICE_ID_MEILHAUS_ME140C: case PCI_DEVICE_ID_MEILHAUS_ME140D: switch (instance->me8254_idx) { case 0: case 2: case 4: case 6: case 8: if (instance->ctr_idx == 0) clk_src &= ~(ME1400CD_8254_ACE_0_CLK_SRC_MASK); else if (instance->ctr_idx == 1) clk_src &= ~(ME1400CD_8254_ACE_1_CLK_SRC_MASK); else clk_src &= ~(ME1400CD_8254_ACE_2_CLK_SRC_MASK); break; default: if (instance->ctr_idx == 0) clk_src &= ~(ME1400CD_8254_BD_0_CLK_SRC_MASK); else if (instance->ctr_idx == 1) clk_src &= ~(ME1400CD_8254_BD_1_CLK_SRC_MASK); else clk_src &= ~(ME1400CD_8254_BD_2_CLK_SRC_MASK); break; } break; case PCI_DEVICE_ID_MEILHAUS_ME4610: case PCI_DEVICE_ID_MEILHAUS_ME4660: case PCI_DEVICE_ID_MEILHAUS_ME4660I: case PCI_DEVICE_ID_MEILHAUS_ME4660S: case PCI_DEVICE_ID_MEILHAUS_ME4660IS: case PCI_DEVICE_ID_MEILHAUS_ME4670: case PCI_DEVICE_ID_MEILHAUS_ME4670I: case PCI_DEVICE_ID_MEILHAUS_ME4670S: case PCI_DEVICE_ID_MEILHAUS_ME4670IS: case PCI_DEVICE_ID_MEILHAUS_ME4680: case PCI_DEVICE_ID_MEILHAUS_ME4680I: case PCI_DEVICE_ID_MEILHAUS_ME4680S: case PCI_DEVICE_ID_MEILHAUS_ME4680IS: case PCI_DEVICE_ID_MEILHAUS_ME8100_A: case PCI_DEVICE_ID_MEILHAUS_ME8100_B: /* No clock source register available */ break; default: PERROR("Invalid device type.\n"); err = ME_ERRNO_INTERNAL; } if (!err) outb(clk_src, instance->clk_src_reg); spin_unlock(instance->clk_src_reg_lock); spin_unlock(&instance->subdevice_lock); ME_SUBDEVICE_EXIT; return err; } static int me1400_ab_ref_config(me8254_subdevice_t * instance, int ref) { uint8_t clk_src; spin_lock(instance->clk_src_reg_lock); clk_src = inb(instance->clk_src_reg); switch (ref) { case ME_REF_CTR_EXTERNAL: if (instance->me8254_idx == 0) { if (instance->ctr_idx == 0) clk_src &= ~(ME1400AB_8254_A_0_CLK_SRC_QUARZ); else if (instance->ctr_idx == 1) clk_src &= ~(ME1400AB_8254_A_1_CLK_SRC_PREV); else clk_src &= ~(ME1400AB_8254_A_2_CLK_SRC_PREV); } else { if (instance->ctr_idx == 0) clk_src &= ~(ME1400AB_8254_B_0_CLK_SRC_QUARZ); else if (instance->ctr_idx == 1) clk_src &= ~(ME1400AB_8254_B_1_CLK_SRC_PREV); else clk_src &= ~(ME1400AB_8254_B_2_CLK_SRC_PREV); } break; case ME_REF_CTR_PREVIOUS: if (instance->me8254_idx == 0) { if (instance->ctr_idx == 0) { PERROR("Invalid reference.\n"); spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_SINGLE_CONFIG; } else if (instance->ctr_idx == 1) clk_src |= (ME1400AB_8254_A_1_CLK_SRC_PREV); else clk_src |= (ME1400AB_8254_A_2_CLK_SRC_PREV); } else { if (instance->ctr_idx == 0) { PERROR("Invalid reference.\n"); spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_SINGLE_CONFIG; } else if (instance->ctr_idx == 1) clk_src |= (ME1400AB_8254_B_1_CLK_SRC_PREV); else clk_src |= (ME1400AB_8254_B_2_CLK_SRC_PREV); } break; case ME_REF_CTR_INTERNAL_1MHZ: if (instance->me8254_idx == 0) { if (instance->ctr_idx == 0) { clk_src |= (ME1400AB_8254_A_0_CLK_SRC_QUARZ); clk_src &= ~(ME1400AB_8254_A_0_CLK_SRC_10MHZ); } else { PERROR("Invalid reference.\n"); spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_SINGLE_CONFIG; } } else { if (instance->ctr_idx == 0) { clk_src |= (ME1400AB_8254_B_0_CLK_SRC_QUARZ); clk_src &= ~(ME1400AB_8254_B_0_CLK_SRC_10MHZ); } else { PERROR("Invalid reference.\n"); spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_SINGLE_CONFIG; } } break; case ME_REF_CTR_INTERNAL_10MHZ: if (instance->me8254_idx == 0) { if (instance->ctr_idx == 0) { clk_src |= (ME1400AB_8254_A_0_CLK_SRC_QUARZ); clk_src |= (ME1400AB_8254_A_0_CLK_SRC_10MHZ); } else { PERROR("Invalid reference.\n"); spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_SINGLE_CONFIG; } } else { if (instance->ctr_idx == 0) { clk_src |= (ME1400AB_8254_A_0_CLK_SRC_QUARZ); clk_src |= (ME1400AB_8254_A_0_CLK_SRC_10MHZ); } else { PERROR("Invalid reference.\n"); spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_SINGLE_CONFIG; } } break; default: PERROR("Invalid reference.\n"); spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_REF; } outb(clk_src, instance->clk_src_reg); spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_SUCCESS; } static int me1400_cd_ref_config(me8254_subdevice_t * instance, int ref) { uint8_t clk_src; spin_lock(instance->clk_src_reg_lock); clk_src = inb(instance->clk_src_reg); switch (ref) { case ME_REF_CTR_EXTERNAL: switch (instance->me8254_idx) { case 0: case 2: case 4: case 6: case 8: if (instance->ctr_idx == 0) clk_src &= ~(ME1400CD_8254_ACE_0_CLK_SRC_MASK); else if (instance->ctr_idx == 1) clk_src &= ~(ME1400CD_8254_ACE_1_CLK_SRC_MASK); else clk_src &= ~(ME1400CD_8254_ACE_2_CLK_SRC_MASK); break; default: if (instance->ctr_idx == 0) clk_src &= ~(ME1400CD_8254_BD_0_CLK_SRC_MASK); else if (instance->ctr_idx == 1) clk_src &= ~(ME1400CD_8254_BD_1_CLK_SRC_MASK); else clk_src &= ~(ME1400CD_8254_BD_2_CLK_SRC_MASK); break; } break; case ME_REF_CTR_PREVIOUS: switch (instance->me8254_idx) { case 0: case 2: case 4: case 6: case 8: if (instance->ctr_idx == 0) { clk_src &= ~(ME1400CD_8254_ACE_0_CLK_SRC_MASK); clk_src |= (ME1400CD_8254_ACE_0_CLK_SRC_PREV); } else if (instance->ctr_idx == 1) { clk_src &= ~(ME1400CD_8254_ACE_1_CLK_SRC_MASK); clk_src |= (ME1400CD_8254_ACE_1_CLK_SRC_PREV); } else { clk_src &= ~(ME1400CD_8254_ACE_2_CLK_SRC_MASK); clk_src |= (ME1400CD_8254_ACE_2_CLK_SRC_PREV); } break; default: if (instance->ctr_idx == 0) { clk_src &= ~(ME1400CD_8254_BD_0_CLK_SRC_MASK); clk_src |= (ME1400CD_8254_BD_0_CLK_SRC_PREV); } else if (instance->ctr_idx == 1) { clk_src &= ~(ME1400CD_8254_BD_1_CLK_SRC_MASK); clk_src |= (ME1400CD_8254_BD_1_CLK_SRC_PREV); } else { clk_src &= ~(ME1400CD_8254_BD_2_CLK_SRC_MASK); clk_src |= (ME1400CD_8254_BD_2_CLK_SRC_PREV); } break; } break; case ME_REF_CTR_INTERNAL_1MHZ: switch (instance->me8254_idx) { case 0: case 2: case 4: case 6: case 8: if (instance->ctr_idx == 0) { clk_src &= ~(ME1400CD_8254_ACE_0_CLK_SRC_MASK); clk_src |= (ME1400CD_8254_ACE_0_CLK_SRC_1MHZ); } else { PERROR("Invalid reference.\n"); spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_REF; } break; default: if (instance->ctr_idx == 0) { clk_src &= ~(ME1400CD_8254_BD_0_CLK_SRC_MASK); clk_src |= (ME1400CD_8254_BD_0_CLK_SRC_1MHZ); } else { PERROR("Invalid reference.\n"); spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_REF; } break; } break; case ME_REF_CTR_INTERNAL_10MHZ: switch (instance->me8254_idx) { case 0: case 2: case 4: case 6: case 8: if (instance->ctr_idx == 0) { clk_src &= ~(ME1400CD_8254_ACE_0_CLK_SRC_MASK); clk_src |= (ME1400CD_8254_ACE_0_CLK_SRC_10MHZ); } else { PERROR("Invalid reference.\n"); spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_REF; } break; default: if (instance->ctr_idx == 0) { clk_src &= ~(ME1400CD_8254_BD_0_CLK_SRC_MASK); clk_src |= (ME1400CD_8254_BD_0_CLK_SRC_10MHZ); } else { PERROR("Invalid reference.\n"); spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_REF; } break; } break; default: PERROR("Invalid reference.\n"); spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_REF; } outb(clk_src, instance->clk_src_reg); spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_SUCCESS; } static int me4600_ref_config(me8254_subdevice_t * instance, int ref) { switch (ref) { case ME_REF_CTR_EXTERNAL: // Nothing to do break; default: PERROR("Invalid reference.\n"); // spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_REF; } return ME_ERRNO_SUCCESS; } static int me8100_ref_config(me8254_subdevice_t * instance, int ref) { switch (ref) { case ME_REF_CTR_EXTERNAL: // Nothing to do break; default: PERROR("Invalid reference.\n"); // spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_REF; } return ME_ERRNO_SUCCESS; } static int me8254_io_single_config(struct me_subdevice *subdevice, struct file *filep, int channel, int single_config, int ref, int trig_chan, int trig_type, int trig_edge, int flags) { me8254_subdevice_t *instance; int err; PDEBUG("executed.\n"); if (channel) { PERROR("Invalid channel.\n"); return ME_ERRNO_INVALID_CHANNEL; } instance = (me8254_subdevice_t *) subdevice; if (flags) { PERROR("Invalid flag specified.\n"); return ME_ERRNO_INVALID_FLAGS; } ME_SUBDEVICE_ENTER; spin_lock(&instance->subdevice_lock); // Configure the counter modes if (instance->ctr_idx == 0) { if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_0) { outb(ME8254_CTRL_SC0 | ME8254_CTRL_LM | ME8254_CTRL_M0 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_1) { outb(ME8254_CTRL_SC0 | ME8254_CTRL_LM | ME8254_CTRL_M1 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_2) { outb(ME8254_CTRL_SC0 | ME8254_CTRL_LM | ME8254_CTRL_M2 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_3) { outb(ME8254_CTRL_SC0 | ME8254_CTRL_LM | ME8254_CTRL_M3 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_4) { outb(ME8254_CTRL_SC0 | ME8254_CTRL_LM | ME8254_CTRL_M4 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_5) { outb(ME8254_CTRL_SC0 | ME8254_CTRL_LM | ME8254_CTRL_M5 | ME8254_CTRL_BIN, instance->ctrl_reg); } else { PERROR("Invalid single configuration.\n"); spin_unlock(&instance->subdevice_lock); return ME_ERRNO_INVALID_SINGLE_CONFIG; } } else if (instance->ctr_idx == 1) { if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_0) { outb(ME8254_CTRL_SC1 | ME8254_CTRL_LM | ME8254_CTRL_M0 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_1) { outb(ME8254_CTRL_SC1 | ME8254_CTRL_LM | ME8254_CTRL_M1 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_2) { outb(ME8254_CTRL_SC1 | ME8254_CTRL_LM | ME8254_CTRL_M2 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_3) { outb(ME8254_CTRL_SC1 | ME8254_CTRL_LM | ME8254_CTRL_M3 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_4) { outb(ME8254_CTRL_SC1 | ME8254_CTRL_LM | ME8254_CTRL_M4 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_5) { outb(ME8254_CTRL_SC1 | ME8254_CTRL_LM | ME8254_CTRL_M5 | ME8254_CTRL_BIN, instance->ctrl_reg); } else { PERROR("Invalid single configuration.\n"); spin_unlock(&instance->subdevice_lock); return ME_ERRNO_INVALID_SINGLE_CONFIG; } } else { if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_0) { outb(ME8254_CTRL_SC2 | ME8254_CTRL_LM | ME8254_CTRL_M0 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_1) { outb(ME8254_CTRL_SC2 | ME8254_CTRL_LM | ME8254_CTRL_M1 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_2) { outb(ME8254_CTRL_SC2 | ME8254_CTRL_LM | ME8254_CTRL_M2 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_3) { outb(ME8254_CTRL_SC2 | ME8254_CTRL_LM | ME8254_CTRL_M3 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_4) { outb(ME8254_CTRL_SC2 | ME8254_CTRL_LM | ME8254_CTRL_M4 | ME8254_CTRL_BIN, instance->ctrl_reg); } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_5) { outb(ME8254_CTRL_SC2 | ME8254_CTRL_LM | ME8254_CTRL_M5 | ME8254_CTRL_BIN, instance->ctrl_reg); } else { PERROR("Invalid single configuration.\n"); spin_unlock(&instance->subdevice_lock); return ME_ERRNO_INVALID_SINGLE_CONFIG; } } switch (instance->device_id) { case PCI_DEVICE_ID_MEILHAUS_ME1400: case PCI_DEVICE_ID_MEILHAUS_ME14E0: case PCI_DEVICE_ID_MEILHAUS_ME140A: case PCI_DEVICE_ID_MEILHAUS_ME14EA: case PCI_DEVICE_ID_MEILHAUS_ME140B: case PCI_DEVICE_ID_MEILHAUS_ME14EB: err = me1400_ab_ref_config(instance, ref); if (err) { spin_unlock(&instance->subdevice_lock); return err; } break; case PCI_DEVICE_ID_MEILHAUS_ME140C: case PCI_DEVICE_ID_MEILHAUS_ME140D: err = me1400_cd_ref_config(instance, ref); if (err) { spin_unlock(&instance->subdevice_lock); return err; } break; case PCI_DEVICE_ID_MEILHAUS_ME4610: case PCI_DEVICE_ID_MEILHAUS_ME4660: case PCI_DEVICE_ID_MEILHAUS_ME4660I: case PCI_DEVICE_ID_MEILHAUS_ME4660S: case PCI_DEVICE_ID_MEILHAUS_ME4660IS: case PCI_DEVICE_ID_MEILHAUS_ME4670: case PCI_DEVICE_ID_MEILHAUS_ME4670I: case PCI_DEVICE_ID_MEILHAUS_ME4670S: case PCI_DEVICE_ID_MEILHAUS_ME4670IS: case PCI_DEVICE_ID_MEILHAUS_ME4680: case PCI_DEVICE_ID_MEILHAUS_ME4680I: case PCI_DEVICE_ID_MEILHAUS_ME4680S: case PCI_DEVICE_ID_MEILHAUS_ME4680IS: err = me4600_ref_config(instance, ref); if (err) { spin_unlock(&instance->subdevice_lock); return err; } break; case PCI_DEVICE_ID_MEILHAUS_ME8100_A: case PCI_DEVICE_ID_MEILHAUS_ME8100_B: err = me8100_ref_config(instance, ref); if (err) { spin_unlock(&instance->subdevice_lock); return err; } break; default: PERROR("Invalid device type.\n"); spin_unlock(&instance->subdevice_lock); // spin_unlock(instance->clk_src_reg_lock); return ME_ERRNO_INVALID_SINGLE_CONFIG; } spin_unlock(&instance->subdevice_lock); ME_SUBDEVICE_EXIT; return ME_ERRNO_SUCCESS; } static int me8254_io_single_read(struct me_subdevice *subdevice, struct file *filep, int channel, int *value, int time_out, int flags) { me8254_subdevice_t *instance; uint16_t lo_byte; uint16_t hi_byte; PDEBUG("executed.\n"); if (channel) { PERROR("Invalid channel.\n"); return ME_ERRNO_INVALID_CHANNEL; } instance = (me8254_subdevice_t *) subdevice; if (flags) { PERROR("Invalid flag specified.\n"); return ME_ERRNO_INVALID_FLAGS; } ME_SUBDEVICE_ENTER; spin_lock(&instance->subdevice_lock); spin_lock(instance->ctrl_reg_lock); if (instance->ctr_idx == 0) outb(ME8254_CTRL_SC0 | ME8254_CTRL_TLO, instance->ctrl_reg); else if (instance->ctr_idx == 1) outb(ME8254_CTRL_SC1 | ME8254_CTRL_TLO, instance->ctrl_reg); else outb(ME8254_CTRL_SC2 | ME8254_CTRL_TLO, instance->ctrl_reg); lo_byte = inb(instance->val_reg); hi_byte = inb(instance->val_reg); spin_unlock(instance->ctrl_reg_lock); *value = lo_byte | (hi_byte << 8); spin_unlock(&instance->subdevice_lock); ME_SUBDEVICE_EXIT; return ME_ERRNO_SUCCESS; } static int me8254_io_single_write(struct me_subdevice *subdevice, struct file *filep, int channel, int value, int time_out, int flags) { me8254_subdevice_t *instance; PDEBUG("executed.\n"); if (channel) { PERROR("Invalid channel.\n"); return ME_ERRNO_INVALID_CHANNEL; } instance = (me8254_subdevice_t *) subdevice; if (flags) { PERROR("Invalid flag specified.\n"); return ME_ERRNO_INVALID_FLAGS; } ME_SUBDEVICE_ENTER; spin_lock(&instance->subdevice_lock); outb(value, instance->val_reg); outb((value >> 8), instance->val_reg); spin_unlock(&instance->subdevice_lock); ME_SUBDEVICE_EXIT; return ME_ERRNO_SUCCESS; } static int me8254_query_number_channels(struct me_subdevice *subdevice, int *number) { PDEBUG("executed.\n"); *number = ME8254_NUMBER_CHANNELS; return ME_ERRNO_SUCCESS; } static int me8254_query_subdevice_type(struct me_subdevice *subdevice, int *type, int *subtype) { PDEBUG("executed.\n"); *type = ME_TYPE_CTR; *subtype = ME_SUBTYPE_CTR_8254; return ME_ERRNO_SUCCESS; } static int me8254_query_subdevice_caps(struct me_subdevice *subdevice, int *caps) { me8254_subdevice_t *instance; PDEBUG("executed.\n"); instance = (me8254_subdevice_t *) subdevice; *caps = instance->caps; return ME_ERRNO_SUCCESS; } static int me8254_query_subdevice_caps_args(struct me_subdevice *subdevice, int cap, int *args, int count) { PDEBUG("executed.\n"); if (count != 1) { PERROR("Invalid capability argument count.\n"); return ME_ERRNO_INVALID_CAP_ARG_COUNT; } if (cap == ME_CAP_CTR_WIDTH) { args[0] = ME8254_CTR_WIDTH; } else { PERROR("Invalid capability.\n"); return ME_ERRNO_INVALID_CAP; } return ME_ERRNO_SUCCESS; } static uint32_t me1400AB_get_val_reg(uint32_t reg_base, unsigned int me8254_idx, unsigned int ctr_idx) { switch (me8254_idx) { case 0: return (reg_base + ME1400AB_8254_A_0_VAL_REG + ctr_idx); default: return (reg_base + ME1400AB_8254_B_0_VAL_REG + ctr_idx); } return 0; } static uint32_t me1400AB_get_ctrl_reg(uint32_t reg_base, unsigned int me8254_idx, unsigned int ctr_idx) { switch (me8254_idx) { case 0: return (reg_base + ME1400AB_8254_A_CTRL_REG); default: return (reg_base + ME1400AB_8254_B_CTRL_REG); } return 0; } static uint32_t me1400AB_get_clk_src_reg(uint32_t reg_base, unsigned int me8254_idx, unsigned int ctr_idx) { switch (me8254_idx) { case 0: return (reg_base + ME1400AB_CLK_SRC_REG); default: return (reg_base + ME1400AB_CLK_SRC_REG); } return 0; } static uint32_t me1400CD_get_val_reg(uint32_t reg_base, unsigned int me8254_idx, unsigned int ctr_idx) { switch (me8254_idx) { case 0: return (reg_base + ME1400C_8254_A_0_VAL_REG + ctr_idx); case 1: return (reg_base + ME1400C_8254_B_0_VAL_REG + ctr_idx); case 2: return (reg_base + ME1400C_8254_C_0_VAL_REG + ctr_idx); case 3: return (reg_base + ME1400C_8254_D_0_VAL_REG + ctr_idx); case 4: return (reg_base + ME1400C_8254_E_0_VAL_REG + ctr_idx); case 5: return (reg_base + ME1400D_8254_A_0_VAL_REG + ctr_idx); case 6: return (reg_base + ME1400D_8254_B_0_VAL_REG + ctr_idx); case 7: return (reg_base + ME1400D_8254_C_0_VAL_REG + ctr_idx); case 8: return (reg_base + ME1400D_8254_D_0_VAL_REG + ctr_idx); default: return (reg_base + ME1400D_8254_E_0_VAL_REG + ctr_idx); } return 0; } static uint32_t me1400CD_get_ctrl_reg(uint32_t reg_base, unsigned int me8254_idx, unsigned int ctr_idx) { switch (me8254_idx) { case 0: return (reg_base + ME1400C_8254_A_CTRL_REG); case 1: return (reg_base + ME1400C_8254_B_CTRL_REG); case 2: return (reg_base + ME1400C_8254_C_CTRL_REG); case 3: return (reg_base + ME1400C_8254_D_CTRL_REG); case 4: return (reg_base + ME1400C_8254_E_CTRL_REG); case 5: return (reg_base + ME1400D_8254_A_CTRL_REG); case 6: return (reg_base + ME1400D_8254_B_CTRL_REG); case 7: return (reg_base + ME1400D_8254_C_CTRL_REG); case 8: return (reg_base + ME1400D_8254_D_CTRL_REG); default: return (reg_base + ME1400D_8254_E_CTRL_REG); } return 0; } static uint32_t me1400CD_get_clk_src_reg(uint32_t reg_base, unsigned int me8254_idx, unsigned int ctr_idx) { switch (me8254_idx) { case 0: return (reg_base + ME1400C_CLK_SRC_0_REG); case 1: return (reg_base + ME1400C_CLK_SRC_0_REG); case 2: return (reg_base + ME1400C_CLK_SRC_1_REG); case 3: return (reg_base + ME1400C_CLK_SRC_1_REG); case 4: return (reg_base + ME1400C_CLK_SRC_2_REG); case 5: return (reg_base + ME1400D_CLK_SRC_0_REG); case 6: return (reg_base + ME1400D_CLK_SRC_0_REG); case 7: return (reg_base + ME1400D_CLK_SRC_1_REG); case 8: return (reg_base + ME1400D_CLK_SRC_1_REG); default: return (reg_base + ME1400D_CLK_SRC_2_REG); } return 0; } static uint32_t me4600_get_val_reg(uint32_t reg_base, unsigned int me8254_idx, unsigned int ctr_idx) { return (reg_base + ME4600_8254_0_VAL_REG + ctr_idx); } static uint32_t me4600_get_ctrl_reg(uint32_t reg_base, unsigned int me8254_idx, unsigned int ctr_idx) { return (reg_base + ME4600_8254_CTRL_REG); } static uint32_t me8100_get_val_reg(uint32_t reg_base, unsigned int me8254_idx, unsigned int ctr_idx) { return (reg_base + ME8100_COUNTER_REG_0 + ctr_idx * 2); } static uint32_t me8100_get_ctrl_reg(uint32_t reg_base, unsigned int me8254_idx, unsigned int ctr_idx) { return (reg_base + ME8100_COUNTER_CTRL_REG); } me8254_subdevice_t *me8254_constructor(uint32_t device_id, uint32_t reg_base, unsigned int me8254_idx, unsigned int ctr_idx, spinlock_t * ctrl_reg_lock, spinlock_t * clk_src_reg_lock) { me8254_subdevice_t *subdevice; int err; PDEBUG("executed.\n"); // Allocate memory for subdevice instance subdevice = kmalloc(sizeof(me8254_subdevice_t), GFP_KERNEL); if (!subdevice) { PERROR("Cannot get memory for 8254 instance.\n"); return NULL; } memset(subdevice, 0, sizeof(me8254_subdevice_t)); // Check if counter index is out of range if (ctr_idx > 2) { PERROR("Counter index is out of range.\n"); kfree(subdevice); return NULL; } // Initialize subdevice base class err = me_subdevice_init(&subdevice->base); if (err) { PERROR("Cannot initialize subdevice base class instance.\n"); kfree(subdevice); return NULL; } // Initialize spin locks. spin_lock_init(&subdevice->subdevice_lock); subdevice->ctrl_reg_lock = ctrl_reg_lock; subdevice->clk_src_reg_lock = clk_src_reg_lock; // Save type of Meilhaus device subdevice->device_id = device_id; // Save the indices subdevice->me8254_idx = me8254_idx; subdevice->ctr_idx = ctr_idx; // Do device specific initialization switch (device_id) { case PCI_DEVICE_ID_MEILHAUS_ME140A: case PCI_DEVICE_ID_MEILHAUS_ME14EA: // Check if 8254 index is out of range if (me8254_idx > 0) { PERROR("8254 index is out of range.\n"); me_subdevice_deinit(&subdevice->base); kfree(subdevice); return NULL; } case PCI_DEVICE_ID_MEILHAUS_ME140B: // Fall through case PCI_DEVICE_ID_MEILHAUS_ME14EB: // Check if 8254 index is out of range if (me8254_idx > 1) { PERROR("8254 index is out of range.\n"); me_subdevice_deinit(&subdevice->base); kfree(subdevice); return NULL; } // Initialize the counters capabilities if (ctr_idx == 0) subdevice->caps = ME_CAPS_CTR_CLK_INTERNAL_1MHZ | ME_CAPS_CTR_CLK_INTERNAL_10MHZ | ME_CAPS_CTR_CLK_EXTERNAL; else subdevice->caps = ME_CAPS_CTR_CLK_PREVIOUS | ME_CAPS_CTR_CLK_EXTERNAL; // Get the counters registers subdevice->val_reg = me1400AB_get_val_reg(reg_base, me8254_idx, ctr_idx); subdevice->ctrl_reg = me1400AB_get_ctrl_reg(reg_base, me8254_idx, ctr_idx); subdevice->clk_src_reg = me1400AB_get_clk_src_reg(reg_base, me8254_idx, ctr_idx); break; case PCI_DEVICE_ID_MEILHAUS_ME140C: // Check if 8254 index is out of range if (me8254_idx > 4) { PERROR("8254 index is out of range.\n"); me_subdevice_deinit(&subdevice->base); kfree(subdevice); return NULL; } case PCI_DEVICE_ID_MEILHAUS_ME140D: // Check if 8254 index is out of range if (me8254_idx > 9) { PERROR("8254 index is out of range.\n"); me_subdevice_deinit(&subdevice->base); kfree(subdevice); return NULL; } // Initialize the counters capabilities if (ctr_idx == 0) { if (me8254_idx == 0) subdevice->caps = ME_CAPS_CTR_CLK_PREVIOUS | ME_CAPS_CTR_CLK_INTERNAL_1MHZ | ME_CAPS_CTR_CLK_INTERNAL_10MHZ | ME_CAPS_CTR_CLK_EXTERNAL; else subdevice->caps = ME_CAPS_CTR_CLK_INTERNAL_1MHZ | ME_CAPS_CTR_CLK_INTERNAL_10MHZ | ME_CAPS_CTR_CLK_EXTERNAL; } else subdevice->caps = ME_CAPS_CTR_CLK_PREVIOUS | ME_CAPS_CTR_CLK_EXTERNAL; // Get the counters registers subdevice->val_reg = me1400CD_get_val_reg(reg_base, me8254_idx, ctr_idx); subdevice->ctrl_reg = me1400CD_get_ctrl_reg(reg_base, me8254_idx, ctr_idx); subdevice->clk_src_reg = me1400CD_get_clk_src_reg(reg_base, me8254_idx, ctr_idx); break; case PCI_DEVICE_ID_MEILHAUS_ME4610: case PCI_DEVICE_ID_MEILHAUS_ME4660: case PCI_DEVICE_ID_MEILHAUS_ME4660I: case PCI_DEVICE_ID_MEILHAUS_ME4660S: case PCI_DEVICE_ID_MEILHAUS_ME4660IS: case PCI_DEVICE_ID_MEILHAUS_ME4670: case PCI_DEVICE_ID_MEILHAUS_ME4670I: case PCI_DEVICE_ID_MEILHAUS_ME4670S: case PCI_DEVICE_ID_MEILHAUS_ME4670IS: case PCI_DEVICE_ID_MEILHAUS_ME4680: case PCI_DEVICE_ID_MEILHAUS_ME4680I: case PCI_DEVICE_ID_MEILHAUS_ME4680S: case PCI_DEVICE_ID_MEILHAUS_ME4680IS: // Check if 8254 index is out of range if (me8254_idx > 0) { PERROR("8254 index is out of range.\n"); me_subdevice_deinit(&subdevice->base); kfree(subdevice); return NULL; } // Initialize the counters capabilities subdevice->caps = ME_CAPS_CTR_CLK_EXTERNAL; // Get the counters registers subdevice->val_reg = me4600_get_val_reg(reg_base, me8254_idx, ctr_idx); subdevice->ctrl_reg = me4600_get_ctrl_reg(reg_base, me8254_idx, ctr_idx); subdevice->clk_src_reg = 0; // Not used break; case PCI_DEVICE_ID_MEILHAUS_ME8100_A: case PCI_DEVICE_ID_MEILHAUS_ME8100_B: // Check if 8254 index is out of range if (me8254_idx > 0) { PERROR("8254 index is out of range.\n"); me_subdevice_deinit(&subdevice->base); kfree(subdevice); return NULL; } // Initialize the counters capabilities subdevice->caps = ME_CAPS_CTR_CLK_EXTERNAL; // Get the counters registers subdevice->val_reg = me8100_get_val_reg(reg_base, me8254_idx, ctr_idx); subdevice->ctrl_reg = me8100_get_ctrl_reg(reg_base, me8254_idx, ctr_idx); subdevice->clk_src_reg = 0; // Not used break; case PCI_DEVICE_ID_MEILHAUS_ME4650: case PCI_DEVICE_ID_MEILHAUS_ME1400: case PCI_DEVICE_ID_MEILHAUS_ME14E0: PERROR("No 8254 subdevices available for subdevice device.\n"); me_subdevice_deinit(&subdevice->base); kfree(subdevice); return NULL; default: PERROR("Unknown device type.\n"); me_subdevice_deinit(&subdevice->base); kfree(subdevice); return NULL; } // Overload subdevice base class methods. subdevice->base.me_subdevice_io_reset_subdevice = me8254_io_reset_subdevice; subdevice->base.me_subdevice_io_single_config = me8254_io_single_config; subdevice->base.me_subdevice_io_single_read = me8254_io_single_read; subdevice->base.me_subdevice_io_single_write = me8254_io_single_write; subdevice->base.me_subdevice_query_number_channels = me8254_query_number_channels; subdevice->base.me_subdevice_query_subdevice_type = me8254_query_subdevice_type; subdevice->base.me_subdevice_query_subdevice_caps = me8254_query_subdevice_caps; subdevice->base.me_subdevice_query_subdevice_caps_args = me8254_query_subdevice_caps_args; return subdevice; }