summaryrefslogtreecommitdiff
path: root/src/hal/drivers/pluto_common.h
blob: 70f41f69da5ecbca8d3c5ece23e54884a1f1ce8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//    Copyright (C) 2007 Jeff Epler
//
//    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 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

#ifdef SIM
#include <sys/io.h>
#include <errno.h>
#else
#include <asm/io.h>
#endif

#include "hal_parport.h"

int ioaddr = 0x378;
int ioaddr_hi = 0;
int epp_wide = 1;
int watchdog = 1;
struct hal_parport_t portdata;

RTAPI_MP_INT(ioaddr, "Address of parallel port where pluto-p is attached");
RTAPI_MP_INT(ioaddr_hi,
	"Secondary address of parallel port (0 to use ioaddr+0x400)");
RTAPI_MP_INT(epp_wide, "Use 16- and 32-bit EPP transfers with hardware EPP");
RTAPI_MP_INT(watchdog,
	"Enable hardware watchdog to tristate outputs if EMC crashes");

#ifndef llabs // linux/kernel.h may provide labs for realtime systems
static int64_t llabs(int64_t l) { if(l < 0) return -l; return l; }
#endif

static inline int64_t extend(int64_t old, int newlow, int nbits) {
    int64_t mask = (1<<nbits) - 1;
    int64_t maxdelta = mask / 2;
    int64_t oldhigh = old & ~mask;
    int64_t oldlow = old & mask;
    int64_t candidate1, candidate2;

    candidate1 = oldhigh | newlow;
    if(oldlow < newlow) candidate2 = candidate1 - (1<<nbits);
    else                candidate2 = candidate1 + (1<<nbits);

    if (llabs(old-candidate1) > maxdelta)
	return candidate2;
    else
	return candidate1;
}

static inline void EPP_DIR_WRITE(void) { }
static inline void EPP_DIR_READ(void) { }
static inline void EPP_ADDR(int w) {
    outb(w, ioaddr+3);
}

static inline void EPP_WRITE(int w) {
    outb(w, ioaddr+4);
}

static inline int EPP_READ(void) {
    return inb(ioaddr+4);
}


static inline __u32 read32(void) {
    unsigned char a, b, c, d;

    if(epp_wide)
	return inl(ioaddr+4);

    a = EPP_READ();
    b = EPP_READ();
    c = EPP_READ();
    d = EPP_READ();

    return a + (b<<8) + (c<<16) + (d<<24);
}

static inline void write32(long w) {
    if(epp_wide) {
	outl(w, ioaddr+4);
	return;
    }

    EPP_WRITE(w);
    EPP_WRITE(w >> 8);
    EPP_WRITE(w >> 16);
    EPP_WRITE(w >> 24);
}

static inline void write16(int w) {
    if(epp_wide) {
	outw(w, ioaddr+4);
	return;
    }

    EPP_WRITE(w & 0xff);
    EPP_WRITE(w >> 8);
}


#define FIRMWARE_SIZE 19895
static void pluto_program(unsigned char firmware[FIRMWARE_SIZE]) {
    int byte, bit;
    int i;
    rtapi_print_msg(RTAPI_MSG_INFO, "uploading firmware\n");

    // pull the reset low -- bit 2 of status register
    // keep it low 2 microseconds
    for(i=0; i<4; i++) outb(0, ioaddr+2);

    // let it go high again
    // delay 10 microseconds to guarantee nStatus high
    for(i=0; i<20; i++) outb(4, ioaddr+2);

    // Now program the device...
    for(byte = 0; byte < FIRMWARE_SIZE; byte++) {
	for(bit = 0; bit < 8; bit++) {
	    int v = firmware[byte] & (1<<bit);
	    if(v) outb(0xff, ioaddr); else outb(0, ioaddr);
	    outb(0|4, ioaddr+2);
	    outb(1|4, ioaddr+2);
	    outb(0|4, ioaddr+2);
	}
    }
    rtapi_print_msg(RTAPI_MSG_INFO, "done\n");
}

static void pluto_clear_error_register(void) {
    /* To clear timeout some chips require double read */
    int r = inb(ioaddr+1);
    outb (r | 0x01, ioaddr+1); /* Some reset by writing 1 */
    outb (r & 0xfe, ioaddr+1); /* Others by writing 0 */
}

static void pluto_cleanup(void) {
    hal_parport_release(&portdata);
}

static int pluto_setup(unsigned char *firmware) {
    int retval = hal_parport_get(comp_id, &portdata, ioaddr, ioaddr_hi,
        PARPORT_MODE_EPP);
    int status;

    if(retval < 0)
        return retval;

    ioaddr = portdata.base;
    ioaddr_hi = portdata.base_hi;

    outb(4, ioaddr + 2);        // set control lines and input mode
    if(ioaddr_hi != -1)
        outb(0, ioaddr_hi + 0x2);    // select SPP mode in ECR
    pluto_program(firmware);

    if(ioaddr_hi != -1) {
        outb(0x80, ioaddr_hi + 0x2); // select EPP mode in ECR
    }

    // Check for presence of working EPP hardware
    pluto_clear_error_register();
    EPP_ADDR(0);
    EPP_DIR_READ();
    EPP_READ();
    status = inb(ioaddr+1) & 1;
    if(status) {
        rtapi_print_msg(RTAPI_MSG_ERR, "Failed to communicate with pluto-servo board after programming firmware.\n");
        return -EIO;
    }
    return 0;
}