diff options
author | Sebastian Kuzminsky <seb@highlab.com> | 2008-08-17 07:36:10 +0000 |
---|---|---|
committer | Sebastian Kuzminsky <seb@highlab.com> | 2008-08-17 07:36:10 +0000 |
commit | 75d4948653c7249960a272684b6f6484a3d0e7c3 (patch) | |
tree | ef10a2f610b15ec6eecf815a53cb6b2862e6dbd9 | |
parent | 3bae57ee4fbfe8d16c7b92260e7c152e0481ae51 (diff) | |
download | linuxcnc-75d4948653c7249960a272684b6f6484a3d0e7c3.tar.gz linuxcnc-75d4948653c7249960a272684b6f6484a3d0e7c3.zip |
Give HAL interfaces to all IO pins, not just the full GPIOs. IO pins
used by active module instances have restricted interfaces.
Improved IO Port handling. Support open-drain pins.
Updated README to reflect reality.
Report firmware timestamp when loading.
Improved config string parsing.
-rw-r--r-- | docs/man/man9/hostmot2.9 | 69 | ||||
-rw-r--r-- | src/hal/drivers/mesa-hostmot2/ChangeLog | 15 | ||||
-rw-r--r-- | src/hal/drivers/mesa-hostmot2/README | 171 | ||||
-rw-r--r-- | src/hal/drivers/mesa-hostmot2/TODO | 30 | ||||
-rw-r--r-- | src/hal/drivers/mesa-hostmot2/bitfile.c | 4 | ||||
-rw-r--r-- | src/hal/drivers/mesa-hostmot2/hostmot2.c | 9 | ||||
-rw-r--r-- | src/hal/drivers/mesa-hostmot2/hostmot2.h | 19 | ||||
-rw-r--r-- | src/hal/drivers/mesa-hostmot2/ioport.c | 341 | ||||
-rw-r--r-- | src/hal/drivers/mesa-hostmot2/pins.c | 20 |
9 files changed, 434 insertions, 244 deletions
diff --git a/docs/man/man9/hostmot2.9 b/docs/man/man9/hostmot2.9 index fd2eacae4..7f8c42802 100644 --- a/docs/man/man9/hostmot2.9 +++ b/docs/man/man9/hostmot2.9 @@ -47,7 +47,7 @@ to provide zero or more instances of each of these four Modules. The HostMot2 firmware runs on an FPGA board. The board interfaces with the computer via PCI or EPP, and interfaces with motion control hardware -such as servos and stepper motors via I/O pins on the boards. +such as servos and stepper motors via I/O pins on the board. Each I/O pin can be configured, at board-driver load time, to serve one of two purposes: either as a particular I/O pin of a particular @@ -58,6 +58,14 @@ the board's pins are used by the Module instances. The user can disable Module instances at board-driver load time, by specifying a hostmot2 config string modparam. Any pins which belong to Module instances that have been disabled automatically become GPIOs. + +All IO pins have some HAL presence, whether they belong to an active +module instance or are full GPIOs. GPIOs can be changed (at run-time) +between inputs, normal outputs, and open drains, and have a flexible +HAL interface. IO pins that belongs to active module instances are +constrained by the requirements of the owning module, and have a more +limited interface in the HAL . This is described in the General Purpose +I/O section below. .SH config modparam The board-driver modules accept a config string modparam at load time. @@ -214,30 +222,57 @@ a step begins, in seconds. a step ends, in seconds. .SH General Purpose I/O -I/O Pins on the board which are not used by one of the Module instances -above are exported to HAL as GPIO pins. GPIO pins have names like +I/O pins on the board which are not used by a module instance are exported +to HAL as full GPIO pins. Full GPIO pins can be configured at run-time +to be inputs, outputs, or open drains, and have a HAL interface that +exposes this flexibility. IO pins that are owned by an active module +instance are constrained by the requirements of the owning module, +and have a restricted HAL interface. + +GPIOs have names like "hm2_<BoardType>.<BoardNum>.gpio.<PortName>.<PinNum>". PinNum is a three-digit number. PortName and PinNum correspond to the I/O Pin info as given in Mesa Electronics' manual for the board. -Each GPIO has the following HAL Pins: +Each GPIO can have the following HAL Pins: (bit out) in & in_not: State (normal and inverted) of the hardware -input pin. (Like CDI for Digital Input). +input pin. This follows the Canonical Device Interface for Digital Input. +Only full GPIO pins and IO pins used as inputs by active module instances +have these pins. (bit in) out: Value to be written (possibly inverted) to the hardware -output pin. (Like the CDI for Digital Output.) - -Each GPIO has the following Parameters: - -(bin r/w) is_output: If set to 1, the GPIO is an output, and the values -of the "in" and "in_not" HAL pins are undefined. If set to 0, the GPIO -is an input, and writes to the "out" HAL pin have no effect. - -(bin r/w) invert_output: If set to 1, the value that will appear on -the board's I/O pin will be the inverse of the value written to HAL's -"out" pin. (This corresponds to the 'invert' parameter in the CDI for -Digital Output.) +output pin. This follows the Canonical Device Interface for Digital +Output. Only full GPIO pins have this pin. + +Each GPIO can have the following Parameters: + +(bit r/w) is_output: If set to 0, the GPIO is an input. The IO pin is +put in a high-impedance state (pulled up to 5V), to be driven by other +devices. The logic value on the IO pin is available in the "in" and +"in_not" HAL pins. Writes to the "out" HAL pin have no effect. If this +parameter is set to 1, the GPIO is an output; its behavior then depends +on the "is_opendrain" parameter. Only full GPIO pins have this parameter. + +(bit r/w) is_opendrain: This parameter only has an effect if the +"is_output" parameter is true. If this parameter is false, the GPIO +behaves as a normal output pin: the IO pin on the connector is driven +to the value specified by the "out" HAL pin (possibly inverted), and the +value of the "in" and "in_not" HAL pins is undefined. If this parameter +is true, the GPIO behaves as an open-drain pin. Writing 0 to the "out" +HAL pin drives the IO pin low, writing 1 to the "out" HAL pin puts the +IO pin in a high-impedance state. In this high-impedance state the IO +pin floats (pulled up to 5V), and other devices can drive the value; the +resulting value on the IO pin is available on the "in" and "in_not" pins. +Only full GPIO pins and IO pins used as outputs by active module instances +have this parameter. + +(bit r/w) invert_output: This parameter only has an effect if the +"is_output" parameter is true. If this parameter is true, the output +value of the GPIO will be the inverse of the value on the "out" HAL pin. +This corresponds to the 'invert' parameter in the Canonical Device +Interface for Digital Output. Only full GPIO pins and IO pins used as +outputs by active module instances have this parameter. .SH Watchdog The HostMot2 firmware may include a watchdog Module; if it does, diff --git a/src/hal/drivers/mesa-hostmot2/ChangeLog b/src/hal/drivers/mesa-hostmot2/ChangeLog index c1d268216..ea0728b4e 100644 --- a/src/hal/drivers/mesa-hostmot2/ChangeLog +++ b/src/hal/drivers/mesa-hostmot2/ChangeLog @@ -1,4 +1,19 @@ +released 2008-08-17 + hostmot2 0.7 + + Give HAL interfaces to all IO pins, not just the full GPIOs. IO pins + used by active module instances have restricted interfaces. + + Improved IO Port handling. Support open-drain pins. + + Updated README to reflect reality. + + Report firmware timestamp when loading. + + Improved config string parsing. + + released 2008-08-15 hostmot2 0.6 diff --git a/src/hal/drivers/mesa-hostmot2/README b/src/hal/drivers/mesa-hostmot2/README index 1c5072569..807690ed0 100644 --- a/src/hal/drivers/mesa-hostmot2/README +++ b/src/hal/drivers/mesa-hostmot2/README @@ -30,10 +30,36 @@ Watchdog: -Architecture: +Pins, IOPorts, and GPIOs: + + Pins are the little pieces of metal at the connectors that carry + signals. Pins are described by the Pin Descriptor array in the + firmware. Each pin has a Pin Descriptor (PD). Each pin is connected + to one or two module instances: an IOPort instance and (optionally) + some instance of some other module. The IOPort is considered the + "primary" user of the pin, and the other module instance (if any) + is considered the secondary user. The PD specifies which module + (if any) is the secondary user of this pin, which instance of that + module, which of the module instance's pins it is, and whether the + module instance uses the pin as an input or an output. (See the + hostmot2 register map for the specifics.) The IOPort instance that + is the primary user has a register that selects whether the pin is + driven by the primary or the secondary user. This setting is chosen + by the user at module load time, by specifying the config string. + + IOPorts are instances of the IOPort Module. Each IOPort instance + governs the pins on one I/O connector (24 pins per connector). + + GPIOs are the HAL representations of pins. Not all pins will have a + corresponding GPIO, and not all GPIOs look the same. For example, + pins that are governed by a module instance may be configured (by + that module instance) as either input or output, and that setting + is not changeable by the user and not exported to HAL. - program boards with desired firmware before loading the driver, - using the "bfload" userspace utility + + + +Architecture: load the hostmot2 module, it does nothing but export a couple of kernel @@ -41,7 +67,7 @@ Architecture: load the low-level driver(s) for the board-type(s) you want to use - (hm2_7i43 and/or hm2_5i20) + (hm2_7i43 and/or hm2_5i20), giving each a config string modparam the board-driver setup does this: @@ -58,122 +84,81 @@ Architecture: allocate a hostmot2_t struct to hold this running instance of the hostmot2 firmare, add the hm2_lowlevel_io_t to it - call llio to read the idrom header into the hm2_t - - parse & validate the idrom header - - call llio to read the MDs into a buffer in the hm2_t - - call an hm2 function to parse the MDs - - validate each MD, skipping unknown ones & failing - the load if any invalid MDs are found - - FIXME: ok down to here - - exports the HAL pins, params, and functs: - hm2.<BoardType>.<BoardNum>.<FeatureType>.<FeatureNum>.<PinOrParamName> - - calls llio write to configure the functions - found + parse the config string - call llio to read the PDs into a buffer in the hm2_t + fetch the specified firmware, call the llio to program the fpga - parse the PDs + call llio to read the IDROM header, Pin Descriptors, + and Module Descriptors into the hm2_t - call llio write to configure the io pins - - register this hm2 instance - - - the per-instance HAL-exported functions are generic hm2 functions that - use board-specific i/o functions (hm2_lowlevel_io_t). Each generic - hm2 function is exported to HAL once for each hm2 instance that can - use it. Each HAL function is the same hm2 function with different - hostmot2_t's for arguments. - - - At runtime, it will eventually be desirable to use the TranslationRAM - to map all the on-board registers to be consecutive, and do a single - bulk transfer to read all the stuff into the driver. Not sure if TRAM - is right for the writing. Also, some funcs are currently read/write, - that may have to change: pet_watchdog and stepgen_update - - m7i43-hm2.pet-watchdog (R/W) - m7i43-hm2.gpio-read (R) - m7i43-hm2.encoder-update-counters (R) - - m7i43-hm2.pwmgen-update (W) - m7i43-hm2.stepgen-update (R/W) - m7i43-hm2.gpio-write (W) - - Read: - - encoder_update_counters - - stepgen_update (R/W) - - gpio_read + parse & validate the idrom header - pet_watchdog (R/W) + parse the MDs: walk the list of MDs read, calling + the module drivers to validate each one (skip unknown + MDs, and fail the load if any invalid MDs are found). + Each module driver does this: - Write: + verify MD is consistent (ie, all MD fields match + expected values) - pwmgen_update + verify this is the first MD of this GTAG (type) + we've seen (ie, there can be only one encoder MD, + one pwmgen MD, etc) - stepgen_update (R/W) + Do any module-specific sanity checks - gpio_update_ddr + Initialize the module structure in the hm2 - gpio_write + register tram read/write regions - pet_watchdog (R/W) + allocate an initialize all non-tram register buffers + and any extra memory the module driver needs + export HAL objects, initialize pin & param values + (FIXME: ioport can't do it here) - Efficient reading means reading registers that are consecutive all - together, directly into their destinations in a struct. It's one - thing to do this on a per-register basis, and much harder to do in - a global way using the TRAM. + return number of module instances actually used - The win of doing it this way is that you save yourself some addressing - cycles on EPP, and you save some function calls at runtime. + Allocate tram buffers (based on module-drivers' + registrations from the previous step) - Currently hm2 does this: + Configure the pins owned by the active module instances (source & direction) - read MDs + export & initialize gpio HAL objects (hm2->pin) - parse MDs + set up the raw interface, if enabled (export & initialize Raw HAL objects) - for each MD + hm2_force_write (tell all module-drivers to write + their internal representation out to the hardware; this + completely configures the non-tram parts of the hardware) - allocate an array of instances (each entry in the array - describes one instance, each instance has a "hw" part - and a "hal" part) + read the tram, tell each module driver to initialize its + read-tram structures, then tell each one to process the + (same) read-tram buffer + tell each module driver to initialize its write-tram + structures, then tell each one to prepare a tram write, + then write the tram - For per-register it would look like this instead: + export the main read/write functions - read MDs + if the llio claims to be threadsafe, export the gpio + read/write functions - parse MDs - for each MD + FIXME: ok down to here - allocate an array of of instance-hal objects (each entry - in the array contains one instance's hal info, like the - hal part in the old scheme) - for each register: + the per-board HAL-exported read/write functions are generic hm2 + functions that use board-specific i/o functions (hm2_lowlevel_io_t). + Each generic hm2 function is exported to HAL once for each hm2 + instance that can use it. Each HAL function is the same hm2 function + with different hostmot2_t's for arguments. - allocate an array of instance-hw -register objects - (each entry contains a buffer for reading the - register (for one-per-function) or registers (for - one-per-instance)) - Yum, could make the "allocate a register buffer" function - smart: have it allocate space for each request contiguously - in a larger buffer and set the TRAM... MMmmm, yeesssss... - <rubs hands together> + At runtime, it will eventually be desirable to use the TranslationRAM + to map all the on-board registers to be consecutive, and do a single + bulk transfer to read all the stuff into the driver. Some inspiration taken from Jeffry Molanus' driver (U. Twente). diff --git a/src/hal/drivers/mesa-hostmot2/TODO b/src/hal/drivers/mesa-hostmot2/TODO index ac277e067..4de8055dd 100644 --- a/src/hal/drivers/mesa-hostmot2/TODO +++ b/src/hal/drivers/mesa-hostmot2/TODO @@ -2,9 +2,18 @@ next steps: - firmware loading + include .PIN files as docs - default firmware? + JMK suggested auto-generating man pages from the PIN files... + + + add missing stepgen pins, general stepgen work + + + cradek wants encoder index support (i mailed pcw) + + + better sample configs, maybe move my test scripts there, document better on driver unload, it should stop all the motors and "safe" the @@ -100,6 +109,12 @@ translation ram (tram): need to deal with the "strobe" bit + currently the driver takes advantage of the fact that the module + instances are contiguous in hm2 register space. Things'll have + to change to support non-contiguous access. Could make a smart + "allocate a register buffer" function smart: have it allocate space + for each request contiguously in a larger buffer and set the TRAM + @@ -168,13 +183,13 @@ pwmgen: ioport: - move inverted output from driver to fpga - stepgen: + velocity_fb is broken: http://emergent.unpy.net/files/sandbox/hm2-step-scoped2.png + the software stepgen uses float seconds for the timing, but that cuts off at like 100 ns, shouldnt it be u32 seconds instead? or wait, is it float nanoseconds? @@ -196,6 +211,10 @@ stepgen: support velocity control + support smaller stepgens - most dont need 6 pins... + + config="stepgen="off,on:width=2" + @@ -215,9 +234,6 @@ watchdog: 5i20: - stepgen doesnt work?! No matter what I write to the Rate register, - i get no steps on the IO Pins and the Accumulator doesnt change. - turn hm2_5i20 driver into a generic PCI driver: hm2_anyio_pci diff --git a/src/hal/drivers/mesa-hostmot2/bitfile.c b/src/hal/drivers/mesa-hostmot2/bitfile.c index 5c57060a4..c58300b86 100644 --- a/src/hal/drivers/mesa-hostmot2/bitfile.c +++ b/src/hal/drivers/mesa-hostmot2/bitfile.c @@ -96,15 +96,19 @@ static int bitfile_parse_and_verify_chunk(const struct firmware *fw, bitfile_t * switch (tag) { case 'a': + // Design name return bitfile_do_small_chunk(fw, &bitfile->a, i); case 'b': + // Part ID return bitfile_do_small_chunk(fw, &bitfile->b, i); case 'c': + // Design date return bitfile_do_small_chunk(fw, &bitfile->c, i); case 'd': + // Design time return bitfile_do_small_chunk(fw, &bitfile->d, i); case 'e': diff --git a/src/hal/drivers/mesa-hostmot2/hostmot2.c b/src/hal/drivers/mesa-hostmot2/hostmot2.c index 127e9f386..d2ea33519 100644 --- a/src/hal/drivers/mesa-hostmot2/hostmot2.c +++ b/src/hal/drivers/mesa-hostmot2/hostmot2.c @@ -197,6 +197,10 @@ static int hm2_parse_config_string(hostmot2_t *hm2, char *config_string) { token = strsep(&config_string, " "); if (token == NULL) break; + if (token[0] == '\0') { + if ((config_string == NULL) || (config_string[0] == '\0')) break; + continue; + } if (strncmp(token, "num_encoders=", 13) == 0) { token += 13; @@ -836,7 +840,8 @@ int hm2_register(hm2_lowlevel_io_t *llio, char *config_string) { goto fail0; } - INFO("relevant bitfile info:\n"); + INFO("firmware %s:\n", hm2->config.firmware); + INFO(" %s %s %s\n", bitfile.a.data, bitfile.c.data, bitfile.d.data); INFO(" Part Name: %s\n", bitfile.b.data); INFO(" FPGA Config: %d bytes\n", bitfile.e.size); @@ -878,6 +883,7 @@ int hm2_register(hm2_lowlevel_io_t *llio, char *config_string) { // // export a parameter to deal with communication errors + // NOTE: this is really only useful for EPP boards, PCI doesnt use it // { @@ -986,7 +992,6 @@ int hm2_register(hm2_lowlevel_io_t *llio, char *config_string) { // allocate memory for the PC's copy of the HostMot2's registers // - // FIXME: this allocates memory, need to free it if hm2_register fails later r = hm2_allocate_tram_regions(hm2); if (r < 0) { ERR("error allocating memory for HostMot2 registers\n"); diff --git a/src/hal/drivers/mesa-hostmot2/hostmot2.h b/src/hal/drivers/mesa-hostmot2/hostmot2.h index b2a7a7ee9..a242de0d0 100644 --- a/src/hal/drivers/mesa-hostmot2/hostmot2.h +++ b/src/hal/drivers/mesa-hostmot2/hostmot2.h @@ -143,6 +143,7 @@ typedef struct { struct { hal_bit_t is_output; + hal_bit_t is_opendrain; hal_bit_t invert_output; } param; @@ -157,8 +158,18 @@ typedef struct { u8 sec_unit; u8 primary_tag; - // these are how the driver keeps track of them - int gtag; // the actual function using this pin + + // + // below here is how the driver keeps track of each pin + // + + // the actual function using this pin + int gtag; + + // either HM2_PIN_DIR_IS_INPUT or HM2_PIN_DIR_IS_OUTPUT + // if gtag != gpio, how the owning module instance configured it at load-time + // if gtag == gpio, this gets copied from the .is_output parameter + int direction; // if the driver decides to make this pin a gpio, it'll allocate the // instance struct to manage it, otherwise instance is NULL @@ -299,16 +310,18 @@ typedef struct { u32 ddr_addr; u32 *ddr_reg; - u32 *written_ddr; // FIXME: not a register, but a copy of the most recently written ddr + u32 *written_ddr; // not a register, but a copy of the most recently written value u32 alt_source_addr; u32 *alt_source_reg; u32 open_drain_addr; u32 *open_drain_reg; + u32 *written_open_drain; // not a register, but a copy of the most recently written value u32 output_invert_addr; u32 *output_invert_reg; + u32 *written_output_invert; // not a register, but a copy of the most recently written value u32 clock_frequency; u8 version; diff --git a/src/hal/drivers/mesa-hostmot2/ioport.c b/src/hal/drivers/mesa-hostmot2/ioport.c index a4fbd3a3d..6b3107c42 100644 --- a/src/hal/drivers/mesa-hostmot2/ioport.c +++ b/src/hal/drivers/mesa-hostmot2/ioport.c @@ -120,11 +120,27 @@ int hm2_ioport_parse_md(hostmot2_t *hm2, int md_index) { goto fail3; } + // this one's not a real register + hm2->ioport.written_open_drain = (u32 *)kmalloc(hm2->ioport.num_instances * sizeof(u32), GFP_KERNEL); + if (hm2->ioport.written_open_drain == NULL) { + ERR("out of memory!\n"); + r = -ENOMEM; + goto fail4; + } + hm2->ioport.output_invert_reg = (u32 *)kmalloc(hm2->ioport.num_instances * sizeof(u32), GFP_KERNEL); if (hm2->ioport.output_invert_reg == NULL) { ERR("out of memory!\n"); r = -ENOMEM; - goto fail4; + goto fail5; + } + + // this one's not a real register + hm2->ioport.written_output_invert = (u32 *)kmalloc(hm2->ioport.num_instances * sizeof(u32), GFP_KERNEL); + if (hm2->ioport.written_output_invert == NULL) { + ERR("out of memory!\n"); + r = -ENOMEM; + goto fail6; } @@ -133,11 +149,13 @@ int hm2_ioport_parse_md(hostmot2_t *hm2, int md_index) { // for (i = 0; i < hm2->ioport.num_instances; i ++) { - hm2->ioport.ddr_reg[i] = 0; // all are inputs - hm2->ioport.written_ddr[i] = 0; // we're starting out in sync - hm2->ioport.alt_source_reg[i] = 0; // they're all gpios - hm2->ioport.open_drain_reg[i] = 0; // none are open drain - hm2->ioport.output_invert_reg[i] = 0; // none are output-inverted + hm2->ioport.ddr_reg[i] = 0; // all are inputs + hm2->ioport.written_ddr[i] = 0; // we're starting out in sync + hm2->ioport.alt_source_reg[i] = 0; // they're all gpios + hm2->ioport.open_drain_reg[i] = 0; // none are open drain + hm2->ioport.written_open_drain[i] = 0; // starting out in sync + hm2->ioport.output_invert_reg[i] = 0; // none are output-inverted + hm2->ioport.written_output_invert[i] = 0; // starting out in sync } // we can't export this one to HAL yet, because some pins may be allocated to other modules @@ -145,6 +163,12 @@ int hm2_ioport_parse_md(hostmot2_t *hm2, int md_index) { return hm2->ioport.num_instances; +fail6: + kfree(hm2->ioport.output_invert_reg); + +fail5: + kfree(hm2->ioport.written_open_drain); + fail4: kfree(hm2->ioport.open_drain_reg); @@ -171,7 +195,9 @@ void hm2_ioport_cleanup(hostmot2_t *hm2) { if (hm2->ioport.written_ddr != NULL) kfree(hm2->ioport.written_ddr); if (hm2->ioport.alt_source_reg != NULL) kfree(hm2->ioport.alt_source_reg); if (hm2->ioport.open_drain_reg != NULL) kfree(hm2->ioport.open_drain_reg); + if (hm2->ioport.written_open_drain!= NULL) kfree(hm2->ioport.written_open_drain); if (hm2->ioport.output_invert_reg != NULL) kfree(hm2->ioport.output_invert_reg); + if (hm2->ioport.written_output_invert != NULL) kfree(hm2->ioport.written_output_invert); } @@ -184,94 +210,135 @@ int hm2_ioport_gpio_export_hal(hostmot2_t *hm2) { for (i = 0; i < hm2->num_pins; i ++) { int port = i / hm2->idrom.port_width; - if (hm2->pin[i].gtag != HM2_GTAG_IOPORT) { - continue; - } - + // all pins get *some* gpio HAL presence hm2->pin[i].instance = (hm2_gpio_instance_t *)hal_malloc(sizeof(hm2_gpio_instance_t)); if (hm2->pin[i].instance == NULL) { ERR("out of memory!\n"); return -ENOMEM; } - // pins - r = hal_pin_bit_newf( - HAL_OUT, - &(hm2->pin[i].instance->hal.pin.in), - hm2->llio->comp_id, - "%s.gpio.%s.%03d.in", - hm2->llio->name, - hm2->llio->ioport_connector_name[port], - i - ); - if (r != HAL_SUCCESS) { - ERR("error %d adding gpio pin, aborting\n", r); - return -EINVAL; - } - r = hal_pin_bit_newf( - HAL_OUT, - &(hm2->pin[i].instance->hal.pin.in_not), - hm2->llio->comp_id, - "%s.gpio.%s.%03d.in_not", - hm2->llio->name, - hm2->llio->ioport_connector_name[port], - i - ); - if (r != HAL_SUCCESS) { - ERR("error %d adding gpio pin, aborting\n", r); - return -EINVAL; - } + // + // it's a full GPIO or it's an input for some other module + // + + if ( + (hm2->pin[i].gtag == HM2_GTAG_IOPORT) + || (hm2->pin[i].direction == HM2_PIN_DIR_IS_INPUT) + ) { + + // pins + r = hal_pin_bit_newf( + HAL_OUT, + &(hm2->pin[i].instance->hal.pin.in), + hm2->llio->comp_id, + "%s.gpio.%s.%03d.in", + hm2->llio->name, + hm2->llio->ioport_connector_name[port], + i + ); + if (r != HAL_SUCCESS) { + ERR("error %d adding gpio pin, aborting\n", r); + return -EINVAL; + } - r = hal_pin_bit_newf( - HAL_IN, - &(hm2->pin[i].instance->hal.pin.out), - hm2->llio->comp_id, - "%s.gpio.%s.%03d.out", - hm2->llio->name, - hm2->llio->ioport_connector_name[port], - i - ); - if (r != HAL_SUCCESS) { - ERR("error %d adding gpio pin, aborting\n", r); - return -EINVAL; + r = hal_pin_bit_newf( + HAL_OUT, + &(hm2->pin[i].instance->hal.pin.in_not), + hm2->llio->comp_id, + "%s.gpio.%s.%03d.in_not", + hm2->llio->name, + hm2->llio->ioport_connector_name[port], + i + ); + if (r != HAL_SUCCESS) { + ERR("error %d adding gpio pin, aborting\n", r); + return -EINVAL; + } } - // parameters - r = hal_param_bit_newf( - HAL_RW, - &(hm2->pin[i].instance->hal.param.is_output), - hm2->llio->comp_id, - "%s.gpio.%s.%03d.is_output", - hm2->llio->name, - hm2->llio->ioport_connector_name[port], - i - ); - if (r != HAL_SUCCESS) { - ERR("error %d adding gpio param, aborting\n", r); - return -EINVAL; - } - r = hal_param_bit_newf( - HAL_RW, - &(hm2->pin[i].instance->hal.param.invert_output), - hm2->llio->comp_id, - "%s.gpio.%s.%03d.invert_output", - hm2->llio->name, - hm2->llio->ioport_connector_name[port], - i - ); - if (r != HAL_SUCCESS) { - ERR("error %d adding gpio param, aborting\n", r); - return -EINVAL; + // + // it's a full GPIO or it's an output for some other module + // + + if ( + (hm2->pin[i].gtag == HM2_GTAG_IOPORT) + || (hm2->pin[i].direction == HM2_PIN_DIR_IS_OUTPUT) + ) { + + r = hal_param_bit_newf( + HAL_RW, + &(hm2->pin[i].instance->hal.param.invert_output), + hm2->llio->comp_id, + "%s.gpio.%s.%03d.invert_output", + hm2->llio->name, + hm2->llio->ioport_connector_name[port], + i + ); + if (r != HAL_SUCCESS) { + ERR("error %d adding gpio param, aborting\n", r); + return -EINVAL; + } + + r = hal_param_bit_newf( + HAL_RW, + &(hm2->pin[i].instance->hal.param.is_opendrain), + hm2->llio->comp_id, + "%s.gpio.%s.%03d.is_opendrain", + hm2->llio->name, + hm2->llio->ioport_connector_name[port], + i + ); + if (r != HAL_SUCCESS) { + ERR("error %d adding gpio param, aborting\n", r); + return -EINVAL; + } + + hm2->pin[i].instance->hal.param.invert_output = 0; + hm2->pin[i].instance->hal.param.is_opendrain = 0; } - // initialize - hm2_set_pin_direction(hm2, i, HM2_PIN_DIR_IS_INPUT); - *(hm2->pin[i].instance->hal.pin.out) = 0; - hm2->pin[i].instance->hal.param.is_output = 0; - hm2->pin[i].instance->hal.param.invert_output = 0; + // + // it's a full GPIO + // + + if (hm2->pin[i].gtag == HM2_GTAG_IOPORT) { + + r = hal_pin_bit_newf( + HAL_IN, + &(hm2->pin[i].instance->hal.pin.out), + hm2->llio->comp_id, + "%s.gpio.%s.%03d.out", + hm2->llio->name, + hm2->llio->ioport_connector_name[port], + i + ); + if (r != HAL_SUCCESS) { + ERR("error %d adding gpio pin, aborting\n", r); + return -EINVAL; + } + + *(hm2->pin[i].instance->hal.pin.out) = 0; + + // parameters + r = hal_param_bit_newf( + HAL_RW, + &(hm2->pin[i].instance->hal.param.is_output), + hm2->llio->comp_id, + "%s.gpio.%s.%03d.is_output", + hm2->llio->name, + hm2->llio->ioport_connector_name[port], + i + ); + if (r != HAL_SUCCESS) { + ERR("error %d adding gpio param, aborting\n", r); + return -EINVAL; + } + + hm2->pin[i].instance->hal.param.is_output = 0; + } } return 0; @@ -312,47 +379,99 @@ static void hm2_ioport_force_write_ddr(hostmot2_t *hm2) { } -void hm2_ioport_force_write(hostmot2_t *hm2) { +static void hm2_ioport_force_write_output_invert(hostmot2_t *hm2) { int size = hm2->ioport.num_instances * sizeof(u32); - - hm2_ioport_force_write_ddr(hm2); - hm2->llio->write(hm2->llio, hm2->ioport.alt_source_addr, hm2->ioport.alt_source_reg, size); - hm2->llio->write(hm2->llio, hm2->ioport.open_drain_addr, hm2->ioport.open_drain_reg, size); hm2->llio->write(hm2->llio, hm2->ioport.output_invert_addr, hm2->ioport.output_invert_reg, size); + memcpy(hm2->ioport.written_output_invert, hm2->ioport.output_invert_reg, size); } -void hm2_ioport_write(hostmot2_t *hm2) { - int port; - int port_pin; - - int io_pin; - - int need_update_ddr = 0; +static void hm2_ioport_force_write_open_drain(hostmot2_t *hm2) { + int size = hm2->ioport.num_instances * sizeof(u32); + hm2->llio->write(hm2->llio, hm2->ioport.open_drain_addr, hm2->ioport.open_drain_reg, size); + memcpy(hm2->ioport.written_open_drain, hm2->ioport.open_drain_reg, size); +} - // - // update ioport variables - // +void hm2_ioport_update(hostmot2_t *hm2) { + int port; + int port_pin; for (port = 0; port < hm2->ioport.num_instances; port ++) { for (port_pin = 0; port_pin < hm2->idrom.port_width; port_pin ++) { - io_pin = (port * hm2->idrom.port_width) + port_pin; - if (hm2->pin[io_pin].gtag != HM2_GTAG_IOPORT) continue; + int io_pin = (port * hm2->idrom.port_width) + port_pin; - if (hm2->pin[io_pin].instance->hal.param.is_output) { + if (hm2->pin[io_pin].gtag == HM2_GTAG_IOPORT) { + if (hm2->pin[io_pin].instance->hal.param.is_output) { + hm2->pin[io_pin].direction = HM2_PIN_DIR_IS_OUTPUT; + } else { + hm2->pin[io_pin].direction = HM2_PIN_DIR_IS_INPUT; + } + } + + if (hm2->pin[io_pin].direction == HM2_PIN_DIR_IS_OUTPUT) { hm2->ioport.ddr_reg[port] |= (1 << port_pin); // set the bit in the ddr register + + // Open Drain Register + if (hm2->pin[io_pin].instance->hal.param.is_opendrain) { + hm2->ioport.open_drain_reg[port] |= (1 << port_pin); // set the bit in the open drain register + } else { + hm2->ioport.open_drain_reg[port] &= ~(1 << port_pin); // clear the bit in the open drain register + } + + // Invert Output Register + if (hm2->pin[io_pin].instance->hal.param.invert_output) { + hm2->ioport.output_invert_reg[port] |= (1 << port_pin); // set the bit in the output invert register + } else { + hm2->ioport.output_invert_reg[port] &= ~(1 << port_pin); // clear the bit in the output invert register + } } else { + hm2->ioport.open_drain_reg[port] &= ~(1 << port_pin); // clear the bit in the open drain register hm2->ioport.ddr_reg[port] &= ~(1 << port_pin); // clear the bit in the ddr register + // it doesnt matter what the Invert Output register says } } + } +} + + +void hm2_ioport_force_write(hostmot2_t *hm2) { + int size = hm2->ioport.num_instances * sizeof(u32); + + hm2_ioport_update(hm2); + + hm2_ioport_force_write_ddr(hm2); + hm2_ioport_force_write_output_invert(hm2); + hm2_ioport_force_write_open_drain(hm2); + + hm2->llio->write(hm2->llio, hm2->ioport.alt_source_addr, hm2->ioport.alt_source_reg, size); +} + + +void hm2_ioport_write(hostmot2_t *hm2) { + int port; + + hm2_ioport_update(hm2); + + for (port = 0; port < hm2->ioport.num_instances; port ++) { if (hm2->ioport.written_ddr[port] != hm2->ioport.ddr_reg[port]) { - need_update_ddr = 1; + hm2_ioport_force_write_ddr(hm2); + break; + } + } + + for (port = 0; port < hm2->ioport.num_instances; port ++) { + if (hm2->ioport.written_open_drain[port] != hm2->ioport.open_drain_reg[port]) { + hm2_ioport_force_write_open_drain(hm2); + break; } } - if (need_update_ddr) { - hm2_ioport_force_write_ddr(hm2); + for (port = 0; port < hm2->ioport.num_instances; port ++) { + if (hm2->ioport.written_output_invert[port] != hm2->ioport.output_invert_reg[port]) { + hm2_ioport_force_write_output_invert(hm2); + break; + } } } @@ -391,8 +510,7 @@ void hm2_ioport_gpio_process_tram_read(hostmot2_t *hm2) { int io_pin = (port * hm2->idrom.port_width) + port_pin; hal_bit_t bit; - if (hm2->pin[io_pin].gtag != HM2_GTAG_IOPORT) continue; - if (hm2->pin[io_pin].instance->hal.param.is_output) continue; + if (hm2->pin[io_pin].direction != HM2_PIN_DIR_IS_INPUT) continue; bit = (hm2->ioport.data_read_reg[port] >> port_pin) & 0x1; *hm2->pin[io_pin].instance->hal.pin.in = bit; @@ -420,14 +538,11 @@ void hm2_ioport_gpio_prepare_tram_write(hostmot2_t *hm2) { for (port = 0; port < hm2->ioport.num_instances; port ++) { for (port_pin = 0; port_pin < hm2->idrom.port_width; port_pin ++) { int io_pin = (port * hm2->idrom.port_width) + port_pin; - hal_bit_t out; if (hm2->pin[io_pin].gtag != HM2_GTAG_IOPORT) continue; - if (!hm2->pin[io_pin].instance->hal.param.is_output) continue; - out = *(hm2->pin[io_pin].instance->hal.pin.out) ^ hm2->pin[io_pin].instance->hal.param.invert_output; hm2->ioport.data_write_reg[port] &= ~(1 << port_pin); // zero the bit - hm2->ioport.data_write_reg[port] |= (out << port_pin); // and set it as appropriate + hm2->ioport.data_write_reg[port] |= (*(hm2->pin[io_pin].instance->hal.pin.out) << port_pin); // and set it as appropriate } } } @@ -444,13 +559,13 @@ void hm2_ioport_gpio_read(hostmot2_t *hm2) { hm2->ioport.num_instances * sizeof(u32) ); + // FIXME: this block duplicates code in hm2_ioport_gpio_process_tram_read() for (port = 0; port < hm2->ioport.num_instances; port ++) { for (port_pin = 0; port_pin < hm2->idrom.port_width; port_pin ++) { int io_pin = (port * hm2->idrom.port_width) + port_pin; hal_bit_t bit; - if (hm2->pin[io_pin].gtag != HM2_GTAG_IOPORT) continue; - if (hm2->pin[io_pin].instance->hal.param.is_output) continue; + if (hm2->pin[io_pin].direction != HM2_PIN_DIR_IS_INPUT) continue; bit = (hm2->ioport.data_read_reg[port] >> port_pin) & 0x1; *hm2->pin[io_pin].instance->hal.pin.in = bit; @@ -464,19 +579,17 @@ void hm2_ioport_gpio_write(hostmot2_t *hm2) { int port; int port_pin; - hm2_ioport_write(hm2); // this updates the config registers that need it + hm2_ioport_write(hm2); // this updates any config registers that need it + // FIXME: this block duplicates code in hm2_ioport_gpio_prepare_tram_write() for (port = 0; port < hm2->ioport.num_instances; port ++) { for (port_pin = 0; port_pin < hm2->idrom.port_width; port_pin ++) { int io_pin = (port * hm2->idrom.port_width) + port_pin; - hal_bit_t out; if (hm2->pin[io_pin].gtag != HM2_GTAG_IOPORT) continue; - if (!hm2->pin[io_pin].instance->hal.param.is_output) continue; - out = *(hm2->pin[io_pin].instance->hal.pin.out) ^ hm2->pin[io_pin].instance->hal.param.invert_output; hm2->ioport.data_write_reg[port] &= ~(1 << port_pin); // zero the bit - hm2->ioport.data_write_reg[port] |= (out << port_pin); // and set it as appropriate + hm2->ioport.data_write_reg[port] |= (*(hm2->pin[io_pin].instance->hal.pin.out) << port_pin); // and set it as appropriate } } diff --git a/src/hal/drivers/mesa-hostmot2/pins.c b/src/hal/drivers/mesa-hostmot2/pins.c index 119df1ccc..ef6e48830 100644 --- a/src/hal/drivers/mesa-hostmot2/pins.c +++ b/src/hal/drivers/mesa-hostmot2/pins.c @@ -211,13 +211,12 @@ void hm2_set_pin_direction(hostmot2_t *hm2, int pin_number, int direction) { return; } - if (direction == HM2_PIN_DIR_IS_INPUT) { - hm2->ioport.ddr_reg[ioport_number] &= ~(1 << bit_number); - } else if (direction == HM2_PIN_DIR_IS_OUTPUT) { - hm2->ioport.ddr_reg[ioport_number] |= (1 << bit_number); - } else { + if ((direction != HM2_PIN_DIR_IS_INPUT) && (direction != HM2_PIN_DIR_IS_OUTPUT)) { WARN("invalid pin direction 0x%08X\n", direction); + return; } + + hm2->pin[pin_number].direction = direction; } @@ -281,6 +280,7 @@ static void hm2_pins_allocate(hostmot2_t *hm2, int gtag, int num_instances) { // sets up all the IOPort instances, return 0 on success, -errno on failure void hm2_configure_pins(hostmot2_t *hm2) { + int i; // // the bits in the alt_source register of the ioport function say @@ -292,7 +292,7 @@ void hm2_configure_pins(hostmot2_t *hm2) { // // if a pin is marked as an input in the ddr, it can be used for its // function (encoder, say) *and* as a digital input pin without - // conflict, but (FIXME) the driver does not yet support this + // conflict // // Each function instance that is not disabled by the relevant // num_<functions> modparam has all its pins marked 1 in the alt_source @@ -301,12 +301,16 @@ void hm2_configure_pins(hostmot2_t *hm2) { // gpios. // + // everything defaults to GPIO input... + for (i = 0; i < hm2->num_pins; i ++) { + hm2_set_pin_source(hm2, i, HM2_PIN_SOURCE_IS_PRIMARY); + hm2_set_pin_direction(hm2, i, HM2_PIN_DIR_IS_INPUT); + } + // ... then modules get to take what they want hm2_pins_allocate(hm2, HM2_GTAG_ENCODER, hm2->encoder.num_instances); hm2_pins_allocate(hm2, HM2_GTAG_PWMGEN, hm2->pwmgen.num_instances); hm2_pins_allocate(hm2, HM2_GTAG_STEPGEN, hm2->stepgen.num_instances); - - // anything not allocated remains a gpio input } |