xref: /kernel/linux/linux-5.10/drivers/ide/gayle.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *  Amiga Gayle IDE Driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *     Created 9 Jul 1997 by Geert Uytterhoeven
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *  This file is subject to the terms and conditions of the GNU General Public
78c2ecf20Sopenharmony_ci *  License.  See the file COPYING in the main directory of this archive for
88c2ecf20Sopenharmony_ci *  more details.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/types.h>
128c2ecf20Sopenharmony_ci#include <linux/mm.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
158c2ecf20Sopenharmony_ci#include <linux/ide.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/zorro.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <asm/setup.h>
228c2ecf20Sopenharmony_ci#include <asm/amigahw.h>
238c2ecf20Sopenharmony_ci#include <asm/amigaints.h>
248c2ecf20Sopenharmony_ci#include <asm/amigayle.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci    /*
288c2ecf20Sopenharmony_ci     *  Offsets from one of the above bases
298c2ecf20Sopenharmony_ci     */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define GAYLE_CONTROL	0x101a
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci    /*
348c2ecf20Sopenharmony_ci     *  These are at different offsets from the base
358c2ecf20Sopenharmony_ci     */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define GAYLE_IRQ_4000	0xdd3020	/* MSB = 1, Harddisk is source of */
388c2ecf20Sopenharmony_ci#define GAYLE_IRQ_1200	0xda9000	/* interrupt */
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci    /*
428c2ecf20Sopenharmony_ci     *  Offset of the secondary port for IDE doublers
438c2ecf20Sopenharmony_ci     *  Note that GAYLE_CONTROL is NOT available then!
448c2ecf20Sopenharmony_ci     */
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define GAYLE_NEXT_PORT	0x1000
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define GAYLE_NUM_HWIFS		2
498c2ecf20Sopenharmony_ci#define GAYLE_NUM_PROBE_HWIFS	(ide_doubler ? GAYLE_NUM_HWIFS : \
508c2ecf20Sopenharmony_ci					       GAYLE_NUM_HWIFS-1)
518c2ecf20Sopenharmony_ci#define GAYLE_HAS_CONTROL_REG	(!ide_doubler)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic bool ide_doubler;
548c2ecf20Sopenharmony_cimodule_param_named(doubler, ide_doubler, bool, 0);
558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(doubler, "enable support for IDE doublers");
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci    /*
588c2ecf20Sopenharmony_ci     *  Check and acknowledge the interrupt status
598c2ecf20Sopenharmony_ci     */
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int gayle_test_irq(ide_hwif_t *hwif)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	unsigned char ch;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	ch = z_readb(hwif->io_ports.irq_addr);
668c2ecf20Sopenharmony_ci	if (!(ch & GAYLE_IRQ_IDE))
678c2ecf20Sopenharmony_ci		return 0;
688c2ecf20Sopenharmony_ci	return 1;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic void gayle_a1200_clear_irq(ide_drive_t *drive)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	ide_hwif_t *hwif = drive->hwif;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	(void)z_readb(hwif->io_ports.status_addr);
768c2ecf20Sopenharmony_ci	z_writeb(0x7c, hwif->io_ports.irq_addr);
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void __init gayle_setup_ports(struct ide_hw *hw, unsigned long base,
808c2ecf20Sopenharmony_ci				     unsigned long ctl, unsigned long irq_port)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	int i;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	memset(hw, 0, sizeof(*hw));
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	hw->io_ports.data_addr = base;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	for (i = 1; i < 8; i++)
898c2ecf20Sopenharmony_ci		hw->io_ports_array[i] = base + 2 + i * 4;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	hw->io_ports.ctl_addr = ctl;
928c2ecf20Sopenharmony_ci	hw->io_ports.irq_addr = irq_port;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	hw->irq = IRQ_AMIGA_PORTS;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic const struct ide_port_ops gayle_a4000_port_ops = {
988c2ecf20Sopenharmony_ci	.test_irq		= gayle_test_irq,
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic const struct ide_port_ops gayle_a1200_port_ops = {
1028c2ecf20Sopenharmony_ci	.clear_irq		= gayle_a1200_clear_irq,
1038c2ecf20Sopenharmony_ci	.test_irq		= gayle_test_irq,
1048c2ecf20Sopenharmony_ci};
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic const struct ide_port_info gayle_port_info = {
1078c2ecf20Sopenharmony_ci	.host_flags		= IDE_HFLAG_MMIO | IDE_HFLAG_SERIALIZE |
1088c2ecf20Sopenharmony_ci				  IDE_HFLAG_NO_DMA,
1098c2ecf20Sopenharmony_ci	.irq_flags		= IRQF_SHARED,
1108c2ecf20Sopenharmony_ci	.chipset		= ide_generic,
1118c2ecf20Sopenharmony_ci};
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci    /*
1148c2ecf20Sopenharmony_ci     *  Probe for a Gayle IDE interface (and optionally for an IDE doubler)
1158c2ecf20Sopenharmony_ci     */
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic int __init amiga_gayle_ide_probe(struct platform_device *pdev)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct resource *res;
1208c2ecf20Sopenharmony_ci	struct gayle_ide_platform_data *pdata;
1218c2ecf20Sopenharmony_ci	unsigned long base, ctrlport, irqport;
1228c2ecf20Sopenharmony_ci	unsigned int i;
1238c2ecf20Sopenharmony_ci	int error;
1248c2ecf20Sopenharmony_ci	struct ide_hw hw[GAYLE_NUM_HWIFS], *hws[GAYLE_NUM_HWIFS];
1258c2ecf20Sopenharmony_ci	struct ide_port_info d = gayle_port_info;
1268c2ecf20Sopenharmony_ci	struct ide_host *host;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1298c2ecf20Sopenharmony_ci	if (!res)
1308c2ecf20Sopenharmony_ci		return -ENODEV;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (!request_mem_region(res->start, resource_size(res), "IDE"))
1338c2ecf20Sopenharmony_ci		return -EBUSY;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	pdata = dev_get_platdata(&pdev->dev);
1368c2ecf20Sopenharmony_ci	pr_info("ide: Gayle IDE controller (A%u style%s)\n",
1378c2ecf20Sopenharmony_ci		pdata->explicit_ack ? 1200 : 4000,
1388c2ecf20Sopenharmony_ci		ide_doubler ? ", IDE doubler" : "");
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	base = (unsigned long)ZTWO_VADDR(pdata->base);
1418c2ecf20Sopenharmony_ci	ctrlport = 0;
1428c2ecf20Sopenharmony_ci	irqport = (unsigned long)ZTWO_VADDR(pdata->irqport);
1438c2ecf20Sopenharmony_ci	if (pdata->explicit_ack)
1448c2ecf20Sopenharmony_ci		d.port_ops = &gayle_a1200_port_ops;
1458c2ecf20Sopenharmony_ci	else
1468c2ecf20Sopenharmony_ci		d.port_ops = &gayle_a4000_port_ops;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++, base += GAYLE_NEXT_PORT) {
1498c2ecf20Sopenharmony_ci		if (GAYLE_HAS_CONTROL_REG)
1508c2ecf20Sopenharmony_ci			ctrlport = base + GAYLE_CONTROL;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		gayle_setup_ports(&hw[i], base, ctrlport, irqport);
1538c2ecf20Sopenharmony_ci		hws[i] = &hw[i];
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	error = ide_host_add(&d, hws, i, &host);
1578c2ecf20Sopenharmony_ci	if (error)
1588c2ecf20Sopenharmony_ci		goto out;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, host);
1618c2ecf20Sopenharmony_ci	return 0;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ciout:
1648c2ecf20Sopenharmony_ci	release_mem_region(res->start, resource_size(res));
1658c2ecf20Sopenharmony_ci	return error;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic int __exit amiga_gayle_ide_remove(struct platform_device *pdev)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct ide_host *host = platform_get_drvdata(pdev);
1718c2ecf20Sopenharmony_ci	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	ide_host_remove(host);
1748c2ecf20Sopenharmony_ci	release_mem_region(res->start, resource_size(res));
1758c2ecf20Sopenharmony_ci	return 0;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic struct platform_driver amiga_gayle_ide_driver = {
1798c2ecf20Sopenharmony_ci	.remove = __exit_p(amiga_gayle_ide_remove),
1808c2ecf20Sopenharmony_ci	.driver   = {
1818c2ecf20Sopenharmony_ci		.name	= "amiga-gayle-ide",
1828c2ecf20Sopenharmony_ci	},
1838c2ecf20Sopenharmony_ci};
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cimodule_platform_driver_probe(amiga_gayle_ide_driver, amiga_gayle_ide_probe);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1888c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:amiga-gayle-ide");
189