q40ide.c 4.12 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2
 *  Q40 I/O port IDE Driver
Linus Torvalds's avatar
Linus Torvalds committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 *     (c) Richard Zidlicky
 *
 *  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/types.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#include <linux/ide.h>
18
#include <linux/module.h>
Linus Torvalds's avatar
Linus Torvalds committed
19

20 21
#include <asm/ide.h>

Linus Torvalds's avatar
Linus Torvalds committed
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
    /*
     *  Bases of the IDE interfaces
     */

#define Q40IDE_NUM_HWIFS	2

#define PCIDE_BASE1	0x1f0
#define PCIDE_BASE2	0x170
#define PCIDE_BASE3	0x1e8
#define PCIDE_BASE4	0x168
#define PCIDE_BASE5	0x1e0
#define PCIDE_BASE6	0x160

static const unsigned long pcide_bases[Q40IDE_NUM_HWIFS] = {
    PCIDE_BASE1, PCIDE_BASE2, /* PCIDE_BASE3, PCIDE_BASE4  , PCIDE_BASE5,
    PCIDE_BASE6 */
};

static int q40ide_default_irq(unsigned long base)
{
           switch (base) {
	            case 0x1f0: return 14;
		    case 0x170: return 15;
		    case 0x1e8: return 11;
		    default:
			return 0;
	   }
}


/*
53
 * Addresses are pretranslated for Q40 ISA access.
Linus Torvalds's avatar
Linus Torvalds committed
54
 */
55
static void q40_ide_setup_ports(struct ide_hw *hw, unsigned long base, int irq)
Linus Torvalds's avatar
Linus Torvalds committed
56
{
57
	memset(hw, 0, sizeof(*hw));
Al Viro's avatar
Al Viro committed
58 59 60 61 62 63 64 65 66 67 68
	/* BIG FAT WARNING: 
	   assumption: only DATA port is ever used in 16 bit mode */
	hw->io_ports.data_addr = Q40_ISA_IO_W(base);
	hw->io_ports.error_addr = Q40_ISA_IO_B(base + 1);
	hw->io_ports.nsect_addr = Q40_ISA_IO_B(base + 2);
	hw->io_ports.lbal_addr = Q40_ISA_IO_B(base + 3);
	hw->io_ports.lbam_addr = Q40_ISA_IO_B(base + 4);
	hw->io_ports.lbah_addr = Q40_ISA_IO_B(base + 5);
	hw->io_ports.device_addr = Q40_ISA_IO_B(base + 6);
	hw->io_ports.status_addr = Q40_ISA_IO_B(base + 7);
	hw->io_ports.ctl_addr = Q40_ISA_IO_B(base + 0x206);
69

Linus Torvalds's avatar
Linus Torvalds committed
70 71 72
	hw->irq = irq;
}

73
static void q40ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
74
			      void *buf, unsigned int len)
75
{
76
	unsigned long data_addr = drive->hwif->io_ports.data_addr;
77

78 79 80 81
	if (drive->media == ide_disk && cmd && (cmd->tf_flags & IDE_TFLAG_FS)) {
		__ide_mm_insw(data_addr, buf, (len + 1) / 2);
		return;
	}
82

83
	raw_insw_swapw((u16 *)data_addr, buf, (len + 1) / 2);
84 85
}

86
static void q40ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
87
			       void *buf, unsigned int len)
88
{
89 90
	unsigned long data_addr = drive->hwif->io_ports.data_addr;

91 92 93 94
	if (drive->media == ide_disk && cmd && (cmd->tf_flags & IDE_TFLAG_FS)) {
		__ide_mm_outsw(data_addr, buf, (len + 1) / 2);
		return;
	}
95

96
	raw_outsw_swapw((u16 *)data_addr, buf, (len + 1) / 2);
97
}
Linus Torvalds's avatar
Linus Torvalds committed
98

99 100 101 102 103
/* Q40 has a byte-swapped IDE interface */
static const struct ide_tp_ops q40ide_tp_ops = {
	.exec_command		= ide_exec_command,
	.read_status		= ide_read_status,
	.read_altstatus		= ide_read_altstatus,
104
	.write_devctl		= ide_write_devctl,
105

106
	.dev_select		= ide_dev_select,
107 108 109 110 111 112 113 114 115
	.tf_load		= ide_tf_load,
	.tf_read		= ide_tf_read,

	.input_data		= q40ide_input_data,
	.output_data		= q40ide_output_data,
};

static const struct ide_port_info q40ide_port_info = {
	.tp_ops			= &q40ide_tp_ops,
116
	.host_flags		= IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA,
117
	.irq_flags		= IRQF_SHARED,
118
	.chipset		= ide_generic,
119 120
};

Linus Torvalds's avatar
Linus Torvalds committed
121 122
/* 
 * the static array is needed to have the name reported in /proc/ioports,
123
 * hwif->name unfortunately isn't available yet
Linus Torvalds's avatar
Linus Torvalds committed
124 125 126 127 128 129 130 131 132
 */
static const char *q40_ide_names[Q40IDE_NUM_HWIFS]={
	"ide0", "ide1"
};

/*
 *  Probe for Q40 IDE interfaces
 */

133
static int __init q40ide_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
134 135
{
    int i;
136
    struct ide_hw hw[Q40IDE_NUM_HWIFS], *hws[] = { NULL, NULL };
Linus Torvalds's avatar
Linus Torvalds committed
137 138

    if (!MACH_IS_Q40)
139
      return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
140

141 142
    printk(KERN_INFO "ide: Q40 IDE controller\n");

Linus Torvalds's avatar
Linus Torvalds committed
143
    for (i = 0; i < Q40IDE_NUM_HWIFS; i++) {
144
	const char *name = q40_ide_names[i];
Linus Torvalds's avatar
Linus Torvalds committed
145 146 147 148 149 150 151 152 153 154 155 156

	if (!request_region(pcide_bases[i], 8, name)) {
		printk("could not reserve ports %lx-%lx for %s\n",
		       pcide_bases[i],pcide_bases[i]+8,name);
		continue;
	}
	if (!request_region(pcide_bases[i]+0x206, 1, name)) {
		printk("could not reserve port %lx for %s\n",
		       pcide_bases[i]+0x206,name);
		release_region(pcide_bases[i], 8);
		continue;
	}
157
	q40_ide_setup_ports(&hw[i], pcide_bases[i],
Linus Torvalds's avatar
Linus Torvalds committed
158
			q40ide_default_irq(pcide_bases[i]));
159

160
	hws[i] = &hw[i];
Linus Torvalds's avatar
Linus Torvalds committed
161
    }
162

163
    return ide_host_add(&q40ide_port_info, hws, Q40IDE_NUM_HWIFS, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
164 165
}

166
module_init(q40ide_init);
167 168

MODULE_LICENSE("GPL");