pata_winbond.c 7.4 KB
Newer Older
A
Alan Cox 已提交
1 2 3 4 5 6 7
/*
 *    pata_winbond.c - Winbond VLB ATA controllers
 *	(C) 2006 Red Hat <alan@redhat.com>
 *
 *    Support for the Winbond 83759A when operating in advanced mode.
 *    Multichip mode is not currently supported.
 */
J
Jeff Garzik 已提交
8

A
Alan Cox 已提交
9 10 11 12 13 14 15 16 17 18
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <scsi/scsi_host.h>
#include <linux/libata.h>
#include <linux/platform_device.h>

#define DRV_NAME "pata_winbond"
J
Jeff Garzik 已提交
19
#define DRV_VERSION "0.0.3"
A
Alan Cox 已提交
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

#define NR_HOST 4	/* Two winbond controllers, two channels each */

struct winbond_data {
	unsigned long config;
	struct platform_device *platform_dev;
};

static struct ata_host *winbond_host[NR_HOST];
static struct winbond_data winbond_data[NR_HOST];
static int nr_winbond_host;

#ifdef MODULE
static int probe_winbond = 1;
#else
static int probe_winbond;
#endif

38
static DEFINE_SPINLOCK(winbond_lock);
A
Alan Cox 已提交
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

static void winbond_writecfg(unsigned long port, u8 reg, u8 val)
{
	unsigned long flags;
	spin_lock_irqsave(&winbond_lock, flags);
	outb(reg, port + 0x01);
	outb(val, port + 0x02);
	spin_unlock_irqrestore(&winbond_lock, flags);
}

static u8 winbond_readcfg(unsigned long port, u8 reg)
{
	u8 val;

	unsigned long flags;
	spin_lock_irqsave(&winbond_lock, flags);
	outb(reg, port + 0x01);
	val = inb(port + 0x02);
	spin_unlock_irqrestore(&winbond_lock, flags);

	return val;
}

static void winbond_set_piomode(struct ata_port *ap, struct ata_device *adev)
{
	struct ata_timing t;
	struct winbond_data *winbond = ap->host->private_data;
	int active, recovery;
	u8 reg;
	int timing = 0x88 + (ap->port_no * 4) + (adev->devno * 2);

	reg = winbond_readcfg(winbond->config, 0x81);
J
Jeff Garzik 已提交
71

A
Alan Cox 已提交
72 73 74 75 76 77 78 79 80 81
	/* Get the timing data in cycles */
	if (reg & 0x40)		/* Fast VLB bus, assume 50MHz */
		ata_timing_compute(adev, adev->pio_mode, &t, 20000, 1000);
	else
		ata_timing_compute(adev, adev->pio_mode, &t, 30303, 1000);

	active = (FIT(t.active, 3, 17) - 1) & 0x0F;
	recovery = (FIT(t.recover, 1, 15) + 1) & 0x0F;
	timing = (active << 4) | recovery;
	winbond_writecfg(winbond->config, timing, reg);
J
Jeff Garzik 已提交
82

A
Alan Cox 已提交
83
	/* Load the setup timing */
J
Jeff Garzik 已提交
84

A
Alan Cox 已提交
85 86 87 88 89 90 91 92 93 94 95 96
	reg = 0x35;
	if (adev->class != ATA_DEV_ATA)
		reg |= 0x08;	/* FIFO off */
	if (!ata_pio_need_iordy(adev))
		reg |= 0x02;	/* IORDY off */
	reg |= (FIT(t.setup, 0, 3) << 6);
	winbond_writecfg(winbond->config, timing + 1, reg);
}


