blob: cc9f7753d3114b7cdb39987fdc5f3f9554b56672 [file] [log] [blame]
/* Target dependent code for ARC processor family, for GDB, the GNU debugger.
Copyright 2005, 2008, 2009 Free Software Foundation, Inc.
Contributed by ARC International (www.arc.com)
Authors:
Richard Stuckey <richard.stuckey@arc.com>
A.N. Other <unknown>
This file is part of GDB.
This program 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 3 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, see <http://www.gnu.org/licenses/>. */
/******************************************************************************/
/* */
/* Outline: */
/* This module implements operations for reading data from / writing data */
/* to a parallel port using a GPIO (General Purpose Input/Output) driver. */
/* */
/******************************************************************************/
/* system header files */
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
/* gdb header files */
#include "defs.h"
/* ARC header files */
#include "arc-gpio.h"
/* -------------------------------------------------------------------------- */
/* local data */
/* -------------------------------------------------------------------------- */
// I/O control numbers.
// Linux kernel uses 0x54XX for special purposes. Avoid such.
// We'll pick large numbers.
// We don't use the linux convention of dividing the IOC into a bunch
// of bit fields. We can always switch to this later, as the
// IOC 0 returns the driver version (which IOC value will never change).
#define GPIO_IOC_VERSION 0 // returns version
#define GPIO_IOC_BASE 0xaa3a0000UL // Intended for use on ARCangel 3!
// Switch base address of parallel port. 0x3f8 is assumed.
// WARNING! You can write on any port whatsoever with this driver.
// BE CAREFUL!
#define GPIO_IOC_SET_PORT_BASE (GPIO_IOC_BASE + 1) // cmd, arg=port base
// General input/output ioctl. See GPIO_ioctl struct.
#define GPIO_IOC_DO_IO (GPIO_IOC_BASE + 2) // cmd, arg=GPIO_ioctl *
// For emergency purposes in case the driver is goofed up.
#define GPIO_IOC_HARD_RESET (GPIO_IOC_BASE + 3) // cmd, no arg
// Do you have an antiquated parallel port? You might need to ask
// the driver to use outb_p and inb_p (_p = pause). Default is not to.
#define GPIO_IOC_SET_PAUSE (GPIO_IOC_BASE + 4) // arg = 1 => use pause; otherwise not.
/* parallel port I/O addresses (LPT1) */
#define PORT_BASE_ADDRESS 0x378
#define DATA_PORT_ADDRESS (PORT_BASE_ADDRESS + DATA_PORT)
#define STATUS_PORT_ADDRESS (PORT_BASE_ADDRESS + STATUS_PORT)
#define CONTROL_PORT_ADDRESS (PORT_BASE_ADDRESS + CONTROL_PORT)
#define NULL_FILE_DESCRIPTOR (-1)
typedef struct
{
// This is used for general input and output in the same ioctl.
// N.B. "input" is input TO the port, and "output" is output FROM the port.
//
// inlen is always even and represents a number of pairs:
//
// [0|1|2, value] : write value to port_base+0|1|2.
// [0x80|0x81|0x82, <ignored>] : read value from port_base+0|1|2
// and append result to outbuf.
//
// Thus one can intermix read and write in the same ioctl.
// inlen is replaced by the number of input bytes consumed.
unsigned inlen;
char *inbuf;
// outlen is replaced by the number of output bytes written.
unsigned outlen;
char *outbuf;
} GPIO_ioctl;
/* Buffers to hold data read from / written to ports; we generally read/write
only 1 byte of data at a time, so the buffers need hold only 1 byte pair each. */
static char input_buffer [2];
static char output_buffer[2];
static GPIO_ioctl ioctl_data = { 0, input_buffer, 0, output_buffer };
/* A file descriptor for the GPIO driver. */
static int gpio_driver = NULL_FILE_DESCRIPTOR;
/* Debug data. */
// static const char name[] = {'D', 'S', 'C'};
/* -------------------------------------------------------------------------- */
/* externally visible data */
/* -------------------------------------------------------------------------- */
/* This is set to TRUE if an I/O error occurs in accessing the port. */
Boolean gpio_port_error;
/* -------------------------------------------------------------------------- */
/* externally visible functions */
/* -------------------------------------------------------------------------- */
/* Initialization of the GPIO interface. */
Boolean
gpio_open (void)
{
/* Open the driver, if not already open. */
if (gpio_driver == NULL_FILE_DESCRIPTOR)
{
gpio_driver = open(GPIO_DEVICE, O_RDWR);
if ((gpio_driver == NULL_FILE_DESCRIPTOR) ||
ioctl(gpio_driver, GPIO_IOC_HARD_RESET) ||
ioctl(gpio_driver, GPIO_IOC_SET_PORT_BASE, PORT_BASE_ADDRESS))
{
warning(_("unable to open JTAG port (device " GPIO_DEVICE "): %s"),
strerror(errno));
return FALSE;
}
gpio_port_error = FALSE;
}
return TRUE;
}
/* Close the GPIO interface. */
void
gpio_close (void)
{
/* Close the driver, if not already closed. */
if (gpio_driver != NULL_FILE_DESCRIPTOR)
{
/* Close file descriptor opened for communication with gpio driver. */
if (close(gpio_driver) == -1)
warning(_("unable to close JTAG port (device " GPIO_DEVICE "): %s"),
strerror(errno));
gpio_driver = NULL_FILE_DESCRIPTOR;
}
}
/* Write a byte of data to the given port. */
void
gpio_write (ParallelPort port, Byte data)
{
ioctl_data.inlen = 2;
ioctl_data.inbuf[0] = (char) port;
ioctl_data.inbuf[1] = (char) data;
if (ioctl(gpio_driver, GPIO_IOC_DO_IO, &ioctl_data))
error(_("Failure writing to port %d: %s"), port, strerror(errno));
/* If no data has been consumed by the port. */
if (ioctl_data.inlen == 0)
gpio_port_error = TRUE;
// DEBUG("ioctl_data.inlen: %d\n", ioctl_data.inlen);
// DEBUG("GPIO write: %02x -> %c\n", data, name[port]);
}
/* Read a byte of data from the given port. */
Byte
gpio_read (ParallelPort port)
{
ioctl_data.inlen = 2;
ioctl_data.inbuf[0] = (char) (port + 0x80);
// ioctl_data.inbuf[1] is ignored
/* N.B. outlen must be set! */
ioctl_data.outlen = 1;
ioctl_data.outbuf[0] = (char) 0; // in case the read fails
if (ioctl(gpio_driver, GPIO_IOC_DO_IO, &ioctl_data))
error(_("Failure reading from port %d: %s"), port, strerror(errno));
/* If no data has been provided by the port */
if (ioctl_data.outlen == 0)
gpio_port_error = TRUE;
// DEBUG("ioctl_data.outlen: %d\n", ioctl_data.outlen);
// DEBUG("GPIO read : %02x <- %c\n", (Byte) ioctl_data.outbuf[0], name[port]);
return (Byte) ioctl_data.outbuf[0];
}
/* Write a series of bytes of data to the ports. */
void
gpio_write_array (GPIO_Pair array[], unsigned int num_elements)
{
char buffer[num_elements * 2];
GPIO_ioctl data = { 0, buffer, 0, NULL };
unsigned int i;
for (i = 0; i < num_elements; i++)
{
buffer[data.inlen++] = (char) array[i].port;
buffer[data.inlen++] = (char) array[i].data;
}
if (ioctl(gpio_driver, GPIO_IOC_DO_IO, &data))
error(_("Failure writing to port: %s"), strerror(errno));
/* If no data has been consumed by the port. */
if (ioctl_data.inlen == 0)
gpio_port_error = TRUE;
}
/******************************************************************************/