1 /*
2  * hs-amiga-os4.c - AmigaOS 4.x specific PCI hardsid driver.
3  *
4  * Written by
5  *  Marco van den Heuvel <blackystardust68@yahoo.com>
6  *
7  * This file is part of VICE, the Versatile Commodore Emulator.
8  * See README for copyright notice.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23  *  02111-1307  USA.
24  *
25  */
26 
27 #include "vice.h"
28 
29 #ifdef AMIGA_SUPPORT
30 
31 #if defined(HAVE_HARDSID) && defined(AMIGA_OS4)
32 
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 
37 #include "hardsid.h"
38 #include "hs-amiga.h"
39 
40 #if !defined(USE_SDLUI) && !defined(USE_SDLUI2)
41 # include "loadlibs.h"
42 #else
43 # include "archdep.h"
44 #endif
45 
46 #include "log.h"
47 #include "types.h"
48 
49 
50 static unsigned char read_sid(unsigned char reg, int chipno); /* Read a SID register */
51 static void write_sid(unsigned char reg, unsigned char data, int chipno); /* Write a SID register */
52 
53 #define MAXSID 4
54 
55 static int sids_found = -1;
56 
57 static int hssids[MAXSID] = {-1, -1, -1, -1};
58 
59 /* read value from SIDs */
hs_os4_read(uint16_t addr,int chipno)60 int hs_os4_read(uint16_t addr, int chipno)
61 {
62     /* check if chipno and addr is valid */
63     if (chipno < MAXSID && hssids[chipno] != -1 && addr < 0x20) {
64         return read_sid(addr, hssids[chipno]);
65     }
66     return 0;
67 }
68 
69 /* write value into SID */
hs_os4_store(uint16_t addr,uint8_t val,int chipno)70 void hs_os4_store(uint16_t addr, uint8_t val, int chipno)
71 {
72     /* check if chipno and addr is valid */
73     if (chipno < MAXSID && hssids[chipno] != -1 && addr < 0x20) {
74         write_sid(addr, val, hssids[chipno]);
75     }
76 }
77 
78 #undef BYTE
79 #undef WORD
80 #include <exec/types.h>
81 #include <proto/expansion.h>
82 #include <proto/exec.h>
83 
84 static struct PCIIFace *IPCI = NULL;
85 
86 static struct PCIDevice *HSDevPCI = NULL;
87 static struct PCIResourceRange *HSDevBAR = NULL;
88 int HSLock = FALSE;
89 
detect_sid(int chipno)90 static int detect_sid(int chipno)
91 {
92     int i;
93 
94     for (i = 0x18; i >= 0; --i) {
95         write_sid((unsigned short)i, 0, chipno);
96     }
97 
98     write_sid(0x12, 0xff, chipno);
99 
100     for (i = 0; i < 100; ++i) {
101         if (read_sid(0x1b, chipno)) {
102             return 0;
103         }
104     }
105 
106     write_sid(0x0e, 0xff, chipno);
107     write_sid(0x0f, 0xff, chipno);
108     write_sid(0x12, 0x20, chipno);
109 
110     for (i = 0; i < 100; ++i) {
111         if (read_sid(0x1b, chipno)) {
112             return 1;
113         }
114     }
115     return 0;
116 }
117 
close_device(void)118 static void close_device(void)
119 {
120     if (HSDevBAR) {
121         HSDevPCI->FreeResourceRange(HSDevBAR);
122     }
123     if (HSLock) {
124         HSDevPCI->Unlock();
125     }
126     if (IPCI) {
127         IExec->DropInterface((struct Interface *)IPCI);
128     }
129 }
130 
hs_os4_open(void)131 int hs_os4_open(void)
132 {
133     unsigned int i, j;
134 
135     if (!sids_found) {
136         return -1;
137     }
138 
139     if (sids_found > 0) {
140         return 0;
141     }
142 
143     sids_found = 0;
144 
145     log_message(LOG_DEFAULT, "Detecting PCI HardSID boards.");
146 
147     if (!pci_lib_loaded) {
148         log_message(LOG_DEFAULT, "Expansion library not available.");
149         return -1;
150     }
151 
152     IPCI = (struct PCIIFace *)IExec->GetInterface(ExpansionBase, "pci", 1, NULL);
153     if (!IPCI) {
154         log_message(LOG_DEFAULT, "Unable to obtain PCI expansion interface.");
155         return -1;
156     }
157 
158     /* Try and find a HS on the PCI bus */
159     HSDevPCI = IPCI->FindDeviceTags(FDT_VendorID, 0x6581,
160                                     FDT_DeviceID, 0x8580,
161                                     FDT_Index,    0,
162                                     TAG_DONE);
163     if (!HSDevPCI) {
164         log_message(LOG_DEFAULT, "Unable to find a PCI HardSID board.");
165         close_device();
166         return -1;
167     }
168 
169     /* Lock the device, since we're a driver */
170     HSLock = HSDevPCI->Lock(PCI_LOCK_SHARED);
171     if (!HSLock) {
172         log_message(LOG_DEFAULT, "Unable to lock the PCI HardSID. Another driver may have an exclusive lock.");
173         close_device();
174         return -1;
175     }
176 
177     /* Get the resource range */
178     HSDevBAR = HSDevPCI->GetResourceRange(0);
179     if (!HSDevBAR) {
180         log_message(LOG_DEFAULT, "Unable to get PCI HardSID resource range 0.");
181         close_device();
182         return -1;
183     }
184 
185     /* Reset the hardsid PCI interface (as per hardsid linux driver) */
186     HSDevPCI->OutByte(HSDevBAR->BaseAddress + 0x00, 0xff);
187     HSDevPCI->OutByte(HSDevBAR->BaseAddress + 0x02, 0xff);
188     usleep(100);
189     HSDevPCI->OutByte(HSDevBAR->BaseAddress + 0x02, 0x24);
190 
191     for (j = 0; j < MAXSID; ++j) {
192         if (detect_sid(j)) {
193             hssids[sids_found] = j;
194             sids_found++;
195         }
196     }
197 
198     if (!sids_found) {
199         log_message(LOG_DEFAULT, "No PCI HardSID boards found.");
200         close_device();
201         return -1;
202     }
203 
204     /* mute all sids */
205     for (j = 0; j < MAXSID; ++j) {
206         if (hssids[j] != -1) {
207             for (i = 0; i < 32; i++) {
208                 write_sid(i, 0, hssids[j]);
209             }
210         }
211     }
212 
213     log_message(LOG_DEFAULT, "PCI HardSID: opened at $%X with %d SIDs.", HSDevBAR->BaseAddress, sids_found);
214 
215     return 0;
216 }
217 
hs_os4_close(void)218 int hs_os4_close(void)
219 {
220     unsigned int i, j;
221 
222     /* mute all sids */
223     for (j = 0; j < MAXSID; ++j) {
224         if (hssids[j] != -1) {
225             for (i = 0; i < 32; i++) {
226                 write_sid(i, 0, hssids[j]);
227             }
228         }
229         hssids[j] = -1;
230     }
231 
232     close_device();
233 
234     log_message(LOG_DEFAULT, "PCI HardSID: closed.");
235 
236     sids_found = -1;
237 
238     return 0;
239 }
240 
read_sid(unsigned char reg,int chipno)241 static unsigned char read_sid(unsigned char reg, int chipno)
242 {
243     unsigned char ret;
244 
245     HSDevPCI->OutByte(HSDevBAR->BaseAddress + 4, ((chipno << 6) | (reg & 0x1f) | 0x20));
246     usleep(2);
247     HSDevPCI->OutByte(HSDevBAR->BaseAddress, 0x20);
248     ret = HSDevPCI->InByte(HSDevBAR->BaseAddress);
249     HSDevPCI->OutByte(HSDevBAR->BaseAddress, 0x80);
250 
251     return ret;
252 }
253 
write_sid(unsigned char reg,unsigned char data,int chipno)254 static void write_sid(unsigned char reg, unsigned char data, int chipno)
255 {
256     HSDevPCI->OutWord(HSDevBAR->BaseAddress + 3, ((chipno << 14) | (reg & 0x1f) << 8) | data);
257 }
258 
hs_os4_available(void)259 int hs_os4_available(void)
260 {
261     return sids_found;
262 }
263 #endif
264 #endif
265