static void winbond_data_xfer(struct ata_device *adev, unsigned char *buf, unsigned int buflen, int write_data)
{
T
Tejun Heo 已提交
97
	struct ata_port *ap = adev->link->ap;
A
Alan Cox 已提交
98 99 100 101
	int slop = buflen & 3;

	if (ata_id_has_dword_io(adev->id)) {
		if (write_data)
T
Tejun Heo 已提交
102
			iowrite32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
A
Alan Cox 已提交
103
		else
T
Tejun Heo 已提交
104
			ioread32_rep(ap->ioaddr.data_addr, buf, buflen >> 2);
A
Alan Cox 已提交
105 106

		if (unlikely(slop)) {
A
Al Viro 已提交
107
			__le32 pad = 0;
A
Alan Cox 已提交
108 109
			if (write_data) {
				memcpy(&pad, buf + buflen - slop, slop);
A
Al Viro 已提交
110
				iowrite32(le32_to_cpu(pad), ap->ioaddr.data_addr);
A
Alan Cox 已提交
111
			} else {
A
Al Viro 已提交
112
				pad = cpu_to_le32(ioread32(ap->ioaddr.data_addr));
A
Alan Cox 已提交
113 114 115 116
				memcpy(buf + buflen - slop, &pad, slop);
			}
		}
	} else
T
Tejun Heo 已提交
117
		ata_data_xfer(adev, buf, buflen, write_data);
A
Alan Cox 已提交
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
}

static struct scsi_host_template winbond_sht = {
	.module			= THIS_MODULE,
	.name			= DRV_NAME,
	.ioctl			= ata_scsi_ioctl,
	.queuecommand		= ata_scsi_queuecmd,
	.can_queue		= ATA_DEF_QUEUE,
	.this_id		= ATA_SHT_THIS_ID,
	.sg_tablesize		= LIBATA_MAX_PRD,
	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,
	.emulated		= ATA_SHT_EMULATED,
	.use_clustering		= ATA_SHT_USE_CLUSTERING,
	.proc_name		= DRV_NAME,
	.dma_boundary		= ATA_DMA_BOUNDARY,
	.slave_configure	= ata_scsi_slave_config,
134
	.slave_destroy		= ata_scsi_slave_destroy,
A
Alan Cox 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
	.bios_param		= ata_std_bios_param,
};

static struct ata_port_operations winbond_port_ops = {
	.set_piomode	= winbond_set_piomode,

	.tf_load	= ata_tf_load,
	.tf_read	= ata_tf_read,
	.check_status 	= ata_check_status,
	.exec_command	= ata_exec_command,
	.dev_select 	= ata_std_dev_select,

	.freeze		= ata_bmdma_freeze,
	.thaw		= ata_bmdma_thaw,
	.error_handler	= ata_bmdma_error_handler,
	.post_internal_cmd = ata_bmdma_post_internal_cmd,
A
Alan Cox 已提交
151
	.cable_detect	= ata_cable_40wire,
A
Alan Cox 已提交
152 153 154 155 156 157 158

	.qc_prep 	= ata_qc_prep,
	.qc_issue	= ata_qc_issue_prot,

	.data_xfer	= winbond_data_xfer,

	.irq_clear	= ata_bmdma_irq_clear,
159
	.irq_on		= ata_irq_on,
A
Alan Cox 已提交
160

161
	.port_start	= ata_sff_port_start,
A
Alan Cox 已提交
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
};

/**
 *	winbond_init_one		-	attach a winbond interface
 *	@type: Type to display
 *	@io: I/O port start
 *	@irq: interrupt line
 *	@fast: True if on a > 33Mhz VLB
 *
 *	Register a VLB bus IDE interface. Such interfaces are PIO and we
 *	assume do not support IRQ sharing.
 */

