PCMCIA Memory Read Throughput

Adrian Cox adrian at humboldt.co.uk
Fri Jun 8 08:38:27 EDT 2007


I have a problem with read throughput on 16-bit memory access. I have a
prototype of a PCMCIA card based on the Elan VMB5000:

http://www.elandigitalsystems.com/asic/vmb5000.php

I'm testing the driver on a Dell D600, with an O2 micro bridge:
02:01.0 CardBus bridge: O2 Micro, Inc. OZ711EC1 SmartCardBus Controller (rev 20)
02:01.1 CardBus bridge: O2 Micro, Inc. OZ711EC1 SmartCardBus Controller (rev 20)

The main transfer routine for the card reads a FIFO in 16 bit operations
using "rep;movs" under Linux and READ_REGISTER_BUFFER_USHORT under
Windows. The problem is that this transfer runs faster under Windows
than Linux. Running Linux an oscilloscope shows one transfer every 2us,
giving a peak throughput of just under 1MByte/s.

Is 2us an expected cycle time for reads from 16-bit memory on a PCMCIA
card?  My contact at Elan suggests that the throughput should be higher,
and that this is a problem with the Linux socket driver.

The setup portions of the driver are below. (The full driver is
available to anyone who wants to see, but it currently contains several
hundred lines of sysfs attributes for debugging.)

	link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
	link->irq.IRQInfo1 = IRQ_LEVEL_ID;
	link->conf.Attributes = CONF_ENABLE_IRQ;
	link->conf.IntType = INT_MEMORY_AND_IO;
	result = elan_pcmcia_config_io(link);
	if (result) {
		dev_err(&link->dev, "Unable to get I/O ports\n");
		goto err_req;
	}
	dev_info(&link->dev, "Got IO base 0x%x\n", link->io.BasePort1);
	elan->io_base = link->io.BasePort1;

	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
	result = pcmcia_request_irq(link, &link->irq);

	if (result) {
		dev_err(&link->dev, "Error %d on request irq\n", result);
		goto err_req;
	}
	dev_info(&link->dev, "Got irq %d\n", link->irq.AssignedIRQ);
	elan->irq = link->irq.AssignedIRQ;

	memset(&win_req, 0, sizeof(win_req));
	win_req.Attributes = WIN_ADDR_SPACE_MEM | WIN_ENABLE | WIN_DATA_WIDTH_16;
	win_req.Size = PAGE_SIZE;
	result = pcmcia_request_window(&link, &win_req, &elan->win);
	if (result) {
		dev_err(&link->dev, "Error %d on request window\n", result);
		goto err_req;
	} 
	elan->base = win_req.Base;
	dev_info(&link->dev, "Prev flags 0x%x\n", elan->win->ctl.flags);
	elan->win->ctl.speed = 0;
	memreq.CardOffset = 0;
	memreq.Page = 0;
	result = pcmcia_map_mem_page(elan->win, &memreq);
	if (result) {
		dev_warn(&link->dev, "Error %d on map_page\n", result);
	} 

	dev_info(&link->dev, "Got window base 0x%lx size 0x%x\n", win_req.Base, win_req.Size);

	if (pcmcia_request_configuration(link, &link->conf)) {
		dev_err(&link->dev, "Unable to request configuration\n");
		result = -EIO;
		goto err_irq;
	}

The elan_pcmcia_config_io function is:

/* Find allowed I/O port addresses from PCMCIA configuration tuples */
static int elan_pcmcia_config_io(struct pcmcia_device *link)
{
	tuple_t tuple;
	cisparse_t parse;
	int result;
	u_short buf[64];

	tuple.TupleData = (cisdata_t *)buf;
	tuple.TupleDataMax = sizeof(buf);
	tuple.TupleOffset = 0;
	tuple.Attributes = 0;

	tuple.DesiredTuple = CISTPL_CONFIG;
	result = pcmcia_get_first_tuple(link, &tuple);
	if (result != CS_SUCCESS)
		return result;
	result = pcmcia_get_tuple_data(link, &tuple);
	if (result != CS_SUCCESS)
		return result;
	result = pcmcia_parse_tuple(link, &tuple, &parse);
	if (result != CS_SUCCESS)
		return result;
	dev_info(&link->dev, "Config base 0x%x present 0x%x\n",
		parse.config.base, parse.config.rmask[0]);
	
	link->conf.ConfigBase = parse.config.base;
	link->conf.Present = parse.config.rmask[0];

	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
	result = pcmcia_get_first_tuple(link, &tuple);
	while (result == CS_SUCCESS) {
		cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
		cistpl_io_t *io = &(parse.cftable_entry.io);

		if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
				pcmcia_parse_tuple(link, &tuple, &parse) != 0)
			goto next_entry;

		dev_info(&link->dev, "Config %d\n", cfg->index);
		if (cfg->index == 0 || cfg->io.nwin == 0)
			goto next_entry;

		link->conf.ConfigIndex = cfg->index;
		if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM))
			link->conf.Vpp =
			    cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000;
		dev_info(&link->dev, "IO windows %d: 0x%x(%d) 0x%x(%d)\n",
			io->nwin, io->win[0].base, io->win[0].len,io->win[1].base, io->win[1].len);
		link->io.BasePort1 = io->win[0].base;
		link->io.NumPorts1 = io->win[0].len;
		link->io.IOAddrLines = 16; // io->flags & CISTPL_IO_LINES_MASK;
		result = pcmcia_request_io(link, &link->io);
		if (! result)
			return 0;
		next_entry:
		result = pcmcia_get_next_tuple(link, &tuple);
	}

	return result;
}

-- 
Adrian Cox <adrian at humboldt.co.uk>




More information about the linux-pcmcia mailing list