pata_ixp4xx_cf.c 5.8 KB
Newer Older
1 2
/*
 * ixp4xx PATA/Compact Flash driver
3
 * Copyright (C) 2006-07 Tower Technologies
4 5 6 7 8
 * Author: Alessandro Zummo <a.zummo@towertech.it>
 *
 * An ATA driver to handle a Compact Flash connected
 * to the ixp4xx expansion bus in TrueIDE mode. The CF
 * must have it chip selects connected to two CS lines
9 10 11
 * on the ixp4xx. In the irq is not available, you might
 * want to modify both this driver and libata to run in
 * polling mode.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * 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/kernel.h>
#include <linux/module.h>
#include <linux/libata.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <scsi/scsi_host.h>

#define DRV_NAME	"pata_ixp4xx_cf"
27
#define DRV_VERSION	"0.2"
28

29
static int ixp4xx_set_mode(struct ata_link *link, struct ata_device **error)
30
{
31
	struct ata_device *dev;
32

T
Tejun Heo 已提交
33 34 35 36 37 38
	ata_for_each_dev(dev, link, ENABLED) {
		ata_dev_printk(dev, KERN_INFO, "configured for PIO0\n");
		dev->pio_mode = XFER_PIO_0;
		dev->xfer_mode = XFER_PIO_0;
		dev->xfer_shift = ATA_SHIFT_PIO;
		dev->flags |= ATA_DFLAG_PIO;
39
	}
A
Alan 已提交
40
	return 0;
41 42
}

43 44
static unsigned int ixp4xx_mmio_data_xfer(struct ata_device *dev,
				unsigned char *buf, unsigned int buflen, int rw)
45 46 47 48
{
	unsigned int i;
	unsigned int words = buflen >> 1;
	u16 *buf16 = (u16 *) buf;
49
	struct ata_port *ap = dev->link->ap;
50
	void __iomem *mmio = ap->ioaddr.data_addr;
51 52 53 54 55 56 57 58 59
	struct ixp4xx_pata_data *data = ap->host->dev->platform_data;

	/* set the expansion bus in 16bit mode and restore
	 * 8 bit mode after the transaction.
	 */
	*data->cs0_cfg &= ~(0x01);
	udelay(100);

	/* Transfer multiple of 2 bytes */
60
	if (rw == READ)
61 62
		for (i = 0; i < words; i++)
			buf16[i] = readw(mmio);
63 64 65
	else
		for (i = 0; i < words; i++)
			writew(buf16[i], mmio);
66 67 68 69 70 71

	/* Transfer trailing 1 byte, if any. */
	if (unlikely(buflen & 0x01)) {
		u16 align_buf[1] = { 0 };
		unsigned char *trailing_buf = buf + buflen - 1;

72
		if (rw == READ) {
73 74
			align_buf[0] = readw(mmio);
			memcpy(trailing_buf, align_buf, 1);
75 76 77
		} else {
			memcpy(align_buf, trailing_buf, 1);
			writew(align_buf[0], mmio);
78
		}
79
		words++;
80 81 82 83
	}

	udelay(100);
	*data->cs0_cfg |= 0x01;
84 85

	return words << 1;
86 87 88
}

static struct scsi_host_template ixp4xx_sht = {
89
	ATA_PIO_SHT(DRV_NAME),
90 91 92
};

static struct ata_port_operations ixp4xx_port_ops = {
93
	.inherits		= &ata_sff_port_ops,
T
Tejun Heo 已提交
94
	.sff_data_xfer		= ixp4xx_mmio_data_xfer,
95
	.cable_detect		= ata_cable_40wire,
96
	.set_mode		= ixp4xx_set_mode,
97 98
};

99
static void ixp4xx_setup_port(struct ata_port *ap,
100 101
			      struct ixp4xx_pata_data *data,
			      unsigned long raw_cs0, unsigned long raw_cs1)
