1/* 2 * Amiga Gayle IDE Driver 3 * 4 * Created 9 Jul 1997 by Geert Uytterhoeven 5 * 6 * This file is subject to the terms and conditions of the GNU General Public 7 * License. See the file COPYING in the main directory of this archive for 8 * more details. 9 */ 10 11#include <linux/types.h> 12#include <linux/mm.h> 13#include <linux/interrupt.h> 14#include <linux/blkdev.h> 15#include <linux/ide.h> 16#include <linux/init.h> 17#include <linux/zorro.h> 18#include <linux/module.h> 19#include <linux/platform_device.h> 20 21#include <asm/setup.h> 22#include <asm/amigahw.h> 23#include <asm/amigaints.h> 24#include <asm/amigayle.h> 25 26 27 /* 28 * Offsets from one of the above bases 29 */ 30 31#define GAYLE_CONTROL 0x101a 32 33 /* 34 * These are at different offsets from the base 35 */ 36 37#define GAYLE_IRQ_4000 0xdd3020 /* MSB = 1, Harddisk is source of */ 38#define GAYLE_IRQ_1200 0xda9000 /* interrupt */ 39 40 41 /* 42 * Offset of the secondary port for IDE doublers 43 * Note that GAYLE_CONTROL is NOT available then! 44 */ 45 46#define GAYLE_NEXT_PORT 0x1000 47 48#define GAYLE_NUM_HWIFS 2 49#define GAYLE_NUM_PROBE_HWIFS (ide_doubler ? GAYLE_NUM_HWIFS : \ 50 GAYLE_NUM_HWIFS-1) 51#define GAYLE_HAS_CONTROL_REG (!ide_doubler) 52 53static bool ide_doubler; 54module_param_named(doubler, ide_doubler, bool, 0); 55MODULE_PARM_DESC(doubler, "enable support for IDE doublers"); 56 57 /* 58 * Check and acknowledge the interrupt status 59 */ 60 61static int gayle_test_irq(ide_hwif_t *hwif) 62{ 63 unsigned char ch; 64 65 ch = z_readb(hwif->io_ports.irq_addr); 66 if (!(ch & GAYLE_IRQ_IDE)) 67 return 0; 68 return 1; 69} 70 71static void gayle_a1200_clear_irq(ide_drive_t *drive) 72{ 73 ide_hwif_t *hwif = drive->hwif; 74 75 (void)z_readb(hwif->io_ports.status_addr); 76 z_writeb(0x7c, hwif->io_ports.irq_addr); 77} 78 79static void __init gayle_setup_ports(struct ide_hw *hw, unsigned long base, 80 unsigned long ctl, unsigned long irq_port) 81{ 82 int i; 83 84 memset(hw, 0, sizeof(*hw)); 85 86 hw->io_ports.data_addr = base; 87 88 for (i = 1; i < 8; i++) 89 hw->io_ports_array[i] = base + 2 + i * 4; 90 91 hw->io_ports.ctl_addr = ctl; 92 hw->io_ports.irq_addr = irq_port; 93 94 hw->irq = IRQ_AMIGA_PORTS; 95} 96 97static const struct ide_port_ops gayle_a4000_port_ops = { 98 .test_irq = gayle_test_irq, 99}; 100 101static const struct ide_port_ops gayle_a1200_port_ops = { 102 .clear_irq = gayle_a1200_clear_irq, 103 .test_irq = gayle_test_irq, 104}; 105 106static const struct ide_port_info gayle_port_info = { 107 .host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_SERIALIZE | 108 IDE_HFLAG_NO_DMA, 109 .irq_flags = IRQF_SHARED, 110 .chipset = ide_generic, 111}; 112 113 /* 114 * Probe for a Gayle IDE interface (and optionally for an IDE doubler) 115 */ 116 117static int __init amiga_gayle_ide_probe(struct platform_device *pdev) 118{ 119 struct resource *res; 120 struct gayle_ide_platform_data *pdata; 121 unsigned long base, ctrlport, irqport; 122 unsigned int i; 123 int error; 124 struct ide_hw hw[GAYLE_NUM_HWIFS], *hws[GAYLE_NUM_HWIFS]; 125 struct ide_port_info d = gayle_port_info; 126 struct ide_host *host; 127 128 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 129 if (!res) 130 return -ENODEV; 131 132 if (!request_mem_region(res->start, resource_size(res), "IDE")) 133 return -EBUSY; 134 135 pdata = dev_get_platdata(&pdev->dev); 136 pr_info("ide: Gayle IDE controller (A%u style%s)\n", 137 pdata->explicit_ack ? 1200 : 4000, 138 ide_doubler ? ", IDE doubler" : ""); 139 140 base = (unsigned long)ZTWO_VADDR(pdata->base); 141 ctrlport = 0; 142 irqport = (unsigned long)ZTWO_VADDR(pdata->irqport); 143 if (pdata->explicit_ack) 144 d.port_ops = &gayle_a1200_port_ops; 145 else 146 d.port_ops = &gayle_a4000_port_ops; 147 148 for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++, base += GAYLE_NEXT_PORT) { 149 if (GAYLE_HAS_CONTROL_REG) 150 ctrlport = base + GAYLE_CONTROL; 151 152 gayle_setup_ports(&hw[i], base, ctrlport, irqport); 153 hws[i] = &hw[i]; 154 } 155 156 error = ide_host_add(&d, hws, i, &host); 157 if (error) 158 goto out; 159 160 platform_set_drvdata(pdev, host); 161 return 0; 162 163out: 164 release_mem_region(res->start, resource_size(res)); 165 return error; 166} 167 168static int __exit amiga_gayle_ide_remove(struct platform_device *pdev) 169{ 170 struct ide_host *host = platform_get_drvdata(pdev); 171 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 172 173 ide_host_remove(host); 174 release_mem_region(res->start, resource_size(res)); 175 return 0; 176} 177 178static struct platform_driver amiga_gayle_ide_driver = { 179 .remove = __exit_p(amiga_gayle_ide_remove), 180 .driver = { 181 .name = "amiga-gayle-ide", 182 }, 183}; 184 185module_platform_driver_probe(amiga_gayle_ide_driver, amiga_gayle_ide_probe); 186 187MODULE_LICENSE("GPL"); 188MODULE_ALIAS("platform:amiga-gayle-ide"); 189