[kernel] r7857 - in dists/sid/linux-2.6/debian: . arch/m68k patches
patches/series
Christian T. Steigies
cts at alioth.debian.org
Sun Nov 19 23:58:29 UTC 2006
Author: cts
Date: Mon Nov 20 00:58:24 2006
New Revision: 7857
Added:
dists/sid/linux-2.6/debian/patches/m68k-amiga.patch
dists/sid/linux-2.6/debian/patches/m68k-apollo.patch
dists/sid/linux-2.6/debian/patches/m68k-atari-ethernet.patch
dists/sid/linux-2.6/debian/patches/m68k-atari-keyboard.patch
dists/sid/linux-2.6/debian/patches/m68k-atari-mouse.patch
dists/sid/linux-2.6/debian/patches/m68k-atari-scsi.patch
dists/sid/linux-2.6/debian/patches/m68k-atari-serial.patch
dists/sid/linux-2.6/debian/patches/m68k-atari-video.patch
dists/sid/linux-2.6/debian/patches/m68k-atari.patch
dists/sid/linux-2.6/debian/patches/m68k-misc.patch
dists/sid/linux-2.6/debian/patches/series/6-extra
Modified:
dists/sid/linux-2.6/debian/arch/m68k/config.atari
dists/sid/linux-2.6/debian/changelog
Log:
add various atari patches, but do not re-enable building for atari yet
Modified: dists/sid/linux-2.6/debian/arch/m68k/config.atari
==============================================================================
--- dists/sid/linux-2.6/debian/arch/m68k/config.atari (original)
+++ dists/sid/linux-2.6/debian/arch/m68k/config.atari Mon Nov 20 00:58:24 2006
@@ -101,3 +101,14 @@
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_ATARI_PARTITION=y
CONFIG_CRC32=y
+# enable new ethernet drivers
+CONFIG_ATARI_ROM_ISA=y
+CONFIG_ARANYM=y
+CONFIG_NATFEAT=y
+# Atari SCSI driver is buggy
+# CONFIG_ATARI_SCSI is not set
+# enable ethernet
+CONFIG_NET_ETHERNET=y
+CONFIG_ATARILANCE=m
+CONFIG_ATARI_NFETH=m
+CONFIG_ATARI_ETHERNEC=m
Modified: dists/sid/linux-2.6/debian/changelog
==============================================================================
--- dists/sid/linux-2.6/debian/changelog (original)
+++ dists/sid/linux-2.6/debian/changelog Mon Nov 20 00:58:24 2006
@@ -79,7 +79,11 @@
* Added support for TI ez430 development tool ID in ti_usb.
Thanks to Oleg Verych for providing the patch.
- -- maximilian attems <maks at sternwelten.at> Sun, 19 Nov 2006 23:29:04 +0100
+ [ Christian T. Steigies ]
+ * Added support for Atari EtherNEC, Aranym, video, keyboard, mouse, and serial
+ by Michael Schmitz
+
+ -- Christian T. Steigies <cts at debian.org> Mon, 20 Nov 2006 00:48:50 +0100
linux-2.6 (2.6.18-5) unstable; urgency=low
Added: dists/sid/linux-2.6/debian/patches/m68k-amiga.patch
==============================================================================
--- (empty file)
+++ dists/sid/linux-2.6/debian/patches/m68k-amiga.patch Mon Nov 20 00:58:24 2006
@@ -0,0 +1,22 @@
+diff -urN linux-m68k/drivers/char/ioext.h linux-schmitz/drivers/char/ioext.h
+--- linux-m68k/drivers/char/ioext.h 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/drivers/char/ioext.h 2006-11-19 21:37:26.000000000 +0100
+@@ -6,7 +6,6 @@
+ #ifndef _IOEXT_H_
+ #define _IOEXT_H_
+
+-#include <linux/config.h>
+ #include <linux/netdevice.h>
+
+ #include "16c552.h"
+diff -urN linux-m68k/drivers/char/plip_ioext.c linux-schmitz/drivers/char/plip_ioext.c
+--- linux-m68k/drivers/char/plip_ioext.c 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/drivers/char/plip_ioext.c 2006-11-19 21:37:26.000000000 +0100
+@@ -27,7 +27,6 @@
+ #include <asm/amigaints.h>
+ #include <linux/zorro.h>
+
+-#include <linux/config.h>
+ #include <linux/kernel.h>
+ #include <linux/fcntl.h>
+ #include <linux/string.h>
Added: dists/sid/linux-2.6/debian/patches/m68k-apollo.patch
==============================================================================
--- (empty file)
+++ dists/sid/linux-2.6/debian/patches/m68k-apollo.patch Mon Nov 20 00:58:24 2006
@@ -0,0 +1,54 @@
+diff -urN linux-m68k/arch/m68k/apollo/dma.c linux-schmitz/arch/m68k/apollo/dma.c
+--- linux-m68k/arch/m68k/apollo/dma.c 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/arch/m68k/apollo/dma.c 1970-01-01 01:00:00.000000000 +0100
+@@ -1,50 +0,0 @@
+-#include <linux/types.h>
+-#include <linux/kernel.h>
+-#include <linux/mm.h>
+-#include <linux/kd.h>
+-#include <linux/tty.h>
+-#include <linux/console.h>
+-
+-#include <asm/setup.h>
+-#include <asm/bootinfo.h>
+-#include <asm/system.h>
+-#include <asm/pgtable.h>
+-#include <asm/apollodma.h>
+-#include <asm/io.h>
+-
+-/* note only works for 16 Bit 1 page DMA's */
+-
+-static unsigned short next_free_xlat_entry=0;
+-
+-unsigned short dma_map_page(unsigned long phys_addr,int count,int type) {
+-
+- unsigned long page_aligned_addr=phys_addr & (~((1<<12)-1));
+- unsigned short start_map_addr=page_aligned_addr >> 10;
+- unsigned short free_xlat_entry, *xlat_map_entry;
+- int i;
+-
+- free_xlat_entry=next_free_xlat_entry;
+- for(i=0,xlat_map_entry=addr_xlat_map+(free_xlat_entry<<2);i<8;i++,xlat_map_entry++) {
+-#if 0
+- printk("phys_addr: %x, page_aligned_addr: %x, start_map_addr: %x\n",phys_addr,page_aligned_addr,start_map_addr+i);
+-#endif
+- out_be16(xlat_map_entry, start_map_addr+i);
+- }
+-
+- next_free_xlat_entry+=2;
+- if(next_free_xlat_entry>125)
+- next_free_xlat_entry=0;
+-
+-#if 0
+- printk("next_free_xlat_entry: %d\n",next_free_xlat_entry);
+-#endif
+-
+- return free_xlat_entry<<10;
+-}
+-
+-void dma_unmap_page(unsigned short dma_addr) {
+-
+- return ;
+-
+-}
+-
Added: dists/sid/linux-2.6/debian/patches/m68k-atari-ethernet.patch
==============================================================================
--- (empty file)
+++ dists/sid/linux-2.6/debian/patches/m68k-atari-ethernet.patch Mon Nov 20 00:58:24 2006
@@ -0,0 +1,2081 @@
+diff -urN linux-m68k/arch/m68k/Kconfig linux-schmitz/arch/m68k/Kconfig
+--- linux-m68k/arch/m68k/Kconfig 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/arch/m68k/Kconfig 2006-11-19 21:37:26.000000000 +0100
+@@ -132,6 +132,31 @@
+ information about which PCI hardware does work under Linux and which
+ doesn't.
+
++config ATARI_ROM_ISA
++ bool "Atari ROM port ISA adapter support"
++ depends on ATARI
++ help
++ This option enables support for the ROM port ISA adapter used to
++ operate ISA cards on Atari. Only 8 bit cards are supported, and
++ no interrupt lines are connected.
++ The only driver currently using this adapter is the EtherNEC
++ driver for RTL8019AS based NE2000 compatible network cards.
++
++config ARANYM
++ bool "ARAnyM emulator support "
++ depends on ATARI
++ help
++ This option enables support for the ARAnyM emulator support
++ features. To use this kernel on ARAnyM, say Y here; otherwise say N.
++
++config NATFEAT
++ bool
++ depends on ARANYM
++ default y
++ help
++ This option enables support for ARAnyM native features, such as
++ access to a disk image as /dev/hda. Useful with the ARANYM option.
++
+ config MAC
+ bool "Macintosh support"
+ depends on !MMU_SUN3
+diff -urN linux-m68k/arch/m68k/atari/Makefile linux-schmitz/arch/m68k/atari/Makefile
+--- linux-m68k/arch/m68k/atari/Makefile 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/arch/m68k/atari/Makefile 2006-11-19 21:37:26.000000000 +0100
+@@ -3,8 +3,12 @@
+ #
+
+ obj-y := config.o time.o debug.o ataints.o stdma.o \
+- atasound.o stram.o atari_ksyms.o
++ atakeyb.o atasound.o stram.o atari_ksyms.o
+
+ ifeq ($(CONFIG_PCI),y)
+ obj-$(CONFIG_HADES) += hades-pci.o
+ endif
++
++ifeq ($(CONFIG_NATFEAT),y)
++obj-$(CONFIG_NATFEAT) += natfeat.o
++endif
+diff -urN linux-m68k/arch/m68k/atari/config.c linux-schmitz/arch/m68k/atari/config.c
+--- linux-m68k/arch/m68k/atari/config.c 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/arch/m68k/atari/config.c 2006-11-19 21:37:26.000000000 +0100
+@@ -32,6 +32,10 @@
+ #include <linux/ioport.h>
+ #include <linux/vt_kern.h>
+
++#ifdef CONFIG_NATFEAT
++#include "natfeat.h"
++#endif
++
+ #include <asm/bootinfo.h>
+ #include <asm/setup.h>
+ #include <asm/atarihw.h>
+@@ -207,6 +211,12 @@
+ }
+ }
+
++void atari_poweroff(void)
++{
++#ifdef CONFIG_NATFEAT
++ nf_shutdown();
++#endif
++}
+
+ /*
+ * Setup the Atari configuration info
+@@ -218,6 +228,10 @@
+
+ memset(&atari_hw_present, 0, sizeof(atari_hw_present));
+
++#ifdef CONFIG_NATFEAT
++ nf_init();
++#endif
++
+ atari_debug_init();
+
+ ioport_resource.end = 0xFFFFFFFF; /* Change size of I/O space from 64KB
+@@ -229,6 +243,8 @@
+ mach_get_hardware_list = atari_get_hardware_list;
+ mach_gettimeoffset = atari_gettimeoffset;
+ mach_reset = atari_reset;
++ mach_halt = atari_poweroff;
++ mach_power_off = atari_poweroff;
+ mach_max_dma_address = 0xffffff;
+ #if defined(CONFIG_INPUT_M68K_BEEP) || defined(CONFIG_INPUT_M68K_BEEP_MODULE)
+ mach_beep = atari_mksound;
+@@ -614,6 +630,11 @@
+
+ static void atari_get_model(char *model)
+ {
++#ifdef CONFIG_NATFEAT
++ if (nf_name1(model, 80)) { // char model[80] defined in kernel/setup.c
++ return;
++ }
++#endif
+ strcpy(model, "Atari ");
+ switch (atari_mch_cookie >> 16) {
+ case ATARI_MCH_ST:
+diff -urN linux-m68k/arch/m68k/atari/ethernet_nfapi.h linux-schmitz/arch/m68k/atari/ethernet_nfapi.h
+--- linux-m68k/arch/m68k/atari/ethernet_nfapi.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/arch/m68k/atari/ethernet_nfapi.h 2006-11-19 21:37:26.000000000 +0100
+@@ -0,0 +1,56 @@
++/*
++ * ARAnyM ethernet driver - header file.
++ *
++ * Copyright (c) 2002-2004 Standa and Petr of ARAnyM dev team (see AUTHORS)
++ *
++ * This file is part of the ARAnyM project which builds a new and powerful
++ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
++ *
++ * ARAnyM 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.
++ *
++ * ARAnyM 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 ARAnyM; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#ifndef _ARAETHER_NFAPI_H
++#define _ARAETHER_NFAPI_H
++
++/*
++ The code for the FreeMiNT driver was moved to the FreeMiNT CVS:
++ freemint/sys/sockets/xif/nfeth.
++
++ If you edit this file then you would need to synchronize it with the
++ driver in the freemint CVS (nfeth_nfapi.h).
++
++ if you change anything in the enum {} below you have to increase
++ this ARAETHER_NFAPI_VERSION!
++*/
++#define ARAETHER_NFAPI_VERSION 0x00000005
++
++enum {
++ GET_VERSION = 0, /* no parameters, return NFAPI_VERSION in d0 */
++ XIF_INTLEVEL, /* no parameters, return Interrupt Level in d0 */
++ XIF_IRQ, /* acknowledge interrupt from host */
++ XIF_START, /* (ethX), called on 'ifup', start receiver thread */
++ XIF_STOP, /* (ethX), called on 'ifdown', stop the thread */
++ XIF_READLENGTH, /* (ethX), return size of network data block to read */
++ XIF_READBLOCK, /* (ethX, buffer, size), read block of network data */
++ XIF_WRITEBLOCK, /* (ethX, buffer, size), write block of network data */
++ XIF_GET_MAC, /* (ethX, buffer, size), return MAC HW addr in buffer */
++ XIF_GET_IPHOST, /* (ethX, buffer, size), return IP address of host */
++ XIF_GET_IPATARI, /* (ethX, buffer, size), return IP address of atari */
++ XIF_GET_NETMASK /* (ethX, buffer, size), return IP netmask */
++};
++
++#define ETH(a) (nfEtherID + a)
++
++#endif /* _ARAETHER_NFAPI_H */
+diff -urN linux-m68k/arch/m68k/atari/natfeat.c linux-schmitz/arch/m68k/atari/natfeat.c
+--- linux-m68k/arch/m68k/atari/natfeat.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/arch/m68k/atari/natfeat.c 2006-11-19 21:37:26.000000000 +0100
+@@ -0,0 +1,231 @@
++/*
++ * natfeat.c - ARAnyM hardware support via Native Features (natfeats)
++ *
++ * Copyright (c) 2005 Petr Stehlik of ARAnyM dev team
++ *
++ * This software may be used and distributed according to the terms of
++ * the GNU General Public License (GPL), incorporated herein by reference.
++ */
++
++#include <linux/config.h>
++#include <linux/types.h>
++#include <linux/string.h> // strncpy()
++#include <linux/kernel.h> // snprintf()
++#include <asm/io.h> // virt_to_phys()
++
++#include "natfeat.h"
++
++#ifdef CONFIG_ATARI_NFETH
++#include "natfeat_ethernet_nfapi.h"
++#endif
++
++#ifndef TRUE
++#define TRUE 1
++#endif
++
++#ifndef FALSE
++#define FALSE 0
++#endif
++
++static unsigned long nf_get_id_instr = 0x73004e75UL;
++static unsigned long nf_call_instr = 0x73014e75UL;
++
++static struct nf_ops _nf_ops = { &nf_get_id_instr, &nf_call_instr };
++static struct nf_ops *nf_ops = NULL;
++
++#define NF_GETID(a) nf_ops->get_id(virt_to_phys(a))
++
++int detect_native_features(void)
++{
++ int ret;
++ long save_sp, save_vbr;
++ static long tmp_vectors[5];
++ static char *nf_version = "NF_VERSION";
++
++ __asm__ __volatile__
++ ( "movec %/vbr,%2\n\t" /* save vbr value */
++ "movel #Liierr,%3@(0x10)\n\t" /* set Illegal Insn vec */
++ "movec %3,%/vbr\n\t" /* set up temporary vectors */
++ "movel %/sp,%1\n\t" /* save sp */
++ "moveq #0,%0\n\t" /* assume no NatFeats */
++ "clrl %/d0\n\t" /* clear ID value register */
++ "movel %4,%/sp@\n\t"
++ "subql #4,%/sp\n\t"
++ "dc 0x7300\n\t" /* call NatFeat GetID */
++ "tstl %/d0\n\t" /* check ID value register */
++ "sne %0\n\t" /* if non-zero NatFeats work */
++ "Liierr:\n\t"
++ "movel %1,%/sp\n\t" /* restore sp */
++ "movec %2,%/vbr" /* restore vbr */
++ : "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
++ : "a" (tmp_vectors), "a" (virt_to_phys(nf_version))
++ : "d0" /* reg d0 used by NatFeats */
++ );
++
++ return( ret );
++}
++
++struct nf_ops * nf_init(void)
++{
++ if (detect_native_features()) {
++ nf_ops = &_nf_ops;
++ nf_debug("NatFeats found\n");
++ return nf_ops;
++ }
++
++ return NULL;
++}
++
++/****************/
++/* NF Basic Set */
++int nf_name1(char *buf, int bufsize)
++{
++ if (nf_ops) {
++ static long nfid_name = 0;
++ if (nfid_name == 0) {
++ nfid_name = NF_GETID("NF_NAME");
++ }
++
++ if (nfid_name) {
++ nf_ops->call(nfid_name+1, virt_to_phys(buf), bufsize); // TODO: lock buf to prevent swap out
++ return TRUE;
++ }
++ }
++
++ return FALSE;
++}
++
++int nf_debug(const char *msg)
++{
++ if (nf_ops) {
++ static long nfid_stderr = 0;
++ if (nfid_stderr == 0) {
++ nfid_stderr = NF_GETID("NF_STDERR");
++ }
++
++ if (nfid_stderr) {
++ nf_ops->call(nfid_stderr, virt_to_phys(msg)); // TODO: lock msg to prevent swap out
++ return TRUE;
++ }
++ }
++
++ return FALSE;
++}
++
++int nf_shutdown(void)
++{
++ if (nf_ops) {
++ long shutdown_id = NF_GETID("NF_SHUTDOWN");
++
++ if (shutdown_id) {
++ nf_ops->call(shutdown_id);
++ return TRUE; /* never returns actually */
++ }
++ }
++
++ return FALSE;
++}
++
++#ifdef CONFIG_ATARI_NFETH
++
++/****************************/
++/* NatFeat Ethernet support */
++static long nfEtherID = 0;
++
++static int is_nf_eth(void)
++{
++ if (nf_ops) {
++ if (nfEtherID == 0) {
++ nfEtherID = NF_GETID("ETHERNET");
++ }
++ }
++ return (nfEtherID != 0);
++}
++
++int nf_ethernet_check_version(char *errmsg, int errmsglen)
++{
++ if (is_nf_eth()) {
++ static unsigned long host_ver = 0;
++ if (host_ver == 0) {
++ host_ver = nf_ops->call(ETH(GET_VERSION));
++ }
++ if (host_ver == ARAETHER_NFAPI_VERSION) {
++ return TRUE;
++ }
++ else {
++ // API version mismatch
++ if (errmsg != NULL) {
++ snprintf(errmsg, errmsglen,
++ "NatFeat Ethernet API: expected %d, got %ld",
++ ARAETHER_NFAPI_VERSION, host_ver);
++ }
++ return FALSE;
++ }
++ }
++ if (errmsg != NULL) {
++ strncpy(errmsg, "NatFeat Ethernet support not found", errmsglen);
++ }
++ return FALSE;
++}
++
++int nf_ethernet_get_irq(void)
++{
++ return is_nf_eth() ? nf_ops->call(ETH(XIF_INTLEVEL)) : 0;
++}
++
++int nf_ethernet_get_hw_addr(int ethX, char *buffer, int bufsize)
++{
++ if (is_nf_eth()) {
++ return nf_ops->call(ETH(XIF_GET_MAC), (unsigned long)ethX, virt_to_phys(buffer), (unsigned long)bufsize); // TODO: lock buffer to prevent swap out
++ }
++ return FALSE;
++}
++
++int nf_ethernet_interrupt(int bit)
++{
++ if (is_nf_eth()) {
++ return nf_ops->call(ETH(XIF_IRQ), (unsigned long)bit);
++ }
++ return 0;
++}
++
++int nf_ethernet_read_packet_len(int ethX)
++{
++ if (is_nf_eth()) {
++ return nf_ops->call(ETH(XIF_READLENGTH), (unsigned long)ethX);
++ }
++ return 0;
++}
++
++void nf_ethernet_read_block(int ethX, char *buffer, int len)
++{
++ if (is_nf_eth()) {
++ nf_ops->call(ETH(XIF_READBLOCK), (unsigned long)ethX, virt_to_phys(buffer), (unsigned long)len); // TODO: lock buffer to prevent swap out
++ }
++}
++
++void nf_ethernet_write_block(int ethX, char *buffer, int len)
++{
++ if (is_nf_eth()) {
++ nf_ops->call(ETH(XIF_WRITEBLOCK), (unsigned long)ethX, virt_to_phys(buffer), (unsigned long)len); // TODO: lock buffer to prevent swap out
++ }
++}
++
++void nf_ethernet_xif_start(int ethX)
++{
++ if (is_nf_eth()) {
++ nf_ops->call(ETH(XIF_START), (unsigned long)ethX);
++ }
++}
++
++void nf_ethernet_xif_stop(int ethX)
++{
++ if (is_nf_eth()) {
++ nf_ops->call(ETH(XIF_STOP), (unsigned long)ethX);
++ }
++}
++#endif // CONFIG_ATARI_NFETH
++
++/*
++vim:ts=4:sw=4:
++*/
+diff -urN linux-m68k/arch/m68k/atari/natfeat.h linux-schmitz/arch/m68k/atari/natfeat.h
+--- linux-m68k/arch/m68k/atari/natfeat.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/arch/m68k/atari/natfeat.h 2006-11-19 21:37:26.000000000 +0100
+@@ -0,0 +1,35 @@
++/*
++ * ARAnyM hardware support via Native Features (natfeats)
++ *
++ * Copyright (c) 2005 Petr Stehlik of ARAnyM dev team
++ *
++ * This software may be used and distributed according to the terms of
++ * the GNU General Public License (GPL), incorporated herein by reference.
++ */
++
++#ifndef _natfeat_h
++#define _natfeat_h
++
++struct nf_ops
++{
++ long (*get_id)(const char *);
++ long (*call)(long id, ...);
++ long res[3];
++};
++
++struct nf_ops *nf_init(void);
++
++int nf_name(char *buf, int bufsize);
++int nf_debug(const char *msg);
++int nf_shutdown(void);
++
++int nf_ethernet_check_version(char *errmsg, int errmsglen);
++int nf_ethernet_get_irq(void);
++int nf_ethernet_get_hw_addr(int ethX, char *buffer, int bufsize);
++int nf_ethernet_interrupt(int bit);
++int nf_ethernet_read_packet_len(int ethX);
++void nf_ethernet_read_block(int ethX, char *buffer, int len);
++void nf_ethernet_write_block(int ethX, char *buffer, int len);
++void nf_ethernet_xif_start(int ethX);
++void nf_ethernet_xif_stop(int ethX);
++# endif /* _natfeat_h */
+diff -urN linux-m68k/arch/m68k/atari/natfeat_ethernet_nfapi.h linux-schmitz/arch/m68k/atari/natfeat_ethernet_nfapi.h
+--- linux-m68k/arch/m68k/atari/natfeat_ethernet_nfapi.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/arch/m68k/atari/natfeat_ethernet_nfapi.h 2006-11-19 21:37:26.000000000 +0100
+@@ -0,0 +1,56 @@
++/*
++ * ARAnyM ethernet driver - header file.
++ *
++ * Copyright (c) 2002-2004 Standa and Petr of ARAnyM dev team (see AUTHORS)
++ *
++ * This file is part of the ARAnyM project which builds a new and powerful
++ * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
++ *
++ * ARAnyM 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.
++ *
++ * ARAnyM 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 ARAnyM; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#ifndef _ARAETHER_NFAPI_H
++#define _ARAETHER_NFAPI_H
++
++/*
++ The code for the FreeMiNT driver was moved to the FreeMiNT CVS:
++ freemint/sys/sockets/xif/nfeth.
++
++ If you edit this file then you would need to synchronize it with the
++ driver in the freemint CVS (nfeth_nfapi.h).
++
++ if you change anything in the enum {} below you have to increase
++ this ARAETHER_NFAPI_VERSION!
++*/
++#define ARAETHER_NFAPI_VERSION 0x00000005
++
++enum {
++ GET_VERSION = 0, /* no parameters, return NFAPI_VERSION in d0 */
++ XIF_INTLEVEL, /* no parameters, return Interrupt Level in d0 */
++ XIF_IRQ, /* acknowledge interrupt from host */
++ XIF_START, /* (ethX), called on 'ifup', start receiver thread */
++ XIF_STOP, /* (ethX), called on 'ifdown', stop the thread */
++ XIF_READLENGTH, /* (ethX), return size of network data block to read */
++ XIF_READBLOCK, /* (ethX, buffer, size), read block of network data */
++ XIF_WRITEBLOCK, /* (ethX, buffer, size), write block of network data */
++ XIF_GET_MAC, /* (ethX, buffer, size), return MAC HW addr in buffer */
++ XIF_GET_IPHOST, /* (ethX, buffer, size), return IP address of host */
++ XIF_GET_IPATARI, /* (ethX, buffer, size), return IP address of atari */
++ XIF_GET_NETMASK /* (ethX, buffer, size), return IP netmask */
++};
++
++#define ETH(a) (nfEtherID + a)
++
++#endif /* _ARAETHER_NFAPI_H */
+diff -urN linux-m68k/drivers/net/Kconfig linux-schmitz/drivers/net/Kconfig
+--- linux-m68k/drivers/net/Kconfig 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/drivers/net/Kconfig 2006-11-19 21:37:26.000000000 +0100
+@@ -396,6 +396,22 @@
+ ACSI port ("ACSI node"). The driver works (has to work...) with a
+ polled I/O scheme, so it's rather slow :-(
+
++config ATARI_NFETH
++ tristate "Atari NatFeat Ethernet support"
++ depends on NET_ETHERNET && ATARI && NATFEAT
++ help
++ Say Y to include support for the ARAnyM NatFeat network device
++ which will emulate a regular ethernet device while presenting an
++ ethertap device to the host system.
++
++config ATARI_ETHERNEC
++ tristate "Atari EtherNEC Ethernet support"
++ depends on NET_ETHERNET && ATARI && ATARI_ROM_ISA
++ help
++ Say Y to include support for the EtherNEC network adapter for the
++ ROM port. The driver works by polling instead of interrupts, so it
++ is quite slow.
++
+ config SUN3LANCE
+ tristate "Sun3/Sun3x on-board LANCE support"
+ depends on NET_ETHERNET && (SUN3 || SUN3X)
+diff -urN linux-m68k/drivers/net/Makefile linux-schmitz/drivers/net/Makefile
+--- linux-m68k/drivers/net/Makefile 2006-11-19 21:35:30.000000000 +0100
++++ linux-schmitz/drivers/net/Makefile 2006-11-19 21:37:26.000000000 +0100
+@@ -182,6 +182,8 @@
+ obj-$(CONFIG_ATARILANCE) += atarilance.o
+ obj-$(CONFIG_ATARI_BIONET) += atari_bionet.o
+ obj-$(CONFIG_ATARI_PAMSNET) += atari_pamsnet.o
++obj-$(CONFIG_ATARI_NFETH) += atari_nfeth.o
++obj-$(CONFIG_ATARI_ETHERNEC) += atari_ethernec.o 8390.o
+ obj-$(CONFIG_A2065) += a2065.o
+ obj-$(CONFIG_HYDRA) += hydra.o 8390.o
+ obj-$(CONFIG_ARIADNE) += ariadne.o
+diff -urN linux-m68k/drivers/net/Space.c linux-schmitz/drivers/net/Space.c
+--- linux-m68k/drivers/net/Space.c 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/drivers/net/Space.c 2006-11-19 21:37:26.000000000 +0100
+@@ -74,6 +74,8 @@
+ extern struct net_device *seeq8005_probe(int unit);
+ extern struct net_device *smc_init(int unit);
+ extern struct net_device *atarilance_probe(int unit);
++extern struct net_device *atari_ethernec_probe(int unit);
++extern struct net_device *atari_nfeth_probe(struct net_device *);
+ extern struct net_device *sun3lance_probe(int unit);
+ extern struct net_device *sun3_82586_probe(int unit);
+ extern struct net_device *apne_probe(int unit);
+@@ -261,6 +263,12 @@
+ #ifdef CONFIG_ATARILANCE /* Lance-based Atari ethernet boards */
+ {atarilance_probe, 0},
+ #endif
++#ifdef CONFIG_ATARI_NFETH /* ARAnyM tun/tap device */
++ {atari_nfeth_probe, 0},
++#endif
++#ifdef CONFIG_ATARI_ETHERNEC /* NE2000 based ROM port ethernet cards */
++ {atari_ethernec_probe, 0},
++#endif
+ #ifdef CONFIG_SUN3LANCE /* sun3 onboard Lance chip */
+ {sun3lance_probe, 0},
+ #endif
+diff -urN linux-m68k/drivers/net/atari_ethernec.c linux-schmitz/drivers/net/atari_ethernec.c
+--- linux-m68k/drivers/net/atari_ethernec.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/drivers/net/atari_ethernec.c 2006-11-19 21:37:26.000000000 +0100
+@@ -0,0 +1,1065 @@
++/*
++ * atari_ethernec.c: Atari cartridge port ethernet adapter
++ * (C) 2006 Michael Schmitz
++ *
++ * Modified after:
++ */
++
++/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
++/*
++ Written 1992-94 by Donald Becker.
++
++ Copyright 1993 United States Government as represented by the
++ Director, National Security Agency.
++
++ This software may be used and distributed according to the terms
++ of the GNU General Public License, incorporated herein by reference.
++
++ The author may be reached as becker at scyld.com, or C/O
++ Scyld Computing Corporation, 410 Severn Ave., Suite 210, Annapolis MD 21403
++
++ This driver should work with many programmed-I/O 8390-based ethernet
++ boards. Currently it supports the NE1000, NE2000, many clones,
++ and some Cabletron products.
++
++ Changelog:
++
++ Paul Gortmaker : use ENISR_RDC to monitor Tx PIO uploads, made
++ sanity checks and bad clone support optional.
++ Paul Gortmaker : new reset code, reset card after probe at boot.
++ Paul Gortmaker : multiple card support for module users.
++ Paul Gortmaker : Support for PCI ne2k clones, similar to lance.c
++ Paul Gortmaker : Allow users with bad cards to avoid full probe.
++ Paul Gortmaker : PCI probe changes, more PCI cards supported.
++ rjohnson at analogic.com : Changed init order so an interrupt will only
++ occur after memory is allocated for dev->priv. Deallocated memory
++ last in cleanup_modue()
++ Richard Guenther : Added support for ISAPnP cards
++ Paul Gortmaker : Discontinued PCI support - use ne2k-pci.c instead.
++ Hayato Fujiwara : Add m32r support.
++
++*/
++
++/*
++ * From the driver distribution kit by Thomas Redelberger:
++ *
++ * Hardware circuit description (see directory ETHERNEC for schematics)
++ *
++ * As there is no reset line on the CP, a resistor and a capacitor are
++ * used to reset the NE card on power up.
++ *
++ * Reading from the NE card is done by a read cycle on the CP at address
++ * /ROM4 + 512*ISA address as the ISA address lines A0-A4 are connected
++ * to CP A9-A13. /ROM4 going low will start the ISA read cycle, enable
++ * the ISA bus buffers of the NE card and start decoding of the ISA IO
++ * address by the NE card. /ROM4 going high ends the cycle and the
++ * processor latches the data.
++ *
++ * Because the CP is read only writing to the NE card must be done with
++ * the trick to read from addresses that stand for the data. Dummy reads
++ * at /ROM3 base address + data*2 + ISA address*512 effect this. You
++ * might wonder why everything appears to be shifted up one bit. There is
++ * no CP "A0" address line. There are the signals /UDS and /LDS instead
++ * typical for the 68000 family. The original design which generated an
++ * "A0" worked on an ST and an STE but did not on a Falcon.
++ *
++ * The falling edge of /ROM3 enables the CP address lines A1-A8 onto the
++ * data bus and starts the ISA write cycle. The rising edge will end the
++ * ISA write cycle and the NE latches the data. The processor will also
++ * see and just read this same data but that is harmless.
++ * Elmar Hilgart reported that the bus buffer IC shall be an TTL F-type
++ * to keep up with the fast cycles on the Falcon.
++ *
++ * Base addresses:
++ * rom4 EQU $00fa0000 ; ROM4 base address
++ * rom3 EQU $00fb0000 ; ROM3 base address
++ *
++ */
++
++/* Routines for the NatSemi-based designs (NE[12]000). */
++
++static const char version1[] =
++"ne.c:v1.10 9/23/94 Donald Becker (becker at scyld.com)\n";
++static const char version2[] =
++"atari_ethernec.c 11/10/06 Michael Schmitz (schmitz at debian.org)\n";
++
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/wait.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/isapnp.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/jiffies.h>
++
++#include <asm/system.h>
++#include <asm/atarihw.h>
++#include <asm/atariints.h>
++#include <asm/io.h>
++
++#include "8390.h"
++
++#define DRV_NAME "ethernec"
++
++/* Some defines that people can play with if so inclined. */
++
++/* Do we support clones that don't adhere to 14,15 of the SAprom ? */
++#define SUPPORT_NE_BAD_CLONES
++
++/* Do we perform extra sanity checks on stuff ? */
++/* #define NE_SANITY_CHECK */
++
++/* Do we implement the read before write bugfix ? */
++/* #define NE_RW_BUGFIX */
++
++/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
++/* #define PACKETBUF_MEMSIZE 0x40 */
++
++/* A zero-terminated list of I/O addresses to be probed at boot. */
++#ifndef MODULE
++static unsigned int netcard_portlist[] __initdata = {
++ 0x300, 0x280, 0x320, 0x340, 0x360, 0x380, 0
++};
++#endif
++
++static struct isapnp_device_id isapnp_clone_list[] __initdata = {
++ { ISAPNP_CARD_ID('A','X','E',0x2011),
++ ISAPNP_VENDOR('A','X','E'), ISAPNP_FUNCTION(0x2011),
++ (long) "NetGear EA201" },
++ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
++ ISAPNP_VENDOR('E','D','I'), ISAPNP_FUNCTION(0x0216),
++ (long) "NN NE2000" },
++ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
++ ISAPNP_VENDOR('P','N','P'), ISAPNP_FUNCTION(0x80d6),
++ (long) "Generic PNP" },
++ { } /* terminate list */
++};
++
++MODULE_DEVICE_TABLE(isapnp, isapnp_clone_list);
++
++#ifdef SUPPORT_NE_BAD_CLONES
++/* A list of bad clones that we none-the-less recognize. */
++static struct { const char *name8, *name16; unsigned char SAprefix[4];}
++bad_clone_list[] __initdata = {
++ {"DE100", "DE200", {0x00, 0xDE, 0x01,}},
++ {"DE120", "DE220", {0x00, 0x80, 0xc8,}},
++ {"DFI1000", "DFI2000", {'D', 'F', 'I',}}, /* Original, eh? */
++ {"EtherNext UTP8", "EtherNext UTP16", {0x00, 0x00, 0x79}},
++ {"NE1000","NE2000-invalid", {0x00, 0x00, 0xd8}}, /* Ancient real NE1000. */
++ {"NN1000", "NN2000", {0x08, 0x03, 0x08}}, /* Outlaw no-name clone. */
++ {"4-DIM8","4-DIM16", {0x00,0x00,0x4d,}}, /* Outlaw 4-Dimension cards. */
++ {"Con-Intl_8", "Con-Intl_16", {0x00, 0x00, 0x24}}, /* Connect Int'nl */
++ {"ET-100","ET-200", {0x00, 0x45, 0x54}}, /* YANG and YA clone */
++ {"COMPEX","COMPEX16",{0x00,0x80,0x48}}, /* Broken ISA Compex cards */
++ {"E-LAN100", "E-LAN200", {0x00, 0x00, 0x5d}}, /* Broken ne1000 clones */
++ {"PCM-4823", "PCM-4823", {0x00, 0xc0, 0x6c}}, /* Broken Advantech MoBo */
++ {"REALTEK", "RTL8019", {0x00, 0x00, 0xe8}}, /* no-name with Realtek chip */
++#if defined(CONFIG_TOSHIBA_RBTX4927) || defined(CONFIG_TOSHIBA_RBTX4938)
++ {"RBHMA4X00-RTL8019", "RBHMA4X00/RTL8019", {0x00, 0x60, 0x0a}}, /* Toshiba built-in */
++#endif
++ {"LCS-8834", "LCS-8836", {0x04, 0x04, 0x37}}, /* ShinyNet (SET) */
++ {NULL,}
++};
++#endif
++
++/* ---- No user-serviceable parts below ---- */
++
++#define NE_BASE (dev->base_addr)
++#define NE_CMD 0x00
++#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
++#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
++#define NE_IO_EXTENT 0x20
++
++#define NE1SM_START_PG 0x20 /* First page of TX buffer */
++#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
++#define NESM_START_PG 0x40 /* First page of TX buffer */
++#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
++
++#if defined(CONFIG_PLAT_MAPPI)
++# define DCR_VAL 0x4b
++#elif defined(CONFIG_PLAT_OAKS32R) || \
++ defined(CONFIG_TOSHIBA_RBTX4927) || defined(CONFIG_TOSHIBA_RBTX4938) || \
++ defined(CONFIG_ATARI_ETHERNEC)
++# define DCR_VAL 0x48 /* 8-bit mode */
++#else
++# define DCR_VAL 0x49
++#endif
++
++#if defined(CONFIG_ATARI_ETHERNEC)
++# define ETHERNEC_RTL_8019_BASE 0x300
++# define ETHERNEC_RTL_8019_IRQ IRQ_MFP_TIMD
++#endif
++
++static int ne_probe1(struct net_device *dev, int ioaddr);
++static int ne_probe_isapnp(struct net_device *dev);
++
++static int ne_open(struct net_device *dev);
++static int ne_close(struct net_device *dev);
++
++static void ne_reset_8390(struct net_device *dev);
++static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
++ int ring_page);
++static void ne_block_input(struct net_device *dev, int count,
++ struct sk_buff *skb, int ring_offset);
++static void ne_block_output(struct net_device *dev, const int count,
++ const unsigned char *buf, const int start_page);
++
++
++/*
++ * The Atari ROM port has no interrupt line, so we poll the card instead.
++ */
++
++static int use_poll = 0;
++
++/* This is used by cleanup, to prevent the module from being unloaded while
++ * intrpt_routine is still in the task queue
++ */
++static wait_queue_head_t WaitQ;
++
++static struct work_struct tqueue;
++
++static struct {
++ struct work_struct poll_queue;
++ struct timer_list poll_timer;
++ struct net_device *dev;
++} poll_ops;
++
++static struct net_device *poll_dev = NULL;
++
++static void atari_ethernec_int(unsigned long dev_addr)
++{
++ struct net_device *dev = poll_dev;
++
++ if(!dev) {
++ /* If cleanup wants us to die */
++ if (waitqueue_active(&WaitQ))
++ wake_up(&WaitQ); /* Now cleanup_module can return */
++ else
++ /* Put ourselves back in the task queue */
++ schedule_delayed_work(&tqueue, 1);
++ return;
++ }
++
++ if (netif_running(dev)) {
++ ei_interrupt(dev->irq, dev, NULL);
++ }
++
++ /* If cleanup wants us to die */
++ if (waitqueue_active(&WaitQ))
++ wake_up(&WaitQ); /* Now cleanup_module can return */
++ else
++ /* Put ourselves back in the task queue */
++ schedule_delayed_work(&tqueue, 0); /* reduced delay from 1 */
++}
++
++#if defined (ETHERNEC_USE_POLL)
++static void atari_ethernec_poll_handler(unsigned long dev_addr)
++{
++ struct net_device *dev = poll_dev;
++
++ if(!dev || !dev->poll_controller)
++ return;
++
++ if (netif_running(dev))
++ dev->poll_controller(dev);
++
++ schedule_work(&poll_ops.poll_queue);
++ mod_timer(&poll_ops.poll_timer, jiffies + HZ/100);
++}
++#endif
++
++static void atari_ethernec_start_poll(struct net_device *dev)
++{
++ poll_dev = dev;
++
++ init_waitqueue_head(&WaitQ);
++
++ INIT_WORK(&tqueue, (void (*)(void *))atari_ethernec_int, dev);
++ schedule_delayed_work(&tqueue, 1);
++#if defined (ETHERNEC_USE_POLL)
++ if ((!poll_ops.poll_queue.func || poll_ops.poll_queue.func == ei_interrupt)) {
++ if (!poll_ops.poll_queue.func)
++ INIT_WORK(&poll_ops.poll_queue, ei_interrupt, dev);
++
++ init_timer(&poll_ops.poll_timer);
++ poll_ops.poll_timer.function = atari_ethernec_poll_handler;
++ poll_ops.poll_timer.expires = jiffies + HZ / 5;
++ poll_ops.poll_timer.data = (unsigned long ) dev;
++ add_timer(&poll_ops.poll_timer);
++ }
++#endif
++}
++
++static void atari_ethernec_stop_poll(struct net_device *dev)
++{
++ poll_dev = NULL;
++
++ if (dev) {
++ sleep_on(&WaitQ);
++ }
++
++#if defined (ETHERNEC_USE_POLL)
++ if (poll_ops.poll_queue.func == ei_interrupt) {
++ del_timer_sync(&poll_ops.poll_timer);
++ }
++#endif
++}
++
++
++/* Probe for various non-shared-memory ethercards.
++
++ NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
++ buffer memory space. NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of
++ the SAPROM, while other supposed NE2000 clones must be detected by their
++ SA prefix.
++
++ Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
++ mode results in doubled values, which can be detected and compensated for.
++
++ The probe is also responsible for initializing the card and filling
++ in the 'dev' and 'ei_status' structures.
++
++ We use the minimum memory size for some ethercard product lines, iff we can't
++ distinguish models. You can increase the packet buffer size by setting
++ PACKETBUF_MEMSIZE. Reported Cabletron packet buffer locations are:
++ E1010 starts at 0x100 and ends at 0x2000.
++ E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
++ E2010 starts at 0x100 and ends at 0x4000.
++ E2010-x starts at 0x100 and ends at 0xffff. */
++
++static int __init do_ne_probe(struct net_device *dev)
++{
++ unsigned int base_addr = dev->base_addr;
++ int rv;
++#ifndef MODULE
++ int orig_irq = dev->irq;
++#endif
++
++ SET_MODULE_OWNER(dev);
++
++ /* First check any supplied i/o locations. User knows best. <cough> */
++ if (base_addr > 0x1ff) { /* Check a single specified location. */
++ rv =ne_probe1(dev, base_addr);
++ if (!rv && use_poll) {
++ /* Seems we have a valid device here; set up polling routine */
++ poll_dev = dev;
++ atari_ethernec_start_poll(dev);
++ }
++ return rv;
++ } else if (base_addr != 0) /* Don't probe at all. */
++ return -ENXIO;
++
++ /* Then look for any installed ISAPnP clones */
++ if (isapnp_present() && (ne_probe_isapnp(dev) == 0))
++ return 0;
++
++#ifndef MODULE
++ /* Last resort. The semi-risky ISA auto-probe. */
++ for (base_addr = 0; netcard_portlist[base_addr] != 0; base_addr++) {
++ int ioaddr = netcard_portlist[base_addr];
++ dev->irq = orig_irq;
++ rv = ne_probe1(dev, ioaddr);
++ if (rv == 0) {
++ if (use_poll) {
++ poll_dev = dev;
++ atari_ethernec_start_poll(dev);
++ }
++ return 0;
++ }
++ }
++#endif
++
++ return -ENODEV;
++}
++
++#ifndef MODULE
++struct net_device * __init atari_ethernec_probe(int unit)
++{
++ struct net_device *dev = alloc_ei_netdev();
++ int err;
++
++ if (!dev)
++ return ERR_PTR(-ENOMEM);
++
++ sprintf(dev->name, "eth%d", unit);
++ netdev_boot_setup_check(dev);
++
++#ifdef CONFIG_ATARI_ETHERNEC
++ dev->base_addr = ETHERNEC_RTL_8019_BASE;
++ dev->irq = ETHERNEC_RTL_8019_IRQ;
++#endif
++ err = do_ne_probe(dev);
++ if (err)
++ goto out;
++
++ /* Seems we have a valid device here; set up polling routine */
++ return dev;
++out:
++ free_netdev(dev);
++ return ERR_PTR(err);
++}
++#endif
++
++static int __init ne_probe_isapnp(struct net_device *dev)
++{
++ int i;
++
++ for (i = 0; isapnp_clone_list[i].vendor != 0; i++) {
++ struct pnp_dev *idev = NULL;
++
++ while ((idev = pnp_find_dev(NULL,
++ isapnp_clone_list[i].vendor,
++ isapnp_clone_list[i].function,
++ idev))) {
++ /* Avoid already found cards from previous calls */
++ if (pnp_device_attach(idev) < 0)
++ continue;
++ if (pnp_activate_dev(idev) < 0) {
++ pnp_device_detach(idev);
++ continue;
++ }
++ /* if no io and irq, search for next */
++ if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) {
++ pnp_device_detach(idev);
++ continue;
++ }
++ /* found it */
++ dev->base_addr = pnp_port_start(idev, 0);
++ dev->irq = pnp_irq(idev, 0);
++ printk(KERN_INFO "atari_ethernec.c: ISAPnP reports %s at i/o %#lx, irq %d.\n",
++ (char *) isapnp_clone_list[i].driver_data,
++ dev->base_addr, dev->irq);
++ if (ne_probe1(dev, dev->base_addr) != 0) { /* Shouldn't happen. */
++ printk(KERN_ERR "atari_ethernec.c: Probe of ISAPnP card at %#lx failed.\n", dev->base_addr);
++ pnp_device_detach(idev);
++ return -ENXIO;
++ }
++ ei_status.priv = (unsigned long)idev;
++ break;
++ }
++ if (!idev)
++ continue;
++ return 0;
++ }
++
++ return -ENODEV;
++}
++
++static int __init ne_probe1(struct net_device *dev, int ioaddr)
++{
++ int i;
++ unsigned char SA_prom[32];
++ int wordlength = 2;
++ const char *name = NULL;
++ int start_page, stop_page;
++ int neX000, ctron, copam, bad_card;
++ int reg0, ret;
++ static unsigned version_printed;
++
++ if (!request_region(ioaddr, NE_IO_EXTENT, DRV_NAME))
++ return -EBUSY;
++
++ reg0 = inb_p(ioaddr);
++ if (reg0 == 0xFF) {
++ ret = -ENODEV;
++ goto err_out;
++ }
++
++ /* Do a preliminary verification that we have a 8390. */
++ {
++ int regd;
++ outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
++ regd = inb_p(ioaddr + 0x0d);
++ outb_p(0xff, ioaddr + 0x0d);
++ outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
++ inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
++ if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
++ outb_p(reg0, ioaddr);
++ outb_p(regd, ioaddr + 0x0d); /* Restore the old values. */
++ ret = -ENODEV;
++ goto err_out;
++ }
++ }
++
++ if (ei_debug && version_printed++ == 0)
++ printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2);
++
++ /* A user with a poor card that fails to ack the reset, or that
++ does not have a valid 0x57,0x57 signature can still use this
++ without having to recompile. Specifying an i/o address along
++ with an otherwise unused dev->mem_end value of "0xBAD" will
++ cause the driver to skip these parts of the probe. */
++
++ bad_card = ((dev->base_addr != 0) && (dev->mem_end == 0xbad));
++
++ /* Reset card. Who knows what dain-bramaged state it was left in. */
++
++ {
++ unsigned long reset_start_time = jiffies;
++
++ /* DON'T change these to inb_p/outb_p or reset will fail on clones. */
++ outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
++
++ while ((inb_p(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
++ if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
++ if (bad_card) {
++ printk(" (warning: no reset ack)");
++ break;
++ } else {
++ // MSch: ARAnyM exits here
++ printk(" not found (no reset ack).\n");
++ ret = -ENODEV;
++ goto err_out;
++ }
++ }
++
++ outb_p(0xff, ioaddr + EN0_ISR); /* Ack all intr. */
++ }
++
++ /* Read the 16 bytes of station address PROM.
++ We must first initialize registers, similar to NS8390_init(eifdev, 0).
++ We can't reliably read the SAPROM address without this.
++ (I learned the hard way!). */
++ {
++ struct {unsigned char value, offset; } program_seq[] =
++ {
++ {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
++ {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */
++ {0x00, EN0_RCNTLO}, /* Clear the count regs. */
++ {0x00, EN0_RCNTHI},
++ {0x00, EN0_IMR}, /* Mask completion irq. */
++ {0xFF, EN0_ISR},
++ {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
++ {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
++ {32, EN0_RCNTLO},
++ {0x00, EN0_RCNTHI},
++ {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
++ {0x00, EN0_RSARHI},
++ {E8390_RREAD+E8390_START, E8390_CMD},
++ };
++
++ for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
++ outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
++
++ }
++ for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
++ SA_prom[i] = inb(ioaddr + NE_DATAPORT);
++ SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
++ if (SA_prom[i] != SA_prom[i+1])
++ wordlength = 1;
++ }
++
++ if (wordlength == 2)
++ {
++ for (i = 0; i < 16; i++)
++ SA_prom[i] = SA_prom[i+i];
++ /* We must set the 8390 for word mode. */
++ outb_p(DCR_VAL, ioaddr + EN0_DCFG);
++ start_page = NESM_START_PG;
++
++ /*
++ * Realtek RTL8019AS datasheet says that the PSTOP register
++ * shouldn't exceed 0x60 in 8-bit mode.
++ * This chip can be identified by reading the signature from
++ * the remote byte count registers (otherwise write-only)...
++ */
++ if ((DCR_VAL & 0x01) == 0 && /* 8-bit mode */
++ inb(ioaddr + EN0_RCNTLO) == 0x50 &&
++ inb(ioaddr + EN0_RCNTHI) == 0x70)
++ stop_page = 0x60;
++ else
++ stop_page = NESM_STOP_PG;
++ } else {
++ start_page = NE1SM_START_PG;
++ stop_page = NE1SM_STOP_PG;
++ }
++
++#if defined(CONFIG_PLAT_MAPPI) || defined(CONFIG_PLAT_OAKS32R)
++ neX000 = ((SA_prom[14] == 0x57 && SA_prom[15] == 0x57)
++ || (SA_prom[14] == 0x42 && SA_prom[15] == 0x42));
++#else
++ neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
++#endif
++ ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
++ copam = (SA_prom[14] == 0x49 && SA_prom[15] == 0x00);
++
++ /* Set up the rest of the parameters. */
++ if (neX000 || bad_card || copam) {
++ name = (wordlength == 2) ? "NE2000" : "NE1000";
++ }
++ else if (ctron)
++ {
++ name = (wordlength == 2) ? "Ctron-8" : "Ctron-16";
++ start_page = 0x01;
++ stop_page = (wordlength == 2) ? 0x40 : 0x20;
++ }
++ else
++ {
++#ifdef SUPPORT_NE_BAD_CLONES
++ /* Ack! Well, there might be a *bad* NE*000 clone there.
++ Check for total bogus addresses. */
++ for (i = 0; bad_clone_list[i].name8; i++)
++ {
++ if (SA_prom[0] == bad_clone_list[i].SAprefix[0] &&
++ SA_prom[1] == bad_clone_list[i].SAprefix[1] &&
++ SA_prom[2] == bad_clone_list[i].SAprefix[2])
++ {
++ if (wordlength == 2)
++ {
++ name = bad_clone_list[i].name16;
++ } else {
++ name = bad_clone_list[i].name8;
++ }
++ break;
++ }
++ }
++ if (bad_clone_list[i].name8 == NULL)
++ {
++ printk(" not found (invalid signature %2.2x %2.2x).\n",
++ SA_prom[14], SA_prom[15]);
++ ret = -ENXIO;
++ goto err_out;
++ }
++#else
++ printk(" not found.\n");
++ ret = -ENXIO;
++ goto err_out;
++#endif
++ }
++
++ if (dev->irq < 2)
++ {
++ unsigned long cookie = probe_irq_on();
++ outb_p(0x50, ioaddr + EN0_IMR); /* Enable one interrupt. */
++ outb_p(0x00, ioaddr + EN0_RCNTLO);
++ outb_p(0x00, ioaddr + EN0_RCNTHI);
++ outb_p(E8390_RREAD+E8390_START, ioaddr); /* Trigger it... */
++ mdelay(10); /* wait 10ms for interrupt to propagate */
++ outb_p(0x00, ioaddr + EN0_IMR); /* Mask it again. */
++ dev->irq = probe_irq_off(cookie);
++ if (ei_debug > 2)
++ printk(" autoirq is %d\n", dev->irq);
++ } else if (dev->irq == 2)
++ /* Fixup for users that don't know that IRQ 2 is really IRQ 9,
++ or don't know which one to set. */
++ dev->irq = 9;
++
++ /*
++ * use timer based polling!
++ */
++ if (! dev->irq) {
++ printk(" failed to detect IRQ line. Assuming irq %d\n", ETHERNEC_RTL_8019_IRQ);
++ dev->irq = ETHERNEC_RTL_8019_IRQ;
++ /* timer routine set up in atari_ethernec_probe() */
++ if (dev->irq == IRQ_MFP_TIMD) {
++ /* set Timer D data Register */
++ mfp.tim_dt_d = 123; /* 200 Hz */
++ /* start timer D, div = 1:100 */
++ mfp.tim_ct_cd = (mfp.tim_ct_cd & 0xf0) | 0x6;
++ }
++ ret = request_irq(dev->irq, ei_interrupt, 0, name, dev);
++ if (ret) {
++ printk (" unable to get IRQ %d (errno=%d), polling instead.\n", dev->irq, ret);
++ use_poll = 1;
++ }
++ } else {
++
++ /* Snarf the interrupt now. There's no point in waiting since we cannot
++ share and the board will usually be enabled. */
++ ret = request_irq(dev->irq, ei_interrupt, 0, name, dev);
++ if (ret) {
++ printk (" unable to get IRQ %d (errno=%d).\n", dev->irq, ret);
++ goto err_out;
++ }
++ }
++ dev->base_addr = ioaddr;
++
++#ifdef CONFIG_PLAT_MAPPI
++ outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP,
++ ioaddr + E8390_CMD); /* 0x61 */
++ for (i = 0 ; i < ETHER_ADDR_LEN ; i++) {
++ dev->dev_addr[i] = SA_prom[i]
++ = inb_p(ioaddr + EN1_PHYS_SHIFT(i));
++ printk(" %2.2x", SA_prom[i]);
++ }
++#else
++ for(i = 0; i < ETHER_ADDR_LEN; i++) {
++ printk(" %2.2x", SA_prom[i]);
++ dev->dev_addr[i] = SA_prom[i];
++ }
++#endif
++
++ printk("\n%s: %s found at %#x, using IRQ %d.\n",
++ dev->name, name, ioaddr, dev->irq);
++
++ ei_status.name = name;
++ ei_status.tx_start_page = start_page;
++ ei_status.stop_page = stop_page;
++
++ /* Use 16-bit mode only if this wasn't overridden by DCR_VAL */
++ ei_status.word16 = (wordlength == 2 && (DCR_VAL & 0x01));
++
++ ei_status.rx_start_page = start_page + TX_PAGES;
++#ifdef PACKETBUF_MEMSIZE
++ /* Allow the packet buffer size to be overridden by know-it-alls. */
++ ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
++#endif
++
++ ei_status.reset_8390 = &ne_reset_8390;
++ ei_status.block_input = &ne_block_input;
++ ei_status.block_output = &ne_block_output;
++ ei_status.get_8390_hdr = &ne_get_8390_hdr;
++ ei_status.priv = 0;
++ dev->open = &ne_open;
++ dev->stop = &ne_close;
++#ifdef CONFIG_NET_POLL_CONTROLLER
++ dev->poll_controller = ei_poll;
++#endif
++ NS8390_init(dev, 0);
++
++ ret = register_netdev(dev);
++ if (ret)
++ goto out_irq;
++ return 0;
++
++out_irq:
++ free_irq(dev->irq, dev);
++err_out:
++ release_region(ioaddr, NE_IO_EXTENT);
++ return ret;
++}
++
++static int ne_open(struct net_device *dev)
++{
++ ei_open(dev);
++ return 0;
++}
++
++static int ne_close(struct net_device *dev)
++{
++ if (ei_debug > 1)
++ printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name);
++ ei_close(dev);
++ return 0;
++}
++
++/* Hard reset the card. This used to pause for the same period that a
++ 8390 reset command required, but that shouldn't be necessary. */
++
++static void ne_reset_8390(struct net_device *dev)
++{
++ unsigned long reset_start_time = jiffies;
++
++ if (ei_debug > 1)
++ printk(KERN_DEBUG "resetting the 8390 t=%ld...", jiffies);
++
++ /* DON'T change these to inb_p/outb_p or reset will fail on clones. */
++ outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
++
++ ei_status.txing = 0;
++ ei_status.dmaing = 0;
++
++ /* This check _should_not_ be necessary, omit eventually. */
++ while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
++ if (time_after(jiffies, reset_start_time + 2*HZ/100)) {
++ printk(KERN_WARNING "%s: ne_reset_8390() did not complete.\n", dev->name);
++ break;
++ }
++ outb_p(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */
++}
++
++/* Grab the 8390 specific header. Similar to the block_input routine, but
++ we don't need to be concerned with ring wrap as the header will be at
++ the start of a page, so we optimize accordingly. */
++
++static void ne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
++{
++ int nic_base = dev->base_addr;
++
++ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
++
++ if (ei_status.dmaing)
++ {
++ printk(KERN_EMERG "%s: DMAing conflict in ne_get_8390_hdr "
++ "[DMAstat:%d][irqlock:%d].\n",
++ dev->name, ei_status.dmaing, ei_status.irqlock);
++ return;
++ }
++
++ ei_status.dmaing |= 0x01;
++ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
++ outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
++ outb_p(0, nic_base + EN0_RCNTHI);
++ outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */
++ outb_p(ring_page, nic_base + EN0_RSARHI);
++ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
++
++ if (ei_status.word16)
++ insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
++ else
++ insb(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));
++
++ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
++ ei_status.dmaing &= ~0x01;
++
++ le16_to_cpus(&hdr->count);
++}
++
++/* Block input and output, similar to the Crynwr packet driver. If you
++ are porting to a new ethercard, look at the packet driver source for hints.
++ The NEx000 doesn't share the on-board packet memory -- you have to put
++ the packet out through the "remote DMA" dataport using outb. */
++
++static void ne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
++{
++#ifdef NE_SANITY_CHECK
++ int xfer_count = count;
++#endif
++ int nic_base = dev->base_addr;
++ char *buf = skb->data;
++
++ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
++ if (ei_status.dmaing)
++ {
++ printk(KERN_EMERG "%s: DMAing conflict in ne_block_input "
++ "[DMAstat:%d][irqlock:%d].\n",
++ dev->name, ei_status.dmaing, ei_status.irqlock);
++ return;
++ }
++ ei_status.dmaing |= 0x01;
++ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
++ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
++ outb_p(count >> 8, nic_base + EN0_RCNTHI);
++ outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
++ outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
++ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
++ if (ei_status.word16)
++ {
++ insw(NE_BASE + NE_DATAPORT,buf,count>>1);
++ if (count & 0x01)
++ {
++ buf[count-1] = inb(NE_BASE + NE_DATAPORT);
++#ifdef NE_SANITY_CHECK
++ xfer_count++;
++#endif
++ }
++ } else {
++ insb(NE_BASE + NE_DATAPORT, buf, count);
++ }
++
++#ifdef NE_SANITY_CHECK
++ /* This was for the ALPHA version only, but enough people have
++ been encountering problems so it is still here. If you see
++ this message you either 1) have a slightly incompatible clone
++ or 2) have noise/speed problems with your bus. */
++
++ if (ei_debug > 1)
++ {
++ /* DMA termination address check... */
++ int addr, tries = 20;
++ do {
++ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
++ -- it's broken for Rx on some cards! */
++ int high = inb_p(nic_base + EN0_RSARHI);
++ int low = inb_p(nic_base + EN0_RSARLO);
++ addr = (high << 8) + low;
++ if (((ring_offset + xfer_count) & 0xff) == low)
++ break;
++ } while (--tries > 0);
++ if (tries <= 0)
++ printk(KERN_WARNING "%s: RX transfer address mismatch,"
++ "%#4.4x (expected) vs. %#4.4x (actual).\n",
++ dev->name, ring_offset + xfer_count, addr);
++ }
++#endif
++ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
++ ei_status.dmaing &= ~0x01;
++}
++
++static void ne_block_output(struct net_device *dev, int count,
++ const unsigned char *buf, const int start_page)
++{
++ int nic_base = NE_BASE;
++ unsigned long dma_start;
++#ifdef NE_SANITY_CHECK
++ int retries = 0;
++#endif
++
++ /* Round the count up for word writes. Do we need to do this?
++ What effect will an odd byte count have on the 8390?
++ I should check someday. */
++
++ if (ei_status.word16 && (count & 0x01))
++ count++;
++
++ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
++ if (ei_status.dmaing)
++ {
++ printk(KERN_EMERG "%s: DMAing conflict in ne_block_output."
++ "[DMAstat:%d][irqlock:%d]\n",
++ dev->name, ei_status.dmaing, ei_status.irqlock);
++ return;
++ }
++ ei_status.dmaing |= 0x01;
++ /* We should already be in page 0, but to be safe... */
++ outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
++
++#ifdef NE_SANITY_CHECK
++retry:
++#endif
++
++#ifdef NE8390_RW_BUGFIX
++ /* Handle the read-before-write bug the same way as the
++ Crynwr packet driver -- the NatSemi method doesn't work.
++ Actually this doesn't always work either, but if you have
++ problems with your NEx000 this is better than nothing! */
++
++ outb_p(0x42, nic_base + EN0_RCNTLO);
++ outb_p(0x00, nic_base + EN0_RCNTHI);
++ outb_p(0x42, nic_base + EN0_RSARLO);
++ outb_p(0x00, nic_base + EN0_RSARHI);
++ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
++ /* Make certain that the dummy read has occurred. */
++ udelay(6);
++#endif
++
++ outb_p(ENISR_RDC, nic_base + EN0_ISR);
++
++ /* Now the normal output. */
++ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
++ outb_p(count >> 8, nic_base + EN0_RCNTHI);
++ outb_p(0x00, nic_base + EN0_RSARLO);
++ outb_p(start_page, nic_base + EN0_RSARHI);
++
++ outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
++ if (ei_status.word16) {
++ outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
++ } else {
++ outsb(NE_BASE + NE_DATAPORT, buf, count);
++ }
++
++ dma_start = jiffies;
++
++#ifdef NE_SANITY_CHECK
++ /* This was for the ALPHA version only, but enough people have
++ been encountering problems so it is still here. */
++
++ if (ei_debug > 1)
++ {
++ /* DMA termination address check... */
++ int addr, tries = 20;
++ do {
++ int high = inb_p(nic_base + EN0_RSARHI);
++ int low = inb_p(nic_base + EN0_RSARLO);
++ addr = (high << 8) + low;
++ if ((start_page << 8) + count == addr)
++ break;
++ } while (--tries > 0);
++
++ if (tries <= 0)
++ {
++ printk(KERN_WARNING "%s: Tx packet transfer address mismatch,"
++ "%#4.4x (expected) vs. %#4.4x (actual).\n",
++ dev->name, (start_page << 8) + count, addr);
++ if (retries++ == 0)
++ goto retry;
++ }
++ }
++#endif
++
++ while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
++ if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */
++ printk(KERN_WARNING "%s: timeout waiting for Tx RDC.\n", dev->name);
++ ne_reset_8390(dev);
++ NS8390_init(dev,1);
++ break;
++ }
++
++ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
++ ei_status.dmaing &= ~0x01;
++ return;
++}
++
++
++#ifdef MODULE
++#define MAX_NE_CARDS 4 /* Max number of NE cards per module */
++static struct net_device *dev_ne[MAX_NE_CARDS];
++static int io[MAX_NE_CARDS];
++static int irq[MAX_NE_CARDS];
++static int bad[MAX_NE_CARDS]; /* 0xbad = bad sig or no reset ack */
++
++module_param_array(io, int, NULL, 0);
++module_param_array(irq, int, NULL, 0);
++module_param_array(bad, int, NULL, 0);
++module_param(use_poll, int, 0);
++MODULE_PARM_DESC(io, "I/O base address(es),required");
++MODULE_PARM_DESC(irq, "IRQ number(s)");
++MODULE_PARM_DESC(bad, "Accept card(s) with bad signatures");
++MODULE_PARM_DESC(use_poll, "Use timer interrupt to poll driver");
++MODULE_DESCRIPTION("NE1000/NE2000 ISA/PnP Ethernet driver");
++MODULE_LICENSE("GPL");
++
++/* This is set up so that no ISA autoprobe takes place. We can't guarantee
++that the ne2k probe is the last 8390 based probe to take place (as it
++is at boot) and so the probe will get confused by any other 8390 cards.
++ISA device autoprobes on a running machine are not recommended anyway. */
++
++int __init init_module(void)
++{
++ int this_dev, found = 0;
++
++ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
++ struct net_device *dev = alloc_ei_netdev();
++ if (!dev)
++ break;
++ dev->irq = irq[this_dev];
++ dev->mem_end = bad[this_dev];
++ dev->base_addr = io[this_dev];
++ if (do_ne_probe(dev) == 0) {
++ dev_ne[found++] = dev;
++ continue;
++ }
++ free_netdev(dev);
++ if (found)
++ break;
++ if (io[this_dev] != 0)
++ printk(KERN_WARNING "atari_ethernec.c: No NE*000 card found at i/o = %#x\n", io[this_dev]);
++ else
++ printk(KERN_NOTICE "atari_ethernec.c: You must supply \"io=0xNNN\" value(s) for ISA cards.\n");
++ return -ENXIO;
++ }
++ if (found)
++ return 0;
++ return -ENODEV;
++}
++
++static void cleanup_card(struct net_device *dev)
++{
++ struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv;
++ if (idev)
++ pnp_device_detach(idev);
++ free_irq(dev->irq, dev);
++ release_region(dev->base_addr, NE_IO_EXTENT);
++}
++
++void cleanup_module(void)
++{
++ int this_dev;
++
++ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
++ struct net_device *dev = dev_ne[this_dev];
++ if (dev) {
++ if (use_poll)
++ atari_ethernec_stop_poll(dev);
++ unregister_netdev(dev);
++ cleanup_card(dev);
++ free_netdev(dev);
++ }
++ }
++}
++#endif /* MODULE */
+diff -urN linux-m68k/drivers/net/atari_nfeth.c linux-schmitz/drivers/net/atari_nfeth.c
+--- linux-m68k/drivers/net/atari_nfeth.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/drivers/net/atari_nfeth.c 2006-11-19 21:37:26.000000000 +0100
+@@ -0,0 +1,327 @@
++/*
++ * atari_nfeth.c - ARAnyM ethernet card driver for GNU/Linux
++ *
++ * Copyright (c) 2005 Milan Jurik, Petr Stehlik of ARAnyM dev team
++ *
++ * Based on ARAnyM driver for FreeMiNT written by Standa Opichal
++ *
++ * This software may be used and distributed according to the terms of
++ * the GNU General Public License (GPL), incorporated herein by reference.
++ */
++
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/module.h>
++#include <asm/atariints.h>
++
++#include "../../arch/m68k/atari/natfeat.h"
++
++#define DRV_NAME "atari_nfeth"
++#define DRV_VERSION "0.3"
++#define DRV_RELDATE "10/12/2005"
++
++/* These identify the driver base version and may not be removed. */
++static char version[] __devinitdata =
++KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " S.Opichal, M.Jurik, P.Stehlik\n"
++KERN_INFO " http://aranym.atari.org/\n";
++
++MODULE_AUTHOR("Milan Jurik");
++MODULE_DESCRIPTION("Atari NFeth driver");
++MODULE_LICENSE("GPL");
++/*
++MODULE_PARM(atari_nfeth_debug, "i");
++MODULE_PARM_DESC(atari_nfeth_debug, "atari_nfeth_debug level (1-2)");
++*/
++
++#undef DEBUG
++
++struct atari_nfeth_private {
++ int ethX;
++ struct net_device_stats stats;
++ spinlock_t lock;
++};
++
++static inline int getEthX(struct net_device *dev)
++{
++ return ((struct atari_nfeth_private *)netdev_priv(dev))->ethX;
++}
++
++int atari_nfeth_open(struct net_device *dev);
++int atari_nfeth_stop(struct net_device *dev);
++irqreturn_t atari_nfeth_interrupt(int irq, void *dev_id, struct pt_regs *fp);
++int atari_nfeth_xmit(struct sk_buff *skb, struct net_device *dev);
++
++int atari_nfeth_open(struct net_device *dev)
++{
++ nf_ethernet_xif_start(getEthX(dev));
++
++ /* Set IRQ */
++ dev->irq = nf_ethernet_get_irq();
++ if (request_irq(dev->irq, atari_nfeth_interrupt, IRQ_TYPE_PRIO, dev->name, dev)) {
++ printk( DRV_NAME ": request for irq %d failed\n", dev->irq);
++ return( 0 );
++ }
++
++ /* Clean statistics */
++ memset(&(((struct atari_nfeth_private *)netdev_priv(dev))->stats), 0, sizeof(((struct atari_nfeth_private *)(dev->priv))->stats));
++
++ spin_lock_init(&(((struct atari_nfeth_private *)netdev_priv(dev))->lock));
++
++#ifdef DEBUG
++ printk( DRV_NAME ": open");
++#endif
++
++ /* Ready for data */
++ netif_start_queue(dev);
++
++ return 0;
++}
++
++int atari_nfeth_stop(struct net_device *dev)
++{
++ /* No more data */
++ netif_stop_queue(dev);
++
++ /* Release IRQ */
++ free_irq(dev->irq, dev);
++
++ nf_ethernet_xif_stop(getEthX(dev));
++
++ return 0;
++}
++
++/*
++ * Read a packet out of the adapter and pass it to the upper layers
++ */
++static irqreturn_t inline recv_packet (struct net_device *dev)
++{
++ int handled = 0;
++ unsigned short pktlen;
++ struct sk_buff *skb;
++ struct atari_nfeth_private *anp = (struct atari_nfeth_private *)netdev_priv(dev);
++
++ if (dev == NULL) {
++ printk(DRV_NAME " recv_packet(): interrupt for unknown device.\n");
++ return IRQ_NONE;
++ }
++
++ /* read packet length (excluding 32 bit crc) */
++ pktlen = nf_ethernet_read_packet_len(getEthX(dev));
++
++#ifdef DEBUG
++ printk(DRV_NAME ": recv_packet: %i", pktlen);
++#endif
++
++ //if (pktlen < 32)
++ if (!pktlen)
++ {
++#ifdef DEBUG
++ printk(DRV_NAME ": recv_packet: pktlen == 0");
++#endif
++ anp->stats.rx_errors++;
++ return IRQ_RETVAL(handled);
++ }
++
++ skb = dev_alloc_skb(pktlen + 2);
++ if (skb == NULL)
++ {
++#ifdef DEBUG
++ printk(DRV_NAME ": recv_packet: out of mem (buf_alloc failed)");
++#endif
++ anp->stats.rx_dropped++;
++ return IRQ_RETVAL(handled);
++ }
++
++ skb->dev = dev;
++ skb_reserve( skb, 2 ); /* 16 Byte align */
++ skb_put( skb, pktlen ); /* make room */
++ nf_ethernet_read_block(getEthX(dev), skb->data, pktlen);
++
++ skb->protocol = eth_type_trans(skb, dev);
++ netif_rx(skb);
++ dev->last_rx = jiffies;
++ anp->stats.rx_packets++;
++ anp->stats.rx_bytes += pktlen;
++
++ /* and enqueue packet */
++ handled = 1;
++ return IRQ_RETVAL(handled);
++}
++
++irqreturn_t atari_nfeth_interrupt(int irq, void *dev_id, struct pt_regs *fp)
++{
++ struct net_device *dev = dev_id;
++ struct atari_nfeth_private *anp = (struct atari_nfeth_private *)netdev_priv(dev);
++ int this_dev_irq_bit;
++ int irq_for_eth_bitmask;
++ if (dev == NULL) {
++#ifdef DEBUG
++ printk(DRV_NAME " atari_nfeth_interrupt(): interrupt for unknown device.\n");
++#endif
++ return IRQ_NONE;
++ }
++ spin_lock(&anp->lock);
++ irq_for_eth_bitmask = nf_ethernet_interrupt(0);
++ this_dev_irq_bit = 1 << (anp->ethX);
++ if (this_dev_irq_bit & irq_for_eth_bitmask) {
++ recv_packet(dev);
++ nf_ethernet_interrupt(this_dev_irq_bit);
++ }
++#ifdef DEBUG
++ else {
++ printk(DRV_NAME " atari_nfeth_interrupt(%d): not for me\n", anp->ethX);
++ }
++#endif
++ spin_unlock(&anp->lock);
++}
++
++int atari_nfeth_xmit(struct sk_buff *skb, struct net_device *dev)
++{
++ int len;
++ char *data, shortpkt[ETH_ZLEN];
++ struct atari_nfeth_private *anp = netdev_priv(dev);
++
++ data = skb->data;
++ len = skb->len;
++ if (len < ETH_ZLEN) {
++ memset(shortpkt, 0, ETH_ZLEN);
++ memcpy(shortpkt, data, len);
++ data = shortpkt;
++ len = ETH_ZLEN;
++ }
++
++ dev->trans_start = jiffies;
++
++#ifdef DEBUG
++ printk( DRV_NAME ": send %d bytes", len);
++#endif
++ nf_ethernet_write_block(getEthX(dev), data, len);
++
++ anp->stats.tx_packets++;
++ anp->stats.tx_bytes += len;
++
++ dev_kfree_skb(skb);
++ return 0;
++}
++
++static void atari_nfeth_tx_timeout(struct net_device *dev)
++{
++ struct atari_nfeth_private *anp = netdev_priv(dev);
++ anp->stats.tx_errors++;
++ netif_wake_queue(dev);
++}
++
++static struct net_device_stats *atari_nfeth_get_stats(struct net_device *dev)
++{
++ struct atari_nfeth_private *anp = netdev_priv(dev);
++ return &(anp->stats);
++}
++
++// probe1() - HW detection
++// probe() - set module owner, found == 1, probe1()
++// init() - probe()
++
++static int __init atari_nfeth_probe1(struct net_device *dev, int ethX)
++{
++ static int did_version = 0;
++ static int did_notinstall = 0;
++ char errmsg[60];
++
++ if ( ! nf_ethernet_check_version(errmsg, sizeof(errmsg)-1) ) {
++ if (did_notinstall++ == 0)
++ printk (DRV_NAME " not installed - %s\n", errmsg);
++ return -ENODEV;
++ }
++
++ /* Get MAC address */
++ if (! nf_ethernet_get_hw_addr(ethX, (unsigned char *)&(dev->dev_addr), ETH_ALEN)) {
++#ifdef DEBUG
++ printk(DRV_NAME " eth%d not installed - not defined\n", ethX);
++#endif
++ return -ENODEV;
++ }
++
++ ether_setup(dev);
++
++ dev->open = &atari_nfeth_open;
++ dev->stop = &atari_nfeth_stop;
++ dev->hard_start_xmit = &atari_nfeth_xmit;
++ dev->tx_timeout = &atari_nfeth_tx_timeout;
++ dev->get_stats = &atari_nfeth_get_stats;
++ dev->flags |= NETIF_F_NO_CSUM;
++
++ //if ((dev->priv = kmalloc(sizeof(struct atari_nfeth_private), GFP_KERNEL)) == NULL)
++ // return -ENOMEM;
++ ((struct atari_nfeth_private *)(dev->priv))->ethX = ethX; /* index of NF NIC */
++
++ if (did_version++ == 0)
++ printk(version);
++
++ return 0;
++}
++
++struct net_device * __init atari_nfeth_probe(int unit)
++{
++ int err;
++ static int found = 0;
++ struct net_device *dev;
++
++ dev = alloc_etherdev(sizeof(struct atari_nfeth_private));
++ if (!dev)
++ return ERR_PTR(-ENOMEM);
++ if (unit >= 0) {
++ sprintf(dev->name, "eth%d", unit);
++ netdev_boot_setup_check(dev);
++ }
++ SET_MODULE_OWNER(dev);
++
++ if (!atari_nfeth_probe1(dev, found++)) {
++ err = register_netdev(dev);
++ if (err == -EIO) {
++ printk(DRV_NAME ": NatFeat Ethernet not found. Module not loaded.\n");
++ }
++ if (!err)
++ return dev;
++ }
++
++ return ERR_PTR(-ENODEV);
++}
++
++#ifdef MODULE
++static struct net_device * atari_nfeth_dev;
++
++int atari_nfeth_init(void)
++{
++ // int err;
++
++ if (IS_ERR(atari_nfeth_dev = atari_nfeth_probe(0))) {
++ return PTR_ERR((atarilance_dev);
++ }
++
++ // ? atari_nfeth_dev.init = atari_nfeth_probe;
++#if 0
++ if ((err = register_netdev(&atari_nfeth_dev))) {
++ if (err == -EIO) {
++ printk(DRV_NAME ": NatFeat Ethernet not found. Module not loaded.\n");
++ }
++ return err;
++ }
++#endif
++
++ return 0;
++}
++
++void atari_nfeth_cleanup(void)
++{
++ unregister_netdev(&atari_nfeth_dev);
++ free_netdev(atari_nfeth_dev); // ?
++}
++
++module_init(atari_nfeth_init);
++module_exit(atari_nfeth_cleanup);
++
++#endif /* MODULE */
++
++/*
++vim:ts=4:sw=4:
++*/
+diff -urN linux-m68k/include/asm-m68k/raw_io.h linux-schmitz/include/asm-m68k/raw_io.h
+--- linux-m68k/include/asm-m68k/raw_io.h 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/include/asm-m68k/raw_io.h 2006-11-19 21:37:27.000000000 +0100
+@@ -54,6 +54,46 @@
+ #define raw_outw(val,port) out_be16((port),(val))
+ #define raw_outl(val,port) out_be32((port),(val))
+
++/*
++ * Atari ROM port (cartridge port) ISA adapter, used for the EtherNEC NE2000
++ * network card driver.
++ * The ISA adapter connects address lines A9-A13 to ISA address lines A0-A4,
++ * and hardwires the rest of the ISA addresses for a base address of 0x300.
++ *
++ * Data lines D8-D15 are connected to ISA data lines D0-D7 for reading.
++ * For writes, address lines A1-A8 are latched to ISA data lines D0-D7
++ * (meaning the bit pattern on A1-A8 can be read back as byte).
++ *
++ * Reads and writes are byte only.
++ */
++
++#if defined(CONFIG_ATARI_ROM_ISA)
++#define rom_in_8(addr) \
++ ({ u16 __v = (*(__force volatile u16 *) (addr)); __v >>= 8; __v; })
++#define rom_in_be16(addr) \
++ ({ u16 __v = (*(__force volatile u16 *) (addr)); __v >>= 8; __v; })
++#define rom_in_be32(addr) \
++ ({ u32 __v = (*(__force volatile u32 *) (addr)); __v >>= 8; __v; })
++#define rom_in_le16(addr) \
++ ({ u16 __v = le16_to_cpu(*(__force volatile u16 *) (addr)); __v >>= 8; __v; })
++#define rom_in_le32(addr) \
++ ({ u32 __v = le32_to_cpu(*(__force volatile u32 *) (addr)); __v >>= 8; __v; })
++
++#define rom_out_8(addr,b) ({u8 __w, __v = (b); __w = ((*(__force volatile u8 *) ((addr) + 0x10000 + (__v<<1)))); })
++#define rom_out_be16(addr,w) ({u16 __w, __v = (w); __w = ((*(__force volatile u16 *) ((addr) + 0x10000 + (__v<<1)))); })
++#define rom_out_be32(addr,l) ({u32 __w, __v = (l); __w = ((*(__force volatile u32 *) ((addr) + 0x10000 + (__v<<1)))); })
++#define rom_out_le16(addr,w) ({u16 __w, __v = cpu_to_le16(w); __w = ((*(__force volatile u16 *) ((addr) + 0x10000 + (__v<<1)))); })
++#define rom_out_le32(addr,l) ({u32 __w, __v = cpu_to_le32(l); __w = ((*(__force volatile u32 *) ((addr) + 0x10000 + (__v<<1)))); })
++
++#define raw_rom_inb rom_in_8
++#define raw_rom_inw rom_in_be16
++#define raw_rom_inl rom_in_be32
++
++#define raw_rom_outb(val,port) rom_out_8((port),(val))
++#define raw_rom_outw(val,port) rom_out_be16((port),(val))
++#define raw_rom_outl(val,port) rom_out_be32((port),(val))
++#endif /* CONFIG_ATARI_ROM_ISA */
++
+ static inline void raw_insb(volatile u8 __iomem *port, u8 *buf, unsigned int len)
+ {
+ unsigned int i;
+@@ -336,6 +376,62 @@
+ : "d0", "a0", "a1", "d6");
+ }
+
++
++#if defined(CONFIG_ATARI_ROM_ISA)
++static inline void raw_rom_insb(volatile u8 __iomem *port, u8 *buf, unsigned int len)
++{
++ unsigned int i;
++
++ for (i = 0; i < len; i++)
++ *buf++ = rom_in_8(port);
++}
++
++static inline void raw_rom_outsb(volatile u8 __iomem *port, const u8 *buf,
++ unsigned int len)
++{
++ unsigned int i;
++
++ for (i = 0; i < len; i++)
++ rom_out_8(port, *buf++);
++}
++
++static inline void raw_rom_insw(volatile u16 __iomem *port, u16 *buf,
++ unsigned int nr)
++{
++ unsigned int i;
++
++ for (i = 0; i < nr; i++)
++ *buf++ = rom_in_be16(port);
++}
++
++static inline void raw_rom_outsw(volatile u16 __iomem *port, const u16 *buf,
++ unsigned int nr)
++{
++ unsigned int i;
++
++ for (i = 0; i < nr; i++)
++ rom_out_be16(port, *buf++);
++}
++
++static inline void raw_rom_insw_swapw(volatile u16 __iomem *port, u16 *buf,
++ unsigned int nr)
++{
++ unsigned int i;
++
++ for (i = 0; i < nr; i++)
++ *buf++ = rom_in_le16(port);
++}
++
++static inline void raw_rom_outsw_swapw(volatile u16 __iomem *port, const u16 *buf,
++ unsigned int nr)
++{
++ unsigned int i;
++
++ for (i = 0; i < nr; i++)
++ rom_out_le16(port, *buf++);
++}
++#endif /* CONFIG_ATARI_ROM_ISA */
++
+ #define __raw_writel raw_outl
+
+ #endif /* __KERNEL__ */
Added: dists/sid/linux-2.6/debian/patches/m68k-atari-keyboard.patch
==============================================================================
--- (empty file)
+++ dists/sid/linux-2.6/debian/patches/m68k-atari-keyboard.patch Mon Nov 20 00:58:24 2006
@@ -0,0 +1,1059 @@
+diff -urN linux-m68k/arch/m68k/atari/atakeyb.c linux-schmitz/arch/m68k/atari/atakeyb.c
+--- linux-m68k/arch/m68k/atari/atakeyb.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/arch/m68k/atari/atakeyb.c 2006-11-19 21:37:26.000000000 +0100
+@@ -0,0 +1,751 @@
++/*
++ * linux/atari/atakeyb.c
++ *
++ * Atari Keyboard driver for 680x0 Linux
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive
++ * for more details.
++ */
++
++/*
++ * Atari support by Robert de Vries
++ * enhanced by Bjoern Brauel and Roman Hodek
++ */
++
++#include <linux/config.h>
++#include <linux/sched.h>
++#include <linux/kernel.h>
++#include <linux/interrupt.h>
++#include <linux/errno.h>
++#include <linux/keyboard.h>
++#include <linux/delay.h>
++#include <linux/timer.h>
++#include <linux/kd.h>
++#include <linux/random.h>
++#include <linux/init.h>
++#include <linux/kbd_kern.h>
++
++#include <asm/atariints.h>
++#include <asm/atarihw.h>
++#include <asm/atarikb.h>
++#include <asm/atari_joystick.h>
++#include <asm/irq.h>
++
++static void atakeyb_rep( unsigned long ignore );
++extern unsigned int keymap_count;
++
++/* Hook for MIDI serial driver */
++void (*atari_MIDI_interrupt_hook) (void);
++/* Hook for mouse driver */
++void (*atari_mouse_interrupt_hook) (char *);
++/* Hook for keyboard inputdev driver */
++void (*atari_input_keyboard_interrupt_hook) (unsigned char, char);
++/* Hook for mouse inputdev driver */
++void (*atari_input_mouse_interrupt_hook) (char *);
++
++/* variables for IKBD self test: */
++
++/* state: 0: off; >0: in progress; >1: 0xf1 received */
++static volatile int ikbd_self_test;
++/* timestamp when last received a char */
++static volatile unsigned long self_test_last_rcv;
++/* bitmap of keys reported as broken */
++static unsigned long broken_keys[128/(sizeof(unsigned long)*8)] = { 0, };
++
++#define BREAK_MASK (0x80)
++
++/*
++ * ++roman: The following changes were applied manually:
++ *
++ * - The Alt (= Meta) key works in combination with Shift and
++ * Control, e.g. Alt+Shift+a sends Meta-A (0xc1), Alt+Control+A sends
++ * Meta-Ctrl-A (0x81) ...
++ *
++ * - The parentheses on the keypad send '(' and ')' with all
++ * modifiers (as would do e.g. keypad '+'), but they cannot be used as
++ * application keys (i.e. sending Esc O c).
++ *
++ * - HELP and UNDO are mapped to be F21 and F24, resp, that send the
++ * codes "\E[M" and "\E[P". (This is better than the old mapping to
++ * F11 and F12, because these codes are on Shift+F1/2 anyway.) This
++ * way, applications that allow their own keyboard mappings
++ * (e.g. tcsh, X Windows) can be configured to use them in the way
++ * the label suggests (providing help or undoing).
++ *
++ * - Console switching is done with Alt+Fx (consoles 1..10) and
++ * Shift+Alt+Fx (consoles 11..20).
++ *
++ * - The misc. special function implemented in the kernel are mapped
++ * to the following key combinations:
++ *
++ * ClrHome -> Home/Find
++ * Shift + ClrHome -> End/Select
++ * Shift + Up -> Page Up
++ * Shift + Down -> Page Down
++ * Alt + Help -> show system status
++ * Shift + Help -> show memory info
++ * Ctrl + Help -> show registers
++ * Ctrl + Alt + Del -> Reboot
++ * Alt + Undo -> switch to last console
++ * Shift + Undo -> send interrupt
++ * Alt + Insert -> stop/start output (same as ^S/^Q)
++ * Alt + Up -> Scroll back console (if implemented)
++ * Alt + Down -> Scroll forward console (if implemented)
++ * Alt + CapsLock -> NumLock
++ *
++ * ++Andreas:
++ *
++ * - Help mapped to K_HELP
++ * - Undo mapped to K_UNDO (= K_F246)
++ * - Keypad Left/Right Parenthesis mapped to new K_PPAREN[LR]
++ */
++
++static u_short ataplain_map[NR_KEYS] __initdata = {
++ 0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036,
++ 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf008, 0xf009,
++ 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69,
++ 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73,
++ 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b,
++ 0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76,
++ 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf200,
++ 0xf703, 0xf020, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104,
++ 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf200, 0xf200, 0xf114,
++ 0xf603, 0xf200, 0xf30b, 0xf601, 0xf200, 0xf602, 0xf30a, 0xf200,
++ 0xf600, 0xf200, 0xf115, 0xf07f, 0xf200, 0xf200, 0xf200, 0xf200,
++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++ 0xf200, 0xf1ff, 0xf11b, 0xf312, 0xf313, 0xf30d, 0xf30c, 0xf307,
++ 0xf308, 0xf309, 0xf304, 0xf305, 0xf306, 0xf301, 0xf302, 0xf303,
++ 0xf300, 0xf310, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
++ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200
++};
++
++typedef enum kb_state_t
++{
++ KEYBOARD, AMOUSE, RMOUSE, JOYSTICK, CLOCK, RESYNC
++} KB_STATE_T;
++
++#define IS_SYNC_CODE(sc) ((sc) >= 0x04 && (sc) <= 0xfb)
++
++typedef struct keyboard_state
++{
++ unsigned char buf[6];
++ int len;
++ KB_STATE_T state;
++} KEYBOARD_STATE;
++
++KEYBOARD_STATE kb_state;
++
++#define DEFAULT_KEYB_REP_DELAY (HZ/4)
++#define DEFAULT_KEYB_REP_RATE (HZ/25)
++
++/* These could be settable by some ioctl() in future... */
++static unsigned int key_repeat_delay = DEFAULT_KEYB_REP_DELAY;
++static unsigned int key_repeat_rate = DEFAULT_KEYB_REP_RATE;
++
++static unsigned char rep_scancode;
++static struct timer_list atakeyb_rep_timer = { function: atakeyb_rep };
++
++struct pt_regs *atakbd_pt_regs;
++
++static void atakeyb_rep( unsigned long ignore )
++
++{
++ atakbd_pt_regs = NULL;
++
++ /* Disable keyboard for the time we call handle_scancode(), else a race
++ * in the keyboard tty queue may happen */
++ atari_disable_irq( IRQ_MFP_ACIA );
++ del_timer( &atakeyb_rep_timer );
++
++ /* A keyboard int may have come in before we disabled the irq, so
++ * double-check whether rep_scancode is still != 0 */
++ if (rep_scancode) {
++ init_timer(&atakeyb_rep_timer);
++ atakeyb_rep_timer.expires = jiffies + key_repeat_rate;
++ add_timer( &atakeyb_rep_timer );
++
++ //handle_scancode(rep_scancode, 1);
++ if (atari_input_keyboard_interrupt_hook)
++ atari_input_keyboard_interrupt_hook(rep_scancode, 1);
++ }
++
++ atari_enable_irq( IRQ_MFP_ACIA );
++}
++
++
++/* ++roman: If a keyboard overrun happened, we can't tell in general how much
++ * bytes have been lost and in which state of the packet structure we are now.
++ * This usually causes keyboards bytes to be interpreted as mouse movements
++ * and vice versa, which is very annoying. It seems better to throw away some
++ * bytes (that are usually mouse bytes) than to misinterpret them. Therefor I
++ * introduced the RESYNC state for IKBD data. In this state, the bytes up to
++ * one that really looks like a key event (0x04..0xf2) or the start of a mouse
++ * packet (0xf8..0xfb) are thrown away, but at most 2 bytes. This at least
++ * speeds up the resynchronization of the event structure, even if maybe a
++ * mouse movement is lost. However, nothing is perfect. For bytes 0x01..0x03,
++ * it's really hard to decide whether they're mouse or keyboard bytes. Since
++ * overruns usually occur when moving the Atari mouse rapidly, they're seen as
++ * mouse bytes here. If this is wrong, only a make code of the keyboard gets
++ * lost, which isn't too bad. Loosing a break code would be disastrous,
++ * because then the keyboard repeat strikes...
++ */
++
++static irqreturn_t atari_keyboard_interrupt(int irq, void *dummy, struct pt_regs *fp)
++{
++ u_char acia_stat;
++ int scancode;
++ int break_flag;
++
++ /* save frame for register dump */
++ atakbd_pt_regs = fp;
++
++ repeat:
++ if (acia.mid_ctrl & ACIA_IRQ)
++ if (atari_MIDI_interrupt_hook)
++ atari_MIDI_interrupt_hook();
++ acia_stat = acia.key_ctrl;
++ /* check out if the interrupt came from this ACIA */
++ if (!((acia_stat | acia.mid_ctrl) & ACIA_IRQ))
++ return IRQ_HANDLED;
++
++ if (acia_stat & ACIA_OVRN)
++ {
++ /* a very fast typist or a slow system, give a warning */
++ /* ...happens often if interrupts were disabled for too long */
++ printk( KERN_DEBUG "Keyboard overrun\n" );
++ scancode = acia.key_data;
++ /* Turn off autorepeating in case a break code has been lost */
++ del_timer( &atakeyb_rep_timer );
++ rep_scancode = 0;
++ if (ikbd_self_test)
++ /* During self test, don't do resyncing, just process the code */
++ goto interpret_scancode;
++ else if (IS_SYNC_CODE(scancode)) {
++ /* This code seem already to be the start of a new packet or a
++ * single scancode */
++ kb_state.state = KEYBOARD;
++ goto interpret_scancode;
++ }
++ else {
++ /* Go to RESYNC state and skip this byte */
++ kb_state.state = RESYNC;
++ kb_state.len = 1; /* skip max. 1 another byte */
++ goto repeat;
++ }
++ }
++
++ if (acia_stat & ACIA_RDRF) /* received a character */
++ {
++ scancode = acia.key_data; /* get it or reset the ACIA, I'll get it! */
++ tasklet_schedule(&keyboard_tasklet);
++ interpret_scancode:
++ switch (kb_state.state)
++ {
++ case KEYBOARD:
++ switch (scancode)
++ {
++ case 0xF7:
++ kb_state.state = AMOUSE;
++ kb_state.len = 0;
++ break;
++
++ case 0xF8:
++ case 0xF9:
++ case 0xFA:
++ case 0xFB:
++ kb_state.state = RMOUSE;
++ kb_state.len = 1;
++ kb_state.buf[0] = scancode;
++ break;
++
++ case 0xFC:
++ kb_state.state = CLOCK;
++ kb_state.len = 0;
++ break;
++
++ case 0xFE:
++ case 0xFF:
++ kb_state.state = JOYSTICK;
++ kb_state.len = 1;
++ kb_state.buf[0] = scancode;
++ break;
++
++ case 0xF1:
++ /* during self-test, note that 0xf1 received */
++ if (ikbd_self_test) {
++ ++ikbd_self_test;
++ self_test_last_rcv = jiffies;
++ break;
++ }
++ /* FALL THROUGH */
++
++ default:
++ break_flag = scancode & BREAK_MASK;
++ scancode &= ~BREAK_MASK;
++ if (ikbd_self_test) {
++ /* Scancodes sent during the self-test stand for broken
++ * keys (keys being down). The code *should* be a break
++ * code, but nevertheless some AT keyboard interfaces send
++ * make codes instead. Therefore, simply ignore
++ * break_flag...
++ * */
++ int keyval = plain_map[scancode], keytyp;
++
++ set_bit( scancode, broken_keys );
++ self_test_last_rcv = jiffies;
++ keyval = plain_map[scancode];
++ keytyp = KTYP(keyval) - 0xf0;
++ keyval = KVAL(keyval);
++
++ printk( KERN_WARNING "Key with scancode %d ", scancode );
++ if (keytyp == KT_LATIN || keytyp == KT_LETTER) {
++ if (keyval < ' ')
++ printk( "('^%c') ", keyval + '@' );
++ else
++ printk( "('%c') ", keyval );
++ }
++ printk( "is broken -- will be ignored.\n" );
++ break;
++ }
++ else if (test_bit( scancode, broken_keys ))
++ break;
++
++#if 0 // FIXME; hangs at boot
++ if (break_flag) {
++ del_timer( &atakeyb_rep_timer );
++ rep_scancode = 0;
++ }
++ else {
++ del_timer( &atakeyb_rep_timer );
++ rep_scancode = scancode;
++ atakeyb_rep_timer.expires = jiffies + key_repeat_delay;
++ add_timer( &atakeyb_rep_timer );
++ }
++#endif
++
++ // handle_scancode(scancode, !break_flag);
++ if (atari_input_keyboard_interrupt_hook)
++ atari_input_keyboard_interrupt_hook((unsigned char) scancode, !break_flag);
++ break;
++ }
++ break;
++
++ case AMOUSE:
++ kb_state.buf[kb_state.len++] = scancode;
++ if (kb_state.len == 5)
++ {
++ kb_state.state = KEYBOARD;
++ /* not yet used */
++ /* wake up someone waiting for this */
++ }
++ break;
++
++ case RMOUSE:
++ kb_state.buf[kb_state.len++] = scancode;
++ if (kb_state.len == 3)
++ {
++ kb_state.state = KEYBOARD;
++ if (atari_mouse_interrupt_hook)
++ atari_mouse_interrupt_hook(kb_state.buf);
++ }
++ break;
++
++ case JOYSTICK:
++ kb_state.buf[1] = scancode;
++ kb_state.state = KEYBOARD;
++#ifdef FIXED_ATARI_JOYSTICK
++ atari_joystick_interrupt(kb_state.buf);
++#endif
++ break;
++
++ case CLOCK:
++ kb_state.buf[kb_state.len++] = scancode;
++ if (kb_state.len == 6)
++ {
++ kb_state.state = KEYBOARD;
++ /* wake up someone waiting for this.
++ But will this ever be used, as Linux keeps its own time.
++ Perhaps for synchronization purposes? */
++ /* wake_up_interruptible(&clock_wait); */
++ }
++ break;
++
++ case RESYNC:
++ if (kb_state.len <= 0 || IS_SYNC_CODE(scancode)) {
++ kb_state.state = KEYBOARD;
++ goto interpret_scancode;
++ }
++ kb_state.len--;
++ break;
++ }
++ }
++
++#if 0
++ if (acia_stat & ACIA_CTS)
++ /* cannot happen */;
++#endif
++
++ if (acia_stat & (ACIA_FE | ACIA_PE))
++ {
++ printk("Error in keyboard communication\n");
++ }
++
++ /* handle_scancode() can take a lot of time, so check again if
++ * some character arrived
++ */
++ goto repeat;
++}
++
++/*
++ * I write to the keyboard without using interrupts, I poll instead.
++ * This takes for the maximum length string allowed (7) at 7812.5 baud
++ * 8 data 1 start 1 stop bit: 9.0 ms
++ * If this takes too long for normal operation, interrupt driven writing
++ * is the solution. (I made a feeble attempt in that direction but I
++ * kept it simple for now.)
++ */
++void ikbd_write(const char *str, int len)
++{
++ u_char acia_stat;
++
++ if ((len < 1) || (len > 7))
++ panic("ikbd: maximum string length exceeded");
++ while (len)
++ {
++ acia_stat = acia.key_ctrl;
++ if (acia_stat & ACIA_TDRE)
++ {
++ acia.key_data = *str++;
++ len--;
++ }
++ }
++}
++
++/* Reset (without touching the clock) */
++void ikbd_reset(void)
++{
++ static const char cmd[2] = { 0x80, 0x01 };
++
++ ikbd_write(cmd, 2);
++
++ /* if all's well code 0xF1 is returned, else the break codes of
++ all keys making contact */
++}
++
++/* Set mouse button action */
++void ikbd_mouse_button_action(int mode)
++{
++ char cmd[2] = { 0x07, mode };
++
++ ikbd_write(cmd, 2);
++}
++
++/* Set relative mouse position reporting */
++void ikbd_mouse_rel_pos(void)
++{
++ static const char cmd[1] = { 0x08 };
++
++ ikbd_write(cmd, 1);
++}
++
++/* Set absolute mouse position reporting */
++void ikbd_mouse_abs_pos(int xmax, int ymax)
++{
++ char cmd[5] = { 0x09, xmax>>8, xmax&0xFF, ymax>>8, ymax&0xFF };
++
++ ikbd_write(cmd, 5);
++}
++
++/* Set mouse keycode mode */
++void ikbd_mouse_kbd_mode(int dx, int dy)
++{
++ char cmd[3] = { 0x0A, dx, dy };
++
++ ikbd_write(cmd, 3);
++}
++
++/* Set mouse threshold */
++void ikbd_mouse_thresh(int x, int y)
++{
++ char cmd[3] = { 0x0B, x, y };
++
++ ikbd_write(cmd, 3);
++}
++
++/* Set mouse scale */
++void ikbd_mouse_scale(int x, int y)
++{
++ char cmd[3] = { 0x0C, x, y };
++
++ ikbd_write(cmd, 3);
++}
++
++/* Interrogate mouse position */
++void ikbd_mouse_pos_get(int *x, int *y)
++{
++ static const char cmd[1] = { 0x0D };
++
++ ikbd_write(cmd, 1);
++
++ /* wait for returning bytes */
++}
++
++/* Load mouse position */
++void ikbd_mouse_pos_set(int x, int y)
++{
++ char cmd[6] = { 0x0E, 0x00, x>>8, x&0xFF, y>>8, y&0xFF };
++
++ ikbd_write(cmd, 6);
++}
++
++/* Set Y=0 at bottom */
++void ikbd_mouse_y0_bot(void)
++{
++ static const char cmd[1] = { 0x0F };
++
++ ikbd_write(cmd, 1);
++}
++
++/* Set Y=0 at top */
++void ikbd_mouse_y0_top(void)
++{
++ static const char cmd[1] = { 0x10 };
++
++ ikbd_write(cmd, 1);
++}
++
++/* Resume */
++void ikbd_resume(void)
++{
++ static const char cmd[1] = { 0x11 };
++
++ ikbd_write(cmd, 1);
++}
++
++/* Disable mouse */
++void ikbd_mouse_disable(void)
++{
++ static const char cmd[1] = { 0x12 };
++
++ ikbd_write(cmd, 1);
++}
++
++/* Pause output */
++void ikbd_pause(void)
++{
++ static const char cmd[1] = { 0x13 };
++
++ ikbd_write(cmd, 1);
++}
++
++/* Set joystick event reporting */
++void ikbd_joystick_event_on(void)
++{
++ static const char cmd[1] = { 0x14 };
++
++ ikbd_write(cmd, 1);
++}
++
++/* Set joystick interrogation mode */
++void ikbd_joystick_event_off(void)
++{
++ static const char cmd[1] = { 0x15 };
++
++ ikbd_write(cmd, 1);
++}
++
++/* Joystick interrogation */
++void ikbd_joystick_get_state(void)
++{
++ static const char cmd[1] = { 0x16 };
++
++ ikbd_write(cmd, 1);
++}
++
++#if 0
++/* This disables all other ikbd activities !!!! */
++/* Set joystick monitoring */
++void ikbd_joystick_monitor(int rate)
++{
++ static const char cmd[2] = { 0x17, rate };
++
++ ikbd_write(cmd, 2);
++
++ kb_state.state = JOYSTICK_MONITOR;
++}
++#endif
++
++/* some joystick routines not in yet (0x18-0x19) */
++
++/* Disable joysticks */
++void ikbd_joystick_disable(void)
++{
++ static const char cmd[1] = { 0x1A };
++
++ ikbd_write(cmd, 1);
++}
++
++/* Time-of-day clock set */
++void ikbd_clock_set(int year, int month, int day, int hour, int minute, int second)
++{
++ char cmd[7] = { 0x1B, year, month, day, hour, minute, second };
++
++ ikbd_write(cmd, 7);
++}
++
++/* Interrogate time-of-day clock */
++void ikbd_clock_get(int *year, int *month, int *day, int *hour, int *minute, int second)
++{
++ static const char cmd[1] = { 0x1C };
++
++ ikbd_write(cmd, 1);
++}
++
++/* Memory load */
++void ikbd_mem_write(int address, int size, char *data)
++{
++ panic("Attempt to write data into keyboard memory");
++}
++
++/* Memory read */
++void ikbd_mem_read(int address, char data[6])
++{
++ char cmd[3] = { 0x21, address>>8, address&0xFF };
++
++ ikbd_write(cmd, 3);
++
++ /* receive data and put it in data */
++}
++
++/* Controller execute */
++void ikbd_exec(int address)
++{
++ char cmd[3] = { 0x22, address>>8, address&0xFF };
++
++ ikbd_write(cmd, 3);
++}
++
++/* Status inquiries (0x87-0x9A) not yet implemented */
++
++/* Set the state of the caps lock led. */
++void atari_kbd_leds (unsigned int leds)
++{
++ char cmd[6] = {32, 0, 4, 1, 254 + ((leds & 4) != 0), 0};
++ ikbd_write(cmd, 6);
++}
++
++/*
++ * The original code sometimes left the interrupt line of
++ * the ACIAs low forever. I hope, it is fixed now.
++ *
++ * Martin Rogge, 20 Aug 1995
++ */
++
++static int atari_keyb_done = 0;
++
++int __init atari_keyb_init(void)
++{
++
++ if (atari_keyb_done)
++ return 0;
++
++ /* setup key map */
++ memcpy(key_maps[0], ataplain_map, sizeof(plain_map));
++
++ kb_state.state = KEYBOARD;
++ kb_state.len = 0;
++
++ request_irq(IRQ_MFP_ACIA, atari_keyboard_interrupt, IRQ_TYPE_SLOW,
++ "keyboard/mouse/MIDI", atari_keyboard_interrupt);
++
++ atari_turnoff_irq(IRQ_MFP_ACIA);
++ do {
++ /* reset IKBD ACIA */
++ acia.key_ctrl = ACIA_RESET ; // |
++ // (atari_switches & ATARI_SWITCH_IKBD) ? ACIA_RHTID : 0;
++ (void)acia.key_ctrl;
++ (void)acia.key_data;
++
++ /* reset MIDI ACIA */
++ acia.mid_ctrl = ACIA_RESET ; // |
++ // (atari_switches & ATARI_SWITCH_MIDI) ? ACIA_RHTID : 0;
++ (void)acia.mid_ctrl;
++ (void)acia.mid_data;
++
++ /* divide 500kHz by 64 gives 7812.5 baud */
++ /* 8 data no parity 1 start 1 stop bit */
++ /* receive interrupt enabled */
++ /* RTS low (except if switch selected), transmit interrupt disabled */
++ acia.key_ctrl = (ACIA_DIV64|ACIA_D8N1S|ACIA_RIE) ; // |
++ // ((atari_switches & ATARI_SWITCH_IKBD) ?
++ // ACIA_RHTID : ACIA_RLTID);
++
++ acia.mid_ctrl = ACIA_DIV16 | ACIA_D8N1S ; // |
++ // (atari_switches & ATARI_SWITCH_MIDI) ? ACIA_RHTID : 0;
++ }
++ /* make sure the interrupt line is up */
++ while ((mfp.par_dt_reg & 0x10) == 0);
++
++ /* enable ACIA Interrupts */
++ mfp.active_edge &= ~0x10;
++ atari_turnon_irq(IRQ_MFP_ACIA);
++
++ ikbd_self_test = 1;
++ ikbd_reset();
++ /* wait for a period of inactivity (here: 0.25s), then assume the IKBD's
++ * self-test is finished */
++ self_test_last_rcv = jiffies;
++ while (time_before(jiffies, self_test_last_rcv + HZ/4))
++ barrier();
++ /* if not incremented: no 0xf1 received */
++ if (ikbd_self_test == 1)
++ printk( KERN_ERR "WARNING: keyboard self test failed!\n" );
++ ikbd_self_test = 0;
++
++ ikbd_mouse_disable();
++ ikbd_joystick_disable();
++
++#ifdef FIXED_ATARI_JOYSTICK
++ atari_joystick_init();
++#endif
++
++ // flag init done
++ atari_keyb_done = 1;
++ return 0;
++}
++
++
++int atari_kbdrate( struct kbd_repeat *k )
++
++{
++ if (k->delay > 0) {
++ /* convert from msec to jiffies */
++ key_repeat_delay = (k->delay * HZ + 500) / 1000;
++ if (key_repeat_delay < 1)
++ key_repeat_delay = 1;
++ }
++ if (k->period > 0) {
++ key_repeat_rate = (k->period * HZ + 500) / 1000;
++ if (key_repeat_rate < 1)
++ key_repeat_rate = 1;
++ }
++
++ k->delay = key_repeat_delay * 1000 / HZ;
++ k->period = key_repeat_rate * 1000 / HZ;
++
++ return( 0 );
++}
++
++int atari_kbd_translate(unsigned char keycode, unsigned char *keycodep, char raw_mode)
++{
++#ifdef CONFIG_MAGIC_SYSRQ
++ /* ALT+HELP pressed? */
++ if ((keycode == 98) && ((shift_state & 0xff) == 8))
++ *keycodep = 0xff;
++ else
++#endif
++ *keycodep = keycode;
++ return 1;
++}
++
+diff -urN linux-m68k/drivers/input/keyboard/Kconfig linux-schmitz/drivers/input/keyboard/Kconfig
+--- linux-m68k/drivers/input/keyboard/Kconfig 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/drivers/input/keyboard/Kconfig 2006-11-19 21:37:26.000000000 +0100
+@@ -153,6 +153,16 @@
+ To compile this driver as a module, choose M here: the
+ module will be called amikbd.
+
++config KEYBOARD_ATARI
++ tristate "Atari keyboard"
++ depends on ATARI
++ help
++ Say Y here if you are running Linux on any Atari and have a keyboard
++ attached.
++
++ To compile this driver as a module, choose M here: the
++ module will be called atakbd.
++
+ config KEYBOARD_HIL_OLD
+ tristate "HP HIL keyboard support (simple driver)"
+ depends on GSC || HP300
+diff -urN linux-m68k/drivers/input/keyboard/Makefile linux-schmitz/drivers/input/keyboard/Makefile
+--- linux-m68k/drivers/input/keyboard/Makefile 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/drivers/input/keyboard/Makefile 2006-11-19 21:37:26.000000000 +0100
+@@ -9,6 +9,7 @@
+ obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
+ obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
+ obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
++obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
+ obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
+ obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
+ obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
+diff -urN linux-m68k/drivers/input/keyboard/atakbd.c linux-schmitz/drivers/input/keyboard/atakbd.c
+--- linux-m68k/drivers/input/keyboard/atakbd.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/drivers/input/keyboard/atakbd.c 2006-11-19 21:37:26.000000000 +0100
+@@ -0,0 +1,269 @@
++/*
++ * atakbd.c
++ *
++ * Copyright (c) 2005 Michael Schmitz
++ *
++ * Based on amikbd.c, which is
++ *
++ * Copyright (c) 2000-2001 Vojtech Pavlik
++ *
++ * Based on the work of:
++ * Hamish Macdonald
++ */
++
++/*
++ * Atari keyboard driver for Linux/m68k
++ *
++ * The low level init and interrupt stuff is handled in arch/mm68k/atari/atakeyb.c
++ * (the keyboard ACIA also handles the mouse and joystick data, and the keyboard
++ * interrupt is shared with the MIDI ACIA so MIDI data also get handled there).
++ * This driver only deals with handing key events off to the input layer.
++ */
++
++/*
++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Should you need to contact me, the author, you can do so either by
++ * e-mail - mail your message to <vojtech at ucw.cz>, or by paper mail:
++ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++
++#include <asm/atariints.h>
++#include <asm/atarihw.h>
++#include <asm/atarikb.h>
++#include <asm/irq.h>
++
++MODULE_AUTHOR("Michael Schmitz <schmitz at biophys.uni-duesseldorf.de>");
++MODULE_DESCRIPTION("Atari keyboard driver");
++MODULE_LICENSE("GPL");
++
++/*
++ 0x47: KP_7 71
++ 0x48: KP_8 72
++ 0x49: KP_9 73
++ 0x62: KP_/ 98
++ 0x4b: KP_4 75
++ 0x4c: KP_5 76
++ 0x4d: KP_6 77
++ 0x37: KP_* 55
++ 0x4f: KP_1 79
++ 0x50: KP_2 80
++ 0x51: KP_3 81
++ 0x4a: KP_- 74
++ 0x52: KP_0 82
++ 0x53: KP_. 83
++ 0x4e: KP_+ 78
++
++ 0x67: Up 103
++ 0x6c: Down 108
++ 0x69: Left 105
++ 0x6a: Right 106
++ */
++
++
++static unsigned char atakbd_keycode[0x72] = { // american layout
++ [0] = KEY_GRAVE,
++ [1] = KEY_ESC,
++ [2] = KEY_1,
++ [3] = KEY_2,
++ [4] = KEY_3,
++ [5] = KEY_4,
++ [6] = KEY_5,
++ [7] = KEY_6,
++ [8] = KEY_7,
++ [9] = KEY_8,
++ [10] = KEY_9,
++ [11] = KEY_0,
++ [12] = KEY_MINUS,
++ [13] = KEY_EQUAL,
++ [14] = KEY_BACKSPACE,
++ [15] = KEY_TAB,
++ [16] = KEY_Q,
++ [17] = KEY_W,
++ [18] = KEY_E,
++ [19] = KEY_R,
++ [20] = KEY_T,
++ [21] = KEY_Y,
++ [22] = KEY_U,
++ [23] = KEY_I,
++ [24] = KEY_O,
++ [25] = KEY_P,
++ [26] = KEY_LEFTBRACE,
++ [27] = KEY_RIGHTBRACE,
++ [28] = KEY_ENTER,
++ [29] = KEY_LEFTCTRL,
++ [30] = KEY_A,
++ [31] = KEY_S,
++ [32] = KEY_D,
++ [33] = KEY_F,
++ [34] = KEY_G,
++ [35] = KEY_H,
++ [36] = KEY_J,
++ [37] = KEY_K,
++ [38] = KEY_L,
++ [39] = KEY_SEMICOLON,
++ [40] = KEY_APOSTROPHE,
++ [41] = KEY_BACKSLASH, // FIXME, '#'
++ [42] = KEY_LEFTSHIFT,
++ [43] = KEY_GRAVE, // FIXME: '~'
++ [44] = KEY_Z,
++ [45] = KEY_X,
++ [46] = KEY_C,
++ [47] = KEY_V,
++ [48] = KEY_B,
++ [49] = KEY_N,
++ [50] = KEY_M,
++ [51] = KEY_COMMA,
++ [52] = KEY_DOT,
++ [53] = KEY_SLASH,
++ [54] = KEY_RIGHTSHIFT,
++ [55] = KEY_KPASTERISK,
++ [56] = KEY_LEFTALT,
++ [57] = KEY_SPACE,
++ [58] = KEY_CAPSLOCK,
++ [59] = KEY_F1,
++ [60] = KEY_F2,
++ [61] = KEY_F3,
++ [62] = KEY_F4,
++ [63] = KEY_F5,
++ [64] = KEY_F6,
++ [65] = KEY_F7,
++ [66] = KEY_F8,
++ [67] = KEY_F9,
++ [68] = KEY_F10,
++ [69] = KEY_ESC,
++ [70] = KEY_DELETE,
++ [71] = KEY_KP7,
++ [72] = KEY_KP8,
++ [73] = KEY_KP9,
++ [74] = KEY_KPMINUS,
++ [75] = KEY_KP4,
++ [76] = KEY_KP5,
++ [77] = KEY_KP6,
++ [78] = KEY_KPPLUS,
++ [79] = KEY_KP1,
++ [80] = KEY_KP2,
++ [81] = KEY_KP3,
++ [82] = KEY_KP0, // FIXME
++ [83] = KEY_KPDOT, // FIXME
++ [90] = KEY_KPLEFTPAREN,
++ [91] = KEY_KPRIGHTPAREN,
++ [92] = KEY_KPSLASH,
++ [93] = KEY_KPASTERISK,
++ [94] = KEY_KPPLUS,
++ [95] = KEY_HELP,
++ [96] = KEY_BACKSLASH, // FIXME: '<'
++ [97] = KEY_UNDO, // FIXME
++ [98] = KEY_KPSLASH, // FIXME
++ [99] = KEY_KPLEFTPAREN,
++ [100] = KEY_KPRIGHTPAREN,
++ [101] = KEY_KPSLASH,
++ [102] = KEY_KPASTERISK,
++ [103] = KEY_UP,
++ [104] = KEY_KP8,
++ [105] = KEY_LEFT,
++ [106] = KEY_RIGHT,
++ [107] = KEY_KP5,
++ [108] = KEY_DOWN,
++ [109] = KEY_KP1,
++ [110] = KEY_KP2,
++ [111] = KEY_KP3,
++ [112] = KEY_KP0,
++ [113] = KEY_KPDOT
++};
++
++static struct input_dev *atakbd_dev;
++
++static void atakbd_interrupt(unsigned char scancode, char down)
++{
++
++ if (scancode < 0x72) { /* scancodes < 0xf2 are keys */
++
++ // report raw events here?
++
++ scancode = atakbd_keycode[scancode];
++ input_regs(atakbd_dev, atakbd_pt_regs);
++
++ if (scancode == KEY_CAPSLOCK) { /* CapsLock is a toggle switch key on Amiga */
++ input_report_key(atakbd_dev, scancode, 1);
++ input_report_key(atakbd_dev, scancode, 0);
++ input_sync(atakbd_dev);
++ } else {
++ input_report_key(atakbd_dev, scancode, down);
++ input_sync(atakbd_dev);
++ }
++ } else /* scancodes >= 0xf2 are mouse data, most likely */
++ printk(KERN_INFO "atakbd: unhandled scancode %x\n", scancode);
++
++ return;
++}
++
++static int __init atakbd_init(void)
++{
++ int i;
++
++ if (!ATARIHW_PRESENT(ST_MFP))
++ return -EIO;
++
++ // TODO: request_mem_region if not done in arch code
++
++ if (!(atakbd_dev = input_allocate_device()))
++ return -ENOMEM;
++
++ // need to init core driver if not already done so
++ if (atari_keyb_init())
++ return -ENODEV;
++
++ atakbd_dev->name = "Atari Keyboard";
++ atakbd_dev->phys = "atakbd/input0";
++ atakbd_dev->id.bustype = BUS_ATARI;
++ atakbd_dev->id.vendor = 0x0001;
++ atakbd_dev->id.product = 0x0001;
++ atakbd_dev->id.version = 0x0100;
++
++ atakbd_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
++ atakbd_dev->keycode = atakbd_keycode;
++ atakbd_dev->keycodesize = sizeof(unsigned char);
++ atakbd_dev->keycodemax = ARRAY_SIZE(atakbd_keycode);
++
++ for (i = 0; i < 0x72; i++)
++ if (atakbd_keycode[i])
++ set_bit(atakbd_keycode[i], atakbd_dev->keybit);
++
++ // TODO: populate key_maps
++
++ input_register_device(atakbd_dev);
++
++ atari_input_keyboard_interrupt_hook = atakbd_interrupt;
++
++ printk(KERN_INFO "input: %s at IKBD ACIA\n", atakbd_dev->name);
++
++ return 0;
++}
++
++static void __exit atakbd_exit(void)
++{
++ atari_input_keyboard_interrupt_hook = NULL;
++ input_unregister_device(atakbd_dev);
++}
++
++module_init(atakbd_init);
++module_exit(atakbd_exit);
Added: dists/sid/linux-2.6/debian/patches/m68k-atari-mouse.patch
==============================================================================
--- (empty file)
+++ dists/sid/linux-2.6/debian/patches/m68k-atari-mouse.patch Mon Nov 20 00:58:24 2006
@@ -0,0 +1,201 @@
+diff -urN linux-m68k/drivers/input/mouse/Kconfig linux-schmitz/drivers/input/mouse/Kconfig
+--- linux-m68k/drivers/input/mouse/Kconfig 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/drivers/input/mouse/Kconfig 2006-11-19 21:37:26.000000000 +0100
+@@ -96,6 +96,16 @@
+ To compile this driver as a module, choose M here: the
+ module will be called amimouse.
+
++config MOUSE_ATARI
++ tristate "Atari mouse"
++ depends on ATARI
++ help
++ Say Y here if you have an Atari and want its native mouse
++ supported by the kernel.
++
++ To compile this driver as a module, choose M here: the
++ module will be called atarimouse.
++
+ config MOUSE_RISCPC
+ tristate "Acorn RiscPC mouse"
+ depends on ARCH_ACORN
+diff -urN linux-m68k/drivers/input/mouse/Makefile linux-schmitz/drivers/input/mouse/Makefile
+--- linux-m68k/drivers/input/mouse/Makefile 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/drivers/input/mouse/Makefile 2006-11-19 21:37:26.000000000 +0100
+@@ -5,6 +5,7 @@
+ # Each configuration option enables a list of files.
+
+ obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
++obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
+ obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
+ obj-$(CONFIG_MOUSE_INPORT) += inport.o
+ obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
+diff -urN linux-m68k/drivers/input/mouse/atarimouse.c linux-schmitz/drivers/input/mouse/atarimouse.c
+--- linux-m68k/drivers/input/mouse/atarimouse.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/drivers/input/mouse/atarimouse.c 2006-11-19 21:37:26.000000000 +0100
+@@ -0,0 +1,166 @@
++/*
++ * Atari mouse driver for Linux/m68k
++ *
++ * Copyright (c) 2005 Michael Schmitz
++ *
++ * Based on:
++ * Amiga mouse driver for Linux/m68k
++ *
++ * Copyright (c) 2000-2002 Vojtech Pavlik
++ *
++ */
++/*
++ * The low level init and interrupt stuff is handled in arch/mm68k/atari/atakeyb.c
++ * (the keyboard ACIA also handles the mouse and joystick data, and the keyboard
++ * interrupt is shared with the MIDI ACIA so MIDI data also get handled there).
++ * This driver only deals with handing key events off to the input layer.
++ *
++ * Largely based on the old:
++ *
++ * Atari Mouse Driver for Linux
++ * by Robert de Vries (robert at and.nl) 19Jul93
++ *
++ * 16 Nov 1994 Andreas Schwab
++ * Compatibility with busmouse
++ * Support for three button mouse (shamelessly stolen from MiNT)
++ * third button wired to one of the joystick directions on joystick 1
++ *
++ * 1996/02/11 Andreas Schwab
++ * Module support
++ * Allow multiple open's
++ *
++ * Converted to use new generic busmouse code. 5 Apr 1998
++ * Russell King <rmk at arm.uk.linux.org>
++ */
++
++
++/*
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published by
++ * the Free Software Foundation
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/interrupt.h>
++
++#include <asm/irq.h>
++#include <asm/setup.h>
++#include <asm/system.h>
++#include <asm/uaccess.h>
++#include <asm/atarihw.h>
++#include <asm/atarikb.h>
++#include <asm/atariints.h>
++
++MODULE_AUTHOR("Michael Schmitz <schmitz at biophys.uni-duesseldorf.de>");
++MODULE_DESCRIPTION("Atari mouse driver");
++MODULE_LICENSE("GPL");
++
++static int mouse_threshold[2] = {2,2};
++#ifdef __MODULE__
++MODULE_PARM(mouse_threshold, "2i");
++#endif
++#ifdef FIXED_ATARI_JOYSTICK
++extern int atari_mouse_buttons;
++#endif
++static int atamouse_used = 0;
++static int atamouse_lastx, atamouse_lasty;
++
++static struct input_dev *atamouse_dev;
++
++static void atamouse_interrupt(char *buf)
++{
++ int buttons;
++ int nx, ny, dx, dy;
++
++/* ikbd_mouse_disable(); */
++
++ buttons = ((buf[0] & 1)
++ | ((buf[0] & 2) << 1)
++#ifdef FIXED_ATARI_JOYSTICK
++ | (atari_mouse_buttons & 2));
++ atari_mouse_buttons = buttons;
++#else
++ );
++#endif
++/* ikbd_mouse_rel_pos(); */
++
++ /* only relative events get here */
++ dx = buf[1];
++ dy = -buf[2];
++
++ input_regs(atamouse_dev, atakbd_pt_regs);
++
++ input_report_rel(atamouse_dev, REL_X, dx);
++ input_report_rel(atamouse_dev, REL_Y, dy);
++
++ input_report_key(atamouse_dev, BTN_LEFT, buttons & 0x1);
++ input_report_key(atamouse_dev, BTN_MIDDLE, buttons & 0x2);
++ input_report_key(atamouse_dev, BTN_RIGHT, buttons & 0x4);
++
++ input_sync(atamouse_dev);
++
++ return;
++}
++
++static int atamouse_open(struct input_dev *dev)
++{
++ if (atamouse_used++)
++ return 0;
++
++#ifdef FIXED_ATARI_JOYSTICK
++ atari_mouse_buttons = 0;
++#endif
++ ikbd_mouse_y0_top ();
++ ikbd_mouse_thresh (mouse_threshold[0], mouse_threshold[1]);
++ ikbd_mouse_rel_pos();
++ atari_input_mouse_interrupt_hook = atamouse_interrupt;
++ return 0;
++}
++
++static void atamouse_close(struct input_dev *dev)
++{
++ if (!--atamouse_used) {
++ ikbd_mouse_disable();
++ atari_mouse_interrupt_hook = NULL;
++ }
++}
++
++static int __init atamouse_init(void)
++{
++ if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ST_MFP))
++ return -ENODEV;
++
++ if (!(atamouse_dev = input_allocate_device()))
++ return -ENOMEM;
++
++ if (!(atari_keyb_init()))
++ return -ENODEV;
++
++ atamouse_dev->name = "Atari mouse";
++ atamouse_dev->phys = "atamouse/input0";
++ atamouse_dev->id.bustype = BUS_ATARI;
++ atamouse_dev->id.vendor = 0x0001;
++ atamouse_dev->id.product = 0x0002;
++ atamouse_dev->id.version = 0x0100;
++
++ atamouse_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
++ atamouse_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
++ atamouse_dev->keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
++ atamouse_dev->open = atamouse_open;
++ atamouse_dev->close = atamouse_close;
++
++ input_register_device(atamouse_dev);
++
++ printk(KERN_INFO "input: %s at keyboard ACIA\n", atamouse_dev->name);
++ return 0;
++}
++
++static void __exit atamouse_exit(void)
++{
++ input_unregister_device(atamouse_dev);
++}
++
++module_init(atamouse_init);
++module_exit(atamouse_exit);
Added: dists/sid/linux-2.6/debian/patches/m68k-atari-scsi.patch
==============================================================================
--- (empty file)
+++ dists/sid/linux-2.6/debian/patches/m68k-atari-scsi.patch Mon Nov 20 00:58:24 2006
@@ -0,0 +1,215 @@
+diff -urN linux-m68k/drivers/scsi/Kconfig linux-schmitz/drivers/scsi/Kconfig
+--- linux-m68k/drivers/scsi/Kconfig 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/drivers/scsi/Kconfig 2006-11-19 21:37:26.000000000 +0100
+@@ -1713,7 +1713,7 @@
+
+ config ATARI_SCSI
+ tristate "Atari native SCSI support"
+- depends on ATARI && SCSI && BROKEN
++ depends on ATARI && SCSI
+ select SCSI_SPI_ATTRS
+ ---help---
+ If you have an Atari with built-in NCR5380 SCSI controller (TT,
+diff -urN linux-m68k/drivers/scsi/atari_NCR5380.c linux-schmitz/drivers/scsi/atari_NCR5380.c
+--- linux-m68k/drivers/scsi/atari_NCR5380.c 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/drivers/scsi/atari_NCR5380.c 2006-11-19 21:37:26.000000000 +0100
+@@ -716,7 +716,7 @@
+ printk("NCR5380_print_status: no memory for print buffer\n");
+ return;
+ }
+- len = NCR5380_proc_info(pr_bfr, &start, 0, PAGE_SIZE, HOSTNO, 0);
++ len = NCR5380_proc_info(instance, pr_bfr, &start, 0, PAGE_SIZE, 0);
+ pr_bfr[len] = 0;
+ printk("\n%s\n", pr_bfr);
+ free_page((unsigned long) pr_bfr);
+@@ -878,6 +878,46 @@
+ }
+
+ /*
++ * our own old-style timeout update
++ */
++/*
++ * The strategy is to cause the timer code to call scsi_times_out()
++ * when the soonest timeout is pending.
++ * The arguments are used when we are queueing a new command, because
++ * we do not want to subtract the time used from this time, but when we
++ * set the timer, we want to take this value into account.
++ */
++
++int atari_scsi_update_timeout(Scsi_Cmnd * SCset, int timeout)
++{
++ int rtn;
++
++ /*
++ * We are using the new error handling code to actually register/deregister
++ * timers for timeout.
++ */
++
++ if (!timer_pending(&SCset->eh_timeout)) {
++ rtn = 0;
++ } else {
++ rtn = SCset->eh_timeout.expires - jiffies;
++ }
++
++ if (timeout == 0) {
++ del_timer(&SCset->eh_timeout);
++ SCset->eh_timeout.data = (unsigned long) NULL;
++ SCset->eh_timeout.expires = 0;
++ } else {
++ if (SCset->eh_timeout.data != (unsigned long) NULL)
++ del_timer(&SCset->eh_timeout);
++ SCset->eh_timeout.data = (unsigned long) SCset;
++ SCset->eh_timeout.expires = jiffies + timeout;
++ add_timer(&SCset->eh_timeout);
++ }
++ return rtn;
++}
++
++/*
+ * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd,
+ * void (*done)(Scsi_Cmnd *))
+ *
+@@ -902,7 +942,7 @@
+ Scsi_Cmnd *tmp;
+ int oldto;
+ unsigned long flags;
+- extern int update_timeout(Scsi_Cmnd * SCset, int timeout);
++ // extern int update_timeout(Scsi_Cmnd * SCset, int timeout);
+
+ #if (NDEBUG & NDEBUG_NO_WRITE)
+ switch (cmd->cmnd[0]) {
+@@ -956,6 +996,7 @@
+
+ cmd->result = 0;
+
++ // moved local_irq_save() from here
+
+ /*
+ * Insert the cmd into the issue queue. Note that REQUEST SENSE
+@@ -964,7 +1005,6 @@
+ * sense data is only guaranteed to be valid while the condition exists.
+ */
+
+- local_irq_save(flags);
+ /* ++guenther: now that the issue queue is being set up, we can lock ST-DMA.
+ * Otherwise a running NCR5380_main may steal the lock.
+ * Lock before actually inserting due to fairness reasons explained in
+@@ -977,11 +1017,15 @@
+ * because also a timer int can trigger an abort or reset, which would
+ * alter queues and touch the lock.
+ */
++#if 1
+ if (!IS_A_TT()) {
+- oldto = update_timeout(cmd, 0);
++ oldto = atari_scsi_update_timeout(cmd, 0);
+ falcon_get_lock();
+- update_timeout(cmd, oldto);
++ atari_scsi_update_timeout(cmd, oldto);
+ }
++#endif
++ // moved local_irq_save() here for now
++ local_irq_save(flags);
+ if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ LIST(cmd, hostdata->issue_queue);
+ NEXT(cmd) = hostdata->issue_queue;
+@@ -1435,7 +1479,7 @@
+ local_irq_restore(flags);
+
+ /* Wait for arbitration logic to complete */
+-#if NCR_TIMEOUT
++#if defined(NCR_TIMEOUT)
+ {
+ unsigned long timeout = jiffies + 2*NCR_TIMEOUT;
+
+diff -urN linux-m68k/drivers/scsi/atari_scsi.c linux-schmitz/drivers/scsi/atari_scsi.c
+--- linux-m68k/drivers/scsi/atari_scsi.c 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/drivers/scsi/atari_scsi.c 2006-11-19 21:37:26.000000000 +0100
+@@ -558,11 +558,11 @@
+
+ local_irq_save(flags);
+
+- while( !in_interrupt() && falcon_got_lock && stdma_others_waiting() )
++ while( !in_irq() && falcon_got_lock && stdma_others_waiting() )
+ sleep_on( &falcon_fairness_wait );
+
+ while (!falcon_got_lock) {
+- if (in_interrupt())
++ if (in_irq())
+ panic( "Falcon SCSI hasn't ST-DMA lock in interrupt" );
+ if (!falcon_trying_lock) {
+ falcon_trying_lock = 1;
+@@ -764,7 +764,6 @@
+ return( 1 );
+ }
+
+-#ifdef MODULE
+ int atari_scsi_release (struct Scsi_Host *sh)
+ {
+ if (IS_A_TT())
+@@ -773,7 +772,6 @@
+ atari_stram_free (atari_dma_buffer);
+ return 1;
+ }
+-#endif
+
+ void __init atari_scsi_setup(char *str, int *ints)
+ {
+diff -urN linux-m68k/drivers/scsi/atari_scsi.h linux-schmitz/drivers/scsi/atari_scsi.h
+--- linux-m68k/drivers/scsi/atari_scsi.h 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/drivers/scsi/atari_scsi.h 2006-11-19 21:37:26.000000000 +0100
+@@ -21,11 +21,7 @@
+ int atari_scsi_detect (struct scsi_host_template *);
+ const char *atari_scsi_info (struct Scsi_Host *);
+ int atari_scsi_reset (Scsi_Cmnd *, unsigned int);
+-#ifdef MODULE
+ int atari_scsi_release (struct Scsi_Host *);
+-#else
+-#define atari_scsi_release NULL
+-#endif
+
+ /* The values for CMD_PER_LUN and CAN_QUEUE are somehow arbitrary. Higher
+ * values should work, too; try it! (but cmd_per_lun costs memory!) */
+@@ -43,7 +39,9 @@
+
+ #define ATARI_FALCON_CAN_QUEUE 8
+ #define ATARI_FALCON_CMD_PER_LUN 1
+-#define ATARI_FALCON_SG_TABLESIZE SG_NONE
++/* Set to 1 in order to shut up warning message in bio code
++ */
++#define ATARI_FALCON_SG_TABLESIZE 1 // SG_NONE
+
+ #define DEFAULT_USE_TAGGED_QUEUING 0
+
+@@ -63,6 +61,32 @@
+ #define NCR5380_dma_xfer_len(i,cmd,phase) \
+ atari_dma_xfer_len(cmd->SCp.this_residual,cmd,((phase) & SR_IO) ? 0 : 1)
+
++/* former generic SCSI error handling stuff */
++
++#define SCSI_ABORT_SNOOZE 0
++#define SCSI_ABORT_SUCCESS 1
++#define SCSI_ABORT_PENDING 2
++#define SCSI_ABORT_BUSY 3
++#define SCSI_ABORT_NOT_RUNNING 4
++#define SCSI_ABORT_ERROR 5
++
++#define SCSI_RESET_SNOOZE 0
++#define SCSI_RESET_PUNT 1
++#define SCSI_RESET_SUCCESS 2
++#define SCSI_RESET_PENDING 3
++#define SCSI_RESET_WAKEUP 4
++#define SCSI_RESET_NOT_RUNNING 5
++#define SCSI_RESET_ERROR 6
++
++#define SCSI_RESET_SYNCHRONOUS 0x01
++#define SCSI_RESET_ASYNCHRONOUS 0x02
++#define SCSI_RESET_SUGGEST_BUS_RESET 0x04
++#define SCSI_RESET_SUGGEST_HOST_RESET 0x08
++
++#define SCSI_RESET_BUS_RESET 0x100
++#define SCSI_RESET_HOST_RESET 0x200
++#define SCSI_RESET_ACTION 0xff
++
+ /* Debugging printk definitions:
+ *
+ * ARB -> arbitration
Added: dists/sid/linux-2.6/debian/patches/m68k-atari-serial.patch
==============================================================================
--- (empty file)
+++ dists/sid/linux-2.6/debian/patches/m68k-atari-serial.patch Mon Nov 20 00:58:24 2006
@@ -0,0 +1,1747 @@
+diff -urN linux-m68k/drivers/char/Makefile linux-schmitz/drivers/char/Makefile
+--- linux-m68k/drivers/char/Makefile 2006-11-19 21:35:29.000000000 +0100
++++ linux-schmitz/drivers/char/Makefile 2006-11-19 21:37:26.000000000 +0100
+@@ -30,6 +30,7 @@
+ obj-$(CONFIG_MOXA_INTELLIO) += moxa.o
+ obj-$(CONFIG_SIBYTE_SB1250_DUART) += sb1250_duart.o
+ obj-$(CONFIG_A2232) += ser_a2232.o generic_serial.o
++obj-$(CONFIG_ATARI_SCC) += atari_scc.o generic_serial.o
+ obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o
+ obj-$(CONFIG_MOXA_SMARTIO) += mxser.o
+ obj-$(CONFIG_COMPUTONE) += ip2/
+diff -urN linux-m68k/drivers/char/atari_scc.c linux-schmitz/drivers/char/atari_scc.c
+--- linux-m68k/drivers/char/atari_scc.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/drivers/char/atari_scc.c 2006-11-19 21:37:26.000000000 +0100
+@@ -0,0 +1,1732 @@
++/*
++ * drivers/char/atari_scc.c: Atari TT/Falcon Am8530 SCC serial ports implementation.
++ *
++ * Copyright 2005 Michael Schmitz
++ *
++ * Based on:
++ * drivers/char/vme_scc.c: MVME147, MVME162, BVME6000 SCC serial ports
++ * implementation.
++ * Copyright 1999 Richard Hirst <richard at sleepie.demon.co.uk>
++ *
++ * which, in turn, was
++ *
++ * Based on atari_SCC.c which was
++ * Copyright 1994-95 Roman Hodek <Roman.Hodek at informatik.uni-erlangen.de>
++ * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive
++ * for more details.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/config.h>
++#include <linux/kdev_t.h>
++#include <asm/io.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/ioport.h>
++#include <linux/interrupt.h>
++#include <linux/errno.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/mm.h>
++#include <linux/serial.h>
++#include <linux/fcntl.h>
++#include <linux/major.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/miscdevice.h>
++#include <linux/console.h>
++#include <linux/init.h>
++#include <asm/setup.h>
++#include <asm/uaccess.h>
++#include <asm/bootinfo.h>
++
++#include <asm/atarihw.h>
++#include <asm/atariints.h>
++
++#include <linux/generic_serial.h>
++#include "scc.h"
++
++#define CONFIG_TT_SCC 1
++#define CONFIG_FALCON_SCC 1
++
++#define CHANNEL_A 0
++#define CHANNEL_B 1
++
++#define SCC_MINOR_BASE 64
++
++/* Shadows for all SCC write registers */
++static unsigned char scc_shadow[2][16];
++
++/* Location to access for SCC register access delay */
++static volatile unsigned char *scc_del = NULL;
++
++/* To keep track of STATUS_REG state for detection of Ext/Status int source */
++static unsigned char scc_last_status_reg[2];
++
++/***************************** Prototypes *****************************/
++
++/* Function prototypes */
++static void scc_disable_tx_interrupts(void * ptr);
++static void scc_enable_tx_interrupts(void * ptr);
++static void scc_disable_rx_interrupts(void * ptr);
++static void scc_enable_rx_interrupts(void * ptr);
++static int scc_get_CD(void * ptr);
++static void scc_shutdown_port(void * ptr);
++static int scc_set_real_termios(void *ptr);
++static void scc_hungup(void *ptr);
++static void scc_close(void *ptr);
++static int scc_chars_in_buffer(void * ptr);
++static int scc_open(struct tty_struct * tty, struct file * filp);
++static int scc_ioctl(struct tty_struct * tty, struct file * filp,
++ unsigned int cmd, unsigned long arg);
++static void scc_throttle(struct tty_struct *tty);
++static void scc_unthrottle(struct tty_struct *tty);
++static irqreturn_t scc_tx_int(int irq, void *data, struct pt_regs *fp);
++static irqreturn_t scc_rx_int(int irq, void *data, struct pt_regs *fp);
++static irqreturn_t scc_stat_int(int irq, void *data, struct pt_regs *fp);
++static irqreturn_t scc_spcond_int(int irq, void *data, struct pt_regs *fp);
++static void scc_setsignals(struct scc_port *port, int dtr, int rts);
++static void scc_break_ctl(struct tty_struct *tty, int break_state);
++
++static struct tty_driver *scc_driver;
++
++struct scc_port scc_ports[2];
++
++int scc_initialized = 0;
++
++/*
++ * Flags to indicate one of the serial ports has already been initialized by the
++ * serial debug driver. We may want to hold off reinitializing ...
++ */
++
++/* Flag that Modem1 port is already initialized and used */
++extern int atari_SCC_init_done;
++/* Can be set somewhere, if a SCC master reset has already be done and should
++ * not be repeated; used by kgdb */
++extern int atari_SCC_reset_done;
++
++/*---------------------------------------------------------------------------
++ * Interface from generic_serial.c back here
++ *--------------------------------------------------------------------------*/
++
++static struct real_driver scc_real_driver = {
++ scc_disable_tx_interrupts,
++ scc_enable_tx_interrupts,
++ scc_disable_rx_interrupts,
++ scc_enable_rx_interrupts,
++ scc_get_CD,
++ scc_shutdown_port,
++ scc_set_real_termios,
++ scc_chars_in_buffer,
++ scc_close,
++ scc_hungup,
++ NULL
++};
++
++static struct tty_operations scc_ops = {
++ .open = scc_open,
++ .close = gs_close,
++ .write = gs_write,
++ .put_char = gs_put_char,
++ .flush_chars = gs_flush_chars,
++ .write_room = gs_write_room,
++ .chars_in_buffer = gs_chars_in_buffer,
++ .flush_buffer = gs_flush_buffer,
++ .ioctl = scc_ioctl,
++ .throttle = scc_throttle,
++ .unthrottle = scc_unthrottle,
++ .set_termios = gs_set_termios,
++ .stop = gs_stop,
++ .start = gs_start,
++ .hangup = gs_hangup,
++ .break_ctl = scc_break_ctl,
++};
++
++static unsigned SCC_clocks[2][2] = {
++ /* RTxC */ /* TRxC */
++ { SCC_BAUD_BASE_PCLK4, SCC_BAUD_BASE_NONE }, /* Channel A */
++ { SCC_BAUD_BASE_TIMC, SCC_BAUD_BASE_BCLK } /* Channel B */
++};
++
++/* BRG values for the standard speeds and the various clock sources */
++
++typedef struct {
++ unsigned clksrc; /* clock source to use or -1 for not possible */
++ unsigned div; /* divisor: 1, 2 and 4 correspond to
++ * direct 1:16, 1:32 and 1:64 modes,
++ * divisors >= 4 yield a BRG value of
++ * div/2-2 (in 1:16 mode)
++ */
++} BAUD_ENTRY;
++
++/* A pointer for each channel to the current baud table */
++static BAUD_ENTRY *scc_baud_table[2];
++
++/* Baud table format:
++ *
++ * Each entry consists of the clock source (CLK_RTxC, CLK_TRxC or
++ * CLK_PCLK) and a divisor. The following rules apply to the divisor:
++ *
++ * - CLK_RTxC: 1 or even (1, 2 and 4 are the direct modes, > 4 use
++ * the BRG)
++ *
++ * - CLK_TRxC: 1, 2 or 4 (no BRG, only direct modes possible)
++ *
++ * - CLK_PCLK: >= 4 and even (no direct modes, only BRG)
++ *
++ */
++
++/* This table is used if RTxC = 3.672 MHz. This is the case for TT's
++ * channel A and for both channels on the Mega STE/Falcon. (TRxC is unused)
++ */
++
++static BAUD_ENTRY bdtab_norm[20] = {
++ /* B0 */ { 0, 0 },
++ /* B50 */ { CLK_RTxC, 4590 },
++ /* B75 */ { CLK_RTxC, 3060 },
++ /* B110 */ { CLK_PCLK, 4576 },
++ /* B134 */ { CLK_PCLK, 3756 },
++ /* B150 */ { CLK_RTxC, 1530 },
++ /* B200 */ { CLK_PCLK, 2516 },
++ /* B300 */ { CLK_PCLK, 1678 },
++ /* B600 */ { CLK_PCLK, 838 },
++ /* B1200 */ { CLK_PCLK, 420 },
++ /* B1800 */ { CLK_PCLK, 280 },
++ /* B2400 */ { CLK_PCLK, 210 },
++ /* B4800 */ { CLK_RTxC, 48 },
++ /* B9600 */ { CLK_RTxC, 24 },
++ /* B19200 */ { CLK_RTxC, 12 },
++ /* B38400 */ { CLK_RTxC, 6 }, /* #15 spd_extra */
++ /* B57600 */ { CLK_RTxC, 4 }, /* #16 spd_hi */
++ /* B115200 */ { CLK_RTxC, 2 }, /* #17 spd_vhi */
++ /* B230400 */ { CLK_RTxC, 1 }, /* #18 spd_shi */
++ /* B460800 */ { 0, 0 } /* #19 spd_warp: Impossible */
++};
++
++/* This is a special table for the TT channel B with 307.2 kHz at RTxC
++ * and 2.4576 MHz at TRxC
++ */
++static BAUD_ENTRY bdtab_TTChB[20] = {
++ /* B0 */ { 0, 0 },
++ /* B50 */ { CLK_RTxC, 384 },
++ /* B75 */ { CLK_RTxC, 256 },
++ /* B110 */ { CLK_PCLK, 4576 },
++ /* B134 */ { CLK_PCLK, 3756 },
++ /* B150 */ { CLK_RTxC, 128 },
++ /* B200 */ { CLK_RTxC, 96 },
++ /* B300 */ { CLK_RTxC, 64 },
++ /* B600 */ { CLK_RTxC, 32 },
++ /* B1200 */ { CLK_RTxC, 16 },
++ /* B1800 */ { CLK_PCLK, 280 },
++ /* B2400 */ { CLK_RTxC, 8 },
++ /* B4800 */ { CLK_RTxC, 4 },
++ /* B9600 */ { CLK_RTxC, 2 },
++ /* B19200 */ { CLK_RTxC, 1 },
++ /* B38400 */ { CLK_TRxC, 4 },
++ /* B57600 */ { CLK_TRxC, 2 }, /* 57600 is not possible, use 76800 instead */
++ /* B115200 */ { CLK_TRxC, 1 }, /* 115200 is not possible, use 153600 instead */
++ /* B230400 */ { 0, 0 }, /* #18 spd_shi: Impossible */
++ /* B460800 */ { 0, 0 } /* #19 spd_warp: Impossible */
++};
++
++
++/**
++ * tty_flip_buffer_flush - terminal
++ * @tty: tty to flush
++ *
++ * Queue a flush of the terminal flip buffers to the line discipline. This
++ * function may also be called from IRQ context if tty->low_latency is set.
++ *
++ * In the event of the queue being busy for flipping the work will be
++ * held off and retried later.
++ *
++ * Locking: tty buffer lock. Driver locks in low latency mode.
++ */
++
++void tty_flip_buffer_flush(struct tty_struct *tty)
++{
++ unsigned long flags;
++ spin_lock_irqsave(&tty->buf.lock, flags);
++ if (tty->buf.tail != NULL)
++ tty->buf.tail->commit = tty->buf.tail->used;
++ spin_unlock_irqrestore(&tty->buf.lock, flags);
++
++ if (tty->low_latency)
++ schedule_work(&tty->buf.work);
++ else
++ schedule_delayed_work(&tty->buf.work, 1);
++}
++
++/*----------------------------------------------------------------------------
++ * atari_scc_init() and support functions
++ *---------------------------------------------------------------------------*/
++
++static int scc_init_drivers(void)
++{
++ int error;
++
++ scc_driver = alloc_tty_driver(2);
++ if (!scc_driver)
++ return -ENOMEM;
++ scc_driver->owner = THIS_MODULE;
++ scc_driver->driver_name = "scc";
++ scc_driver->name = "ttyS";
++ // scc_driver->devfs_name = "tts/";
++ scc_driver->major = TTY_MAJOR;
++ scc_driver->minor_start = SCC_MINOR_BASE;
++ scc_driver->type = TTY_DRIVER_TYPE_SERIAL;
++ scc_driver->subtype = SERIAL_TYPE_NORMAL;
++ scc_driver->init_termios = tty_std_termios;
++ scc_driver->init_termios.c_cflag =
++ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
++ scc_driver->flags = TTY_DRIVER_REAL_RAW;
++
++ tty_set_operations(scc_driver, &scc_ops);
++
++ if ((error = tty_register_driver(scc_driver))) {
++ printk(KERN_ERR "scc: Couldn't register scc driver, error = %d\n",
++ error);
++ put_tty_driver(scc_driver);
++ return 1;
++ }
++
++ return 0;
++}
++
++
++/* ports[] array is indexed by line no (i.e. [0] for ttyS0, [1] for ttyS1).
++ */
++
++static void scc_init_portstructs(void)
++{
++ struct scc_port *port;
++ int i;
++
++ for (i = 0; i < 2; i++) {
++ port = scc_ports + i;
++ port->gs.magic = SCC_MAGIC;
++ port->gs.close_delay = HZ/2;
++ port->gs.closing_wait = 30 * HZ;
++ port->gs.rd = &scc_real_driver;
++#ifdef NEW_WRITE_LOCKING
++ port->gs.port_write_sem = MUTEX;
++#endif
++ init_waitqueue_head(&port->gs.open_wait);
++ init_waitqueue_head(&port->gs.close_wait);
++ }
++}
++
++
++#ifdef CONFIG_TT_SCC
++static int atari_tt_scc_init(void)
++{
++ struct scc_port *port;
++
++ printk(KERN_INFO "SCC: Atari TT Serial Driver\n");
++ /* FIXME channel A may be switchable between modem and LAN port */
++ /* Init channel A */
++ if (atari_SCC_init_done)
++ printk(KERN_INFO "SCC: already initialized, expect trouble!\n");
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: init channel A\n");
++#endif
++ port = &scc_ports[0];
++ port->channel = CHANNEL_A;
++ port->ctrlp = (volatile unsigned char *)&scc.cha_a_ctrl;
++ port->datap = port->ctrlp + 1;
++ port->port_a = &scc_ports[0];
++ port->port_b = &scc_ports[1];
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: request channel A irqs, port = %p\n", port);
++#endif
++ request_irq(IRQ_SCCA_TX, scc_tx_int, IRQ_TYPE_PRIO,
++ "SCC-A TX", port);
++ request_irq(IRQ_SCCA_STAT, scc_stat_int, IRQ_TYPE_PRIO,
++ "SCC-A status", port);
++ request_irq(IRQ_SCCA_RX, scc_rx_int, IRQ_TYPE_PRIO,
++ "SCC-A RX", port);
++ request_irq(IRQ_SCCA_SPCOND, scc_spcond_int, IRQ_TYPE_PRIO,
++ "SCC-A special cond", port);
++ {
++ SCC_ACCESS_INIT(port);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: read SCC status\n");
++#endif
++ /* on the first access, read status register to reset internal pointers */
++ SCCread(STATUS_REG);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: reset SCC\n");
++#endif
++ /* FIXME: master reset, once only */
++ SCCwrite(MASTER_INT_CTRL, MIC_HARD_RESET);
++ udelay(40);
++
++ /* disable interrupts for this channel */
++ SCCwrite(INT_AND_DMA_REG, 0);
++ /* Set the interrupt vector ; 0x60 for all Atari models */
++ SCCwrite(INT_VECTOR_REG, 0x60);
++ /* Interrupt parameters: vector includes status, status low */
++ SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT);
++ SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB);
++
++ /* disable interrupts for this channel */
++ SCCwrite(INT_AND_DMA_REG, 0);
++ }
++
++ if (!atari_SCC_init_done) {
++ /* Init channel B */
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: init channel B\n");
++#endif
++ port = &scc_ports[1];
++ port->channel = CHANNEL_B;
++ port->ctrlp = (volatile unsigned char *)&scc.cha_b_ctrl;
++ port->datap = port->ctrlp + 1;
++ port->port_a = &scc_ports[0];
++ port->port_b = &scc_ports[1];
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: request channel B irqs, port = %p\n", port);
++#endif
++ request_irq(IRQ_SCCB_TX, scc_tx_int, IRQ_TYPE_PRIO,
++ "SCC-B TX", port);
++ request_irq(IRQ_SCCB_STAT, scc_stat_int, IRQ_TYPE_PRIO,
++ "SCC-B status", port);
++ request_irq(IRQ_SCCB_RX, scc_rx_int, IRQ_TYPE_PRIO,
++ "SCC-B RX", port);
++ request_irq(IRQ_SCCB_SPCOND, scc_spcond_int, IRQ_TYPE_PRIO,
++ "SCC-B special cond", port);
++ {
++ SCC_ACCESS_INIT(port);
++
++ /* disable interrupts for this channel */
++ SCCwrite(INT_AND_DMA_REG, 0);
++ }
++/* not implemented yet */
++#if 0
++ request_irq(IRQ_TT_MFP_RI, scc_ri_int, IRQ_TYPE_SLOW,
++ "TT-MFP ring indicator (modem 2)", port);
++#endif
++
++ }
++
++ /* once only: initalize MFP timer C for RTxC */
++ tt_mfp.tim_ct_cd = (tt_mfp.tim_ct_cd & ~0x70) | 0x10;
++ tt_mfp.tim_dt_c = 1;
++ atari_turnoff_irq(IRQ_TT_MFP_TIMC);
++
++ /* set baud tables */
++ scc_baud_table[CHANNEL_A] = bdtab_norm;
++ scc_baud_table[CHANNEL_B] = bdtab_TTChB;
++
++ /* Initialise the tty driver structures and register */
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: scc_init_portstructs()\n");
++#endif
++ scc_init_portstructs();
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: scc_init_drivers()\n");
++#endif
++ scc_init_drivers();
++
++ return 0;
++}
++#endif
++
++
++#ifdef CONFIG_FALCON_SCC
++static int atari_falcon_scc_init(void)
++{
++ struct scc_port *port;
++
++ printk(KERN_INFO "SCC: Atari Falcon Serial Driver\n");
++ if (atari_SCC_init_done)
++ printk(KERN_INFO "SCC: already initialized, expect trouble!\n");
++
++ /* Init channel A */
++ port = &scc_ports[0];
++ port->channel = CHANNEL_A;
++ port->ctrlp = (volatile unsigned char *)&scc.cha_a_ctrl;
++ port->datap = port->ctrlp + 2;
++ port->port_a = &scc_ports[0];
++ port->port_b = &scc_ports[1];
++ request_irq(IRQ_SCCA_TX, scc_tx_int, IRQ_TYPE_PRIO,
++ "SCC-A TX", port);
++ request_irq(IRQ_SCCA_STAT, scc_stat_int, IRQ_TYPE_PRIO,
++ "SCC-A status", port);
++ request_irq(IRQ_SCCA_RX, scc_rx_int, IRQ_TYPE_PRIO,
++ "SCC-A RX", port);
++ request_irq(IRQ_SCCA_SPCOND, scc_spcond_int, IRQ_TYPE_PRIO,
++ "SCC-A special cond", port);
++ {
++ SCC_ACCESS_INIT(port);
++
++ /* on the first access, read status register to reset internal pointers */
++ SCCread(STATUS_REG);
++
++ /* FIXME: master reset, once only */
++ SCCwrite(MASTER_INT_CTRL, MIC_HARD_RESET);
++ udelay(40);
++
++ /* disable interrupts for this channel */
++ SCCwrite(INT_AND_DMA_REG, 0);
++ /* Set the interrupt vector */
++ SCCwrite(INT_VECTOR_REG, 0x60);
++ /* Interrupt parameters: vector includes status, status low */
++ SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT);
++ SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB);
++ }
++
++ /* conditionalize if port in use by console ?? */
++ /* Init channel B */
++ port = &scc_ports[1];
++ port->channel = CHANNEL_B;
++ port->ctrlp = (volatile unsigned char *)&scc.cha_b_ctrl;
++ port->datap = port->ctrlp + 2;
++ port->port_a = &scc_ports[0];
++ port->port_b = &scc_ports[1];
++ request_irq(IRQ_SCCB_TX, scc_tx_int, IRQ_TYPE_PRIO,
++ "SCC-B TX", port);
++ request_irq(IRQ_SCCB_STAT, scc_stat_int, IRQ_TYPE_PRIO,
++ "SCC-B status", port);
++ request_irq(IRQ_SCCB_RX, scc_rx_int, IRQ_TYPE_PRIO,
++ "SCC-B RX", port);
++ request_irq(IRQ_SCCB_SPCOND, scc_spcond_int, IRQ_TYPE_PRIO,
++ "SCC-B special cond", port);
++
++ {
++ SCC_ACCESS_INIT(port); /* Either channel will do */
++
++ /* disable interrupts for this channel */
++ SCCwrite(INT_AND_DMA_REG, 0);
++ }
++
++ /* set baud tables */
++ scc_baud_table[CHANNEL_A] = bdtab_norm;
++ scc_baud_table[CHANNEL_B] = bdtab_norm;
++
++ /* Initialise the tty driver structures and register */
++ scc_init_portstructs();
++ scc_init_drivers();
++
++ return 0;
++}
++#endif
++
++
++#ifdef CONFIG_ST_SCC
++static int atari_st_scc_init(void)
++{
++ struct scc_port *port;
++
++ int escc = ATARIHW_PRESENT(ST_ESCC);
++
++ printk(KERN_INFO "SCC: Atari MegaST/E Serial Driver\n");
++ /* FIXME: ports reversed logic */
++ /* Init channel A */
++ port = &scc_ports[1];
++ port->channel = CHANNEL_A;
++ port->ctrlp = (volatile unsigned char *)(escc ? &st_escc.cha_a_ctrl : &scc.cha_a_ctrl);
++ port->datap = port->ctrlp + 4;
++ port->port_a = &scc_ports[1];
++ port->port_b = &scc_ports[0];
++ request_irq(IRQ_SCCA_TX, scc_tx_int, IRQ_TYPE_PRIO,
++ "SCC-A TX", port);
++ request_irq(IRQ_SCCA_STAT, scc_stat_int, IRQ_TYPE_PRIO,
++ "SCC-A status", port);
++ request_irq(IRQ_SCCA_RX, scc_rx_int, IRQ_TYPE_PRIO,
++ "SCC-A RX", port);
++ request_irq(SCCA_SPCOND, scc_spcond_int, IRQ_TYPE_PRIO,
++ "SCC-A special cond", port);
++ {
++ SCC_ACCESS_INIT(port);
++
++ /* on the first access, read status register to reset internal pointers */
++ SCCread(STATUS_REG);
++
++ /* FIXME: master reset, once only */
++ SCCwrite(MASTER_INT_CTRL, MIC_HARD_RESET);
++ udelay(40);
++
++ /* disable interrupts for this channel */
++ SCCwrite(INT_AND_DMA_REG, 0);
++ /* Set the interrupt vector */
++ SCCwrite(INT_VECTOR_REG, BVME_IRQ_SCC_BASE);
++ /* Interrupt parameters: vector includes status, status low */
++ SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT);
++ SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB);
++ }
++
++ /* Init channel B */
++ port = &scc_ports[0];
++ port->channel = CHANNEL_B;
++ port->ctrlp = (volatile unsigned char *)(escc ? &st_escc.cha_b_ctrl : &scc.cha_b_ctrl);
++ port->datap = port->ctrlp + 4;
++ port->port_a = &scc_ports[0];
++ port->port_b = &scc_ports[1];
++ request_irq(IRQ_SCCB_TX, scc_tx_int, IRQ_TYPE_PRIO,
++ "SCC-B TX", port);
++ request_irq(IRQ_SCCB_STAT, scc_stat_int, IRQ_TYPE_PRIO,
++ "SCC-B status", port);
++ request_irq(IRQ_SCCB_RX, scc_rx_int, IRQ_TYPE_PRIO,
++ "SCC-B RX", port);
++ request_irq(IRQ_SCCB_SPCOND, scc_spcond_int, IRQ_TYPE_PRIO,
++ "SCC-B special cond", port);
++
++ {
++ SCC_ACCESS_INIT(port); /* Either channel will do */
++
++ /* disable interrupts for this channel */
++ SCCwrite(INT_AND_DMA_REG, 0);
++ }
++
++ /* set baud tables */
++ scc_baud_table[CHANNEL_A] = bdtab_norm;
++ scc_baud_table[CHANNEL_B] = bdtab_norm;
++
++ /* Initialise the tty driver structures and register */
++ scc_init_portstructs();
++ scc_init_drivers();
++
++ return 0;
++}
++#endif
++
++
++int atari_scc_init(void)
++{
++ int res = -ENODEV;
++ static int called = 0;
++
++ if (called)
++ return res;
++ called = 1;
++
++ if (!(ATARIHW_PRESENT(SCC) || ATARIHW_PRESENT(ST_ESCC)))
++ return( -ENODEV );
++
++ scc_del = &mfp.par_dt_reg;
++
++#ifdef CONFIG_TT_SCC
++ if (MACH_IS_TT)
++ res = atari_tt_scc_init();
++#endif
++#ifdef CONFIG_FALCON_SCC
++ if (MACH_IS_FALCON)
++ res = atari_falcon_scc_init();
++#endif
++#ifdef CONFIG_ST_SCC
++ if (MACH_IS_ST)
++ res = atari_st_scc_init();
++#endif
++ return res;
++}
++
++void atari_scc_cleanup(void)
++{
++ struct scc_port *port;
++
++ tty_unregister_driver(scc_driver);
++ port = &scc_ports[0];
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: free channel A irqs, port = %p\n", port);
++#endif
++ free_irq(IRQ_SCCA_TX, port);
++ free_irq(IRQ_SCCA_STAT, port);
++ free_irq(IRQ_SCCA_RX, port);
++ free_irq(IRQ_SCCA_SPCOND, port);
++
++ port = &scc_ports[1];
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: free channel A irqs, port = %p\n", port);
++#endif
++ free_irq(IRQ_SCCB_TX, port);
++ free_irq(IRQ_SCCB_STAT, port);
++ free_irq(IRQ_SCCB_RX, port);
++ free_irq(IRQ_SCCB_SPCOND, port);
++
++}
++
++module_init(atari_scc_init);
++module_exit(atari_scc_cleanup);
++
++/*---------------------------------------------------------------------------
++ * Interrupt handlers
++ *--------------------------------------------------------------------------*/
++
++static irqreturn_t scc_rx_int(int irq, void *data, struct pt_regs *fp)
++{
++ unsigned char ch;
++ struct scc_port *port = data;
++ struct tty_struct *tty = port->gs.tty;
++ SCC_ACCESS_INIT(port);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: rx_int ...\n");
++#endif
++ ch = SCCread_NB(RX_DATA_REG);
++ if (!tty) {
++ printk(KERN_WARNING "scc_rx_int with NULL tty!\n");
++ SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
++ return IRQ_HANDLED;
++ }
++ tty_insert_flip_char(tty, ch, 0);
++
++ /* Check if another character is already ready; in that case, the
++ * spcond_int() function must be used, because this character may have an
++ * error condition that isn't signalled by the interrupt vector used!
++ */
++ if (SCCread(INT_PENDING_REG) &
++ (port->channel == CHANNEL_A ? IPR_A_RX : IPR_B_RX)) {
++ scc_spcond_int (irq, data, fp);
++ return IRQ_HANDLED;
++ }
++
++ SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
++
++ tty_flip_buffer_flush(tty);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: rx_int done\n");
++#endif
++ return IRQ_HANDLED;
++}
++
++
++static irqreturn_t scc_spcond_int(int irq, void *data, struct pt_regs *fp)
++{
++ struct scc_port *port = data;
++ struct tty_struct *tty = port->gs.tty;
++ unsigned char stat, ch, err;
++ int int_pending_mask = port->channel == CHANNEL_A ?
++ IPR_A_RX : IPR_B_RX;
++ SCC_ACCESS_INIT(port);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: spcond_int ...\n");
++#endif
++ if (!tty) {
++ printk(KERN_WARNING "scc_spcond_int with NULL tty!\n");
++ SCCwrite(COMMAND_REG, CR_ERROR_RESET);
++ SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
++ return IRQ_HANDLED;
++ }
++ do {
++ stat = SCCread(SPCOND_STATUS_REG);
++ ch = SCCread_NB(RX_DATA_REG);
++
++ if (stat & SCSR_RX_OVERRUN)
++ err = TTY_OVERRUN;
++ else if (stat & SCSR_PARITY_ERR)
++ err = TTY_PARITY;
++ else if (stat & SCSR_CRC_FRAME_ERR)
++ err = TTY_FRAME;
++ else
++ err = 0;
++
++ tty_insert_flip_char(tty, ch, err);
++
++ /* ++TeSche: *All* errors have to be cleared manually,
++ * else the condition persists for the next chars
++ */
++ if (err)
++ SCCwrite(COMMAND_REG, CR_ERROR_RESET);
++
++ } while(SCCread(INT_PENDING_REG) & int_pending_mask);
++
++ SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
++
++ tty_flip_buffer_flush(tty);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: spcond_int done\n");
++#endif
++ return IRQ_HANDLED;
++}
++
++/* not implemented yet */
++#if 0
++static void scc_ri_int(int irq, void *data, struct pt_regs *fp)
++{
++ struct scc_port *port = data;
++ /* update input line counter */
++ port->icount.rng++;
++ wake_up_interruptible(&port->delta_msr_wait);
++}
++#endif
++
++static irqreturn_t scc_tx_int(int irq, void *data, struct pt_regs *fp)
++{
++ struct scc_port *port = data;
++ SCC_ACCESS_INIT(port);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: tx_int irq %d port %p ...\n", irq, data);
++#endif
++ if (!port->gs.tty) {
++ printk(KERN_WARNING "scc_tx_int with NULL tty!\n");
++ SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
++ SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET);
++ SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
++ return IRQ_HANDLED;
++ }
++ while ((SCCread_NB(STATUS_REG) & SR_TX_BUF_EMPTY)) {
++ if (port->x_char) {
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: tx_int writing char %c\n", port->x_char);
++#endif
++ SCCwrite(TX_DATA_REG, port->x_char);
++ port->x_char = 0;
++ }
++ else if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped ||
++ port->gs.tty->hw_stopped) {
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: nothing to do!\n");
++#endif
++ break;
++ } else {
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: tx_int writing buf %c\n", port->gs.xmit_buf[port->gs.xmit_tail]);
++#endif
++ SCCwrite(TX_DATA_REG, port->gs.xmit_buf[port->gs.xmit_tail++]);
++ port->gs.xmit_tail = port->gs.xmit_tail & (SERIAL_XMIT_SIZE-1);
++ if (--port->gs.xmit_cnt <= 0)
++ break;
++ }
++ }
++ if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped ||
++ port->gs.tty->hw_stopped) {
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: nothing to do, disabling int\n");
++#endif
++ /* disable tx interrupts */
++ SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
++ SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET); /* disable tx_int on next tx underrun? */
++ port->gs.flags &= ~GS_TX_INTEN;
++ }
++ if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) {
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: waking up tty!\n");
++#endif
++ tty_wakeup(port->gs.tty);
++ }
++
++ SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: tx_int done\n");
++#endif
++ return IRQ_HANDLED;
++}
++
++
++static irqreturn_t scc_stat_int(int irq, void *data, struct pt_regs *fp)
++{
++ struct scc_port *port = data;
++ unsigned channel = port->channel;
++ unsigned char last_sr, sr, changed;
++ SCC_ACCESS_INIT(port);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: stat_int ...\n");
++#endif
++ last_sr = scc_last_status_reg[channel];
++ sr = scc_last_status_reg[channel] = SCCread_NB(STATUS_REG);
++ changed = last_sr ^ sr;
++
++ if (changed & SR_DCD) {
++ port->c_dcd = !!(sr & SR_DCD);
++ if (!(port->gs.flags & ASYNC_CHECK_CD))
++ ; /* Don't report DCD changes */
++ else if (port->c_dcd) {
++ /* Are we blocking in open?*/
++ wake_up_interruptible(&port->gs.open_wait);
++ }
++ else {
++ if (port->gs.tty)
++ tty_hangup (port->gs.tty);
++ }
++ }
++
++ // FIXME: CTS and DSR status changes?
++
++ SCCwrite(COMMAND_REG, CR_EXTSTAT_RESET);
++ SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: stat_int done\n");
++#endif
++ return IRQ_HANDLED;
++}
++
++
++/*---------------------------------------------------------------------------
++ * generic_serial.c callback funtions
++ *--------------------------------------------------------------------------*/
++
++static void scc_disable_tx_interrupts(void *ptr)
++{
++ struct scc_port *port = ptr;
++ unsigned long flags;
++ SCC_ACCESS_INIT(port);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: disable_tx_int ...\n");
++#endif
++ local_irq_save(flags);
++ SCCmod(INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0);
++ port->gs.flags &= ~GS_TX_INTEN;
++ local_irq_restore(flags);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: disable_tx_int done!\n");
++#endif
++}
++
++
++static void scc_enable_tx_interrupts(void *ptr)
++{
++ struct scc_port *port = ptr;
++ unsigned long flags;
++ SCC_ACCESS_INIT(port);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: enable_tx_int ...\n");
++#endif
++ local_irq_save(flags);
++ SCCmod(INT_AND_DMA_REG, 0xff, IDR_TX_INT_ENAB);
++ /* restart the transmitter */
++ scc_tx_int (0, port, 0);
++ local_irq_restore(flags);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: enable_tx_int done!\n");
++#endif
++}
++
++
++static void scc_disable_rx_interrupts(void *ptr)
++{
++ struct scc_port *port = ptr;
++ unsigned long flags;
++ SCC_ACCESS_INIT(port);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: disable_rx_int ...\n");
++#endif
++ local_irq_save(flags);
++ SCCmod(INT_AND_DMA_REG,
++ ~(IDR_RX_INT_MASK|IDR_PARERR_AS_SPCOND|IDR_EXTSTAT_INT_ENAB), 0);
++ local_irq_restore(flags);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: disable_rx_int done!\n");
++#endif
++}
++
++
++static void scc_enable_rx_interrupts(void *ptr)
++{
++ struct scc_port *port = ptr;
++ unsigned long flags;
++ SCC_ACCESS_INIT(port);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: enable_rx_int ...\n");
++#endif
++ local_irq_save(flags);
++ SCCmod(INT_AND_DMA_REG, 0xff,
++ IDR_EXTSTAT_INT_ENAB|IDR_PARERR_AS_SPCOND|IDR_RX_INT_ALL);
++ local_irq_restore(flags);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: enable_rx_int done!\n");
++#endif
++}
++
++
++static int scc_get_CD(void *ptr)
++{
++ struct scc_port *port = ptr;
++ unsigned channel = port->channel;
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: get_CD!\n");
++#endif
++ return !!(scc_last_status_reg[channel] & SR_DCD);
++}
++
++
++static void scc_shutdown_port(void *ptr)
++{
++ struct scc_port *port = ptr;
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: shutdown_port ...\n");
++#endif
++ port->gs.flags &= ~ GS_ACTIVE;
++ if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) {
++ scc_setsignals (port, 0, 0);
++ }
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: shutdown_port done!\n");
++#endif
++}
++
++
++static int scc_set_real_termios (void *ptr)
++{
++ /* the SCC has char sizes 5,7,6,8 in that order! */
++ static int chsize_map[4] = { 0, 2, 1, 3 };
++ unsigned cflag, baud, baudbits, baudidx, brgmode, clkmode, clksrc, div, chsize, channel, brgval = 0;
++ unsigned long flags;
++ struct scc_port *port = ptr;
++ SCC_ACCESS_INIT(port);
++
++ if (!port->gs.tty || !port->gs.tty->termios) return 0;
++
++ channel = port->channel;
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: termios for channel %p\n", channel);
++#endif
++ cflag = port->gs.tty->termios->c_cflag;
++ baud = port->gs.baud;
++ baudbits = cflag & CBAUD;
++ chsize = (cflag & CSIZE) >> 4;
++
++ if (baud == 0) {
++ /* speed == 0 -> drop DTR */
++ local_irq_save(flags);
++ SCCmod(TX_CTRL_REG, ~TCR_DTR, 0);
++ local_irq_restore(flags);
++ return 0;
++ }
++ else if ((MACH_IS_TT && (baud < 50 || baud > 115200)) ||
++ (MACH_IS_FALCON && (baud < 50 || baud > 230400))) {
++ printk(KERN_NOTICE "SCC: Bad speed requested, %d\n", baud);
++ return 0;
++ }
++
++ if (cflag & CLOCAL)
++ port->gs.flags &= ~ASYNC_CHECK_CD;
++ else
++ port->gs.flags |= ASYNC_CHECK_CD;
++
++ // calculate brgval for Atari; enable direct modes!
++
++ /* convert baud rate from gs.baud to table index, set custom divisor eventually */
++
++ div = 0;
++ clksrc = 0;
++ baudidx = 0;
++
++ switch (baud) {
++ case 50:
++ baudidx = 1;
++ break;
++ case 75:
++ baudidx = 2;
++ break;
++ case 110:
++ baudidx = 3;
++ break;
++ case 134:
++ baudidx = 4;
++ break;
++ case 150:
++ baudidx = 5;
++ break;
++ case 200:
++ baudidx = 6;
++ break;
++ case 300:
++ baudidx = 7;
++ break;
++ case 600:
++ baudidx = 8;
++ break;
++ case 1200:
++ baudidx = 9;
++ break;
++ case 1800:
++ baudidx = 10;
++ break;
++ case 2400:
++ baudidx = 11;
++ break;
++ case 4800:
++ baudidx = 12;
++ break;
++ case 9600:
++ baudidx = 13;
++ break;
++ case 19200:
++ baudidx = 14;
++ break;
++ case 38400:
++ baudidx = 15;
++ break;
++ case 57600:
++ baudidx = 16;
++ port->gs.tty->low_latency = 1;
++ break;
++ case 115200:
++ baudidx = 17;
++ port->gs.tty->low_latency = 1;
++ break;
++ case 230400:
++ baudidx = 18;
++ port->gs.tty->low_latency = 1;
++ break;
++ default:
++ baudidx = 15;
++ }
++
++ /* do we have a custom divisor ?? */
++ if (!div) {
++ if (baudidx > 19) baudidx = 19;
++ clksrc = scc_baud_table[channel][baudidx].clksrc;
++ div = scc_baud_table[channel][baudidx].div;
++ if(!div)
++ {
++ printk(" SCC_change_speed: divisor = 0 !!!");
++ return 0;
++ }
++ }
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: termios baud %d baudbits %d baudidx %d \n clksrc %d div %d\n",
++ baud, baudbits, baudidx, clksrc, div);
++#endif
++ /* compute the SCC's clock source, clock mode, BRG mode and BRG
++ * value from clksrc and div
++ */
++ if (div <= 4) {
++ clkmode = (div == 1 ? A1CR_CLKMODE_x16 :
++ div == 2 ? A1CR_CLKMODE_x32 :
++ A1CR_CLKMODE_x64);
++ clksrc = (clksrc == CLK_RTxC
++ ? CCR_TXCLK_RTxC | CCR_RXCLK_RTxC
++ : CCR_TXCLK_TRxC | CCR_RXCLK_TRxC);
++ brgmode = 0; /* off */
++ brgval = 0;
++ }
++ else {
++ brgval = div/2 - 2;
++ brgmode = (DCR_BRG_ENAB |
++ (clksrc == CLK_PCLK ? DCR_BRG_USE_PCLK : 0));
++ clkmode = A1CR_CLKMODE_x16;
++ clksrc = CCR_TXCLK_BRG | CCR_RXCLK_BRG;
++ }
++
++ //printk(KERN_INFO "SCC: termios baud %d baudbits %d baudidx %d \n clksrc %d clkmode %d div %d brgval %d brgmode %d\n",
++ // baud, baudbits, baudidx, clksrc, clkmode, div, brgval, brgmode);
++
++ /* Now we have all parameters and can go to set them: */
++ local_irq_save(flags);
++
++#ifdef DEBUG
++ printk( " brgval=%d brgmode=%02x clkmode=%02x clksrc=%02x\n",
++ brgval, brgmode, clkmode, clksrc );
++#endif
++ /* receiver's character size */
++ SCCmod( RX_CTRL_REG, ~RCR_CHSIZE_MASK, chsize_map[chsize] << 6 );
++#ifdef DEBUG
++ printk( " RX_CTRL_REG <- %02x\n", SCCread( RX_CTRL_REG ) );
++#endif
++
++ // clock mode changes depending on baud rate
++ /* parity and stop bits (both, Tx and Rx) and clock mode */
++ SCCmod (AUX1_CTRL_REG,
++ ~(A1CR_PARITY_MASK | A1CR_MODE_MASK | A1CR_CLKMODE_MASK),
++ ((cflag & PARENB
++ ? (cflag & PARODD ? A1CR_PARITY_ODD : A1CR_PARITY_EVEN)
++ : A1CR_PARITY_NONE)
++ | (cflag & CSTOPB ? A1CR_MODE_ASYNC_2 : A1CR_MODE_ASYNC_1)
++ | clkmode));
++
++#ifdef DEBUG
++ printk( " AUX1_CTRL_REG <- %02x\n", SCCread( AUX1_CTRL_REG ) );
++#endif
++ /* sender's character size, set DTR for valid baud rate */
++ SCCmod(TX_CTRL_REG, ~TCR_CHSIZE_MASK, chsize_map[chsize] << 5 | TCR_DTR);
++#ifdef DEBUG
++ printk( " TX_CTRL_REG <- %02x\n", SCCread( TX_CTRL_REG ) );
++#endif
++
++ // clock sources change for TT !!
++ /* clock sources never change */
++ /* clock sources */
++ SCCmod( CLK_CTRL_REG, ~(CCR_TXCLK_MASK | CCR_RXCLK_MASK), clksrc );
++#ifdef DEBUG
++ printk( " CLK_CTRL_REG <- %02x\n", SCCread( CLK_CTRL_REG ) );
++#endif
++
++ /* disable BRG before changing the value */
++ SCCmod(DPLL_CTRL_REG, ~DCR_BRG_ENAB, 0);
++ /* BRG value */
++ SCCwrite(TIMER_LOW_REG, brgval & 0xff);
++ SCCwrite(TIMER_HIGH_REG, (brgval >> 8) & 0xff);
++ /* BRG enable, and clock source never changes */
++ //SCCmod(DPLL_CTRL_REG, 0xff, DCR_BRG_ENAB);
++ SCCmod(DPLL_CTRL_REG, ~(DCR_BRG_ENAB | DCR_BRG_USE_PCLK), brgmode);
++#ifdef DEBUG
++ printk( " TIMER_LOW_REG <- %02x\n", SCCread( TIMER_LOW_REG ) );
++ printk( " TIMER_HIGH_REG <- %02x\n", SCCread( TIMER_HIGH_REG ) );
++#endif
++#ifdef DEBUG
++ printk( " DPLL_CTRL_REG <- %02x\n", SCCread( DPLL_CTRL_REG ) );
++#endif
++
++ local_irq_restore(flags);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: done termios for channel %d\n", channel);
++#endif
++ return 0;
++}
++
++
++static int scc_chars_in_buffer (void *ptr)
++{
++ struct scc_port *port = ptr;
++#ifdef DEBUG
++ int rv;
++#endif
++ SCC_ACCESS_INIT(port);
++#ifdef DEBUG
++ rv = (SCCread (SPCOND_STATUS_REG) & SCSR_ALL_SENT) ? 0 : 1;
++ printk(KERN_INFO "SCC: chars_in_buffer: %d\n", rv);
++ return rv;
++#else
++ return (SCCread (SPCOND_STATUS_REG) & SCSR_ALL_SENT) ? 0 : 1;
++#endif
++}
++
++
++/* Comment taken from sx.c (2.4.0):
++ I haven't the foggiest why the decrement use count has to happen
++ here. The whole linux serial drivers stuff needs to be redesigned.
++ My guess is that this is a hack to minimize the impact of a bug
++ elsewhere. Thinking about it some more. (try it sometime) Try
++ running minicom on a serial port that is driven by a modularized
++ driver. Have the modem hangup. Then remove the driver module. Then
++ exit minicom. I expect an "oops". -- REW */
++
++static void scc_hungup(void *ptr)
++{
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: hungup ...\n");
++#endif
++ scc_disable_tx_interrupts(ptr);
++ scc_disable_rx_interrupts(ptr);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: hungup done\n");
++#endif
++}
++
++
++static void scc_close(void *ptr)
++{
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: close ...\n");
++#endif
++ scc_disable_tx_interrupts(ptr);
++ scc_disable_rx_interrupts(ptr);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: close done\n");
++#endif
++}
++
++
++/*---------------------------------------------------------------------------
++ * Internal support functions
++ *--------------------------------------------------------------------------*/
++
++static void scc_setsignals(struct scc_port *port, int dtr, int rts)
++{
++ unsigned long flags;
++ unsigned char t;
++ SCC_ACCESS_INIT(port);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: setsignals dtr %d rts %d...\n", dtr, rts);
++#endif
++ local_irq_save(flags);
++ t = SCCread(TX_CTRL_REG);
++ if (dtr >= 0) t = dtr? (t | TCR_DTR): (t & ~TCR_DTR);
++ if (rts >= 0) t = rts? (t | TCR_RTS): (t & ~TCR_RTS);
++ SCCwrite(TX_CTRL_REG, t);
++ local_irq_restore(flags);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: setsignals done\n");
++#endif
++}
++
++
++static void scc_send_xchar(struct tty_struct *tty, char ch)
++{
++ struct scc_port *port = (struct scc_port *)tty->driver_data;
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: send_xchar ...\n");
++#endif
++ port->x_char = ch;
++ if (ch)
++ scc_enable_tx_interrupts(port);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: send_xchar done\n");
++#endif
++}
++
++
++/*---------------------------------------------------------------------------
++ * Driver entrypoints referenced from above
++ *--------------------------------------------------------------------------*/
++
++static int scc_open (struct tty_struct * tty, struct file * filp)
++{
++ int line = tty->index;
++ int retval;
++ struct scc_port *port = &scc_ports[line];
++ int i, channel = port->channel;
++ unsigned long flags;
++
++ if (atari_SCC_init_done && line == 1)
++ return -ENODEV;
++
++ SCC_ACCESS_INIT(port);
++
++ static const struct {
++ unsigned reg, val;
++ } scc_init_tab[] = {
++ /* no parity, 1 stop bit, async, 1:16 */
++ { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x64 },
++ /* parity error is special cond, ints disabled, no DMA */
++ { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB },
++ /* Rx 8 bits/char, no auto enable, Rx off */
++ { RX_CTRL_REG, RCR_CHSIZE_8 },
++ /* DTR off, Tx 8 bits/char, RTS off, Tx off */
++ { TX_CTRL_REG, TCR_CHSIZE_8 },
++ /* special features off */
++ { AUX2_CTRL_REG, 0 },
++ /* RTxC is XTAL, TRxC is input, both clocks = RTxC */
++ { CLK_CTRL_REG, CCR_TRxCOUT_XTAL | CCR_TXCLK_RTxC | CCR_RXCLK_RTxC },
++ { DPLL_CTRL_REG, 0 },
++ /* Start Rx */
++ { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 },
++ /* Start Tx */
++ { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 },
++ /* Ext/Stat ints: CTS, DCD, SYNC (DSR) */
++ { INT_CTRL_REG, ICR_ENAB_DCD_INT | ICR_ENAB_CTS_INT | ICR_ENAB_SYNC_INT },
++ /* Reset Ext/Stat ints */
++ { COMMAND_REG, CR_EXTSTAT_RESET },
++ /* ...again */
++ { COMMAND_REG, CR_EXTSTAT_RESET },
++ /* Rx int always, TX int off, Ext/Stat int on */
++ { INT_AND_DMA_REG, IDR_EXTSTAT_INT_ENAB |
++ IDR_PARERR_AS_SPCOND | IDR_RX_INT_ALL }
++ };
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: open port ...\n");
++#endif
++ if (!(port->gs.flags & ASYNC_INITIALIZED)) {
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: init port ...\n");
++#endif
++ local_irq_save(flags);
++
++ SCCmod( MASTER_INT_CTRL, 0x3f,
++ channel == 0 ? MIC_CH_A_RESET : MIC_CH_B_RESET );
++ udelay(40); /* extra delay after a reset */
++
++ for (i=0; i<sizeof(scc_init_tab)/sizeof(*scc_init_tab); ++i)
++ SCCwrite(scc_init_tab[i].reg, scc_init_tab[i].val);
++
++
++ /* remember status register for detection of DCD and CTS changes */
++ scc_last_status_reg[channel] = SCCread(STATUS_REG);
++
++ port->c_dcd = 0; /* Prevent initial 1->0 interrupt */
++ scc_setsignals (port, 1,1);
++ local_irq_restore(flags);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: init port done!\n");
++#endif
++ }
++
++ tty->driver_data = port;
++ port->gs.tty = tty;
++ port->gs.count++;
++#ifdef DEBUG
++ printk(KERN_WARNING "SCC: gs init port ...\n");
++#endif
++ retval = gs_init_port(&port->gs);
++ if (retval) {
++ port->gs.count--;
++ return retval;
++ }
++#ifdef DEBUG
++ printk(KERN_WARNING "SCC: gs init port done!\n");
++#endif
++ port->gs.flags |= GS_ACTIVE;
++
++#ifdef DEBUG
++ printk(KERN_WARNING "SCC: gs wait ready ...\n");
++#endif
++ retval = gs_block_til_ready(port, filp);
++#ifdef DEBUG
++ printk(KERN_WARNING "SCC: gs wait ready done!\n");
++#endif
++ if (retval) {
++ port->gs.count--;
++ return retval;
++ }
++
++ port->c_dcd = scc_get_CD (port);
++
++#ifdef DEBUG
++ printk(KERN_WARNING "SCC: enable rx ints ...\n");
++#endif
++ scc_enable_rx_interrupts(port);
++#ifdef DEBUG
++ printk(KERN_WARNING "SCC: enable rx ints done!\n");
++
++ printk(KERN_INFO "SCC: open port done!\n");
++#endif
++ return 0;
++}
++
++
++static void scc_throttle (struct tty_struct * tty)
++{
++ struct scc_port *port = (struct scc_port *)tty->driver_data;
++ unsigned long flags;
++ SCC_ACCESS_INIT(port);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: throttle ...\n");
++#endif
++ if (tty->termios->c_cflag & CRTSCTS) {
++ local_irq_save(flags);
++ SCCmod(TX_CTRL_REG, ~TCR_RTS, 0);
++ local_irq_restore(flags);
++ }
++ if (I_IXOFF(tty))
++ scc_send_xchar(tty, STOP_CHAR(tty));
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: throttle done!\n");
++#endif
++}
++
++
++static void scc_unthrottle (struct tty_struct * tty)
++{
++ struct scc_port *port = (struct scc_port *)tty->driver_data;
++ unsigned long flags;
++ SCC_ACCESS_INIT(port);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: unthrottle ...\n");
++#endif
++ if (tty->termios->c_cflag & CRTSCTS) {
++ local_irq_save(flags);
++ SCCmod(TX_CTRL_REG, 0xff, TCR_RTS);
++ local_irq_restore(flags);
++ }
++ if (I_IXOFF(tty))
++ scc_send_xchar(tty, START_CHAR(tty));
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: unthrottle done!\n");
++#endif
++}
++
++
++static int scc_ioctl(struct tty_struct *tty, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct scc_port *port = (struct scc_port *) tty->driver_data;
++ int retval;
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: ioctl! cmd %d, arg %p \n", cmd, arg);
++#endif
++ //if (serial_paranoia_check(info, tty->device, "zs_ioctl"))
++ // return -ENODEV;
++
++ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
++ (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
++ (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
++ if (tty->flags & (1 << TTY_IO_ERROR))
++ return -EIO;
++ }
++
++ switch (cmd) {
++ case TCSBRK: /* SVID version: non-zero arg --> no break */
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: ioctl TCSBRK\n");
++#endif
++ retval = tty_check_change(tty);
++ if (retval)
++ return retval;
++ tty_wait_until_sent(tty, 0);
++ //if (!arg)
++ // send_break(info, HZ/4); /* 1/4 second */
++ return 0;
++ case TCSBRKP: /* support for POSIX tcsendbreak() */
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: ioctl TCSBRKP\n");
++#endif
++ retval = tty_check_change(tty);
++ if (retval)
++ return retval;
++ tty_wait_until_sent(tty, 0);
++ //send_break(info, arg ? arg*(HZ/10) : HZ/4);
++ return 0;
++ case TIOCGSOFTCAR:
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: ioctl TIOCGSOFTCAR\n");
++#endif
++ if (put_user(C_CLOCAL(tty) ? 1 : 0,
++ (unsigned long *) arg))
++ return -EFAULT;
++ return 0;
++ case TIOCSSOFTCAR:
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: ioctl TIOCSSOFTCAR\n");
++#endif
++ if (get_user(arg, (unsigned long *) arg))
++ return -EFAULT;
++ tty->termios->c_cflag =
++ ((tty->termios->c_cflag & ~CLOCAL) |
++ (arg ? CLOCAL : 0));
++ return 0;
++ case TIOCMGET:
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: ioctl TIOCMGET\n");
++#endif
++ //return get_modem_info(info, (unsigned int *) arg);
++ return 0;
++ case TIOCMBIS:
++ case TIOCMBIC:
++ case TIOCMSET:
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: ioctl TIOCMSET\n");
++#endif
++ //return set_modem_info(info, cmd, (unsigned int *) arg);
++ return 0;
++ case TIOCGSERIAL:
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: ioctl TIOCGSERIAL\n");
++#endif
++ return 0;
++ //return get_serial_info(info,
++ // (struct serial_struct *) arg);
++ case TIOCSSERIAL:
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: ioctl TIOCSSERIAL\n");
++#endif
++ return 0;
++ //return set_serial_info(info,
++ // (struct serial_struct *) arg);
++ case TIOCSERGETLSR: /* Get line status register */
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: ioctl TIOCSERGETLSR\n");
++#endif
++ return 0;
++ //return get_lsr_info(info, (unsigned int *) arg);
++
++ case TIOCSERGSTRUCT:
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: ioctl TIOCSERGSTRUCT\n");
++#endif
++ return 0;
++ if (copy_to_user((struct scc_port *) arg,
++ port, sizeof(struct scc_port)))
++ return -EFAULT;
++ return 0;
++
++ default:
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: ioctl default\n");
++#endif
++ return -ENOIOCTLCMD;
++ }
++ return 0;
++
++ return -ENOIOCTLCMD;
++}
++
++
++static void scc_break_ctl(struct tty_struct *tty, int break_state)
++{
++ struct scc_port *port = (struct scc_port *)tty->driver_data;
++ unsigned long flags;
++ SCC_ACCESS_INIT(port);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: break ctl ...\n");
++#endif
++ local_irq_save(flags);
++ SCCmod(TX_CTRL_REG, ~TCR_SEND_BREAK,
++ break_state ? TCR_SEND_BREAK : 0);
++ local_irq_restore(flags);
++#ifdef DEBUG
++ printk(KERN_INFO "SCC: break ctl done!\n");
++#endif
++}
++
++
++/*---------------------------------------------------------------------------
++ * Serial console stuff...
++ *--------------------------------------------------------------------------*/
++#if 1
++#define scc_delay() \
++ do { \
++ __asm__ __volatile__ ( "tstb %0" : : "g" (*scc_del) : "cc" );\
++ } while (0)
++
++#define SCC_WRITE(reg,val) \
++ do { \
++ scc.cha_b_ctrl = (reg); \
++ scc_delay(); \
++ scc.cha_b_ctrl = (val); \
++ scc_delay(); \
++ } while(0)
++
++/* loops_per_jiffy isn't initialized yet, so we can't use udelay(). This does a
++ * delay of ~ 60us. */
++#define LONG_DELAY() \
++ do { \
++ int i; \
++ for( i = 100; i > 0; --i ) \
++ scc_delay(); \
++ } while(0)
++
++static void atari_init_scc_port( int cflag )
++{
++ extern int atari_SCC_reset_done;
++ static int clksrc_table[9] =
++ /* reg 11: 0x50 = BRG, 0x00 = RTxC, 0x28 = TRxC */
++ { 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x00, 0x00 };
++ static int brgsrc_table[9] =
++ /* reg 14: 0 = RTxC, 2 = PCLK */
++ { 2, 2, 2, 2, 2, 2, 0, 2, 2 };
++ static int clkmode_table[9] =
++ /* reg 4: 0x40 = x16, 0x80 = x32, 0xc0 = x64 */
++ { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x80 };
++ static int div_table[9] =
++ /* reg12 (BRG low) */
++ { 208, 138, 103, 50, 24, 11, 1, 0, 0 };
++
++ int baud = cflag & CBAUD;
++ int clksrc, clkmode, div, reg3, reg5;
++
++ scc_del = &mfp.par_dt_reg;
++
++ if (cflag & CBAUDEX)
++ baud += B38400;
++ if (baud < B1200 || baud > B38400+2)
++ baud = B9600; /* use default 9600bps for non-implemented rates */
++ baud -= B1200; /* tables starts at 1200bps */
++
++ clksrc = clksrc_table[baud];
++ clkmode = clkmode_table[baud];
++ div = div_table[baud];
++ if (ATARIHW_PRESENT(TT_MFP) && baud >= 6) {
++ /* special treatment for TT, where rates >= 38400 are done via TRxC */
++ clksrc = 0x28; /* TRxC */
++ clkmode = baud == 6 ? 0xc0 :
++ baud == 7 ? 0x80 : /* really 76800bps */
++ 0x40; /* really 153600bps */
++ div = 0;
++ }
++
++ reg3 = (cflag & CSIZE) == CS8 ? 0xc0 : 0x40;
++ reg5 = (cflag & CSIZE) == CS8 ? 0x60 : 0x20 | 0x82 /* assert DTR/RTS */;
++
++ (void)scc.cha_b_ctrl; /* reset reg pointer */
++ SCC_WRITE( 9, 0xc0 ); /* reset */
++ LONG_DELAY(); /* extra delay after WR9 access */
++ SCC_WRITE( 4, (cflag & PARENB) ? ((cflag & PARODD) ? 0x01 : 0x03) : 0 |
++ 0x04 /* 1 stopbit */ |
++ clkmode );
++ SCC_WRITE( 3, reg3 );
++ SCC_WRITE( 5, reg5 );
++ SCC_WRITE( 9, 0 ); /* no interrupts */
++ LONG_DELAY(); /* extra delay after WR9 access */
++ SCC_WRITE( 10, 0 ); /* NRZ mode */
++ SCC_WRITE( 11, clksrc ); /* main clock source */
++ SCC_WRITE( 12, div ); /* BRG value */
++ SCC_WRITE( 13, 0 ); /* BRG high byte */
++ SCC_WRITE( 14, brgsrc_table[baud] );
++ SCC_WRITE( 14, brgsrc_table[baud] | (div ? 1 : 0) );
++ SCC_WRITE( 3, reg3 | 1 );
++ SCC_WRITE( 5, reg5 | 8 );
++
++ atari_SCC_reset_done = 1;
++ atari_SCC_init_done = 1;
++}
++
++static void scc_ch_write (char ch)
++{
++ volatile char *p = NULL;
++
++ if (MACH_IS_TT || MACH_IS_FALCON)
++ p = (volatile char *)&scc.cha_b_ctrl;
++
++ if (MACH_IS_ST)
++ p = (volatile char *)&scc.cha_b_ctrl;
++
++ if (MACH_IS_STE)
++ p = (volatile char *)&st_escc.cha_b_ctrl;
++
++ do {
++ scc_delay();
++ }
++ while (!(*p & 4));
++ // scc_delay();
++ // *p = 8;
++ scc_delay();
++ *(p+1) = ch;
++}
++
++/* The console must be locked when we get here. */
++
++static void scc_console_write (struct console *co, const char *str, unsigned count)
++{
++ unsigned long flags;
++
++ //printk("scc_console_write: %s\n", str);
++ local_irq_save(flags);
++
++ while (count--)
++ {
++ if (*str == '\n')
++ scc_ch_write ('\r');
++ scc_ch_write (*str++);
++ }
++ local_irq_restore(flags);
++ //printk("scc_console_write done!\n");
++}
++
++static struct tty_driver *scc_console_device(struct console *c, int *index)
++{
++ *index = c->index;
++ return scc_driver;
++}
++
++
++static int __init scc_console_setup(struct console *co, char *options)
++{
++ printk("scc_console_setup: initializing SCC port B\n");
++ atari_init_scc_port(B9600|CS8);
++ printk("scc_console_setup: done!\n");
++ return 0;
++}
++
++
++static struct console sercons = {
++ .name = "ttyS",
++ .write = scc_console_write,
++ .device = scc_console_device,
++ .setup = scc_console_setup,
++ .flags = CON_PRINTBUFFER,
++ .index = -1,
++};
++
++
++static int __init vme_scc_console_init(void)
++{
++ if (MACH_IS_TT ||
++ MACH_IS_ST ||
++ MACH_IS_FALCON)
++ register_console(&sercons);
++ return 0;
++}
++
++console_initcall(vme_scc_console_init);
++#endif
++
++/***************************** End of Functions *********************/
++
++MODULE_AUTHOR("Michael Schmitz");
++MODULE_DESCRIPTION("Atari Amd8350 SCC serial driver");
++MODULE_LICENSE("GPL");
Added: dists/sid/linux-2.6/debian/patches/m68k-atari-video.patch
==============================================================================
--- (empty file)
+++ dists/sid/linux-2.6/debian/patches/m68k-atari-video.patch Mon Nov 20 00:58:24 2006
@@ -0,0 +1,3978 @@
+diff -urN linux-m68k/drivers/video/Kconfig linux-schmitz/drivers/video/Kconfig
+--- linux-m68k/drivers/video/Kconfig 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/drivers/video/Kconfig 2006-11-19 21:37:27.000000000 +0100
+@@ -404,7 +404,10 @@
+
+ config FB_ATARI
+ bool "Atari native chipset support"
+- depends on (FB = y) && ATARI && BROKEN
++ depends on (FB = y) && ATARI
++ select FB_CFB_FILLRECT
++ select FB_CFB_COPYAREA
++ select FB_CFB_IMAGEBLIT
+ help
+ This is the frame buffer device driver for the builtin graphics
+ chipset found in Ataris.
+diff -urN linux-m68k/drivers/video/Makefile linux-schmitz/drivers/video/Makefile
+--- linux-m68k/drivers/video/Makefile 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/drivers/video/Makefile 2006-11-19 21:37:27.000000000 +0100
+@@ -64,7 +64,8 @@
+ obj-$(CONFIG_FB_LEO) += leo.o sbuslib.o
+ obj-$(CONFIG_FB_SGIVW) += sgivwfb.o
+ obj-$(CONFIG_FB_ACORN) += acornfb.o
+-obj-$(CONFIG_FB_ATARI) += atafb.o
++obj-$(CONFIG_FB_ATARI) += atafb.o c2p.o atafb_mfb.o \
++ atafb_iplan2p2.o atafb_iplan2p4.o atafb_iplan2p8.o
+ obj-$(CONFIG_FB_MAC) += macfb.o
+ obj-$(CONFIG_FB_HGA) += hgafb.o
+ obj-$(CONFIG_FB_IGA) += igafb.o
+diff -urN linux-m68k/drivers/video/atafb.c linux-schmitz/drivers/video/atafb.c
+--- linux-m68k/drivers/video/atafb.c 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/drivers/video/atafb.c 2006-11-19 21:37:27.000000000 +0100
+@@ -53,6 +53,7 @@
+ #include <linux/errno.h>
+ #include <linux/string.h>
+ #include <linux/mm.h>
++#include <linux/tty.h>
+ #include <linux/slab.h>
+ #include <linux/delay.h>
+ #include <linux/init.h>
+@@ -71,14 +72,11 @@
+ #include <linux/fb.h>
+ #include <asm/atarikb.h>
+
+-#include <video/fbcon.h>
+-#include <video/fbcon-cfb8.h>
+-#include <video/fbcon-cfb16.h>
+-#include <video/fbcon-iplan2p2.h>
+-#include <video/fbcon-iplan2p4.h>
+-#include <video/fbcon-iplan2p8.h>
+-#include <video/fbcon-mfb.h>
+-
++#include "c2p.h" // FIXME: rewrite for iplan2p
++#include "atafb_mfb.h"
++#include "atafb_iplan2p2.h"
++#include "atafb_iplan2p4.h"
++#include "atafb_iplan2p8.h"
+
+ #define SWITCH_ACIA 0x01 /* modes for switch on OverScan */
+ #define SWITCH_SND6 0x40
+@@ -88,6 +86,27 @@
+
+ #define up(x, r) (((x) + (r) - 1) & ~((r)-1))
+
++ /*
++ * Interface to the world
++ */
++
++static int atafb_check_var(struct fb_var_screeninfo *var,
++ struct fb_info *info);
++static int atafb_set_par(struct fb_info *info);
++static int atafb_setcolreg(unsigned regno, unsigned red, unsigned green,
++ unsigned blue, unsigned transp,
++ struct fb_info *info);
++static int atafb_blank(int blank, struct fb_info *info);
++static int atafb_pan_display(struct fb_var_screeninfo *var,
++ struct fb_info *info);
++static void atafb_fillrect(struct fb_info *info,
++ const struct fb_fillrect *rect);
++static void atafb_copyarea(struct fb_info *info,
++ const struct fb_copyarea *region);
++static void atafb_imageblit(struct fb_info *info,
++ const struct fb_image *image);
++static int atafb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg);
++
+
+ static int default_par=0; /* default resolution (0=none) */
+
+@@ -101,9 +120,15 @@
+ static int sttt_xres_virtual=640,sttt_yres_virtual=400;
+ static int ovsc_offset=0, ovsc_addlen=0;
+
++ /*
++ * Hardware parameters for current mode
++ */
++
+ static struct atafb_par {
+ void *screen_base;
+ int yres_virtual;
++ u_long next_line;
++ u_long next_plane;
+ #if defined ATAFB_TT || defined ATAFB_STE
+ union {
+ struct {
+@@ -136,6 +161,8 @@
+ } hw;
+ } current_par;
+
++struct fb_var_screeninfo current_var;
++
+ /* Don't calculate an own resolution, and thus don't change the one found when
+ * booting (currently used for the Falcon to keep settings for internal video
+ * hardware extensions (e.g. ScreenBlaster) */
+@@ -164,7 +191,13 @@
+ #define VMO_PREMASK 0x0c
+ #endif
+
+-static struct fb_info fb_info;
++static struct fb_info fb_info = {
++ .fix = {
++ .id = "Atari ",
++ .visual = FB_VISUAL_PSEUDOCOLOR,
++ .accel = FB_ACCEL_NONE
++ }
++};
+
+ static void *screen_base; /* base address of screen */
+ static void *real_screen_base; /* (only for Overscan) */
+@@ -175,8 +208,6 @@
+
+ static int mono_moni=0;
+
+-static struct display disp;
+-
+
+ #ifdef ATAFB_EXT
+ /* external video handling */
+@@ -250,6 +281,75 @@
+ extern int fontwidth_8x16;
+ extern unsigned char fontdata_8x16[];
+
++/*
++ * struct fb_ops {
++ * * open/release and usage marking
++ * struct module *owner;
++ * int (*fb_open)(struct fb_info *info, int user);
++ * int (*fb_release)(struct fb_info *info, int user);
++ *
++ * * For framebuffers with strange non linear layouts or that do not
++ * * work with normal memory mapped access
++ * ssize_t (*fb_read)(struct file *file, char __user *buf, size_t count, loff_t *ppos);
++ * ssize_t (*fb_write)(struct file *file, const char __user *buf, size_t count, loff_t *ppos);
++ *
++ * * checks var and eventually tweaks it to something supported,
++ * * DOES NOT MODIFY PAR *
++ * int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
++ *
++ * * set the video mode according to info->var *
++ * int (*fb_set_par)(struct fb_info *info);
++ *
++ * * set color register *
++ * int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
++ * unsigned blue, unsigned transp, struct fb_info *info);
++ *
++ * * set color registers in batch *
++ * int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
++ *
++ * * blank display *
++ * int (*fb_blank)(int blank, struct fb_info *info);
++ *
++ * * pan display *
++ * int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
++ *
++ * *** The meat of the drawing engine ***
++ * * Draws a rectangle *
++ * void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
++ * * Copy data from area to another *
++ * void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
++ * * Draws a image to the display *
++ * void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
++ *
++ * * Draws cursor *
++ * int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
++ *
++ * * Rotates the display *
++ * void (*fb_rotate)(struct fb_info *info, int angle);
++ *
++ * * wait for blit idle, optional *
++ * int (*fb_sync)(struct fb_info *info);
++ *
++ * * perform fb specific ioctl (optional) *
++ * int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,
++ * unsigned long arg);
++ *
++ * * Handle 32bit compat ioctl (optional) *
++ * int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,
++ * unsigned long arg);
++ *
++ * * perform fb specific mmap *
++ * int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
++ *
++ * * save current hardware state *
++ * void (*fb_save_state)(struct fb_info *info);
++ *
++ * * restore saved state *
++ * void (*fb_restore_state)(struct fb_info *info);
++ * } ;
++ */
++
++
+ /* ++roman: This structure abstracts from the underlying hardware (ST(e),
+ * TT, or Falcon.
+ *
+@@ -263,6 +363,7 @@
+ * struct atafb_par *par )
+ * This function should fill in the 'fix' structure based on the
+ * values in the 'par' structure.
++ * !!! Obsolete, perhaps !!!
+ *
+ * int (*decode_var)( struct fb_var_screeninfo *var,
+ * struct atafb_par *par )
+@@ -279,6 +380,7 @@
+ *
+ * void (*get_par)( struct atafb_par *par )
+ * Fill the hardware's 'par' structure.
++ * !!! Used only by detect() !!!
+ *
+ * void (*set_par)( struct atafb_par *par )
+ * Set the hardware according to 'par'.
+@@ -417,6 +519,124 @@
+
+ static int num_atafb_predefined=ARRAY_SIZE(atafb_predefined);
+
++ /*
++ * Tags used to indicate a specific Pixel Clock
++ *
++ * tag is the shift value to get the timings in 35 ns units
++ */
++
++enum { TAG_SHRES, TAG_HIRES, TAG_LORES };
++
++
++static struct fb_videomode atafb_modedb[] __initdata = {
++
++ /*
++ * Atari Video Modes
++ *
++ * If you change these, make sure to update DEFMODE_* as well!
++ */
++
++ {
++ /* 640x200, 15 kHz, 60 Hz (NTSC) */
++ "ntsc", 60, 640, 200, TAG_HIRES, 106, 86, 44, 16, 76, 2,
++ FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 640x400, 15 kHz, 60 Hz interlaced (NTSC) */
++ "ntsc-lace", 60, 640, 400, TAG_HIRES, 106, 86, 88, 33, 76, 4,
++ FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 640x256, 15 kHz, 50 Hz (PAL) */
++ "pal", 50, 640, 256, TAG_HIRES, 106, 86, 40, 14, 76, 2,
++ FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 640x512, 15 kHz, 50 Hz interlaced (PAL) */
++ "pal-lace", 50, 640, 512, TAG_HIRES, 106, 86, 80, 29, 76, 4,
++ FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 640x480, 29 kHz, 57 Hz */
++ "multiscan", 57, 640, 480, TAG_SHRES, 96, 112, 29, 8, 72, 8,
++ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 640x960, 29 kHz, 57 Hz interlaced */
++ "multiscan-lace", 57, 640, 960, TAG_SHRES, 96, 112, 58, 16, 72, 16,
++ 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 640x200, 15 kHz, 72 Hz */
++ "euro36", 72, 640, 200, TAG_HIRES, 92, 124, 6, 6, 52, 5,
++ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 640x400, 15 kHz, 72 Hz interlaced */
++ "euro36-lace", 72, 640, 400, TAG_HIRES, 92, 124, 12, 12, 52, 10,
++ 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 640x400, 29 kHz, 68 Hz */
++ "euro72", 68, 640, 400, TAG_SHRES, 164, 92, 9, 9, 80, 8,
++ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 640x800, 29 kHz, 68 Hz interlaced */
++ "euro72-lace", 68, 640, 800, TAG_SHRES, 164, 92, 18, 18, 80, 16,
++ 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 800x300, 23 kHz, 70 Hz */
++ "super72", 70, 800, 300, TAG_SHRES, 212, 140, 10, 11, 80, 7,
++ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 800x600, 23 kHz, 70 Hz interlaced */
++ "super72-lace", 70, 800, 600, TAG_SHRES, 212, 140, 20, 22, 80, 14,
++ 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 640x200, 27 kHz, 57 Hz doublescan */
++ "dblntsc", 57, 640, 200, TAG_SHRES, 196, 124, 18, 17, 80, 4,
++ 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
++ }, {
++ /* 640x400, 27 kHz, 57 Hz */
++ "dblntsc-ff", 57, 640, 400, TAG_SHRES, 196, 124, 36, 35, 80, 7,
++ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 640x800, 27 kHz, 57 Hz interlaced */
++ "dblntsc-lace", 57, 640, 800, TAG_SHRES, 196, 124, 72, 70, 80, 14,
++ 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 640x256, 27 kHz, 47 Hz doublescan */
++ "dblpal", 47, 640, 256, TAG_SHRES, 196, 124, 14, 13, 80, 4,
++ 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
++ }, {
++ /* 640x512, 27 kHz, 47 Hz */
++ "dblpal-ff", 47, 640, 512, TAG_SHRES, 196, 124, 28, 27, 80, 7,
++ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 640x1024, 27 kHz, 47 Hz interlaced */
++ "dblpal-lace", 47, 640, 1024, TAG_SHRES, 196, 124, 56, 54, 80, 14,
++ 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
++ },
++
++ /*
++ * VGA Video Modes
++ */
++
++ {
++ /* 640x480, 31 kHz, 60 Hz (VGA) */
++ "vga", 60, 640, 480, TAG_SHRES, 64, 96, 30, 9, 112, 2,
++ 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
++ }, {
++ /* 640x400, 31 kHz, 70 Hz (VGA) */
++ "vga70", 70, 640, 400, TAG_SHRES, 64, 96, 35, 12, 112, 2,
++ FB_SYNC_VERT_HIGH_ACT | FB_SYNC_COMP_HIGH_ACT, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
++ },
++
++};
++
++#define NUM_TOTAL_MODES ARRAY_SIZE(atafb_modedb)
++
++static char *mode_option __initdata = NULL;
++
++ /* default modes */
++
++#define DEFMODE_TT 2 /* "pal" for PAL OCS/ECS */
++#define DEFMODE_F30 0 /* "ntsc" for NTSC OCS/ECS */
++#define DEFMODE_STE 3 /* "pal-lace" for flicker fixed PAL (A3000) */
++#define DEFMODE_EXT 1 /* "ntsc-lace" for flicker fixed NTSC (A3000) */
++
+
+ static int
+ get_video_mode(char *vname)
+@@ -1290,6 +1510,15 @@
+ par->screen_base = screen_base + var->yoffset * linelen;
+ par->hw.falcon.xoffset = 0;
+
++ // FIXME!!! sort of works, no crash
++ //par->next_line = linelen;
++ //par->next_plane = yres_virtual * linelen;
++ par->next_line = linelen;
++ par->next_plane = 2;
++ // crashes
++ //par->next_plane = linelen;
++ //par->next_line = yres_virtual * linelen;
++
+ return 0;
+ }
+
+@@ -1372,6 +1601,8 @@
+ else {
+ var->red.offset=0;
+ var->red.length = hw->ste_mode ? 4 : 6;
++ if (var->red.length > var->bits_per_pixel)
++ var->red.length = var->bits_per_pixel;
+ var->red.msb_right=0;
+ var->grayscale=0;
+ var->blue=var->green=var->red;
+@@ -1583,10 +1814,12 @@
+
+
+ static int falcon_pan_display( struct fb_var_screeninfo *var,
+- struct atafb_par *par )
++ struct fb_info *info )
+ {
++ struct atafb_par *par=(struct atafb_par *)info->par;
++
+ int xoffset;
+- int bpp = fb_display[fb_info.currcon].var.bits_per_pixel;
++ int bpp = info->var.bits_per_pixel;
+
+ if (bpp == 1)
+ var->xoffset = up(var->xoffset, 32);
+@@ -1597,13 +1830,13 @@
+ var->xoffset = up(var->xoffset, 2);
+ }
+ par->hw.falcon.line_offset = bpp *
+- (fb_display[fb_info.currcon].var.xres_virtual - fb_display[fb_info.currcon].var.xres) / 16;
++ (info->var.xres_virtual - info->var.xres) / 16;
+ if (par->hw.falcon.xoffset)
+ par->hw.falcon.line_offset -= bpp;
+ xoffset = var->xoffset - par->hw.falcon.xoffset;
+
+ par->screen_base = screen_base +
+- (var->yoffset * fb_display[fb_info.currcon].var.xres_virtual + xoffset) * bpp / 8;
++ (var->yoffset * info->var.xres_virtual + xoffset) * bpp / 8;
+ if (fbhw->set_screen_base)
+ fbhw->set_screen_base (par->screen_base);
+ else
+@@ -2294,15 +2527,17 @@
+
+
+ static int pan_display( struct fb_var_screeninfo *var,
+- struct atafb_par *par )
++ struct fb_info *info )
+ {
++ struct atafb_par *par=(struct atafb_par *)info->par;
++
+ if (!fbhw->set_screen_base ||
+ (!ATARIHW_PRESENT(EXTD_SHIFTER) && var->xoffset))
+ return -EINVAL;
+ var->xoffset = up(var->xoffset, 16);
+ par->screen_base = screen_base +
+- (var->yoffset * fb_display[fb_info.currcon].var.xres_virtual + var->xoffset)
+- * fb_display[fb_info.currcon].var.bits_per_pixel / 8;
++ (var->yoffset * info->var.xres_virtual + var->xoffset)
++ * info->var.bits_per_pixel / 8;
+ fbhw->set_screen_base (par->screen_base);
+ return 0;
+ }
+@@ -2344,7 +2579,7 @@
+
+
+
+-static void atafb_get_par( struct atafb_par *par )
++static void ata_get_par( struct atafb_par *par )
+ {
+ if (current_par_valid) {
+ *par=current_par;
+@@ -2354,7 +2589,7 @@
+ }
+
+
+-static void atafb_set_par( struct atafb_par *par )
++static void ata_set_par( struct atafb_par *par )
+ {
+ fbhw->set_par(par);
+ current_par=*par;
+@@ -2371,10 +2606,10 @@
+ /* used for hardware scrolling */
+
+ static int
+-fb_update_var(int con, struct fb_info *info)
++fb_update_var(struct fb_info *info)
+ {
+- int off=fb_display[con].var.yoffset*fb_display[con].var.xres_virtual*
+- fb_display[con].var.bits_per_pixel>>3;
++ int off=info->var.yoffset*info->var.xres_virtual*
++ info->var.bits_per_pixel>>3;
+
+ current_par.screen_base=screen_base + off;
+
+@@ -2392,106 +2627,82 @@
+ return err;
+ activate=var->activate;
+ if (((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive)
+- atafb_set_par(&par);
++ ata_set_par(&par);
+ fbhw->encode_var(var, &par);
+ var->activate=activate;
+ return 0;
+ }
+
+ static int
+-atafb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
++atafb_get_fix(struct fb_fix_screeninfo *fix, struct fb_info *info)
+ {
+ struct atafb_par par;
+- if (con == -1)
+- atafb_get_par(&par);
+- else {
+ int err;
+- if ((err=fbhw->decode_var(&fb_display[con].var,&par)))
++ // Get fix directly from hw (case con == -1 before) ??
++ if ((err=fbhw->decode_var(&info->var,&par)))
+ return err;
+- }
+ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+ return fbhw->encode_fix(fix, &par);
+ }
+
+ static int
+-atafb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++atafb_get_var(struct fb_var_screeninfo *var, struct fb_info *info)
+ {
+ struct atafb_par par;
+- if (con == -1) {
+- atafb_get_par(&par);
++
++ ata_get_par(&par);
+ fbhw->encode_var(var, &par);
+- }
+- else
+- *var=fb_display[con].var;
++
+ return 0;
+ }
+
++// No longer called by fbcon!
++// Still called by set_var internally
++
+ static void
+-atafb_set_disp(int con, struct fb_info *info)
++atafb_set_disp(struct fb_info *info)
+ {
+- struct fb_fix_screeninfo fix;
+- struct fb_var_screeninfo var;
+- struct display *display;
++ atafb_get_var(&info->var, info);
++ atafb_get_fix(&info->fix, info);
+
+- if (con >= 0)
+- display = &fb_display[con];
+- else
+- display = &disp; /* used during initialization */
++ info->screen_base = (void *)info->fix.smem_start;
+
+- atafb_get_fix(&fix, con, info);
+- atafb_get_var(&var, con, info);
+- if (con == -1)
+- con=0;
+- info->screen_base = (void *)fix.smem_start;
+- display->visual = fix.visual;
+- display->type = fix.type;
+- display->type_aux = fix.type_aux;
+- display->ypanstep = fix.ypanstep;
+- display->ywrapstep = fix.ywrapstep;
+- display->line_length = fix.line_length;
+- if (fix.visual != FB_VISUAL_PSEUDOCOLOR &&
+- fix.visual != FB_VISUAL_DIRECTCOLOR)
+- display->can_soft_blank = 0;
+- else
+- display->can_soft_blank = 1;
+- display->inverse =
+- (fix.visual == FB_VISUAL_MONO01 ? !inverse : inverse);
+- switch (fix.type) {
++ switch (info->fix.type) {
+ case FB_TYPE_INTERLEAVED_PLANES:
+- switch (var.bits_per_pixel) {
++ switch (info->var.bits_per_pixel) {
+ #ifdef FBCON_HAS_IPLAN2P2
+ case 2:
+- display->dispsw = &fbcon_iplan2p2;
++ // display->dispsw = &fbcon_iplan2p2;
+ break;
+ #endif
+ #ifdef FBCON_HAS_IPLAN2P4
+ case 4:
+- display->dispsw = &fbcon_iplan2p4;
++ // display->dispsw = &fbcon_iplan2p4;
+ break;
+ #endif
+ #ifdef FBCON_HAS_IPLAN2P8
+ case 8:
+- display->dispsw = &fbcon_iplan2p8;
++ // display->dispsw = &fbcon_iplan2p8;
+ break;
+ #endif
+ }
+ break;
+ case FB_TYPE_PACKED_PIXELS:
+- switch (var.bits_per_pixel) {
++ switch (info->var.bits_per_pixel) {
+ #ifdef FBCON_HAS_MFB
+ case 1:
+- display->dispsw = &fbcon_mfb;
++ // display->dispsw = &fbcon_mfb;
+ break;
+ #endif
+ #ifdef FBCON_HAS_CFB8
+ case 8:
+- display->dispsw = &fbcon_cfb8;
++ // display->dispsw = &fbcon_cfb8;
+ break;
+ #endif
+ #ifdef FBCON_HAS_CFB16
+ case 16:
+- display->dispsw = &fbcon_cfb16;
+- display->dispsw_data = fbcon_cfb16_cmap;
++ // display->dispsw = &fbcon_cfb16;
++ // display->dispsw_data = fbcon_cfb16_cmap;
+ break;
+ #endif
+ }
+@@ -2504,71 +2715,881 @@
+ {
+ int err,oldxres,oldyres,oldbpp,oldxres_virtual,
+ oldyres_virtual,oldyoffset;
+- if ((err=do_fb_set_var(var, con==info->currcon)))
++ if ((err=do_fb_set_var(var, 1)))
+ return err;
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+- oldxres=fb_display[con].var.xres;
+- oldyres=fb_display[con].var.yres;
+- oldxres_virtual=fb_display[con].var.xres_virtual;
+- oldyres_virtual=fb_display[con].var.yres_virtual;
+- oldbpp=fb_display[con].var.bits_per_pixel;
+- oldyoffset=fb_display[con].var.yoffset;
+- fb_display[con].var=*var;
++ oldxres=info->var.xres;
++ oldyres=info->var.yres;
++ oldxres_virtual=info->var.xres_virtual;
++ oldyres_virtual=info->var.yres_virtual;
++ oldbpp=info->var.bits_per_pixel;
++ oldyoffset=info->var.yoffset;
++ info->var=*var;
+ if (oldxres != var->xres || oldyres != var->yres
+ || oldxres_virtual != var->xres_virtual
+ || oldyres_virtual != var->yres_virtual
+ || oldbpp != var->bits_per_pixel
+ || oldyoffset != var->yoffset) {
+- atafb_set_disp(con, info);
+- (*fb_info.changevar)(con);
+- fb_alloc_cmap(&fb_display[con].cmap, 0, 0);
+- do_install_cmap(con, info);
++ atafb_set_disp(info);
+ }
+ }
+ var->activate=0;
+ return 0;
+ }
+
+-
+-
+-static int
+-atafb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
++static int atafb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
++ u_int transp, struct fb_info *info)
+ {
+- if (con == info->currcon) /* current console ? */
+- return fb_get_cmap(cmap, kspc, fbhw->getcolreg, info);
+- else
+- if (fb_display[con].cmap.len) /* non default colormap ? */
+- fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+- else
+- fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+- cmap, kspc ? 0 : 2);
+- return 0;
++ red >>= 8;
++ green >>= 8;
++ blue >>= 8;
++ return info->fbops->fb_setcolreg(regno, red, green, blue, transp, info);
+ }
+
+ static int
+-atafb_pan_display(struct fb_var_screeninfo *var, int con, struct fb_info *info)
++atafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+ {
+ int xoffset = var->xoffset;
+ int yoffset = var->yoffset;
+ int err;
+
+- if ( xoffset < 0 || xoffset + fb_display[con].var.xres > fb_display[con].var.xres_virtual
+- || yoffset < 0 || yoffset + fb_display[con].var.yres > fb_display[con].var.yres_virtual)
++ if (var->vmode & FB_VMODE_YWRAP) {
++ if (var->yoffset < 0 ||
++ var->yoffset >= info->var.yres_virtual || var->xoffset)
+ return -EINVAL;
++ } else {
++ if (var->xoffset+info->var.xres > info->var.xres_virtual ||
++ var->yoffset+info->var.yres > info->var.yres_virtual)
++ return -EINVAL;
++ }
+
+- if (con == info->currcon) {
+ if (fbhw->pan_display) {
+- if ((err = fbhw->pan_display(var, ¤t_par)))
++ if ((err = fbhw->pan_display(var, info)))
+ return err;
+ }
+ else
+ return -EINVAL;
+- }
+- fb_display[con].var.xoffset = var->xoffset;
+- fb_display[con].var.yoffset = var->yoffset;
++
++ info->var.xoffset = var->xoffset;
++ info->var.yoffset = var->yoffset;
++
++ if (var->vmode & FB_VMODE_YWRAP)
++ info->var.vmode |= FB_VMODE_YWRAP;
++ else
++ info->var.vmode &= ~FB_VMODE_YWRAP;
++
+ return 0;
+ }
+
++#if BITS_PER_LONG == 32
++#define BYTES_PER_LONG 4
++#define SHIFT_PER_LONG 5
++#elif BITS_PER_LONG == 64
++#define BYTES_PER_LONG 8
++#define SHIFT_PER_LONG 6
++#else
++#define Please update me
++#endif
++
++
++ /*
++ * Compose two values, using a bitmask as decision value
++ * This is equivalent to (a & mask) | (b & ~mask)
++ */
++
++static inline unsigned long comp(unsigned long a, unsigned long b,
++ unsigned long mask)
++{
++ return ((a ^ b) & mask) ^ b;
++}
++
++
++static inline unsigned long xor(unsigned long a, unsigned long b,
++ unsigned long mask)
++{
++ return (a & mask) ^ b;
++}
++
++
++ /*
++ * Unaligned forward bit copy using 32-bit or 64-bit memory accesses
++ */
++
++static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
++ int src_idx, u32 n)
++{
++ unsigned long first, last;
++ int shift = dst_idx-src_idx, left, right;
++ unsigned long d0, d1;
++ int m;
++
++ if (!n)
++ return;
++
++ shift = dst_idx-src_idx;
++ first = ~0UL >> dst_idx;
++ last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
++
++ if (!shift) {
++ // Same alignment for source and dest
++
++ if (dst_idx+n <= BITS_PER_LONG) {
++ // Single word
++ if (last)
++ first &= last;
++ *dst = comp(*src, *dst, first);
++ } else {
++ // Multiple destination words
++ // Leading bits
++ if (first) {
++ *dst = comp(*src, *dst, first);
++ dst++;
++ src++;
++ n -= BITS_PER_LONG-dst_idx;
++ }
++
++ // Main chunk
++ n /= BITS_PER_LONG;
++ while (n >= 8) {
++ *dst++ = *src++;
++ *dst++ = *src++;
++ *dst++ = *src++;
++ *dst++ = *src++;
++ *dst++ = *src++;
++ *dst++ = *src++;
++ *dst++ = *src++;
++ *dst++ = *src++;
++ n -= 8;
++ }
++ while (n--)
++ *dst++ = *src++;
++
++ // Trailing bits
++ if (last)
++ *dst = comp(*src, *dst, last);
++ }
++ } else {
++ // Different alignment for source and dest
++
++ right = shift & (BITS_PER_LONG-1);
++ left = -shift & (BITS_PER_LONG-1);
++
++ if (dst_idx+n <= BITS_PER_LONG) {
++ // Single destination word
++ if (last)
++ first &= last;
++ if (shift > 0) {
++ // Single source word
++ *dst = comp(*src >> right, *dst, first);
++ } else if (src_idx+n <= BITS_PER_LONG) {
++ // Single source word
++ *dst = comp(*src << left, *dst, first);
++ } else {
++ // 2 source words
++ d0 = *src++;
++ d1 = *src;
++ *dst = comp(d0 << left | d1 >> right, *dst,
++ first);
++ }
++ } else {
++ // Multiple destination words
++ d0 = *src++;
++ // Leading bits
++ if (shift > 0) {
++ // Single source word
++ *dst = comp(d0 >> right, *dst, first);
++ dst++;
++ n -= BITS_PER_LONG-dst_idx;
++ } else {
++ // 2 source words
++ d1 = *src++;
++ *dst = comp(d0 << left | d1 >> right, *dst,
++ first);
++ d0 = d1;
++ dst++;
++ n -= BITS_PER_LONG-dst_idx;
++ }
++
++ // Main chunk
++ m = n % BITS_PER_LONG;
++ n /= BITS_PER_LONG;
++ while (n >= 4) {
++ d1 = *src++;
++ *dst++ = d0 << left | d1 >> right;
++ d0 = d1;
++ d1 = *src++;
++ *dst++ = d0 << left | d1 >> right;
++ d0 = d1;
++ d1 = *src++;
++ *dst++ = d0 << left | d1 >> right;
++ d0 = d1;
++ d1 = *src++;
++ *dst++ = d0 << left | d1 >> right;
++ d0 = d1;
++ n -= 4;
++ }
++ while (n--) {
++ d1 = *src++;
++ *dst++ = d0 << left | d1 >> right;
++ d0 = d1;
++ }
++
++ // Trailing bits
++ if (last) {
++ if (m <= right) {
++ // Single source word
++ *dst = comp(d0 << left, *dst, last);
++ } else {
++ // 2 source words
++ d1 = *src;
++ *dst = comp(d0 << left | d1 >> right,
++ *dst, last);
++ }
++ }
++ }
++ }
++}
++
++
++ /*
++ * Unaligned reverse bit copy using 32-bit or 64-bit memory accesses
++ */
++
++static void bitcpy_rev(unsigned long *dst, int dst_idx,
++ const unsigned long *src, int src_idx, u32 n)
++{
++ unsigned long first, last;
++ int shift = dst_idx-src_idx, left, right;
++ unsigned long d0, d1;
++ int m;
++
++ if (!n)
++ return;
++
++ dst += (n-1)/BITS_PER_LONG;
++ src += (n-1)/BITS_PER_LONG;
++ if ((n-1) % BITS_PER_LONG) {
++ dst_idx += (n-1) % BITS_PER_LONG;
++ dst += dst_idx >> SHIFT_PER_LONG;
++ dst_idx &= BITS_PER_LONG-1;
++ src_idx += (n-1) % BITS_PER_LONG;
++ src += src_idx >> SHIFT_PER_LONG;
++ src_idx &= BITS_PER_LONG-1;
++ }
++
++ shift = dst_idx-src_idx;
++ first = ~0UL << (BITS_PER_LONG-1-dst_idx);
++ last = ~(~0UL << (BITS_PER_LONG-1-((dst_idx-n) % BITS_PER_LONG)));
++
++ if (!shift) {
++ // Same alignment for source and dest
++
++ if ((unsigned long)dst_idx+1 >= n) {
++ // Single word
++ if (last)
++ first &= last;
++ *dst = comp(*src, *dst, first);
++ } else {
++ // Multiple destination words
++ // Leading bits
++ if (first) {
++ *dst = comp(*src, *dst, first);
++ dst--;
++ src--;
++ n -= dst_idx+1;
++ }
++
++ // Main chunk
++ n /= BITS_PER_LONG;
++ while (n >= 8) {
++ *dst-- = *src--;
++ *dst-- = *src--;
++ *dst-- = *src--;
++ *dst-- = *src--;
++ *dst-- = *src--;
++ *dst-- = *src--;
++ *dst-- = *src--;
++ *dst-- = *src--;
++ n -= 8;
++ }
++ while (n--)
++ *dst-- = *src--;
++
++ // Trailing bits
++ if (last)
++ *dst = comp(*src, *dst, last);
++ }
++ } else {
++ // Different alignment for source and dest
++
++ right = shift & (BITS_PER_LONG-1);
++ left = -shift & (BITS_PER_LONG-1);
++
++ if ((unsigned long)dst_idx+1 >= n) {
++ // Single destination word
++ if (last)
++ first &= last;
++ if (shift < 0) {
++ // Single source word
++ *dst = comp(*src << left, *dst, first);
++ } else if (1+(unsigned long)src_idx >= n) {
++ // Single source word
++ *dst = comp(*src >> right, *dst, first);
++ } else {
++ // 2 source words
++ d0 = *src--;
++ d1 = *src;
++ *dst = comp(d0 >> right | d1 << left, *dst,
++ first);
++ }
++ } else {
++ // Multiple destination words
++ d0 = *src--;
++ // Leading bits
++ if (shift < 0) {
++ // Single source word
++ *dst = comp(d0 << left, *dst, first);
++ dst--;
++ n -= dst_idx+1;
++ } else {
++ // 2 source words
++ d1 = *src--;
++ *dst = comp(d0 >> right | d1 << left, *dst,
++ first);
++ d0 = d1;
++ dst--;
++ n -= dst_idx+1;
++ }
++
++ // Main chunk
++ m = n % BITS_PER_LONG;
++ n /= BITS_PER_LONG;
++ while (n >= 4) {
++ d1 = *src--;
++ *dst-- = d0 >> right | d1 << left;
++ d0 = d1;
++ d1 = *src--;
++ *dst-- = d0 >> right | d1 << left;
++ d0 = d1;
++ d1 = *src--;
++ *dst-- = d0 >> right | d1 << left;
++ d0 = d1;
++ d1 = *src--;
++ *dst-- = d0 >> right | d1 << left;
++ d0 = d1;
++ n -= 4;
++ }
++ while (n--) {
++ d1 = *src--;
++ *dst-- = d0 >> right | d1 << left;
++ d0 = d1;
++ }
++
++ // Trailing bits
++ if (last) {
++ if (m <= left) {
++ // Single source word
++ *dst = comp(d0 >> right, *dst, last);
++ } else {
++ // 2 source words
++ d1 = *src;
++ *dst = comp(d0 >> right | d1 << left,
++ *dst, last);
++ }
++ }
++ }
++ }
++}
++
++
++ /*
++ * Unaligned forward inverting bit copy using 32-bit or 64-bit memory
++ * accesses
++ */
++
++static void bitcpy_not(unsigned long *dst, int dst_idx,
++ const unsigned long *src, int src_idx, u32 n)
++{
++ unsigned long first, last;
++ int shift = dst_idx-src_idx, left, right;
++ unsigned long d0, d1;
++ int m;
++
++ if (!n)
++ return;
++
++ shift = dst_idx-src_idx;
++ first = ~0UL >> dst_idx;
++ last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
++
++ if (!shift) {
++ // Same alignment for source and dest
++
++ if (dst_idx+n <= BITS_PER_LONG) {
++ // Single word
++ if (last)
++ first &= last;
++ *dst = comp(~*src, *dst, first);
++ } else {
++ // Multiple destination words
++ // Leading bits
++ if (first) {
++ *dst = comp(~*src, *dst, first);
++ dst++;
++ src++;
++ n -= BITS_PER_LONG-dst_idx;
++ }
++
++ // Main chunk
++ n /= BITS_PER_LONG;
++ while (n >= 8) {
++ *dst++ = ~*src++;
++ *dst++ = ~*src++;
++ *dst++ = ~*src++;
++ *dst++ = ~*src++;
++ *dst++ = ~*src++;
++ *dst++ = ~*src++;
++ *dst++ = ~*src++;
++ *dst++ = ~*src++;
++ n -= 8;
++ }
++ while (n--)
++ *dst++ = ~*src++;
++
++ // Trailing bits
++ if (last)
++ *dst = comp(~*src, *dst, last);
++ }
++ } else {
++ // Different alignment for source and dest
++
++ right = shift & (BITS_PER_LONG-1);
++ left = -shift & (BITS_PER_LONG-1);
++
++ if (dst_idx+n <= BITS_PER_LONG) {
++ // Single destination word
++ if (last)
++ first &= last;
++ if (shift > 0) {
++ // Single source word
++ *dst = comp(~*src >> right, *dst, first);
++ } else if (src_idx+n <= BITS_PER_LONG) {
++ // Single source word
++ *dst = comp(~*src << left, *dst, first);
++ } else {
++ // 2 source words
++ d0 = ~*src++;
++ d1 = ~*src;
++ *dst = comp(d0 << left | d1 >> right, *dst,
++ first);
++ }
++ } else {
++ // Multiple destination words
++ d0 = ~*src++;
++ // Leading bits
++ if (shift > 0) {
++ // Single source word
++ *dst = comp(d0 >> right, *dst, first);
++ dst++;
++ n -= BITS_PER_LONG-dst_idx;
++ } else {
++ // 2 source words
++ d1 = ~*src++;
++ *dst = comp(d0 << left | d1 >> right, *dst,
++ first);
++ d0 = d1;
++ dst++;
++ n -= BITS_PER_LONG-dst_idx;
++ }
++
++ // Main chunk
++ m = n % BITS_PER_LONG;
++ n /= BITS_PER_LONG;
++ while (n >= 4) {
++ d1 = ~*src++;
++ *dst++ = d0 << left | d1 >> right;
++ d0 = d1;
++ d1 = ~*src++;
++ *dst++ = d0 << left | d1 >> right;
++ d0 = d1;
++ d1 = ~*src++;
++ *dst++ = d0 << left | d1 >> right;
++ d0 = d1;
++ d1 = ~*src++;
++ *dst++ = d0 << left | d1 >> right;
++ d0 = d1;
++ n -= 4;
++ }
++ while (n--) {
++ d1 = ~*src++;
++ *dst++ = d0 << left | d1 >> right;
++ d0 = d1;
++ }
++
++ // Trailing bits
++ if (last) {
++ if (m <= right) {
++ // Single source word
++ *dst = comp(d0 << left, *dst, last);
++ } else {
++ // 2 source words
++ d1 = ~*src;
++ *dst = comp(d0 << left | d1 >> right,
++ *dst, last);
++ }
++ }
++ }
++ }
++}
++
++
++ /*
++ * Unaligned 32-bit pattern fill using 32/64-bit memory accesses
++ */
++
++static void bitfill32(unsigned long *dst, int dst_idx, u32 pat, u32 n)
++{
++ unsigned long val = pat;
++ unsigned long first, last;
++
++ if (!n)
++ return;
++
++#if BITS_PER_LONG == 64
++ val |= val << 32;
++#endif
++
++ first = ~0UL >> dst_idx;
++ last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
++
++ if (dst_idx+n <= BITS_PER_LONG) {
++ // Single word
++ if (last)
++ first &= last;
++ *dst = comp(val, *dst, first);
++ } else {
++ // Multiple destination words
++ // Leading bits
++ if (first) {
++ *dst = comp(val, *dst, first);
++ dst++;
++ n -= BITS_PER_LONG-dst_idx;
++ }
++
++ // Main chunk
++ n /= BITS_PER_LONG;
++ while (n >= 8) {
++ *dst++ = val;
++ *dst++ = val;
++ *dst++ = val;
++ *dst++ = val;
++ *dst++ = val;
++ *dst++ = val;
++ *dst++ = val;
++ *dst++ = val;
++ n -= 8;
++ }
++ while (n--)
++ *dst++ = val;
++
++ // Trailing bits
++ if (last)
++ *dst = comp(val, *dst, last);
++ }
++}
++
++
++ /*
++ * Unaligned 32-bit pattern xor using 32/64-bit memory accesses
++ */
++
++static void bitxor32(unsigned long *dst, int dst_idx, u32 pat, u32 n)
++{
++ unsigned long val = pat;
++ unsigned long first, last;
++
++ if (!n)
++ return;
++
++#if BITS_PER_LONG == 64
++ val |= val << 32;
++#endif
++
++ first = ~0UL >> dst_idx;
++ last = ~(~0UL >> ((dst_idx+n) % BITS_PER_LONG));
++
++ if (dst_idx+n <= BITS_PER_LONG) {
++ // Single word
++ if (last)
++ first &= last;
++ *dst = xor(val, *dst, first);
++ } else {
++ // Multiple destination words
++ // Leading bits
++ if (first) {
++ *dst = xor(val, *dst, first);
++ dst++;
++ n -= BITS_PER_LONG-dst_idx;
++ }
++
++ // Main chunk
++ n /= BITS_PER_LONG;
++ while (n >= 4) {
++ *dst++ ^= val;
++ *dst++ ^= val;
++ *dst++ ^= val;
++ *dst++ ^= val;
++ n -= 4;
++ }
++ while (n--)
++ *dst++ ^= val;
++
++ // Trailing bits
++ if (last)
++ *dst = xor(val, *dst, last);
++ }
++}
++
++static inline void fill_one_line(int bpp, unsigned long next_plane,
++ unsigned long *dst, int dst_idx, u32 n,
++ u32 color)
++{
++ while (1) {
++ dst += dst_idx >> SHIFT_PER_LONG;
++ dst_idx &= (BITS_PER_LONG-1);
++ bitfill32(dst, dst_idx, color & 1 ? ~0 : 0, n);
++ if (!--bpp)
++ break;
++ color >>= 1;
++ dst_idx += next_plane*8;
++ }
++}
++
++static inline void xor_one_line(int bpp, unsigned long next_plane,
++ unsigned long *dst, int dst_idx, u32 n,
++ u32 color)
++{
++ while (color) {
++ dst += dst_idx >> SHIFT_PER_LONG;
++ dst_idx &= (BITS_PER_LONG-1);
++ bitxor32(dst, dst_idx, color & 1 ? ~0 : 0, n);
++ if (!--bpp)
++ break;
++ color >>= 1;
++ dst_idx += next_plane*8;
++ }
++}
++
++
++static void atafb_fillrect(struct fb_info *info,
++ const struct fb_fillrect *rect)
++{
++ struct atafb_par *par = (struct atafb_par *)info->par;
++ int dst_idx, x2, y2;
++ unsigned long *dst;
++ u32 width, height;
++
++ if (!rect->width || !rect->height)
++ return;
++
++ /*
++ * We could use hardware clipping but on many cards you get around
++ * hardware clipping by writing to framebuffer directly.
++ * */
++ x2 = rect->dx + rect->width;
++ y2 = rect->dy + rect->height;
++ x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
++ y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
++ width = x2 - rect->dx;
++ height = y2 - rect->dy;
++
++ if (info->var.bits_per_pixel == 1)
++ atafb_mfb_fillrect(info, par->next_line, rect->color,
++ rect->dy, rect->dx, height, width);
++ else if (info->var.bits_per_pixel == 2)
++ atafb_iplan2p2_fillrect(info, par->next_line, rect->color,
++ rect->dy, rect->dx, height, width);
++ else if (info->var.bits_per_pixel == 4)
++ atafb_iplan2p4_fillrect(info, par->next_line, rect->color,
++ rect->dy, rect->dx, height, width);
++ else
++ atafb_iplan2p8_fillrect(info, par->next_line, rect->color,
++ rect->dy, rect->dx, height, width);
++
++ return;
++}
++
++static inline void copy_one_line(int bpp, unsigned long next_plane,
++ unsigned long *dst, int dst_idx,
++ unsigned long *src, int src_idx, u32 n)
++{
++ while (1) {
++ dst += dst_idx >> SHIFT_PER_LONG;
++ dst_idx &= (BITS_PER_LONG-1);
++ src += src_idx >> SHIFT_PER_LONG;
++ src_idx &= (BITS_PER_LONG-1);
++ bitcpy(dst, dst_idx, src, src_idx, n);
++ if (!--bpp)
++ break;
++ dst_idx += next_plane*8;
++ src_idx += next_plane*8;
++ }
++}
++
++static inline void copy_one_line_rev(int bpp, unsigned long next_plane,
++ unsigned long *dst, int dst_idx,
++ unsigned long *src, int src_idx, u32 n)
++{
++ while (1) {
++ dst += dst_idx >> SHIFT_PER_LONG;
++ dst_idx &= (BITS_PER_LONG-1);
++ src += src_idx >> SHIFT_PER_LONG;
++ src_idx &= (BITS_PER_LONG-1);
++ bitcpy_rev(dst, dst_idx, src, src_idx, n);
++ if (!--bpp)
++ break;
++ dst_idx += next_plane*8;
++ src_idx += next_plane*8;
++ }
++}
++
++
++static void atafb_copyarea(struct fb_info *info,
++ const struct fb_copyarea *area)
++{
++ struct atafb_par *par = (struct atafb_par *)info->par;
++ int x2, y2;
++ u32 dx, dy, sx, sy, width, height;
++ unsigned long *dst, *src;
++ int dst_idx, src_idx;
++ int rev_copy = 0;
++
++ /* clip the destination */
++ x2 = area->dx + area->width;
++ y2 = area->dy + area->height;
++ dx = area->dx > 0 ? area->dx : 0;
++ dy = area->dy > 0 ? area->dy : 0;
++ x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
++ y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
++ width = x2 - dx;
++ height = y2 - dy;
++
++ /* update sx,sy */
++ sx = area->sx + (dx - area->dx);
++ sy = area->sy + (dy - area->dy);
++
++ /* the source must be completely inside the virtual screen */
++ if (sx < 0 || sy < 0 || (sx + width) > info->var.xres_virtual ||
++ (sy + height) > info->var.yres_virtual)
++ return;
++
++ if (dy > sy || (dy == sy && dx > sx)) {
++ dy += height;
++ sy += height;
++ rev_copy = 1;
++ }
++
++ if (info->var.bits_per_pixel == 1)
++ atafb_mfb_copyarea(info, par->next_line, sy, sx, dy, dx, height, width);
++ else if (info->var.bits_per_pixel == 2)
++ atafb_iplan2p2_copyarea(info, par->next_line, sy, sx, dy, dx, height, width);
++ else if (info->var.bits_per_pixel == 4)
++ atafb_iplan2p4_copyarea(info, par->next_line, sy, sx, dy, dx, height, width);
++ else
++ atafb_iplan2p8_copyarea(info, par->next_line, sy, sx, dy, dx, height, width);
++
++ return;
++}
++
++
++static inline void expand_one_line(int bpp, unsigned long next_plane,
++ unsigned long *dst, int dst_idx, u32 n,
++ const u8 *data, u32 bgcolor, u32 fgcolor)
++{
++ const unsigned long *src;
++ int src_idx;
++
++ while (1) {
++ dst += dst_idx >> SHIFT_PER_LONG;
++ dst_idx &= (BITS_PER_LONG-1);
++ if ((bgcolor ^ fgcolor) & 1) {
++ src = (unsigned long *)((unsigned long)data & ~(BYTES_PER_LONG-1));
++ src_idx = ((unsigned long)data & (BYTES_PER_LONG-1))*8;
++ if (fgcolor & 1)
++ bitcpy(dst, dst_idx, src, src_idx, n);
++ else
++ bitcpy_not(dst, dst_idx, src, src_idx, n);
++ /* set or clear */
++ } else
++ bitfill32(dst, dst_idx, fgcolor & 1 ? ~0 : 0, n);
++ if (!--bpp)
++ break;
++ bgcolor >>= 1;
++ fgcolor >>= 1;
++ dst_idx += next_plane*8;
++ }
++}
++
++
++static void atafb_imageblit(struct fb_info *info, const struct fb_image *image)
++{
++ struct atafb_par *par = (struct atafb_par *)info->par;
++ int x2, y2;
++ unsigned long *dst;
++ int dst_idx;
++ const char *src;
++ u32 dx, dy, width, height, pitch;
++
++ /*
++ * We could use hardware clipping but on many cards you get around
++ * hardware clipping by writing to framebuffer directly like we are
++ * doing here.
++ */
++ x2 = image->dx + image->width;
++ y2 = image->dy + image->height;
++ dx = image->dx;
++ dy = image->dy;
++ x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
++ y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
++ width = x2 - dx;
++ height = y2 - dy;
++
++ if (image->depth == 1) {
++ // used for font data
++ dst = (unsigned long *)
++ ((unsigned long)info->screen_base & ~(BYTES_PER_LONG-1));
++ dst_idx = ((unsigned long)info->screen_base & (BYTES_PER_LONG-1))*8;
++ dst_idx += dy*par->next_line*8+dx;
++ src = image->data;
++ pitch = (image->width+7)/8;
++ while (height--) {
++
++ if (info->var.bits_per_pixel == 1)
++ atafb_mfb_linefill(info, par->next_line,
++ dy, dx, width, src,
++ image->bg_color, image->fg_color);
++ else if (info->var.bits_per_pixel == 2)
++ atafb_iplan2p2_linefill(info, par->next_line,
++ dy, dx, width, src,
++ image->bg_color, image->fg_color);
++ else if (info->var.bits_per_pixel == 4)
++ atafb_iplan2p4_linefill(info, par->next_line,
++ dy, dx, width, src,
++ image->bg_color, image->fg_color);
++ else
++ atafb_iplan2p8_linefill(info, par->next_line,
++ dy, dx, width, src,
++ image->bg_color, image->fg_color);
++ dy++;
++ src += pitch;
++ }
++ } else {
++ // only used for logo
++ c2p(info->screen_base, image->data, dx, dy, width, height,
++ par->next_line, par->next_plane, image->width,
++ info->var.bits_per_pixel);
++ }
++}
+ static int
+ atafb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
+ {
+@@ -2585,7 +3606,7 @@
+ if (copy_from_user((void *)¤t_par, (void *)arg,
+ sizeof(struct atafb_par)))
+ return -EFAULT;
+- atafb_set_par(¤t_par);
++ ata_set_par(¤t_par);
+ return 0;
+ #endif
+ }
+@@ -2614,22 +3635,65 @@
+ cmap.transp=NULL;
+ cmap.start=0;
+ cmap.len=16;
+- fb_set_cmap(&cmap, 1, info);
++ fb_set_cmap(&cmap, info);
+ }
++#if 0
+ else
+- do_install_cmap(info->currcon, info);
++ do_install_cmap(info);
++#endif
++ return 0;
++}
++
++ /*
++ * New fbcon interface ...
++ */
++
++ /* check var by decoding var into hw par, rounding if necessary,
++ * then encoding hw par back into new, validated var */
++static int atafb_check_var(struct fb_var_screeninfo *var,
++ struct fb_info *info)
++{
++ int err;
++ struct atafb_par par;
++
++ /* Validate wanted screen parameters */
++ if ((err = fbhw->decode_var(var, &par)))
++ return err;
++
++ /* Encode (possibly rounded) screen parameters */
++ fbhw->encode_var(var, &par);
++ return 0;
++}
++
++ /* actually set hw par by decoding var, then setting hardware from
++ * hw par just decoded */
++static int atafb_set_par(struct fb_info *info)
++{
++ struct atafb_par *par = (struct atafb_par *)info->par;
++
++ /* Decode wanted screen parameters */
++ fbhw->decode_var(&info->var, par);
++ fbhw->encode_fix(&info->fix, par);
++
++ /* Set new videomode */
++ ata_set_par(par);
++
+ return 0;
+ }
+
++
++
++
+ static struct fb_ops atafb_ops = {
+ .owner = THIS_MODULE,
+- .fb_get_fix = atafb_get_fix,
+- .fb_get_var = atafb_get_var,
+- .fb_set_var = atafb_set_var,
+- .fb_get_cmap = atafb_get_cmap,
+- .fb_set_cmap = gen_set_cmap,
+- .fb_pan_display =atafb_pan_display,
++ .fb_check_var = atafb_check_var,
++ .fb_set_par = atafb_set_par,
++ .fb_setcolreg = atafb_setcolreg,
+ .fb_blank = atafb_blank,
++ .fb_pan_display = atafb_pan_display,
++ .fb_fillrect = atafb_fillrect,
++ .fb_copyarea = atafb_copyarea,
++ .fb_imageblit = atafb_imageblit,
+ .fb_ioctl = atafb_ioctl,
+ };
+
+@@ -2674,17 +3738,19 @@
+ default_mem_req=min_mem;
+ }
+
++// Obsolete
+ static int
+ atafb_switch(int con, struct fb_info *info)
+ {
++#if 0
+ /* Do we have to save the colormap ? */
+- if (fb_display[info->currcon].cmap.len)
+- fb_get_cmap(&fb_display[info->currcon].cmap, 1, fbhw->getcolreg,
++ if (info->cmap.len)
++ fb_get_cmap(info->cmap, 1, fbhw->getcolreg,
+ info);
+- do_fb_set_var(&fb_display[con].var,1);
+- info->currcon=con;
++#endif
++ do_fb_set_var(&info->var,1);
+ /* Install new colormap */
+- do_install_cmap(con, info);
++ // do_install_cmap(info);
+ return 0;
+ }
+
+@@ -2692,40 +3758,59 @@
+ {
+ int pad;
+ int detected_mode;
++ unsigned int defmode;
+ unsigned long mem_req;
+
++#ifndef MODULE
++ char *option = NULL;
++
++ if (fb_get_options("atafb", &option)) {
++ return -ENODEV;
++ }
++ atafb_setup(option);
++#endif
++ printk("atafb_init: start\n");
++
+ if (!MACH_IS_ATARI)
+ return -ENXIO;
+
+ do {
+ #ifdef ATAFB_EXT
+ if (external_addr) {
++ printk("atafb_init: initializing external hw\n");
+ fbhw = &ext_switch;
+ atafb_ops.fb_setcolreg = &ext_setcolreg;
++ defmode = DEFMODE_EXT;
+ break;
+ }
+ #endif
+ #ifdef ATAFB_TT
+ if (ATARIHW_PRESENT(TT_SHIFTER)) {
++ printk("atafb_init: initializing TT hw\n");
+ fbhw = &tt_switch;
+ atafb_ops.fb_setcolreg = &tt_setcolreg;
++ defmode = DEFMODE_TT;
+ break;
+ }
+ #endif
+ #ifdef ATAFB_FALCON
+ if (ATARIHW_PRESENT(VIDEL_SHIFTER)) {
++ printk("atafb_init: initializing Falcon hw\n");
+ fbhw = &falcon_switch;
+ atafb_ops.fb_setcolreg = &falcon_setcolreg;
+ request_irq(IRQ_AUTO_4, falcon_vbl_switcher, IRQ_TYPE_PRIO,
+ "framebuffer/modeswitch", falcon_vbl_switcher);
++ defmode = DEFMODE_F30;
+ break;
+ }
+ #endif
+ #ifdef ATAFB_STE
+ if (ATARIHW_PRESENT(STND_SHIFTER) ||
+ ATARIHW_PRESENT(EXTD_SHIFTER)) {
++ printk("atafb_init: initializing ST/E hw\n");
+ fbhw = &st_switch;
+ atafb_ops.fb_setcolreg = &stste_setcolreg;
++ defmode = DEFMODE_STE;
+ break;
+ }
+ fbhw = &st_switch;
+@@ -2770,6 +3855,8 @@
+ kernel_set_cachemode(screen_base, screen_len,
+ IOMAP_WRITETHROUGH);
+ }
++ printk("atafb: screen_base %p real_screen_base %p screen_len %ld\n",
++ screen_base, real_screen_base, screen_len);
+ #ifdef ATAFB_EXT
+ }
+ else {
+@@ -2789,32 +3876,46 @@
+ }
+ #endif /* ATAFB_EXT */
+
+- strcpy(fb_info.modename, "Atari Builtin ");
+- fb_info.changevar = NULL;
++// strcpy(fb_info.mode->name, "Atari Builtin ");
+ fb_info.fbops = &atafb_ops;
+- fb_info.disp = &disp;
+- fb_info.currcon = -1;
+- fb_info.switch_con = &atafb_switch;
+- fb_info.updatevar = &fb_update_var;
+- fb_info.flags = FBINFO_FLAG_DEFAULT;
++ // try to set default (detected; requested) var
+ do_fb_set_var(&atafb_predefined[default_par-1], 1);
+- strcat(fb_info.modename, fb_var_names[default_par-1][0]);
+-
+- atafb_get_var(&disp.var, -1, &fb_info);
+- atafb_set_disp(-1, &fb_info);
+- do_install_cmap(0, &fb_info);
++ // reads hw state into current par, which may not be sane yet
++ ata_get_par(¤t_par);
++ fb_info.par = ¤t_par;
++ // tries to read from HW which may not be initialized yet
++ // so set sane var first, then call atafb_set_par
++ atafb_get_var(¤t_var, &fb_info);
++ fb_info.var = current_var;
++ fb_info.flags = FBINFO_FLAG_DEFAULT;
+
+- if (register_framebuffer(&fb_info) < 0)
++ if (!fb_find_mode(&fb_info.var, &fb_info, mode_option, atafb_modedb,
++ NUM_TOTAL_MODES, &atafb_modedb[defmode],
++ current_var.bits_per_pixel)) {
+ return -EINVAL;
++ }
++
++// strcat(fb_info.mode->name, fb_var_names[default_par-1][0]);
++
++ atafb_set_disp(&fb_info);
++
++ fb_alloc_cmap(&(fb_info.cmap), 1<<fb_info.var.bits_per_pixel, 0);
+
+ printk("Determined %dx%d, depth %d\n",
+- disp.var.xres, disp.var.yres, disp.var.bits_per_pixel);
+- if ((disp.var.xres != disp.var.xres_virtual) ||
+- (disp.var.yres != disp.var.yres_virtual))
++ fb_info.var.xres, fb_info.var.yres, fb_info.var.bits_per_pixel);
++ if ((current_var.xres != current_var.xres_virtual) ||
++ (current_var.yres != current_var.yres_virtual))
+ printk(" virtual %dx%d\n",
+- disp.var.xres_virtual, disp.var.yres_virtual);
+- printk("fb%d: %s frame buffer device, using %dK of video memory\n",
+- fb_info.node, fb_info.modename, screen_len>>10);
++ current_var.xres_virtual, current_var.yres_virtual);
++
++ if (register_framebuffer(&fb_info) < 0)
++ return -EINVAL;
++
++ // FIXME: mode needs setting!
++ //printk("fb%d: %s frame buffer device, using %dK of video memory\n",
++ // fb_info.node, fb_info.mode->name, screen_len>>10);
++ printk("fb%d: frame buffer device, using %dK of video memory\n",
++ fb_info.node, screen_len>>10);
+
+ /* TODO: This driver cannot be unloaded yet */
+ return 0;
+@@ -3038,19 +4139,16 @@
+ char *this_opt;
+ int temp;
+
+- fb_info.fontname[0] = '\0';
+-
+ if (!options || !*options)
+ return 0;
+
+ while ((this_opt = strsep(&options, ",")) != NULL) {
+ if (!*this_opt) continue;
+- if ((temp=get_video_mode(this_opt)))
++ if ((temp=get_video_mode(this_opt))) {
+ default_par=temp;
+- else if (! strcmp(this_opt, "inverse"))
++ mode_option = this_opt;
++ } else if (! strcmp(this_opt, "inverse"))
+ inverse=1;
+- else if (!strncmp(this_opt, "font:", 5))
+- strcpy(fb_info.fontname, this_opt+5);
+ else if (! strncmp(this_opt, "hwscroll_",9)) {
+ hwscroll=simple_strtoul(this_opt+9, NULL, 10);
+ if (hwscroll < 0)
+@@ -3086,11 +4184,14 @@
+ return 0;
+ }
+
++module_init(atafb_init);
++
+ #ifdef MODULE
+ MODULE_LICENSE("GPL");
+
+-int init_module(void)
++int cleanup_module(void)
+ {
+- return atafb_init();
++ unregister_framebuffer(&fb_info);
++ return atafb_deinit();
+ }
+ #endif /* MODULE */
+diff -urN linux-m68k/drivers/video/atafb_iplan2p2.c linux-schmitz/drivers/video/atafb_iplan2p2.c
+--- linux-m68k/drivers/video/atafb_iplan2p2.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/drivers/video/atafb_iplan2p2.c 2006-11-19 21:37:27.000000000 +0100
+@@ -0,0 +1,353 @@
++/*
++ * linux/drivers/video/iplan2p2.c -- Low level frame buffer operations for
++ * interleaved bitplanes à la Atari (2
++ * planes, 2 bytes interleave)
++ *
++ * Created 5 Apr 1997 by Geert Uytterhoeven
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ */
++
++#include <linux/module.h>
++#include <linux/tty.h>
++#include <linux/console.h>
++#include <linux/string.h>
++#include <linux/fb.h>
++
++#include <asm/byteorder.h>
++
++#ifdef __mc68000__
++#include <asm/setup.h>
++#endif
++
++#include "console/fbcon.h"
++#include "atafb_utils.h"
++#include "atafb_iplan2p2.h"
++
++
++ /*
++ * Interleaved bitplanes à la Atari (2 planes, 2 bytes interleave)
++ */
++
++/* Increment/decrement 2 plane addresses */
++
++#define INC_2P(p) do { if (!((long)(++(p)) & 1)) (p) += 2; } while(0)
++#define DEC_2P(p) do { if ((long)(--(p)) & 1) (p) -= 2; } while(0)
++
++ /* Convert a standard 4 bit color to our 2 bit color assignment:
++ * If at least two RGB channels are active, the low bit is turned on;
++ * The intensity bit (b3) is shifted into b1.
++ */
++
++static const u8 color_2p[] = { 0, 0, 0, 1, 0, 1, 1, 1, 2, 2, 2, 3, 2, 3, 3, 3 };
++#define COLOR_2P(c) color_2p[c]
++
++/* Perform the m68k movepw operation. */
++static inline void movepw(u8 *d, u16 val)
++{
++#if defined __mc68000__ && !defined CPU_M68060_ONLY
++ asm volatile ("movepw %1,%0@(0)" : : "a" (d), "d" (val));
++#else
++ d[0] = (val >> 16) & 0xff;
++ d[2] = val & 0xff;
++#endif
++}
++
++/* Sets the bytes in the visible column at d, height h, to the value
++ * val for a 2 plane screen. The bits of the color in 'color' are
++ * moved (8 times) to the respective bytes. This means:
++ *
++ * for(h times; d += bpr)
++ * *d = (color & 1) ? 0xff : 0;
++ * *(d+2) = (color & 2) ? 0xff : 0;
++ */
++
++static __inline__ void memclear_2p_col(void *d, size_t h, u16 val, int bpr)
++{
++ u8 *dd = d;
++ do {
++ movepw(dd, val);
++ dd += bpr;
++ } while (--h);
++}
++
++/* Sets a 2 plane region from 'd', length 'count' bytes, to the color
++ * in val1. 'd' has to be an even address and count must be divisible
++ * by 8, because only whole words and all planes are accessed. I.e.:
++ *
++ * for(count/4 times)
++ * *d = *(d+1) = (color & 1) ? 0xff : 0;
++ * *(d+2) = *(d+3) = (color & 2) ? 0xff : 0;
++ */
++
++static __inline__ void memset_even_2p(void *d, size_t count, u32 val)
++{
++ u32 *dd = d;
++
++ count /= 4;
++ while (count--)
++ *dd++ = val;
++}
++
++/* Copies a 2 plane column from 's', height 'h', to 'd'. */
++
++static __inline__ void memmove_2p_col (void *d, void *s, int h, int bpr)
++{
++ u8 *dd = d, *ss = s;
++
++ while (h--) {
++ dd[0] = ss[0];
++ dd[2] = ss[2];
++ dd += bpr;
++ ss += bpr;
++ }
++}
++
++
++/* This expands a 2 bit color into a short for movepw (2 plane) operations. */
++
++static const u16 two2byte[] = {
++ 0x0000, 0xff00, 0x00ff, 0xffff
++};
++
++static __inline__ u16 expand2w(u8 c)
++{
++ return two2byte[c];
++}
++
++
++/* This expands a 2 bit color into one long for a movel operation
++ * (2 planes).
++ */
++
++static const u32 two2word[] = {
++#ifndef __LITTLE_ENDIAN
++ 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
++#else
++ 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
++#endif
++};
++
++static __inline__ u32 expand2l(u8 c)
++{
++ return two2word[c];
++}
++
++
++/* This duplicates a byte 2 times into a short. */
++
++static __inline__ u16 dup2w(u8 c)
++{
++ u16 rv;
++
++ rv = c;
++ rv |= c << 8;
++ return rv;
++}
++
++
++void atafb_iplan2p2_copyarea(struct fb_info *info, u_long next_line,
++ int sy, int sx, int dy, int dx,
++ int height, int width)
++{
++ /* bmove() has to distinguish two major cases: If both, source and
++ * destination, start at even addresses or both are at odd
++ * addresses, just the first odd and last even column (if present)
++ * require special treatment (memmove_col()). The rest between
++ * then can be copied by normal operations, because all adjacent
++ * bytes are affected and are to be stored in the same order.
++ * The pathological case is when the move should go from an odd
++ * address to an even or vice versa. Since the bytes in the plane
++ * words must be assembled in new order, it seems wisest to make
++ * all movements by memmove_col().
++ */
++
++ if (sx == 0 && dx == 0 && width * 2 == next_line) {
++ /* Special (but often used) case: Moving whole lines can be
++ * done with memmove()
++ */
++ fb_memmove((u8 *)info->screen_base + dy * next_line,
++ (u8 *)info->screen_base + sy * next_line,
++ next_line * height);
++ } else {
++ int rows, cols;
++ u8 *src;
++ u8 *dst;
++ int bytes = next_line;
++ int linesize;
++ u_int colsize;
++ u_int upwards = (dy < sy) || (dy == sy && dx < sx);
++
++ linesize = bytes;
++ colsize = height;
++ if ((sx & 1) == (dx & 1)) {
++ /* odd->odd or even->even */
++
++ if (upwards) {
++ src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1);
++ dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1);
++ if (sx & 1) {
++ memmove_2p_col(dst, src, colsize, bytes);
++ src += 3;
++ dst += 3;
++ --width;
++ }
++ if (width > 1) {
++ for(rows = colsize; rows > 0; --rows) {
++ fb_memmove (dst, src, (width >> 1) * 4);
++ src += bytes;
++ dst += bytes;
++ }
++ }
++
++ if (width & 1) {
++ src -= colsize * bytes;
++ dst -= colsize * bytes;
++ memmove_2p_col(dst + (width>>1)*4, src + (width>>1)*4,
++ colsize, bytes);
++ }
++ } else {
++ if (!((sx+width-1) & 1)) {
++ src = (u8 *)info->screen_base + sy * linesize + ((sx+width-1)>>1)*4;
++ dst = (u8 *)info->screen_base + dy * linesize + ((dx+width-1)>>1)*4;
++ memmove_2p_col(dst, src, colsize, bytes);
++ --width;
++ }
++ src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1);
++ dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1);
++ if (width > 1) {
++ src += colsize * bytes + (sx & 1)*3;
++ dst += colsize * bytes + (sx & 1)*3;
++ for(rows = colsize; rows > 0; --rows) {
++ src -= bytes;
++ dst -= bytes;
++ fb_memmove (dst, src, (width>>1)*4);
++ }
++ }
++ if (width & 1)
++ memmove_2p_col(dst-3, src-3, colsize, bytes);
++ }
++ } else {
++ /* odd->even or even->odd */
++
++ if (upwards) {
++ src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1);
++ dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1);
++ for(cols = width; cols > 0; --cols) {
++ memmove_2p_col(dst, src, colsize, bytes);
++ INC_2P(src);
++ INC_2P(dst);
++ }
++ } else {
++ sx += width-1;
++ dx += width-1;
++ src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1);
++ dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1);
++ for(cols = width; cols > 0; --cols) {
++ memmove_2p_col(dst, src, colsize, bytes);
++ DEC_2P(src);
++ DEC_2P(dst);
++ }
++ }
++ }
++ }
++}
++
++void atafb_iplan2p2_fillrect(struct fb_info *info, u_long next_line, u32 color,
++ int sy, int sx, int height, int width)
++{
++ u32 offset;
++ u8 *start;
++ int rows;
++ int bytes = next_line;
++ int lines;
++ u32 size;
++ u32 cval;
++ u16 pcval;
++
++ cval = expand2l(color);
++
++ lines = height;
++
++ if (sx == 0 && width * 2 == bytes) {
++ offset = (sy * bytes);
++ size = lines * bytes;
++ memset_even_2p((u8 *)info->screen_base + offset, size, cval);
++ } else {
++ offset = sy * bytes + (sx>>1)*4 + (sx & 1);
++ start = (u8 *) info->screen_base + offset;
++ pcval = expand2w(color);
++
++ /* Clears are split if the region starts at an odd column or
++ * end at an even column. These extra columns are spread
++ * across the interleaved planes. All in between can be
++ * cleared by normal fb_memclear_small(), because both bytes of
++ * the single plane words are affected.
++ */
++
++ if (sx & 1) {
++ memclear_2p_col(start, lines, pcval, bytes);
++ start += 3;
++ width--;
++ }
++ if (width & 1) {
++ memclear_2p_col(start + (width>>1)*4, lines, pcval, bytes);
++ width--;
++ }
++ if (width)
++ for(rows = lines; rows-- ; start += bytes)
++ memset_even_2p(start, width*2, cval);
++ }
++}
++
++void atafb_iplan2p2_linefill(struct fb_info *info, u_long next_line,
++ int dy, int dx, u32 width,
++ const u8 *data, u32 bgcolor, u32 fgcolor)
++{
++ u8 *dest;
++ u8 *cdat;
++ int rows;
++ int bytes = next_line;
++ u16 eorx, fgx, bgx, fdx;
++
++ // source data
++ cdat = data;
++
++ // advance from odd to even (dx>>3) by 3: 0 1 4 5 ....
++ // dest = (p->screen_base + yy * p->next_line + (xx>>1)*4 + (xx & 1));
++ dest = (u8*) info->screen_base + dy * bytes + (dx>>4)*4 + ((dx>>3)&1);
++
++ fgx = expand2w(fgcolor);
++ bgx = expand2w(bgcolor);
++ eorx = fgx ^ bgx;
++
++ for(rows = width/8; rows-- ;) {
++ fdx = dup2w(*cdat++);
++ movepw(dest, (fdx & eorx) ^ bgx);
++ INC_2P(dest);
++ }
++
++}
++
++#ifdef MODULE
++MODULE_LICENSE("GPL");
++
++int init_module(void)
++{
++ return 0;
++}
++
++void cleanup_module(void)
++{}
++#endif /* MODULE */
++
++
++ /*
++ * Visible symbols for modules
++ */
++
++EXPORT_SYMBOL(atafb_iplan2p2_copyarea);
++EXPORT_SYMBOL(atafb_iplan2p2_fillrect);
++EXPORT_SYMBOL(atafb_iplan2p2_linefill);
+diff -urN linux-m68k/drivers/video/atafb_iplan2p2.h linux-schmitz/drivers/video/atafb_iplan2p2.h
+--- linux-m68k/drivers/video/atafb_iplan2p2.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/drivers/video/atafb_iplan2p2.h 2006-11-19 21:37:27.000000000 +0100
+@@ -0,0 +1,27 @@
++/*
++ * FBcon low-level driver for Atari interleaved bitplanes (8 planes) (iplan2p8)
++ */
++
++#ifndef _VIDEO_FBCON_IPLAN2P2_H
++#define _VIDEO_FBCON_IPLAN2P2_H
++
++#ifdef MODULE
++#if defined(CONFIG_FBCON_IPLAN2P2) || defined(CONFIG_FBCON_IPLAN2P2_MODULE)
++#define FBCON_HAS_IPLAN2P2
++#endif
++#else
++#if defined(CONFIG_FBCON_IPLAN2P2)
++#define FBCON_HAS_IPLAN2P2
++#endif
++#endif
++
++extern void atafb_iplan2p2_copyarea(struct fb_info *info, u_long next_line, int sy, int sx, int dy,
++ int dx, int height, int width);
++extern void atafb_iplan2p2_fillrect(struct fb_info *info, u_long next_line, u32 color,
++ int sy, int sx, int height, int width);
++extern void atafb_iplan2p2_linefill(struct fb_info *info, u_long next_line,
++ int dy, int dx, u32 width,
++ const u8 *data, u32 bgcolor, u32 fgcolor);
++
++
++#endif /* _VIDEO_FBCON_IPLAN2P8_H */
+diff -urN linux-m68k/drivers/video/atafb_iplan2p4.c linux-schmitz/drivers/video/atafb_iplan2p4.c
+--- linux-m68k/drivers/video/atafb_iplan2p4.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/drivers/video/atafb_iplan2p4.c 2006-11-19 21:37:27.000000000 +0100
+@@ -0,0 +1,361 @@
++/*
++ * linux/drivers/video/iplan2p4.c -- Low level frame buffer operations for
++ * interleaved bitplanes à la Atari (8
++ * planes, 2 bytes interleave)
++ *
++ * Created 5 Apr 1997 by Geert Uytterhoeven
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ */
++
++#include <linux/module.h>
++#include <linux/tty.h>
++#include <linux/console.h>
++#include <linux/string.h>
++#include <linux/fb.h>
++
++#include <asm/byteorder.h>
++
++#ifdef __mc68000__
++#include <asm/setup.h>
++#endif
++
++#include "console/fbcon.h"
++#include "atafb_utils.h"
++#include "atafb_iplan2p4.h"
++
++
++ /*
++ * Interleaved bitplanes à la Atari (4 planes, 2 bytes interleave)
++ */
++
++/* Increment/decrement 4 plane addresses */
++
++#define INC_4P(p) do { if (!((long)(++(p)) & 1)) (p) += 6; } while(0)
++#define DEC_4P(p) do { if ((long)(--(p)) & 1) (p) -= 6; } while(0)
++
++/* Perform the m68k movepl operation. */
++static inline void movepl(u8 *d, u32 val)
++{
++#if defined __mc68000__ && !defined CPU_M68060_ONLY
++ asm volatile ("movepl %1,%0@(0)"
++ : : "a" (d), "d" (val));
++#else
++ d[0] = (val >> 24) & 0xff;
++ d[2] = (val >> 16) & 0xff;
++ d[4] = (val >> 8) & 0xff;
++ d[6] = val & 0xff;
++#endif
++}
++
++/* Sets the bytes in the visible column at d, height h, to the value
++ * val for a 4 plane screen. The bits of the color in 'color' are
++ * moved (8 times) to the respective bytes. This means:
++ *
++ * for(h times; d += bpr)
++ * *d = (color & 1) ? 0xff : 0;
++ * *(d+2) = (color & 2) ? 0xff : 0;
++ * *(d+4) = (color & 4) ? 0xff : 0;
++ * *(d+6) = (color & 8) ? 0xff : 0;
++ */
++
++static __inline__ void memclear_4p_col(void *d, size_t h, u32 val, int bpr)
++{
++ u8 *dd = d;
++ do {
++ movepl(dd, val);
++ dd += bpr;
++ } while (--h);
++}
++
++/* Sets a 4 plane region from 'd', length 'count' bytes, to the color
++ * in val1/val2. 'd' has to be an even address and count must be divisible
++ * by 8, because only whole words and all planes are accessed. I.e.:
++ *
++ * for(count/8 times)
++ * *d = *(d+1) = (color & 1) ? 0xff : 0;
++ * *(d+2) = *(d+3) = (color & 2) ? 0xff : 0;
++ * *(d+4) = *(d+5) = (color & 4) ? 0xff : 0;
++ * *(d+6) = *(d+7) = (color & 8) ? 0xff : 0;
++ */
++
++static __inline__ void memset_even_4p(void *d, size_t count, u32 val1,
++ u32 val2)
++{
++ u32 *dd = d;
++
++ count /= 8;
++ while (count--) {
++ *dd++ = val1;
++ *dd++ = val2;
++ }
++}
++
++/* Copies a 4 plane column from 's', height 'h', to 'd'. */
++
++static __inline__ void memmove_4p_col (void *d, void *s, int h, int bpr)
++{
++ u8 *dd = d, *ss = s;
++
++ while (h--) {
++ dd[0] = ss[0];
++ dd[2] = ss[2];
++ dd[4] = ss[4];
++ dd[6] = ss[6];
++ dd += bpr;
++ ss += bpr;
++ }
++}
++
++
++/* This expands a 4 bit color into a long for movepl (4 plane) operations. */
++
++static const u32 four2byte[] = {
++ 0x00000000, 0xff000000, 0x00ff0000, 0xffff0000,
++ 0x0000ff00, 0xff00ff00, 0x00ffff00, 0xffffff00,
++ 0x000000ff, 0xff0000ff, 0x00ff00ff, 0xffff00ff,
++ 0x0000ffff, 0xff00ffff, 0x00ffffff, 0xffffffff
++};
++
++static __inline__ u32 expand4l(u8 c)
++{
++ return four2byte[c];
++}
++
++
++/* This expands a 4 bit color into two longs for two movel operations
++ * (4 planes).
++ */
++static const u32 two2word[] = {
++#ifndef __LITTLE_ENDIAN
++ 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff,
++#else
++ 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff,
++#endif
++};
++
++static __inline__ void expand4dl(u8 c, u32 *ret1, u32 *ret2)
++{
++ *ret1 = two2word[c & 3];
++ *ret2 = two2word[c >> 2];
++}
++
++
++/* This duplicates a byte 4 times into a long. */
++
++static __inline__ u32 dup4l(u8 c)
++{
++ u32 rv;
++
++ rv = c;
++ rv |= rv << 8;
++ rv |= rv << 16;
++ return rv;
++}
++
++
++void atafb_iplan2p4_copyarea(struct fb_info *info, u_long next_line,
++ int sy, int sx, int dy, int dx,
++ int height, int width)
++{
++ /* bmove() has to distinguish two major cases: If both, source and
++ * destination, start at even addresses or both are at odd
++ * addresses, just the first odd and last even column (if present)
++ * require special treatment (memmove_col()). The rest between
++ * then can be copied by normal operations, because all adjacent
++ * bytes are affected and are to be stored in the same order.
++ * The pathological case is when the move should go from an odd
++ * address to an even or vice versa. Since the bytes in the plane
++ * words must be assembled in new order, it seems wisest to make
++ * all movements by memmove_col().
++ */
++
++ if (sx == 0 && dx == 0 && width * 4 == next_line) {
++ /* Special (but often used) case: Moving whole lines can be
++ * done with memmove()
++ */
++ fb_memmove((u8 *)info->screen_base + dy * next_line,
++ (u8 *)info->screen_base + sy * next_line,
++ next_line * height);
++ } else {
++ int rows, cols;
++ u8 *src;
++ u8 *dst;
++ int bytes = next_line;
++ int linesize;
++ u_int colsize;
++ u_int upwards = (dy < sy) || (dy == sy && dx < sx);
++
++ linesize = bytes;
++ colsize = height;
++ if ((sx & 1) == (dx & 1)) {
++ /* odd->odd or even->even */
++
++ if (upwards) {
++ src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*8 + (sx & 1);
++ dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*8 + (dx & 1);
++ if (sx & 1) {
++ memmove_4p_col(dst, src, colsize, bytes);
++ src += 7;
++ dst += 7;
++ --width;
++ }
++ if (width > 1) {
++ for(rows = colsize; rows > 0; --rows) {
++ fast_memmove (dst, src, (width >> 1) * 8);
++ src += bytes;
++ dst += bytes;
++ }
++ }
++
++ if (width & 1) {
++ src -= colsize * bytes;
++ dst -= colsize * bytes;
++ memmove_4p_col(dst + (width>>1)*8, src + (width>>1)*8,
++ colsize, bytes);
++ }
++ } else {
++ if (!((sx+width-1) & 1)) {
++ src = (u8 *)info->screen_base + sy * linesize + ((sx+width-1)>>1)*8;
++ dst = (u8 *)info->screen_base + dy * linesize + ((dx+width-1)>>1)*8;
++ memmove_4p_col(dst, src, colsize, bytes);
++ --width;
++ }
++ src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*8 + (sx & 1);
++ dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*8 + (dx & 1);
++ if (width > 1) {
++ src += colsize * bytes + (sx & 1)*7;
++ dst += colsize * bytes + (sx & 1)*7;
++ for(rows = colsize; rows > 0; --rows) {
++ src -= bytes;
++ dst -= bytes;
++ fb_memmove (dst, src, (width>>1)*8);
++ }
++ }
++ if (width & 1)
++ memmove_4p_col(dst-7, src-7, colsize, bytes);
++ }
++ } else {
++ /* odd->even or even->odd */
++
++ if (upwards) {
++ src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*8 + (sx & 1);
++ dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*8 + (dx & 1);
++ for(cols = width; cols > 0; --cols) {
++ memmove_4p_col(dst, src, colsize, bytes);
++ INC_4P(src);
++ INC_4P(dst);
++ }
++ } else {
++ sx += width-1;
++ dx += width-1;
++ src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*8 + (sx & 1);
++ dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*8 + (dx & 1);
++ for(cols = width; cols > 0; --cols) {
++ memmove_4p_col(dst, src, colsize, bytes);
++ DEC_4P(src);
++ DEC_4P(dst);
++ }
++ }
++ }
++ }
++}
++
++void atafb_iplan2p4_fillrect(struct fb_info *info, u_long next_line, u32 color,
++ int sy, int sx, int height, int width)
++{
++ u32 offset;
++ u8 *start;
++ int rows;
++ int bytes = next_line;
++ int lines;
++ u32 size;
++ u32 cval1, cval2, pcval;
++
++ expand4dl(color, &cval1, &cval2);
++
++ lines = height;
++
++ if (sx == 0 && width * 4 == bytes) {
++ offset = (sy * bytes);
++ size = lines * bytes;
++ memset_even_4p((u8 *)info->screen_base + offset, size, cval1, cval2);
++ } else {
++ offset = sy * bytes + (sx>>1)*8 + (sx & 1);
++ start = (u8 *) info->screen_base + offset;
++ pcval = expand4l(color);
++
++ /* Clears are split if the region starts at an odd column or
++ * end at an even column. These extra columns are spread
++ * across the interleaved planes. All in between can be
++ * cleared by normal fb_memclear_small(), because both bytes of
++ * the single plane words are affected.
++ */
++
++ if (sx & 1) {
++ memclear_4p_col(start, lines, pcval, bytes);
++ start += 7;
++ width--;
++ }
++ if (width & 1) {
++ memclear_4p_col(start + (width>>1)*8, lines, pcval, bytes);
++ width--;
++ }
++ if (width)
++ for(rows = lines; rows-- ; start += bytes)
++ memset_even_4p(start, width*4, cval1, cval2);
++ }
++}
++
++void atafb_iplan2p4_linefill(struct fb_info *info, u_long next_line,
++ int dy, int dx, u32 width,
++ const u8 *data, u32 bgcolor, u32 fgcolor)
++{
++ u8 *dest;
++ u8 *cdat;
++ int rows;
++ int bytes = next_line;
++ u32 eorx, fgx, bgx, fdx;
++
++ // source data
++ cdat = data;
++
++ // advance from odd to even dx by 7: 0 1 8 9 ...
++ // dest = (p->screen_base + yy * bytes + (xx>>1)*8 + (xx & 1));
++
++ dest = (u8*) info->screen_base + dy * bytes + (dx>>4)*8 + ((dx>>3)&1);
++
++ fgx = expand4l(fgcolor);
++ bgx = expand4l(bgcolor);
++ eorx = fgx ^ bgx;
++
++ for(rows = width/8; rows-- ;) {
++ fdx = dup4l(*cdat++);
++ movepl(dest, (fdx & eorx) ^ bgx);
++ INC_4P(dest);
++ }
++
++}
++
++#ifdef MODULE
++MODULE_LICENSE("GPL");
++
++int init_module(void)
++{
++ return 0;
++}
++
++void cleanup_module(void)
++{}
++#endif /* MODULE */
++
++
++ /*
++ * Visible symbols for modules
++ */
++
++EXPORT_SYMBOL(atafb_iplan2p4_copyarea);
++EXPORT_SYMBOL(atafb_iplan2p4_fillrect);
++EXPORT_SYMBOL(atafb_iplan2p4_linefill);
+diff -urN linux-m68k/drivers/video/atafb_iplan2p4.h linux-schmitz/drivers/video/atafb_iplan2p4.h
+--- linux-m68k/drivers/video/atafb_iplan2p4.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/drivers/video/atafb_iplan2p4.h 2006-11-19 21:37:27.000000000 +0100
+@@ -0,0 +1,27 @@
++/*
++ * FBcon low-level driver for Atari interleaved bitplanes (8 planes) (iplan2p8)
++ */
++
++#ifndef _VIDEO_FBCON_IPLAN2P4_H
++#define _VIDEO_FBCON_IPLAN2P4_H
++
++#ifdef MODULE
++#if defined(CONFIG_FBCON_IPLAN2P4) || defined(CONFIG_FBCON_IPLAN2P4_MODULE)
++#define FBCON_HAS_IPLAN2P4
++#endif
++#else
++#if defined(CONFIG_FBCON_IPLAN2P4)
++#define FBCON_HAS_IPLAN2P4
++#endif
++#endif
++
++extern void atafb_iplan2p4_copyarea(struct fb_info *info, u_long next_line, int sy, int sx, int dy,
++ int dx, int height, int width);
++extern void atafb_iplan2p4_fillrect(struct fb_info *info, u_long next_line, u32 color,
++ int sy, int sx, int height, int width);
++extern void atafb_iplan2p4_linefill(struct fb_info *info, u_long next_line,
++ int dy, int dx, u32 width,
++ const u8 *data, u32 bgcolor, u32 fgcolor);
++
++
++#endif /* _VIDEO_FBCON_IPLAN2P4_H */
+diff -urN linux-m68k/drivers/video/atafb_iplan2p8.c linux-schmitz/drivers/video/atafb_iplan2p8.c
+--- linux-m68k/drivers/video/atafb_iplan2p8.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/drivers/video/atafb_iplan2p8.c 2006-11-19 21:37:27.000000000 +0100
+@@ -0,0 +1,396 @@
++/*
++ * linux/drivers/video/iplan2p8.c -- Low level frame buffer operations for
++ * interleaved bitplanes à la Atari (8
++ * planes, 2 bytes interleave)
++ *
++ * Created 5 Apr 1997 by Geert Uytterhoeven
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ */
++
++#include <linux/module.h>
++#include <linux/tty.h>
++#include <linux/console.h>
++#include <linux/string.h>
++#include <linux/fb.h>
++
++#include <asm/byteorder.h>
++
++#ifdef __mc68000__
++#include <asm/setup.h>
++#endif
++
++#include "console/fbcon.h"
++#include "atafb_utils.h"
++#include "atafb_iplan2p8.h"
++
++
++ /*
++ * Interleaved bitplanes à la Atari (8 planes, 2 bytes interleave)
++ *
++ * In 8 plane mode, 256 colors would be possible, but only the first
++ * 16 are used by the console code (the upper 4 bits are
++ * background/unused). For that, the following functions mask off the
++ * higher 4 bits of each color.
++ */
++
++/* Increment/decrement 8 plane addresses */
++
++#define INC_8P(p) do { if (!((long)(++(p)) & 1)) (p) += 14; } while(0)
++#define DEC_8P(p) do { if ((long)(--(p)) & 1) (p) -= 12; } while(0)
++
++/* Perform the m68k movepl operation extended to 64 bits. */
++static inline void movepl2(u8 *d, u32 val1, u32 val2)
++{
++#if defined __mc68000__ && !defined CPU_M68060_ONLY
++ asm volatile ("movepl %1,%0@(0); movepl %2,%0@(8)"
++ : : "a" (d), "d" (val1), "d" (val2));
++#else
++ d[0] = (val1 >> 24) & 0xff;
++ d[2] = (val1 >> 16) & 0xff;
++ d[4] = (val1 >> 8) & 0xff;
++ d[6] = val1 & 0xff;
++ d[8] = (val2 >> 24) & 0xff;
++ d[10] = (val2 >> 16) & 0xff;
++ d[12] = (val2 >> 8) & 0xff;
++ d[14] = val2 & 0xff;
++#endif
++}
++
++/* Sets the bytes in the visible column at d, height h, to the value
++ * val1,val2 for a 8 plane screen. The bits of the color in 'color' are
++ * moved (8 times) to the respective bytes. This means:
++ *
++ * for(h times; d += bpr)
++ * *d = (color & 1) ? 0xff : 0;
++ * *(d+2) = (color & 2) ? 0xff : 0;
++ * *(d+4) = (color & 4) ? 0xff : 0;
++ * *(d+6) = (color & 8) ? 0xff : 0;
++ * *(d+8) = (color & 16) ? 0xff : 0;
++ * *(d+10) = (color & 32) ? 0xff : 0;
++ * *(d+12) = (color & 64) ? 0xff : 0;
++ * *(d+14) = (color & 128) ? 0xff : 0;
++ */
++
++static __inline__ void memclear_8p_col(void *d, size_t h, u32 val1,
++ u32 val2, int bpr)
++{
++ u8 *dd = d;
++ do {
++ movepl2(dd, val1, val2);
++ dd += bpr;
++ } while (--h);
++}
++
++/* Sets a 8 plane region from 'd', length 'count' bytes, to the color
++ * val1..val4. 'd' has to be an even address and count must be divisible
++ * by 16, because only whole words and all planes are accessed. I.e.:
++ *
++ * for(count/16 times)
++ * *d = *(d+1) = (color & 1) ? 0xff : 0;
++ * *(d+2) = *(d+3) = (color & 2) ? 0xff : 0;
++ * *(d+4) = *(d+5) = (color & 4) ? 0xff : 0;
++ * *(d+6) = *(d+7) = (color & 8) ? 0xff : 0;
++ * *(d+8) = *(d+9) = (color & 16) ? 0xff : 0;
++ * *(d+10) = *(d+11) = (color & 32) ? 0xff : 0;
++ * *(d+12) = *(d+13) = (color & 64) ? 0xff : 0;
++ * *(d+14) = *(d+15) = (color & 128) ? 0xff : 0;
++ */
++
++static __inline__ void memset_even_8p(void *d, size_t count, u32 val1,
++ u32 val2, u32 val3, u32 val4)
++{
++ u32 *dd = d;
++
++ count /= 16;
++ while (count--) {
++ *dd++ = val1;
++ *dd++ = val2;
++ *dd++ = val3;
++ *dd++ = val4;
++ }
++}
++
++/* Copies a 8 plane column from 's', height 'h', to 'd'. */
++
++static __inline__ void memmove_8p_col (void *d, void *s, int h, int bpr)
++{
++ u8 *dd = d, *ss = s;
++
++ while (h--) {
++ dd[0] = ss[0];
++ dd[2] = ss[2];
++ dd[4] = ss[4];
++ dd[6] = ss[6];
++ dd[8] = ss[8];
++ dd[10] = ss[10];
++ dd[12] = ss[12];
++ dd[14] = ss[14];
++ dd += bpr;
++ ss += bpr;
++ }
++}
++
++
++/* This expands a 8 bit color into two longs for two movepl (8 plane)
++ * operations.
++ */
++
++static const u32 four2long[] =
++{
++ 0x00000000, 0xff000000, 0x00ff0000, 0xffff0000,
++ 0x0000ff00, 0xff00ff00, 0x00ffff00, 0xffffff00,
++ 0x000000ff, 0xff0000ff, 0x00ff00ff, 0xffff00ff,
++ 0x0000ffff, 0xff00ffff, 0x00ffffff, 0xffffffff,
++};
++
++static __inline__ void expand8dl(u8 c, u32 *ret1, u32 *ret2)
++{
++ *ret1 = four2long[c & 15];
++ *ret2 = four2long[c >> 4];
++}
++
++
++/* This expands a 8 bit color into four longs for four movel operations
++ * (8 planes).
++ */
++
++static const u32 two2word[] =
++{
++#ifndef __LITTLE_ENDIAN
++ 0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
++#else
++ 0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
++#endif
++};
++
++static inline void expand8ql(u8 c, u32 *rv1, u32 *rv2, u32 *rv3, u32 *rv4)
++{
++ *rv1 = two2word[c & 4];
++ *rv2 = two2word[(c >> 2) & 4];
++ *rv3 = two2word[(c >> 4) & 4];
++ *rv4 = two2word[c >> 6];
++}
++
++
++/* This duplicates a byte 4 times into a long. */
++
++static __inline__ u32 dup4l(u8 c)
++{
++ u32 rv;
++
++ rv = c;
++ rv |= rv << 8;
++ rv |= rv << 16;
++ return rv;
++}
++
++
++void atafb_iplan2p8_copyarea(struct fb_info *info, u_long next_line,
++ int sy, int sx, int dy, int dx,
++ int height, int width)
++{
++ /* bmove() has to distinguish two major cases: If both, source and
++ * destination, start at even addresses or both are at odd
++ * addresses, just the first odd and last even column (if present)
++ * require special treatment (memmove_col()). The rest between
++ * then can be copied by normal operations, because all adjacent
++ * bytes are affected and are to be stored in the same order.
++ * The pathological case is when the move should go from an odd
++ * address to an even or vice versa. Since the bytes in the plane
++ * words must be assembled in new order, it seems wisest to make
++ * all movements by memmove_col().
++ */
++
++ printk("copyarea: dx %d dy %d sx %d sy %d\n", dx, dy, sy, sy);
++
++ if (sx == 0 && dx == 0 && width * 8 == next_line) {
++ /* Special (but often used) case: Moving whole lines can be
++ * done with memmove()
++ */
++ fast_memmove((u8 *)info->screen_base + dy * next_line,
++ (u8 *)info->screen_base + sy * next_line,
++ next_line * height);
++ } else {
++ int rows, cols;
++ u8 *src;
++ u8 *dst;
++ int bytes = next_line;
++ int linesize;
++ u_int colsize;
++ u_int upwards = (dy < sy) || (dy == sy && dx < sx);
++
++ linesize = bytes;
++ colsize = height;
++ if ((sx & 1) == (dx & 1)) {
++ /* odd->odd or even->even */
++
++ if (upwards) {
++ src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1);
++ dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1);
++ if (sx & 1) {
++ memmove_8p_col(dst, src, colsize, bytes);
++ src += 15;
++ dst += 15;
++ --width;
++ }
++ if (width > 1) {
++ for(rows = colsize; rows > 0; --rows) {
++ fast_memmove (dst, src, (width >> 1) * 16);
++ src += bytes;
++ dst += bytes;
++ }
++ }
++
++ if (width & 1) {
++ src -= colsize * bytes;
++ dst -= colsize * bytes;
++ memmove_8p_col(dst + (width>>1)*16, src + (width>>1)*16,
++ colsize, bytes);
++ }
++ } else {
++ if (!((sx+width-1) & 1)) {
++ src = (u8 *)info->screen_base + sy * linesize + ((sx+width-1)>>1)*16;
++ dst = (u8 *)info->screen_base + dy * linesize + ((dx+width-1)>>1)*16;
++ memmove_8p_col(dst, src, colsize, bytes);
++ --width;
++ }
++ src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1);
++ dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1);
++ if (width > 1) {
++ src += colsize * bytes + (sx & 1)*15;
++ dst += colsize * bytes + (sx & 1)*15;
++ for(rows = colsize; rows > 0; --rows) {
++ src -= bytes;
++ dst -= bytes;
++ fast_memmove (dst, src, (width>>1)*16);
++ }
++ }
++ if (width & 1)
++ memmove_8p_col(dst-15, src-15, colsize, bytes);
++ }
++ } else {
++ /* odd->even or even->odd */
++
++ if (upwards) {
++ src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1);
++ dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1);
++ for(cols = width; cols > 0; --cols) {
++ memmove_8p_col(dst, src, colsize, bytes);
++ INC_8P(src);
++ INC_8P(dst);
++ }
++ } else {
++ sx += width-1;
++ dx += width-1;
++ src = (u8 *)info->screen_base + sy * linesize + (sx>>1)*16 + (sx & 1);
++ dst = (u8 *)info->screen_base + dy * linesize + (dx>>1)*16 + (dx & 1);
++ for(cols = width; cols > 0; --cols) {
++ memmove_8p_col(dst, src, colsize, bytes);
++ DEC_8P(src);
++ DEC_8P(dst);
++ }
++ }
++ }
++ }
++}
++
++void atafb_iplan2p8_fillrect(struct fb_info *info, u_long next_line, u32 color,
++ int sy, int sx, int height, int width)
++{
++ u32 offset;
++ u8 *start;
++ int rows;
++ int bytes = next_line;
++ int lines;
++ u32 size;
++ u32 cval1, cval2, cval3, cval4, pcval1, pcval2;
++
++ expand8ql(color, &cval1, &cval2, &cval3, &cval4);
++
++ lines = height;
++
++ if (sx == 0 && width * 8 == bytes) {
++ offset = (sy * bytes);
++ size = lines * bytes;
++ memset_even_8p((u8 *)info->screen_base + offset, size, cval1, cval2, cval3, cval4);
++ } else {
++ offset = sy * bytes + (sx>>1)*2 + (sx & 1);
++ start = (u8 *) info->screen_base + offset;
++ expand8dl(color, &pcval1, &pcval2);
++
++ /* Clears are split if the region starts at an odd column or
++ * end at an even column. These extra columns are spread
++ * across the interleaved planes. All in between can be
++ * cleared by normal fb_memclear_small(), because both bytes of
++ * the single plane words are affected.
++ */
++
++ if (sx & 1) {
++ memclear_8p_col(start, lines, pcval1, pcval2, bytes);
++ start += 7;
++ width--;
++ }
++ if (width & 1) {
++ memclear_8p_col(start + (width>>1)*2, lines, pcval1,
++ pcval2, bytes);
++ width--;
++ }
++ if (width)
++ for(rows = lines; rows-- ; start += bytes)
++ memset_even_8p(start, width, cval1, cval2, cval3, cval4);
++ }
++}
++
++void atafb_iplan2p8_linefill(struct fb_info *info, u_long next_line,
++ int dy, int dx, u32 width,
++ const u8 *data, u32 bgcolor, u32 fgcolor)
++{
++ u8 *dest;
++ u8 *cdat;
++ int rows;
++ int bytes = next_line;
++ u32 eorx1, eorx2, fgx1, fgx2, bgx1, bgx2, fdx;
++
++ // source data
++ cdat = data;
++
++ // advance from odd to even (dx>>3) by 16
++ // dest = (p->screen_base + yy * bytes + (xx>>1)*16 + (xx & 1));
++
++
++ dest = (u8*) info->screen_base + dy * bytes + (dx>>4)*16 + ((dx>>3)&1);
++
++ expand8dl(fgcolor, &fgx1, &fgx2);
++ expand8dl(bgcolor, &bgx1, &bgx2);
++ eorx1 = fgx1 ^ bgx1; eorx2 = fgx2 ^ bgx2;
++
++ for(rows = width/8; rows-- ;) {
++ fdx = dup4l(*cdat++);
++ movepl2(dest, (fdx & eorx1) ^ bgx1, (fdx & eorx2) ^ bgx2);
++ INC_8P(dest);
++ }
++}
++
++#ifdef MODULE
++MODULE_LICENSE("GPL");
++
++int init_module(void)
++{
++ return 0;
++}
++
++void cleanup_module(void)
++{}
++#endif /* MODULE */
++
++
++ /*
++ * Visible symbols for modules
++ */
++
++EXPORT_SYMBOL(atafb_iplan2p8_copyarea);
++EXPORT_SYMBOL(atafb_iplan2p8_fillrect);
++EXPORT_SYMBOL(atafb_iplan2p8_linefill);
+diff -urN linux-m68k/drivers/video/atafb_iplan2p8.h linux-schmitz/drivers/video/atafb_iplan2p8.h
+--- linux-m68k/drivers/video/atafb_iplan2p8.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/drivers/video/atafb_iplan2p8.h 2006-11-19 21:37:27.000000000 +0100
+@@ -0,0 +1,27 @@
++/*
++ * FBcon low-level driver for Atari interleaved bitplanes (8 planes) (iplan2p8)
++ */
++
++#ifndef _VIDEO_FBCON_IPLAN2P8_H
++#define _VIDEO_FBCON_IPLAN2P8_H
++
++#ifdef MODULE
++#if defined(CONFIG_FBCON_IPLAN2P8) || defined(CONFIG_FBCON_IPLAN2P8_MODULE)
++#define FBCON_HAS_IPLAN2P8
++#endif
++#else
++#if defined(CONFIG_FBCON_IPLAN2P8)
++#define FBCON_HAS_IPLAN2P8
++#endif
++#endif
++
++extern void atafb_iplan2p8_copyarea(struct fb_info *info, u_long next_line, int sy, int sx, int dy,
++ int dx, int height, int width);
++extern void atafb_iplan2p8_fillrect(struct fb_info *info, u_long next_line, u32 color,
++ int sy, int sx, int height, int width);
++extern void atafb_iplan2p8_linefill(struct fb_info *info, u_long next_line,
++ int dy, int dx, u32 width,
++ const u8 *data, u32 bgcolor, u32 fgcolor);
++
++
++#endif /* _VIDEO_FBCON_IPLAN2P8_H */
+diff -urN linux-m68k/drivers/video/atafb_mfb.c linux-schmitz/drivers/video/atafb_mfb.c
+--- linux-m68k/drivers/video/atafb_mfb.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/drivers/video/atafb_mfb.c 2006-11-19 21:37:27.000000000 +0100
+@@ -0,0 +1,115 @@
++/*
++ * linux/drivers/video/mfb.c -- Low level frame buffer operations for
++ * monochrome
++ *
++ * Created 5 Apr 1997 by Geert Uytterhoeven
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ */
++
++#include <linux/module.h>
++#include <linux/tty.h>
++#include <linux/console.h>
++#include <linux/string.h>
++#include <linux/fb.h>
++
++#include "console/fbcon.h"
++#include "atafb_utils.h"
++#include "atafb_mfb.h"
++
++
++ /*
++ * Monochrome
++ */
++
++void atafb_mfb_copyarea(struct fb_info *info, u_long next_line,
++ int sy, int sx, int dy, int dx,
++ int height, int width)
++{
++ u8 *src, *dest;
++ u_int rows;
++
++ if (sx == 0 && dx == 0 && width == next_line) {
++ src = (u8 *)info->screen_base+sy*(width>>3);
++ dest = (u8 *)info->screen_base+dy*(width>>3);
++ fb_memmove(dest, src, height*(width>>3));
++ } else if (dy <= sy) {
++ src = (u8 *)info->screen_base+sy*next_line+(sx>>3);
++ dest = (u8 *)info->screen_base+dy*next_line+(dx>>3);
++ for (rows = height; rows--;) {
++ fb_memmove(dest, src, (width>>3));
++ src += next_line;
++ dest += next_line;
++ }
++ } else {
++ src = (u8 *)info->screen_base+((sy+height)-1)*next_line+(sx>>3);
++ dest = (u8 *)info->screen_base+((dy+height)-1)*next_line+(dx>>3);
++ for (rows = height; rows--;) {
++ fb_memmove(dest, src, (width>>3));
++ src -= next_line;
++ dest -= next_line;
++ }
++ }
++}
++
++void atafb_mfb_fillrect(struct fb_info *info, u_long next_line, u32 color,
++ int sy, int sx,
++ int height, int width)
++{
++ u8 *dest;
++ u_int rows;
++
++ dest = (u8 *)info->screen_base+sy*next_line+(sx>>3);
++
++ if (sx == 0 && width == next_line) {
++ if (color)
++ fb_memset255(dest, height*(width>>3));
++ else
++ fb_memclear(dest, height*(width>>3));
++ } else
++ for (rows = height; rows--; dest += next_line)
++ if (color)
++ fb_memset255(dest, (width>>3));
++ else
++ fb_memclear_small(dest, (width>>3));
++}
++
++void atafb_mfb_linefill(struct fb_info *info, u_long next_line,
++ int dy, int dx, u32 width,
++ const u8 *data, u32 bgcolor, u32 fgcolor)
++{
++ u8 *dest, *cdat;
++ u_int rows;
++ u8 d;
++
++ dest = (u8*) info->screen_base+dy*next_line+(dx>>3) + (dx & 1);
++ cdat = data;
++
++ for (rows = width/8; rows--; /* check margins */ ) {
++ // use fast_memmove or fb_memmove
++ *dest++ = *cdat++;
++ }
++}
++
++#ifdef MODULE
++MODULE_LICENSE("GPL");
++
++int init_module(void)
++{
++ return 0;
++}
++
++void cleanup_module(void)
++{}
++#endif /* MODULE */
++
++
++ /*
++ * Visible symbols for modules
++ */
++
++EXPORT_SYMBOL(atafb_mfb_copyarea);
++EXPORT_SYMBOL(atafb_mfb_fillrect);
++EXPORT_SYMBOL(atafb_mfb_linefill);
+diff -urN linux-m68k/drivers/video/atafb_mfb.h linux-schmitz/drivers/video/atafb_mfb.h
+--- linux-m68k/drivers/video/atafb_mfb.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/drivers/video/atafb_mfb.h 2006-11-19 21:37:27.000000000 +0100
+@@ -0,0 +1,26 @@
++/*
++ * FBcon low-level driver for Monochrome (mfb)
++ */
++
++#ifndef _VIDEO_FBCON_MFB_H
++#define _VIDEO_FBCON_MFB_H
++
++#ifdef MODULE
++#if defined(CONFIG_FBCON_MFB) || defined(CONFIG_FBCON_MFB_MODULE)
++#define FBCON_HAS_MFB
++#endif
++#else
++#if defined(CONFIG_FBCON_MFB)
++#define FBCON_HAS_MFB
++#endif
++#endif
++
++extern void atafb_mfb_copyarea(struct fb_info *info, u_long next_line, int sy, int sx, int dy,
++ int dx, int height, int width);
++extern void atafb_mfb_fillrect(struct fb_info *info, u_long next_line, u32 color,
++ int sy, int sx, int height, int width);
++extern void atafb_mfb_linefill(struct fb_info *info, u_long next_line,
++ int dy, int dx, u32 width,
++ const u8 *data, u32 bgcolor, u32 fgcolor);
++
++#endif /* _VIDEO_FBCON_MFB_H */
+diff -urN linux-m68k/drivers/video/atafb_utils.h linux-schmitz/drivers/video/atafb_utils.h
+--- linux-m68k/drivers/video/atafb_utils.h 1970-01-01 01:00:00.000000000 +0100
++++ linux-schmitz/drivers/video/atafb_utils.h 2006-11-19 21:37:27.000000000 +0100
+@@ -0,0 +1,570 @@
++#ifndef _VIDEO_ATAFB_UTILS_H
++#define _VIDEO_ATAFB_UTILS_H
++
++/* ================================================================= */
++/* Utility Assembler Functions */
++/* ================================================================= */
++
++
++#if defined(__mc68000__)
++
++/* ====================================================================== */
++
++/* Those of a delicate disposition might like to skip the next couple of
++ * pages.
++ *
++ * These functions are drop in replacements for memmove and
++ * memset(_, 0, _). However their five instances add at least a kilobyte
++ * to the object file. You have been warned.
++ *
++ * Not a great fan of assembler for the sake of it, but I think
++ * that these routines are at least 10 times faster than their C
++ * equivalents for large blits, and that's important to the lowest level of
++ * a graphics driver. Question is whether some scheme with the blitter
++ * would be faster. I suspect not for simple text system - not much
++ * asynchrony.
++ *
++ * Code is very simple, just gruesome expansion. Basic strategy is to
++ * increase data moved/cleared at each step to 16 bytes to reduce
++ * instruction per data move overhead. movem might be faster still
++ * For more than 15 bytes, we try to align the write direction on a
++ * longword boundary to get maximum speed. This is even more gruesome.
++ * Unaligned read/write used requires 68020+ - think this is a problem?
++ *
++ * Sorry!
++ */
++
++
++/* ++roman: I've optimized Robert's original versions in some minor
++ * aspects, e.g. moveq instead of movel, let gcc choose the registers,
++ * use movem in some places...
++ * For other modes than 1 plane, lots of more such assembler functions
++ * were needed (e.g. the ones using movep or expanding color values).
++ */
++
++/* ++andreas: more optimizations:
++ subl #65536,d0 replaced by clrw d0; subql #1,d0 for dbcc
++ addal is faster than addaw
++ movep is rather expensive compared to ordinary move's
++ some functions rewritten in C for clarity, no speed loss */
++
++static __inline__ void *fb_memclear_small(void *s, size_t count)
++{
++ if (!count)
++ return(0);
++
++ __asm__ __volatile__(
++ "lsrl #1,%1 ; jcc 1f ; moveb %2,%0 at -\n\t"
++ "1: lsrl #1,%1 ; jcc 1f ; movew %2,%0 at -\n\t"
++ "1: lsrl #1,%1 ; jcc 1f ; movel %2,%0 at -\n\t"
++ "1: lsrl #1,%1 ; jcc 1f ; movel %2,%0 at - ; movel %2,%0 at -\n\t"
++ "1:"
++ : "=a" (s), "=d" (count)
++ : "d" (0), "0" ((char *)s+count), "1" (count)
++ );
++ __asm__ __volatile__(
++ "subql #1,%1 ; jcs 3f\n\t"
++ "movel %2,%%d4; movel %2,%%d5; movel %2,%%d6\n\t"
++ "2: moveml %2/%%d4/%%d5/%%d6,%0 at -\n\t"
++ "dbra %1,2b\n\t"
++ "3:"
++ : "=a" (s), "=d" (count)
++ : "d" (0), "0" (s), "1" (count)
++ : "d4", "d5", "d6"
++ );
++
++ return(0);
++}
++
++
++static __inline__ void *fb_memclear(void *s, size_t count)
++{
++ if (!count)
++ return(0);
++
++ if (count < 16) {
++ __asm__ __volatile__(
++ "lsrl #1,%1 ; jcc 1f ; clrb %0 at +\n\t"
++ "1: lsrl #1,%1 ; jcc 1f ; clrw %0 at +\n\t"
++ "1: lsrl #1,%1 ; jcc 1f ; clrl %0 at +\n\t"
++ "1: lsrl #1,%1 ; jcc 1f ; clrl %0 at + ; clrl %0 at +\n\t"
++ "1:"
++ : "=a" (s), "=d" (count)
++ : "0" (s), "1" (count)
++ );
++ } else {
++ long tmp;
++ __asm__ __volatile__(
++ "movel %1,%2\n\t"
++ "lsrl #1,%2 ; jcc 1f ; clrb %0 at + ; subqw #1,%1\n\t"
++ "lsrl #1,%2 ; jcs 2f\n\t" /* %0 increased=>bit 2 switched*/
++ "clrw %0 at + ; subqw #2,%1 ; jra 2f\n\t"
++ "1: lsrl #1,%2 ; jcc 2f\n\t"
++ "clrw %0 at + ; subqw #2,%1\n\t"
++ "2: movew %1,%2; lsrl #2,%1 ; jeq 6f\n\t"
++ "lsrl #1,%1 ; jcc 3f ; clrl %0 at +\n\t"
++ "3: lsrl #1,%1 ; jcc 4f ; clrl %0 at + ; clrl %0 at +\n\t"
++ "4: subql #1,%1 ; jcs 6f\n\t"
++ "5: clrl %0 at +; clrl %0 at + ; clrl %0 at + ; clrl %0 at +\n\t"
++ "dbra %1,5b ; clrw %1; subql #1,%1; jcc 5b\n\t"
++ "6: movew %2,%1; btst #1,%1 ; jeq 7f ; clrw %0 at +\n\t"
++ "7: ; btst #0,%1 ; jeq 8f ; clrb %0 at +\n\t"
++ "8:"
++ : "=a" (s), "=d" (count), "=d" (tmp)
++ : "0" (s), "1" (count)
++ );
++ }
++
++ return(0);
++}
++
++
++static __inline__ void *fb_memset255(void *s, size_t count)
++{
++ if (!count)
++ return(0);
++
++ __asm__ __volatile__(
++ "lsrl #1,%1 ; jcc 1f ; moveb %2,%0 at -\n\t"
++ "1: lsrl #1,%1 ; jcc 1f ; movew %2,%0 at -\n\t"
++ "1: lsrl #1,%1 ; jcc 1f ; movel %2,%0 at -\n\t"
++ "1: lsrl #1,%1 ; jcc 1f ; movel %2,%0 at - ; movel %2,%0 at -\n\t"
++ "1:"
++ : "=a" (s), "=d" (count)
++ : "d" (-1), "0" ((char *)s+count), "1" (count)
++ );
++ __asm__ __volatile__(
++ "subql #1,%1 ; jcs 3f\n\t"
++ "movel %2,%%d4; movel %2,%%d5; movel %2,%%d6\n\t"
++ "2: moveml %2/%%d4/%%d5/%%d6,%0 at -\n\t"
++ "dbra %1,2b\n\t"
++ "3:"
++ : "=a" (s), "=d" (count)
++ : "d" (-1), "0" (s), "1" (count)
++ : "d4", "d5", "d6"
++ );
++
++ return(0);
++}
++
++
++static __inline__ void *fb_memmove(void *d, const void *s, size_t count)
++{
++ if (d < s) {
++ if (count < 16) {
++ __asm__ __volatile__(
++ "lsrl #1,%2 ; jcc 1f ; moveb %1 at +,%0 at +\n\t"
++ "1: lsrl #1,%2 ; jcc 1f ; movew %1 at +,%0 at +\n\t"
++ "1: lsrl #1,%2 ; jcc 1f ; movel %1 at +,%0 at +\n\t"
++ "1: lsrl #1,%2 ; jcc 1f ; movel %1 at +,%0 at + ; movel %1 at +,%0 at +\n\t"
++ "1:"
++ : "=a" (d), "=a" (s), "=d" (count)
++ : "0" (d), "1" (s), "2" (count)
++ );
++ } else {
++ long tmp;
++ __asm__ __volatile__(
++ "movel %0,%3\n\t"
++ "lsrl #1,%3 ; jcc 1f ; moveb %1 at +,%0 at + ; subqw #1,%2\n\t"
++ "lsrl #1,%3 ; jcs 2f\n\t" /* %0 increased=>bit 2 switched*/
++ "movew %1 at +,%0 at + ; subqw #2,%2 ; jra 2f\n\t"
++ "1: lsrl #1,%3 ; jcc 2f\n\t"
++ "movew %1 at +,%0 at + ; subqw #2,%2\n\t"
++ "2: movew %2,%-; lsrl #2,%2 ; jeq 6f\n\t"
++ "lsrl #1,%2 ; jcc 3f ; movel %1 at +,%0 at +\n\t"
++ "3: lsrl #1,%2 ; jcc 4f ; movel %1 at +,%0 at + ; movel %1 at +,%0 at +\n\t"
++ "4: subql #1,%2 ; jcs 6f\n\t"
++ "5: movel %1 at +,%0 at +;movel %1 at +,%0 at +\n\t"
++ "movel %1 at +,%0 at +;movel %1 at +,%0 at +\n\t"
++ "dbra %2,5b ; clrw %2; subql #1,%2; jcc 5b\n\t"
++ "6: movew %+,%2; btst #1,%2 ; jeq 7f ; movew %1 at +,%0 at +\n\t"
++ "7: ; btst #0,%2 ; jeq 8f ; moveb %1 at +,%0 at +\n\t"
++ "8:"
++ : "=a" (d), "=a" (s), "=d" (count), "=d" (tmp)
++ : "0" (d), "1" (s), "2" (count)
++ );
++ }
++ } else {
++ if (count < 16) {
++ __asm__ __volatile__(
++ "lsrl #1,%2 ; jcc 1f ; moveb %1 at -,%0 at -\n\t"
++ "1: lsrl #1,%2 ; jcc 1f ; movew %1 at -,%0 at -\n\t"
++ "1: lsrl #1,%2 ; jcc 1f ; movel %1 at -,%0 at -\n\t"
++ "1: lsrl #1,%2 ; jcc 1f ; movel %1 at -,%0 at - ; movel %1 at -,%0 at -\n\t"
++ "1:"
++ : "=a" (d), "=a" (s), "=d" (count)
++ : "0" ((char *) d + count), "1" ((char *) s + count), "2" (count)
++ );
++ } else {
++ long tmp;
++ __asm__ __volatile__(
++ "movel %0,%3\n\t"
++ "lsrl #1,%3 ; jcc 1f ; moveb %1 at -,%0 at - ; subqw #1,%2\n\t"
++ "lsrl #1,%3 ; jcs 2f\n\t" /* %0 increased=>bit 2 switched*/
++ "movew %1 at -,%0 at - ; subqw #2,%2 ; jra 2f\n\t"
++ "1: lsrl #1,%3 ; jcc 2f\n\t"
++ "movew %1 at -,%0 at - ; subqw #2,%2\n\t"
++ "2: movew %2,%-; lsrl #2,%2 ; jeq 6f\n\t"
++ "lsrl #1,%2 ; jcc 3f ; movel %1 at -,%0 at -\n\t"
++ "3: lsrl #1,%2 ; jcc 4f ; movel %1 at -,%0 at - ; movel %1 at -,%0 at -\n\t"
++ "4: subql #1,%2 ; jcs 6f\n\t"
++ "5: movel %1 at -,%0 at -;movel %1 at -,%0 at -\n\t"
++ "movel %1 at -,%0 at -;movel %1 at -,%0 at -\n\t"
++ "dbra %2,5b ; clrw %2; subql #1,%2; jcc 5b\n\t"
++ "6: movew %+,%2; btst #1,%2 ; jeq 7f ; movew %1 at -,%0 at -\n\t"
++ "7: ; btst #0,%2 ; jeq 8f ; moveb %1 at -,%0 at -\n\t"
++ "8:"
++ : "=a" (d), "=a" (s), "=d" (count), "=d" (tmp)
++ : "0" ((char *) d + count), "1" ((char *) s + count), "2" (count)
++ );
++ }
++ }
++
++ return(0);
++}
++
++
++/* ++andreas: Simple and fast version of memmove, assumes size is
++ divisible by 16, suitable for moving the whole screen bitplane */
++static __inline__ void fast_memmove(char *dst, const char *src, size_t size)
++{
++ if (!size)
++ return;
++ if (dst < src)
++ __asm__ __volatile__
++ ("1:"
++ " moveml %0 at +,%/d0/%/d1/%/a0/%/a1\n"
++ " moveml %/d0/%/d1/%/a0/%/a1,%1@\n"
++ " addql #8,%1; addql #8,%1\n"
++ " dbra %2,1b\n"
++ " clrw %2; subql #1,%2\n"
++ " jcc 1b"
++ : "=a" (src), "=a" (dst), "=d" (size)
++ : "0" (src), "1" (dst), "2" (size / 16 - 1)
++ : "d0", "d1", "a0", "a1", "memory");
++ else
++ __asm__ __volatile__
++ ("1:"
++ " subql #8,%0; subql #8,%0\n"
++ " moveml %0@,%/d0/%/d1/%/a0/%/a1\n"
++ " moveml %/d0/%/d1/%/a0/%/a1,%1 at -\n"
++ " dbra %2,1b\n"
++ " clrw %2; subql #1,%2\n"
++ " jcc 1b"
++ : "=a" (src), "=a" (dst), "=d" (size)
++ : "0" (src + size), "1" (dst + size), "2" (size / 16 - 1)
++ : "d0", "d1", "a0", "a1", "memory");
++}
++
++#elif defined(CONFIG_SUN4)
++
++/* You may think that I'm crazy and that I should use generic
++ routines. No, I'm not: sun4's framebuffer crashes if we std
++ into it, so we cannot use memset. */
++
++static __inline__ void *sun4_memset(void *s, char val, size_t count)
++{
++ int i;
++ for(i=0; i<count;i++)
++ ((char *) s) [i] = val;
++ return s;
++}
++
++static __inline__ void *fb_memset255(void *s, size_t count)
++{
++ return sun4_memset(s, 255, count);
++}
++
++static __inline__ void *fb_memclear(void *s, size_t count)
++{
++ return sun4_memset(s, 0, count);
++}
++
++static __inline__ void *fb_memclear_small(void *s, size_t count)
++{
++ return sun4_memset(s, 0, count);
++}
++
++/* To be honest, this is slow_memmove :). But sun4 is crappy, so what we can do. */
++static __inline__ void fast_memmove(void *d, const void *s, size_t count)
++{
++ int i;
++ if (d<s) {
++ for (i=0; i<count; i++)
++ ((char *) d)[i] = ((char *) s)[i];
++ } else
++ for (i=0; i<count; i++)
++ ((char *) d)[count-i-1] = ((char *) s)[count-i-1];
++}
++
++static __inline__ void *fb_memmove(char *dst, const char *src, size_t size)
++{
++ fast_memmove(dst, src, size);
++ return dst;
++}
++
++#else
++
++static __inline__ void *fb_memclear_small(void *s, size_t count)
++{
++ char *xs = (char *) s;
++
++ while (count--)
++ fb_writeb(0, xs++);
++
++ return s;
++}
++
++static __inline__ void *fb_memclear(void *s, size_t count)
++{
++ unsigned long xs = (unsigned long) s;
++
++ if (count < 8)
++ goto rest;
++
++ if (xs & 1) {
++ fb_writeb(0, xs++);
++ count--;
++ }
++ if (xs & 2) {
++ fb_writew(0, xs);
++ xs += 2;
++ count -= 2;
++ }
++ while (count > 3) {
++ fb_writel(0, xs);
++ xs += 4;
++ count -= 4;
++ }
++rest:
++ while (count--)
++ fb_writeb(0, xs++);
++
++ return s;
++}
++
++static __inline__ void *fb_memset255(void *s, size_t count)
++{
++ unsigned long xs = (unsigned long) s;
++
++ if (count < 8)
++ goto rest;
++
++ if (xs & 1) {
++ fb_writeb(0xff, xs++);
++ count--;
++ }
++ if (xs & 2) {
++ fb_writew(0xffff, xs);
++ xs += 2;
++ count -= 2;
++ }
++ while (count > 3) {
++ fb_writel(0xffffffff, xs);
++ xs += 4;
++ count -= 4;
++ }
++rest:
++ while (count--)
++ fb_writeb(0xff, xs++);
++
++ return s;
++}
++
++#if defined(__i386__)
++
++static __inline__ void fast_memmove(void *d, const void *s, size_t count)
++{
++ int d0, d1, d2, d3;
++ if (d < s) {
++__asm__ __volatile__ (
++ "cld\n\t"
++ "shrl $1,%%ecx\n\t"
++ "jnc 1f\n\t"
++ "movsb\n"
++ "1:\tshrl $1,%%ecx\n\t"
++ "jnc 2f\n\t"
++ "movsw\n"
++ "2:\trep\n\t"
++ "movsl"
++ : "=&c" (d0), "=&D" (d1), "=&S" (d2)
++ :"0"(count),"1"((long)d),"2"((long)s)
++ :"memory");
++ } else {
++__asm__ __volatile__ (
++ "std\n\t"
++ "shrl $1,%%ecx\n\t"
++ "jnc 1f\n\t"
++ "movb 3(%%esi),%%al\n\t"
++ "movb %%al,3(%%edi)\n\t"
++ "decl %%esi\n\t"
++ "decl %%edi\n"
++ "1:\tshrl $1,%%ecx\n\t"
++ "jnc 2f\n\t"
++ "movw 2(%%esi),%%ax\n\t"
++ "movw %%ax,2(%%edi)\n\t"
++ "decl %%esi\n\t"
++ "decl %%edi\n\t"
++ "decl %%esi\n\t"
++ "decl %%edi\n"
++ "2:\trep\n\t"
++ "movsl\n\t"
++ "cld"
++ : "=&c" (d0), "=&D" (d1), "=&S" (d2), "=&a" (d3)
++ :"0"(count),"1"(count-4+(long)d),"2"(count-4+(long)s)
++ :"memory");
++ }
++}
++
++static __inline__ void *fb_memmove(char *dst, const char *src, size_t size)
++{
++ fast_memmove(dst, src, size);
++ return dst;
++}
++
++#else /* !__i386__ */
++
++ /*
++ * Anyone who'd like to write asm functions for other CPUs?
++ * (Why are these functions better than those from include/asm/string.h?)
++ */
++
++static __inline__ void *fb_memmove(void *d, const void *s, size_t count)
++{
++ unsigned long dst, src;
++
++ if (d < s) {
++ dst = (unsigned long) d;
++ src = (unsigned long) s;
++
++ if ((count < 8) || ((dst ^ src) & 3))
++ goto restup;
++
++ if (dst & 1) {
++ fb_writeb(fb_readb(src++), dst++);
++ count--;
++ }
++ if (dst & 2) {
++ fb_writew(fb_readw(src), dst);
++ src += 2;
++ dst += 2;
++ count -= 2;
++ }
++ while (count > 3) {
++ fb_writel(fb_readl(src), dst);
++ src += 4;
++ dst += 4;
++ count -= 4;
++ }
++
++ restup:
++ while (count--)
++ fb_writeb(fb_readb(src++), dst++);
++ } else {
++ dst = (unsigned long) d + count;
++ src = (unsigned long) s + count;
++
++ if ((count < 8) || ((dst ^ src) & 3))
++ goto restdown;
++
++ if (dst & 1) {
++ src--;
++ dst--;
++ count--;
++ fb_writeb(fb_readb(src), dst);
++ }
++ if (dst & 2) {
++ src -= 2;
++ dst -= 2;
++ count -= 2;
++ fb_writew(fb_readw(src), dst);
++ }
++ while (count > 3) {
++ src -= 4;
++ dst -= 4;
++ count -= 4;
++ fb_writel(fb_readl(src), dst);
++ }
++
++ restdown:
++ while (count--) {
++ src--;
++ dst--;
++ fb_writeb(fb_readb(src), dst);
++ }
++ }
++
++ return d;
++}
++
++static __inline__ void fast_memmove(char *d, const char *s, size_t count)
++{
++ unsigned long dst, src;
++
++ if (d < s) {
++ dst = (unsigned long) d;
++ src = (unsigned long) s;
++
++ if ((count < 8) || ((dst ^ src) & 3))
++ goto restup;
++
++ if (dst & 1) {
++ fb_writeb(fb_readb(src++), dst++);
++ count--;
++ }
++ if (dst & 2) {
++ fb_writew(fb_readw(src), dst);
++ src += 2;
++ dst += 2;
++ count -= 2;
++ }
++ while (count > 3) {
++ fb_writel(fb_readl(src), dst);
++ src += 4;
++ dst += 4;
++ count -= 4;
++ }
++
++ restup:
++ while (count--)
++ fb_writeb(fb_readb(src++), dst++);
++ } else {
++ dst = (unsigned long) d + count;
++ src = (unsigned long) s + count;
++
++ if ((count < 8) || ((dst ^ src) & 3))
++ goto restdown;
++
++ if (dst & 1) {
++ src--;
++ dst--;
++ count--;
++ fb_writeb(fb_readb(src), dst);
++ }
++ if (dst & 2) {
++ src -= 2;
++ dst -= 2;
++ count -= 2;
++ fb_writew(fb_readw(src), dst);
++ }
++ while (count > 3) {
++ src -= 4;
++ dst -= 4;
++ count -= 4;
++ fb_writel(fb_readl(src), dst);
++ }
++
++ restdown:
++ while (count--) {
++ src--;
++ dst--;
++ fb_writeb(fb_readb(src), dst);
++ }
++ }
++}
++
++#endif /* !__i386__ */
++
++#endif /* !__mc68000__ */
++
++#endif /* _VIDEO_ATAFB_UTILS_H */
+diff -urN linux-m68k/fs/fat/inode.c linux-schmitz/fs/fat/inode.c
+--- linux-m68k/fs/fat/inode.c 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/fs/fat/inode.c 2006-11-19 21:37:27.000000000 +0100
+@@ -11,7 +11,6 @@
+ */
+
+ #include <linux/module.h>
+-#include <linux/config.h>
+ #include <linux/init.h>
+ #include <linux/time.h>
+ #include <linux/slab.h>
+@@ -1358,8 +1357,8 @@
+ total_clusters = (total_sectors - sbi->data_start) / sbi->sec_per_clus;
+
+ if (!sbi->options.atari) {
+- if (sbi->fat_bits != 32)
+- sbi->fat_bits = (total_clusters > MAX_FAT12) ? 16 : 12;
++ if (sbi->fat_bits != 32)
++ sbi->fat_bits = (total_clusters > MAX_FAT12) ? 16 : 12;
+ } else {
+ int sectors;
+ /* Atari GEMDOS partitions always have 16-bit fat */
+diff -urN linux-m68k/include/asm-m68k/atarikb.h linux-schmitz/include/asm-m68k/atarikb.h
+--- linux-m68k/include/asm-m68k/atarikb.h 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/include/asm-m68k/atarikb.h 2006-11-19 21:37:27.000000000 +0100
+@@ -36,5 +36,11 @@
+ extern void (*atari_MIDI_interrupt_hook) (void);
+ /* Hook for mouse driver */
+ extern void (*atari_mouse_interrupt_hook) (char *);
++/* Hook for keyboard inputdev driver */
++extern void (*atari_input_keyboard_interrupt_hook) (unsigned char, char);
++/* Hook for mouse inputdev driver */
++extern void (*atari_input_mouse_interrupt_hook) (char *);
++
++extern struct pt_regs *atakbd_pt_regs;
+
+ #endif /* _LINUX_ATARIKB_H */
+diff -urN linux-m68k/include/asm-m68k/ide.h linux-schmitz/include/asm-m68k/ide.h
+--- linux-m68k/include/asm-m68k/ide.h 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/include/asm-m68k/ide.h 2006-11-19 21:37:27.000000000 +0100
+@@ -108,13 +108,19 @@
+ #ifdef CONFIG_BLK_DEV_FALCON_IDE
+ #define IDE_ARCH_LOCK
+
++/* MSch 20061021: even without avoiding registering the IDE interrupt on top
++ * of stdma_irq, I still get locking bug warnings suggesting mutiple instances
++ * of ide_do_request are running. The warning is printed now only if the status
++ * of falconide_intr_lock and stdma_islocked() conflict! */
++
+ extern int falconide_intr_lock;
+
+ static __inline__ void ide_release_lock (void)
+ {
+ if (MACH_IS_ATARI) {
+- if (falconide_intr_lock == 0) {
++ if (falconide_intr_lock == 0 && stdma_islocked()) {
+ printk("ide_release_lock: bug\n");
++ stdma_debug();
+ return;
+ }
+ falconide_intr_lock = 0;
+diff -urN linux-m68k/include/asm-m68k/io.h linux-schmitz/include/asm-m68k/io.h
+--- linux-m68k/include/asm-m68k/io.h 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/include/asm-m68k/io.h 2006-11-19 21:37:27.000000000 +0100
+@@ -82,9 +82,22 @@
+ #endif
+ #endif /* AMIGA_PCMCIA */
+
++#ifdef CONFIG_ATARI_ROM_ISA
+
++#define enec_isa_read_base 0xfffa0000
++#define enec_isa_write_base 0xfffb0000
+
+-#ifdef CONFIG_ISA
++#define ENEC_ISA_IO_B(ioaddr) (enec_isa_read_base+((((unsigned long)(ioaddr))&0x1F)<<9))
++#define ENEC_ISA_IO_W(ioaddr) (enec_isa_read_base+((((unsigned long)(ioaddr))&0x1F)<<9))
++#define ENEC_ISA_MEM_B(madr) (enec_isa_read_base+((((unsigned long)(madr))&0x1F)<<9))
++#define ENEC_ISA_MEM_W(madr) (enec_isa_read_base+((((unsigned long)(madr))&0x1F)<<9))
++
++#define MULTI_ISA 0
++#endif /* ATARI */
++
++
++
++#if defined(CONFIG_ISA) || defined(CONFIG_ATARI_ROM_ISA)
+
+ #if MULTI_ISA == 0
+ #undef MULTI_ISA
+@@ -93,6 +106,7 @@
+ #define Q40_ISA (1)
+ #define GG2_ISA (2)
+ #define AG_ISA (3)
++#define ENEC_ISA (4)
+
+ #if defined(CONFIG_Q40) && !defined(MULTI_ISA)
+ #define ISA_TYPE Q40_ISA
+@@ -106,6 +120,10 @@
+ #define ISA_TYPE GG2_ISA
+ #define ISA_SEX 0
+ #endif
++#if defined(CONFIG_ATARI_ROM_ISA) && !defined(MULTI_ISA)
++#define ISA_TYPE ENEC_ISA
++#define ISA_SEX 0
++#endif
+
+ #ifdef MULTI_ISA
+ extern int isa_type;
+@@ -133,6 +151,9 @@
+ #ifdef CONFIG_AMIGA_PCMCIA
+ case AG_ISA: return (u8 __iomem *)AG_ISA_IO_B(addr);
+ #endif
++#ifdef CONFIG_ATARI_ROM_ISA
++ case ENEC_ISA: return (u8 __iomem *)ENEC_ISA_IO_B(addr);
++#endif
+ default: return NULL; /* avoid warnings, just in case */
+ }
+ }
+@@ -149,6 +170,9 @@
+ #ifdef CONFIG_AMIGA_PCMCIA
+ case AG_ISA: return (u16 __iomem *)AG_ISA_IO_W(addr);
+ #endif
++#ifdef CONFIG_ATARI_ROM_ISA
++ case ENEC_ISA: return (u16 __iomem *)ENEC_ISA_IO_W(addr);
++#endif
+ default: return NULL; /* avoid warnings, just in case */
+ }
+ }
+@@ -165,6 +189,9 @@
+ #ifdef CONFIG_AMIGA_PCMCIA
+ case AG_ISA: return (u8 __iomem *)addr;
+ #endif
++#ifdef CONFIG_ATARI_ROM_ISA
++ case ENEC_ISA: return (u8 __iomem *)ENEC_ISA_MEM_B(addr);
++#endif
+ default: return NULL; /* avoid warnings, just in case */
+ }
+ }
+@@ -181,6 +208,9 @@
+ #ifdef CONFIG_AMIGA_PCMCIA
+ case AG_ISA: return (u16 __iomem *)addr;
+ #endif
++#ifdef CONFIG_ATARI_ROM_ISA
++ case ENEC_ISA: return (u16 __iomem *)ENEC_ISA_MEM_W(addr);
++#endif
+ default: return NULL; /* avoid warnings, just in case */
+ }
+ }
+@@ -200,6 +230,19 @@
+ (ISA_SEX ? out_be16(isa_mtw((unsigned long)(p)),(val)) \
+ : out_le16(isa_mtw((unsigned long)(p)),(val)))
+
++#if defined(CONFIG_ATARI_ROM_ISA)
++#define isa_rom_inb(port) rom_in_8(isa_itb(port))
++#define isa_rom_inw(port) (ISA_SEX ? rom_in_be16(isa_itw(port)) : rom_in_le16(isa_itw(port)))
++
++#define isa_rom_outb(val,port) rom_out_8(isa_itb(port),(val))
++#define isa_rom_outw(val,port) (ISA_SEX ? rom_out_be16(isa_itw(port),(val)) : rom_out_le16(isa_itw(port),(val)))
++
++#define isa_rom_writeb(val,p) rom_out_8(isa_mtb((unsigned long)(p)),(val))
++#define isa_rom_writew(val,p) \
++ (ISA_SEX ? rom_out_be16(isa_mtw((unsigned long)(p)),(val)) \
++ : rom_out_le16(isa_mtw((unsigned long)(p)),(val)))
++#endif
++
+ static inline void isa_delay(void)
+ {
+ switch(ISA_TYPE)
+@@ -213,6 +256,9 @@
+ #ifdef CONFIG_AMIGA_PCMCIA
+ case AG_ISA: break;
+ #endif
++#ifdef CONFIG_ATARI_ROM_ISA
++ case ENEC_ISA: break;
++#endif
+ default: break; /* avoid warnings */
+ }
+ }
+@@ -234,10 +280,33 @@
+ #define isa_outsw(port, buf, nr) \
+ (ISA_SEX ? raw_outsw(isa_itw(port), (u16 *)(buf), (nr)) : \
+ raw_outsw_swapw(isa_itw(port), (u16 *)(buf), (nr)))
++
++#if defined(CONFIG_ATARI_ROM_ISA)
++#define isa_rom_inb_p(p) ({u8 v=isa_rom_inb(p);isa_delay();v;})
++#define isa_rom_inw_p(p) ({u16 v=isa_rom_inw(p);isa_delay();v;})
++#define isa_rom_inl_p(p) ({u32 v=isa_rom_inl(p);isa_delay();v;})
++#define isa_rom_outb_p(v,p) ({isa_rom_outb((v),(p));isa_delay();})
++#define isa_rom_outw_p(v,p) ({isa_rom_outw((v),(p));isa_delay();})
++#define isa_rom_outl_p(v,p) ({isa_rom_outl((v),(p));isa_delay();})
++
++#define isa_rom_insb(port, buf, nr) raw_rom_insb(isa_itb(port), (u8 *)(buf), (nr))
++
++#define isa_rom_insw(port, buf, nr) \
++ (ISA_SEX ? raw_rom_insw(isa_itw(port), (u16 *)(buf), (nr)) : \
++ raw_rom_insw_swapw(isa_itw(port), (u16 *)(buf), (nr)))
++
++#define isa_rom_outsb(port, buf, nr) raw_rom_outsb(isa_itb(port), (u8 *)(buf), (nr))
++
++#define isa_rom_outsw(port, buf, nr) \
++ (ISA_SEX ? raw_rom_outsw(isa_itw(port), (u16 *)(buf), (nr)) : \
++ raw_rom_outsw_swapw(isa_itw(port), (u16 *)(buf), (nr)))
++
++#endif
++
+ #endif /* CONFIG_ISA */
+
+
+-#if defined(CONFIG_ISA) && !defined(CONFIG_PCI)
++#if defined(CONFIG_ISA) && !defined(CONFIG_PCI) && !defined(CONFIG_ATARI_ROM_ISA)
+ #define inb isa_inb
+ #define inb_p isa_inb_p
+ #define outb isa_outb
+@@ -306,7 +375,34 @@
+ #endif
+ #endif /* CONFIG_PCI */
+
+-#if !defined(CONFIG_ISA) && !defined(CONFIG_PCI) && defined(CONFIG_HP300)
++#if defined(CONFIG_ATARI_ROM_ISA)
++/*
++ * kernel with both ROM port ISA and IDE compiled in, those have
++ * conflicting defs for in/out. Simply consider port < 1024
++ * ROM port ISA and everything else regular ISA for IDE. read,write not defined
++ * in this case
++ */
++#define inb(port) ((port)<1024 ? isa_rom_inb(port) : in_8(port))
++#define inb_p(port) ((port)<1024 ? isa_rom_inb_p(port) : in_8(port))
++#define inw(port) ((port)<1024 ? isa_rom_inw(port) : in_le16(port))
++#define inw_p(port) ((port)<1024 ? isa_rom_inw_p(port) : in_le16(port))
++#define inl(port) ((port)<1024 ? isa_rom_inl(port) : in_le32(port))
++#define inl_p(port) ((port)<1024 ? isa_rom_inl_p(port) : in_le32(port))
++
++#define outb(val,port) ((port)<1024 ? isa_rom_outb((val),(port)) : out_8((port),(val)))
++#define outb_p(val,port) ((port)<1024 ? isa_rom_outb_p((val),(port)) : out_8((port),(val)))
++#define outw(val,port) ((port)<1024 ? isa_rom_outw((val),(port)) : out_le16((port),(val)))
++#define outw_p(val,port) ((port)<1024 ? isa_rom_outw_p((val),(port)) : out_le16((port),(val)))
++#define outl(val,port) ((port)<1024 ? isa_rom_outl((val),(port)) : out_le32((port),(val)))
++#define outl_p(val,port) ((port)<1024 ? isa_rom_outl_p((val),(port)) : out_le32((port),(val)))
++
++#define insb isa_rom_insb
++#define insw isa_rom_insw
++#define outsb isa_rom_outsb
++#define outsw isa_rom_outsw
++#endif
++
++#if !defined(CONFIG_ISA) && !defined(CONFIG_PCI) && !defined(CONFIG_ATARI_ROM_ISA) && defined(CONFIG_HP300)
+ /*
+ * We need to define dummy functions otherwise drivers/serial/8250.c doesn't link
+ */
Added: dists/sid/linux-2.6/debian/patches/m68k-atari.patch
==============================================================================
--- (empty file)
+++ dists/sid/linux-2.6/debian/patches/m68k-atari.patch Mon Nov 20 00:58:24 2006
@@ -0,0 +1,115 @@
+diff -urN linux-m68k/arch/m68k/atari/stdma.c linux-schmitz/arch/m68k/atari/stdma.c
+--- linux-m68k/arch/m68k/atari/stdma.c 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/arch/m68k/atari/stdma.c 2006-11-19 21:37:26.000000000 +0100
+@@ -48,7 +48,7 @@
+ static void *stdma_isr_data; /* data passed to isr */
+ static DECLARE_WAIT_QUEUE_HEAD(stdma_wait); /* wait queue for ST-DMA */
+
+-
++static unsigned long stdma_locked_from, stdma_released_from;
+
+
+ /***************************** Prototypes *****************************/
+@@ -90,6 +90,9 @@
+ stdma_locked = 1;
+ stdma_isr = handler;
+ stdma_isr_data = data;
++
++ stdma_locked_from = __builtin_return_address(0);
++
+ local_irq_restore(flags);
+ }
+
+@@ -114,6 +117,7 @@
+ stdma_locked = 0;
+ stdma_isr = NULL;
+ stdma_isr_data = NULL;
++ stdma_released_from = __builtin_return_address(0);
+ wake_up(&stdma_wait);
+
+ local_irq_restore(flags);
+@@ -159,6 +163,29 @@
+
+
+ /*
++ * Function: int stdma_debug( void )
++ *
++ * Purpose: Report current ST-DMA locking status.
++ * Note: Returned status is only valid if ints are disabled while calling and
++ * as long as they remain disabled.
++ * If called with ints enabled, status can change only from locked to
++ * unlocked, because ints may not lock the ST-DMA.
++ *
++ * Inputs: none
++ *
++ * Returns: none
++ *
++ */
++
++void stdma_debug(void)
++{
++ printk("stdma_debug at %p: status %d, locked from %p unlocked from %p\n",
++ __builtin_return_address(0), stdma_locked, stdma_locked_from,
++ stdma_released_from);
++ return;
++}
++
++/*
+ * Function: void stdma_init( void )
+ *
+ * Purpose: Initialize the ST-DMA chip access controlling.
+@@ -175,7 +202,9 @@
+ void __init stdma_init(void)
+ {
+ stdma_isr = NULL;
+- request_irq(IRQ_MFP_FDC, stdma_int, IRQ_TYPE_SLOW,
++ stdma_locked_from = NULL;
++ stdma_released_from = NULL;
++ request_irq(IRQ_MFP_FDC, stdma_int, IRQ_TYPE_SLOW /* | SA_SHIRQ */,
+ "ST-DMA: floppy/ACSI/IDE/Falcon-SCSI", stdma_int);
+ }
+
+diff -urN linux-m68k/arch/m68k/atari/time.c linux-schmitz/arch/m68k/atari/time.c
+--- linux-m68k/arch/m68k/atari/time.c 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/arch/m68k/atari/time.c 2006-11-19 21:37:26.000000000 +0100
+@@ -217,7 +217,7 @@
+ if (in_atomic() || irqs_disabled())
+ mdelay(1);
+ else
+- schedule_timeout_interruptible(HWCLK_POLL_INTERVAL);
++ schedule_timeout_interruptible(HWCLK_POLL_INTERVAL);
+ }
+
+ local_irq_save(flags);
+diff -urN linux-m68k/arch/m68k/kernel/entry.S linux-schmitz/arch/m68k/kernel/entry.S
+--- linux-m68k/arch/m68k/kernel/entry.S 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/arch/m68k/kernel/entry.S 2006-11-19 21:37:26.000000000 +0100
+@@ -711,4 +711,28 @@
+ .long sys_inotify_init
+ .long sys_inotify_add_watch /* 285 */
+ .long sys_inotify_rm_watch
++ .long sys_migrate_pages
++ .long sys_openat
++ .long sys_mkdirat
++ .long sys_mknodat /* 290 */
++ .long sys_fchownat
++ .long sys_futimesat
++ .long sys_fstatat64
++ .long sys_unlinkat
++ .long sys_renameat /* 295 */
++ .long sys_linkat
++ .long sys_symlinkat
++ .long sys_readlinkat
++ .long sys_fchmodat
++ .long sys_faccessat /* 300 */
++ .long sys_ni_syscall /* Reserved for pselect6 */
++ .long sys_ni_syscall /* Reserved for ppoll */
++ .long sys_unshare
++ .long sys_set_robust_list
++ .long sys_get_robust_list /* 305 */
++ .long sys_splice
++ .long sys_sync_file_range
++ .long sys_tee
++ .long sys_vmsplice
++ .long sys_move_pages /* 310 */
+
Added: dists/sid/linux-2.6/debian/patches/m68k-misc.patch
==============================================================================
--- (empty file)
+++ dists/sid/linux-2.6/debian/patches/m68k-misc.patch Mon Nov 20 00:58:24 2006
@@ -0,0 +1,136 @@
+diff -urN linux-m68k/arch/m68k/Makefile linux-schmitz/arch/m68k/Makefile
+--- linux-m68k/arch/m68k/Makefile 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/arch/m68k/Makefile 2006-11-19 21:37:26.000000000 +0100
+@@ -22,7 +22,7 @@
+ LDFLAGS_MODULE += -T $(srctree)/arch/m68k/kernel/module.lds
+ ifneq ($(COMPILE_ARCH),$(ARCH))
+ # prefix for cross-compiling binaries
+- CROSS_COMPILE = m68k-linux-
++ CROSS_COMPILE = m68k-linux-gnu-
+ endif
+
+ ifdef CONFIG_SUN3
+diff -urN linux-m68k/arch/m68k/kernel/vmlinux-std.lds linux-schmitz/arch/m68k/kernel/vmlinux-std.lds
+--- linux-m68k/arch/m68k/kernel/vmlinux-std.lds 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/arch/m68k/kernel/vmlinux-std.lds 2006-09-20 05:42:06.000000000 +0200
+@@ -66,11 +66,6 @@
+ __con_initcall_start = .;
+ .con_initcall.init : { *(.con_initcall.init) }
+ __con_initcall_end = .;
+- .m68k_fixup : {
+- __start_fixup = .;
+- *(.m68k_fixup)
+- __stop_fixup = .;
+- }
+ SECURITY_INIT
+ . = ALIGN(8192);
+ __initramfs_start = .;
+diff -urN linux-m68k/arch/m68k/kernel/vmlinux-sun3.lds linux-schmitz/arch/m68k/kernel/vmlinux-sun3.lds
+--- linux-m68k/arch/m68k/kernel/vmlinux-sun3.lds 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/arch/m68k/kernel/vmlinux-sun3.lds 2006-09-20 05:42:06.000000000 +0200
+@@ -8,7 +8,7 @@
+ jiffies = jiffies_64 + 4;
+ SECTIONS
+ {
+- . = 0xE002000;
++ . = 0xE004000;
+ _text = .; /* Text and read-only data */
+ .text : {
+ *(.head)
+@@ -60,11 +60,6 @@
+ __con_initcall_start = .;
+ .con_initcall.init : { *(.con_initcall.init) }
+ __con_initcall_end = .;
+- .m68k_fixup : {
+- __start_fixup = .;
+- *(.m68k_fixup)
+- __stop_fixup = .;
+- }
+ SECURITY_INIT
+ . = ALIGN(8192);
+ __initramfs_start = .;
+diff -urN linux-m68k/arch/m68k/mm/motorola.c linux-schmitz/arch/m68k/mm/motorola.c
+--- linux-m68k/arch/m68k/mm/motorola.c 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/arch/m68k/mm/motorola.c 2006-11-19 21:37:26.000000000 +0100
+@@ -47,7 +47,6 @@
+ #define INIT_MAPPED_SIZE (4UL<<20)
+
+ extern unsigned long availmem;
+-extern struct mem_info m68k_ramdisk;
+
+ static pte_t * __init kernel_page_table(void)
+ {
+@@ -211,11 +210,7 @@
+ int i;
+
+ #ifdef DEBUG
+- {
+- extern unsigned long availmem;
+- printk ("start of paging_init (%p, %lx)\n",
+- kernel_pg_dir, availmem);
+- }
++ printk ("start of paging_init (%p, %lx)\n", kernel_pg_dir, availmem);
+ #endif
+
+ /* Fix the cache mode in the page descriptors for the 680[46]0. */
+diff -urN linux-m68k/include/asm-m68k/unistd.h linux-schmitz/include/asm-m68k/unistd.h
+--- linux-m68k/include/asm-m68k/unistd.h 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/include/asm-m68k/unistd.h 2006-11-19 21:37:27.000000000 +0100
+@@ -289,10 +289,34 @@
+ #define __NR_inotify_init 284
+ #define __NR_inotify_add_watch 285
+ #define __NR_inotify_rm_watch 286
++#define __NR_migrate_pages 287
++#define __NR_openat 288
++#define __NR_mkdirat 289
++#define __NR_mknodat 290
++#define __NR_fchownat 291
++#define __NR_futimesat 292
++#define __NR_fstatat64 293
++#define __NR_unlinkat 294
++#define __NR_renameat 295
++#define __NR_linkat 296
++#define __NR_symlinkat 297
++#define __NR_readlinkat 298
++#define __NR_fchmodat 299
++#define __NR_faccessat 300
++#define __NR_pselect6 301
++#define __NR_ppoll 302
++#define __NR_unshare 303
++#define __NR_set_robust_list 304
++#define __NR_get_robust_list 305
++#define __NR_splice 306
++#define __NR_sync_file_range 307
++#define __NR_tee 308
++#define __NR_vmsplice 309
++#define __NR_move_pages 310
+
+ #ifdef __KERNEL__
+
+-#define NR_syscalls 287
++#define NR_syscalls 311
+
+ /* user-visible error numbers are in the range -1 - -124: see
+ <asm-m68k/errno.h> */
+diff -urN linux-m68k/include/linux/input.h linux-schmitz/include/linux/input.h
+--- linux-m68k/include/linux/input.h 2006-09-20 05:42:06.000000000 +0200
++++ linux-schmitz/include/linux/input.h 2006-11-19 21:37:27.000000000 +0100
+@@ -657,6 +657,7 @@
+ #define BUS_I2C 0x18
+ #define BUS_HOST 0x19
+ #define BUS_GSC 0x1A
++#define BUS_ATARI 0x1B
+
+ /*
+ * Values describing the status of an effect
+diff -urN linux-m68k/scripts/mod/modpost.c linux-schmitz/scripts/mod/modpost.c
+--- linux-m68k/scripts/mod/modpost.c 2006-11-19 21:35:33.000000000 +0100
++++ linux-schmitz/scripts/mod/modpost.c 2006-09-20 05:42:06.000000000 +0200
+@@ -1181,7 +1181,6 @@
+ buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n"
+ " .exit = cleanup_module,\n"
+ "#endif\n");
+- buf_printf(b, " .arch = MODULE_ARCH_INIT,\n");
+ buf_printf(b, "};\n");
+ }
+
Added: dists/sid/linux-2.6/debian/patches/series/6-extra
==============================================================================
--- (empty file)
+++ dists/sid/linux-2.6/debian/patches/series/6-extra Mon Nov 20 00:58:24 2006
@@ -0,0 +1,10 @@
++ m68k-misc.patch m68k
++ m68k-amiga.patch m68k
++ m68k-atari.patch m68k
++ m68k-atari-ethernet.patch m68k
++ m68k-atari-keyboard.patch m68k
++ m68k-atari-mouse.patch m68k
++ m68k-atari-scsi.patch m68k
++ m68k-atari-serial.patch m68k
++ m68k-atari-video.patch m68k
++ m68k-apollo.patch m68k
More information about the Kernel-svn-changes
mailing list