static __init int winbond_init_one(unsigned long port)
{
	struct platform_device *pdev;
	u8 reg;
179
	int i, rc;
A
Alan Cox 已提交
180 181 182 183 184 185 186 187 188 189 190 191

	reg = winbond_readcfg(port, 0x81);
	reg |= 0x80;	/* jumpered mode off */
	winbond_writecfg(port, 0x81, reg);
	reg = winbond_readcfg(port, 0x83);
	reg |= 0xF0;	/* local control */
	winbond_writecfg(port, 0x83, reg);
	reg = winbond_readcfg(port, 0x85);
	reg |= 0xF0;	/* programmable timing */
	winbond_writecfg(port, 0x85, reg);

	reg = winbond_readcfg(port, 0x81);
J
Jeff Garzik 已提交
192

A
Alan Cox 已提交
193 194 195 196
	if (!(reg & 0x03))		/* Disabled */
		return 0;

	for (i = 0; i < 2 ; i ++) {
T
Tejun Heo 已提交
197
		unsigned long cmd_port = 0x1F0 - (0x80 * i);
198
		unsigned long ctl_port = cmd_port + 0x206;
199 200
		struct ata_host *host;
		struct ata_port *ap;
T
Tejun Heo 已提交
201
		void __iomem *cmd_addr, *ctl_addr;
A
Alan Cox 已提交
202

203 204 205 206 207 208 209 210 211 212 213
		if (!(reg & (1 << i)))
			continue;

		pdev = platform_device_register_simple(DRV_NAME, nr_winbond_host, NULL, 0);
		if (IS_ERR(pdev))
			return PTR_ERR(pdev);

		rc = -ENOMEM;
		host = ata_host_alloc(&pdev->dev, 1);
		if (!host)
			goto err_unregister;
214
		ap = host->ports[0];
215 216 217

		rc = -ENOMEM;
		cmd_addr = devm_ioport_map(&pdev->dev, cmd_port, 8);
218
		ctl_addr = devm_ioport_map(&pdev->dev, ctl_port, 1);
219 220 221
		if (!cmd_addr || !ctl_addr)
			goto err_unregister;

222 223
		ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", cmd_port, ctl_port);

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
		ap->ops = &winbond_port_ops;
		ap->pio_mask = 0x1F;
		ap->flags |= ATA_FLAG_SLAVE_POSS;
		ap->ioaddr.cmd_addr = cmd_addr;
		ap->ioaddr.altstatus_addr = ctl_addr;
		ap->ioaddr.ctl_addr = ctl_addr;
		ata_std_ports(&ap->ioaddr);

		/* hook in a private data structure per channel */
		host->private_data = &winbond_data[nr_winbond_host];
		winbond_data[nr_winbond_host].config = port;
		winbond_data[nr_winbond_host].platform_dev = pdev;

		/* activate */
		rc = ata_host_activate(host, 14 + i, ata_interrupt, 0,
				       &winbond_sht);
		if (rc)
			goto err_unregister;

		winbond_host[nr_winbond_host++] = dev_get_drvdata(&pdev->dev);
A
Alan Cox 已提交
244 245 246
	}

	return 0;
247 248 249 250

 err_unregister:
	platform_device_unregister(pdev);
	return rc;
A
Alan Cox 已提交
251 252 253 254 255 256 257 258 259 260 261 262 263 264
}

/**
 *	winbond_init		-	attach winbond interfaces
 *
 *	Attach winbond IDE interfaces by scanning the ports it may occupy.
 */

static __init int winbond_init(void)
{
	static const unsigned long config[2] = { 0x130, 0x1B0 };

	int ct = 0;
	int i;
J
Jeff Garzik 已提交
265

A
Alan Cox 已提交
266 267 268 269 270 271 272 273 274 275 276 277 278 279
	if (probe_winbond == 0)
		return -ENODEV;

	/*
 	 *	Check both base addresses
	 */

	for (i = 0; i < 2; i++) {
		if (probe_winbond & (1<<i)) {
			int ret = 0;
			unsigned long port = config[i];

			if (request_region(port, 2, "pata_winbond")) {
				ret = winbond_init_one(port);
280
				if (ret <= 0)
A
Alan Cox 已提交
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
					release_region(port, 2);
				else ct+= ret;
			}
		}
	}
	if (ct != 0)
		return 0;
	return -ENODEV;
}

static __exit void winbond_exit(void)
{
	int i;

	for (i = 0; i < nr_winbond_host; i++) {
296
		ata_host_detach(winbond_host[i]);
A
Alan Cox 已提交
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
		release_region(winbond_data[i].config, 2);
		platform_device_unregister(winbond_data[i].platform_dev);
	}
}

MODULE_AUTHOR("Alan Cox");
MODULE_DESCRIPTION("low-level driver for Winbond VL ATA");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);

module_init(winbond_init);
module_exit(winbond_exit);

module_param(probe_winbond, int, 0);