102
{
103
	struct ata_ioports *ioaddr = &ap->ioaddr;
104 105 106
	unsigned long raw_cmd = raw_cs0;
	unsigned long raw_ctl = raw_cs1 + 0x06;

T
Tejun Heo 已提交
107 108 109
	ioaddr->cmd_addr	= data->cs0;
	ioaddr->altstatus_addr	= data->cs1 + 0x06;
	ioaddr->ctl_addr	= data->cs1 + 0x06;
110

T
Tejun Heo 已提交
111
	ata_sff_std_ports(ioaddr);
112 113 114 115 116 117 118

#ifndef __ARMEB__

	/* adjust the addresses to handle the address swizzling of the
	 * ixp4xx in little endian mode.
	 */

T
Tejun Heo 已提交
119 120 121 122 123 124 125 126 127 128 129 130 131
	*(unsigned long *)&ioaddr->data_addr		^= 0x02;
	*(unsigned long *)&ioaddr->cmd_addr		^= 0x03;
	*(unsigned long *)&ioaddr->altstatus_addr	^= 0x03;
	*(unsigned long *)&ioaddr->ctl_addr		^= 0x03;
	*(unsigned long *)&ioaddr->error_addr		^= 0x03;
	*(unsigned long *)&ioaddr->feature_addr		^= 0x03;
	*(unsigned long *)&ioaddr->nsect_addr		^= 0x03;
	*(unsigned long *)&ioaddr->lbal_addr		^= 0x03;
	*(unsigned long *)&ioaddr->lbam_addr		^= 0x03;
	*(unsigned long *)&ioaddr->lbah_addr		^= 0x03;
	*(unsigned long *)&ioaddr->device_addr		^= 0x03;
	*(unsigned long *)&ioaddr->status_addr		^= 0x03;
	*(unsigned long *)&ioaddr->command_addr		^= 0x03;
132 133 134

	raw_cmd ^= 0x03;
	raw_ctl ^= 0x03;
135
#endif
136 137

	ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", raw_cmd, raw_ctl);
138 139 140 141 142 143
}

static __devinit int ixp4xx_pata_probe(struct platform_device *pdev)
{
	unsigned int irq;
	struct resource *cs0, *cs1;
144 145
	struct ata_host *host;
	struct ata_port *ap;
146 147 148 149 150 151 152 153
	struct ixp4xx_pata_data *data = pdev->dev.platform_data;

	cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);

	if (!cs0 || !cs1)
		return -EINVAL;

154 155 156 157 158 159
	/* allocate host */
	host = ata_host_alloc(&pdev->dev, 1);
	if (!host)
		return -ENOMEM;

	/* acquire resources and fill host */
160
	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
161

162 163
	data->cs0 = devm_ioremap(&pdev->dev, cs0->start, 0x1000);
	data->cs1 = devm_ioremap(&pdev->dev, cs1->start, 0x1000);
164

165 166 167
	if (!data->cs0 || !data->cs1)
		return -ENOMEM;

168 169
	irq = platform_get_irq(pdev, 0);
	if (irq)
170
		irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
171 172 173 174 175

	/* Setup expansion bus chip selects */
	*data->cs0_cfg = data->cs0_bits;
	*data->cs1_cfg = data->cs1_bits;

176
	ap = host->ports[0];
177

178
	ap->ops	= &ixp4xx_port_ops;
179
	ap->pio_mask = ATA_PIO4;
180
	ap->flags |= ATA_FLAG_NO_ATAPI;
181

182
	ixp4xx_setup_port(ap, data, cs0->start, cs1->start);
183 184 185

	dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");

186
	/* activate host */
T
Tejun Heo 已提交
187
	return ata_host_activate(host, irq, ata_sff_interrupt, 0, &ixp4xx_sht);
188 189 190 191 192 193
}

static __devexit int ixp4xx_pata_remove(struct platform_device *dev)
{
	struct ata_host *host = platform_get_drvdata(dev);

194
	ata_host_detach(host);
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221

	return 0;
}

static struct platform_driver ixp4xx_pata_platform_driver = {
	.driver	 = {
		.name   = DRV_NAME,
		.owner  = THIS_MODULE,
	},
	.probe		= ixp4xx_pata_probe,
	.remove		= __devexit_p(ixp4xx_pata_remove),
};

static int __init ixp4xx_pata_init(void)
{
	return platform_driver_register(&ixp4xx_pata_platform_driver);
}

static void __exit ixp4xx_pata_exit(void)
{
	platform_driver_unregister(&ixp4xx_pata_platform_driver);
}

MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("low-level driver for ixp4xx Compact Flash PATA");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
222
MODULE_ALIAS("platform:" DRV_NAME);
223 224 225

module_init(ixp4xx_pata_init);
module_exit(ixp4xx_pata_exit);