You can subscribe to this list here.
2005 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
(1) |
Sep
(18) |
Oct
(21) |
Nov
(68) |
Dec
(130) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2006 |
Jan
(12) |
Feb
(41) |
Mar
(44) |
Apr
(60) |
May
(31) |
Jun
(43) |
Jul
(45) |
Aug
(54) |
Sep
(19) |
Oct
(12) |
Nov
(14) |
Dec
(30) |
2007 |
Jan
(87) |
Feb
(41) |
Mar
(19) |
Apr
(33) |
May
(69) |
Jun
(100) |
Jul
(100) |
Aug
(86) |
Sep
(30) |
Oct
(149) |
Nov
(45) |
Dec
(59) |
2008 |
Jan
(136) |
Feb
(124) |
Mar
(67) |
Apr
(107) |
May
(168) |
Jun
(83) |
Jul
(160) |
Aug
(72) |
Sep
(166) |
Oct
(159) |
Nov
(192) |
Dec
(185) |
2009 |
Jan
(108) |
Feb
(189) |
Mar
(151) |
Apr
(219) |
May
(273) |
Jun
(297) |
Jul
(217) |
Aug
(157) |
Sep
(181) |
Oct
(146) |
Nov
(239) |
Dec
(173) |
2010 |
Jan
(175) |
Feb
(206) |
Mar
(210) |
Apr
(180) |
May
(165) |
Jun
(126) |
Jul
(150) |
Aug
(198) |
Sep
(305) |
Oct
(280) |
Nov
(279) |
Dec
(263) |
2011 |
Jan
(232) |
Feb
(182) |
Mar
(124) |
Apr
(126) |
May
(133) |
Jun
(257) |
Jul
(153) |
Aug
(134) |
Sep
(136) |
Oct
(120) |
Nov
(185) |
Dec
(132) |
2012 |
Jan
(158) |
Feb
(145) |
Mar
(257) |
Apr
(197) |
May
(253) |
Jun
(266) |
Jul
(406) |
Aug
(327) |
Sep
(334) |
Oct
(253) |
Nov
(197) |
Dec
(227) |
2013 |
Jan
(243) |
Feb
(355) |
Mar
(263) |
Apr
(256) |
May
(154) |
Jun
(291) |
Jul
(319) |
Aug
(159) |
Sep
(181) |
Oct
(212) |
Nov
(76) |
Dec
(143) |
2014 |
Jan
(322) |
Feb
(523) |
Mar
(364) |
Apr
(836) |
May
(629) |
Jun
(592) |
Jul
(579) |
Aug
(459) |
Sep
(574) |
Oct
(639) |
Nov
(470) |
Dec
(368) |
2015 |
Jan
(504) |
Feb
(554) |
Mar
(686) |
Apr
(463) |
May
(305) |
Jun
(285) |
Jul
(188) |
Aug
(151) |
Sep
(171) |
Oct
(149) |
Nov
(164) |
Dec
(164) |
2016 |
Jan
(189) |
Feb
(158) |
Mar
(154) |
Apr
(153) |
May
(187) |
Jun
(184) |
Jul
(200) |
Aug
(228) |
Sep
(252) |
Oct
(270) |
Nov
(286) |
Dec
(361) |
2017 |
Jan
(346) |
Feb
(266) |
Mar
(251) |
Apr
(132) |
May
(175) |
Jun
(161) |
Jul
(195) |
Aug
(43) |
Sep
|
Oct
|
Nov
|
Dec
|
From: Vitaly W. <vw...@ru...> - 2005-11-22 08:10:06
|
Hi Greg, IMHO your expert opinion on both cores should be very useful for final convergence! Best regards, Vitaly Greg KH wrote: >On Mon, Nov 21, 2005 at 10:17:48AM -0800, David Brownell wrote: > > >>But I agree that this issue is worth trying to close. I'd be interested >>in any comments from Andrew or Greg, but probably LKML is the best place >>to have technical discussions related to "which framework". >> >> > >I was hoping that you all would work together to hash out a framework >that everyone agreed on. That way the decision would be easy :) > >thanks, > >greg k-h > > >------------------------------------------------------- >This SF.Net email is sponsored by the JBoss Inc. Get Certified Today >Register for a JBoss Training Course. Free Certification Exam >for All Training Attendees Through End of 2005. For more info visit: >http://ads.osdn.com/?ad_id=7628&alloc_id=16845&op=click >_______________________________________________ >spi-devel-general mailing list >spi...@li... >https://lists.sourceforge.net/lists/listinfo/spi-devel-general > > > > |
From: Greg KH <gr...@kr...> - 2005-11-22 07:29:02
|
On Mon, Nov 21, 2005 at 10:17:48AM -0800, David Brownell wrote: > But I agree that this issue is worth trying to close. I'd be interested > in any comments from Andrew or Greg, but probably LKML is the best place > to have technical discussions related to "which framework". I was hoping that you all would work together to hash out a framework that everyone agreed on. That way the decision would be easy :) thanks, greg k-h |
From: Vitaly W. <vw...@ru...> - 2005-11-22 06:00:48
|
David Brownell wrote: >>>- (A) uses more complicated structure of SPI message, that contains one >>> or more atomic transfers, and (B) >>> offers the only spi_msg that represents the atomic transfer on SPI bus. >>> The similar approach can be implemented >>> in (B), and actually is implemented. But my imp[ression is that >>> such enhancement may be added later.. >>> >>> >>I wouldn't have said that the message structure in (A) is more complex then (B). For example, in >>(B) you have many flags which controls things like SPI mode which are not needed in every message. >>Once the SPI controller has been setup for a particular slave device you don't need to constantly >>send this information. >> >> > >And in fact, constantly sending it means some drivers will have to waste >time constantly checking it, in case it changed. If that setup is stored >in controller registers, it's a lot better to just have the setup() call >be responsible for changing the communication parameters. (This is the >approach used by both MMC and PCMCIA, for what it's worth...) > > > I'm not aware if MMC/PCMCIA guys are happy with this approach :), but anyway what you're talking about here makes sense. > > >>In (B) how to do you handle SPI devices which require to send several messages with out releasing >>their cs? There are/will be some devices which require this. >> >> > >In fact, that's why the transfer segments are grouped. One builds SPI >protocol requests out of several such segments. A very common idiom is >writing a command, then reading its response. Chipselect must stay >active during the whole sequence. > >Adding support for such a basic mechanism "later" doesn't seem like >a good idea to me. > > It is supported by means of flags. I'm afraid your note is pointless here. > > > >>>- (A) uses workqueues to queue and handle SPI messages, and (B) >>> allocates the kernel thread to the same purpose. >>> Using workqueues is not very good solution in real-time environment; I >>> think that allocating and starting the >>> separate thread will give us more predictable and stable results; >>> >>> >>Where does (A) use a workqueue? (A) doesn't use a workqueue or thread and instead leaves it up to >>the adapter driver how to handle the messages that it gets sent (which in the case of some drivers >>will mean no thread or workqueue). (B) is _forcing_ a thread on the adapter which the adapter may >>not need. >> >> > >Exactly. That's one of the things I meant when I recently listed some of the >top goals of the framework I did: > >(a) SPI shouldn't perpetuate the driver model botches of I2C; >(b) ditto I2C's "everything is synchronous" botches; >(c) it should work well with DMA, to support things like DataFlash; >(d) given the variety of SPI chips, protocol controls are needed; >(e) place minimal implementation constraints on controller drivers. > >So for example one way you know that (c) is well met is that it's the same >approach used in USB (both host and peripheral/gadget sides); that's been >working well for quite a few years now. (Despite comments from Dmitry >and Vitaly to the contrary.) > > Lemme point you out that if somehting is "working" on a limited number of platforms within the limited number of use cases, that's not necessarily a correct implementation. > > > >>>- (A) has some assumptions on buffers that are passed down to spi >>> functions. >>> >>> > >Make that "requirements"; FWIW they're the same ones that apply to all >other kernel driver frameworks I've seen: that buffers be DMA-safe. >It would not be helpful (IMO) to define different rules; that's also >called the "Principle of Least Astonishment". :) > > Yeah within this requirement it's correct. But that requirement may really make the SPI controller driver a lot more complex if - it has to send something received from the userland - it needs to timely send some credentials (what is the case for the WLAN driver, for instance). > > > >>> If some controller driver (or bus driver >>> in terms of (B)) tries to perform DMA transfers, it must copy the >>> passed buffers to some memory allocated >>> using GFP_DMA flag and map it using dma_map_single. >>> >>> > >Based on this and other comments from Dmitry/Vitaly, I suspect they >don't see how the Linux DMA APIs work. The correct statement is that if >a controller driver wants to use DMA, it must dma_{map,unmap}_single(). >The upper level drivers don't _need_ to worry about that. > > The upper level drivers do need to worry about their buffers being DMAable then which requirement adds more complexity. For instance, that means that the upper level drivers can't use static/const for the data being sent, what might be pretty much annoying when you have to send the same data multiple times. Our approach is definitely more robust, although it may be reasonable to simplify it somehow. >>>- (A) retrieves SPI message from the queue in sequential order (FIFO), >>> >>> > >Only with respect to a given device. It would make no sense to reorder the >queue so that writing X, then Y, then Z would morph into "X Z Y" or "Z Y X". :) > >It's specifically _undefined_ how requests going to different devices are ordered. >Some hardware will be happier if things are synchronized (e.g. to a vertical >retrace IRQ), some systems might need to prioritize certain devices, and so on. > >I do think FIFO makes a good general policy, for boards without any of those >special requirements. > > > > >>> but (B) provides more flexible way by providing >>> special callback to retrieve next message from queue. This callback may >>> implement its own discipline of scheduling >>> SPI messages. In any way, the default is FIFO. >>> >>> >>I think (A) is missing a method of adding extra spi_message(s) in callback to extend the current >>transfer on that SPI device. I can imagine a case where you will be required to read status >>information from a device and in this status information is the length of the data it has just >>received (for example if it was a network adapter). Straight after reading this information the >>device would start sending the data it has received but when the read status message was issued >>the length of the data wasn't known. >> >> > >Do you actually have hardware which works that way? That would be an example >of a system that needs some specific prioritization of transfers (see below). > > Let's just agree on the _fact_ that here our approach overcame yours. IMO it's pretty evident. > > > >>Currently with (A) we would have to stop the transfer and >>restart the whole thing again, this time using the length of the data we found form the last >>message. >> >> > >Well, each transfer segement would clearly stop, but if that segment had >the flag set which says "leave chipselect active", then the controller >driver would have the flexibility to prioritize transfers to that chip. > > Hey, you've told us earlier you don't need any flags, right? If we start talking about flags necessity, then your *complicated* approach to the spi_message doesn't make sense, sorry. >>>- (A) uses standartized way to provide CS information, and (B) relies on >>> functional drivers callbacks, which looks more >>> flexible to me. >>> >>> >>I'm not sure what you mean here. You need to provide the cs numbers with SPI device in order for >>the core to create the unique addres and entry in sysfs. >> >> > >I'm not sure what he means either. :) > >Stephen's PXA2xx SPI driver uses callbacks internally, but that's kind of >specific to that PXA hardware ... there's no chipselect handled by the >controller, one of the dozens of GPIOs must be chosen and that's clearly >a board-specific mechanism (uses controller_data as I recall). He tells >me he plans to post the latest version of that -- many updates including >PXA255 SSP support (not just NSSP) and code shrinkage -- early next week. > >But most of the SPI controllers I've seen just have a fixed number of >chipselects, typically four, handled directly by the controller. That's >why the "standardized way" is just to use a 0..N chipselect number. > > Suppose we have a specific driver or system service (API) implemented to handle chip selects. We'll have to duplicate its functionality within your approach what is incorrect in architectural terms. Our approach is free from this drawback. Vitaly |
From: David B. <da...@pa...> - 2005-11-21 21:27:48
|
On Monday 21 November 2005 12:15 pm, Mark Underwood wrote: > > --- dmitry pervushin <dpe...@gm...> wrote: > > > The list of main differences between David Brownell's SPI framework (A) > > and my one (B): A == http://marc.theaimsgroup.com/?l=linux-kernel&m=113169588230519&w=2 plus a handful of rather minor tweaks (ssize_t, comments, etc). B == http://sourceforge.net/mailarchive/message.php?msg_id=13824397 but it needs updates to match current 2.6.15-rc code. > > - (A) uses more complicated structure of SPI message, that contains one > > or more atomic transfers, and (B) > > offers the only spi_msg that represents the atomic transfer on SPI bus. > > The similar approach can be implemented > > in (B), and actually is implemented. But my imp[ression is that > > such enhancement may be added later.. > > I wouldn't have said that the message structure in (A) is more complex then (B). For example, in > (B) you have many flags which controls things like SPI mode which are not needed in every message. > Once the SPI controller has been setup for a particular slave device you don't need to constantly > send this information. And in fact, constantly sending it means some drivers will have to waste time constantly checking it, in case it changed. If that setup is stored in controller registers, it's a lot better to just have the setup() call be responsible for changing the communication parameters. (This is the approach used by both MMC and PCMCIA, for what it's worth...) > In (B) how to do you handle SPI devices which require to send several messages with out releasing > their cs? There are/will be some devices which require this. In fact, that's why the transfer segments are grouped. One builds SPI protocol requests out of several such segments. A very common idiom is writing a command, then reading its response. Chipselect must stay active during the whole sequence. Adding support for such a basic mechanism "later" doesn't seem like a good idea to me. > > - (A) uses workqueues to queue and handle SPI messages, and (B) > > allocates the kernel thread to the same purpose. > > Using workqueues is not very good solution in real-time environment; I > > think that allocating and starting the > > separate thread will give us more predictable and stable results; > > Where does (A) use a workqueue? (A) doesn't use a workqueue or thread and instead leaves it up to > the adapter driver how to handle the messages that it gets sent (which in the case of some drivers > will mean no thread or workqueue). (B) is _forcing_ a thread on the adapter which the adapter may > not need. Exactly. That's one of the things I meant when I recently listed some of the top goals of the framework I did: (a) SPI shouldn't perpetuate the driver model botches of I2C; (b) ditto I2C's "everything is synchronous" botches; (c) it should work well with DMA, to support things like DataFlash; (d) given the variety of SPI chips, protocol controls are needed; (e) place minimal implementation constraints on controller drivers. So for example one way you know that (c) is well met is that it's the same approach used in USB (both host and peripheral/gadget sides); that's been working well for quite a few years now. (Despite comments from Dmitry and Vitaly to the contrary.) > > - (A) has some assumptions on buffers that are passed down to spi > > functions. Make that "requirements"; FWIW they're the same ones that apply to all other kernel driver frameworks I've seen: that buffers be DMA-safe. It would not be helpful (IMO) to define different rules; that's also called the "Principle of Least Astonishment". :) > > If some controller driver (or bus driver > > in terms of (B)) tries to perform DMA transfers, it must copy the > > passed buffers to some memory allocated > > using GFP_DMA flag and map it using dma_map_single. Based on this and other comments from Dmitry/Vitaly, I suspect they don't see how the Linux DMA APIs work. The correct statement is that if a controller driver wants to use DMA, it must dma_{map,unmap}_single(). The upper level drivers don't _need_ to worry about that. However, some key infrastructure is in place to let SPI protocol drivers (the ones passing messages through the controller then the bus) provide pre-mapped buffers if the eventually _want_ to do that. They'd likely be allocated with dma_alloc_coherent() or through a dma_pool. That would be useful for cases like an MMC/SD block driver that talks SPI, since the scatterlists will come down from the block layer ... lower level drivers should be able to ignore details like how dma_{map,unmap}_sg() works. > > From the other > > hand, (B) relies on callbacks provided > > by SPI device driver to allocate memory for DMA transfers, but keeps > > ability to pass user-allocated buffers down > > to SPI functions by specifying flags in SPI message. SPI message being > > a fundamental essense looks better to me when > > it's as simple as possible. Especially when we don't lose any > > flexibility which is exacly our case (buffers that are > > allocated as well as message itself/provided by user, DMA-capable > > buffers..) > > But allocating and freeing buffer is a core kernel thing not a SPI thing. To me you are adding > more complexity then is needed and your saying this is keeping things simple? That's how I read his comments too. Moreover, that particular kind of complexity is the confusing kind ... it makes it a lot harder to see what's going on, since it's all hidden behind layers of indirection. Indirection is of course useful sometimes. But not in this case, where there are much simpler idioms, with the advantage that most other kernel APIs use them. > > - (A) retrieves SPI message from the queue in sequential order (FIFO), Only with respect to a given device. It would make no sense to reorder the queue so that writing X, then Y, then Z would morph into "X Z Y" or "Z Y X". :) It's specifically _undefined_ how requests going to different devices are ordered. Some hardware will be happier if things are synchronized (e.g. to a vertical retrace IRQ), some systems might need to prioritize certain devices, and so on. I do think FIFO makes a good general policy, for boards without any of those special requirements. > > but (B) provides more flexible way by providing > > special callback to retrieve next message from queue. This callback may > > implement its own discipline of scheduling > > SPI messages. In any way, the default is FIFO. > > I think (A) is missing a method of adding extra spi_message(s) in callback to extend the current > transfer on that SPI device. I can imagine a case where you will be required to read status > information from a device and in this status information is the length of the data it has just > received (for example if it was a network adapter). Straight after reading this information the > device would start sending the data it has received but when the read status message was issued > the length of the data wasn't known. Do you actually have hardware which works that way? That would be an example of a system that needs some specific prioritization of transfers (see below). > Currently with (A) we would have to stop the transfer and > restart the whole thing again, this time using the length of the data we found form the last > message. Well, each transfer segement would clearly stop, but if that segment had the flag set which says "leave chipselect active", then the controller driver would have the flexibility to prioritize transfers to that chip. > A better solution would to be able to add an extra message during the callback from the first > message as now we know then length we can setup a transfer that would be the correct size. > However, this message must be the next message that the adapter sends as if another message for > another SPI device was sent before then the cs line of the device we are talking to would be > deselected and we would have to start again. I suspect that in terms of API, *if* that semantic is really needed (as in, you have hardware that needs it) then it should be made into a flag in the spi_message. Clearly, it'd make things more complicated for the SPI controller driver; drivers that don't implement that semantic would need to know when it's required, so they could fail cleanly. And drivers that _do_ know about it would need to avoid doing things like shuffling completions off to some tasklet (while starting the next transfer ASAP, getting I/O overlap) ... they'd need to make stronger guarantees about transaction sequencing, at a certain cost in terms of potential throughput. > My proposal is that in the callback from a spi_message being sent it returns a pointer to the next > spi_message which the adapter will send before it continues sending any other messages (this is > like the adapter being locked by the SPI device), if no other messages need to be sent atomically > in the callback of current message then the SPI device driver would just return NULL. The thing I don't like about that model is that, just like the Linux-USB API for interrupt and isochronous transfers in the 2.4 kernels, it swallows fault modes so that drivers can't know when things break. Better IMO to just keep the same API in all cases, and require that callback to directly submit a (new?) transfer if that's needed. If the controller can't accept it, it'll know right away, and then the protocol driver will be able to do something appropriate. > > - (A) uses standartized way to provide CS information, and (B) relies on > > functional drivers callbacks, which looks more > > flexible to me. > > I'm not sure what you mean here. You need to provide the cs numbers with SPI device in order for > the core to create the unique addres and entry in sysfs. I'm not sure what he means either. :) Stephen's PXA2xx SPI driver uses callbacks internally, but that's kind of specific to that PXA hardware ... there's no chipselect handled by the controller, one of the dozens of GPIOs must be chosen and that's clearly a board-specific mechanism (uses controller_data as I recall). He tells me he plans to post the latest version of that -- many updates including PXA255 SSP support (not just NSSP) and code shrinkage -- early next week. But most of the SPI controllers I've seen just have a fixed number of chipselects, typically four, handled directly by the controller. That's why the "standardized way" is just to use a 0..N chipselect number. > However, (A) is not checking to see if the cs that a registering device wants to use is already in > use, this needs to be added, and the same is true for registering spi masters. Yes, I even have a "FIXME Paranoia argues that ..." comment in that code. I think the best way to handle that is probably to get the driver name out of the device name, thereby punting that check to the driver model. So the devices would have names like "spi3.2" and the driver name would be in "modalias"; I think that'll be a simple enough change to the framework, now that I've thought of it. This is not a case where we _need_ to act much like platform_device. - Dave |
From: Mark U. <bas...@ya...> - 2005-11-21 21:23:34
|
--- Vitaly Wool <vw...@ru...> wrote: > Mark Underwood wrote: > > >>The list of main differences between David Brownell's SPI framework (A) > >>and my one (B): > >>- (A) uses more complicated structure of SPI message, that contains one > >>or more atomic transfers, and (B) > >> offers the only spi_msg that represents the atomic transfer on SPI bus. > >>The similar approach can be imple- > >> mented in (B), and actually is implemented. But my imp[ression is that > >>such enhancement may be added later.. > >> > >> > > > >I wouldn't have said that the message structure in (A) is more complex then (B). For example, > in > >(B) you have many flags which controls things like SPI mode which are not needed in every > message. > >Once the SPI controller has been setup for a particular slave device you don't need to > constantly > >send this information. > >In (B) how to do you handle SPI devices which require to send several messages with out > releasing > >their cs? There are/will be some devices which require this. > > > > > Please see the explanation for the 'flags' in Documentation/spi.txt > within the patch. I can see that you can leave cs active at the end of a transfer but that's not my point. How do you make sure that message for other SPI devices don't get send while the cs of the current device is high? > > > > > > >>- (A) uses workqueues to queue and handle SPI messages, and (B) > >>allocates the kernel thread to the same purpose. > >> Using workqueues is not very good solution in real-time environment; I > >>think that allocating and starting the > >> separate thread will give us more predictable and stable results; > >> > >> > > > >Where does (A) use a workqueue? (A) doesn't use a workqueue or thread and instead leaves it up > to > >the adapter driver how to handle the messages that it gets sent (which in the case of some > drivers > >will mean no thread or workqueue). (B) is _forcing_ a thread on the adapter which the adapter > may > >not need. > > > > > I bet the drivers that don't need neither threads not workqueue there's > no need in async transfers as well. :) > On the other hand, threads is a flexible mechanism for handling async > stuff, and there won't be a lot of threads so the overhead won't be high. > You might also want to ask why you can't change the steering wheel > placement in your car from right-side to rleft-side although you travel > by car to continental Europe once per decade. ;-) Sorry I'm not following you here. Example: An interrupt driven PIO doesn't need a thread or a workqueue. When it is idle the call to its transfer function can start off the first transfer and after that the interrupt routine will check for a new transfer when it has finished the current one. David provided other examples so if you are still not sure search through the archives. > > > > > > >>- (A) has some assumptions on buffers that are passed down to spi > >>functions. If some controller driver (or bus driver > >> in terms of (B)) tries to perform DMA transfers, it must copy the > >>passed buffers to some memory allocated > >> using GFP_DMA flag and map it using dma_map_single. From the other > >>hand, (B) relies on callbacks provided > >> by SPI device driver to allocate memory for DMA transfers, but keeps > >>ability to pass user-allocated buffers down > >> to SPI functions by specifying flags in SPI message. SPI message being > >>a fundamental essense looks better to me when > >> it's as simple as possible. Especially when we don't lose any > >>flexibility which is exacly our case (buffers that are > >> allocated as well as message itself/provided by user, DMA-capable > >>buffers..) > >> > >> > > > >But allocating and freeing buffer is a core kernel thing not a SPI thing. To me you are adding > >more complexity then is needed and your saying this is keeping things simple? > > > > > I'm afraid that you're not quite getting the whole concept. The concept > is to provide thorough and stable solution. > Given that the buffer passed is declared as, say, static, the whole > kernel might crash if we try to pass it to DMA. David's core itself is > not capable of filtering that and letting the driver decide adds more > complexity to the driver. > If we're choosing between adding complexity to the core and adding it to > the particular drivers, it's definitely better to add it to the core cuz > it's done _once_. I'm not 100% sure how David is handling this, but one option would be to have a not_dmaable flag which states that the buffers used in this message are not DMAable and in this case the adapter driver will either do a PIO transfer or bounce the data to/from a DMAable buffer it allocated itself. I don't see why a SPI adapter driver needs to supply alloc/free callbacks when a simple flag would do the job. > > >>- (A) uses standartized way to provide CS information, and (B) relies on > >>functional drivers callbacks, which looks more > >> flexible to me. > >> > >> > > > >I'm not sure what you mean here. You need to provide the cs numbers with SPI device in order > for > >the core to create the unique addres and entry in sysfs. > >However, (A) is not checking to see if the cs that a registering device wants to use is already > in > >use, this needs to be added, and the same is true for registering spi masters. > > > > > Can you please elaborate on that? If I register a SPI device on cs1 of spi-1 and later try to register another device on cs1 of spi-1 I would expect the spi core layer to fail the registration of the second device. Mark > > Vitaly > - > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to maj...@vg... > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ > ___________________________________________________________ To help you stay safe and secure online, we've developed the all new Yahoo! Security Centre. http://uk.security.yahoo.com |
From: Vitaly W. <vw...@ru...> - 2005-11-21 20:56:21
|
Mark Underwood wrote: >>The list of main differences between David Brownell's SPI framework (A) >>and my one (B): >>- (A) uses more complicated structure of SPI message, that contains one >>or more atomic transfers, and (B) >> offers the only spi_msg that represents the atomic transfer on SPI bus. >>The similar approach can be imple- >> mented in (B), and actually is implemented. But my imp[ression is that >>such enhancement may be added later.. >> >> > >I wouldn't have said that the message structure in (A) is more complex then (B). For example, in >(B) you have many flags which controls things like SPI mode which are not needed in every message. >Once the SPI controller has been setup for a particular slave device you don't need to constantly >send this information. >In (B) how to do you handle SPI devices which require to send several messages with out releasing >their cs? There are/will be some devices which require this. > > Please see the explanation for the 'flags' in Documentation/spi.txt within the patch. > > >>- (A) uses workqueues to queue and handle SPI messages, and (B) >>allocates the kernel thread to the same purpose. >> Using workqueues is not very good solution in real-time environment; I >>think that allocating and starting the >> separate thread will give us more predictable and stable results; >> >> > >Where does (A) use a workqueue? (A) doesn't use a workqueue or thread and instead leaves it up to >the adapter driver how to handle the messages that it gets sent (which in the case of some drivers >will mean no thread or workqueue). (B) is _forcing_ a thread on the adapter which the adapter may >not need. > > I bet the drivers that don't need neither threads not workqueue there's no need in async transfers as well. :) On the other hand, threads is a flexible mechanism for handling async stuff, and there won't be a lot of threads so the overhead won't be high. You might also want to ask why you can't change the steering wheel placement in your car from right-side to rleft-side although you travel by car to continental Europe once per decade. ;-) > > >>- (A) has some assumptions on buffers that are passed down to spi >>functions. If some controller driver (or bus driver >> in terms of (B)) tries to perform DMA transfers, it must copy the >>passed buffers to some memory allocated >> using GFP_DMA flag and map it using dma_map_single. From the other >>hand, (B) relies on callbacks provided >> by SPI device driver to allocate memory for DMA transfers, but keeps >>ability to pass user-allocated buffers down >> to SPI functions by specifying flags in SPI message. SPI message being >>a fundamental essense looks better to me when >> it's as simple as possible. Especially when we don't lose any >>flexibility which is exacly our case (buffers that are >> allocated as well as message itself/provided by user, DMA-capable >>buffers..) >> >> > >But allocating and freeing buffer is a core kernel thing not a SPI thing. To me you are adding >more complexity then is needed and your saying this is keeping things simple? > > I'm afraid that you're not quite getting the whole concept. The concept is to provide thorough and stable solution. Given that the buffer passed is declared as, say, static, the whole kernel might crash if we try to pass it to DMA. David's core itself is not capable of filtering that and letting the driver decide adds more complexity to the driver. If we're choosing between adding complexity to the core and adding it to the particular drivers, it's definitely better to add it to the core cuz it's done _once_. >>- (A) uses standartized way to provide CS information, and (B) relies on >>functional drivers callbacks, which looks more >> flexible to me. >> >> > >I'm not sure what you mean here. You need to provide the cs numbers with SPI device in order for >the core to create the unique addres and entry in sysfs. >However, (A) is not checking to see if the cs that a registering device wants to use is already in >use, this needs to be added, and the same is true for registering spi masters. > > Can you please elaborate on that? Vitaly |
From: Vitaly W. <vw...@ru...> - 2005-11-21 20:31:50
|
Hi, apart of just struggling whose core is better, I'd like to find rationale in both. I think that we've got to have a final convergence of the cores, so let's pass over to constructive dialogue. >I'll comment that you've significantly mischaracterized the work I've >done in your "list of main differences" in several respects, but I've >not yet read the whole thing. > > Can you please review both the list and the new core and comment on the recent one? >Also, that won't work on current GIT trees because it's not caught >up to the "remove third argument to device_driver.suspend()" patch, >which Russell King did as followup to the previous version of my >patch. (URL for current version of my patch is below, which IS >caught up to that and other 2.6.14+ changes.) > > > > True, although it works with 2.6.14 which is probably the most recent kernel needed for anyone else aside :-) Will respin shortly anyway. >>I see no discussion on the kernel/spi-devel list on this patch. I am >>working on TI OMAP2420 (linux-omap-open-source), I want to use this >>latest spi framework for omap24xx spi driver implementation. >> >> > >I'd suggest instead using the light weight framework I sent before: > > http://marc.theaimsgroup.com/?l=linux-kernel&m=113169588230519&w=2 > >That's got a bunch of drivers working in it right now, including an >ads7846 (touchscreen/sensor) driver that runs on both OMAP and PXA >controller drivers. Reusable drivers ... what a concept! :) > > Well, I can agree that for simple SPI devices David's core might be easier to use. >>SPI already works on 24xx using custom hack for audio and touchscreen >>but it can not be submitted to linux-omap tree, as it is not following >>proper driver model. >> >> > >That's one of the reasons I started on the SPI stuff ... the current >MicroWire hookup (for TSC210x audio/touchscreen and ADS7846 touchscreen) >sort of sucks from the driver model perspective. But then, so did the >first half dozen SPI "framework" patches I saw posted on LKML, so I >though it might help if I fixed that not-so-little issue. :) > >I've tried to take the best of the frameworks I've seen, subject to the >basic constraints that > >(a) SPI shouldn't perpetuate the driver model botches of I2C; >(b) ditto I2C's "everything is synchronous" botches; > > Those two are fulfilled in both cores. >(c) it should work well with DMA, to support things like DataFlash; > > Lemme point once again that David' core is not DMA-ready. More comments on that will follow, lemme get some sleep :) >(d) given the variety of SPI chips, protocol controls are needed; > > IMHO this one is fulfilled in both. >(e) place minimal implementation constraints on controller drivers. > > Yeah David's core fulfilling this one well. However, my view on that is it's leaving too much for the controller driver to implement, requiring so much sophistication from it that it partially disapproves the whole concept of having the _core_. >>If you guys give me the latest update on this framework and it's >>possiblity of getting accepted in linux-kernel mailing list? >>_otherwise_ I can take this framework write a driver for 24xx and >>submit to linux-omap-open-source mailing list, so that it can get omap >>specific audience + more testing. >> >> > >Anything can be accepted onto LKML ... it's an open list! The issue >is more whether some API starts to become official, by merging into >first the MM tree and then the mainstream kernel. That matters because >the point of a framework is code re-use, and that won't happen if there >are multiple frameworks. > > Yeah and I do suggest that we start a true convergence of the cores. I think that it should happen after David thoroughly reviews/comments Dmitry's notes on his core rather than just say in bare words that they're invalid. Vitaly |
From: Mark U. <bas...@ya...> - 2005-11-21 20:15:55
|
--- dmitry pervushin <dpe...@gm...> wrote: > This is an updated version of SPI framework from me, Dmitry Pervushin. > It seems that now it is good time to consolidate our SPI frameworks to > push it to kernel :) > > We've tested our SPI core as well with bus drivers with wireless LAN > driver and achieved good performance with relatively small overhead. > This proves the viability of this framework in real life even in > real-time environment. The size of .text is > still about 2,500 bytes, that is comparable with David Brownell's > framework size. > > I think now is the time to start the final convergence process for these > two cores and get the final core > into the mainline kernel. And in order to understand where we need to > converge, I created the main differences > list (see below). > > The list of main differences between David Brownell's SPI framework (A) > and my one (B): > - (A) uses more complicated structure of SPI message, that contains one > or more atomic transfers, and (B) > offers the only spi_msg that represents the atomic transfer on SPI bus. > The similar approach can be imple- > mented in (B), and actually is implemented. But my imp[ression is that > such enhancement may be added later.. I wouldn't have said that the message structure in (A) is more complex then (B). For example, in (B) you have many flags which controls things like SPI mode which are not needed in every message. Once the SPI controller has been setup for a particular slave device you don't need to constantly send this information. In (B) how to do you handle SPI devices which require to send several messages with out releasing their cs? There are/will be some devices which require this. > - (A) uses workqueues to queue and handle SPI messages, and (B) > allocates the kernel thread to the same purpose. > Using workqueues is not very good solution in real-time environment; I > think that allocating and starting the > separate thread will give us more predictable and stable results; Where does (A) use a workqueue? (A) doesn't use a workqueue or thread and instead leaves it up to the adapter driver how to handle the messages that it gets sent (which in the case of some drivers will mean no thread or workqueue). (B) is _forcing_ a thread on the adapter which the adapter may not need. > - (A) has some assumptions on buffers that are passed down to spi > functions. If some controller driver (or bus driver > in terms of (B)) tries to perform DMA transfers, it must copy the > passed buffers to some memory allocated > using GFP_DMA flag and map it using dma_map_single. From the other > hand, (B) relies on callbacks provided > by SPI device driver to allocate memory for DMA transfers, but keeps > ability to pass user-allocated buffers down > to SPI functions by specifying flags in SPI message. SPI message being > a fundamental essense looks better to me when > it's as simple as possible. Especially when we don't lose any > flexibility which is exacly our case (buffers that are > allocated as well as message itself/provided by user, DMA-capable > buffers..) But allocating and freeing buffer is a core kernel thing not a SPI thing. To me you are adding more complexity then is needed and your saying this is keeping things simple? > - (A) retrieves SPI message from the queue in sequential order (FIFO), > but (B) provides more flexible way by providing > special callback to retrieve next message from queue. This callback may > implement its own discipline of scheduling > SPI messages. In any way, the default is FIFO. I think (A) is missing a method of adding extra spi_message(s) in callback to extend the current transfer on that SPI device. I can imagine a case where you will be required to read status information from a device and in this status information is the length of the data it has just received (for example if it was a network adapter). Straight after reading this information the device would start sending the data it has received but when the read status message was issued the length of the data wasn't known. Currently with (A) we would have to stop the transfer and restart the whole thing again, this time using the length of the data we found form the last message. A better solution would to be able to add an extra message during the callback from the first message as now we know then length we can setup a transfer that would be the correct size. However, this message must be the next message that the adapter sends as if another message for another SPI device was sent before then the cs line of the device we are talking to would be deselected and we would have to start again. My proposal is that in the callback from a spi_message being sent it returns a pointer to the next spi_message which the adapter will send before it continues sending any other messages (this is like the adapter being locked by the SPI device), if no other messages need to be sent atomically in the callback of current message then the SPI device driver would just return NULL. Example: ======== /* callback/complete routine of a SPI device/protocol driver */ int my_spi_callback (void *data) { struct my_status_struct = data; struct spi_message read_message; /* Check to see if we have received any data */ if (my_status_struct->read_length) { /* Create a new spi_message to read the data which will be * the very next thing the device will send */ read_message = kzalloc(...) ... return read_message; } else /* No data to be read so don't append another message */ return NULL; } > - (A) uses standartized way to provide CS information, and (B) relies on > functional drivers callbacks, which looks more > flexible to me. I'm not sure what you mean here. You need to provide the cs numbers with SPI device in order for the core to create the unique addres and entry in sysfs. However, (A) is not checking to see if the cs that a registering device wants to use is already in use, this needs to be added, and the same is true for registering spi masters. Mark ___________________________________________________________ To help you stay safe and secure online, we've developed the all new Yahoo! Security Centre. http://uk.security.yahoo.com |
From: Vitaly W. <vw...@ru...> - 2005-11-21 20:10:45
|
David Brownell wrote: >As for "decision", I've heard from several people who said they were >building controller or protocol drivers in that framework, and wondered >when it would get into the MM tree. I wouldn't have heard such comments >about what Dmitry and Vitaly have done, of course. > > Well, we've got WiFi working over SPI support using our core, as well as MMC over SPI support and support for some less important devices like SPI EEPROMs etc. |
From: Vitaly W. <vw...@ru...> - 2005-11-21 18:26:39
|
Hi, David Brownell wrote: >(c) it should work well with DMA, to support things like DataFlash; > > yep! And I'm afraid it's not applicable to David's core though. Vitaly |
From: David B. <da...@pa...> - 2005-11-21 18:23:42
|
On Monday 21 November 2005 7:54 am, Rolf Offermanns wrote: > On Monday 21 November 2005 15:49, Vitaly Wool wrote: > > Hello guys, > > > > answering you on behalf of Dmitry. > > We're also working on SPI driver development for the core Dmitry and me > > wrote. The latest core itself was posted to the Linux Kernel Mailing > > list and to spi...@li... 11/11 so you are > > encouraged to use it :) > > What about the "Simple SPI core" from David Brownell? Did I miss a decision > for or against one or the other? I've decided it's still worth refreshing ... :) http://marc.theaimsgroup.com/?l=linux-kernel&m=113169588230519&w=2 As for "decision", I've heard from several people who said they were building controller or protocol drivers in that framework, and wondered when it would get into the MM tree. I wouldn't have heard such comments about what Dmitry and Vitaly have done, of course. - Dave |
From: David B. <da...@pa...> - 2005-11-21 18:18:01
|
On Monday 21 November 2005 1:29 am, Komal Shah wrote: > --- dmitry pervushin <dpe...@gm...> wrote: > > > This is an updated version of SPI framework from me, Dmitry > > Pervushin. > > It seems that now it is good time to consolidate our SPI frameworks > > to push it to kernel :) Hmm, I didn't see this message until just now when I found it in the archives at http://sourceforge.net/mailarchive/forum.php?thread_id=8945243&forum_id=45866 I'll comment that you've significantly mischaracterized the work I've done in your "list of main differences" in several respects, but I've not yet read the whole thing. Also, that won't work on current GIT trees because it's not caught up to the "remove third argument to device_driver.suspend()" patch, which Russell King did as followup to the previous version of my patch. (URL for current version of my patch is below, which IS caught up to that and other 2.6.14+ changes.) > I see no discussion on the kernel/spi-devel list on this patch. I am > working on TI OMAP2420 (linux-omap-open-source), I want to use this > latest spi framework for omap24xx spi driver implementation. I'd suggest instead using the light weight framework I sent before: http://marc.theaimsgroup.com/?l=linux-kernel&m=113169588230519&w=2 That's got a bunch of drivers working in it right now, including an ads7846 (touchscreen/sensor) driver that runs on both OMAP and PXA controller drivers. Reusable drivers ... what a concept! :) > SPI already works on 24xx using custom hack for audio and touchscreen > but it can not be submitted to linux-omap tree, as it is not following > proper driver model. That's one of the reasons I started on the SPI stuff ... the current MicroWire hookup (for TSC210x audio/touchscreen and ADS7846 touchscreen) sort of sucks from the driver model perspective. But then, so did the first half dozen SPI "framework" patches I saw posted on LKML, so I though it might help if I fixed that not-so-little issue. :) I've tried to take the best of the frameworks I've seen, subject to the basic constraints that (a) SPI shouldn't perpetuate the driver model botches of I2C; (b) ditto I2C's "everything is synchronous" botches; (c) it should work well with DMA, to support things like DataFlash; (d) given the variety of SPI chips, protocol controls are needed; (e) place minimal implementation constraints on controller drivers. I think I basically succeeded in that. The most recent iterations of Dmitry's framework that I've seen fell short IMO on all except (b), though I don't know about his newest patch (URL above). > If you guys give me the latest update on this framework and it's > possiblity of getting accepted in linux-kernel mailing list? > _otherwise_ I can take this framework write a driver for 24xx and > submit to linux-omap-open-source mailing list, so that it can get omap > specific audience + more testing. Anything can be accepted onto LKML ... it's an open list! The issue is more whether some API starts to become official, by merging into first the MM tree and then the mainstream kernel. That matters because the point of a framework is code re-use, and that won't happen if there are multiple frameworks. My own plan was to ask if Andrew would let my patch cook in the MM tree for a while, after Stephen posted his pxa2xx_spi update. (I consider a decent basic patchset would be the core, pxa2xx_spi, ads7846, and maybe the DataFlash driver even though that's just ported not tested. I could submit the platform support for Lubbock, which is one of the defacto reference platforms for PXA 25x chips ... a holdup has been the fact that some of the post-2.6.14 ARM MMU rework broke booting there, fixes are on the way after much headscratching.) I know there are other folk already working on that same framework, who want a quasi-official blessing. But I agree that this issue is worth trying to close. I'd be interested in any comments from Andrew or Greg, but probably LKML is the best place to have technical discussions related to "which framework". - Dave |
From: Vitaly W. <vw...@ru...> - 2005-11-21 15:58:58
|
Rolf Offermanns wrote: >On Monday 21 November 2005 15:49, Vitaly Wool wrote: > > >>Hello guys, >> >>answering you on behalf of Dmitry. >>We're also working on SPI driver development for the core Dmitry and me >>wrote. The latest core itself was posted to the Linux Kernel Mailing >>list and to spi...@li... 11/11 so you are >>encouraged to use it :) >> >> > >What about the "Simple SPI core" from David Brownell? Did I miss a decision >for or against one or the other? > >-Rolf > > I don't think there's a decision yet. Anyway, the cores are converging. See also Dmitry Pervushin's message in LKML with the comparison of the cores. What I can say however is the David's core doesn't work with Philips ARMv5 board (Premo) and it adds a whole lot of complication to the bus driver to overcome that. Vitaly |
From: Rolf O. <rof...@sy...> - 2005-11-21 15:54:38
|
On Monday 21 November 2005 15:49, Vitaly Wool wrote: > Hello guys, > > answering you on behalf of Dmitry. > We're also working on SPI driver development for the core Dmitry and me > wrote. The latest core itself was posted to the Linux Kernel Mailing > list and to spi...@li... 11/11 so you are > encouraged to use it :) What about the "Simple SPI core" from David Brownell? Did I miss a decision for or against one or the other? -Rolf -- Rolf Offermanns <rof...@sy...> SYSGO AG Tel.: +49-6136-9948-0 Am Pfaffenstein 14 Fax: +49-6136-9948-10 55270 Klein-Winternheim http://www.sysgo.com |
From: Vitaly W. <vw...@ru...> - 2005-11-21 14:49:23
|
Hello guys, answering you on behalf of Dmitry. We're also working on SPI driver development for the core Dmitry and me wrote. The latest core itself was posted to the Linux Kernel Mailing list and to spi...@li... 11/11 so you are encouraged to use it :) Thanks, Vitaly Komal Shah wrote: >--- dmitry pervushin <dpe...@gm...> wrote: > > > >>This is an updated version of SPI framework from me, Dmitry >>Pervushin. >>It seems that now it is good time to consolidate our SPI frameworks >>to >>push it to kernel :) >> >> >> > >I see no discussion on the kernel/spi-devel list on this patch. I am >working on TI OMAP2420 (linux-omap-open-source), I want to use this >latest spi framework for omap24xx spi driver implementation. > >SPI already works on 24xx using custom hack for audio and touchscreen >but it can not be submitted to linux-omap tree, as it is not following >proper driver model. > >If you guys give me the latest update on this framework and it's >possiblity of getting accepted in linux-kernel mailing list? >_otherwise_ I can take this framework write a driver for 24xx and >submit to linux-omap-open-source mailing list, so that it can get omap >specific audience + more testing. > >---Komal Shah >http://komalshah.blogspot.com/ > > > > >__________________________________ >Yahoo! Mail - PC Magazine Editors' Choice 2005 >http://mail.yahoo.com > > >------------------------------------------------------- >This SF.Net email is sponsored by the JBoss Inc. Get Certified Today >Register for a JBoss Training Course. Free Certification Exam >for All Training Attendees Through End of 2005. For more info visit: >http://ads.osdn.com/?ad_id=7628&alloc_id=16845&op=click >_______________________________________________ >spi-devel-general mailing list >spi...@li... >https://lists.sourceforge.net/lists/listinfo/spi-devel-general > > > > |
From: Jaeger, J. <Joa...@di...> - 2005-11-21 14:43:41
|
Hi Dmitry, > This is an updated version of SPI framework from me, Dmitry > Pervushin. > It seems that now it is good time to consolidate our SPI frameworks > to > push it to kernel :) We are working on several platforms: AT91RM9200, NS9360, NS9750, PXA270, S3C2410, S3C2440 We have been looking for a SPI interface for a while, because we want to integrate a common SPI interface on all of our platforms, based on the 2.6.14 kernel. The work you, and others, have done, looks very good. And we would like to support you with this framework. If you could provide me with the latest code, we would integrate it in our platforms, test it, give you feedback from our tests and provide the code to the community. With best regards Joachim -- Joachim Jaeger FS Forth-Systeme GmbH A DIGI International Company Kueferstrasse 8 79206 Breisach Germany Phone: +49-7667-908-0 Fax: +49-7667-908-200 mailto:joa...@di... web: www.fsforth.de, www.digi.com |
From: Komal S. <kom...@ya...> - 2005-11-21 09:29:31
|
--- dmitry pervushin <dpe...@gm...> wrote: > This is an updated version of SPI framework from me, Dmitry > Pervushin. > It seems that now it is good time to consolidate our SPI frameworks > to > push it to kernel :) > I see no discussion on the kernel/spi-devel list on this patch. I am working on TI OMAP2420 (linux-omap-open-source), I want to use this latest spi framework for omap24xx spi driver implementation. SPI already works on 24xx using custom hack for audio and touchscreen but it can not be submitted to linux-omap tree, as it is not following proper driver model. If you guys give me the latest update on this framework and it's possiblity of getting accepted in linux-kernel mailing list? _otherwise_ I can take this framework write a driver for 24xx and submit to linux-omap-open-source mailing list, so that it can get omap specific audience + more testing. ---Komal Shah http://komalshah.blogspot.com/ __________________________________ Start your day with Yahoo! - Make it your home page! http://www.yahoo.com/r/hs |
From: Komal S. <kom...@ya...> - 2005-11-21 09:14:24
|
--- dmitry pervushin <dpe...@gm...> wrote: > This is an updated version of SPI framework from me, Dmitry > Pervushin. > It seems that now it is good time to consolidate our SPI frameworks > to > push it to kernel :) > I see no discussion on the kernel/spi-devel list on this patch. I am working on TI OMAP2420 (linux-omap-open-source), I want to use this latest spi framework for omap24xx spi driver implementation. SPI already works on 24xx using custom hack for audio and touchscreen but it can not be submitted to linux-omap tree, as it is not following proper driver model. If you guys give me the latest update on this framework and it's possiblity of getting accepted in linux-kernel mailing list? _otherwise_ I can take this framework write a driver for 24xx and submit to linux-omap-open-source mailing list, so that it can get omap specific audience + more testing. ---Komal Shah http://komalshah.blogspot.com/ __________________________________ Yahoo! Mail - PC Magazine Editors' Choice 2005 http://mail.yahoo.com |
From: dmitry p. <dpe...@gm...> - 2005-11-11 10:16:26
|
This is an updated version of SPI framework from me, Dmitry Pervushin. It seems that now it is good time to consolidate our SPI frameworks to push it to kernel :) We've tested our SPI core as well with bus drivers with wireless LAN driver and achieved good performance with relatively small overhead. This proves the viability of this framework in real life even in real-time environment. The size of .text is still about 2,500 bytes, that is comparable with David Brownell's framework size. I think now is the time to start the final convergence process for these two cores and get the final core into the mainline kernel. And in order to understand where we need to converge, I created the main differences list (see below). The list of main differences between David Brownell's SPI framework (A) and my one (B): - (A) uses more complicated structure of SPI message, that contains one or more atomic transfers, and (B) offers the only spi_msg that represents the atomic transfer on SPI bus. The similar approach can be imple- mented in (B), and actually is implemented. But my imp[ression is that such enhancement may be added later.. - (A) uses workqueues to queue and handle SPI messages, and (B) allocates the kernel thread to the same purpose. Using workqueues is not very good solution in real-time environment; I think that allocating and starting the separate thread will give us more predictable and stable results; - (A) has some assumptions on buffers that are passed down to spi functions. If some controller driver (or bus driver in terms of (B)) tries to perform DMA transfers, it must copy the passed buffers to some memory allocated using GFP_DMA flag and map it using dma_map_single. From the other hand, (B) relies on callbacks provided by SPI device driver to allocate memory for DMA transfers, but keeps ability to pass user-allocated buffers down to SPI functions by specifying flags in SPI message. SPI message being a fundamental essense looks better to me when it's as simple as possible. Especially when we don't lose any flexibility which is exacly our case (buffers that are allocated as well as message itself/provided by user, DMA-capable buffers..) - (A) retrieves SPI message from the queue in sequential order (FIFO), but (B) provides more flexible way by providing special callback to retrieve next message from queue. This callback may implement its own discipline of scheduling SPI messages. In any way, the default is FIFO. - (A) uses standartized way to provide CS information, and (B) relies on functional drivers callbacks, which looks more flexible to me. SIgned-off-by: dmitry pervushin <dpe...@gm...> linux/include/linux/spi.h | 285 +++++++++++++++ linux/Documentation/spi.txt | 382 ++++++++++++++++++++ linux/arch/arm/Kconfig | 2 linux/drivers/Kconfig | 2 linux/drivers/Makefile | 1 linux/drivers/spi/Kconfig | 33 + linux/drivers/spi/Makefile | 14 linux/drivers/spi/spi-core.c | 645 +++++++++++++++++++++++++++++++++++ linux/drivers/spi/spi-dev.c | 219 +++++++++++ 9 files changed, 1583 insertions(+) Index: linux/arch/arm/Kconfig =================================================================== --- linux.orig/arch/arm/Kconfig +++ linux/arch/arm/Kconfig @@ -834,6 +834,8 @@ source "drivers/ssi/Kconfig" source "drivers/mmc/Kconfig" +source "drivers/spi/Kconfig" + endmenu source "ktools/Kconfig" Index: linux/drivers/Kconfig =================================================================== --- linux.orig/drivers/Kconfig +++ linux/drivers/Kconfig @@ -42,6 +42,8 @@ source "drivers/char/Kconfig" source "drivers/i2c/Kconfig" +source "drivers/spi/Kconfig" + source "drivers/w1/Kconfig" source "drivers/misc/Kconfig" Index: linux/drivers/Makefile =================================================================== --- linux.orig/drivers/Makefile +++ linux/drivers/Makefile @@ -67,3 +67,4 @@ obj-$(CONFIG_DPM) += dpm/ obj-$(CONFIG_MMC) += mmc/ obj-y += firmware/ obj-$(CONFIG_EVENT_BROKER) += evb/ +obj-$(CONFIG_SPI) += spi/ Index: linux/drivers/spi/Kconfig =================================================================== --- /dev/null +++ linux/drivers/spi/Kconfig @@ -0,0 +1,33 @@ +# +# SPI device configuration +# +menu "SPI support" + +config SPI + default Y + tristate "SPI (Serial Peripheral Interface) bus support" + default false + help + Say Y if you need to enable SPI support on your kernel. + Say M if you want to create the spi-core loadable module. + +config SPI_DEBUG + bool "SPI debug output" + depends on SPI + default false + help + Say Y there if you'd like to see debug output from SPI drivers + If unsure, say N + +config SPI_CHARDEV + default Y + tristate "SPI device interface" + depends on SPI + help + Say Y here to use /dev/spiNN device files. They make it possible to have user-space + programs use the SPI bus. + This support is also available as a module. If so, the module + will be called spi-dev. + +endmenu + Index: linux/drivers/spi/Makefile =================================================================== --- /dev/null +++ linux/drivers/spi/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the kernel spi bus driver. +# + +obj-$(CONFIG_SPI) += spi-core.o +# bus drivers +# ...functional drivers +# ...and the common spi-dev driver +obj-$(CONFIG_SPI_CHARDEV) += spi-dev.o + +ifeq ($(CONFIG_SPI_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif + Index: linux/drivers/spi/spi-core.c =================================================================== --- /dev/null +++ linux/drivers/spi/spi-core.c @@ -0,0 +1,645 @@ +/* + * drivers/spi/spi-core.c + * + * Copyright (C) 2005 MontaVista Software, Inc <so...@mv...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/proc_fs.h> +#include <linux/kmod.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/kthread.h> +#include <linux/spi.h> +#include <asm/atomic.h> + +static int spi_thread(void *context); + +/** + * spi_bus_match_name - verify that driver matches device on spi bus + * + * @dev: device that hasn't yet being assigned to any driver + * @drv: driver for spi device + * + * Drivers and devices on SPI bus are matched by name, just like the + * platform devices, with exception of SPI_DEV_CHAR. Driver with this name + * will be matched against any device +**/ +static int spi_bus_match_name(struct device *dev, struct device_driver *drv) +{ + return !strcmp(drv->name, SPI_DEV_CHAR) || + !strcmp(TO_SPI_DEV(dev)->name, drv->name); +} + +/** + * spi_bus_suspend - suspend all devices on the spi bus + * + * @dev: spi device to be suspended + * @state: state of device to be set + * + * This function set device on SPI bus to state `state', just like platform_bus does +**/ +static int spi_bus_suspend(struct device * dev, u32 state) +{ + int ret = 0; + + if (dev->driver && dev->driver->suspend) { + ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE); + if (ret == 0) + ret = dev->driver->suspend(dev, state, SUSPEND_SAVE_STATE); + if (ret == 0) + ret = dev->driver->suspend(dev, state, SUSPEND_POWER_DOWN); + } + return ret; +} + +/** + * spi_bus_resume - resume functioning of all devices on spi bus + * + * @dev: device to resume + * + * This function resumes device on SPI bus, just like platform_bus does +**/ +static int spi_bus_resume(struct device * dev) +{ + int ret = 0; + + if (dev->driver && dev->driver->resume) { + ret = dev->driver->resume(dev, RESUME_POWER_ON); + if (ret == 0) + ret = dev->driver->resume(dev, RESUME_RESTORE_STATE); + if (ret == 0) + ret = dev->driver->resume(dev, RESUME_ENABLE); + } + return ret; +} + +/** + * spi_bus - the &bus_type structure for SPI devices and drivers + * + * @name: the name of subsystem, "spi" here + * @match: function that matches devices to their drivers + * @suspend: PM callback to suspend device + * @resume: PM callback to resume device +**/ +struct bus_type spi_bus = { + .name = "spi", + .match = spi_bus_match_name, + .suspend = spi_bus_suspend, + .resume = spi_bus_resume, +}; + +/** + * spi_bus_driver_init - init internal bus driver structures + * + * @bus: registered spi_bus_driver structure + * @dev: device that represents spi controller + * + * Once registered by spi_bus_register, the bus driver needs initialization, that + * includes starting thread, initializing internal structures.. The best place where + * the spi_bus_driver_init is in the `probe' function, when we already sure that passed + * device object is SPI master controller +**/ +int spi_bus_driver_init(struct spi_bus_driver *bus, struct device *dev) +{ + struct spi_bus_data *pd = + kmalloc(sizeof(struct spi_bus_data), GFP_KERNEL); + int err = 0; + + if (!pd) { + err = -ENOMEM; + goto init_failed_1; + } + atomic_set(&pd->exiting, 0); + pd->bus = bus; + init_MUTEX(&pd->lock); + INIT_LIST_HEAD(&pd->msgs); + init_waitqueue_head(&pd->queue); + pd->id = dev->bus_id; + pd->thread = kthread_run(spi_thread, pd, "%s-work", pd->id); + if (IS_ERR(pd->thread)) { + err = PTR_ERR(pd->thread); + goto init_failed_2; + } + dev->platform_data = pd; + return 0; + +init_failed_2: + kfree(pd); +init_failed_1: + return err; +} + +/** + * __spi_bus_free -- unregister all children of the spi bus + * + * @dev: the spi bus `device' object + * @context: not used, will be NULL + * + * This is internal function that is called when unregistering bus driver. Responsibility + * of this function is freeing the resources that were requested by spi_bus_driver_init + **/ +static int __spi_bus_free(struct device *dev, void *context) +{ + struct spi_bus_data *pd = dev->platform_data; + + if (pd) { + atomic_inc(&pd->exiting); + kthread_stop(pd->thread); + kfree(pd); + } + + dev_dbg(dev, "unregistering children\n"); + /* + * NOTE: the loop below might needs redesign. Currently + * we delete devices from the head of children list + * until the list is empty; that's because the function + * device_for_each_child will hold the semaphore needed + * for deletion of device + */ + while (!list_empty(&dev->children)) { + struct device *child = + list_entry(dev->children.next, struct device, node); + spi_device_del(TO_SPI_DEV(child)); + } + return 0; +} + +/** + * spi_bus_driver_unregister - unregister SPI bus controller from the system + * + * @bus_driver: driver registered by call to spi_bus_driver_register + * + * unregisters the SPI bus from the system. Before unregistering, it deletes + * each SPI device on the bus using call to __spi_device_free +**/ +void spi_bus_driver_unregister(struct spi_bus_driver *bus_driver) +{ + if (bus_driver) { + driver_for_each_dev(&bus_driver->driver, NULL, __spi_bus_free); + driver_unregister(&bus_driver->driver); + } +} + +/** + * spi_device_release - release the spi device structure + * + * @dev: spi_device to be released + * + * Pointer to this function will be put to dev->release place + * This function gets called as a part of device removing +**/ +void spi_device_release(struct device *dev) +{ + struct spi_device* sdev = TO_SPI_DEV(dev); + + kfree( sdev ); +} + +/** + * spi_device_add - add the new (discovered) SPI device to the bus. Mostly used by bus drivers + * + * @parent: the bus device object + * @name: name of device (non-null!) + * @bus_data: bus data to be assigned to device + * + * SPI devices usually cannot be discovered by SPI bus driver, so it needs to take the configuration + * somewhere from hardcoded structures, and prepare bus_data for its devices +**/ +struct spi_device* spi_device_add(struct device *parent, char *name, void *bus_data) +{ + struct spi_device* dev; + + if (!name) + goto dev_add_out; + + dev = kmalloc(sizeof(struct spi_device), GFP_KERNEL); + if( !dev ) + goto dev_add_out; + + memset(&dev->dev, 0, sizeof(dev->dev)); + dev->dev.parent = parent; + dev->dev.bus = &spi_bus; + strncpy(dev->name, name, sizeof(dev->name)); + strncpy(dev->dev.bus_id, name, sizeof(dev->dev.bus_id)); + dev->dev.release = spi_device_release; + dev->dev.platform_data = bus_data; + + if (device_register(&dev->dev)<0) { + dev_dbg(parent, "device '%s' cannot be added\n", name); + goto dev_add_out_2; + } + return dev; + +dev_add_out_2: + kfree(dev); +dev_add_out: + return NULL; +} + +/** + * spi_queue - queue the message to be processed asynchronously + * + * @msg: message to be sent + * + * This function queues the message to spi bus driver's queue. The bus driver + * retrieves the message from queue according to its own rules (see retrieve method) + * and sends the message to target device. If message has no callback method, originator + * of message would get no chance to know where the message is processed. The better + * solution is using spi_transfer function, which will return error code if no callback + * is provided, or transfer the message synchronously. +**/ +int spi_queue(struct spi_msg *msg) +{ + struct device *dev = &msg->device->dev; + struct spi_bus_data *pd = dev->parent->platform_data; + + down(&pd->lock); + list_add_tail(&msg->link, &pd->msgs); + dev_dbg(dev->parent, "message has been queued\n"); + up(&pd->lock); + wake_up_interruptible(&pd->queue); + return 0; +} + +/** + * __spi_transfer_callback - callback to process synchronous messages + * + * @msg: message that is about to complete + * @code: message status + * + * callback for synchronously processed message. If spi_transfer determines + * that there is no callback provided neither by msg->status nor callback + * parameter, the __spi_transfer_callback will be used, and spi_transfer + * does not return until transfer is finished + * +**/ +static void __spi_transfer_callback(struct spi_msg *msg, int code) +{ + if (code & (SPIMSG_OK | SPIMSG_FAILED)) + complete((struct completion *)msg->context); +} + +/* + * spi_transfer - transfer the message either in sync or async way + * + * @msg: message to process + * @callback: user-supplied callback + * + * If both msg->status and callback are set, the error code of -EINVAL + * will be returned + */ +int spi_transfer(struct spi_msg *msg, void (*callback) (struct spi_msg *, int)) +{ + struct completion msg_done; + int err = -EINVAL; + + if (callback && !msg->status) { + msg->status = callback; + callback = NULL; + } + + if (!callback) { + if (!msg->status) { + init_completion(&msg_done); + msg->context = &msg_done; + msg->status = __spi_transfer_callback; + spi_queue(msg); + wait_for_completion(&msg_done); + err = 0; + } else { + err = spi_queue(msg); + } + } + + return err; +} + +/** + * spi_thread_awake - function that called to determine if thread needs to process any messages + * + * @bd: pointer to struct spi_bus_data + * + * Thread wakes up if there is signal to exit (bd->exiting is set) or there are any messages + * in bus' queue. + */ +static int spi_thread_awake(struct spi_bus_data *bd) +{ + int ret; + + if (atomic_read(&bd->exiting)) { + return 1; + } + down(&bd->lock); + ret = !list_empty(&bd->msgs); + up(&bd->lock); + return ret; +} + +static void spi_async_callback (void *_msg ) +{ + struct spi_msg *msg = _msg; + + msg->status ( msg, SPIMSG_OK ); +} + +/** + * spi_bus_fifo_retrieve - simple function to retrieve the first message from the queue + * + * @this: spi_bus_driver that needs to retrieve next message from queue + * @data: pointer to spi_bus_data structure associated with spi_bus_driver + * + * This is pretty simple `retrieve' function. It retrieves the first message from the queue, + * and does not care about target of the message. For simple cases, this function is the best + * and the fastest solution to provide as retrieve method of bus driver + **/ +static struct spi_msg *spi_bus_fifo_retrieve (struct spi_bus_driver *this, struct spi_bus_data *data) +{ + return list_entry(data->msgs.next, struct spi_msg, link) +} + +/** + * spi_bus_simple_retrieve -- retrieve message from the queue with taking into account previous target + * + * @this: spi_bus_driver that needs to retrieve next message from queue + * @data: pointer to spi_bus_data structure associated with spi_bus_driver + * + * this function is more complex than spi_bus_fifo_retrieve; it takes into account the already selected + * device on SPI bus, and tries to retrieve the message targeted to the same device. + * + **/ +static struct spi_msg *spi_bus_simple_retrieve( struct spi_bus_driver *this, struct spi_bus_data *data) +{ + int found = 0; + struct spi_msg *msg; + + list_for_each_entry(msg, &data->msgs, link) { + if (!data->selected_device || msg->device == data->selected_device) { + found = 1; + break; + } + } + if (!found) + /* + * all messages for current selected_device + * are processed. + * let's switch to another device + */ + msg = list_entry(data->msgs.next, struct spi_msg, link); + + return msg; +} + +/** + * spi_bus_next_msg - the wrapper for retrieve method for bus driver + * + * @this: spi_bus_driver that needs to retrieve next message from queue + * @data: pointer to spi_bus_data structure associated with spi_bus_driver + * + * If bus driver provides the `retrieve' method, it is called to retrieve the next message + * from queue. Otherwise, the spi_bus_fifo_retrieve is called + * + **/ +static struct spi_msg *spi_bus_next_msg( struct spi_bus_driver *this, struct spi_bus_data *data) +{ + if (!this) + return NULL; + if (this->retrieve) + return this->retrieve (this, data); + return spi_bus_fifo_retrieve( this, data ); +} + +/** + * spi_thread - the thread that calls bus functions to perform actual transfers + * + * @pd: pointer to struct spi_bus_data with bus-specific data + * + * This function is started as separate thread to perform actual + * transfers on SPI bus + **/ +static int spi_thread(void *context) +{ + struct spi_bus_data *bd = context; + struct spi_msg *msg; + int xfer_status; + struct workqueue_struct *wq; + + wq = create_workqueue ( bd->id ); + if (!wq) + pr_debug( "%s: cannot create workqueue, async callbacks will be unavailable\n", bd->id ); + + while (!kthread_should_stop()) { + + wait_event_interruptible(bd->queue, spi_thread_awake(bd)); + + if (atomic_read(&bd->exiting)) + goto thr_exit; + + down(&bd->lock); + while (!list_empty(&bd->msgs)) { + /* + * this part is locked by bus_data->lock, + * to protect spi_msg extraction + */ + msg = spi_bus_next_msg( bd->bus, bd ); + + /* verify if device needs re-selecting */ + if (bd->selected_device != msg->device) { + if (bd->selected_device && bd->bus->deselect) + bd->bus->deselect (bd->selected_device); + bd->selected_device = msg->device; + if (bd->bus->select) + bd->bus->select (bd->selected_device); + } + list_del(&msg->link); + up(&bd->lock); + + /* + * and this part is locked by device's lock; + * spi_queue will be able to queue new + * messages + * + * note that bd->selected_device is locked, not msg->device + * they are the same, but msg can be freed in msg->status function + */ + spi_device_lock(&bd->selected_device); + if (bd->bus->set_clock && msg->clock) + bd->bus->set_clock(msg->device->dev.parent, + msg->clock); + xfer_status = bd->bus->xfer(msg); + if (msg->status) { + if (msg->flags & SPI_M_ASYNC_CB ) { + INIT_WORK( &msg->wq_item, spi_async_callback, msg ); + queue_work (wq, &msg->wq_item ); + } else { + msg->status(msg, + xfer_status == 0 ? SPIMSG_OK : + SPIMSG_FAILED); + } + } + + spi_device_unlock(&bd_selected->device); + + /* lock the bus_data again... */ + down(&bd->lock); + } + if (bd->bus->deselect) + bd->bus->deselect(bd->selected_device); + bd->selected_device = NULL; + /* device has been just deselected, unlocking the bus */ + up(&bd->lock); + } + +thr_exit: + if (wq) + destroy_workqueue (wq); + return 0; +} + +/** + * spi_write - send data to a device on an SPI bus + * + * @dev: the target device + * @buf: buffer to be sent + * @len: buffer's length + * + * Returns the number of bytes transferred, or negative error code. +**/ +int spi_write(struct spi_device *dev, const char *buf, int len) +{ + struct spi_msg *msg = spimsg_alloc(dev, SPI_M_WR, len, NULL); + int ret; + + memcpy(spimsg_buffer_wr(msg), buf, len); + ret = spi_transfer(msg, NULL); + return ret == 1 ? len : ret; +} + +/** + * spi_read - receive data from a device on an SPI bus + * + * @dev: the target device + * @buf: buffer to be sent + * @len: buffer's length + * + * Returns the number of bytes transferred, or negative error code. +**/ +int spi_read(struct spi_device *dev, char *buf, int len) +{ + int ret; + struct spimsg *msg = spimsg_alloc(dev, SPI_M_RD, len, NULL); + + ret = spi_transfer(msg, NULL); + memcpy(buf, spimsg_buffer_rd(msg), len); + return ret == 1 ? len : ret; +} + +/** + * spi_bus_populate/spi_bus_populate2 - populate the bus + * + * @parent: the SPI bus device object + * @devices: string that represents bus population + * @devices_s: array of structures that represents bus population + * @callback: optional pointer to function that called on each device's add + * + * These two functions intended to populate the SPI bus corresponding to + * device passed as 1st parameter. The difference is in the way to describe + * new SPI slave devices: the spi_bus_populate takes the ASCII string delimited + * by '\0', where each section matches one SPI device name _and_ its parameters, + * and the spi_bus_populate2 takes the array of structures spi_device_desc. + * + * If some device cannot be added because of spi_device_add fail, the function will + * not try to parse the rest of list + */ +int spi_bus_populate(struct device *parent, + char *devices, + void (*callback) (struct device * bus, + struct spi_device * new_dev)) +{ + struct spi_device *new_device; + int count = 0; + + while (devices[0]) { + dev_dbg(parent, " discovered new SPI device, name '%s'\n", + devices); + if ((new_device = spi_device_add(parent, devices, NULL)) == NULL) + break; + if (callback) + callback(parent, new_device); + devices += (strlen(devices) + 1); + count++; + } + return count; +} + +int spi_bus_populate2(struct device *parent, + struct spi_device_desc* devices_s, + void (*callback) (struct device* bus, + struct spi_device *new_dev, + void* params)) +{ + struct spi_device *new_device; + int count = 0; + + while (devices_s->name) { + dev_dbg(parent, " discovered new SPI device, name '%s'\n", + devices->name ); + if ((new_device = spi_device_add(parent, devices_s->name, devices_s->params)) == NULL) + break; + if (callback) + callback(parent, new_device, devices_s->params); + devices++; + count++; + } + return count; +} + +/** + * spi_bus_reset - reset the spi bus + * + * @bus: device object that represents the SPI bus + * @context: u32 value to be passed to reset method of bus + * + * This is simple wrapper for bus' `reset' method + * +**/ +void spi_bus_reset (struct device* bus, u32 context) +{ + if (bus && bus->driver && TO_SPI_BUS_DRIVER(bus->driver)->reset) + TO_SPI_BUS_DRIVER(bus->driver)->reset( bus, context ); +} + +int __init spi_core_init(void) +{ + return bus_register(&spi_bus); +} + +subsys_initcall(spi_core_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("dmitry pervushin <dpe...@ru...>"); + +EXPORT_SYMBOL_GPL(spi_bus_reset); +EXPORT_SYMBOL_GPL(spi_queue); +EXPORT_SYMBOL_GPL(spi_device_add); +EXPORT_SYMBOL_GPL(spi_bus_driver_unregister); +EXPORT_SYMBOL_GPL(spi_bus_populate); +EXPORT_SYMBOL_GPL(spi_bus_populate2); +EXPORT_SYMBOL_GPL(spi_transfer); +EXPORT_SYMBOL_GPL(spi_write); +EXPORT_SYMBOL_GPL(spi_read); +EXPORT_SYMBOL_GPL(spi_bus); +EXPORT_SYMBOL_GPL(spi_bus_driver_init); +EXPORT_SYMBOL_GPL(spi_bus_fifo_retrieve); +EXPORT_SYMBOL_GPL(spi_bus_simple_retrieve); + Index: linux/drivers/spi/spi-dev.c =================================================================== --- /dev/null +++ linux/drivers/spi/spi-dev.c @@ -0,0 +1,219 @@ +/* + spi-dev.c - spi driver, char device interface + + Copyright (C) 2005 MontaVista Software, Inc <so...@mv...> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include <linux/init.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/version.h> +#include <linux/smp_lock.h> + +#include <linux/init.h> +#include <asm/uaccess.h> +#include <linux/spi.h> + +#define SPI_TRANSFER_MAX 65535L + +struct spidev_driver_data { + int minor; +}; + +static ssize_t spidev_read(struct file *file, char *buf, size_t count, + loff_t * offset); +static ssize_t spidev_write(struct file *file, const char *buf, size_t count, + loff_t * offset); + +static int spidev_open(struct inode *inode, struct file *file); +static int spidev_release(struct inode *inode, struct file *file); +static int __init spidev_init(void); + +static void spidev_cleanup(void); + +static int spidev_probe(struct device *dev); +static int spidev_remove(struct device *dev); + +static struct file_operations spidev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = spidev_read, + .write = spidev_write, + .open = spidev_open, + .release = spidev_release, +}; + +static struct class_simple *spidev_class; + +static struct spi_driver spidev_driver = { + .driver = { + .name = SPI_DEV_CHAR, + .probe = spidev_probe, + .remove = spidev_remove, + }, +}; + +static int spidev_minor; + +static int spidev_probe(struct device *dev) +{ + struct spidev_driver_data *drvdata; + + drvdata = kmalloc(sizeof(struct spidev_driver_data), GFP_KERNEL); + if (!drvdata) { + dev_dbg(dev, "allocating drvdata failed\n"); + return -ENOMEM; + } + + drvdata->minor = spidev_minor++; + dev_dbg(dev, "setting device's(%p) minor to %d\n", dev, drvdata->minor); + dev_set_drvdata(dev, drvdata); + + class_simple_device_add(spidev_class, + MKDEV(SPI_MAJOR, drvdata->minor), + NULL, "spi%d", drvdata->minor); + dev_dbg(dev, " added\n"); + return 0; +} + +static int spidev_remove(struct device *dev) +{ + struct spidev_driver_data *drvdata; + + drvdata = (struct spidev_driver_data *)dev_get_drvdata(dev); + class_simple_device_remove(MKDEV(SPI_MAJOR, drvdata->minor)); + kfree(drvdata); + dev_dbg(dev, " removed\n"); + return 0; +} + +static ssize_t spidev_read(struct file *file, char *buf, size_t count, + loff_t * offset) +{ + struct spi_device *dev = (struct spi_device *)file->private_data; + if (count > SPI_TRANSFER_MAX) + count = SPI_TRANSFER_MAX; + return spi_read(dev, buf, count); +} + +static ssize_t spidev_write(struct file *file, const char *buf, size_t count, + loff_t * offset) +{ + struct spi_device *dev = (struct spi_device *)file->private_data; + if (count > SPI_TRANSFER_MAX) + count = SPI_TRANSFER_MAX; + return spi_write(dev, buf, count); +} + +struct spidev_openclose { + unsigned int minor; + struct file *file; +}; + +static int spidev_do_open(struct device *the_dev, void *context) +{ + struct spidev_openclose *o = (struct spidev_openclose *)context; + struct spi_device *dev = TO_SPI_DEV(the_dev); + struct spidev_driver_data *drvdata; + + drvdata = (struct spidev_driver_data *)dev_get_drvdata(the_dev); + if (!drvdata) { + pr_debug("%s: oops, drvdata is NULL !\n", __FUNCTION__); + goto do_open_fail; + } + + pr_debug("drvdata->minor = %d vs %d\n", drvdata->minor, o->minor); + if (drvdata->minor == o->minor) { + get_device(&dev->dev); + o->file->private_data = dev; + return 1; + } + +do_open_fail: + return 0; +} + +int spidev_open(struct inode *inode, struct file *file) +{ + struct spidev_openclose o; + int status; + + o.minor = iminor(inode); + o.file = file; + status = driver_for_each_dev(&spidev_driver.driver, &o, spidev_do_open); + if (status == 0) { + status = -ENODEV; + } + return status < 0 ? status : 0; +} + +static int spidev_release(struct inode *inode, struct file *file) +{ + struct spi_device *dev = file->private_data; + + if (dev) + put_device(&dev->dev); + file->private_data = NULL; + + return 0; +} + +static int __init spidev_init(void) +{ + int res; + + if ((res = register_chrdev(SPI_MAJOR, "spi", &spidev_fops)) != 0) { + goto out; + } + + spidev_class = class_simple_create(THIS_MODULE, "spi"); + if (IS_ERR(spidev_class)) { + printk(KERN_ERR "%s: error creating class\n", __FUNCTION__); + res = -EINVAL; + goto out_unreg; + } + + if ((res = spi_driver_add(&spidev_driver)) != 0) + goto out_unreg; + + printk("SPI /dev entries driver.\n"); + return 0; + +out_unreg: + unregister_chrdev(SPI_MAJOR, "spi"); +out: + printk(KERN_ERR "%s: Driver initialization failed\n", __FILE__); + return res; +} + +static void spidev_cleanup(void) +{ + spi_driver_del(&spidev_driver); + class_simple_destroy(spidev_class); + unregister_chrdev(SPI_MAJOR, "spi"); +} + +MODULE_AUTHOR("dmitry pervushin <dpe...@ru...>"); +MODULE_DESCRIPTION("SPI /dev entries driver"); +MODULE_LICENSE("GPL"); + +module_init(spidev_init); +module_exit(spidev_cleanup); Index: linux/Documentation/spi.txt =================================================================== --- /dev/null +++ linux/Documentation/spi.txt @@ -0,0 +1,382 @@ +Documentation/spi.txt +======================================================== +Table of contents +1. Introduction -- what is SPI ? +2. Purposes of this code +3. SPI devices stack +3.1 SPI outline +3.2 How the SPI devices gets discovered and probed ? +3.3 DMA and SPI messages +4. SPI functions and structures reference +5. How to contact authors +======================================================== + +1. What is SPI ? +---------------- +SPI stands for "Serial Peripheral Interface", a full-duplex synchronous +serial interface for connecting low-/medium-bandwidth external devices +using four wires. SPI devices communicate using a master/slave relation- +ship over two data lines and two control lines: +- Master Out Slave In (MOSI): supplies the output data from the master + to the inputs of the slaves; +- Master In Slave Out (MISO): supplies the output data from a slave to + the input of the master. It is important to note that there can be no + more than one slave that is transmitting data during any particular + transfer; +- Serial Clock (SCLK): a control line driven by the master, regulating + the flow of data bits; +- Slave Select (SS): a control line that allows slaves to be turned on + and off with hardware control. +More information is also available at http://en.wikipedia.org/wiki/Serial_Peripheral_Interface + +2. Purposes of this code +------------------------ +The supplied patch is starting point for implementing drivers for various +SPI busses as well as devices connected to these busses. Currently, the +SPI core supports only for MASTER mode for system running Linux. + +3. SPI devices stack +-------------------- + +3.1 The SPI outline + +The SPI infrastructure deals with several levels of abstraction. They are +"SPI bus", "SPI bus driver", "SPI slave device" and "SPI device driver". The +"SPI bus" is hardware device, which usually called "SPI adapter", and has +"SPI slave devices" connected. From the Linux' point of view, the "SPI bus" is +structure of type platform_device, and "SPI slave device" is structure of type +spi_device. The "SPI bus driver" is the driver which controls the whole +SPI bus (and, particularly, creates and destroys "SPI slave devices" on the bus), +and "SPI device driver" is driver that controls the only device on the SPI +bus, controlled by "SPI bus driver". "SPI device driver" can indirectly +call "SPI bus driver" to send/receive messages using API provided by SPI +core, and provide its own interface to the kernel and/or userland. +So, the device stack looks as follows: + + +--------------+ +---------+ + | some_bus | | spi_bus | + +--------------+ +---------+ + |..| | + |..|--------+ +---------------+ + +------------+| is parent to | SPI devices | + | SPI busses |+-------------> | | + +------------+ +---------------+ + | | + +----------------+ +----------------------+ + | SPI bus driver | | SPI device driver | + +----------------+ +----------------------+ + +3.2 How do the SPI devices gets discovered and probed ? + +In general, the SPI bus driver cannot effective discover devices +on its bus. Fortunately, the devices on SPI bus usually implemented +onboard, so the following method has been chosen: the SPI bus driver +calls the function named spi_bus_populate and passed the `topology +string' to it. The function will parse the string and call the callback +for each device, just before registering it. This allows bus driver +to determine parameters like CS# for each device, retrieve them from +string and store somewhere like spi_device->platform_data. An example: + err = spi_bus_populate( the_spi_bus, + "Dev1 0 1 2\0" "Dev2 2 1 0\0", + extract_name ) +In this example, function like extract_name would put the '\0' on the +1st space of device's name, so names will become just "Dev1", "Dev2", +and the rest of string will become parameters of device. + +The another way is to create array of structures spi_device_desc and pass +this array to function spi_bus_populate2, like this: + struct spi_device_desc spi_slaves[] = { + [0] = { + .name = "device1", + .param = device1_params, + }, + [1] = { + .name = "device2", + .param = NULL, + } + [2] = { + NULL, NULL + }; + spi_bus_populate2( the_spi_bus, spi_slaves, callback ); + +3.3. DMA and SPI messages +------------------------- + +To handle DMA transfers on SPI bus, any device driver might provide special +callbacks to allocate/free/get access to buffer. These callbacks are defined +in subsection iii of section 4. +To send data using DMA, the buffers should be allocated using +dma_alloc_coherent function. Usually buffers are allocated statically or +using kmalloc function. +To allow drivers to allocate buffers in non-standard +When one allocates the structure for spi message, it needs to provide target +device. If its driver wants to allocate buffer in driver-specific way, it may +provide its own allocation/free methods: alloc and free. If driver does not +provide these methods, kmalloc and kfree will be used. +After allocation, the buffer must be accessed to copy the buffer to be send +or retrieve buffer that has been just received from device. If buffer was +allocated using driver's alloc method, it(buffer) will be accessed using +get_buffer. Driver should provide accessible buffer that corresponds buffer +allocated by driver's alloc method. If there is no get_buffer method, +the result of alloc will be used. +After reading/writing from/to buffer, it will be released by call to driver's +release_buffer method. + + +4. SPI functions are structures reference +----------------------------------------- +This section describes structures and functions that listed +in include/linux/spi.h + +i. struct spi_msg +~~~~~~~~~~~~~~~~~ + +struct spi_msg { + unsigned char flags; + unsigned short len; + unsigned long clock; + struct spi_device* device; + void *context; + struct list_head link; + void (*status)( struct spi_msg* msg, int code ); + void *devbuf_rd, *devbuf_wr; + u8 *databuf_rd, *databuf_wr; +}; +This structure represents the message that SPI device driver sends to the +SPI bus driver to handle. +Fields: + flags combination of message flags + SPI_M_RD "read" operation (from device to host) + SPI_M_WR "write" operation (from host to device) + SPI_M_CS assert the CS signal before sending the message + SPI_M_CSREL clear the CS signal after sending the message + SPI_M_CSPOL set clock polarity to high + SPI_M_CPHA set clock phase to high + len length, in bytes, of allocated buffer + clock reserved, set to zero + device the target device of the message + context user-defined field; to associate any user data with the message + link used by bus driver to queue messages + status user-provided callback function to inform about message flow + devbuf_rd, devbuf_wr + so-called "device buffers". These buffers allocated by the + device driver, if device driver provides approproate callback. + Otherwise, the kmalloc API will be used. + databuf_rd, databuf_wr + pointers to access content of device buffers. They are acquired + using get_buffer callback, if device driver provides one. + Otherwise, they are just pointers to corresponding + device buffers + +struct spi_msg* spimsg_alloc( struct spi_device* device, + unsigned flags, + unsigned short len, + void (*status)( struct spi_msg*, int code ) ) +This functions is called to allocate the spi_msg structure and set the +corresponding fields in structure. If device->platform_data provides callbacks +to handle buffers, alloc/get_buffer are to be used. Returns NULL on errors. + +struct void spimsg_free( struct spi_msg* msg ) +Deallocate spi_msg as well as internal buffers. If msg->device->platform_data +provides callbacks to handle buffers, release_buffer and free are to be used. + +u8* spimsg_buffer_rd( struct spi_msg* msg ) +u8* spimsg_buffer_wr( struct spi_msg* msg ) +u8* spimsg_buffer( struct spi_msg* ) +Return the corresponding data buffer, which can be directly modified by driver. +spimsg_buffer checks flags and return either databuf_rd or databuf_wr basing on +value of `flags' in spi_msg structure. + +ii. struct spi_device +~~~~~~~~~~~~~~~~~~~~~ + +struct spi_device +{ + char name[ BUS_ID_SIZE ]; + struct device dev; +}; +This structure represents the physical device on SPI bus. The SPI bus driver +will create and register this structure for you. + name the name of the device. It should match to the SPI device + driver name + dev field used to be registered with core + +struct spi_device* spi_device_add( struct device* parent, + char* name ) +This function registers the device on the spi bus, and set its parent +to `parent', which represents the SPI bus. The device name will be set to name, +that should be non-empty, non-NULL string. Returns pointer to allocated +spi_device structure, NULL on error. + +void spi_device_del( struct spi_device* dev ) +Unregister the SPI device. Return value is ignored + +iii. struct spi_driver +~~~~~~~~~~~~~~~~~~~~~~ + +struct spi_driver { + void* (*alloc)( size_t, int ); + void (*free)( const void* ); + unsigned char* (*get_buffer)( struct spi_device*, void* ); + void (*release_buffer)( struct spi_device*, unsigned char*); + void (*control)( struct spi_device*, int mode, u32 ctl ); + struct device_driver driver; +}; +This structure represents the SPI device driver object. Before registering, +all fields of driver sub-structure should be properly filled, e.g., the +`bus_type' should be set to spi_bus. Otherwise, the driver will be incorrectly +registered and its callbacks might never been called. An example of will- +formed spi_driver structure: + extern struct bus_type spi_bus; + static struct spi_driver pnx4008_eeprom_driver = { + .driver = { + .bus = &spi_bus, + .name = "pnx4008-eeprom", + .probe = pnx4008_spiee_probe, + .remove = pnx4008_spiee_remove, + .suspend = pnx4008_spiee_suspend, + .resume = pnx4008_spiee_resume, + }, +}; +The method control gets called during the processing of SPI message. +For detailed description of malloc/free/get_buffer/release_buffer, please +look to section 3.3, "DMA and SPI messages" + + +int spi_driver_add( struct spi_driver* driver ) +Register the SPI device driver with core; returns 0 on no errors, error code +otherwise. + +void spi_driver_del( struct spi_driver* driver ) +Unregisters the SPI device driver; return value ignored. + +iv. struct spi_bus_driver +~~~~~~~~~~~~~~~~~~~~~~~~~ +To handle transactions over the SPI bus, the spi_bus_driver structure must +be prepared and registered with core. Any transactions, either synchronous +or asynchronous, go through spi_bus_driver->xfer function. + +struct spi_bus_driver +{ + int (*xfer)( struct spi_msg* msgs ); + void (*select) ( struct spi_device* arg ); + void (*deselect)( struct spi_device* arg ); + + struct spi_msg *(*retrieve)( struct spi_bus_driver *this, struct spi_bus_data *bd); + void (*reset)( struct spi_bus_driver *this, u32 context); + + struct device_driver driver; +}; + +Fields: + xfer pointer to function to execute actual transaction on SPI bus + msg message to handle + select pointer to function that gets called when bus needs to + select another device to be target of transfers + deselect + pointer to function that gets called before another device + is selected to be the target of transfers + reset + pointer to function that performs reset of SPI bus + retrieve + this function is used to retrieve next message from queue. If NULL, + spi_bus_fifo_retrieve is used + + +spi_bus_driver_register( struct spi_bus_driver* ) + +Register the SPI bus driver with the system. The driver sub-structure should +be properly filled before using this function, otherwise you may get unpredi- +ctable results when trying to exchange data. An example of correctly prepared +spi_bus_driver structure: + static struct spi_bus_driver spipnx_driver = { + .driver = { + .bus = &platform_bus_type, + .name = "spipnx", + .probe = spipnx_probe, + .remove = spipnx_remove, + .suspend = spipnx_suspend, + .resume = spipnx_resume, + }, + .xfer = spipnx_xfer, +}; +The driver and corresponding platform device are matched by name, so, in +order the example abive to work, the platform_device named "spipnx" should +be registered somewhere. + +void spi_bus_driver_unregister( struct spi_bus_driver* ) + +Unregister the SPI bus driver registered by call to spi_buys_driver_register +function; returns void. + +int spi_bus_populate( struct device* parent, + char* devices, + void (*callback)( struct device* parent, struct spi_device* new_one ) ) +This function usually called by SPI bus drivers in order to populate the SPI +bus (see also section 3.2, "How the SPI devices gets discovered and probed ?"). +After creating the spi_device, the spi_bus_populate calls the `callback' +function to allow to modify spi_device's fields before registering it with core. + parent pointer to SPI bus + devices string representing the current topology of SPI bus. It should + be formed like + "dev-1_and_its_info\0dev-2_and_its_info\0another_device\0\0" + the spi_bus_populate delimits this string by '\0' characters, + creates spi_device and after calling the callback registers the + spi_device + callback + pointer to function which could modify spi_device fields just + before registering them with core + +int spi_bus_populate2 (struct device *parent, struct spi_device_desc *devices, + void (*callback) (struct device* parent, struct spi_device* new_dev, + void *params )) +This is another way to populate the bus; but instead of string with device names and +parameters, the array of structures spi_device_desc is passed. Each item in array describes +the SPI slave device to create. + + +v. spi_transfer and spi_queue +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The driver that uses SPI core can initiate transfers either by calling +spi_transfer function (that will wait till the transfer is funished) or +queueing the message using spi_queue function (you need to provide function +that will be called during message is processed). In any way, you need to +prepare the spimsg structure and know the target device to your message to +be sent. + +int spi_transfer( struct spi_msg msgs, + void (*callback)( struct spi_msg* msg, int ) ) +If callback is zero, start synchronous transfer. Otherwise, queue +the message. + msg message to be handled + callback the callback function to be called during + message processing. If NULL, the function + will wait until end of processing. + +int spi_queue( struct spi_device* device, + struct spi_msg* msg ) +Queue the only message to the device. Returns status of queueing. To obtain +status of message processing, you have to provide `status' callback in message +and examine its parameters + msg message to be queued + +vi. the spi_bus variable +~~~~~~~~~~~~~~~~~~~~~~~~ +This variable is created during initialization of spi core, and has to be +specified as `bus' on any SPI device driver (look to section iii, "struct +spi_driver" ). If you do not specify spi_bus, your driver will be never +matched to spi_device and never be probed with hardware. Note that +spi_bus.match points to function that matches drivers and devices by name, +so SPI devices and their drivers should have the same name. + +5. How to contact authors +------------------------- +Do you have any comments ? Enhancements ? Device driver ? Feel free +to contact me: + dpe...@gm... + di...@pe... +Visit our project page: + http://spi-devel.sourceforge.net +Subscribe to mailing list: + spi...@li... + Index: linux/include/linux/spi.h =================================================================== --- /dev/null +++ linux/include/linux/spi.h @@ -0,0 +1,285 @@ +/* + * linux/include/linux/spi/spi.h + * + * Copyright (C) 2005 MontaVista Software, Inc <so...@mv...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * Derived from l3.h by Jamey Hicks + */ +#ifndef SPI_H +#define SPI_H + +#include <linux/types.h> +#include <linux/device.h> + +#define kzalloc(size,type) kcalloc(1,size,type) + +struct spi_device; +struct spi_driver; +struct spi_msg; +struct spi_bus_driver; + +extern struct bus_type spi_bus; + +struct spi_bus_data { + struct semaphore lock; + struct list_head msgs; + atomic_t exiting; + struct task_struct *thread; + wait_queue_head_t queue; + struct spi_device *selected_device; + struct spi_bus_driver *bus; + char *id; +}; + +#define TO_SPI_BUS_DRIVER(drv) container_of( drv, struct spi_bus_driver, driver ) +struct spi_bus_driver { + int (*xfer) (struct spi_msg * msg); + void (*select) (struct spi_device * dev); + void (*deselect) (struct spi_device * dev); + void (*set_clock) (struct device * bus_device, u32 clock_hz); + void (*reset) (struct device *bus_device, u32 context); + struct spi_msg * + (*retrieve) (struct spi_bus_driver *bus, struct spi_bus_data *data); + struct device_driver driver; +}; + +#define TO_SPI_DEV(device) container_of( device, struct spi_device, dev ) +struct spi_device { + char name[BUS_ID_SIZE]; + void *private; + struct device dev; +}; + +#define TO_SPI_DRIVER(drv) container_of( drv, struct spi_driver, driver ) +struct spi_driver { + void *(*alloc) (size_t, int); + void (*free) (const void *); + unsigned char *(*get_buffer) (struct spi_device *, void *); + void (*release_buffer) (struct spi_device *, unsigned char *); + void (*control) (struct spi_device *, int mode, u32 ctl); + struct device_driver driver; +}; + +#define SPI_DEV_DRV( device ) TO_SPI_DRIVER( (device)->dev.driver ) + +#define spi_device_lock( dev ) /* down( dev->dev.sem ) */ +#define spi_device_unlock( dev ) /* up( dev->dev.sem ) */ + +/* + * struct spi_msg + * + * This structure represent the SPI message internally. You should never use fields of this structure directly + * Please use corresponding functions to create/destroy/access fields + * + */ +struct spi_msg { + u32 flags; +#define SPI_M_RD 0x00000001 +#define SPI_M_WR 0x00000002 /**< Write mode flag */ +#define SPI_M_CSREL 0x00000004 /**< CS release level at end of the frame */ +#define SPI_M_CS 0x00000008 /**< CS active level at begining of frame ( default low ) */ +#define SPI_M_CPOL 0x00000010 /**< Clock polarity */ +#define SPI_M_CPHA 0x00000020 /**< Clock Phase */ +#define SPI_M_EXTBUF 0x80000000 /** externally allocated buffers */ +#define SPI_M_ASYNC_CB 0x40000000 /** use workqueue to deliver callbacks */ +#define SPI_M_DNA 0x20000000 /** do not allocate buffers */ + + unsigned short len; /* msg length */ + unsigned long clock; + struct spi_device *device; + void *context; + void *arg; + void *parent; /* in case of complex messages */ + struct list_head link; + + void (*status) (struct spi_msg * msg, int code); + + void *devbuf_rd, *devbuf_wr; + u8 *databuf_rd, *databuf_wr; + + struct work_struct wq_item; +}; + +static inline struct spi_msg *spimsg_alloc(struct spi_device *device, + unsigned flags, + unsigned short len, + void (*status) (struct spi_msg *, + int code)) +{ + struct spi_msg *msg; + struct spi_driver *drv = SPI_DEV_DRV(device); + int msgsize = sizeof (struct spi_msg); + + if (drv->alloc || (flags & (SPI_M_RD|SPI_M_WR)) == (SPI_M_RD | SPI_M_WR ) ) { + pr_debug ( "%s: external buffers\n", __FUNCTION__ ); + flags |= SPI_M_EXTBUF; + } else { + pr_debug ("%s: no ext buffers, msgsize increased from %d by %d to %d\n", __FUNCTION__, + msgsize, len, msgsize + len ); + msgsize += len; + } + + msg = kmalloc( msgsize, GFP_KERNEL); + if (!msg) + return NULL; + memset(msg, 0, sizeof(struct spi_msg)); + msg->len = len; + msg->status = status; + msg->device = device; + msg->flags = flags; + INIT_LIST_HEAD(&msg->link); + + if (flags & SPI_M_DNA ) + return msg; + + /* otherwise, we need to set up pointers */ + if (!(flags & SPI_M_EXTBUF)) { + msg->databuf_rd = msg->databuf_wr = + (u8*)msg + sizeof ( struct spi_msg); + } else { + if (flags & SPI_M_RD) { + msg->devbuf_rd = drv->alloc ? + drv->alloc(len, GFP_KERNEL) : kmalloc(len, GFP_KERNEL); + msg->databuf_rd = drv->get_buffer ? + drv->get_buffer(device, msg->devbuf_rd) : msg->devbuf_rd; + } + if (flags & SPI_M_WR) { + msg->devbuf_wr = drv->alloc ? + drv->alloc(len, GFP_KERNEL) : kmalloc(len, GFP_KERNEL); + msg->databuf_wr = drv->get_buffer ? + drv->get_buffer(device, msg->devbuf_wr) : msg->devbuf_wr; + } + } + pr_debug("%s: msg = %p, RD=(%p,%p) WR=(%p,%p). Actual flags = %s+%s\n", + __FUNCTION__, + msg, + msg->devbuf_rd, msg->databuf_rd, + msg->devbuf_wr, msg->databuf_wr, + msg->flags & SPI_M_RD ? "RD" : "~rd", + msg->flags & SPI_M_WR ? "WR" : "~wr"); + return msg; +} + +static inline void spimsg_free(struct spi_msg *msg) +{ + void (*do_free) (const void *) = kfree; + struct spi_driver *drv = SPI_DEV_DRV(msg->device); + + if (msg) { + + if ( !(msg->flags & SPI_M_DNA) || (msg->flags & SPI_M_EXTBUF) ) { + if (drv->free) + do_free = drv->free; + if (drv->release_buffer) { + if (msg->databuf_rd) + drv->release_buffer(msg->device, + msg->databuf_rd); + if (msg->databuf_wr) + drv->release_buffer(msg->device, + msg->databuf_wr); + } + if (msg->devbuf_rd) + do_free(msg->devbuf_rd); + if (msg->devbuf_wr) + do_free(msg->devbuf_wr); + } + kfree(msg); + } +} + +static inline u8 *spimsg_buffer_rd(struct spi_msg *msg) +{ + return msg ? msg->databuf_rd : NULL; +} + +static inline u8 *spimsg_buffer_wr(struct spi_msg *msg) +{ + return msg ? msg->databuf_wr : NULL; +} + +static inline u8 *spimsg_buffer(struct spi_msg *msg) +{ + if (!msg) + return NULL; + if ((msg->flags & (SPI_M_RD | SPI_M_WR)) == (SPI_M_RD | SPI_M_WR)) { + printk(KERN_ERR "%s: what buffer do you really want ?\n", + __FUNCTION__); + return NULL; + } + if (msg->flags & SPI_M_RD) + return msg->databuf_rd; + if (msg->flags & SPI_M_WR) + return msg->databuf_wr; +} + +static inline void spimsg_set_rd( struct spi_msg* msg, void* buf ) +{ + msg->databuf_rd = buf; +} + +static inline void spimsg_set_wr (struct spi_msg *msg, void *buf ) +{ + msg->databuf_wr = buf; +} + + +#define SPIMSG_OK 0x01 +#define SPIMSG_FAILED 0x80 +#define SPIMSG_STARTED 0x02 +#define SPIMSG_DONE 0x04 + +#define SPI_MAJOR 153 + +struct spi_driver; +struct spi_device; + +static inline int spi_bus_driver_register (struct spi_bus_driver *bus_driver ) +{ + return driver_register (&bus_driver->driver); +} + +void spi_bus_driver_unregister(struct spi_bus_driver *); +int spi_bus_driver_init(struct spi_bus_driver *driver, struct device *dev); +struct spi_device* spi_device_add(struct device *parent, char *name, void *private); + +static inline void spi_device_del(struct spi_device *dev) +{ + device_unregister(&dev->dev); +} +static inline int spi_driver_add(struct spi_driver *drv) +{ + drv->driver.bus = &spi_bus; + return driver_register(&drv->driver); +} +static inline void spi_driver_del(struct spi_driver *drv) +{ + driver_unregister(&drv->driver); +} + +#define SPI_DEV_CHAR "spi-char" + +extern void spi_bus_reset(struct device* bus, u32 context); +extern int spi_write(struct spi_device *dev, const char *buf, int len); +extern int spi_read(struct spi_device *dev, char *buf, int len); + +extern int spi_queue(struct spi_msg *message); +extern int spi_transfer(struct spi_msg *message, + void (*status) (struct spi_msg *, int)); +extern int spi_bus_populate(struct device *parent, char *device, + void (*assign) (struct device *parent, + struct spi_device *)); +struct spi_device_desc { + char* name; + void* params; +}; +extern int spi_bus_populate2(struct device *parent, + struct spi_device_desc *devices, + void (*assign) (struct device *parent, + struct spi_device *, + void *)); + +#endif /* SPI_H */ |
From: dmitry p. <dpe...@gm...> - 2005-11-11 09:38:01
|
This is an updated version of SPI framework from me, Dmitry Pervushin. It seems that now it is good time to consolidate our SPI frameworks to push it to kernel :) We've tested our SPI core as well with bus drivers with wireless LAN driver and achieved good performance with relatively small overhead. This proves the viability of this framework in real life even in real-time environment. The size of .text is still about 2,500 bytes, that is comparable with David Brownell's framework size. I think now is the time to start the final convergence process for these two cores and get the final core into the mainline kernel. And in order to understand where we need to converge, I created the main differences list (see below). The list of main differences between David Brownell's SPI framework (A) and my one (B): - (A) uses more complicated structure of SPI message, that contains one or more atomic transfers, and (B) offers the only spi_msg that represents the atomic transfer on SPI bus. The similar approach can be imple- mented in (B), and actually is implemented. But my imp[ression is that such enhancement may be added later.. - (A) uses workqueues to queue and handle SPI messages, and (B) allocates the kernel thread to the same purpose. Using workqueues is not very good solution in real-time environment; I think that allocating and starting the separate thread will give us more predictable and stable results; - (A) has some assumptions on buffers that are passed down to spi functions. If some controller driver (or bus driver in terms of (B)) tries to perform DMA transfers, it must copy the passed buffers to some memory allocated using GFP_DMA flag and map it using dma_map_single. From the other hand, (B) relies on callbacks provided by SPI device driver to allocate memory for DMA transfers, but keeps ability to pass user-allocated buffers down to SPI functions by specifying flags in SPI message. SPI message being a fundamental essense looks better to me when it's as simple as possible. Especially when we don't lose any flexibility which is exacly our case (buffers that are allocated as well as message itself/provided by user, DMA-capable buffers..) - (A) retrieves SPI message from the queue in sequential order (FIFO), but (B) provides more flexible way by providing special callback to retrieve next message from queue. This callback may implement its own discipline of scheduling SPI messages. In any way, the default is FIFO. - (A) uses standartized way to provide CS information, and (B) relies on functional drivers callbacks, which looks more flexible to me. SIgned-off-by: dmitry pervushin <dpe...@gm...> linux/include/linux/spi.h | 285 +++++++++++++++ linux/Documentation/spi.txt | 382 ++++++++++++++++++++ linux/arch/arm/Kconfig | 2 linux/drivers/Kconfig | 2 linux/drivers/Makefile | 1 linux/drivers/spi/Kconfig | 33 + linux/drivers/spi/Makefile | 14 linux/drivers/spi/spi-core.c | 645 +++++++++++++++++++++++++++++++++++ linux/drivers/spi/spi-dev.c | 219 +++++++++++ 9 files changed, 1583 insertions(+) Index: linux/arch/arm/Kconfig =================================================================== --- linux.orig/arch/arm/Kconfig +++ linux/arch/arm/Kconfig @@ -834,6 +834,8 @@ source "drivers/ssi/Kconfig" source "drivers/mmc/Kconfig" +source "drivers/spi/Kconfig" + endmenu source "ktools/Kconfig" Index: linux/drivers/Kconfig =================================================================== --- linux.orig/drivers/Kconfig +++ linux/drivers/Kconfig @@ -42,6 +42,8 @@ source "drivers/char/Kconfig" source "drivers/i2c/Kconfig" +source "drivers/spi/Kconfig" + source "drivers/w1/Kconfig" source "drivers/misc/Kconfig" Index: linux/drivers/Makefile =================================================================== --- linux.orig/drivers/Makefile +++ linux/drivers/Makefile @@ -67,3 +67,4 @@ obj-$(CONFIG_DPM) += dpm/ obj-$(CONFIG_MMC) += mmc/ obj-y += firmware/ obj-$(CONFIG_EVENT_BROKER) += evb/ +obj-$(CONFIG_SPI) += spi/ Index: linux/drivers/spi/Kconfig =================================================================== --- /dev/null +++ linux/drivers/spi/Kconfig @@ -0,0 +1,33 @@ +# +# SPI device configuration +# +menu "SPI support" + +config SPI + default Y + tristate "SPI (Serial Peripheral Interface) bus support" + default false + help + Say Y if you need to enable SPI support on your kernel. + Say M if you want to create the spi-core loadable module. + +config SPI_DEBUG + bool "SPI debug output" + depends on SPI + default false + help + Say Y there if you'd like to see debug output from SPI drivers + If unsure, say N + +config SPI_CHARDEV + default Y + tristate "SPI device interface" + depends on SPI + help + Say Y here to use /dev/spiNN device files. They make it possible to have user-space + programs use the SPI bus. + This support is also available as a module. If so, the module + will be called spi-dev. + +endmenu + Index: linux/drivers/spi/Makefile =================================================================== --- /dev/null +++ linux/drivers/spi/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the kernel spi bus driver. +# + +obj-$(CONFIG_SPI) += spi-core.o +# bus drivers +# ...functional drivers +# ...and the common spi-dev driver +obj-$(CONFIG_SPI_CHARDEV) += spi-dev.o + +ifeq ($(CONFIG_SPI_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif + Index: linux/drivers/spi/spi-core.c =================================================================== --- /dev/null +++ linux/drivers/spi/spi-core.c @@ -0,0 +1,645 @@ +/* + * drivers/spi/spi-core.c + * + * Copyright (C) 2005 MontaVista Software, Inc <so...@mv...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/proc_fs.h> +#include <linux/kmod.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/kthread.h> +#include <linux/spi.h> +#include <asm/atomic.h> + +static int spi_thread(void *context); + +/** + * spi_bus_match_name - verify that driver matches device on spi bus + * + * @dev: device that hasn't yet being assigned to any driver + * @drv: driver for spi device + * + * Drivers and devices on SPI bus are matched by name, just like the + * platform devices, with exception of SPI_DEV_CHAR. Driver with this name + * will be matched against any device +**/ +static int spi_bus_match_name(struct device *dev, struct device_driver *drv) +{ + return !strcmp(drv->name, SPI_DEV_CHAR) || + !strcmp(TO_SPI_DEV(dev)->name, drv->name); +} + +/** + * spi_bus_suspend - suspend all devices on the spi bus + * + * @dev: spi device to be suspended + * @state: state of device to be set + * + * This function set device on SPI bus to state `state', just like platform_bus does +**/ +static int spi_bus_suspend(struct device * dev, u32 state) +{ + int ret = 0; + + if (dev->driver && dev->driver->suspend) { + ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE); + if (ret == 0) + ret = dev->driver->suspend(dev, state, SUSPEND_SAVE_STATE); + if (ret == 0) + ret = dev->driver->suspend(dev, state, SUSPEND_POWER_DOWN); + } + return ret; +} + +/** + * spi_bus_resume - resume functioning of all devices on spi bus + * + * @dev: device to resume + * + * This function resumes device on SPI bus, just like platform_bus does +**/ +static int spi_bus_resume(struct device * dev) +{ + int ret = 0; + + if (dev->driver && dev->driver->resume) { + ret = dev->driver->resume(dev, RESUME_POWER_ON); + if (ret == 0) + ret = dev->driver->resume(dev, RESUME_RESTORE_STATE); + if (ret == 0) + ret = dev->driver->resume(dev, RESUME_ENABLE); + } + return ret; +} + +/** + * spi_bus - the &bus_type structure for SPI devices and drivers + * + * @name: the name of subsystem, "spi" here + * @match: function that matches devices to their drivers + * @suspend: PM callback to suspend device + * @resume: PM callback to resume device +**/ +struct bus_type spi_bus = { + .name = "spi", + .match = spi_bus_match_name, + .suspend = spi_bus_suspend, + .resume = spi_bus_resume, +}; + +/** + * spi_bus_driver_init - init internal bus driver structures + * + * @bus: registered spi_bus_driver structure + * @dev: device that represents spi controller + * + * Once registered by spi_bus_register, the bus driver needs initialization, that + * includes starting thread, initializing internal structures.. The best place where + * the spi_bus_driver_init is in the `probe' function, when we already sure that passed + * device object is SPI master controller +**/ +int spi_bus_driver_init(struct spi_bus_driver *bus, struct device *dev) +{ + struct spi_bus_data *pd = + kmalloc(sizeof(struct spi_bus_data), GFP_KERNEL); + int err = 0; + + if (!pd) { + err = -ENOMEM; + goto init_failed_1; + } + atomic_set(&pd->exiting, 0); + pd->bus = bus; + init_MUTEX(&pd->lock); + INIT_LIST_HEAD(&pd->msgs); + init_waitqueue_head(&pd->queue); + pd->id = dev->bus_id; + pd->thread = kthread_run(spi_thread, pd, "%s-work", pd->id); + if (IS_ERR(pd->thread)) { + err = PTR_ERR(pd->thread); + goto init_failed_2; + } + dev->platform_data = pd; + return 0; + +init_failed_2: + kfree(pd); +init_failed_1: + return err; +} + +/** + * __spi_bus_free -- unregister all children of the spi bus + * + * @dev: the spi bus `device' object + * @context: not used, will be NULL + * + * This is internal function that is called when unregistering bus driver. Responsibility + * of this function is freeing the resources that were requested by spi_bus_driver_init + **/ +static int __spi_bus_free(struct device *dev, void *context) +{ + struct spi_bus_data *pd = dev->platform_data; + + if (pd) { + atomic_inc(&pd->exiting); + kthread_stop(pd->thread); + kfree(pd); + } + + dev_dbg(dev, "unregistering children\n"); + /* + * NOTE: the loop below might needs redesign. Currently + * we delete devices from the head of children list + * until the list is empty; that's because the function + * device_for_each_child will hold the semaphore needed + * for deletion of device + */ + while (!list_empty(&dev->children)) { + struct device *child = + list_entry(dev->children.next, struct device, node); + spi_device_del(TO_SPI_DEV(child)); + } + return 0; +} + +/** + * spi_bus_driver_unregister - unregister SPI bus controller from the system + * + * @bus_driver: driver registered by call to spi_bus_driver_register + * + * unregisters the SPI bus from the system. Before unregistering, it deletes + * each SPI device on the bus using call to __spi_device_free +**/ +void spi_bus_driver_unregister(struct spi_bus_driver *bus_driver) +{ + if (bus_driver) { + driver_for_each_dev(&bus_driver->driver, NULL, __spi_bus_free); + driver_unregister(&bus_driver->driver); + } +} + +/** + * spi_device_release - release the spi device structure + * + * @dev: spi_device to be released + * + * Pointer to this function will be put to dev->release place + * This function gets called as a part of device removing +**/ +void spi_device_release(struct device *dev) +{ + struct spi_device* sdev = TO_SPI_DEV(dev); + + kfree( sdev ); +} + +/** + * spi_device_add - add the new (discovered) SPI device to the bus. Mostly used by bus drivers + * + * @parent: the bus device object + * @name: name of device (non-null!) + * @bus_data: bus data to be assigned to device + * + * SPI devices usually cannot be discovered by SPI bus driver, so it needs to take the configuration + * somewhere from hardcoded structures, and prepare bus_data for its devices +**/ +struct spi_device* spi_device_add(struct device *parent, char *name, void *bus_data) +{ + struct spi_device* dev; + + if (!name) + goto dev_add_out; + + dev = kmalloc(sizeof(struct spi_device), GFP_KERNEL); + if( !dev ) + goto dev_add_out; + + memset(&dev->dev, 0, sizeof(dev->dev)); + dev->dev.parent = parent; + dev->dev.bus = &spi_bus; + strncpy(dev->name, name, sizeof(dev->name)); + strncpy(dev->dev.bus_id, name, sizeof(dev->dev.bus_id)); + dev->dev.release = spi_device_release; + dev->dev.platform_data = bus_data; + + if (device_register(&dev->dev)<0) { + dev_dbg(parent, "device '%s' cannot be added\n", name); + goto dev_add_out_2; + } + return dev; + +dev_add_out_2: + kfree(dev); +dev_add_out: + return NULL; +} + +/** + * spi_queue - queue the message to be processed asynchronously + * + * @msg: message to be sent + * + * This function queues the message to spi bus driver's queue. The bus driver + * retrieves the message from queue according to its own rules (see retrieve method) + * and sends the message to target device. If message has no callback method, originator + * of message would get no chance to know where the message is processed. The better + * solution is using spi_transfer function, which will return error code if no callback + * is provided, or transfer the message synchronously. +**/ +int spi_queue(struct spi_msg *msg) +{ + struct device *dev = &msg->device->dev; + struct spi_bus_data *pd = dev->parent->platform_data; + + down(&pd->lock); + list_add_tail(&msg->link, &pd->msgs); + dev_dbg(dev->parent, "message has been queued\n"); + up(&pd->lock); + wake_up_interruptible(&pd->queue); + return 0; +} + +/** + * __spi_transfer_callback - callback to process synchronous messages + * + * @msg: message that is about to complete + * @code: message status + * + * callback for synchronously processed message. If spi_transfer determines + * that there is no callback provided neither by msg->status nor callback + * parameter, the __spi_transfer_callback will be used, and spi_transfer + * does not return until transfer is finished + * +**/ +static void __spi_transfer_callback(struct spi_msg *msg, int code) +{ + if (code & (SPIMSG_OK | SPIMSG_FAILED)) + complete((struct completion *)msg->context); +} + +/* + * spi_transfer - transfer the message either in sync or async way + * + * @msg: message to process + * @callback: user-supplied callback + * + * If both msg->status and callback are set, the error code of -EINVAL + * will be returned + */ +int spi_transfer(struct spi_msg *msg, void (*callback) (struct spi_msg *, int)) +{ + struct completion msg_done; + int err = -EINVAL; + + if (callback && !msg->status) { + msg->status = callback; + callback = NULL; + } + + if (!callback) { + if (!msg->status) { + init_completion(&msg_done); + msg->context = &msg_done; + msg->status = __spi_transfer_callback; + spi_queue(msg); + wait_for_completion(&msg_done); + err = 0; + } else { + err = spi_queue(msg); + } + } + + return err; +} + +/** + * spi_thread_awake - function that called to determine if thread needs to process any messages + * + * @bd: pointer to struct spi_bus_data + * + * Thread wakes up if there is signal to exit (bd->exiting is set) or there are any messages + * in bus' queue. + */ +static int spi_thread_awake(struct spi_bus_data *bd) +{ + int ret; + + if (atomic_read(&bd->exiting)) { + return 1; + } + down(&bd->lock); + ret = !list_empty(&bd->msgs); + up(&bd->lock); + return ret; +} + +static void spi_async_callback (void *_msg ) +{ + struct spi_msg *msg = _msg; + + msg->status ( msg, SPIMSG_OK ); +} + +/** + * spi_bus_fifo_retrieve - simple function to retrieve the first message from the queue + * + * @this: spi_bus_driver that needs to retrieve next message from queue + * @data: pointer to spi_bus_data structure associated with spi_bus_driver + * + * This is pretty simple `retrieve' function. It retrieves the first message from the queue, + * and does not care about target of the message. For simple cases, this function is the best + * and the fastest solution to provide as retrieve method of bus driver + **/ +static struct spi_msg *spi_bus_fifo_retrieve (struct spi_bus_driver *this, struct spi_bus_data *data) +{ + return list_entry(data->msgs.next, struct spi_msg, link) +} + +/** + * spi_bus_simple_retrieve -- retrieve message from the queue with taking into account previous target + * + * @this: spi_bus_driver that needs to retrieve next message from queue + * @data: pointer to spi_bus_data structure associated with spi_bus_driver + * + * this function is more complex than spi_bus_fifo_retrieve; it takes into account the already selected + * device on SPI bus, and tries to retrieve the message targeted to the same device. + * + **/ +static struct spi_msg *spi_bus_simple_retrieve( struct spi_bus_driver *this, struct spi_bus_data *data) +{ + int found = 0; + struct spi_msg *msg; + + list_for_each_entry(msg, &data->msgs, link) { + if (!data->selected_device || msg->device == data->selected_device) { + found = 1; + break; + } + } + if (!found) + /* + * all messages for current selected_device + * are processed. + * let's switch to another device + */ + msg = list_entry(data->msgs.next, struct spi_msg, link); + + return msg; +} + +/** + * spi_bus_next_msg - the wrapper for retrieve method for bus driver + * + * @this: spi_bus_driver that needs to retrieve next message from queue + * @data: pointer to spi_bus_data structure associated with spi_bus_driver + * + * If bus driver provides the `retrieve' method, it is called to retrieve the next message + * from queue. Otherwise, the spi_bus_fifo_retrieve is called + * + **/ +static struct spi_msg *spi_bus_next_msg( struct spi_bus_driver *this, struct spi_bus_data *data) +{ + if (!this) + return NULL; + if (this->retrieve) + return this->retrieve (this, data); + return spi_bus_fifo_retrieve( this, data ); +} + +/** + * spi_thread - the thread that calls bus functions to perform actual transfers + * + * @pd: pointer to struct spi_bus_data with bus-specific data + * + * This function is started as separate thread to perform actual + * transfers on SPI bus + **/ +static int spi_thread(void *context) +{ + struct spi_bus_data *bd = context; + struct spi_msg *msg; + int xfer_status; + struct workqueue_struct *wq; + + wq = create_workqueue ( bd->id ); + if (!wq) + pr_debug( "%s: cannot create workqueue, async callbacks will be unavailable\n", bd->id ); + + while (!kthread_should_stop()) { + + wait_event_interruptible(bd->queue, spi_thread_awake(bd)); + + if (atomic_read(&bd->exiting)) + goto thr_exit; + + down(&bd->lock); + while (!list_empty(&bd->msgs)) { + /* + * this part is locked by bus_data->lock, + * to protect spi_msg extraction + */ + msg = spi_bus_next_msg( bd->bus, bd ); + + /* verify if device needs re-selecting */ + if (bd->selected_device != msg->device) { + if (bd->selected_device && bd->bus->deselect) + bd->bus->deselect (bd->selected_device); + bd->selected_device = msg->device; + if (bd->bus->select) + bd->bus->select (bd->selected_device); + } + list_del(&msg->link); + up(&bd->lock); + + /* + * and this part is locked by device's lock; + * spi_queue will be able to queue new + * messages + * + * note that bd->selected_device is locked, not msg->device + * they are the same, but msg can be freed in msg->status function + */ + spi_device_lock(&bd->selected_device); + if (bd->bus->set_clock && msg->clock) + bd->bus->set_clock(msg->device->dev.parent, + msg->clock); + xfer_status = bd->bus->xfer(msg); + if (msg->status) { + if (msg->flags & SPI_M_ASYNC_CB ) { + INIT_WORK( &msg->wq_item, spi_async_callback, msg ); + queue_work (wq, &msg->wq_item ); + } else { + msg->status(msg, + xfer_status == 0 ? SPIMSG_OK : + SPIMSG_FAILED); + } + } + + spi_device_unlock(&bd_selected->device); + + /* lock the bus_data again... */ + down(&bd->lock); + } + if (bd->bus->deselect) + bd->bus->deselect(bd->selected_device); + bd->selected_device = NULL; + /* device has been just deselected, unlocking the bus */ + up(&bd->lock); + } + +thr_exit: + if (wq) + destroy_workqueue (wq); + return 0; +} + +/** + * spi_write - send data to a device on an SPI bus + * + * @dev: the target device + * @buf: buffer to be sent + * @len: buffer's length + * + * Returns the number of bytes transferred, or negative error code. +**/ +int spi_write(struct spi_device *dev, const char *buf, int len) +{ + struct spi_msg *msg = spimsg_alloc(dev, SPI_M_WR, len, NULL); + int ret; + + memcpy(spimsg_buffer_wr(msg), buf, len); + ret = spi_transfer(msg, NULL); + return ret == 1 ? len : ret; +} + +/** + * spi_read - receive data from a device on an SPI bus + * + * @dev: the target device + * @buf: buffer to be sent + * @len: buffer's length + * + * Returns the number of bytes transferred, or negative error code. +**/ +int spi_read(struct spi_device *dev, char *buf, int len) +{ + int ret; + struct spimsg *msg = spimsg_alloc(dev, SPI_M_RD, len, NULL); + + ret = spi_transfer(msg, NULL); + memcpy(buf, spimsg_buffer_rd(msg), len); + return ret == 1 ? len : ret; +} + +/** + * spi_bus_populate/spi_bus_populate2 - populate the bus + * + * @parent: the SPI bus device object + * @devices: string that represents bus population + * @devices_s: array of structures that represents bus population + * @callback: optional pointer to function that called on each device's add + * + * These two functions intended to populate the SPI bus corresponding to + * device passed as 1st parameter. The difference is in the way to describe + * new SPI slave devices: the spi_bus_populate takes the ASCII string delimited + * by '\0', where each section matches one SPI device name _and_ its parameters, + * and the spi_bus_populate2 takes the array of structures spi_device_desc. + * + * If some device cannot be added because of spi_device_add fail, the function will + * not try to parse the rest of list + */ +int spi_bus_populate(struct device *parent, + char *devices, + void (*callback) (struct device * bus, + struct spi_device * new_dev)) +{ + struct spi_device *new_device; + int count = 0; + + while (devices[0]) { + dev_dbg(parent, " discovered new SPI device, name '%s'\n", + devices); + if ((new_device = spi_device_add(parent, devices, NULL)) == NULL) + break; + if (callback) + callback(parent, new_device); + devices += (strlen(devices) + 1); + count++; + } + return count; +} + +int spi_bus_populate2(struct device *parent, + struct spi_device_desc* devices_s, + void (*callback) (struct device* bus, + struct spi_device *new_dev, + void* params)) +{ + struct spi_device *new_device; + int count = 0; + + while (devices_s->name) { + dev_dbg(parent, " discovered new SPI device, name '%s'\n", + devices->name ); + if ((new_device = spi_device_add(parent, devices_s->name, devices_s->params)) == NULL) + break; + if (callback) + callback(parent, new_device, devices_s->params); + devices++; + count++; + } + return count; +} + +/** + * spi_bus_reset - reset the spi bus + * + * @bus: device object that represents the SPI bus + * @context: u32 value to be passed to reset method of bus + * + * This is simple wrapper for bus' `reset' method + * +**/ +void spi_bus_reset (struct device* bus, u32 context) +{ + if (bus && bus->driver && TO_SPI_BUS_DRIVER(bus->driver)->reset) + TO_SPI_BUS_DRIVER(bus->driver)->reset( bus, context ); +} + +int __init spi_core_init(void) +{ + return bus_register(&spi_bus); +} + +subsys_initcall(spi_core_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("dmitry pervushin <dpe...@ru...>"); + +EXPORT_SYMBOL_GPL(spi_bus_reset); +EXPORT_SYMBOL_GPL(spi_queue); +EXPORT_SYMBOL_GPL(spi_device_add); +EXPORT_SYMBOL_GPL(spi_bus_driver_unregister); +EXPORT_SYMBOL_GPL(spi_bus_populate); +EXPORT_SYMBOL_GPL(spi_bus_populate2); +EXPORT_SYMBOL_GPL(spi_transfer); +EXPORT_SYMBOL_GPL(spi_write); +EXPORT_SYMBOL_GPL(spi_read); +EXPORT_SYMBOL_GPL(spi_bus); +EXPORT_SYMBOL_GPL(spi_bus_driver_init); +EXPORT_SYMBOL_GPL(spi_bus_fifo_retrieve); +EXPORT_SYMBOL_GPL(spi_bus_simple_retrieve); + Index: linux/drivers/spi/spi-dev.c =================================================================== --- /dev/null +++ linux/drivers/spi/spi-dev.c @@ -0,0 +1,219 @@ +/* + spi-dev.c - spi driver, char device interface + + Copyright (C) 2005 MontaVista Software, Inc <so...@mv...> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include <linux/init.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/version.h> +#include <linux/smp_lock.h> + +#include <linux/init.h> +#include <asm/uaccess.h> +#include <linux/spi.h> + +#define SPI_TRANSFER_MAX 65535L + +struct spidev_driver_data { + int minor; +}; + +static ssize_t spidev_read(struct file *file, char *buf, size_t count, + loff_t * offset); +static ssize_t spidev_write(struct file *file, const char *buf, size_t count, + loff_t * offset); + +static int spidev_open(struct inode *inode, struct file *file); +static int spidev_release(struct inode *inode, struct file *file); +static int __init spidev_init(void); + +static void spidev_cleanup(void); + +static int spidev_probe(struct device *dev); +static int spidev_remove(struct device *dev); + +static struct file_operations spidev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = spidev_read, + .write = spidev_write, + .open = spidev_open, + .release = spidev_release, +}; + +static struct class_simple *spidev_class; + +static struct spi_driver spidev_driver = { + .driver = { + .name = SPI_DEV_CHAR, + .probe = spidev_probe, + .remove = spidev_remove, + }, +}; + +static int spidev_minor; + +static int spidev_probe(struct device *dev) +{ + struct spidev_driver_data *drvdata; + + drvdata = kmalloc(sizeof(struct spidev_driver_data), GFP_KERNEL); + if (!drvdata) { + dev_dbg(dev, "allocating drvdata failed\n"); + return -ENOMEM; + } + + drvdata->minor = spidev_minor++; + dev_dbg(dev, "setting device's(%p) minor to %d\n", dev, drvdata->minor); + dev_set_drvdata(dev, drvdata); + + class_simple_device_add(spidev_class, + MKDEV(SPI_MAJOR, drvdata->minor), + NULL, "spi%d", drvdata->minor); + dev_dbg(dev, " added\n"); + return 0; +} + +static int spidev_remove(struct device *dev) +{ + struct spidev_driver_data *drvdata; + + drvdata = (struct spidev_driver_data *)dev_get_drvdata(dev); + class_simple_device_remove(MKDEV(SPI_MAJOR, drvdata->minor)); + kfree(drvdata); + dev_dbg(dev, " removed\n"); + return 0; +} + +static ssize_t spidev_read(struct file *file, char *buf, size_t count, + loff_t * offset) +{ + struct spi_device *dev = (struct spi_device *)file->private_data; + if (count > SPI_TRANSFER_MAX) + count = SPI_TRANSFER_MAX; + return spi_read(dev, buf, count); +} + +static ssize_t spidev_write(struct file *file, const char *buf, size_t count, + loff_t * offset) +{ + struct spi_device *dev = (struct spi_device *)file->private_data; + if (count > SPI_TRANSFER_MAX) + count = SPI_TRANSFER_MAX; + return spi_write(dev, buf, count); +} + +struct spidev_openclose { + unsigned int minor; + struct file *file; +}; + +static int spidev_do_open(struct device *the_dev, void *context) +{ + struct spidev_openclose *o = (struct spidev_openclose *)context; + struct spi_device *dev = TO_SPI_DEV(the_dev); + struct spidev_driver_data *drvdata; + + drvdata = (struct spidev_driver_data *)dev_get_drvdata(the_dev); + if (!drvdata) { + pr_debug("%s: oops, drvdata is NULL !\n", __FUNCTION__); + goto do_open_fail; + } + + pr_debug("drvdata->minor = %d vs %d\n", drvdata->minor, o->minor); + if (drvdata->minor == o->minor) { + get_device(&dev->dev); + o->file->private_data = dev; + return 1; + } + +do_open_fail: + return 0; +} + +int spidev_open(struct inode *inode, struct file *file) +{ + struct spidev_openclose o; + int status; + + o.minor = iminor(inode); + o.file = file; + status = driver_for_each_dev(&spidev_driver.driver, &o, spidev_do_open); + if (status == 0) { + status = -ENODEV; + } + return status < 0 ? status : 0; +} + +static int spidev_release(struct inode *inode, struct file *file) +{ + struct spi_device *dev = file->private_data; + + if (dev) + put_device(&dev->dev); + file->private_data = NULL; + + return 0; +} + +static int __init spidev_init(void) +{ + int res; + + if ((res = register_chrdev(SPI_MAJOR, "spi", &spidev_fops)) != 0) { + goto out; + } + + spidev_class = class_simple_create(THIS_MODULE, "spi"); + if (IS_ERR(spidev_class)) { + printk(KERN_ERR "%s: error creating class\n", __FUNCTION__); + res = -EINVAL; + goto out_unreg; + } + + if ((res = spi_driver_add(&spidev_driver)) != 0) + goto out_unreg; + + printk("SPI /dev entries driver.\n"); + return 0; + +out_unreg: + unregister_chrdev(SPI_MAJOR, "spi"); +out: + printk(KERN_ERR "%s: Driver initialization failed\n", __FILE__); + return res; +} + +static void spidev_cleanup(void) +{ + spi_driver_del(&spidev_driver); + class_simple_destroy(spidev_class); + unregister_chrdev(SPI_MAJOR, "spi"); +} + +MODULE_AUTHOR("dmitry pervushin <dpe...@ru...>"); +MODULE_DESCRIPTION("SPI /dev entries driver"); +MODULE_LICENSE("GPL"); + +module_init(spidev_init); +module_exit(spidev_cleanup); Index: linux/Documentation/spi.txt =================================================================== --- /dev/null +++ linux/Documentation/spi.txt @@ -0,0 +1,382 @@ +Documentation/spi.txt +======================================================== +Table of contents +1. Introduction -- what is SPI ? +2. Purposes of this code +3. SPI devices stack +3.1 SPI outline +3.2 How the SPI devices gets discovered and probed ? +3.3 DMA and SPI messages +4. SPI functions and structures reference +5. How to contact authors +======================================================== + +1. What is SPI ? +---------------- +SPI stands for "Serial Peripheral Interface", a full-duplex synchronous +serial interface for connecting low-/medium-bandwidth external devices +using four wires. SPI devices communicate using a master/slave relation- +ship over two data lines and two control lines: +- Master Out Slave In (MOSI): supplies the output data from the master + to the inputs of the slaves; +- Master In Slave Out (MISO): supplies the output data from a slave to + the input of the master. It is important to note that there can be no + more than one slave that is transmitting data during any particular + transfer; +- Serial Clock (SCLK): a control line driven by the master, regulating + the flow of data bits; +- Slave Select (SS): a control line that allows slaves to be turned on + and off with hardware control. +More information is also available at http://en.wikipedia.org/wiki/Serial_Peripheral_Interface + +2. Purposes of this code +------------------------ +The supplied patch is starting point for implementing drivers for various +SPI busses as well as devices connected to these busses. Currently, the +SPI core supports only for MASTER mode for system running Linux. + +3. SPI devices stack +-------------------- + +3.1 The SPI outline + +The SPI infrastructure deals with several levels of abstraction. They are +"SPI bus", "SPI bus driver", "SPI slave device" and "SPI device driver". The +"SPI bus" is hardware device, which usually called "SPI adapter", and has +"SPI slave devices" connected. From the Linux' point of view, the "SPI bus" is +structure of type platform_device, and "SPI slave device" is structure of type +spi_device. The "SPI bus driver" is the driver which controls the whole +SPI bus (and, particularly, creates and destroys "SPI slave devices" on the bus), +and "SPI device driver" is driver that controls the only device on the SPI +bus, controlled by "SPI bus driver". "SPI device driver" can indirectly +call "SPI bus driver" to send/receive messages using API provided by SPI +core, and provide its own interface to the kernel and/or userland. +So, the device stack looks as follows: + + +--------------+ +---------+ + | some_bus | | spi_bus | + +--------------+ +---------+ + |..| | + |..|--------+ +---------------+ + +------------+| is parent to | SPI devices | + | SPI busses |+-------------> | | + +------------+ +---------------+ + | | + +----------------+ +----------------------+ + | SPI bus driver | | SPI device driver | + +----------------+ +----------------------+ + +3.2 How do the SPI devices gets discovered and probed ? + +In general, the SPI bus driver cannot effective discover devices +on its bus. Fortunately, the devices on SPI bus usually implemented +onboard, so the following method has been chosen: the SPI bus driver +calls the function named spi_bus_populate and passed the `topology +string' to it. The function will parse the string and call the callback +for each device, just before registering it. This allows bus driver +to determine parameters like CS# for each device, retrieve them from +string and store somewhere like spi_device->platform_data. An example: + err = spi_bus_populate( the_spi_bus, + "Dev1 0 1 2\0" "Dev2 2 1 0\0", + extract_name ) +In this example, function like extract_name would put the '\0' on the +1st space of device's name, so names will become just "Dev1", "Dev2", +and the rest of string will become parameters of device. + +The another way is to create array of structures spi_device_desc and pass +this array to function spi_bus_populate2, like this: + struct spi_device_desc spi_slaves[] = { + [0] = { + .name = "device1", + .param = device1_params, + }, + [1] = { + .name = "device2", + .param = NULL, + } + [2] = { + NULL, NULL + }; + spi_bus_populate2( the_spi_bus, spi_slaves, callback ); + +3.3. DMA and SPI messages +------------------------- + +To handle DMA transfers on SPI bus, any device driver might provide special +callbacks to allocate/free/get access to buffer. These callbacks are defined +in subsection iii of section 4. +To send data using DMA, the buffers should be allocated using +dma_alloc_coherent function. Usually buffers are allocated statically or +using kmalloc function. +To allow drivers to allocate buffers in non-standard +When one allocates the structure for spi message, it needs to provide target +device. If its driver wants to allocate buffer in driver-specific way, it may +provide its own allocation/free methods: alloc and free. If driver does not +provide these methods, kmalloc and kfree will be used. +After allocation, the buffer must be accessed to copy the buffer to be send +or retrieve buffer that has been just received from device. If buffer was +allocated using driver's alloc method, it(buffer) will be accessed using +get_buffer. Driver should provide accessible buffer that corresponds buffer +allocated by driver's alloc method. If there is no get_buffer method, +the result of alloc will be used. +After reading/writing from/to buffer, it will be released by call to driver's +release_buffer method. + + +4. SPI functions are structures reference +----------------------------------------- +This section describes structures and functions that listed +in include/linux/spi.h + +i. struct spi_msg +~~~~~~~~~~~~~~~~~ + +struct spi_msg { + unsigned char flags; + unsigned short len; + unsigned long clock; + struct spi_device* device; + void *context; + struct list_head link; + void (*status)( struct spi_msg* msg, int code ); + void *devbuf_rd, *devbuf_wr; + u8 *databuf_rd, *databuf_wr; +}; +This structure represents the message that SPI device driver sends to the +SPI bus driver to handle. +Fields: + flags combination of message flags + SPI_M_RD "read" operation (from device to host) + SPI_M_WR "write" operation (from host to device) + SPI_M_CS assert the CS signal before sending the message + SPI_M_CSREL clear the CS signal after sending the message + SPI_M_CSPOL set clock polarity to high + SPI_M_CPHA set clock phase to high + len length, in bytes, of allocated buffer + clock reserved, set to zero + device the target device of the message + context user-defined field; to associate any user data with the message + link used by bus driver to queue messages + status user-provided callback function to inform about message flow + devbuf_rd, devbuf_wr + so-called "device buffers". These buffers allocated by the + device driver, if device driver provides approproate callback. + Otherwise, the kmalloc API will be used. + databuf_rd, databuf_wr + pointers to access content of device buffers. They are acquired + using get_buffer callback, if device driver provides one. + Otherwise, they are just pointers to corresponding + device buffers + +struct spi_msg* spimsg_alloc( struct spi_device* device, + unsigned flags, + unsigned short len, + void (*status)( struct spi_msg*, int code ) ) +This functions is called to allocate the spi_msg structure and set the +corresponding fields in structure. If device->platform_data provides callbacks +to handle buffers, alloc/get_buffer are to be used. Returns NULL on errors. + +struct void spimsg_free( struct spi_msg* msg ) +Deallocate spi_msg as well as internal buffers. If msg->device->platform_data +provides callbacks to handle buffers, release_buffer and free are to be used. + +u8* spimsg_buffer_rd( struct spi_msg* msg ) +u8* spimsg_buffer_wr( struct spi_msg* msg ) +u8* spimsg_buffer( struct spi_msg* ) +Return the corresponding data buffer, which can be directly modified by driver. +spimsg_buffer checks flags and return either databuf_rd or databuf_wr basing on +value of `flags' in spi_msg structure. + +ii. struct spi_device +~~~~~~~~~~~~~~~~~~~~~ + +struct spi_device +{ + char name[ BUS_ID_SIZE ]; + struct device dev; +}; +This structure represents the physical device on SPI bus. The SPI bus driver +will create and register this structure for you. + name the name of the device. It should match to the SPI device + driver name + dev field used to be registered with core + +struct spi_device* spi_device_add( struct device* parent, + char* name ) +This function registers the device on the spi bus, and set its parent +to `parent', which represents the SPI bus. The device name will be set to name, +that should be non-empty, non-NULL string. Returns pointer to allocated +spi_device structure, NULL on error. + +void spi_device_del( struct spi_device* dev ) +Unregister the SPI device. Return value is ignored + +iii. struct spi_driver +~~~~~~~~~~~~~~~~~~~~~~ + +struct spi_driver { + void* (*alloc)( size_t, int ); + void (*free)( const void* ); + unsigned char* (*get_buffer)( struct spi_device*, void* ); + void (*release_buffer)( struct spi_device*, unsigned char*); + void (*control)( struct spi_device*, int mode, u32 ctl ); + struct device_driver driver; +}; +This structure represents the SPI device driver object. Before registering, +all fields of driver sub-structure should be properly filled, e.g., the +`bus_type' should be set to spi_bus. Otherwise, the driver will be incorrectly +registered and its callbacks might never been called. An example of will- +formed spi_driver structure: + extern struct bus_type spi_bus; + static struct spi_driver pnx4008_eeprom_driver = { + .driver = { + .bus = &spi_bus, + .name = "pnx4008-eeprom", + .probe = pnx4008_spiee_probe, + .remove = pnx4008_spiee_remove, + .suspend = pnx4008_spiee_suspend, + .resume = pnx4008_spiee_resume, + }, +}; +The method control gets called during the processing of SPI message. +For detailed description of malloc/free/get_buffer/release_buffer, please +look to section 3.3, "DMA and SPI messages" + + +int spi_driver_add( struct spi_driver* driver ) +Register the SPI device driver with core; returns 0 on no errors, error code +otherwise. + +void spi_driver_del( struct spi_driver* driver ) +Unregisters the SPI device driver; return value ignored. + +iv. struct spi_bus_driver +~~~~~~~~~~~~~~~~~~~~~~~~~ +To handle transactions over the SPI bus, the spi_bus_driver structure must +be prepared and registered with core. Any transactions, either synchronous +or asynchronous, go through spi_bus_driver->xfer function. + +struct spi_bus_driver +{ + int (*xfer)( struct spi_msg* msgs ); + void (*select) ( struct spi_device* arg ); + void (*deselect)( struct spi_device* arg ); + + struct spi_msg *(*retrieve)( struct spi_bus_driver *this, struct spi_bus_data *bd); + void (*reset)( struct spi_bus_driver *this, u32 context); + + struct device_driver driver; +}; + +Fields: + xfer pointer to function to execute actual transaction on SPI bus + msg message to handle + select pointer to function that gets called when bus needs to + select another device to be target of transfers + deselect + pointer to function that gets called before another device + is selected to be the target of transfers + reset + pointer to function that performs reset of SPI bus + retrieve + this function is used to retrieve next message from queue. If NULL, + spi_bus_fifo_retrieve is used + + +spi_bus_driver_register( struct spi_bus_driver* ) + +Register the SPI bus driver with the system. The driver sub-structure should +be properly filled before using this function, otherwise you may get unpredi- +ctable results when trying to exchange data. An example of correctly prepared +spi_bus_driver structure: + static struct spi_bus_driver spipnx_driver = { + .driver = { + .bus = &platform_bus_type, + .name = "spipnx", + .probe = spipnx_probe, + .remove = spipnx_remove, + .suspend = spipnx_suspend, + .resume = spipnx_resume, + }, + .xfer = spipnx_xfer, +}; +The driver and corresponding platform device are matched by name, so, in +order the example abive to work, the platform_device named "spipnx" should +be registered somewhere. + +void spi_bus_driver_unregister( struct spi_bus_driver* ) + +Unregister the SPI bus driver registered by call to spi_buys_driver_register +function; returns void. + +int spi_bus_populate( struct device* parent, + char* devices, + void (*callback)( struct device* parent, struct spi_device* new_one ) ) +This function usually called by SPI bus drivers in order to populate the SPI +bus (see also section 3.2, "How the SPI devices gets discovered and probed ?"). +After creating the spi_device, the spi_bus_populate calls the `callback' +function to allow to modify spi_device's fields before registering it with core. + parent pointer to SPI bus + devices string representing the current topology of SPI bus. It should + be formed like + "dev-1_and_its_info\0dev-2_and_its_info\0another_device\0\0" + the spi_bus_populate delimits this string by '\0' characters, + creates spi_device and after calling the callback registers the + spi_device + callback + pointer to function which could modify spi_device fields just + before registering them with core + +int spi_bus_populate2 (struct device *parent, struct spi_device_desc *devices, + void (*callback) (struct device* parent, struct spi_device* new_dev, + void *params )) +This is another way to populate the bus; but instead of string with device names and +parameters, the array of structures spi_device_desc is passed. Each item in array describes +the SPI slave device to create. + + +v. spi_transfer and spi_queue +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The driver that uses SPI core can initiate transfers either by calling +spi_transfer function (that will wait till the transfer is funished) or +queueing the message using spi_queue function (you need to provide function +that will be called during message is processed). In any way, you need to +prepare the spimsg structure and know the target device to your message to +be sent. + +int spi_transfer( struct spi_msg msgs, + void (*callback)( struct spi_msg* msg, int ) ) +If callback is zero, start synchronous transfer. Otherwise, queue +the message. + msg message to be handled + callback the callback function to be called during + message processing. If NULL, the function + will wait until end of processing. + +int spi_queue( struct spi_device* device, + struct spi_msg* msg ) +Queue the only message to the device. Returns status of queueing. To obtain +status of message processing, you have to provide `status' callback in message +and examine its parameters + msg message to be queued + +vi. the spi_bus variable +~~~~~~~~~~~~~~~~~~~~~~~~ +This variable is created during initialization of spi core, and has to be +specified as `bus' on any SPI device driver (look to section iii, "struct +spi_driver" ). If you do not specify spi_bus, your driver will be never +matched to spi_device and never be probed with hardware. Note that +spi_bus.match points to function that matches drivers and devices by name, +so SPI devices and their drivers should have the same name. + +5. How to contact authors +------------------------- +Do you have any comments ? Enhancements ? Device driver ? Feel free +to contact me: + dpe...@gm... + di...@pe... +Visit our project page: + http://spi-devel.sourceforge.net +Subscribe to mailing list: + spi...@li... + Index: linux/include/linux/spi.h =================================================================== --- /dev/null +++ linux/include/linux/spi.h @@ -0,0 +1,285 @@ +/* + * linux/include/linux/spi/spi.h + * + * Copyright (C) 2005 MontaVista Software, Inc <so...@mv...> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * Derived from l3.h by Jamey Hicks + */ +#ifndef SPI_H +#define SPI_H + +#include <linux/types.h> +#include <linux/device.h> + +#define kzalloc(size,type) kcalloc(1,size,type) + +struct spi_device; +struct spi_driver; +struct spi_msg; +struct spi_bus_driver; + +extern struct bus_type spi_bus; + +struct spi_bus_data { + struct semaphore lock; + struct list_head msgs; + atomic_t exiting; + struct task_struct *thread; + wait_queue_head_t queue; + struct spi_device *selected_device; + struct spi_bus_driver *bus; + char *id; +}; + +#define TO_SPI_BUS_DRIVER(drv) container_of( drv, struct spi_bus_driver, driver ) +struct spi_bus_driver { + int (*xfer) (struct spi_msg * msg); + void (*select) (struct spi_device * dev); + void (*deselect) (struct spi_device * dev); + void (*set_clock) (struct device * bus_device, u32 clock_hz); + void (*reset) (struct device *bus_device, u32 context); + struct spi_msg * + (*retrieve) (struct spi_bus_driver *bus, struct spi_bus_data *data); + struct device_driver driver; +}; + +#define TO_SPI_DEV(device) container_of( device, struct spi_device, dev ) +struct spi_device { + char name[BUS_ID_SIZE]; + void *private; + struct device dev; +}; + +#define TO_SPI_DRIVER(drv) container_of( drv, struct spi_driver, driver ) +struct spi_driver { + void *(*alloc) (size_t, int); + void (*free) (const void *); + unsigned char *(*get_buffer) (struct spi_device *, void *); + void (*release_buffer) (struct spi_device *, unsigned char *); + void (*control) (struct spi_device *, int mode, u32 ctl); + struct device_driver driver; +}; + +#define SPI_DEV_DRV( device ) TO_SPI_DRIVER( (device)->dev.driver ) + +#define spi_device_lock( dev ) /* down( dev->dev.sem ) */ +#define spi_device_unlock( dev ) /* up( dev->dev.sem ) */ + +/* + * struct spi_msg + * + * This structure represent the SPI message internally. You should never use fields of this structure directly + * Please use corresponding functions to create/destroy/access fields + * + */ +struct spi_msg { + u32 flags; +#define SPI_M_RD 0x00000001 +#define SPI_M_WR 0x00000002 /**< Write mode flag */ +#define SPI_M_CSREL 0x00000004 /**< CS release level at end of the frame */ +#define SPI_M_CS 0x00000008 /**< CS active level at begining of frame ( default low ) */ +#define SPI_M_CPOL 0x00000010 /**< Clock polarity */ +#define SPI_M_CPHA 0x00000020 /**< Clock Phase */ +#define SPI_M_EXTBUF 0x80000000 /** externally allocated buffers */ +#define SPI_M_ASYNC_CB 0x40000000 /** use workqueue to deliver callbacks */ +#define SPI_M_DNA 0x20000000 /** do not allocate buffers */ + + unsigned short len; /* msg length */ + unsigned long clock; + struct spi_device *device; + void *context; + void *arg; + void *parent; /* in case of complex messages */ + struct list_head link; + + void (*status) (struct spi_msg * msg, int code); + + void *devbuf_rd, *devbuf_wr; + u8 *databuf_rd, *databuf_wr; + + struct work_struct wq_item; +}; + +static inline struct spi_msg *spimsg_alloc(struct spi_device *device, + unsigned flags, + unsigned short len, + void (*status) (struct spi_msg *, + int code)) +{ + struct spi_msg *msg; + struct spi_driver *drv = SPI_DEV_DRV(device); + int msgsize = sizeof (struct spi_msg); + + if (drv->alloc || (flags & (SPI_M_RD|SPI_M_WR)) == (SPI_M_RD | SPI_M_WR ) ) { + pr_debug ( "%s: external buffers\n", __FUNCTION__ ); + flags |= SPI_M_EXTBUF; + } else { + pr_debug ("%s: no ext buffers, msgsize increased from %d by %d to %d\n", __FUNCTION__, + msgsize, len, msgsize + len ); + msgsize += len; + } + + msg = kmalloc( msgsize, GFP_KERNEL); + if (!msg) + return NULL; + memset(msg, 0, sizeof(struct spi_msg)); + msg->len = len; + msg->status = status; + msg->device = device; + msg->flags = flags; + INIT_LIST_HEAD(&msg->link); + + if (flags & SPI_M_DNA ) + return msg; + + /* otherwise, we need to set up pointers */ + if (!(flags & SPI_M_EXTBUF)) { + msg->databuf_rd = msg->databuf_wr = + (u8*)msg + sizeof ( struct spi_msg); + } else { + if (flags & SPI_M_RD) { + msg->devbuf_rd = drv->alloc ? + drv->alloc(len, GFP_KERNEL) : kmalloc(len, GFP_KERNEL); + msg->databuf_rd = drv->get_buffer ? + drv->get_buffer(device, msg->devbuf_rd) : msg->devbuf_rd; + } + if (flags & SPI_M_WR) { + msg->devbuf_wr = drv->alloc ? + drv->alloc(len, GFP_KERNEL) : kmalloc(len, GFP_KERNEL); + msg->databuf_wr = drv->get_buffer ? + drv->get_buffer(device, msg->devbuf_wr) : msg->devbuf_wr; + } + } + pr_debug("%s: msg = %p, RD=(%p,%p) WR=(%p,%p). Actual flags = %s+%s\n", + __FUNCTION__, + msg, + msg->devbuf_rd, msg->databuf_rd, + msg->devbuf_wr, msg->databuf_wr, + msg->flags & SPI_M_RD ? "RD" : "~rd", + msg->flags & SPI_M_WR ? "WR" : "~wr"); + return msg; +} + +static inline void spimsg_free(struct spi_msg *msg) +{ + void (*do_free) (const void *) = kfree; + struct spi_driver *drv = SPI_DEV_DRV(msg->device); + + if (msg) { + + if ( !(msg->flags & SPI_M_DNA) || (msg->flags & SPI_M_EXTBUF) ) { + if (drv->free) + do_free = drv->free; + if (drv->release_buffer) { + if (msg->databuf_rd) + drv->release_buffer(msg->device, + msg->databuf_rd); + if (msg->databuf_wr) + drv->release_buffer(msg->device, + msg->databuf_wr); + } + if (msg->devbuf_rd) + do_free(msg->devbuf_rd); + if (msg->devbuf_wr) + do_free(msg->devbuf_wr); + } + kfree(msg); + } +} + +static inline u8 *spimsg_buffer_rd(struct spi_msg *msg) +{ + return msg ? msg->databuf_rd : NULL; +} + +static inline u8 *spimsg_buffer_wr(struct spi_msg *msg) +{ + return msg ? msg->databuf_wr : NULL; +} + +static inline u8 *spimsg_buffer(struct spi_msg *msg) +{ + if (!msg) + return NULL; + if ((msg->flags & (SPI_M_RD | SPI_M_WR)) == (SPI_M_RD | SPI_M_WR)) { + printk(KERN_ERR "%s: what buffer do you really want ?\n", + __FUNCTION__); + return NULL; + } + if (msg->flags & SPI_M_RD) + return msg->databuf_rd; + if (msg->flags & SPI_M_WR) + return msg->databuf_wr; +} + +static inline void spimsg_set_rd( struct spi_msg* msg, void* buf ) +{ + msg->databuf_rd = buf; +} + +static inline void spimsg_set_wr (struct spi_msg *msg, void *buf ) +{ + msg->databuf_wr = buf; +} + + +#define SPIMSG_OK 0x01 +#define SPIMSG_FAILED 0x80 +#define SPIMSG_STARTED 0x02 +#define SPIMSG_DONE 0x04 + +#define SPI_MAJOR 153 + +struct spi_driver; +struct spi_device; + +static inline int spi_bus_driver_register (struct spi_bus_driver *bus_driver ) +{ + return driver_register (&bus_driver->driver); +} + +void spi_bus_driver_unregister(struct spi_bus_driver *); +int spi_bus_driver_init(struct spi_bus_driver *driver, struct device *dev); +struct spi_device* spi_device_add(struct device *parent, char *name, void *private); + +static inline void spi_device_del(struct spi_device *dev) +{ + device_unregister(&dev->dev); +} +static inline int spi_driver_add(struct spi_driver *drv) +{ + drv->driver.bus = &spi_bus; + return driver_register(&drv->driver); +} +static inline void spi_driver_del(struct spi_driver *drv) +{ + driver_unregister(&drv->driver); +} + +#define SPI_DEV_CHAR "spi-char" + +extern void spi_bus_reset(struct device* bus, u32 context); +extern int spi_write(struct spi_device *dev, const char *buf, int len); +extern int spi_read(struct spi_device *dev, char *buf, int len); + +extern int spi_queue(struct spi_msg *message); +extern int spi_transfer(struct spi_msg *message, + void (*status) (struct spi_msg *, int)); +extern int spi_bus_populate(struct device *parent, char *device, + void (*assign) (struct device *parent, + struct spi_device *)); +struct spi_device_desc { + char* name; + void* params; +}; +extern int spi_bus_populate2(struct device *parent, + struct spi_device_desc *devices, + void (*assign) (struct device *parent, + struct spi_device *, + void *)); + +#endif /* SPI_H */ |
From: <Doe...@in...> - 2005-10-11 02:36:23
|
Hello spi developers, We are working in the development center of Infineon Singapore. Now we are working on a new embedded device that is running the Linux = 2.4.20 kernel. All drivers needed for this new chipset have been coded for the 2.4 = kernel. We have some devices connected via spi on the board. We don't want to reinvent the wheel, so we are looking for a good = spi-stack. It seems your spi-stack would be ideal, as a later port to the 2.6 = kernel is planed. But we would need a back ported version of this spi stack to the 2.4 = kernel. We are highly interested in such a back port of your stack. Please let us know if such a back port is planed or someone is working = on it. We would write adapter and device-drivers for our hardware based on your = stack in this case. With the best regards Johannes Doering, Liu Peng (Pen...@in...), Xu Bingtao (Bin...@in...) Infineon Technologies Asia Pacific Pte Ltd=20 IFAP DC COM=20 8 Kallang Sector, Singapore 349282=20 *Disclaimer* "This e-mail and any attachments are confidential and may contain trade = secrets or privileged or undisclosed information. They may also be = subject to copyright protection. Please do not copy, distribute or = forward this email to anyone unless authorised. If you are not a named = addressee, you must not use, disclose, retain or reproduce all or any = part of the information contained in this e-mail or any attachments. If = you have received this email by mistake please notify the sender = immediately by return email and destroy/delete all copies of the email." |
From: Greg KH <gr...@kr...> - 2005-10-06 19:10:35
|
On Thu, Oct 06, 2005 at 08:02:34PM +0100, Russell King wrote: > > Since this is obsolete infrastructure which is no longer necessary, > we can remove it. Here's an (untested) patch to do exactly that. > > Signed-off-by: Russell King <rmk...@ar...> Acked-by: Greg Kroah-Hartman <gr...@su...> |
From: Russell K. <rmk...@ar...> - 2005-10-06 19:03:17
|
On Thu, Oct 06, 2005 at 07:29:38PM +0100, Russell King wrote: > On Thu, Oct 06, 2005 at 07:23:48PM +0100, Mark Underwood wrote: > > --- David Brownell <da...@pa...> wrote: > > > Vitaly ... comments from Russell and Pavel both addresses your comments > > > about that obsolete parameter. What letter? The one I remember was > > > one responding to Mark Underwood (?) where you complained about issuing > > > three calls for one suspend event. You can't have it both ways!! > > > Either that parameter should be used in the documented way (call the > > > suspend method three times, one right after another) or it should be used > > > more sanely (parameter is constant. > > > > Yes, that was in reply to my SPI subsystem patch set (in which Vitaly > > didn't like the fact that I call suspend/resume 3 times) and then in > > the same thread (in answer to David's response of dropping this as he > > didn't think anyone would mind this) Vitaly said that you can't do this. > > Vitaly has a problem then. We must _not_ call suspend three times > just because it has different "levels" - SUSPEND_DISABLE, > SUSPEND_SAVE_STATE and SUSPEND_POWER_DOWN. > > As I've said earlier in the thread, the only reason these exist is > because no one has gone to the effort of cleaning up the crap left > behind from PM version 1 for the platform devices. > > When PM v2 happened, I just hacked the platform device drivers to > work with this new model. So please consider the three argument > suspend callback a legacy feature and if you're going to use it, > call it exactly once. > > And please document that this is the case for your bus type, and > that the "level" argument is meaningless. Better still, please > do not use the device_driver suspend/resume pointers at all. Same > argument applies - only platform devices use them, and these should > eventually be killed off. Here's a patch to illustrate what I mean. In PM v1, all devices were called at SUSPEND_DISABLE level. Then all devices were called at SUSPEND_SAVE_STATE level, and finally SUSPEND_POWER_DOWN level. However, with PM v2, to maintain compatibility for platform devices, I arranged for the PM v2 suspend/resume callbacks to call the old PM v1 suspend/resume callbacks three times with each level in order so that existing drivers continued to work. Since this is obsolete infrastructure which is no longer necessary, we can remove it. Here's an (untested) patch to do exactly that. Signed-off-by: Russell King <rmk...@ar...> --- Documentation/driver-model/driver.txt | 60 +--------------------------------- arch/arm/common/locomo.c | 10 +---- arch/arm/common/sa1111.c | 11 +----- arch/arm/common/scoop.c | 20 +++++------ arch/arm/mach-pxa/corgi_ssp.c | 24 ++++++------- arch/arm/mach-sa1100/neponset.c | 24 +++++-------- drivers/base/platform.c | 20 +++-------- drivers/char/s3c2410-rtc.c | 20 +++++------ drivers/char/sonypi.c | 14 +++---- drivers/char/watchdog/s3c2410_wdt.c | 34 ++++++++----------- drivers/hwmon/hdaps.c | 6 +-- drivers/i2c/busses/i2c-s3c2410.c | 8 +--- drivers/input/keyboard/corgikbd.c | 22 +++++------- drivers/input/keyboard/spitzkbd.c | 44 +++++++++++------------- drivers/input/serio/i8042.c | 13 ++----- drivers/input/touchscreen/corgi_ts.c | 38 ++++++++++----------- drivers/mfd/mcp-sa11x0.c | 20 +++++------ drivers/mmc/pxamci.c | 8 ++-- drivers/mtd/maps/sa1100-flash.c | 8 ++-- drivers/net/dm9000.c | 8 ++-- drivers/net/irda/sa1100_ir.c | 8 ++-- drivers/net/irda/smsc-ircc2.c | 8 ++-- drivers/net/phy/mdio_bus.c | 20 +++-------- drivers/net/smc91x.c | 8 ++-- drivers/pcmcia/au1000_generic.c | 21 +---------- drivers/pcmcia/hd64465_ss.c | 20 +---------- drivers/pcmcia/i82365.c | 20 +---------- drivers/pcmcia/m32r_cfc.c | 21 +---------- drivers/pcmcia/m32r_pcc.c | 21 +---------- drivers/pcmcia/omap_cf.c | 18 +--------- drivers/pcmcia/pxa2xx_base.c | 26 +++----------- drivers/pcmcia/sa1100_generic.c | 20 +---------- drivers/pcmcia/tcic.c | 20 +---------- drivers/pcmcia/vrc4171_card.c | 24 +------------ drivers/serial/8250.c | 10 +---- drivers/serial/imx.c | 8 ++-- drivers/serial/mpc52xx_uart.c | 8 ++-- drivers/serial/pxa.c | 8 ++-- drivers/serial/s3c2410.c | 8 ++-- drivers/serial/sa1100.c | 8 ++-- drivers/serial/vr41xx_siu.c | 10 +---- drivers/usb/gadget/dummy_hcd.c | 22 ++---------- drivers/usb/gadget/omap_udc.c | 9 +---- drivers/usb/gadget/pxa2xx_udc.c | 17 ++++----- drivers/usb/host/isp116x-hcd.c | 14 ++----- drivers/usb/host/ohci-omap.c | 10 +---- drivers/usb/host/sl811-hcd.c | 10 +---- drivers/video/backlight/corgi_bl.c | 10 ++--- drivers/video/imxfb.c | 10 ++--- drivers/video/pxafb.c | 10 ++--- drivers/video/s1d13xxxfb.c | 7 +-- drivers/video/s3c2410fb.c | 29 ++++++---------- drivers/video/sa1100fb.c | 10 ++--- drivers/video/w100fb.c | 48 +++++++++++++-------------- include/linux/device.h | 17 +-------- sound/arm/pxa2xx-ac97.c | 8 ++-- sound/core/init.c | 10 +---- sound/pci/ac97/ac97_bus.c | 20 +++-------- 58 files changed, 306 insertions(+), 682 deletions(-) diff --git a/Documentation/driver-model/driver.txt b/Documentation/driver-model/driver.txt --- a/Documentation/driver-model/driver.txt +++ b/Documentation/driver-model/driver.txt @@ -196,67 +196,11 @@ it into a supported low-power state. int (*suspend) (struct device * dev, pm_message_t state, u32 level); -suspend is called to put the device in a low power state. There are -several stages to successfully suspending a device, which is denoted in -the @level parameter. Breaking the suspend transition into several -stages affords the platform flexibility in performing device power -management based on the requirements of the system and the -user-defined policy. - -SUSPEND_NOTIFY notifies the device that a suspend transition is about -to happen. This happens on system power state transitions to verify -that all devices can successfully suspend. - -A driver may choose to fail on this call, which should cause the -entire suspend transition to fail. A driver should fail only if it -knows that the device will not be able to be resumed properly when the -system wakes up again. It could also fail if it somehow determines it -is in the middle of an operation too important to stop. - -SUSPEND_DISABLE tells the device to stop I/O transactions. When it -stops transactions, or what it should do with unfinished transactions -is a policy of the driver. After this call, the driver should not -accept any other I/O requests. - -SUSPEND_SAVE_STATE tells the device to save the context of the -hardware. This includes any bus-specific hardware state and -device-specific hardware state. A pointer to this saved state can be -stored in the device's saved_state field. - -SUSPEND_POWER_DOWN tells the driver to place the device in the low -power state requested. - -Whether suspend is called with a given level is a policy of the -platform. Some levels may be omitted; drivers must not assume the -reception of any level. However, all levels must be called in the -order above; i.e. notification will always come before disabling; -disabling the device will come before suspending the device. - -All calls are made with interrupts enabled, except for the -SUSPEND_POWER_DOWN level. +suspend is called to put the device in a low power state. int (*resume) (struct device * dev, u32 level); -Resume is used to bring a device back from a low power state. Like the -suspend transition, it happens in several stages. - -RESUME_POWER_ON tells the driver to set the power state to the state -before the suspend call (The device could have already been in a low -power state before the suspend call to put in a lower power state). - -RESUME_RESTORE_STATE tells the driver to restore the state saved by -the SUSPEND_SAVE_STATE suspend call. - -RESUME_ENABLE tells the driver to start accepting I/O transactions -again. Depending on driver policy, the device may already have pending -I/O requests. - -RESUME_POWER_ON is called with interrupts disabled. The other resume -levels are called with interrupts enabled. - -As with the various suspend stages, the driver must not assume that -any other resume calls have been or will be made. Each call should be -self-contained and not dependent on any external state. +Resume is used to bring a device back from a low power state. Attributes diff --git a/arch/arm/common/locomo.c b/arch/arm/common/locomo.c --- a/arch/arm/common/locomo.c +++ b/arch/arm/common/locomo.c @@ -550,15 +550,12 @@ struct locomo_save_data { u16 LCM_SPIMD; }; -static int locomo_suspend(struct device *dev, pm_message_t state, u32 level) +static int locomo_suspend(struct device *dev, pm_message_t state) { struct locomo *lchip = dev_get_drvdata(dev); struct locomo_save_data *save; unsigned long flags; - if (level != SUSPEND_DISABLE) - return 0; - save = kmalloc(sizeof(struct locomo_save_data), GFP_KERNEL); if (!save) return -ENOMEM; @@ -597,16 +594,13 @@ static int locomo_suspend(struct device return 0; } -static int locomo_resume(struct device *dev, u32 level) +static int locomo_resume(struct device *dev) { struct locomo *lchip = dev_get_drvdata(dev); struct locomo_save_data *save; unsigned long r; unsigned long flags; - if (level != RESUME_ENABLE) - return 0; - save = (struct locomo_save_data *) dev->power.saved_state; if (!save) return 0; diff --git a/arch/arm/common/sa1111.c b/arch/arm/common/sa1111.c --- a/arch/arm/common/sa1111.c +++ b/arch/arm/common/sa1111.c @@ -801,7 +801,7 @@ struct sa1111_save_data { #ifdef CONFIG_PM -static int sa1111_suspend(struct device *dev, pm_message_t state, u32 level) +static int sa1111_suspend(struct device *dev, pm_message_t state) { struct sa1111 *sachip = dev_get_drvdata(dev); struct sa1111_save_data *save; @@ -809,9 +809,6 @@ static int sa1111_suspend(struct device unsigned int val; void __iomem *base; - if (level != SUSPEND_DISABLE) - return 0; - save = kmalloc(sizeof(struct sa1111_save_data), GFP_KERNEL); if (!save) return -ENOMEM; @@ -856,23 +853,19 @@ static int sa1111_suspend(struct device /* * sa1111_resume - Restore the SA1111 device state. * @dev: device to restore - * @level: resume level * * Restore the general state of the SA1111; clock control and * interrupt controller. Other parts of the SA1111 must be * restored by their respective drivers, and must be called * via LDM after this function. */ -static int sa1111_resume(struct device *dev, u32 level) +static int sa1111_resume(struct device *dev) { struct sa1111 *sachip = dev_get_drvdata(dev); struct sa1111_save_data *save; unsigned long flags, id; void __iomem *base; - if (level != RESUME_ENABLE) - return 0; - save = (struct sa1111_save_data *)dev->power.saved_state; if (!save) return 0; diff --git a/arch/arm/common/scoop.c b/arch/arm/common/scoop.c --- a/arch/arm/common/scoop.c +++ b/arch/arm/common/scoop.c @@ -91,24 +91,22 @@ EXPORT_SYMBOL(read_scoop_reg); EXPORT_SYMBOL(write_scoop_reg); #ifdef CONFIG_PM -static int scoop_suspend(struct device *dev, pm_message_t state, uint32_t level) +static int scoop_suspend(struct device *dev, pm_message_t state) { - if (level == SUSPEND_POWER_DOWN) { - struct scoop_dev *sdev = dev_get_drvdata(dev); + struct scoop_dev *sdev = dev_get_drvdata(dev); + + sdev->scoop_gpwr = SCOOP_REG(sdev->base,SCOOP_GPWR); + SCOOP_REG(sdev->base,SCOOP_GPWR) = 0; - sdev->scoop_gpwr = SCOOP_REG(sdev->base,SCOOP_GPWR); - SCOOP_REG(sdev->base,SCOOP_GPWR) = 0; - } return 0; } -static int scoop_resume(struct device *dev, uint32_t level) +static int scoop_resume(struct device *dev) { - if (level == RESUME_POWER_ON) { - struct scoop_dev *sdev = dev_get_drvdata(dev); + struct scoop_dev *sdev = dev_get_drvdata(dev); + + SCOOP_REG(sdev->base,SCOOP_GPWR) = sdev->scoop_gpwr; - SCOOP_REG(sdev->base,SCOOP_GPWR) = sdev->scoop_gpwr; - } return 0; } #else diff --git a/arch/arm/mach-pxa/corgi_ssp.c b/arch/arm/mach-pxa/corgi_ssp.c --- a/arch/arm/mach-pxa/corgi_ssp.c +++ b/arch/arm/mach-pxa/corgi_ssp.c @@ -222,24 +222,22 @@ static int corgi_ssp_remove(struct devic return 0; } -static int corgi_ssp_suspend(struct device *dev, pm_message_t state, u32 level) +static int corgi_ssp_suspend(struct device *dev, pm_message_t state) { - if (level == SUSPEND_POWER_DOWN) { - ssp_flush(&corgi_ssp_dev); - ssp_save_state(&corgi_ssp_dev,&corgi_ssp_state); - } + ssp_flush(&corgi_ssp_dev); + ssp_save_state(&corgi_ssp_dev,&corgi_ssp_state); + return 0; } -static int corgi_ssp_resume(struct device *dev, u32 level) +static int corgi_ssp_resume(struct device *dev) { - if (level == RESUME_POWER_ON) { - GPSR(ssp_machinfo->cs_lcdcon) = GPIO_bit(ssp_machinfo->cs_lcdcon); /* High - Disable LCD Control/Timing Gen */ - GPSR(ssp_machinfo->cs_max1111) = GPIO_bit(ssp_machinfo->cs_max1111); /* High - Disable MAX1111*/ - GPSR(ssp_machinfo->cs_ads7846) = GPIO_bit(ssp_machinfo->cs_ads7846); /* High - Disable ADS7846*/ - ssp_restore_state(&corgi_ssp_dev,&corgi_ssp_state); - ssp_enable(&corgi_ssp_dev); - } + GPSR(ssp_machinfo->cs_lcdcon) = GPIO_bit(ssp_machinfo->cs_lcdcon); /* High - Disable LCD Control/Timing Gen */ + GPSR(ssp_machinfo->cs_max1111) = GPIO_bit(ssp_machinfo->cs_max1111); /* High - Disable MAX1111*/ + GPSR(ssp_machinfo->cs_ads7846) = GPIO_bit(ssp_machinfo->cs_ads7846); /* High - Disable ADS7846*/ + ssp_restore_state(&corgi_ssp_dev,&corgi_ssp_state); + ssp_enable(&corgi_ssp_dev); + return 0; } diff --git a/arch/arm/mach-sa1100/neponset.c b/arch/arm/mach-sa1100/neponset.c --- a/arch/arm/mach-sa1100/neponset.c +++ b/arch/arm/mach-sa1100/neponset.c @@ -183,28 +183,22 @@ static int neponset_suspend(struct devic /* * Save state. */ - if (level == SUSPEND_SAVE_STATE || - level == SUSPEND_DISABLE || - level == SUSPEND_POWER_DOWN) { - if (!dev->power.saved_state) - dev->power.saved_state = kmalloc(sizeof(unsigned int), GFP_KERNEL); - if (!dev->power.saved_state) - return -ENOMEM; + if (!dev->power.saved_state) + dev->power.saved_state = kmalloc(sizeof(unsigned int), GFP_KERNEL); + if (!dev->power.saved_state) + return -ENOMEM; - *(unsigned int *)dev->power.saved_state = NCR_0; - } + *(unsigned int *)dev->power.saved_state = NCR_0; return 0; } static int neponset_resume(struct device *dev, u32 level) { - if (level == RESUME_RESTORE_STATE || level == RESUME_ENABLE) { - if (dev->power.saved_state) { - NCR_0 = *(unsigned int *)dev->power.saved_state; - kfree(dev->power.saved_state); - dev->power.saved_state = NULL; - } + if (dev->power.saved_state) { + NCR_0 = *(unsigned int *)dev->power.saved_state; + kfree(dev->power.saved_state); + dev->power.saved_state = NULL; } return 0; diff --git a/drivers/base/platform.c b/drivers/base/platform.c --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -279,13 +279,9 @@ static int platform_suspend(struct devic { int ret = 0; - if (dev->driver && dev->driver->suspend) { - ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE); - if (ret == 0) - ret = dev->driver->suspend(dev, state, SUSPEND_SAVE_STATE); - if (ret == 0) - ret = dev->driver->suspend(dev, state, SUSPEND_POWER_DOWN); - } + if (dev->driver && dev->driver->suspend) + ret = dev->driver->suspend(dev, state); + return ret; } @@ -293,13 +289,9 @@ static int platform_resume(struct device { int ret = 0; - if (dev->driver && dev->driver->resume) { - ret = dev->driver->resume(dev, RESUME_POWER_ON); - if (ret == 0) - ret = dev->driver->resume(dev, RESUME_RESTORE_STATE); - if (ret == 0) - ret = dev->driver->resume(dev, RESUME_ENABLE); - } + if (dev->driver && dev->driver->resume) + ret = dev->driver->resume(dev); + return ret; } diff --git a/drivers/char/s3c2410-rtc.c b/drivers/char/s3c2410-rtc.c --- a/drivers/char/s3c2410-rtc.c +++ b/drivers/char/s3c2410-rtc.c @@ -519,30 +519,28 @@ static struct timespec s3c2410_rtc_delta static int ticnt_save; -static int s3c2410_rtc_suspend(struct device *dev, pm_message_t state, u32 level) +static int s3c2410_rtc_suspend(struct device *dev, pm_message_t state) { struct rtc_time tm; struct timespec time; time.tv_nsec = 0; - if (level == SUSPEND_POWER_DOWN) { - /* save TICNT for anyone using periodic interrupts */ + /* save TICNT for anyone using periodic interrupts */ - ticnt_save = readb(S3C2410_TICNT); + ticnt_save = readb(S3C2410_TICNT); - /* calculate time delta for suspend */ + /* calculate time delta for suspend */ - s3c2410_rtc_gettime(&tm); - rtc_tm_to_time(&tm, &time.tv_sec); - save_time_delta(&s3c2410_rtc_delta, &time); - s3c2410_rtc_enable(dev, 0); - } + s3c2410_rtc_gettime(&tm); + rtc_tm_to_time(&tm, &time.tv_sec); + save_time_delta(&s3c2410_rtc_delta, &time); + s3c2410_rtc_enable(dev, 0); return 0; } -static int s3c2410_rtc_resume(struct device *dev, u32 level) +static int s3c2410_rtc_resume(struct device *dev) { struct rtc_time tm; struct timespec time; diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1171,19 +1171,17 @@ static int sonypi_disable(void) #ifdef CONFIG_PM static int old_camera_power; -static int sonypi_suspend(struct device *dev, pm_message_t state, u32 level) +static int sonypi_suspend(struct device *dev, pm_message_t state) { - if (level == SUSPEND_DISABLE) { - old_camera_power = sonypi_device.camera_power; - sonypi_disable(); - } + old_camera_power = sonypi_device.camera_power; + sonypi_disable(); + return 0; } -static int sonypi_resume(struct device *dev, u32 level) +static int sonypi_resume(struct device *dev) { - if (level == RESUME_ENABLE) - sonypi_enable(old_camera_power); + sonypi_enable(old_camera_power); return 0; } #endif diff --git a/drivers/char/watchdog/s3c2410_wdt.c b/drivers/char/watchdog/s3c2410_wdt.c --- a/drivers/char/watchdog/s3c2410_wdt.c +++ b/drivers/char/watchdog/s3c2410_wdt.c @@ -464,32 +464,28 @@ static void s3c2410wdt_shutdown(struct d static unsigned long wtcon_save; static unsigned long wtdat_save; -static int s3c2410wdt_suspend(struct device *dev, pm_message_t state, u32 level) +static int s3c2410wdt_suspend(struct device *dev, pm_message_t state) { - if (level == SUSPEND_POWER_DOWN) { - /* Save watchdog state, and turn it off. */ - wtcon_save = readl(wdt_base + S3C2410_WTCON); - wtdat_save = readl(wdt_base + S3C2410_WTDAT); - - /* Note that WTCNT doesn't need to be saved. */ - s3c2410wdt_stop(); - } + /* Save watchdog state, and turn it off. */ + wtcon_save = readl(wdt_base + S3C2410_WTCON); + wtdat_save = readl(wdt_base + S3C2410_WTDAT); + + /* Note that WTCNT doesn't need to be saved. */ + s3c2410wdt_stop(); return 0; } -static int s3c2410wdt_resume(struct device *dev, u32 level) +static int s3c2410wdt_resume(struct device *dev) { - if (level == RESUME_POWER_ON) { - /* Restore watchdog state. */ + /* Restore watchdog state. */ + + writel(wtdat_save, wdt_base + S3C2410_WTDAT); + writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */ + writel(wtcon_save, wdt_base + S3C2410_WTCON); - writel(wtdat_save, wdt_base + S3C2410_WTDAT); - writel(wtdat_save, wdt_base + S3C2410_WTCNT); /* Reset count */ - writel(wtcon_save, wdt_base + S3C2410_WTCON); - - printk(KERN_INFO PFX "watchdog %sabled\n", - (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis"); - } + printk(KERN_INFO PFX "watchdog %sabled\n", + (wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis"); return 0; } diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c --- a/drivers/hwmon/hdaps.c +++ b/drivers/hwmon/hdaps.c @@ -296,11 +296,9 @@ static int hdaps_probe(struct device *de return 0; } -static int hdaps_resume(struct device *dev, u32 level) +static int hdaps_resume(struct device *dev) { - if (level == RESUME_ENABLE) - return hdaps_device_init(); - return 0; + return hdaps_device_init(); } static struct device_driver hdaps_driver = { diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -879,14 +879,12 @@ static int s3c24xx_i2c_remove(struct dev } #ifdef CONFIG_PM -static int s3c24xx_i2c_resume(struct device *dev, u32 level) +static int s3c24xx_i2c_resume(struct device *dev) { struct s3c24xx_i2c *i2c = dev_get_drvdata(dev); - - if (i2c != NULL && level == RESUME_ENABLE) { - dev_dbg(dev, "resume: level %d\n", level); + + if (i2c != NULL) s3c24xx_i2c_init(i2c); - } return 0; } diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c --- a/drivers/input/keyboard/corgikbd.c +++ b/drivers/input/keyboard/corgikbd.c @@ -260,24 +260,22 @@ static void corgikbd_hinge_timer(unsigne } #ifdef CONFIG_PM -static int corgikbd_suspend(struct device *dev, pm_message_t state, uint32_t level) +static int corgikbd_suspend(struct device *dev, pm_message_t state) { - if (level == SUSPEND_POWER_DOWN) { - struct corgikbd *corgikbd = dev_get_drvdata(dev); - corgikbd->suspended = 1; - } + struct corgikbd *corgikbd = dev_get_drvdata(dev); + corgikbd->suspended = 1; + return 0; } -static int corgikbd_resume(struct device *dev, uint32_t level) +static int corgikbd_resume(struct device *dev) { - if (level == RESUME_POWER_ON) { - struct corgikbd *corgikbd = dev_get_drvdata(dev); + struct corgikbd *corgikbd = dev_get_drvdata(dev); + + /* Upon resume, ignore the suspend key for a short while */ + corgikbd->suspend_jiffies=jiffies; + corgikbd->suspended = 0; - /* Upon resume, ignore the suspend key for a short while */ - corgikbd->suspend_jiffies=jiffies; - corgikbd->suspended = 0; - } return 0; } #else diff --git a/drivers/input/keyboard/spitzkbd.c b/drivers/input/keyboard/spitzkbd.c --- a/drivers/input/keyboard/spitzkbd.c +++ b/drivers/input/keyboard/spitzkbd.c @@ -309,34 +309,32 @@ static void spitzkbd_hinge_timer(unsigne } #ifdef CONFIG_PM -static int spitzkbd_suspend(struct device *dev, pm_message_t state, uint32_t level) +static int spitzkbd_suspend(struct device *dev, pm_message_t state) { - if (level == SUSPEND_POWER_DOWN) { - int i; - struct spitzkbd *spitzkbd = dev_get_drvdata(dev); - spitzkbd->suspended = 1; - - /* Set Strobe lines as inputs - *except* strobe line 0 leave this - enabled so we can detect a power button press for resume */ - for (i = 1; i < SPITZ_KEY_STROBE_NUM; i++) - pxa_gpio_mode(spitz_strobes[i] | GPIO_IN); - } + int i; + struct spitzkbd *spitzkbd = dev_get_drvdata(dev); + spitzkbd->suspended = 1; + + /* Set Strobe lines as inputs - *except* strobe line 0 leave this + enabled so we can detect a power button press for resume */ + for (i = 1; i < SPITZ_KEY_STROBE_NUM; i++) + pxa_gpio_mode(spitz_strobes[i] | GPIO_IN); + return 0; } -static int spitzkbd_resume(struct device *dev, uint32_t level) +static int spitzkbd_resume(struct device *dev) { - if (level == RESUME_POWER_ON) { - int i; - struct spitzkbd *spitzkbd = dev_get_drvdata(dev); - - for (i = 0; i < SPITZ_KEY_STROBE_NUM; i++) - pxa_gpio_mode(spitz_strobes[i] | GPIO_OUT | GPIO_DFLT_HIGH); - - /* Upon resume, ignore the suspend key for a short while */ - spitzkbd->suspend_jiffies = jiffies; - spitzkbd->suspended = 0; - } + int i; + struct spitzkbd *spitzkbd = dev_get_drvdata(dev); + + for (i = 0; i < SPITZ_KEY_STROBE_NUM; i++) + pxa_gpio_mode(spitz_strobes[i] | GPIO_OUT | GPIO_DFLT_HIGH); + + /* Upon resume, ignore the suspend key for a short while */ + spitzkbd->suspend_jiffies = jiffies; + spitzkbd->suspended = 0; + return 0; } #else diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -911,12 +911,10 @@ static long i8042_panic_blink(long count * Here we try to restore the original BIOS settings */ -static int i8042_suspend(struct device *dev, pm_message_t state, u32 level) +static int i8042_suspend(struct device *dev, pm_message_t state) { - if (level == SUSPEND_DISABLE) { - del_timer_sync(&i8042_timer); - i8042_controller_reset(); - } + del_timer_sync(&i8042_timer); + i8042_controller_reset(); return 0; } @@ -926,13 +924,10 @@ static int i8042_suspend(struct device * * Here we try to reset everything back to a state in which suspended */ -static int i8042_resume(struct device *dev, u32 level) +static int i8042_resume(struct device *dev) { int i; - if (level != RESUME_ENABLE) - return 0; - if (i8042_ctl_test()) return -1; diff --git a/drivers/input/touchscreen/corgi_ts.c b/drivers/input/touchscreen/corgi_ts.c --- a/drivers/input/touchscreen/corgi_ts.c +++ b/drivers/input/touchscreen/corgi_ts.c @@ -234,34 +234,32 @@ static irqreturn_t ts_interrupt(int irq, } #ifdef CONFIG_PM -static int corgits_suspend(struct device *dev, pm_message_t state, uint32_t level) +static int corgits_suspend(struct device *dev, pm_message_t state) { - if (level == SUSPEND_POWER_DOWN) { - struct corgi_ts *corgi_ts = dev_get_drvdata(dev); - - if (corgi_ts->pendown) { - del_timer_sync(&corgi_ts->timer); - corgi_ts->tc.pressure = 0; - new_data(corgi_ts, NULL); - corgi_ts->pendown = 0; - } - corgi_ts->power_mode = PWR_MODE_SUSPEND; + struct corgi_ts *corgi_ts = dev_get_drvdata(dev); - corgi_ssp_ads7846_putget((1u << ADSCTRL_ADR_SH) | ADSCTRL_STS); + if (corgi_ts->pendown) { + del_timer_sync(&corgi_ts->timer); + corgi_ts->tc.pressure = 0; + new_data(corgi_ts, NULL); + corgi_ts->pendown = 0; } + corgi_ts->power_mode = PWR_MODE_SUSPEND; + + corgi_ssp_ads7846_putget((1u << ADSCTRL_ADR_SH) | ADSCTRL_STS); + return 0; } -static int corgits_resume(struct device *dev, uint32_t level) +static int corgits_resume(struct device *dev) { - if (level == RESUME_POWER_ON) { - struct corgi_ts *corgi_ts = dev_get_drvdata(dev); + struct corgi_ts *corgi_ts = dev_get_drvdata(dev); + + corgi_ssp_ads7846_putget((4u << ADSCTRL_ADR_SH) | ADSCTRL_STS); + /* Enable Falling Edge */ + set_irq_type(corgi_ts->irq_gpio, IRQT_FALLING); + corgi_ts->power_mode = PWR_MODE_ACTIVE; - corgi_ssp_ads7846_putget((4u << ADSCTRL_ADR_SH) | ADSCTRL_STS); - /* Enable Falling Edge */ - set_irq_type(corgi_ts->irq_gpio, IRQT_FALLING); - corgi_ts->power_mode = PWR_MODE_ACTIVE; - } return 0; } #else diff --git a/drivers/mfd/mcp-sa11x0.c b/drivers/mfd/mcp-sa11x0.c --- a/drivers/mfd/mcp-sa11x0.c +++ b/drivers/mfd/mcp-sa11x0.c @@ -219,26 +219,24 @@ static int mcp_sa11x0_remove(struct devi return 0; } -static int mcp_sa11x0_suspend(struct device *dev, pm_message_t state, u32 level) +static int mcp_sa11x0_suspend(struct device *dev, pm_message_t state) { struct mcp *mcp = dev_get_drvdata(dev); - if (level == SUSPEND_DISABLE) { - priv(mcp)->mccr0 = Ser4MCCR0; - priv(mcp)->mccr1 = Ser4MCCR1; - Ser4MCCR0 &= ~MCCR0_MCE; - } + priv(mcp)->mccr0 = Ser4MCCR0; + priv(mcp)->mccr1 = Ser4MCCR1; + Ser4MCCR0 &= ~MCCR0_MCE; + return 0; } -static int mcp_sa11x0_resume(struct device *dev, u32 level) +static int mcp_sa11x0_resume(struct device *dev) { struct mcp *mcp = dev_get_drvdata(dev); - if (level == RESUME_RESTORE_STATE) { - Ser4MCCR1 = priv(mcp)->mccr1; - Ser4MCCR0 = priv(mcp)->mccr0; - } + Ser4MCCR1 = priv(mcp)->mccr1; + Ser4MCCR0 = priv(mcp)->mccr0; + return 0; } diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c --- a/drivers/mmc/pxamci.c +++ b/drivers/mmc/pxamci.c @@ -571,23 +571,23 @@ static int pxamci_remove(struct device * } #ifdef CONFIG_PM -static int pxamci_suspend(struct device *dev, pm_message_t state, u32 level) +static int pxamci_suspend(struct device *dev, pm_message_t state) { struct mmc_host *mmc = dev_get_drvdata(dev); int ret = 0; - if (mmc && level == SUSPEND_DISABLE) + if (mmc) ret = mmc_suspend_host(mmc, state); return ret; } -static int pxamci_resume(struct device *dev, u32 level) +static int pxamci_resume(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); int ret = 0; - if (mmc && level == RESUME_ENABLE) + if (mmc) ret = mmc_resume_host(mmc); return ret; diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -402,21 +402,21 @@ static int __exit sa1100_mtd_remove(stru } #ifdef CONFIG_PM -static int sa1100_mtd_suspend(struct device *dev, pm_message_t state, u32 level) +static int sa1100_mtd_suspend(struct device *dev, pm_message_t state) { struct sa_info *info = dev_get_drvdata(dev); int ret = 0; - if (info && level == SUSPEND_SAVE_STATE) + if (info) ret = info->mtd->suspend(info->mtd); return ret; } -static int sa1100_mtd_resume(struct device *dev, u32 level) +static int sa1100_mtd_resume(struct device *dev) { struct sa_info *info = dev_get_drvdata(dev); - if (info && level == RESUME_RESTORE_STATE) + if (info) info->mtd->resume(info->mtd); return 0; } diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c --- a/drivers/net/dm9000.c +++ b/drivers/net/dm9000.c @@ -1140,11 +1140,11 @@ dm9000_phy_write(struct net_device *dev, } static int -dm9000_drv_suspend(struct device *dev, pm_message_t state, u32 level) +dm9000_drv_suspend(struct device *dev, pm_message_t state) { struct net_device *ndev = dev_get_drvdata(dev); - if (ndev && level == SUSPEND_DISABLE) { + if (ndev) { if (netif_running(ndev)) { netif_device_detach(ndev); dm9000_shutdown(ndev); @@ -1154,12 +1154,12 @@ dm9000_drv_suspend(struct device *dev, p } static int -dm9000_drv_resume(struct device *dev, u32 level) +dm9000_drv_resume(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); board_info_t *db = (board_info_t *) ndev->priv; - if (ndev && level == RESUME_ENABLE) { + if (ndev) { if (netif_running(ndev)) { dm9000_reset(db); diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c --- a/drivers/net/irda/sa1100_ir.c +++ b/drivers/net/irda/sa1100_ir.c @@ -291,12 +291,12 @@ static void sa1100_irda_shutdown(struct /* * Suspend the IrDA interface. */ -static int sa1100_irda_suspend(struct device *_dev, pm_message_t state, u32 level) +static int sa1100_irda_suspend(struct device *_dev, pm_message_t state) { struct net_device *dev = dev_get_drvdata(_dev); struct sa1100_irda *si; - if (!dev || level != SUSPEND_DISABLE) + if (!dev) return 0; si = dev->priv; @@ -316,12 +316,12 @@ static int sa1100_irda_suspend(struct de /* * Resume the IrDA interface. */ -static int sa1100_irda_resume(struct device *_dev, u32 level) +static int sa1100_irda_resume(struct device *_dev) { struct net_device *dev = dev_get_drvdata(_dev); struct sa1100_irda *si; - if (!dev || level != RESUME_ENABLE) + if (!dev) return 0; si = dev->priv; diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c --- a/drivers/net/irda/smsc-ircc2.c +++ b/drivers/net/irda/smsc-ircc2.c @@ -1646,13 +1646,13 @@ static int smsc_ircc_net_close(struct ne return 0; } -static int smsc_ircc_suspend(struct device *dev, pm_message_t state, u32 level) +static int smsc_ircc_suspend(struct device *dev, pm_message_t state) { struct smsc_ircc_cb *self = dev_get_drvdata(dev); IRDA_MESSAGE("%s, Suspending\n", driver_name); - if (level == SUSPEND_DISABLE && !self->io.suspended) { + if (!self->io.suspended) { smsc_ircc_net_close(self->netdev); self->io.suspended = 1; } @@ -1660,11 +1660,11 @@ static int smsc_ircc_suspend(struct devi return 0; } -static int smsc_ircc_resume(struct device *dev, u32 level) +static int smsc_ircc_resume(struct device *dev) { struct smsc_ircc_cb *self = dev_get_drvdata(dev); - if (level == RESUME_ENABLE && self->io.suspended) { + if (self->io.suspended) { smsc_ircc_net_open(self->netdev); self->io.suspended = 0; diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -133,13 +133,9 @@ static int mdio_bus_suspend(struct devic int ret = 0; struct device_driver *drv = dev->driver; - if (drv && drv->suspend) { - ret = drv->suspend(dev, state, SUSPEND_DISABLE); - if (ret == 0) - ret = drv->suspend(dev, state, SUSPEND_SAVE_STATE); - if (ret == 0) - ret = drv->suspend(dev, state, SUSPEND_POWER_DOWN); - } + if (drv && drv->suspend) + ret = drv->suspend(dev, state); + return ret; } @@ -148,13 +144,9 @@ static int mdio_bus_resume(struct device int ret = 0; struct device_driver *drv = dev->driver; - if (drv && drv->resume) { - ret = drv->resume(dev, RESUME_POWER_ON); - if (ret == 0) - ret = drv->resume(dev, RESUME_RESTORE_STATE); - if (ret == 0) - ret = drv->resume(dev, RESUME_ENABLE); - } + if (drv && drv->resume) + ret = drv->resume(dev); + return ret; } diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -2291,11 +2291,11 @@ static int smc_drv_remove(struct device return 0; } -static int smc_drv_suspend(struct device *dev, pm_message_t state, u32 level) +static int smc_drv_suspend(struct device *dev, pm_message_t state) { struct net_device *ndev = dev_get_drvdata(dev); - if (ndev && level == SUSPEND_DISABLE) { + if (ndev) { if (netif_running(ndev)) { netif_device_detach(ndev); smc_shutdown(ndev); @@ -2305,12 +2305,12 @@ static int smc_drv_suspend(struct device return 0; } -static int smc_drv_resume(struct device *dev, u32 level) +static int smc_drv_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct net_device *ndev = dev_get_drvdata(dev); - if (ndev && level == RESUME_ENABLE) { + if (ndev) { struct smc_local *lp = netdev_priv(ndev); smc_enable_device(pdev); if (netif_running(ndev)) { diff --git a/drivers/pcmcia/au1000_generic.c b/drivers/pcmcia/au1000_generic.c --- a/drivers/pcmcia/au1000_generic.c +++ b/drivers/pcmcia/au1000_generic.c @@ -519,30 +519,13 @@ static int au1x00_drv_pcmcia_probe(struc } -static int au1x00_drv_pcmcia_suspend(struct device *dev, pm_message_t state, u32 level) -{ - int ret = 0; - if (level == SUSPEND_SAVE_STATE) - ret = pcmcia_socket_dev_suspend(dev, state); - return ret; -} - -static int au1x00_drv_pcmcia_resume(struct device *dev, u32 level) -{ - int ret = 0; - if (level == RESUME_RESTORE_STATE) - ret = pcmcia_socket_dev_resume(dev); - return ret; -} - - static struct device_driver au1x00_pcmcia_driver = { .probe = au1x00_drv_pcmcia_probe, .remove = au1x00_drv_pcmcia_remove, .name = "au1x00-pcmcia", .bus = &platform_bus_type, - .suspend = au1x00_drv_pcmcia_suspend, - .resume = au1x00_drv_pcmcia_resume + .suspend = pcmcia_socket_dev_suspend, + .resume = pcmcia_socket_dev_resume, }; static struct platform_device au1x00_device = { diff --git a/drivers/pcmcia/hd64465_ss.c b/drivers/pcmcia/hd64465_ss.c --- a/drivers/pcmcia/hd64465_ss.c +++ b/drivers/pcmcia/hd64465_ss.c @@ -844,27 +844,11 @@ static void hs_exit_socket(hs_socket_t * local_irq_restore(flags); } -static int hd64465_suspend(struct device *dev, pm_message_t state, u32 level) -{ - int ret = 0; - if (level == SUSPEND_SAVE_STATE) - ret = pcmcia_socket_dev_suspend(dev, state); - return ret; -} - -static int hd64465_resume(struct device *dev, u32 level) -{ - int ret = 0; - if (level == RESUME_RESTORE_STATE) - ret = pcmcia_socket_dev_resume(dev); - return ret; -} - static struct device_driver hd64465_driver = { .name = "hd64465-pcmcia", .bus = &platform_bus_type, - .suspend = hd64465_suspend, - .resume = hd64465_resume, + .suspend = pcmcia_socket_dev_suspend, + .resume = pcmcia_socket_dev_resume, }; static struct platform_device hd64465_device = { diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c --- a/drivers/pcmcia/i82365.c +++ b/drivers/pcmcia/i82365.c @@ -1332,27 +1332,11 @@ static struct pccard_operations pcic_ope /*====================================================================*/ -static int i82365_suspend(struct device *dev, pm_message_t state, u32 level) -{ - int ret = 0; - if (level == SUSPEND_SAVE_STATE) - ret = pcmcia_socket_dev_suspend(dev, state); - return ret; -} - -static int i82365_resume(struct device *dev, u32 level) -{ - int ret = 0; - if (level == RESUME_RESTORE_STATE) - ret = pcmcia_socket_dev_resume(dev); - return ret; -} - static struct device_driver i82365_driver = { .name = "i82365", .bus = &platform_bus_type, - .suspend = i82365_suspend, - .resume = i82365_resume, + .suspend = pcmcia_socket_dev_suspend, + .resume = pcmcia_socket_dev_resume, }; static struct platform_device i82365_device = { diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c --- a/drivers/pcmcia/m32r_cfc.c +++ b/drivers/pcmcia/m32r_cfc.c @@ -731,28 +731,11 @@ static struct pccard_operations pcc_oper /*====================================================================*/ -static int m32r_pcc_suspend(struct device *dev, pm_message_t state, u32 level) -{ - int ret = 0; - if (level == SUSPEND_SAVE_STATE) - ret = pcmcia_socket_dev_suspend(dev, state); - return ret; -} - -static int m32r_pcc_resume(struct device *dev, u32 level) -{ - int ret = 0; - if (level == RESUME_RESTORE_STATE) - ret = pcmcia_socket_dev_resume(dev); - return ret; -} - - static struct device_driver pcc_driver = { .name = "cfc", .bus = &platform_bus_type, - .suspend = m32r_pcc_suspend, - .resume = m32r_pcc_resume, + .suspend = pcmcia_socket_dev_suspend, + .resume = pcmcia_socket_dev_resume, }; static struct platform_device pcc_device = { diff --git a/drivers/pcmcia/m32r_pcc.c b/drivers/pcmcia/m32r_pcc.c --- a/drivers/pcmcia/m32r_pcc.c +++ b/drivers/pcmcia/m32r_pcc.c @@ -695,28 +695,11 @@ static struct pccard_operations pcc_oper /*====================================================================*/ -static int m32r_pcc_suspend(struct device *dev, pm_message_t state, u32 level) -{ - int ret = 0; - if (level == SUSPEND_SAVE_STATE) - ret = pcmcia_socket_dev_suspend(dev, state); - return ret; -} - -static int m32r_pcc_resume(struct device *dev, u32 level) -{ - int ret = 0; - if (level == RESUME_RESTORE_STATE) - ret = pcmcia_socket_dev_resume(dev); - return ret; -} - - static struct device_driver pcc_driver = { .name = "pcc", .bus = &platform_bus_type, - .suspend = m32r_pcc_suspend, - .resume = m32r_pcc_resume, + .suspend = pcmcia_socket_dev_suspend, + .resume = pcmcia_socket_dev_resume, }; static struct platform_device pcc_device = { diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c --- a/drivers/pcmcia/omap_cf.c +++ b/drivers/pcmcia/omap_cf.c @@ -329,27 +329,13 @@ static int __devexit omap_cf_remove(stru return 0; } -static int omap_cf_suspend(struct device *dev, pm_message_t mesg, u32 level) -{ - if (level != SUSPEND_SAVE_STATE) - return 0; - return pcmcia_socket_dev_suspend(dev, mesg); -} - -static int omap_cf_resume(struct device *dev, u32 level) -{ - if (level != RESUME_RESTORE_STATE) - return 0; - return pcmcia_socket_dev_resume(dev); -} - static struct device_driver omap_cf_driver = { .name = (char *) driver_name, .bus = &platform_bus_type, .probe = omap_cf_probe, .remove = __devexit_p(omap_cf_remove), - .suspend = omap_cf_suspend, - .resume = omap_cf_resume, + .suspend = pcmcia_socket_dev_suspend, + .resume = pcmcia_socket_dev_resume, }; static int __init omap_cf_init(void) diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -205,32 +205,20 @@ int pxa2xx_drv_pcmcia_probe(struct devic } EXPORT_SYMBOL(pxa2xx_drv_pcmcia_probe); -static int pxa2xx_drv_pcmcia_suspend(struct device *dev, pm_message_t state, u32 level) +static int pxa2xx_drv_pcmcia_resume(struct device *dev) { - int ret = 0; - if (level == SUSPEND_SAVE_STATE) - ret = pcmcia_socket_dev_suspend(dev, state); - return ret; -} + struct pcmcia_low_level *ops = dev->platform_data; + int nr = ops ? ops->nr : 0; -static int pxa2xx_drv_pcmcia_resume(struct device *dev, u32 level) -{ - int ret = 0; - if (level == RESUME_RESTORE_STATE) - { - struct pcmcia_low_level *ops = dev->platform_data; - int nr = ops ? ops->nr : 0; - - MECR = nr > 1 ? MECR_CIT | MECR_NOS : (nr > 0 ? MECR_CIT : 0); - ret = pcmcia_socket_dev_resume(dev); - } - return ret; + MECR = nr > 1 ? MECR_CIT | MECR_NOS : (nr > 0 ? MECR_CIT : 0); + + return pcmcia_socket_dev_resume(dev); } static struct device_driver pxa2xx_pcmcia_driver = { .probe = pxa2xx_drv_pcmcia_probe, .remove = soc_common_drv_pcmcia_remove, - .suspend = pxa2xx_drv_pcmcia_suspend, + .suspend = pcmcia_socket_dev_suspend, .resume = pxa2xx_drv_pcmcia_resume, .name = "pxa2xx-pcmcia", .bus = &platform_bus_type, diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c --- a/drivers/pcmcia/sa1100_generic.c +++ b/drivers/pcmcia/sa1100_generic.c @@ -74,29 +74,13 @@ static int sa11x0_drv_pcmcia_probe(struc return ret; } -static int sa11x0_drv_pcmcia_suspend(struct device *dev, pm_message_t state, u32 level) -{ - int ret = 0; - if (level == SUSPEND_SAVE_STATE) - ret = pcmcia_socket_dev_suspend(dev, state); - return ret; -} - -static int sa11x0_drv_pcmcia_resume(struct device *dev, u32 level) -{ - int ret = 0; - if (level == RESUME_RESTORE_STATE) - ret = pcmcia_socket_dev_resume(dev); - return ret; -} - static struct device_driver sa11x0_pcmcia_driver = { .probe = sa11x0_drv_pcmcia_probe, .remove = soc_common_drv_pcmcia_remove, .name = "sa11x0-pcmcia", .bus = &platform_bus_type, - .suspend = sa11x0_drv_pcmcia_suspend, - .resume = sa11x0_drv_pcmcia_resume, + .suspend = pcmcia_socket_dev_suspend, + .resume = pcmcia_socket_dev_resume, }; /* sa11x0_pcmcia_init() diff --git a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c --- a/drivers/pcmcia/tcic.c +++ b/drivers/pcmcia/tcic.c @@ -372,27 +372,11 @@ static int __init get_tcic_id(void) /*====================================================================*/ -static int tcic_drv_suspend(struct device *dev, pm_message_t state, u32 level) -{ - int ret = 0; - if (level == SUSPEND_SAVE_STATE) - ret = pcmcia_socket_dev_suspend(dev, state); - return ret; -} - -static int tcic_drv_resume(struct device *dev, u32 level) -{ - int ret = 0; - if (level == RESUME_RESTORE_STATE) - ret = pcmcia_socket_dev_resume(dev); - return ret; -} - static struct device_driver tcic_driver = { .name = "tcic-pcmcia", .bus = &platform_bus_type, - .suspend = tcic_drv_suspend, - .resume = tcic_drv_resume, + .suspend = pcmcia_socket_dev_suspend, + .resume = pcmcia_socket_dev_resume, }; static struct platform_device tcic_device = { diff --git a/drivers/pcmcia/vrc4171_card.c b/drivers/pcmcia/vrc4171_card.c --- a/drivers/pcmcia/vrc4171_card.c +++ b/drivers/pcmcia/vrc4171_card.c @@ -774,31 +774,11 @@ static int __devinit vrc4171_card_setup( __setup("vrc4171_card=", vrc4171_card_setup); -static int vrc4171_card_suspend(struct device *dev, pm_message_t state, u32 level) -{ - int retval = 0; - - if (level == SUSPEND_SAVE_STATE) - retval = pcmcia_socket_dev_suspend(dev, state); - - return retval; -} - -static int vrc4171_card_resume(struct device *dev, u32 level) -{ - int retval = 0; - - if (level == RESUME_RESTORE_STATE) - retval = pcmcia_socket_dev_resume(dev); - - return retval; -} - static struct device_driver vrc4171_card_driver = { .name = vrc4171_card_name, .bus = &platform_bus_type, - .suspend = vrc4171_card_suspend, - .resume = vrc4171_card_resume, + .suspend = pcmcia_socket_dev_suspend, + .resume = pcmcia_socket_dev_resume, }; static int __devinit vrc4171_card_init(void) diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -2358,13 +2358,10 @@ static int __devexit serial8250_remove(s return 0; } -static int serial8250_suspend(struct device *dev, pm_message_t state, u32 level) +static int serial8250_suspend(struct device *dev, pm_message_t state) { int i; - if (level != SUSPEND_DISABLE) - return 0; - for (i = 0; i < UART_NR; i++) { struct uart_8250_port *up = &serial8250_ports[i]; @@ -2375,13 +2372,10 @@ static int serial8250_suspend(struct dev return 0; } -static int serial8250_resume(struct device *dev, u32 level) +static int serial8250_resume(struct device *dev) { int i; - if (level != RESUME_ENABLE) - return 0; - for (i = 0; i < UART_NR; i++) { struct uart_8250_port *up = &serial8250_ports[i]; diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -895,21 +895,21 @@ static struct uart_driver imx_reg = { .cons = IMX_CONSOLE, }; -static int serial_imx_suspend(struct device *_dev, pm_message_t state, u32 level) +static int serial_imx_suspend(struct device *_dev, pm_message_t state) { struct imx_port *sport = dev_get_drvdata(_dev); - if (sport && level == SUSPEND_DISABLE) + if (sport) uart_suspend_port(&imx_reg, &sport->port); return 0; } -static int serial_imx_resume(struct device *_dev, u32 level) +static int serial_imx_resume(struct device *_dev) { struct imx_port *sport = dev_get_drvdata(_dev); - if (sport && level == RESUME_ENABLE) + if (sport) uart_resume_port(&imx_reg, &sport->port); return 0; diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -781,22 +781,22 @@ mpc52xx_uart_remove(struct device *dev) #ifdef CONFIG_PM static int -mpc52xx_uart_suspend(struct device *dev, pm_message_t state, u32 level) +mpc52xx_uart_suspend(struct device *dev, pm_message_t state) { struct uart_port *port = (struct uart_port *) dev_get_drvdata(dev); - if (sport && level == SUSPEND_DISABLE) + if (sport) uart_suspend_port(&mpc52xx_uart_driver, port); return 0; } static int -mpc52xx_uart_resume(struct device *dev, u32 level) +mpc52xx_uart_resume(struct device *dev) { struct uart_port *port = (struct uart_port *) dev_get_drvdata(dev); - if (port && level == RESUME_ENABLE) + if (port) uart_resume_port(&mpc52xx_uart_driver, port); return 0; diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c --- a/drivers/serial/pxa.c +++ b/drivers/serial/pxa.c @@ -786,21 +786,21 @@ static struct uart_driver serial_pxa_reg .cons = PXA_CONSOLE, }; -static int serial_pxa_suspend(struct device *_dev, pm_message_t state, u32 level) +static int serial_pxa_suspend(struct device *_dev, pm_message_t state) { struct uart_pxa_port *sport = dev_get_drvdata(_dev); - if (sport && level == SUSPEND_DISABLE) + if (sport) uart_suspend_port(&serial_pxa_reg, &sport->port); return 0; } -static int serial_pxa_resume(struct device *_dev, u32 level) +static int serial_pxa_resume(struct device *_dev) { struct uart_pxa_port *sport = dev_get_drvdata(_dev); - if (sport && level == RESUME_ENABLE) + if (sport) uart_resume_port(&serial_pxa_reg, &sport->port); return 0; diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c --- a/drivers/serial/s3c2410.c +++ b/drivers/serial/s3c2410.c @@ -1134,22 +1134,22 @@ int s3c24xx_serial_remove(struct device #ifdef CONFIG_PM -int s3c24xx_serial_suspend(struct device *dev, pm_message_t state, u32 level) +int s3c24xx_serial_suspend(struct device *dev, pm_message_t state) { struct uart_port *port = s3c24xx_dev_to_port(dev); - if (port && level == SUSPEND_DISABLE) + if (port) uart_suspend_port(&s3c24xx_uart_drv, port); return 0; } -int s3c24xx_serial_resume(struct device *dev, u32 level) +int s3c24xx_serial_resume(struct device *dev) { struct uart_port *port = s3c24xx_dev_to_port(dev); struct s3c24xx_uart_port *ourport = to_ourport(port); - if (port && level == RESUME_ENABLE) { + if (port) { clk_enable(ourport->clk); s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port)); clk_disable(ourport->clk); diff --git a/drivers/serial/sa1100.c b/drivers/serial/sa1100.c --- a/drivers/serial/sa1100.c +++ b/drivers/serial/sa1100.c @@ -834,21 +834,21 @@ static struct uart_driver sa1100_reg = { .cons = SA1100_CONSOLE, }; -static int sa1100_serial_suspend(struct device *_dev, pm_message_t state, u32 level) +static int sa1100_serial_suspend(struct device *_dev, pm_message_t state) { struct sa1100_port *sport = dev_get_drvdata(_dev); - if (sport && level == SUSPEND_DISABLE) + if (sport) uart_suspend_port(&sa1100_reg, &sport->port); return 0; } -static int sa1100_serial_resume(struct device *_dev, u32 level) +static int sa1100_serial_resume(struct device *_dev) { struct sa1100_port *sport = dev_get_drvdata(_dev); - if (sport && level == RESUME_ENABLE) + if (sport) uart_resume_port(&sa1100_reg, &sport->port); return 0; diff --git a/drivers/serial/vr41xx_siu.c b/drivers/serial/vr41xx_siu.c --- a/drivers/serial/vr41xx_siu.c +++ b/drivers/serial/vr41xx_siu.c @@ -976,14 +976,11 @@ static int siu_remove(struct device *dev return 0; } -static int siu_suspend(struct device *dev, pm_message_t state, u32 level) +static int siu_suspend(struct device *dev, pm_message_t state) { struct uart_port *port; int i; - if (level != SUSPEND_DISABLE) - return 0; - for (i = 0; i < siu_uart_driver.nr; i++) { port = &siu_uart_ports[i]; if ((port->type == PORT_VR41XX_SIU || @@ -995,14 +992,11 @@ static int siu_suspend(struct device *de return 0; } -static int siu_resume(struct device *dev, u32 level) +static int siu_resume(struct device *dev) { struct uart_port *port; int i; - if (level != RESUME_ENABLE) - return 0; - for (i = 0; i < siu_uart_driver.nr; i++) { port = &siu_uart_ports[i]; if ((port->type == PORT_VR41XX_SIU || diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -935,14 +935,10 @@ static int dummy_udc_remove (struct devi return 0; } -static int dummy_udc_suspend (struct device *dev, pm_message_t state, - u32 level) +static int dummy_udc_suspend (struct device *dev, pm_message_t state) { struct dummy *dum = dev_get_drvdata(dev); - if (level != SUSPEND_DISABLE) - return 0; - dev_dbg (dev, "%s\n", __FUNCTION__); spin_lock_irq (&dum->lock); dum->udc_suspended = 1; @@ -954,13 +950,10 @@ static int dummy_udc_suspend (struct dev return 0; } -static int dummy_udc_resume (struct device *dev, u32 level) +static int dummy_udc_resume (struct device *dev) { struct dummy *dum = dev_get_drvdata(dev); - if (level != RESUME_ENABLE) - return 0; - dev_dbg (dev, "%s\n", __FUNCTION__); spin_lock_irq (&dum->lock); dum->udc_suspended = 0; @@ -1936,14 +1929,10 @@ static int dummy_hcd_remove (struct devi return 0; } -static int dummy_hcd_suspend (struct device *dev, pm_message_t state, - u32 level) +static int dummy_hcd_suspend (struct device *dev, pm_message_t state) { struct usb_hcd *hcd; - if (level != SUSPEND_DISABLE) - return 0; - dev_dbg (dev, "%s\n", __FUNCTION__); hcd = dev_get_drvdata (dev); @@ -1958,13 +1947,10 @@ static int dummy_hcd_suspend (struct dev return 0; } -static int dummy_hcd_resume (struct device *dev, u32 level) +static int dummy_hcd_resume (struct device *dev) { struct usb_hcd *hcd; - if (level != RESUME_ENABLE) - return 0; - dev_dbg (dev, "%s\n", __FUNCTION__); hcd = dev_get_drvdata (dev); hcd->state = HC_STATE_RUNNING; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2909,12 +2909,10 @@ static int __exit omap_udc_remove(struct * may involve talking to an external transceiver (e.g. isp1301). */ -static int omap_udc_suspend(struct device *dev, pm_message_t message, u32 level) +static int omap_udc_suspend(struct device *dev, pm_message_t message) { u32 devstat; - if (level != SUSPEND_POWER_DOWN) - return 0; devstat = UDC_DEVSTAT_REG; /* we're requesting 48 MHz clock if the pullup is enabled @@ -2931,11 +2929,8 @@ static int omap_udc_suspend(struct devic return 0; } -static int omap_udc_resume(struct device *dev, u32 level) +static int omap_udc_resume(struct device *dev) { - if (level != RESUME_POWER_ON) - return 0; - DBG("resume + wakeup/SRP\n"); omap_pullup(&udc->gadget, 1); diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c @@ -2602,24 +2602,23 @@ static int __exit pxa2xx_udc_remove(stru * VBUS IRQs should probably be ignored so that the PXA device just acts * "dead" to USB hosts until system resume. */ -static int pxa2xx_udc_suspend(struct device *dev, pm_message_t state, u32 level) +static int pxa2xx_udc_suspend(struct device *dev, pm_message_t state) { struct pxa2xx_udc *udc = dev_get_drvdata(dev); - if (level == SUSPEND_POWER_DOWN) { - if (!udc->mach->udc_command) - WARN("USB host won't detect disconnect!\n"); - pullup(udc, 0); - } + if (!udc->mach->udc_command) + WARN("USB host won't detect disconnect!\n"); + pullup(udc, 0); + return 0; } -static int pxa2xx_udc_resume(struct device *dev, u32 level) +static int pxa2xx_udc_resume(struct device *dev) { struct pxa2xx_udc *udc = dev_get_drvdata(dev); - if (level == RESUME_POWER_ON) - pullup(udc, 1); + pullup(udc, 1); + return 0; } diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1773,15 +1773,12 @@ static int __init isp116x_probe(struct d /* Suspend of platform device */ -static int isp116x_suspend(struct device *dev, pm_message_t state, u32 phase) +static int isp116x_suspend(struct device *dev, pm_message_t state) { int ret = 0; struct usb_hcd *hcd = dev_get_drvdata(dev); - VDBG("%s: state %x, phase %x\n", __func__, state, phase); - - if (phase != SUSPEND_DISABLE && phase != SUSPEND_POWER_DOWN) - return 0; + VDBG("%s: state %x\n", __func__, state); ret = usb_suspend_device(hcd->self.root_hub, state); if (!ret) { @@ -1796,15 +1793,12 @@ static int isp116x_suspend(struct device /* Resume platform device */ -static int isp116x_resume(struct device *dev, u32 phase) +static int isp116x_resume(struct device *dev) { int ret = 0; struct usb_hcd *hcd = dev_get_drvdata(dev); - VDBG("%s: state %x, phase %x\n", __func__, dev->power.power_state, - phase); - if (phase != RESUME_POWER_ON) - return 0; + VDBG("%s: state %x\n", __func__, dev->power.power_state); ret = usb_resume_device(hcd->self.root_hub); if (!ret) { diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -455,14 +455,11 @@ static int ohci_hcd_omap_drv_remove(stru #ifdef CONFIG_PM -static int ohci_omap_suspend(struct device *dev, pm_message_t message, u32 level) +static int ohci_omap_suspend(struct device *dev, pm_message_t message) { struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); int status = -EINVAL; - if (level != SUSPEND_POWER_DOWN) - return 0; - down(&ohci_to_hcd(ohci)->self.root_hub->serialize); status = ohci_hub_suspend(ohci_to_hcd(ohci)); if (status == 0) { @@ -476,14 +473,11 @@ static int ohci_omap_suspend(struct devi return status; } -static int ohci_omap_resume(struct device *dev, u32 level) +static int ohci_omap_resume(struct device *dev) { struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); int status = 0; - if (level != RESUME_POWER_ON) - return 0; - if (time_before(jiffies, ohci->next_statechange)) msleep(5); ohci->next_statechange = jiffies; diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1784,15 +1784,12 @@ sl811h_probe(struct device *dev) */ static int -sl811h_suspend(struct device *dev, pm_message_t state, u32 phase) +sl811h_suspend(struct device *dev, pm_message_t state) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct sl811 *sl811 = hcd_to_sl811(hcd); int retval = 0; - if (phase != SUSPEND_POWER_DOWN) - return retval; - if (state.event == PM_EVENT_FREEZE) retval = sl811h_hub_suspend(hcd); else if (state.event == PM_EVENT_SUSPEND) @@ -1803,14 +1800,11 @@ sl811h_suspend(struct device *dev, pm_me } static int -sl811h_resume(struct device *dev, u32 phase) +sl811h_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct sl811 *sl811 = hcd_to_sl811(hcd); - if (phase != RESUME_POWER_ON) - return 0; - /* with no "check to see if VBUS is still powered" board hook, * let's assume it'd only be powered to enable remote wakeup. */ diff --git a/drivers/video/backlight/corgi_bl.c b/drivers/video/backlight/corgi_bl.c --- a/drivers/video/backlight/corgi_bl.c +++ b/drivers/video/backlight/corgi_bl.c @@ -73,17 +73,15 @@ static void corgibl_blank(int blank) } #ifdef CONFIG_PM -static int corgibl_suspend(struct device *dev, pm_message_t state, u32 level) +static int corgibl_suspend(struct device *dev, pm_message_t state) { - if (level == SUSPEND_POWER_DOWN) - corgibl_blank(FB_BLANK_POWERDOWN); + corgibl_blank(FB_BLANK_POWERDOWN); return 0; } -static int corgibl_resume(struct device *dev, u32 level) +static int corgibl_resume(struct device *dev) { - if (level == RESUME_POWER_ON) - corgibl_blank(FB_BLANK_UNBLANK); + corgibl_blank(FB_BLANK_UNBLANK); return 0; } #else diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -424,23 +424,21 @@ static void imxfb_setup_gpio(struct imxf * Power management hooks. Note that we won't be called from IRQ context, * unlike the blank functions above, so we may sleep. */ -static int imxfb_suspend(struct device *dev, pm_message_t state, u32 level) +static int imxfb_suspend(struct device *dev, pm_message_t state) { struct imxfb_info *fbi = dev_get_drvdata(dev); pr_debug("%s\n",__FUNCTION__); - if (level == SUSPEND_DISABLE || level == SUSPEND_POWER_DOWN) - imxfb_disable_controller(fbi); + imxfb_disable_controller(fbi); return 0; } -static int imxfb_resume(struct device *dev, u32 level) +static int imxfb_resume(struct device *dev) { struct imxfb_info *fbi = dev_get_drvdata(dev); pr_debug("%s\n",__FUNCTION__); - if (level == RESUME_ENABLE) - imxfb_enable_controller(fbi); + imxfb_enable_controller(fbi); return 0; } #else diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -981,21 +981,19 @@ pxafb_freq_policy(struct notifier_block * Power management hooks. Note that we won't be called from IRQ context, * unlike the blank functions above, so we may sleep. */ -static int pxafb_suspend(struct device *dev, pm_message_t state, u32 level) +static int pxafb_suspend(struct device *dev, pm_message_t state) { struct pxafb_info *fbi = dev_get_drvdata(dev); - if (level == SUSPEND_DISABLE || level == SUSPEND_POWER_DOWN) - set_ctrlr_state(fbi, C_DISABLE_PM); + set_ctrlr_state(fbi, C_DISABLE_PM); return 0; } -static int pxafb_resume(struct device *dev, u32 level) +static int pxafb_resume(struct device *dev) { struct pxafb_info *fbi = dev_get_drvdata(dev); - if (level == RESUME_ENABLE) - set_ctrlr_state(fbi, C_ENABLE_PM); + set_ctrlr_state(fbi, C_ENABLE_PM); return 0; } #else diff --git a/drivers/video/s1d13xxxfb.c b/drivers/video/s1d13xxxfb.c --- a/drivers/video/s1d13xxxfb.c +++ b/drivers/video/s1d13xxxfb.c @@ -655,7 +655,7 @@ bail: } #ifdef CONFIG_PM -static int s1d13xxxfb_suspend(struct device *... [truncated message content] |
From: Russell K. <rmk...@ar...> - 2005-10-06 18:30:05
|
On Thu, Oct 06, 2005 at 07:23:48PM +0100, Mark Underwood wrote: > --- David Brownell <da...@pa...> wrote: > > Vitaly ... comments from Russell and Pavel both addresses your comments > > about that obsolete parameter. What letter? The one I remember was > > one responding to Mark Underwood (?) where you complained about issuing > > three calls for one suspend event. You can't have it both ways!! > > Either that parameter should be used in the documented way (call the > > suspend method three times, one right after another) or it should be used > > more sanely (parameter is constant. > > Yes, that was in reply to my SPI subsystem patch set (in which Vitaly > didn't like the fact that I call suspend/resume 3 times) and then in > the same thread (in answer to David's response of dropping this as he > didn't think anyone would mind this) Vitaly said that you can't do this. Vitaly has a problem then. We must _not_ call suspend three times just because it has different "levels" - SUSPEND_DISABLE, SUSPEND_SAVE_STATE and SUSPEND_POWER_DOWN. As I've said earlier in the thread, the only reason these exist is because no one has gone to the effort of cleaning up the crap left behind from PM version 1 for the platform devices. When PM v2 happened, I just hacked the platform device drivers to work with this new model. So please consider the three argument suspend callback a legacy feature and if you're going to use it, call it exactly once. And please document that this is the case for your bus type, and that the "level" argument is meaningless. Better still, please do not use the device_driver suspend/resume pointers at all. Same argument applies - only platform devices use them, and these should eventually be killed off. -- Russell King Linux kernel 2.6 ARM Linux - http://www.arm.linux.org.uk/ maintainer of: 2.6 Serial core |
From: Mark U. <bas...@ya...> - 2005-10-06 18:23:59
|
--- David Brownell <da...@pa...> wrote: > > > >+/* Suspend/resume in "struct device_driver" don't really need that > > > >+ * strange third parameter, so we just make it a constant and expect > > > >+ * SPI drivers to ignore it just like most platform drivers do. > > > >+ * > > > > > > So you just ignored my letter on that subject :( > > > The fact that you don't need it doesn't mean that other people won't. > > > The fact that there's no clean way to suspend USB doesn't mean that > > > there shouldn't be one for SPI. > > > > The third parameter is obsolete and should only be used to select _one_ > > of the tree suspend calls you will get. > > Vitaly ... comments from Russell and Pavel both addresses your comments > about that obsolete parameter. What letter? The one I remember was > one responding to Mark Underwood (?) where you complained about issuing > three calls for one suspend event. You can't have it both ways!! > Either that parameter should be used in the documented way (call the > suspend method three times, one right after another) or it should be used > more sanely (parameter is constant. Yes, that was in reply to my SPI subsystem patch set (in which Vitaly didn't like the fact that I call suspend/resume 3 times) and then in the same thread (in answer to David's response of dropping this as he didn't think anyone would mind this) Vitaly said that you can't do this. > > USB can suspend just fine, thank you, though starting with 2.6.12 some > bugs seem to have crept in; fixes are in the 2.6.15 prepatchces. > > > > Any additional suspend calls should _not_ create extra usage of this > > parameter. It's a left over from Pat's first driver model incarnation > > which is specific to the platform device drivers. (Mainly it exists > > because no one can be bothered to clean it up.) > > Most folk who've considered the question would like to see it go away. > Except ... making sure every driver in a few dozen architectures still > builds after removing that parameter is more than the usual amount of > janitorial work! > > Progress could start by updating Documentation/driver-model/driver.txt to > say "don't test that parameter", reducing future confusion on this point. Thank you! That would clear up this confusion :). > > - Dave > > - > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to maj...@vg... > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ > ___________________________________________________________ To help you stay safe and secure online, we've developed the all new Yahoo! Security Centre. http://uk.security.yahoo.com |