18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 1996-2001 Linus Torvalds & author (see below) 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/* 78c2ecf20Sopenharmony_ci * Version 0.03 Cleaned auto-tune, added probe 88c2ecf20Sopenharmony_ci * Version 0.04 Added second channel tuning 98c2ecf20Sopenharmony_ci * Version 0.05 Enhanced tuning ; added qd6500 support 108c2ecf20Sopenharmony_ci * Version 0.06 Added dos driver's list 118c2ecf20Sopenharmony_ci * Version 0.07 Second channel bug fix 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * QDI QD6500/QD6580 EIDE controller fast support 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * To activate controller support, use "ide0=qd65xx" 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* 198c2ecf20Sopenharmony_ci * Rewritten from the work of Colten Edwards <pje120@cs.usask.ca> by 208c2ecf20Sopenharmony_ci * Samuel Thibault <samuel.thibault@ens-lyon.org> 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <linux/types.h> 258c2ecf20Sopenharmony_ci#include <linux/kernel.h> 268c2ecf20Sopenharmony_ci#include <linux/delay.h> 278c2ecf20Sopenharmony_ci#include <linux/timer.h> 288c2ecf20Sopenharmony_ci#include <linux/mm.h> 298c2ecf20Sopenharmony_ci#include <linux/ioport.h> 308c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 318c2ecf20Sopenharmony_ci#include <linux/ide.h> 328c2ecf20Sopenharmony_ci#include <linux/init.h> 338c2ecf20Sopenharmony_ci#include <asm/io.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define DRV_NAME "qd65xx" 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include "qd65xx.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * I/O ports are 0x30-0x31 (and 0x32-0x33 for qd6580) 418c2ecf20Sopenharmony_ci * or 0xb0-0xb1 (and 0xb2-0xb3 for qd6580) 428c2ecf20Sopenharmony_ci * -- qd6500 is a single IDE interface 438c2ecf20Sopenharmony_ci * -- qd6580 is a dual IDE interface 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * More research on qd6580 being done by willmore@cig.mot.com (David) 468c2ecf20Sopenharmony_ci * More Information given by Petr Soucek (petr@ryston.cz) 478c2ecf20Sopenharmony_ci * http://www.ryston.cz/petr/vlb 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * base: Timer1 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * base+0x01: Config (R/O) 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * bit 0: ide baseport: 1 = 0x1f0 ; 0 = 0x170 (only useful for qd6500) 578c2ecf20Sopenharmony_ci * bit 1: qd65xx baseport: 1 = 0xb0 ; 0 = 0x30 588c2ecf20Sopenharmony_ci * bit 2: ID3: bus speed: 1 = <=33MHz ; 0 = >33MHz 598c2ecf20Sopenharmony_ci * bit 3: qd6500: 1 = disabled, 0 = enabled 608c2ecf20Sopenharmony_ci * qd6580: 1 618c2ecf20Sopenharmony_ci * upper nibble: 628c2ecf20Sopenharmony_ci * qd6500: 1100 638c2ecf20Sopenharmony_ci * qd6580: either 1010 or 0101 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci * base+0x02: Timer2 (qd6580 only) 678c2ecf20Sopenharmony_ci * 688c2ecf20Sopenharmony_ci * 698c2ecf20Sopenharmony_ci * base+0x03: Control (qd6580 only) 708c2ecf20Sopenharmony_ci * 718c2ecf20Sopenharmony_ci * bits 0-3 must always be set 1 728c2ecf20Sopenharmony_ci * bit 4 must be set 1, but is set 0 by dos driver while measuring vlb clock 738c2ecf20Sopenharmony_ci * bit 0 : 1 = Only primary port enabled : channel 0 for hda, channel 1 for hdb 748c2ecf20Sopenharmony_ci * 0 = Primary and Secondary ports enabled : channel 0 for hda & hdb 758c2ecf20Sopenharmony_ci * channel 1 for hdc & hdd 768c2ecf20Sopenharmony_ci * bit 1 : 1 = only disks on primary port 778c2ecf20Sopenharmony_ci * 0 = disks & ATAPI devices on primary port 788c2ecf20Sopenharmony_ci * bit 2-4 : always 0 798c2ecf20Sopenharmony_ci * bit 5 : status, but of what ? 808c2ecf20Sopenharmony_ci * bit 6 : always set 1 by dos driver 818c2ecf20Sopenharmony_ci * bit 7 : set 1 for non-ATAPI devices on primary port 828c2ecf20Sopenharmony_ci * (maybe read-ahead and post-write buffer ?) 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int timings[4]={-1,-1,-1,-1}; /* stores current timing for each timer */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* 888c2ecf20Sopenharmony_ci * qd65xx_select: 898c2ecf20Sopenharmony_ci * 908c2ecf20Sopenharmony_ci * This routine is invoked to prepare for access to a given drive. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void qd65xx_dev_select(ide_drive_t *drive) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci u8 index = (( (QD_TIMREG(drive)) & 0x80 ) >> 7) | 968c2ecf20Sopenharmony_ci (QD_TIMREG(drive) & 0x02); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (timings[index] != QD_TIMING(drive)) 998c2ecf20Sopenharmony_ci outb(timings[index] = QD_TIMING(drive), QD_TIMREG(drive)); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci outb(drive->select | ATA_DEVICE_OBS, drive->hwif->io_ports.device_addr); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * qd6500_compute_timing 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * computes the timing value where 1088c2ecf20Sopenharmony_ci * lower nibble represents active time, in count of VLB clocks 1098c2ecf20Sopenharmony_ci * upper nibble represents recovery time, in count of VLB clocks 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic u8 qd6500_compute_timing (ide_hwif_t *hwif, int active_time, int recovery_time) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci int clk = ide_vlb_clk ? ide_vlb_clk : 50; 1158c2ecf20Sopenharmony_ci u8 act_cyc, rec_cyc; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (clk <= 33) { 1188c2ecf20Sopenharmony_ci act_cyc = 9 - IDE_IN(active_time * clk / 1000 + 1, 2, 9); 1198c2ecf20Sopenharmony_ci rec_cyc = 15 - IDE_IN(recovery_time * clk / 1000 + 1, 0, 15); 1208c2ecf20Sopenharmony_ci } else { 1218c2ecf20Sopenharmony_ci act_cyc = 8 - IDE_IN(active_time * clk / 1000 + 1, 1, 8); 1228c2ecf20Sopenharmony_ci rec_cyc = 18 - IDE_IN(recovery_time * clk / 1000 + 1, 3, 18); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return (rec_cyc << 4) | 0x08 | act_cyc; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* 1298c2ecf20Sopenharmony_ci * qd6580_compute_timing 1308c2ecf20Sopenharmony_ci * 1318c2ecf20Sopenharmony_ci * idem for qd6580 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic u8 qd6580_compute_timing (int active_time, int recovery_time) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci int clk = ide_vlb_clk ? ide_vlb_clk : 50; 1378c2ecf20Sopenharmony_ci u8 act_cyc, rec_cyc; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci act_cyc = 17 - IDE_IN(active_time * clk / 1000 + 1, 2, 17); 1408c2ecf20Sopenharmony_ci rec_cyc = 15 - IDE_IN(recovery_time * clk / 1000 + 1, 2, 15); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return (rec_cyc << 4) | act_cyc; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* 1468c2ecf20Sopenharmony_ci * qd_find_disk_type 1478c2ecf20Sopenharmony_ci * 1488c2ecf20Sopenharmony_ci * tries to find timing from dos driver's table 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int qd_find_disk_type (ide_drive_t *drive, 1528c2ecf20Sopenharmony_ci int *active_time, int *recovery_time) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct qd65xx_timing_s *p; 1558c2ecf20Sopenharmony_ci char *m = (char *)&drive->id[ATA_ID_PROD]; 1568c2ecf20Sopenharmony_ci char model[ATA_ID_PROD_LEN]; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (*m == 0) 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci strncpy(model, m, ATA_ID_PROD_LEN); 1628c2ecf20Sopenharmony_ci ide_fixstring(model, ATA_ID_PROD_LEN, 1); /* byte-swap */ 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci for (p = qd65xx_timing ; p->offset != -1 ; p++) { 1658c2ecf20Sopenharmony_ci if (!strncmp(p->model, model+p->offset, 4)) { 1668c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: listed !\n", drive->name); 1678c2ecf20Sopenharmony_ci *active_time = p->active; 1688c2ecf20Sopenharmony_ci *recovery_time = p->recovery; 1698c2ecf20Sopenharmony_ci return 1; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* 1768c2ecf20Sopenharmony_ci * qd_set_timing: 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * records the timing 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void qd_set_timing (ide_drive_t *drive, u8 timing) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci unsigned long data = (unsigned long)ide_get_drivedata(drive); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci data &= 0xff00; 1868c2ecf20Sopenharmony_ci data |= timing; 1878c2ecf20Sopenharmony_ci ide_set_drivedata(drive, (void *)data); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: %#x\n", drive->name, timing); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void qd6500_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci u16 *id = drive->id; 1958c2ecf20Sopenharmony_ci int active_time = 175; 1968c2ecf20Sopenharmony_ci int recovery_time = 415; /* worst case values from the dos driver */ 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* FIXME: use drive->pio_mode value */ 1998c2ecf20Sopenharmony_ci if (!qd_find_disk_type(drive, &active_time, &recovery_time) && 2008c2ecf20Sopenharmony_ci (id[ATA_ID_OLD_PIO_MODES] & 0xff) && (id[ATA_ID_FIELD_VALID] & 2) && 2018c2ecf20Sopenharmony_ci id[ATA_ID_EIDE_PIO] >= 240) { 2028c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: PIO mode%d\n", drive->name, 2038c2ecf20Sopenharmony_ci id[ATA_ID_OLD_PIO_MODES] & 0xff); 2048c2ecf20Sopenharmony_ci active_time = 110; 2058c2ecf20Sopenharmony_ci recovery_time = drive->id[ATA_ID_EIDE_PIO] - 120; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci qd_set_timing(drive, qd6500_compute_timing(drive->hwif, 2098c2ecf20Sopenharmony_ci active_time, recovery_time)); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic void qd6580_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci const u8 pio = drive->pio_mode - XFER_PIO_0; 2158c2ecf20Sopenharmony_ci struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio); 2168c2ecf20Sopenharmony_ci unsigned int cycle_time; 2178c2ecf20Sopenharmony_ci int active_time = 175; 2188c2ecf20Sopenharmony_ci int recovery_time = 415; /* worst case values from the dos driver */ 2198c2ecf20Sopenharmony_ci u8 base = (hwif->config_data & 0xff00) >> 8; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (drive->id && !qd_find_disk_type(drive, &active_time, &recovery_time)) { 2228c2ecf20Sopenharmony_ci cycle_time = ide_pio_cycle_time(drive, pio); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci switch (pio) { 2258c2ecf20Sopenharmony_ci case 0: break; 2268c2ecf20Sopenharmony_ci case 3: 2278c2ecf20Sopenharmony_ci if (cycle_time >= 110) { 2288c2ecf20Sopenharmony_ci active_time = 86; 2298c2ecf20Sopenharmony_ci recovery_time = cycle_time - 102; 2308c2ecf20Sopenharmony_ci } else 2318c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Strange recovery time !\n",drive->name); 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci case 4: 2348c2ecf20Sopenharmony_ci if (cycle_time >= 69) { 2358c2ecf20Sopenharmony_ci active_time = 70; 2368c2ecf20Sopenharmony_ci recovery_time = cycle_time - 61; 2378c2ecf20Sopenharmony_ci } else 2388c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Strange recovery time !\n",drive->name); 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci default: 2418c2ecf20Sopenharmony_ci if (cycle_time >= 180) { 2428c2ecf20Sopenharmony_ci active_time = 110; 2438c2ecf20Sopenharmony_ci recovery_time = cycle_time - 120; 2448c2ecf20Sopenharmony_ci } else { 2458c2ecf20Sopenharmony_ci active_time = t->active; 2468c2ecf20Sopenharmony_ci recovery_time = cycle_time - active_time; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: PIO mode%d\n", drive->name,pio); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (!hwif->channel && drive->media != ide_disk) { 2538c2ecf20Sopenharmony_ci outb(0x5f, QD_CONTROL_PORT); 2548c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: ATAPI: disabled read-ahead FIFO " 2558c2ecf20Sopenharmony_ci "and post-write buffer on %s.\n", 2568c2ecf20Sopenharmony_ci drive->name, hwif->name); 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci qd_set_timing(drive, qd6580_compute_timing(active_time, recovery_time)); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* 2638c2ecf20Sopenharmony_ci * qd_testreg 2648c2ecf20Sopenharmony_ci * 2658c2ecf20Sopenharmony_ci * tests if the given port is a register 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int __init qd_testreg(int port) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci unsigned long flags; 2718c2ecf20Sopenharmony_ci u8 savereg, readreg; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci local_irq_save(flags); 2748c2ecf20Sopenharmony_ci savereg = inb_p(port); 2758c2ecf20Sopenharmony_ci outb_p(QD_TESTVAL, port); /* safe value */ 2768c2ecf20Sopenharmony_ci readreg = inb_p(port); 2778c2ecf20Sopenharmony_ci outb(savereg, port); 2788c2ecf20Sopenharmony_ci local_irq_restore(flags); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (savereg == QD_TESTVAL) { 2818c2ecf20Sopenharmony_ci printk(KERN_ERR "Outch ! the probe for qd65xx isn't reliable !\n"); 2828c2ecf20Sopenharmony_ci printk(KERN_ERR "Please contact maintainers to tell about your hardware\n"); 2838c2ecf20Sopenharmony_ci printk(KERN_ERR "Assuming qd65xx is not present.\n"); 2848c2ecf20Sopenharmony_ci return 1; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return (readreg != QD_TESTVAL); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic void __init qd6500_init_dev(ide_drive_t *drive) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 2938c2ecf20Sopenharmony_ci u8 base = (hwif->config_data & 0xff00) >> 8; 2948c2ecf20Sopenharmony_ci u8 config = QD_CONFIG(hwif); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci ide_set_drivedata(drive, (void *)QD6500_DEF_DATA); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic void __init qd6580_init_dev(ide_drive_t *drive) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci ide_hwif_t *hwif = drive->hwif; 3028c2ecf20Sopenharmony_ci unsigned long t1, t2; 3038c2ecf20Sopenharmony_ci u8 base = (hwif->config_data & 0xff00) >> 8; 3048c2ecf20Sopenharmony_ci u8 config = QD_CONFIG(hwif); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (hwif->host_flags & IDE_HFLAG_SINGLE) { 3078c2ecf20Sopenharmony_ci t1 = QD6580_DEF_DATA; 3088c2ecf20Sopenharmony_ci t2 = QD6580_DEF_DATA2; 3098c2ecf20Sopenharmony_ci } else 3108c2ecf20Sopenharmony_ci t2 = t1 = hwif->channel ? QD6580_DEF_DATA2 : QD6580_DEF_DATA; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci ide_set_drivedata(drive, (void *)((drive->dn & 1) ? t2 : t1)); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic const struct ide_tp_ops qd65xx_tp_ops = { 3168c2ecf20Sopenharmony_ci .exec_command = ide_exec_command, 3178c2ecf20Sopenharmony_ci .read_status = ide_read_status, 3188c2ecf20Sopenharmony_ci .read_altstatus = ide_read_altstatus, 3198c2ecf20Sopenharmony_ci .write_devctl = ide_write_devctl, 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci .dev_select = qd65xx_dev_select, 3228c2ecf20Sopenharmony_ci .tf_load = ide_tf_load, 3238c2ecf20Sopenharmony_ci .tf_read = ide_tf_read, 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci .input_data = ide_input_data, 3268c2ecf20Sopenharmony_ci .output_data = ide_output_data, 3278c2ecf20Sopenharmony_ci}; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic const struct ide_port_ops qd6500_port_ops = { 3308c2ecf20Sopenharmony_ci .init_dev = qd6500_init_dev, 3318c2ecf20Sopenharmony_ci .set_pio_mode = qd6500_set_pio_mode, 3328c2ecf20Sopenharmony_ci}; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic const struct ide_port_ops qd6580_port_ops = { 3358c2ecf20Sopenharmony_ci .init_dev = qd6580_init_dev, 3368c2ecf20Sopenharmony_ci .set_pio_mode = qd6580_set_pio_mode, 3378c2ecf20Sopenharmony_ci}; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic const struct ide_port_info qd65xx_port_info __initconst = { 3408c2ecf20Sopenharmony_ci .name = DRV_NAME, 3418c2ecf20Sopenharmony_ci .tp_ops = &qd65xx_tp_ops, 3428c2ecf20Sopenharmony_ci .chipset = ide_qd65xx, 3438c2ecf20Sopenharmony_ci .host_flags = IDE_HFLAG_IO_32BIT | 3448c2ecf20Sopenharmony_ci IDE_HFLAG_NO_DMA, 3458c2ecf20Sopenharmony_ci .pio_mask = ATA_PIO4, 3468c2ecf20Sopenharmony_ci}; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci/* 3498c2ecf20Sopenharmony_ci * qd_probe: 3508c2ecf20Sopenharmony_ci * 3518c2ecf20Sopenharmony_ci * looks at the specified baseport, and if qd found, registers & initialises it 3528c2ecf20Sopenharmony_ci * return 1 if another qd may be probed 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int __init qd_probe(int base) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci int rc; 3588c2ecf20Sopenharmony_ci u8 config, unit, control; 3598c2ecf20Sopenharmony_ci struct ide_port_info d = qd65xx_port_info; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci config = inb(QD_CONFIG_PORT); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (! ((config & QD_CONFIG_BASEPORT) >> 1 == (base == 0xb0)) ) 3648c2ecf20Sopenharmony_ci return -ENODEV; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci unit = ! (config & QD_CONFIG_IDE_BASEPORT); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (unit) 3698c2ecf20Sopenharmony_ci d.host_flags |= IDE_HFLAG_QD_2ND_PORT; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci switch (config & 0xf0) { 3728c2ecf20Sopenharmony_ci case QD_CONFIG_QD6500: 3738c2ecf20Sopenharmony_ci if (qd_testreg(base)) 3748c2ecf20Sopenharmony_ci return -ENODEV; /* bad register */ 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (config & QD_CONFIG_DISABLED) { 3778c2ecf20Sopenharmony_ci printk(KERN_WARNING "qd6500 is disabled !\n"); 3788c2ecf20Sopenharmony_ci return -ENODEV; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci printk(KERN_NOTICE "qd6500 at %#x\n", base); 3828c2ecf20Sopenharmony_ci printk(KERN_DEBUG "qd6500: config=%#x, ID3=%u\n", 3838c2ecf20Sopenharmony_ci config, QD_ID3); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci d.port_ops = &qd6500_port_ops; 3868c2ecf20Sopenharmony_ci d.host_flags |= IDE_HFLAG_SINGLE; 3878c2ecf20Sopenharmony_ci break; 3888c2ecf20Sopenharmony_ci case QD_CONFIG_QD6580_A: 3898c2ecf20Sopenharmony_ci case QD_CONFIG_QD6580_B: 3908c2ecf20Sopenharmony_ci if (qd_testreg(base) || qd_testreg(base + 0x02)) 3918c2ecf20Sopenharmony_ci return -ENODEV; /* bad registers */ 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci control = inb(QD_CONTROL_PORT); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci printk(KERN_NOTICE "qd6580 at %#x\n", base); 3968c2ecf20Sopenharmony_ci printk(KERN_DEBUG "qd6580: config=%#x, control=%#x, ID3=%u\n", 3978c2ecf20Sopenharmony_ci config, control, QD_ID3); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci outb(QD_DEF_CONTR, QD_CONTROL_PORT); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci d.port_ops = &qd6580_port_ops; 4028c2ecf20Sopenharmony_ci if (control & QD_CONTR_SEC_DISABLED) 4038c2ecf20Sopenharmony_ci d.host_flags |= IDE_HFLAG_SINGLE; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci printk(KERN_INFO "qd6580: %s IDE board\n", 4068c2ecf20Sopenharmony_ci (control & QD_CONTR_SEC_DISABLED) ? "single" : "dual"); 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci default: 4098c2ecf20Sopenharmony_ci return -ENODEV; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci rc = ide_legacy_device_add(&d, (base << 8) | config); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (d.host_flags & IDE_HFLAG_SINGLE) 4158c2ecf20Sopenharmony_ci return (rc == 0) ? 1 : rc; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return rc; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic bool probe_qd65xx; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cimodule_param_named(probe, probe_qd65xx, bool, 0); 4238c2ecf20Sopenharmony_ciMODULE_PARM_DESC(probe, "probe for QD65xx chipsets"); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic int __init qd65xx_init(void) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci int rc1, rc2 = -ENODEV; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (probe_qd65xx == 0) 4308c2ecf20Sopenharmony_ci return -ENODEV; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci rc1 = qd_probe(0x30); 4338c2ecf20Sopenharmony_ci if (rc1) 4348c2ecf20Sopenharmony_ci rc2 = qd_probe(0xb0); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (rc1 < 0 && rc2 < 0) 4378c2ecf20Sopenharmony_ci return -ENODEV; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cimodule_init(qd65xx_init); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Samuel Thibault"); 4458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("support of qd65xx vlb ide chipset"); 4468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 447