⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sbus_drivers.txt

📁 linux 内核源代码
💻 TXT
字号:
		Writing SBUS Drivers	    David S. Miller (davem@redhat.com)	The SBUS driver interfaces of the Linux kernel have beenrevamped completely for 2.4.x for several reasons.  Foremost wereperformance and complexity concerns.  This document details thesenew interfaces and how they are used to write an SBUS device driver.	SBUS drivers need to include <asm/sbus.h> to get accessto functions and structures described here.		Probing and Detection	Each SBUS device inside the machine is described by astructure called "struct sbus_dev".  Likewise, each SBUS busfound in the system is described by a "struct sbus_bus".  Foreach SBUS bus, the devices underneath are hung in a tree-likefashion off of the bus structure.	The SBUS device structure contains enough informationfor you to implement your device probing algorithm and obtainthe bits necessary to run your device.  The most commonlyused members of this structure, and their typical usage,will be detailed below.	Here is a piece of skeleton code for performing a deviceprobe in an SBUS driver under Linux:	static int __devinit mydevice_probe_one(struct sbus_dev *sdev)	{		struct mysdevice *mp = kzalloc(sizeof(*mp), GFP_KERNEL);		if (!mp)			return -ENODEV;		...		dev_set_drvdata(&sdev->ofdev.dev, mp);		return 0;		...	}	static int __devinit mydevice_probe(struct of_device *dev,				            const struct of_device_id *match)	{		struct sbus_dev *sdev = to_sbus_device(&dev->dev);		return mydevice_probe_one(sdev);	}	static int __devexit mydevice_remove(struct of_device *dev)	{		struct sbus_dev *sdev = to_sbus_device(&dev->dev);		struct mydevice *mp = dev_get_drvdata(&dev->dev);		return mydevice_remove_one(sdev, mp);	}	static struct of_device_id mydevice_match[] = {		{			.name = "mydevice",		},		{},	};	MODULE_DEVICE_TABLE(of, mydevice_match);	static struct of_platform_driver mydevice_driver = {		.match_table	= mydevice_match,		.probe		= mydevice_probe,		.remove		= __devexit_p(mydevice_remove),		.driver		= {			.name		= "mydevice",		},	};	static int __init mydevice_init(void)	{		return of_register_driver(&mydevice_driver, &sbus_bus_type);	}	static void __exit mydevice_exit(void)	{		of_unregister_driver(&mydevice_driver);	}	module_init(mydevice_init);	module_exit(mydevice_exit);	The mydevice_match table is a series of entries whichdescribes what SBUS devices your driver is meant for.  In thesimplest case you specify a string for the 'name' field.  EverySBUS device with a 'name' property matching your string willbe passed one-by-one to your .probe method.	You should store away your device private state structurepointer in the drvdata area so that you can retrieve it later onin your .remove method.	Any memory allocated, registers mapped, IRQs registered,etc. must be undone by your .remove method so that all resourcesof your device are released by the time it returns.	You should _NOT_ use the for_each_sbus(), for_each_sbusdev(),and for_all_sbusdev() interfaces.  They are deprecated, will beremoved, and no new driver should reference them ever.		Mapping and Accessing I/O Registers	Each SBUS device structure contains an array of descriptorswhich describe each register set. We abuse struct resource for that.They each correspond to the "reg" properties provided by the OBP firmware.	Before you can access your device's registers you must mapthem.  And later if you wish to shutdown your driver (for moduleunload or similar) you must unmap them.  You must treat them asa resource, which you allocate (map) before using and free up(unmap) when you are done with it.	The mapping information is stored in an opaque valuetyped as an "unsigned long".  This is the type of the return valueof the mapping interface, and the arguments to the unmappinginterface.  Let's say you want to map the first set of registers.Perhaps part of your driver software state structure looks like:	struct mydevice {		unsigned long control_regs;	   ...		struct sbus_dev *sdev;	   ...	};	At initialization time you then use the sbus_ioremapinterface to map in your registers, like so:	static void init_one_mydevice(struct sbus_dev *sdev)	{		struct mydevice *mp;		...		mp->control_regs = sbus_ioremap(&sdev->resource[0], 0,					CONTROL_REGS_SIZE, "mydevice regs");		if (!mp->control_regs) {			/* Failure, cleanup and return. */		}	}	Second argument to sbus_ioremap is an offset forcranky devices with broken OBP PROM. The sbus_ioremap uses onlya start address and flags from the resource structure.Therefore it is possible to use the same resource to mapseveral sets of registers or even to fabricate a resourcestructure if driver gets physical address from some private place.This practice is discouraged though. Use whatever OBP PROMprovided to you.	And here is how you might unmap these registers later atdriver shutdown or module unload time, using the sbus_iounmapinterface:	static void mydevice_unmap_regs(struct mydevice *mp)	{		sbus_iounmap(mp->control_regs, CONTROL_REGS_SIZE);	}	Finally, to actually access your registers there are 6interface routines at your disposal.  Accesses are byte (8 bit),word (16 bit), or longword (32 bit) sized.  Here they are:	u8 sbus_readb(unsigned long reg)		/* read byte */	u16 sbus_readw(unsigned long reg)		/* read word */	u32 sbus_readl(unsigned long reg)		/* read longword */	void sbus_writeb(u8 value, unsigned long reg)	/* write byte */	void sbus_writew(u16 value, unsigned long reg)	/* write word */	void sbus_writel(u32 value, unsigned long reg)	/* write longword */	So, let's say your device has a control register of some sortat offset zero.  The following might implement resetting your device:	#define CONTROL		0x00UL	#define CONTROL_RESET	0x00000001	/* Reset hardware */	static void mydevice_reset(struct mydevice *mp)	{		sbus_writel(CONTROL_RESET, mp->regs + CONTROL);	}	Or perhaps there is a data port register at an offset of16 bytes which allows you to read bytes from a fifo in the device:	#define DATA		0x10UL	static u8 mydevice_get_byte(struct mydevice *mp)	{		return sbus_readb(mp->regs + DATA);	}	It's pretty straightforward, and clueful readers may havenoticed that these interfaces mimick the PCI interfaces of theLinux kernel.  This was not by accident.	WARNING:		DO NOT try to treat these opaque register mapping		values as a memory mapped pointer to some structure		which you can dereference.		It may be memory mapped, it may not be.  In fact it		could be a physical address, or it could be the time		of day xor'd with 0xdeadbeef.  :-)		Whatever it is, it's an implementation detail.  The		interface was done this way to shield the driver		author from such complexities.			Doing DVMA	SBUS devices can perform DMA transactions in a way similarto PCI but dissimilar to ISA, e.g. DMA masters supply address.In contrast to PCI, however, that address (a bus address) istranslated by IOMMU before a memory access is performed and thereforeit is virtual. Sun calls this procedure DVMA.	Linux supports two styles of using SBUS DVMA: "consistent memory"and "streaming DVMA". CPU view of consistent memory chunk is, well,consistent with a view of a device. Think of it as an uncached memory.Typically this way of doing DVMA is not very fast and drivers use itmostly for control blocks or queues. On some CPUs we cannot flush orinvalidate individual pages or cache lines and doing explicit flushingover ever little byte in every control block would be wasteful.Streaming DVMA is a preferred way to transfer large amounts of data.This process works in the following way:1. a CPU stops accessing a certain part of memory,   flushes its caches covering that memory;2. a device does DVMA accesses, then posts an interrupt;3. CPU invalidates its caches and starts to access the memory.A single streaming DVMA operation can touch several discontiguousregions of a virtual bus address space. This is called a scatter-gatherDVMA.[TBD: Why do not we neither Solaris attempt to map disjoint pagesinto a single virtual chunk with the help of IOMMU, so that non SGDVMA masters would do SG? It'd be very helpful for RAID.]	In order to perform a consistent DVMA a driver does somethinglike the following:	char *mem;		/* Address in the CPU space */	u32 busa;		/* Address in the SBus space */	mem = (char *) sbus_alloc_consistent(sdev, MYMEMSIZE, &busa);	Then mem is used when CPU accesses this memory and u32is fed to the device so that it can do DVMA. This is typicallydone with an sbus_writel() into some device register.	Do not forget to free the DVMA resources once you are done:	sbus_free_consistent(sdev, MYMEMSIZE, mem, busa);	Streaming DVMA is more interesting. First you allocate somememory suitable for it or pin down some user pages. Then it all workslike this:	char *mem = argumen1;	unsigned int size = argument2;	u32 busa;		/* Address in the SBus space */	*mem = 1;		/* CPU can access */	busa = sbus_map_single(sdev, mem, size);	if (busa == 0) .......	/* Tell the device to use busa here */	/* CPU cannot access the memory without sbus_dma_sync_single() */	sbus_unmap_single(sdev, busa, size);	if (*mem == 0) ....	/* CPU can access again */	It is possible to retain mappings and ask the device toaccess data again and again without calling sbus_unmap_single.However, CPU caches must be invalidated with sbus_dma_sync_singlebefore such access.[TBD but what about writeback caches here... do we have any?]	There is an equivalent set of functions doing the same thingonly with several memory segments at once for devices capable ofscatter-gather transfers. Use the Source, Luke.			Examples	drivers/net/sunhme.c	This is a complicated driver which illustrates many conceptsdiscussed above and plus it handles both PCI and SBUS boards.	drivers/scsi/esp.c	Check it out for scatter-gather DVMA.	drivers/sbus/char/bpp.c	A non-DVMA device.	drivers/net/sunlance.c	Lance driver abuses consistent mappings for data transfer.It is a nifty trick which we do not particularly recommend...Just check it out and know that it's legal.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -