component mesa_7i65 "Support for the Mesa 7i65 Octuple Servo Card";

description """The component takes parameters in the form of a comma-separated
list of bspi (buffered SPI) instance names, for example:

\\fB loadrt mesa_7i65 bspi_chans=hm2_5i23.0.bspi.0, hm2_5i23.0.bspi.1\\fR

The BSPI instances are printed to the dmesg buffer during the Hostmot2 setup
sequence, one for each bspi instance included in the bitfile loaded to each
installed card during the Hostmot2 setup sequence. Type "dmesg" at the terminal
prompt to view the output.""";

pin in float analogue.#.out [8] """Analogue output values. The value will be
limited to a  -1.0 to +1.0 range""";
pin out float analogue.#.in [8] "Analogue outputs read by the 7i65 (in Volts)";
pin out bit digital.#.in [4] "Miscellaneous Digital Inputs";
pin in bit enable.#.out [8] "Amplifier-enable control pins";
pin out bit watchdog.has-bit """Indicates the status of the 7i65 Watchdog (which
is separate from the FPGA card watchdog""";
param rw float scale-# [8] = 10 """Analogue output scale factor. For example if
the scale is 7 then an input of 1.0 will give 7V on the output terminals""";
param rw bit is-bipolar-# [8] = 1 """Set this value to TRUE for a plus/minus
"scale" output. Set to 0 for a 0-"scale" output""";

option extra_setup yes;
option count_function yes;

variable u32 firstrun;

variable u32 *AD5754_1A;
variable u32 *AD5754_1B;
variable u32 *AD5754_1C;
variable u32 *AD5754_1D;
variable u32 *AD5754_2A;
variable u32 *AD5754_2B;
variable u32 *AD5754_2C;
variable u32 *AD5754_2D;
variable u32 *CPLD_write;
variable u32 *CPLD_read;
variable u32 *AD7329_write[8];
variable u32 *AD7329_read[8];

license "GPL";
author "Andy Pugh / Cliff Blackburn";

include "../../../hal/drivers/mesa-hostmot2/hostmot2.h";
;;

// to parse the modparam
char *bspi_chans[16] = {0,};
RTAPI_MP_ARRAY_STRING(bspi_chans, 16, "BSPI Channel names");

static int read(struct __comp_state *__comp_inst){
    int i;
    double aout[8];

    // Write Misc IO & Reset Watchdog
    *CPLD_write = 0xA00 // "write" command
               | (enable_out(7) != 0) << 7 | (enable_out(6) != 0) << 6
               | (enable_out(5) != 0) << 5 | (enable_out(4) != 0) << 4
               | (enable_out(3) != 0) << 3 | (enable_out(2) != 0) << 2
               | (enable_out(1) != 0) << 1 | (enable_out(0) != 0);

    // Read Analog to Digital
    *AD7329_write[0] = 0x00000000;
    *AD7329_write[1] = 0x00000000;
    *AD7329_write[2] = 0x00000000;
    *AD7329_write[3] = 0x00000000;
    *AD7329_write[4] = 0x00000000;
    *AD7329_write[5] = 0x00000000;
    *AD7329_write[6] = 0x00000000;
    *AD7329_write[7] = 0x00009C18; // Reset Sequencer to 0.. jic

    // Limit DAC Outputs
    for (i = 0 ; i < 8 ; i++) {
        if (scale(i) > 10)
            scale(i) = 10;
        if (scale(i) < -10)
            scale(i) = -10;

        aout[i] = analogue_out(i);
        if (aout[i] > 1)
            aout[i] = 1;
        if (is_bipolar(i)) {
            if (aout[i] < -1)
                aout[i] = -1;
        }
        else {
            if (aout[i] < 0)
                aout[i] = 0;
        }
    }

    *AD5754_1A = 0x00000000 |  // address
                     (0x0000FFFF &  // mask
                     (u32)((s16)(aout[0] * scale(0) * 3276.799)));

    *AD5754_1B = 0x00010000 |  // address
                     (0x0000FFFF &  // mask
                     (u32)((s16)(aout[1] * scale(1) * 3276.799)));

    *AD5754_1C = 0x00020000 |  // address
                     (0x0000FFFF &  // mask
                     (u32)((s16)(aout[2] * scale(2) * 3276.799)));

    *AD5754_1D = 0x00030000 |  // address
                     (0x0000FFFF &  // mask
                     (u32)((s16)(aout[3] * scale(3) * 3276.799)));

    *AD5754_2A = 0x00000000 |  // address
                     (0x0000FFFF &  // mask
                     (u32)((s16)(aout[4] * scale(4) * 3276.799)));

    *AD5754_2B = 0x00010000 |  // address
                     (0x0000FFFF &  // mask
                     (u32)((s16)(aout[5] * scale(5) * 3276.799)));

    *AD5754_2C = 0x00020000 |  // address
                     (0x0000FFFF &  // mask
                     (u32)((s16)(aout[6] * scale(6) * 3276.799)));

    *AD5754_2D = 0x00030000 |  // address
                     (0x0000FFFF &  // mask
                     (u32)((s16)(aout[7] * scale(7) * 3276.799)));

    // Setup DAC, watchdog will have reset it if done in setup
    if(firstrun) {
       // Setup DAC
            // DAC: write - range select - all chans - +/-10V
        *AD5754_1A = 0x000C0004;
        *AD5754_2A = 0x000C0004;
            // DAC: power up all channels
        *AD5754_1B = 0x0010000F;
        *AD5754_2B = 0x0010000F;

        // NOP, Reads
        *AD5754_1C = 0x00800000;
        *AD5754_2C = 0x00800000;
        *AD5754_1D = 0x00800000;
        *AD5754_2D = 0x00800000;

        firstrun = false;
    }

    // Read Misc IO and Watchdog from CPLD
    digital_in(0) = (*CPLD_read & 0x1) ? 1:0;
    digital_in(1) = (*CPLD_read & 0x2) ? 1:0;
    digital_in(2) = (*CPLD_read & 0x4) ? 1:0;
    digital_in(3) = (*CPLD_read & 0x8) ? 1:0;
    watchdog_has_bit = (*CPLD_read & 0x100) ? 1:0;

    // Read ADC's into Pins
    for(i=0; i < 8; i++) {
        analogue_in(i) = (double)((s16)((*AD7329_read[i] & 0x1FFF) << 3)) / 3276.8;
    }

    return 0;
}

EXTRA_SETUP(){
    int i, r;
    char *name = bspi_chans[extra_arg]; // This is the string which identifies board and instance

    firstrun = true;
    // Set up channel descriptors
    //hm2_bspi_setup_chan(name, chan, cs, bits, mhz, delay(ns), cpol, cpha, noclear, noecho, samplelate)
    // CS0 loopback Echo, CS0, ~ 4 MHz, CPOL, 32 bits
    r = hm2_bspi_setup_chan(name, 0, 0, 32, 4, 0, 1, 0, 0, 0, 0);
    // CS1 AD5754 No echo, CS1, 25 MHz, CPOL, 24 bits
    r += hm2_bspi_setup_chan(name, 1, 1, 24, 25, 0, 1, 0, 0, 1, 0);
    // CS2 AD5754 No echo, CS2, 25 MHz, CPOL, 24 bits
    r += hm2_bspi_setup_chan(name, 2, 2, 24, 25, 0, 1, 0, 0, 1, 0);
    // CS3 AD7329 Echo, CS3, ~6 MHz, CPOL, 16 bits
    r += hm2_bspi_setup_chan(name, 3, 3, 16, 6, 0, 1, 0, 0, 0, 0);
    // CS4 CPLD Echo, CS4, ~6 MHz, CPOL, 12 bits
    r += hm2_bspi_setup_chan(name, 4, 4, 12, 6, 0, 1, 0, 0, 0, 0);
    // CS5 Not Used No echo, CS5, 25 MHz, 2 bits
    r += hm2_bspi_setup_chan(name, 5, 5, 2, 25, 0, 1, 0, 0, 1, 0);
    // CS6 Not Used No echo, CS5, 25 MHz, 2 bits
    r += hm2_bspi_setup_chan(name, 6, 6, 2, 25, 0, 1, 0, 0, 1, 0);
    // CS7 EEPROM Echo, CS7, ~4 MHz, CPOL,CPHA, 8 bits
    r += hm2_bspi_setup_chan(name, 7, 7, 8, 4, 0, 1, 1, 0, 0, 0);
    // CS7 EEPROM No Echo, CS7, ~4 MHz, CPOL,CPHA, 8 bits
    r += hm2_bspi_setup_chan(name, 8, 7, 8, 4, 0, 1, 1, 0, 1, 0);
    // CS7 EEPROM No Echo, DontClearFrame, CS7, ~4 MHz,CPOL,CPHA, 8 bits
    r += hm2_bspi_setup_chan(name, 9, 7, 8, 4, 0, 1, 1, 1, 1, 0);

    if (r < 0) {
        HM2_ERR_NO_LL("There have been %i errors during channel setup, "
                      "quitting\n", -r);
        return -EINVAL;
    }

    // Setup ADC
        // ADC, chans 0-3 +/-10V
    r += hm2_bspi_write_chan(name, 3, 0x0000A000);
        // ADC, chans 4-7 +/-10V
    r += hm2_bspi_write_chan(name, 3, 0x0000C000);
        // ADC, Sequencer mode 2, Internal Ref, 8x Single Ended
    r += hm2_bspi_write_chan(name, 3, 0x00009C18);

    if (r < 0) {
        HM2_ERR_NO_LL("There have been %i errors during ADC setup, "
                      "quitting\n", -r);
        return -EINVAL;
    }
    // Add BSPI Frames
    r += hm2_tram_add_bspi_frame(name, 4, &CPLD_write,
                            &CPLD_read);

    for(i = 0; i < 8; i++) {
        r += hm2_tram_add_bspi_frame(name, 3, &AD7329_write[i],
                                &AD7329_read[i]);
    }

    r += hm2_tram_add_bspi_frame(name, 1, &AD5754_1A,0);
    r += hm2_tram_add_bspi_frame(name, 1, &AD5754_1B,0);
    r += hm2_tram_add_bspi_frame(name, 1, &AD5754_1C,0);
    r += hm2_tram_add_bspi_frame(name, 1, &AD5754_1D,0);
    r += hm2_tram_add_bspi_frame(name, 2, &AD5754_2A,0);
    r += hm2_tram_add_bspi_frame(name, 2, &AD5754_2B,0);
    r += hm2_tram_add_bspi_frame(name, 2, &AD5754_2C,0);
    r += hm2_tram_add_bspi_frame(name, 2, &AD5754_2D,0);

    // This is required, or nothing happens.
    r += hm2_allocate_bspi_tram(name);

    // Tell the bspi driver which function to call
    r += hm2_bspi_set_read_function(name, (int (*)(void *))&read, __comp_inst);

    // no separate write function in this example, but it would be:
    // r += hm2_bspi_set_write_function(name, &write, __comp_inst);

    if (r < 0) {
        HM2_ERR_NO_LL("There have been %i errors during TRAM allocation setup, "
                      "quitting\n", -r);
        return -EINVAL;
    }
    return 0;
}

int get_count(void){
    int i;
    for (i= 0; bspi_chans[i] != NULL && i < 16 ; i++){}
    return i;
}
