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: David B. <da...@pa...> - 2005-11-30 08:05:33
|
On Tuesday 29 November 2005 10:37 pm, Vitaly Wool wrote: > Yeah... the other limitation is that if there's more than one SPI > controller onboard, spi_write_then_read will serialize the transfers I hope you're not trying to claim that -- if that really matters -- it'd take more than a spare thought to change that so it kmallocs a pointer. (You _could_ get smart and kmalloc one only if the pre-allocated one was being used...) > One more limitation of your SPI core: it cannot be compiled as a module. > What are the reasons behind that BTW? The spi_board_info stuff has to be called at arch_initcall() time along with all the other arch/.../mach-*/board-*.c board-specific init, so those parts clearly can't be in a module. That's not much code though; I think it was in a separate file very early, but that got annoying to work with. (The pieces probably fit together now though.) The rest just didn't seem to matter. I suppose that could be switched around so it's modular ... do you think that's important at all? If so, you know the drill: feel free to submit a patch! 2KB code (on ARM) didn't seem like much static code to worry about; I've saved more code than that just by space-tuning some drivers. ISTR things often get bigger when they link as modules. Anyway, keeping it all in one file was a reasonable incentive towards staying small. :) - Dave |
From: Vitaly W. <vw...@ru...> - 2005-11-30 06:37:19
|
Yeah... the other limitation is that if there's more than one SPI controller onboard, spi_write_then_read will serialize the transfers related to two different controllers what will have significant negative impact on the transfer speed (so DMA won't help increasing the speed in this case). Moreover, if, say, two kernel threads with different priorities are working with two SPI controllers respectively *priority inversion* may happen. One more limitation of your SPI core: it cannot be compiled as a module. What are the reasons behind that BTW? Vitaly David Brownell wrote: >On Tuesday 29 November 2005 10:25 am, Vitaly Wool wrote: > > > >>So spi_write_then_read() can't ever be called from an interrupt context, >>right? >> >> > >Right ... that's true of all the synchronous calls. Kerneldoc for that >one didn't highlight that fact though, so I've updated it. > >- Dave > > > > |
From: David B. <da...@pa...> - 2005-11-29 19:12:46
|
On Tuesday 29 November 2005 10:25 am, Vitaly Wool wrote: > So spi_write_then_read() can't ever be called from an interrupt context, > right? Right ... that's true of all the synchronous calls. Kerneldoc for that one didn't highlight that fact though, so I've updated it. - Dave |
From: Vitaly W. <vw...@ru...> - 2005-11-29 18:25:57
|
David Brownell wrote: >>You need to create the semaphore to protect >>the global 'buf', so if you have two controllers on the same board, you >>would not perform two simultaneous transfers with these controllers, >>right ? >> >> > >Yes, there IS a semaphore: > >+int spi_write_then_read(...) >+{ >+ static DECLARE_MUTEX(lock); >+ ... >+ down(&lock); >+ ... >+ up(&lock); >+ return status; >+} > > > So spi_write_then_read() can't ever be called from an interrupt context, right? Vitaly |
From: David B. <da...@pa...> - 2005-11-29 17:05:11
|
On Tuesday 29 November 2005 7:54 am, dmitry pervushin wrote: > David, > > some additional comments on your code: > > - you've made spi_read_then_write DMA-capable... OK. But why you're > using global for buffer ? To avoid a kmalloc()/kfree() overhead on each call, and keep this first version of the framework simple and small. And correct. > You need to create the semaphore to protect > the global 'buf', so if you have two controllers on the same board, you > would not perform two simultaneous transfers with these controllers, > right ? Yes, there IS a semaphore: +int spi_write_then_read(...) +{ + static DECLARE_MUTEX(lock); + ... + down(&lock); + ... + up(&lock); + return status; +} If "one caller at a time" for that routine turns out to be a problem, it's easily changed. So far, it hasn't been one; maybe it will be later. > I's like to say that buf should be either controller data or > message data The spi_message is allocated on the stack, which is inappropriate for source/destination of a DMA buffer; that's not an option that avoids kmalloc. I'm not sure what you mean about "controller data". It can't be spi_device->controller_data since that's reserved for board specific static state (e.g. chipselect callbacks, for boards that use GPIOs instead of controller registers). > - sometimes you're ignoring return codes of functions that return int, > e.g., bus_register and class_register Those two calls are in spi_init(), where they're unlikely to fail. But you're right, the return codes SHOULD be used; feel free to submit a patch. - Dave |
From: dmitry p. <dpe...@gm...> - 2005-11-29 16:01:51
|
David, some additional comments on your code: - you've made spi_read_then_write DMA-capable... OK. But why you're using global for buffer ? You need to create the semaphore to protect the global 'buf', so if you have two controllers on the same board, you would not perform two simultaneous transfers with these controllers, right ? I's like to say that buf should be either controller data or message data - sometimes you're ignoring return codes of functions that return int, e.g., bus_register and class_register On Mon, 2005-11-28 at 23:36 -0800, David Brownell wrote: > On Monday 28 November 2005 9:14 pm, Vitaly Wool wrote: > > Hi David, > > > > there're a few things I still dislike in your core. Namely, > > > > - leaving too much for the controller driver. I am sure that the core > > should provide default techniques for queue handling, it's a common > > thing to do > > Well, every driver needs _some_ sort of queue handling, but they > don't all need the same handling. I said more earlier: > > http://marc.theaimsgroup.com/?l=linux-kernel&m=112684135722116&w=2 > > And in any case, this takes _maybe_ a couple dozen lines for one > simple driver style, which is IMO clearly on the "don't need > to share code" side of things. It's a trivial price to pay for > that level of flexibility. > > > > - you call wait_for_completion() in the core > > Not exactly. Those synchronous calls are non-essential wrappers, > so I don't think of them as "core". Though one could argue; it's > still true those calls could easily end up unused, and thus be > candidates for link-time dead code removal. > > > > and leave the complete() > > call fot the controller driver. This is IMHO a good way to produce > > erroneous code > > The requirement is simple: for each transfer() call that the controller > driver accepts, it will issue the relevant completion callback once when > that message has been completed. It's the same model used with USB > (both host and peripheral sides), and it's the same model used in various > other systems too. > > It's a simple rule, very easy to understand ... that's what makes it a > good way to **eliminate** coding errors. There's no possible ambiguity > there; any time that rule isn't followed would clearly be a fundamental > driver bug that would cause extensive breakage ... nothing subtle or > hard to find. And there are no special cases to confuse anyone. > > In fact any sane controller driver is going to have one single call > to spi_message->complete(), so if there are more (or less) that'll > be something suspicious for any code reviewer to notice quickly. > > - Dave > > > > > I also think that callbacks for retrieving next message from queue is > > better way to do it. > > > > Greg, BTW, can you please share the review results? > > > > Best regards, > > Vitaly > > > > David Brownell wrote: > > > > >On Wednesday 23 November 2005 2:01 pm, David Brownell wrote: > > > > > > > > >>On Friday 11 November 2005 1:32 am, dmitry pervushin 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 :) > > >>> > > >>> > > >>Agreed. > > >> > > >> > > > > > >And I see Andrew merged my framework into MM, and that it's already > > >gotten some patches. I think that means the next MM kernel (rc2-mm2?) > > >will be a good base for that consolidation. (Dunno about 2.6.16...) > > > > > > > > >What this suggests to me is that there may need to be a few more > > >patches on the way. Drivers, especially ... but also a few in the > > >framework itself. I fully expect someone to want: > > > > > > > > > > > >> - Yours has a "struct spi_driver", mine doesn't. It's probably > > >> inevitable, though not currently needed. Likewise mine has > > >> the struct driver.suspend() and resume() changes, yours doesn't. > > >> > > >> > > > > > >That's in the "best done early" category. > > > > > > > > >Sometime after Stephen posts his updated pxa2xx_spi driver, I'll > > >be able to post the Lubbock board-specific patch ... that'll mean > > >one "reference" board (albeit an old one) has out-of-the-box support > > >on Linux. Given the number of PXA25x systems with an ADS7846, I'd > > >hope to see some testers of that stack (and, surely, bugfixes). > > > > > >One of the things that would IMO help here is getting a bitbanging > > >adapter driver: rub four GPIO pins together, make a fire. ;) > > > > > >If anyone's at work on such a thing (Mark?) please drop a line; > > >it looks to me like this should be very simple, and incidentally > > >help nail down any semantics that aren't described in the docs. > > > > > >- Dave > > > > > > > > > > > ------------------------------------------------------- > This SF.net email is sponsored by: Splunk Inc. Do you grep through log files > for problems? Stop! Download the new AJAX search engine that makes > searching your log files as easy as surfing the web. DOWNLOAD SPLUNK! > http://ads.osdn.com/?ad_id=7637&alloc_id=16865&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-29 08:05:16
|
On Mon, Nov 28, 2005 at 11:04:49PM -0800, Komal Shah wrote: > --- Greg KH <gr...@kr...> wrote: > > > > > > > Greg, BTW, can you please share the review results? > > > > What "review results"? I just got back from vacation... > > Oh!. He is asking for review comments for SPI frameworks by David > Brownell and Vitaly wool + Dmitry Pervushin, which was sent to you by David. Heh, I know what he was referring to, just trying to state why I haven't had the time to look at the code yet. Hopefully tomorrow... thanks, greg k-h |
From: Mark U. <bas...@ya...> - 2005-11-29 07:56:41
|
Hi David, How did you guess :). I'm currently working on a bitbanging driver with parport support. However, I am currently stuck when unregistering my device and am unsure if this is the SPI framework or just my driver. My Linux machine is 'ill' at the moment but I'll get what I have so far and mail it (do you want me to send it to the LKML or just to you seeing as it is still incomplete). Mark --- David Brownell <da...@pa...> wrote: > On Wednesday 23 November 2005 2:01 pm, David Brownell wrote: > > On Friday 11 November 2005 1:32 am, dmitry pervushin 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 :) > > > > Agreed. > > And I see Andrew merged my framework into MM, and that it's already > gotten some patches. I think that means the next MM kernel (rc2-mm2?) > will be a good base for that consolidation. (Dunno about 2.6.16...) > > > What this suggests to me is that there may need to be a few more > patches on the way. Drivers, especially ... but also a few in the > framework itself. I fully expect someone to want: > > > - Yours has a "struct spi_driver", mine doesn't. It's probably > > inevitable, though not currently needed. Likewise mine has > > the struct driver.suspend() and resume() changes, yours doesn't. > > That's in the "best done early" category. > > > Sometime after Stephen posts his updated pxa2xx_spi driver, I'll > be able to post the Lubbock board-specific patch ... that'll mean > one "reference" board (albeit an old one) has out-of-the-box support > on Linux. Given the number of PXA25x systems with an ADS7846, I'd > hope to see some testers of that stack (and, surely, bugfixes). > > One of the things that would IMO help here is getting a bitbanging > adapter driver: rub four GPIO pins together, make a fire. ;) > > If anyone's at work on such a thing (Mark?) please drop a line; > it looks to me like this should be very simple, and incidentally > help nail down any semantics that aren't described in the docs. > > - Dave > > ___________________________________________________________ To help you stay safe and secure online, we've developed the all new Yahoo! Security Centre. http://uk.security.yahoo.com |
From: David B. <da...@pa...> - 2005-11-29 07:36:40
|
On Monday 28 November 2005 9:14 pm, Vitaly Wool wrote: > Hi David, > > there're a few things I still dislike in your core. Namely, > > - leaving too much for the controller driver. I am sure that the core > should provide default techniques for queue handling, it's a common > thing to do Well, every driver needs _some_ sort of queue handling, but they don't all need the same handling. I said more earlier: http://marc.theaimsgroup.com/?l=linux-kernel&m=112684135722116&w=2 And in any case, this takes _maybe_ a couple dozen lines for one simple driver style, which is IMO clearly on the "don't need to share code" side of things. It's a trivial price to pay for that level of flexibility. > - you call wait_for_completion() in the core Not exactly. Those synchronous calls are non-essential wrappers, so I don't think of them as "core". Though one could argue; it's still true those calls could easily end up unused, and thus be candidates for link-time dead code removal. > and leave the complete() > call fot the controller driver. This is IMHO a good way to produce > erroneous code The requirement is simple: for each transfer() call that the controller driver accepts, it will issue the relevant completion callback once when that message has been completed. It's the same model used with USB (both host and peripheral sides), and it's the same model used in various other systems too. It's a simple rule, very easy to understand ... that's what makes it a good way to **eliminate** coding errors. There's no possible ambiguity there; any time that rule isn't followed would clearly be a fundamental driver bug that would cause extensive breakage ... nothing subtle or hard to find. And there are no special cases to confuse anyone. In fact any sane controller driver is going to have one single call to spi_message->complete(), so if there are more (or less) that'll be something suspicious for any code reviewer to notice quickly. - Dave > I also think that callbacks for retrieving next message from queue is > better way to do it. > > Greg, BTW, can you please share the review results? > > Best regards, > Vitaly > > David Brownell wrote: > > >On Wednesday 23 November 2005 2:01 pm, David Brownell wrote: > > > > > >>On Friday 11 November 2005 1:32 am, dmitry pervushin 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 :) > >>> > >>> > >>Agreed. > >> > >> > > > >And I see Andrew merged my framework into MM, and that it's already > >gotten some patches. I think that means the next MM kernel (rc2-mm2?) > >will be a good base for that consolidation. (Dunno about 2.6.16...) > > > > > >What this suggests to me is that there may need to be a few more > >patches on the way. Drivers, especially ... but also a few in the > >framework itself. I fully expect someone to want: > > > > > > > >> - Yours has a "struct spi_driver", mine doesn't. It's probably > >> inevitable, though not currently needed. Likewise mine has > >> the struct driver.suspend() and resume() changes, yours doesn't. > >> > >> > > > >That's in the "best done early" category. > > > > > >Sometime after Stephen posts his updated pxa2xx_spi driver, I'll > >be able to post the Lubbock board-specific patch ... that'll mean > >one "reference" board (albeit an old one) has out-of-the-box support > >on Linux. Given the number of PXA25x systems with an ADS7846, I'd > >hope to see some testers of that stack (and, surely, bugfixes). > > > >One of the things that would IMO help here is getting a bitbanging > >adapter driver: rub four GPIO pins together, make a fire. ;) > > > >If anyone's at work on such a thing (Mark?) please drop a line; > >it looks to me like this should be very simple, and incidentally > >help nail down any semantics that aren't described in the docs. > > > >- Dave > > > > > |
From: Vitaly W. <vw...@ru...> - 2005-11-29 07:19:48
|
Ah, excuse me, I wasn't aware, I thought you were reviewing the cores me and David have sent to you... :) Vitaly Greg KH wrote: >On Tue, Nov 29, 2005 at 08:14:05AM +0300, Vitaly Wool wrote: > > >>Hi David, >> >>there're a few things I still dislike in your core. Namely, >> >>- leaving too much for the controller driver. I am sure that the core >>should provide default techniques for queue handling, it's a common >>thing to do >>- you call wait_for_completion() in the core and leave the complete() >>call fot the controller driver. This is IMHO a good way to produce >>erroneous code >> >>I also think that callbacks for retrieving next message from queue is >>better way to do it. >> >>Greg, BTW, can you please share the review results? >> >> > >What "review results"? I just got back from vacation... > >thanks, > >greg k-h > > > > |
From: Komal S. <kom...@ya...> - 2005-11-29 07:04:59
|
--- Greg KH <gr...@kr...> wrote: > > > > Greg, BTW, can you please share the review results? > > What "review results"? I just got back from vacation... Oh!. He is asking for review comments for SPI frameworks by David Brownell and Vitaly wool + Dmitry Pervushin, which was sent to you by David. ---Komal Shah http://komalshah.blogspot.com/ __________________________________ Yahoo! Mail - PC Magazine Editors' Choice 2005 http://mail.yahoo.com |
From: Greg KH <gr...@kr...> - 2005-11-29 07:01:26
|
On Tue, Nov 29, 2005 at 08:14:05AM +0300, Vitaly Wool wrote: > Hi David, > > there're a few things I still dislike in your core. Namely, > > - leaving too much for the controller driver. I am sure that the core > should provide default techniques for queue handling, it's a common > thing to do > - you call wait_for_completion() in the core and leave the complete() > call fot the controller driver. This is IMHO a good way to produce > erroneous code > > I also think that callbacks for retrieving next message from queue is > better way to do it. > > Greg, BTW, can you please share the review results? What "review results"? I just got back from vacation... thanks, greg k-h |
From: Vitaly W. <vw...@ru...> - 2005-11-29 05:14:18
|
Hi David, there're a few things I still dislike in your core. Namely, - leaving too much for the controller driver. I am sure that the core should provide default techniques for queue handling, it's a common thing to do - you call wait_for_completion() in the core and leave the complete() call fot the controller driver. This is IMHO a good way to produce erroneous code I also think that callbacks for retrieving next message from queue is better way to do it. Greg, BTW, can you please share the review results? Best regards, Vitaly David Brownell wrote: >On Wednesday 23 November 2005 2:01 pm, David Brownell wrote: > > >>On Friday 11 November 2005 1:32 am, dmitry pervushin 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 :) >>> >>> >>Agreed. >> >> > >And I see Andrew merged my framework into MM, and that it's already >gotten some patches. I think that means the next MM kernel (rc2-mm2?) >will be a good base for that consolidation. (Dunno about 2.6.16...) > > >What this suggests to me is that there may need to be a few more >patches on the way. Drivers, especially ... but also a few in the >framework itself. I fully expect someone to want: > > > >> - Yours has a "struct spi_driver", mine doesn't. It's probably >> inevitable, though not currently needed. Likewise mine has >> the struct driver.suspend() and resume() changes, yours doesn't. >> >> > >That's in the "best done early" category. > > >Sometime after Stephen posts his updated pxa2xx_spi driver, I'll >be able to post the Lubbock board-specific patch ... that'll mean >one "reference" board (albeit an old one) has out-of-the-box support >on Linux. Given the number of PXA25x systems with an ADS7846, I'd >hope to see some testers of that stack (and, surely, bugfixes). > >One of the things that would IMO help here is getting a bitbanging >adapter driver: rub four GPIO pins together, make a fire. ;) > >If anyone's at work on such a thing (Mark?) please drop a line; >it looks to me like this should be very simple, and incidentally >help nail down any semantics that aren't described in the docs. > >- Dave > > > >------------------------------------------------------- >This SF.net email is sponsored by: Splunk Inc. Do you grep through log files >for problems? Stop! Download the new AJAX search engine that makes >searching your log files as easy as surfing the web. DOWNLOAD SPLUNK! >http://ads.osdn.com/?ad_id=7637&alloc_id=16865&op=click >_______________________________________________ >spi-devel-general mailing list >spi...@li... >https://lists.sourceforge.net/lists/listinfo/spi-devel-general > > > > |
From: David B. <da...@pa...> - 2005-11-28 20:59:44
|
On Wednesday 23 November 2005 2:01 pm, David Brownell wrote: > On Friday 11 November 2005 1:32 am, dmitry pervushin 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 :) > > Agreed. And I see Andrew merged my framework into MM, and that it's already gotten some patches. I think that means the next MM kernel (rc2-mm2?) will be a good base for that consolidation. (Dunno about 2.6.16...) What this suggests to me is that there may need to be a few more patches on the way. Drivers, especially ... but also a few in the framework itself. I fully expect someone to want: > - Yours has a "struct spi_driver", mine doesn't. It's probably > inevitable, though not currently needed. Likewise mine has > the struct driver.suspend() and resume() changes, yours doesn't. That's in the "best done early" category. Sometime after Stephen posts his updated pxa2xx_spi driver, I'll be able to post the Lubbock board-specific patch ... that'll mean one "reference" board (albeit an old one) has out-of-the-box support on Linux. Given the number of PXA25x systems with an ADS7846, I'd hope to see some testers of that stack (and, surely, bugfixes). One of the things that would IMO help here is getting a bitbanging adapter driver: rub four GPIO pins together, make a fire. ;) If anyone's at work on such a thing (Mark?) please drop a line; it looks to me like this should be very simple, and incidentally help nail down any semantics that aren't described in the docs. - Dave |
From: David B. <da...@pa...> - 2005-11-23 22:17:56
|
On Friday 11 November 2005 1:32 am, dmitry pervushin 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 :) Agreed. Apologies by the way, this message was buried in an email filter. I looked at your patch, compared it to mine, and summarize: - They share the model of a queue of "messages", whih is processed asynchronously. That's a big help. :) - Maybe the biggest difference is in what's in the messages: * You have special DMA hooks where I just expect device drivers to follow the more usual "hands off" approah; * Your messages are single transfers, mine are entire atomic protocol sequences (potentially one chained DMA operation). Neither core supports as much protocol flexibility as it needs; that toolset must grow over time. - Mine lets board-specific device tables be declared in the relevant arch_setup() thing (board-*.c). Both frameworks allow later board specific code to dynamically declare the devices, with binary (Dave's) or parsed-text (Dmitry's) descriptions. - Yours has a "struct spi_driver", mine doesn't. It's probably inevitable, though not currently needed. Likewise mine has the struct driver.suspend() and resume() changes, yours doesn't. - Mine supports "modalias" driver binding through hotplug, or via sysfs for coldplug. Yours doesn't. (Simplified recently.) Those last few seem like easy convergence points. There's other stuff in yours that I don't much like; I won't belabor them here, but key issues are: - Bundling a particular type of userspace interface ... mostly bad because that should be a separate discussion. - Imposes needless policies on how controller drivers are written. - That spi_transfer() call. It can't decide whether it's going to be synchronous or asynchronous ... error prone, confusing. spi_write() and spi_read() always copy memory. - that spimsg_* infrastruture looks quite heavyweight. > 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. It may work with RT_PREEMPT, but it still encourages needless memcpy()! GPL'd WLAN code? Hmmm... ;) - Dave |
From: David B. <da...@pa...> - 2005-11-23 19:05:41
|
> >By the way Vitaly ... I'm still not getting responses directly from you. > >Are you pruning me off CC lists, or is there something between us that's > >filtering out posts? It's certainly hard to respond ... > > > No, definitely I'm not. I'm just responding with "Reply All" button in > my Thunderbird, if you wanna know the details. > And I'm quite surprised to hear about the kind of problems you have as > you're the only one who's having them. Well, as was pointed out offline, you aren't always CC'ing me. In this case you did, and I got your message. > >Vitaly, you're really going to have to come up with some facts if you > >keep claiming the SPI framework I've posted doesn't support DMA. > >(I'm not going to let you pull a SCO on us here.) For that matter, > >you might want to consider the fact that Stephen's pxa2xx_spi > >driver disproves your arguments (it includes DMA support). > > > Again, a single working driver cannot disapprove anything. It certainly can disprove assertions like the one you made... > Evidently enough for anyone more or less familiar with logic, if one > claims that something is working, he should prove it's working > everywhere not somewhere; on the other hand, to disapprove this > statement it's enough to give one example. Which is what I did. You said that the approach I was using did NOT support DMA. I disproved it with several counterexamples ... the whole USB stack for one, using that same DMA approach (in "struct urb" on the host side, and "struct usb_request" on the peripheral side), as well as more specifically that pxa2xx_spi driver (with "struct spi_message"). > The thing is that what we do in the core to provide DMA capabilities > should be implemented in controller driver in your case. > I can agree that it might be considered an addition to the core and not > directly included in it, but leaving rather standard operations to the > controller driver degrades the added value of the core. That's an entirely different argument, note. And in this case, it was a conscious decision not to require any mid-layer logic to interpose itself between layers of SPI driver. Drivers using PIO shouldn't need to pay mapping costs ... but if the mapping were done in a mid-layer, they would always do so. > >Controller drivers don't deal with userland addresses. That's the > >responsibility of the layers on top ... e.g. the filesystem or driver > >code that accepts them. Most just copy to/from userspace, but some > >pay the costs to support direct I/O. > > > > > Oh no, you've got me wrong. I was tryng to say that an upper level > driver which copies the data from the userspace should be copying it to > GFP_DMA buffer, otherwise it won't work or will force the controller > driver to allocate/copy. GFP_DMA is not necessary ... plus, it's usually inappropriate. Except for broken hardware, any buffer returned by kmalloc is fine ... and for that broken hardware, the dma mapping calls should be setting up the relevant bounce buffering. (See how ARM does it for various XScale systems, the dmabounce_* code.) > Basically the upper level driver shouldn't give a damn whether the > buffers should be DMA-able or not since it might be used on different > platforms w/ and w/o DMA capabilities. If it were any effort to provide DMA-safe buffers, I might agree. But it isn't; most buffers come from kmalloc() already. Remember, this is the same requirement applying to the rest of the Linux driver stack. Now that the requirements have been clear for a couple years (Documentation/DMA-mapping.txt, and yes I had something to do with finally getting those rules written down) this is NOT a problem of any note. > >>>- it needs to timely send some credentials (what is the case for the > >>>WLAN driver, for instance). > > >Again, not the responsibility of the lowest level driver. A WLAN driver > >layered on top could, of course. That's the way such things are done in > >other driver stacks. > > > >Are you seriously suggesting that an SPI controller driver should have any > >clue whatever about credentials?? Which of the dozens of schemes should > >become that "special", then ... and why?? > > I'm afraid that you were not reading my response attentively. I was trying, but your words were making no sense to me. > Credentials are stored in a staic-allocated buffer in the upper level > driver. Then they are passed down to the SPI core which will fail to > send the buffer containing the credentials if it's not copied to DMAable > memory somewhere in between. So it'd be just like with any other driver stack. In this case, the issue would be with whatever is passing static credentials where DMA-safe buffers are required. (In fact, static buffers work on every Linux platform I've ever worked with, but it's not guaranteed ... the main issue would be the same one that shows up with dma to/from stack, where cacheline sharing causes chaos on systems without dma-coherent cache.) For what it's worth, usually credentials get copied into an sk_buff, which is DMA-safe since it comes from kmalloc(). If you're talking about some out-of-tree WLAN code, I'd expect those issues would be resolved (in the WLAN code) before that stack gets merged. - Dave |
From: Vitaly W. <vw...@ru...> - 2005-11-23 09:33:19
|
David Brownell wrote: >By the way Vitaly ... I'm still not getting responses directly from you. > >Are you pruning me off CC lists, or is there something between us that's >filtering out posts? It's certainly hard to respond to comments of yours >when I can only find them by going through some list archive I wouldn't >normally read... > > No, definitely I'm not. I'm just responding with "Reply All" button in my Thunderbird, if you wanna know the details. And I'm quite surprised to hear about the kind of problems you have as you're the only one who's having them. >>>>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. >>> >>> >>No, but it's a good indication :). >> >> > >And I'm not sure what a "limited number of platforms" etc is supposed >to suggest. In terms of platforms supporting USB on Linux, there are >surely more of them than there are for PCI ... since every PCI platform >can support USB, and many non-PCI platforms do also. A better word >for that DMA support would seem to be "extensive", not "limited". > >In fact, the support for non-PCI platforms was one of the things that >led to the last DMA-related reworks for USB. The PCI calls wouldn't >work (of course), but USB needs things like DMA-coherent buffers (for >transfer and queue head descriptors, used by most controllers) as well >as bus-neutral DMA operations. And it even works for things like >scatterlist merging through IOMMU mappings ... that wide portability >is **very much** a good indication that (c) is a non-issue with the >framework I've assembled. > > >Vitaly, you're really going to have to come up with some facts if you >keep claiming the SPI framework I've posted doesn't support DMA. >(I'm not going to let you pull a SCO on us here.) For that matter, >you might want to consider the fact that Stephen's pxa2xx_spi >driver disproves your arguments (it includes DMA support). > > Again, a single working driver cannot disapprove anything. Evidently enough for anyone more or less familiar with logic, if one claims that something is working, he should prove it's working everywhere not somewhere; on the other hand, to disapprove this statement it's enough to give one example. However, your latest core should be working on the problematic target as opposed to the previous one. I didn't try that but I think it'll work. The thing is that what we do in the core to provide DMA capabilities should be implemented in controller driver in your case. I can agree that it might be considered an addition to the core and not directly included in it, but leaving rather standard operations to the controller driver degrades the added value of the core. > > > >>>>>>- (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 >>> >>> > >Controller drivers don't deal with userland addresses. That's the >responsibility of the layers on top ... e.g. the filesystem or driver >code that accepts them. Most just copy to/from userspace, but some >pay the costs to support direct I/O. > > Oh no, you've got me wrong. I was tryng to say that an upper level driver which copies the data from the userspace should be copying it to GFP_DMA buffer, otherwise it won't work or will force the controller driver to allocate/copy. And putting a requirement on the upper level driver to always do GFP_DMA kmalloc's might put too high restrictions on the memory usage. Basically the upper level driver shouldn't give a damn whether the buffers should be DMA-able or not since it might be used on different platforms w/ and w/o DMA capabilities. Thus, it's going to be either a requirement for the upper level drivers that implies the SPI core not being transparent to the upper level, or a requirement for each controller driver that might be handling this situation to implement the same thing we did in the core. Neither of these seems reasonable to me. > > > >>>- it needs to timely send some credentials (what is the case for the >>>WLAN driver, for instance). >>> >>> > >Again, not the responsibility of the lowest level driver. A WLAN driver >layered on top could, of course. That's the way such things are done in >other driver stacks. > >Are you seriously suggesting that an SPI controller driver should have any >clue whatever about credentials?? Which of the dozens of schemes should >become that "special", then ... and why?? > > > I'm afraid that you were not reading my response attentively. Credentials are stored in a staic-allocated buffer in the upper level driver. Then they are passed down to the SPI core which will fail to send the buffer containing the credentials if it's not copied to DMAable memory somewhere in between. Vitaly |
From: David B. <da...@pa...> - 2005-11-23 07:08:47
|
Notice also the way that the board-specific default MTD partitioning gets passed up to the driver. ======================================== This is a conversion of the AT91rm9200 DataFlash MTD driver to use the lightweight SPI framework, and no longer be AT91-specific. It compiles down to less than 3KBytes on ARM. The driver allows board-specific init code to provide platform_data with the relevant MTD partitioning information. It works like recent ARM MTD drivers do this ... the default partitioning is board-specific, in the board-xxx.c files not the drivers. UNTESTED. I don't have that boots-from-DataFlash board any more; But the at91_dataflash MTD driver has worked at least through JFFS2, so this shouldn't need too much more work... possibly starting with a new at91_spi driver. --- drivers/mtd/devices/Kconfig | 8 drivers/mtd/devices/Makefile | 1 g26/drivers/mtd/devices/mtd_dataflash.c | 593 ++++++++++++++++++++++++++++++++ g26/include/linux/spi/flash.h | 27 + 4 files changed, 629 insertions(+) --- g26.orig/drivers/mtd/devices/Kconfig 2005-11-22 18:39:39.000000000 -0800 +++ g26/drivers/mtd/devices/Kconfig 2005-11-22 18:40:09.000000000 -0800 @@ -255,5 +255,13 @@ config MTD_DOCPROBE_55AA LinuxBIOS or if you need to recover a DiskOnChip Millennium on which you have managed to wipe the first block. +config MTD_DATAFLASH + tristate "Support for AT45xxx DataFlash" + depends on MTD && SPI_MASTER && EXPERIMENTAL + help + This enables access to AT45xxx DataFlash chips, using SPI. + Sometimes DataFlash chips are packaged inside MMC-format + cards; at this writing, the MMC stack won't handle those. + endmenu --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ g26/include/linux/spi/flash.h 2005-11-22 18:40:09.000000000 -0800 @@ -0,0 +1,27 @@ +#ifndef LINUX_SPI_FLASH_H +#define LINUX_SPI_FLASH_H + +struct mtd_partition; + +/** + * struct flash_platform_data: board-specific flash data + * @name: optional flash device name (eg, as used with mtdparts=) + * @parts: optional array of mtd_partitions for static partitioning + * @nr_parts: number of mtd_partitions for static partitoning + * + * Board init code (in arch/.../mach-xxx/board-yyy.c files) can + * provide information about SPI flash parts (such as DataFlash) to + * help set up the device and its appropriate default partitioning. + * + * Note that for DataFlash, sizes for pages, blocks, and sectors are + * rarely powers of two; and partitions should be sector-aligned. + */ +struct flash_platform_data { + char *name; + struct mtd_partition *parts; + unsigned int nr_parts; + + /* we'll likely add more ... use JEDEC IDs, etc */ +}; + +#endif --- g26.orig/drivers/mtd/devices/Makefile 2005-11-22 18:39:39.000000000 -0800 +++ g26/drivers/mtd/devices/Makefile 2005-11-22 18:40:09.000000000 -0800 @@ -23,3 +23,4 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o +obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ g26/drivers/mtd/devices/mtd_dataflash.c 2005-11-22 18:40:09.000000000 -0800 @@ -0,0 +1,593 @@ +/* + * Atmel AT45xxx DataFlash MTD driver for lightweight SPI framework + * + * Largely derived from at91_dataflash.c: + * Copyright (C) 2003-2005 SAN People (Pty) Ltd + * + * 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. +*/ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/spi/flash.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> + + +/* + * DataFlash is a kind of SPI flash. Most AT45 chips have two buffers in + * each chip, which may be used for double buffered I/O; but this driver + * doesn't (yet) use these for any kind of i/o overlap or prefetching. + * + * Sometimes DataFlash is packaged in MMC-format cards. This driver does + * not (yet) expect to be hotplugged through MMC or SPI. That's fair, + * since the MMC stack can't even use SPI (yet), much less distinguish + * between MMC and DataFlash protocols during enumeration. + */ + +#define CONFIG_DATAFLASH_WRITE_VERIFY + + +/* reads can bypass the buffers */ +#define OP_READ_CONTINUOUS 0xE8 +#define OP_READ_PAGE 0xD2 + +/* group B requests can run even while status reports "busy" */ +#define OP_READ_STATUS 0xD7 /* group B */ + +/* move data between host and buffer */ +#define OP_READ_BUFFER1 0xD4 /* group B */ +#define OP_READ_BUFFER2 0xD6 /* group B */ +#define OP_WRITE_BUFFER1 0x84 /* group B */ +#define OP_WRITE_BUFFER2 0x87 /* group B */ + +/* erasing flash */ +#define OP_ERASE_PAGE 0x81 +#define OP_ERASE_BLOCK 0x50 + +/* move data between buffer and flash */ +#define OP_TRANSFER_BUF1 0x53 +#define OP_TRANSFER_BUF2 0x55 +#define OP_MREAD_BUFFER1 0xD4 +#define OP_MREAD_BUFFER2 0xD6 +#define OP_MWERASE_BUFFER1 0x83 +#define OP_MWERASE_BUFFER2 0x86 +#define OP_MWRITE_BUFFER1 0x88 /* sector must be pre-erased */ +#define OP_MWRITE_BUFFER2 0x89 /* sector must be pre-erased */ + +/* write to buffer, then write-erase to flash */ +#define OP_PROGRAM_VIA_BUF1 0x82 +#define OP_PROGRAM_VIA_BUF2 0x85 + +/* compare buffer to flash */ +#define OP_COMPARE_BUF1 0x60 +#define OP_COMPARE_BUF2 0x61 + +/* read flash to buffer, then write-erase to flash */ +#define OP_REWRITE_VIA_BUF1 0x58 +#define OP_REWRITE_VIA_BUF2 0x59 + +/* newer chips report JEDEC manufacturer and device IDs; chip + * serial number and OTP bits; and per-sector writeprotect. + */ +#define OP_READ_ID 0x9F +#define OP_READ_SECURITY 0x77 +#define OP_WRITE_SECURITY 0x9A /* OTP bits */ + + +struct dataflash { + u8 command[4]; + char name[24]; + + unsigned short page_offset; /* offset in flash address */ + unsigned int page_size; /* of bytes per page */ + + struct semaphore lock; + struct spi_device *spi; + + struct mtd_info mtd; +}; + +/* ......................................................................... */ + +/* + * Return the status of the DataFlash device. + */ +static inline int dataflash_status(struct spi_device *spi) +{ + /* NOTE: at45db321c over 25 MHz wants to write + * a dummy byte after the opcode... + */ + return spi_w8r8(spi, OP_READ_STATUS); +} + +/* + * Poll the DataFlash device until it is READY. + * This usually takes 5-20 msec or so; more for sector erase. + */ +static int dataflash_waitready(struct spi_device *spi) +{ + int status; + + for (;;) { + status = dataflash_status(spi); + if (status < 0) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: status %d?\n", + spi->dev.bus_id, status); + status = 0; + } + + if (status & (1 << 7)) /* RDY/nBSY */ + return status; + + msleep(3); + } +} + +/* ......................................................................... */ + +/* + * Erase pages of flash. + */ +static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct dataflash *priv = (struct dataflash *)mtd->priv; + struct spi_device *spi = priv->spi; + struct spi_transfer x[1] = { { .tx_dma = 0, }, }; + struct spi_message msg; + u8 *command; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=%x len %x\n", + spi->dev.bus_id, + instr->addr, instr->len); + + /* Sanity checks */ + if ((instr->addr + instr->len) > mtd->size + || (instr->len % priv->page_size) != 0 + || (instr->addr % priv->page_size) != 0) + return -EINVAL; + + x[0].tx_buf = command = priv->command; + x[0].len = 4; + msg.transfers = x; + msg.n_transfer = 1; + + down(&priv->lock); + while (instr->len > 0) { + unsigned int pageaddr; + int status; + + /* Calculate flash page address */ + // REVISIT: pageaddr == instr->addr, yes??? + pageaddr = (instr->addr / priv->page_size) << priv->page_offset; + + /* REVISIT block erase is faster, if this page is at a block + * boundary and the next eight pages must all be erased ... + */ + command[0] = OP_ERASE_PAGE; + command[1] = (u8)(pageaddr >> 16); + command[2] = (u8)(pageaddr >> 8); + command[3] = 0; + + DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (%x) %x %x %x [%i]\n", + command[0], command[1], command[2], command[3], + pageaddr); + + status = spi_sync(spi, &msg); + (void) dataflash_waitready(spi); + + if (status < 0) { + printk(KERN_ERR "%s: erase %x, err %d\n", + spi->dev.bus_id, pageaddr, status); + continue; + } + + instr->addr += priv->page_size; /* next page */ + instr->len -= priv->page_size; + } + up(&priv->lock); + + /* Inform MTD subsystem that erase is complete */ + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} + +/* + * Read from the DataFlash device. + * from : Start offset in flash device + * len : Amount to read + * retlen : About of data actually read + * buf : Buffer containing the data + */ +static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct dataflash *priv = (struct dataflash *)mtd->priv; + struct spi_transfer x[2] = { { .tx_dma = 0, }, }; + struct spi_message msg; + unsigned int addr; + u8 *command; + int status; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: read %x..%x\n", + priv->spi->dev.bus_id, (unsigned)from, (unsigned)(from + len)); + + *retlen = 0; + + /* Sanity checks */ + if (!len) + return 0; + if (from + len > mtd->size) + return -EINVAL; + + /* Calculate flash page/byte address */ + addr = (((unsigned)from / priv->page_size) << priv->page_offset) + + ((unsigned)from % priv->page_size); + + command = priv->command; + + DEBUG(MTD_DEBUG_LEVEL3, "READ: (%x) %x %x %x\n", + command[0], command[1], command[2], command[3]); + + x[0].tx_buf = command; + x[0].len = 8; + x[1].rx_buf = buf; + x[1].len = len; + msg.transfers = x; + msg.n_transfer = 2; + + down(&priv->lock); + + /* Continuous read, max clock = f(car) which may be less than + * the peak rate available. Some chips support commands with + * fewer "don't care" bytes. Both buffers stay unchanged. + */ + command[0] = OP_READ_CONTINUOUS; + command[1] = (u8)(addr >> 16); + command[2] = (u8)(addr >> 8); + command[3] = (u8)(addr >> 0); + /* plus 4 "don't care" bytes */ + + status = spi_sync(priv->spi, &msg); + up(&priv->lock); + + if (status >= 0) { + *retlen = msg.actual_length; + status = 0; + } else + DEBUG(MTD_DEBUG_LEVEL1, "%s: read %x..%x --> %d\n", + priv->spi->dev.bus_id, + (unsigned)from, (unsigned)(from + len), + status); + return status; +} + +/* + * Write to the DataFlash device. + * to : Start offset in flash device + * len : Amount to write + * retlen : Amount of data actually written + * buf : Buffer containing the data + */ +static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) +{ + struct dataflash *priv = (struct dataflash *)mtd->priv; + struct spi_device *spi = priv->spi; + struct spi_transfer x[2] = { { .tx_dma = 0, }, }; + struct spi_message msg; + unsigned int pageaddr, addr, offset, writelen; + size_t remaining = len; + u_char *writebuf = (u_char *) buf; + int status = -EINVAL; + u8 *command; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: write %x..%x\n", + spi->dev.bus_id, (unsigned)to, (unsigned)(to + len)); + + *retlen = 0; + + /* Sanity checks */ + if (!len) + return 0; + if ((to + len) > mtd->size) + return -EINVAL; + + x[0].tx_buf = command = priv->command; + x[0].len = 4; + msg.transfers = x; + + pageaddr = ((unsigned)to / priv->page_size); + offset = ((unsigned)to % priv->page_size); + if (offset + len > priv->page_size) + writelen = priv->page_size - offset; + else + writelen = len; + + down(&priv->lock); + while (remaining > 0) { + DEBUG(MTD_DEBUG_LEVEL3, "write @ %i:%i len=%i\n", + pageaddr, offset, writelen); + + /* REVISIT: + * (a) each page in a sector must be rewritten at least + * once every 10K sibling erase/program operations. + * (b) for pages that are already erased, we could + * use WRITE+MWRITE not PROGRAM for ~30% speedup. + * (c) WRITE to buffer could be done while waiting for + * a previous MWRITE/MWERASE to complete ... + * (d) error handling here seems to be mostly missing. + * + * Two persistent bits per page, plus a per-sector counter, + * could support (a) and (b) ... we might consider using + * the second half of sector zero, which is just one block, + * to track that state. (On AT91, that sector should also + * support boot-from-DataFlash.) + */ + + addr = pageaddr << priv->page_offset; + + /* (1) Maybe transfer partial page to Buffer1 */ + if (writelen != priv->page_size) { + command[0] = OP_TRANSFER_BUF1; + command[1] = (addr & 0x00FF0000) >> 16; + command[2] = (addr & 0x0000FF00) >> 8; + command[3] = 0; + + DEBUG(MTD_DEBUG_LEVEL3, "TRANSFER: (%x) %x %x %x\n", + command[0], command[1], command[2], command[3]); + + msg.n_transfer = 1; + status = spi_sync(spi, &msg); + if (status < 0) + DEBUG(MTD_DEBUG_LEVEL1, "%s: xfer %u -> %d \n", + spi->dev.bus_id, addr, status); + + (void) dataflash_waitready(priv->spi); + } + + /* (2) Program full page via Buffer1 */ + addr += offset; + command[0] = OP_PROGRAM_VIA_BUF1; + command[1] = (addr & 0x00FF0000) >> 16; + command[2] = (addr & 0x0000FF00) >> 8; + command[3] = (addr & 0x000000FF); + + DEBUG(MTD_DEBUG_LEVEL3, "PROGRAM: (%x) %x %x %x\n", + command[0], command[1], command[2], command[3]); + + x[1].tx_buf = writebuf; + x[1].len = writelen; + msg.n_transfer = 2; + status = spi_sync(spi, &msg); + if (status < 0) + DEBUG(MTD_DEBUG_LEVEL1, "%s: pgm %u/%u -> %d \n", + spi->dev.bus_id, addr, writelen, status); + + (void) dataflash_waitready(priv->spi); + +#ifdef CONFIG_DATAFLASH_WRITE_VERIFY + + /* (3) Compare to Buffer1 */ + addr = pageaddr << priv->page_offset; + command[0] = OP_COMPARE_BUF1; + command[1] = (addr & 0x00FF0000) >> 16; + command[2] = (addr & 0x0000FF00) >> 8; + command[3] = 0; + + DEBUG(MTD_DEBUG_LEVEL3, "COMPARE: (%x) %x %x %x\n", + command[0], command[1], command[2], command[3]); + + msg.n_transfer = 1; + status = spi_sync(spi, &msg); + if (status < 0) + DEBUG(MTD_DEBUG_LEVEL1, "%s: compare %u -> %d \n", + spi->dev.bus_id, addr, status); + + status = dataflash_waitready(priv->spi); + + /* Check result of the compare operation */ + if ((status & (1 << 6)) == 1) { + printk(KERN_ERR "%s: compare page %u, err %d\n", + spi->dev.bus_id, pageaddr, status); + remaining = 0; + status = -EIO; + break; + } else + status = 0; + +#endif /* CONFIG_DATAFLASH_WRITE_VERIFY */ + + remaining = remaining - writelen; + pageaddr++; + offset = 0; + writebuf += writelen; + *retlen += writelen; + + if (remaining > priv->page_size) + writelen = priv->page_size; + else + writelen = remaining; + } + up(&priv->lock); + + return status; +} + +/* ......................................................................... */ + +/* + * Register DataFlash device with MTD subsystem. + */ +static int __init +add_dataflash(struct spi_device *spi, char *name, + int nr_pages, int pagesize, int pageoffset) +{ + struct dataflash *priv; + struct mtd_info *device; + struct flash_platform_data *pdata = spi->dev.platform_data; + + priv = (struct dataflash *) kzalloc(sizeof *priv, GFP_KERNEL); + if (!priv) + return -ENOMEM; + + init_MUTEX(&priv->lock); + priv->spi = spi; + priv->page_size = pagesize; + priv->page_offset = pageoffset; + + /* name must be usable with cmdlinepart */ + sprintf(priv->name, "spi%d.%d-%s", + spi->master->bus_num, spi->chip_select, + name); + + device = &priv->mtd; + device->name = (pdata && pdata->name) ? pdata->name : priv->name; + device->size = nr_pages * pagesize; + device->erasesize = pagesize; + device->owner = THIS_MODULE; + device->type = MTD_DATAFLASH; + device->flags = MTD_CAP_NORFLASH; + device->erase = dataflash_erase; + device->read = dataflash_read; + device->write = dataflash_write; + device->priv = priv; + + dev_info(&spi->dev, "%s (%d KBytes)\n", name, device->size/1024); + dev_set_drvdata(&spi->dev, priv); + +#ifdef CONFIG_MTD_PARTITIONS + { + struct mtd_partition *parts; + int nr_parts = 0; + +#ifdef CONFIG_MTD_CMDLINE_PARTS + static const char *part_probes[] = { "cmdlinepart", NULL, }; + + nr_parts = parse_mtd_partitions(device, part_probes, &parts, 0); +#endif + + if (nr_parts <= 0 && pdata && pdata->parts) { + parts = pdata->parts; + nr_parts = pdata->nr_parts; + } + + if (nr_parts > 0) + return add_mtd_partitions(device, parts, nr_parts); + } +#endif + return add_mtd_device(device); +} + +/* + * Detect and initialize DataFlash device: + * + * Device Density ID code #Pages PageSize Offset + * AT45DB011B 1Mbit (128K) xx0011xx (0x0c) 512 264 9 + * AT45DB021B 2Mbit (256K) xx0101xx (0x14) 1025 264 9 + * AT45DB041B 4Mbit (512K) xx0111xx (0x1c) 2048 264 9 + * AT45DB081B 8Mbit (1M) xx1001xx (0x24) 4096 264 9 + * AT45DB0161B 16Mbit (2M) xx1011xx (0x2c) 4096 528 10 + * AT45DB0321B 32Mbit (4M) xx1101xx (0x34) 8192 528 10 + * AT45DB0642 64Mbit (8M) xx111xxx (0x3c) 8192 1056 11 + * AT45DB1282 128Mbit (16M) xx0100xx (0x10) 16384 1056 11 + */ +static int __init dataflash_probe(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + int status; + + /* if there's a device there, assume it's dataflash. + * board setup should have set spi->max_speed_max to + * match f(car) for continuous reads, mode 0 or 3. + */ + status = dataflash_status(spi); + if (status > 0 && status != 0xff) { + switch (status & 0x3c) { + case 0x0c: /* 0 0 1 1 x x */ + status = add_dataflash(spi, "AT45DB011B", + 512, 264, 9); + break; + case 0x14: /* 0 1 0 1 x x */ + status = add_dataflash(spi, "AT45DB021B", + 1025, 264, 9); + break; + case 0x1c: /* 0 1 1 1 x x */ + status = add_dataflash(spi, "AT45DB041B", + 2048, 264, 9); + break; + case 0x24: /* 1 0 0 1 x x */ + status = add_dataflash(spi, "AT45DB081B", + 4096, 264, 9); + break; + case 0x2c: /* 1 0 1 1 x x */ + status = add_dataflash(spi, "AT45DB161x", + 4096, 528, 10); + break; + case 0x34: /* 1 1 0 1 x x */ + status = add_dataflash(spi, "AT45DB321x", + 8192, 528, 10); + break; + case 0x38: /* 1 1 1 x x x */ + case 0x3c: + status = add_dataflash(spi, "AT45DB642x", + 8192, 1056, 11); + break; + /* obsolete AT45DB1282 not (yet?) supported */ + default: + printk(KERN_WARNING "%s: unsupported device (%x)\n", + spi->dev.bus_id, status & 0x3c); + status = -EINVAL; + } + } + + return status; +} + +static int __exit dataflash_remove(struct device *dev) +{ + // struct dataflash *flash = dev_get_drvdata(dev); + + dev_warn(dev, "implement remove!\n"); + return -EINVAL; +} + +static struct device_driver dataflash_driver = { + .name = "mtd_dataflash", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + + .probe = dataflash_probe, + .remove = __exit_p(dataflash_remove), + + /* FIXME: investigate suspend and resume... */ +}; + +static int __init dataflash_init(void) +{ + return driver_register(&dataflash_driver); +} +module_init(dataflash_init); + +#if 0 +static void __exit dataflash_exit(void) +{ + driver_unregister(&dataflash_driver); +} +module_exit(dataflash_exit); +#endif + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andrew Victor, David Brownell"); +MODULE_DESCRIPTION("MTD DataFlash driver"); |
From: David B. <da...@pa...> - 2005-11-23 07:08:47
|
There's a now-more-portable touchscreen driver. ======================================== This is a driver for the ADS7846 touchscreen sensor, derived from the corgi_ts and omap_ts drivers. Key differences from those two: - Uses the new SPI framework (minimalist version) - <linux/spi/ads7846.h> abstracts board-specific touchscreen info - Sysfs attributes for the temperature and voltage sensors - Uses fewer ARM-specific IRQ primitives The temperature and voltage sensors show up in sysfs like this: $ pwd /sys/devices/platform/omap-uwire/spi2.0 $ ls bus@ input:event0@ power/ temp1 vbatt driver@ modalias temp0 vaux $ cat modalias ads7846 $ cat temp0 991 $ cat temp1 1177 $ So far only basic testing has been done. There's a fair amount of hardware that uses this sensor, and which also runs Linux, which should eventually be able to use this driver. One portability note may be of special interest. It turns out that not all SPI controllers are happy issuing requests that do things like "write 8 bit command, read 12 bit response". Most of them seem happy to handle various word sizes, so the issue isn't "12 bit response" but rather "different rx and tx write sizes", despite that being a common MicroWire convention. So this version of the driver no longer reads 12 bit native-endian words; it reads 16-bit big-endian responses, then byteswaps them and shifts the results to discard the noise. --- drivers/input/touchscreen/Kconfig | 13 drivers/input/touchscreen/Makefile | 1 g26/drivers/input/touchscreen/ads7846.c | 620 ++++++++++++++++++++++++++++++++ g26/include/linux/spi/ads7846.h | 18 4 files changed, 652 insertions(+) --- g26.orig/drivers/input/touchscreen/Makefile 2005-11-22 18:39:41.000000000 -0800 +++ g26/drivers/input/touchscreen/Makefile 2005-11-22 18:40:08.000000000 -0800 @@ -4,6 +4,7 @@ # Each configuration option enables a list of files. +obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o --- g26.orig/drivers/input/touchscreen/Kconfig 2005-11-22 18:39:41.000000000 -0800 +++ g26/drivers/input/touchscreen/Kconfig 2005-11-22 18:40:08.000000000 -0800 @@ -11,6 +11,19 @@ menuconfig INPUT_TOUCHSCREEN if INPUT_TOUCHSCREEN +config TOUCHSCREEN_ADS7846 + tristate "ADS 7846 based touchscreens" + depends on SPI_MASTER + help + Say Y here if you have a touchscreen interface using the + ADS7846 controller, and your board-specific initialization + code includes that in its table of SPI devices. + + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called ads7846. + config TOUCHSCREEN_BITSY tristate "Compaq iPAQ H3600 (Bitsy) touchscreen" depends on SA1100_BITSY --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ g26/include/linux/spi/ads7846.h 2005-11-22 18:40:08.000000000 -0800 @@ -0,0 +1,18 @@ +/* linux/spi/ads7846.h */ + +/* Touchscreen characteristics vary between boards and models. The + * platform_data for the device's "struct device" holts this information. + * + * It's OK if the min/max values are zero. + */ +struct ads7846_platform_data { + u16 model; /* 7843, 7845, 7846. */ + u16 vref_delay_usecs; /* 0 for external vref; etc */ + u16 x_plate_ohms; + u16 y_plate_ohms; + + u16 x_min, x_max; + u16 y_min, y_max; + u16 pressure_min, pressure_max; +}; + --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ g26/drivers/input/touchscreen/ads7846.c 2005-11-22 18:40:08.000000000 -0800 @@ -0,0 +1,620 @@ +/* + * ADS7846 based touchscreen and sensor driver + * + * Copyright (c) 2005 David Brownell + * + * Using code from: + * - corgi_ts.c + * Copyright (C) 2004-2005 Richard Purdie + * - omap_ts.[hc], ads7846.h, ts_osk.c + * Copyright (C) 2002 MontaVista Software + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2005 Dirk Behme + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/device.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/spi/ads7846.h> + +#ifdef CONFIG_ARM +#include <asm/mach-types.h> +#ifdef CONFIG_ARCH_OMAP +#include <asm/arch/gpio.h> +#endif + +#else +#define set_irq_type(irq,type) do{}while(0) +#endif + + +/* + * This code has been lightly tested on an ads7846. + * Support for ads7843 and ads7845 has only been stubbed in. + * + * Not yet done: investigate the values reported. Are x/y/pressure + * event values sane enough for X11? How accurate are the temperature + * and voltage readings? (System-specific calibration should support + * accuracy of 0.3 degrees C; otherwise it's 2.0 degrees.) + * + * app note sbaa036 talks in more detail about accurate sampling... + * that ought to help in situations like LCDs inducing noise (which + * can also be helped by using synch signals) and more generally. + */ + +#define TS_POLL_PERIOD msecs_to_jiffies(10) + +struct ts_event { + /* For portability, we can't read 12 bit values using SPI (which + * would make the controller deliver them as native byteorder u16 + * with msbs zeroed). Instead, we read them as two 8-byte values, + * which need byteswapping then range adjustment. + */ + __be16 x; + __be16 y; + __be16 z1, z2; +}; + +struct ads7846 { + struct input_dev input; + char phys[32]; + + struct spi_device *spi; + u16 model; + u16 vref_delay_usecs; + u16 x_plate_ohms; + + struct ts_event tc; + + struct spi_transfer xfer[8]; + struct spi_message msg; + + spinlock_t lock; + struct timer_list timer; /* P: lock */ + unsigned pendown:1; /* P: lock */ + unsigned pending:1; /* P: lock */ +// FIXME remove "irq_disabled" + unsigned irq_disabled:1; /* P: lock */ +}; + +/* leave chip selected when we're done, for quicker re-select? */ +#if 0 +#define CS_CHANGE(xfer) ((xfer).cs_change = 1) +#else +#define CS_CHANGE(xfer) ((xfer).cs_change = 0) +#endif + +/*--------------------------------------------------------------------------*/ + +/* The ADS7846 has touchscreen and other sensors. + * Earlier ads784x chips are somewhat compatible. + */ +#define ADS_START (1 << 7) +#define ADS_A2A1A0_d_y (1 << 4) /* differential */ +#define ADS_A2A1A0_d_z1 (3 << 4) /* differential */ +#define ADS_A2A1A0_d_z2 (4 << 4) /* differential */ +#define ADS_A2A1A0_d_x (5 << 4) /* differential */ +#define ADS_A2A1A0_temp0 (0 << 4) /* non-differential */ +#define ADS_A2A1A0_vbatt (2 << 4) /* non-differential */ +#define ADS_A2A1A0_vaux (6 << 4) /* non-differential */ +#define ADS_A2A1A0_temp1 (7 << 4) /* non-differential */ +#define ADS_8_BIT (1 << 3) +#define ADS_12_BIT (0 << 3) +#define ADS_SER (1 << 2) /* non-differential */ +#define ADS_DFR (0 << 2) /* differential */ +#define ADS_PD10_PDOWN (0 << 0) /* lowpower mode + penirq */ +#define ADS_PD10_ADC_ON (1 << 0) /* ADC on */ +#define ADS_PD10_REF_ON (2 << 0) /* vREF on + penirq */ +#define ADS_PD10_ALL_ON (3 << 0) /* ADC + vREF on */ + +#define MAX_12BIT ((1<<12)-1) + +/* leave ADC powered up (disables penirq) between differential samples */ +#define READ_12BIT_DFR(x) (ADS_START | ADS_A2A1A0_d_ ## x \ + | ADS_12_BIT | ADS_DFR) + +static const u8 read_y = READ_12BIT_DFR(y) | ADS_PD10_ADC_ON; +static const u8 read_z1 = READ_12BIT_DFR(z1) | ADS_PD10_ADC_ON; +static const u8 read_z2 = READ_12BIT_DFR(z2) | ADS_PD10_ADC_ON; +static const u8 read_x = READ_12BIT_DFR(x) | ADS_PD10_PDOWN; /* LAST */ + +/* single-ended samples need to first power up reference voltage; + * we leave both ADC and VREF powered + */ +#define READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \ + | ADS_12_BIT | ADS_SER) + +static const u8 ref_on = READ_12BIT_DFR(x) | ADS_PD10_ALL_ON; +static const u8 ref_off = READ_12BIT_DFR(y) | ADS_PD10_PDOWN; + +/*--------------------------------------------------------------------------*/ + +/* + * Non-touchscreen sensors only use single-ended conversions. + */ + +struct ser_req { + u8 command; + u16 scratch; + __be16 sample; + struct spi_message msg; + struct spi_transfer xfer[6]; +}; + +static int ads7846_read12_ser(struct device *dev, unsigned command) +{ + struct spi_device *spi = to_spi_device(dev); + struct ads7846 *ts = dev_get_drvdata(dev); + struct ser_req *req = kzalloc(sizeof *req, SLAB_KERNEL); + int status; + int sample; + + if (!req) + return -ENOMEM; + + /* activate reference, so it has time to settle; */ + req->xfer[0].tx_buf = &ref_on; + req->xfer[0].len = 1; + req->xfer[1].rx_buf = &req->scratch; + req->xfer[1].len = 2; + + /* + * for external VREF, 0 usec (and assume it's always on); + * for 1uF, use 800 usec; + * no cap, 100 usec. + */ + req->xfer[1].delay_usecs = ts->vref_delay_usecs; + + /* take sample */ + req->command = (u8) command; + req->xfer[2].tx_buf = &req->command; + req->xfer[2].len = 1; + req->xfer[3].rx_buf = &req->sample; + req->xfer[3].len = 2; + + /* REVISIT: take a few more samples, and compare ... */ + + /* turn off reference */ + req->xfer[4].tx_buf = &ref_off; + req->xfer[4].len = 1; + req->xfer[5].rx_buf = &req->scratch; + req->xfer[5].len = 2; + + CS_CHANGE(req->xfer[5]); + + /* group all the transfers together, so we can't interfere with + * reading touchscreen state; disable penirq while sampling + */ + req->msg.transfers = req->xfer; + req->msg.n_transfer = 6; + + disable_irq(spi->irq); + status = spi_sync(spi, &req->msg); + enable_irq(spi->irq); + + if (req->msg.status) + status = req->msg.status; + sample = be16_to_cpu(req->sample); + sample = sample >> 4; + kfree(req); + + return status ? status : sample; +} + +#define SHOW(name) static ssize_t \ +name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + ssize_t v = ads7846_read12_ser(dev, \ + READ_12BIT_SER(name) | ADS_PD10_ALL_ON); \ + if (v < 0) \ + return v; \ + return sprintf(buf, "%u\n", (unsigned) v); \ +} \ +static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL); + +SHOW(temp0) +SHOW(temp1) +SHOW(vaux) +SHOW(vbatt) + +/*--------------------------------------------------------------------------*/ + +/* + * PENIRQ only kicks the timer. The timer only reissues the SPI transfer, + * to retrieve touchscreen status. + * + * The SPI transfer completion callback does the real work. It reports + * touchscreen events and reactivates the timer (or IRQ) as appropriate. + */ + +static void ads7846_rx(void *ads) +{ + struct ads7846 *ts = ads; + unsigned Rt; + unsigned sync = 0; + u16 x, y, z1, z2; + unsigned long flags; + + /* adjust: 12 bit samples (left aligned), built from + * two 8 bit values writen msb-first. + */ + x = be16_to_cpu(ts->tc.x) >> 4; + y = be16_to_cpu(ts->tc.y) >> 4; + z1 = be16_to_cpu(ts->tc.z1) >> 4; + z2 = be16_to_cpu(ts->tc.z2) >> 4; + + /* range filtering */ + if (x == MAX_12BIT) + x = 0; + + if (x && z1 && ts->spi->dev.power.power_state.event == PM_EVENT_ON) { + /* compute touch pressure resistance using equation #2 */ + Rt = z2; + Rt -= z1; + Rt *= x; + Rt *= ts->x_plate_ohms; + Rt /= z1; + Rt = (Rt + 2047) >> 12; + } else + Rt = 0; + + /* NOTE: "pendown" is inferred from pressure; we don't rely on + * being able to check nPENIRQ status, or "friendly" trigger modes + * (both-edges is much better than just-falling or low-level). + * + * REVISIT: some boards may require reading nPENIRQ; it's + * needed on 7843. and 7845 reads pressure differently... + * + * REVISIT: the touchscreen might not be connected; this code + * won't notice that, even if nPENIRQ never fires ... + */ + if (!ts->pendown && Rt != 0) { + input_report_key(&ts->input, BTN_TOUCH, 1); + sync = 1; + } else if (ts->pendown && Rt == 0) { + input_report_key(&ts->input, BTN_TOUCH, 0); + sync = 1; + } + + if (Rt) { + input_report_abs(&ts->input, ABS_X, x); + input_report_abs(&ts->input, ABS_Y, y); + input_report_abs(&ts->input, ABS_PRESSURE, Rt); + sync = 1; + } + if (sync) + input_sync(&ts->input); + +#ifdef VERBOSE + if (Rt || ts->pendown) + pr_debug("%s: %d/%d/%d%s\n", ts->spi->dev.bus_id, + x, y, Rt, Rt ? "" : " UP"); +#endif + + /* don't retrigger while we're suspended */ + spin_lock_irqsave(&ts->lock, flags); + + ts->pendown = (Rt != 0); + ts->pending = 0; + + if (ts->spi->dev.power.power_state.event == PM_EVENT_ON) { + if (ts->pendown) + mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD); + else if (ts->irq_disabled) { + ts->irq_disabled = 0; + enable_irq(ts->spi->irq); + } + } + + spin_unlock_irqrestore(&ts->lock, flags); +} + +static void ads7846_timer(unsigned long handle) +{ + struct ads7846 *ts = (void *)handle; + int status = 0; + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + if (!ts->pending) { + ts->pending = 1; + if (!ts->irq_disabled) { + ts->irq_disabled = 1; + disable_irq(ts->spi->irq); + } + status = spi_async(ts->spi, &ts->msg); + if (status) + dev_err(&ts->spi->dev, "spi_async --> %d\n", + status); + } + spin_unlock_irqrestore(&ts->lock, flags); +} + +static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs) +{ + ads7846_timer((unsigned long) handle); + return IRQ_HANDLED; +} + +/*--------------------------------------------------------------------------*/ + +/* non-empty "extra" is needed before 2.6.14-git5 or so */ +#define EXTRA // , u32 level +#define EXTRA2 // , 0 + +static int +ads7846_suspend(struct device *dev, pm_message_t message EXTRA) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&ts->lock, flags); + + ts->spi->dev.power.power_state = message; + + /* are we waiting for IRQ, or polling? */ + if (!ts->pendown) { + if (!ts->irq_disabled) { + ts->irq_disabled = 1; + disable_irq(ts->spi->irq); + } + } else { + /* polling; force a final SPI completion; + * that will clean things up neatly + */ + if (!ts->pending) + mod_timer(&ts->timer, jiffies); + + while (ts->pendown || ts->pending) { + spin_unlock_irqrestore(&ts->lock, flags); + udelay(10); + spin_lock_irqsave(&ts->lock, flags); + } + } + + /* we know the chip's in lowpower mode since we always + * leave it that way after every request + */ + + spin_unlock_irqrestore(&ts->lock, flags); + return 0; +} + +static int ads7846_resume(struct device *dev EXTRA) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + + ts->irq_disabled = 0; + enable_irq(ts->spi->irq); + dev->power.power_state = PMSG_ON; + return 0; +} + +static int __init ads7846_probe(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct ads7846 *ts; + struct ads7846_platform_data *pdata = dev->platform_data; + struct spi_transfer *x; + + if (!spi->irq) { + dev_dbg(dev, "no IRQ?\n"); + return -ENODEV; + } + + if (!pdata) { + dev_dbg(dev, "no platform data?\n"); + return -ENODEV; + } + + /* don't exceed max specified sample rate */ + if (spi->max_speed_hz > (125000 * 16)) { + dev_dbg(dev, "f(sample) %d KHz?\n", + (spi->max_speed_hz/16)/1000); + return -EINVAL; + } + + /* We'd set the wordsize to 12 bits ... except that some controllers + * will then treat the 8 bit command words as 12 bits (and drop the + * four MSBs of the 12 bit result). Result: inputs must be shifted + * to discard the four garbage LSBs. + */ + + if (!(ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL))) + return -ENOMEM; + + dev_set_drvdata(dev, ts); + + ts->spi = spi; + spi->dev.power.power_state = PMSG_ON; + + init_timer(&ts->timer); + ts->timer.data = (unsigned long) ts; + ts->timer.function = ads7846_timer; + + ts->model = pdata->model ? : 7846; + ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; + ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; + + init_input_dev(&ts->input); + + ts->input.dev = dev; + ts->input.name = "ADS784x Touchscreen"; + snprintf(ts->phys, sizeof ts->phys, "%s/input0", dev->bus_id); + ts->input.phys = ts->phys; + + ts->input.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + ts->input.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + input_set_abs_params(&ts->input, ABS_X, + pdata->x_min ? : 0, + pdata->x_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(&ts->input, ABS_Y, + pdata->y_min ? : 0, + pdata->y_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(&ts->input, ABS_PRESSURE, + pdata->pressure_min, pdata->pressure_max, 0, 0); + + input_register_device(&ts->input); + + /* set up the transfers to read touchscreen state; this assumes we + * use formula #2 for pressure, not #3. + */ + x = ts->xfer; + + /* y- still on; turn on only y+ (and ADC) */ + x->tx_buf = &read_y; + x->len = 1; + x++; + x->rx_buf = &ts->tc.y; + x->len = 2; + x++; + + /* turn y+ off, x- on; we'll use formula #2 */ + if (ts->model == 7846) { + x->tx_buf = &read_z1; + x->len = 1; + x++; + x->rx_buf = &ts->tc.z1; + x->len = 2; + x++; + + x->tx_buf = &read_z2; + x->len = 1; + x++; + x->rx_buf = &ts->tc.z2; + x->len = 2; + x++; + } + + /* turn y- off, x+ on, then leave in lowpower */ + x->tx_buf = &read_x; + x->len = 1; + x++; + x->rx_buf = &ts->tc.x; + x->len = 2; + x++; + + CS_CHANGE(x[-1]); + + ts->msg.transfers = ts->xfer; + ts->msg.n_transfer = x - ts->xfer; + ts->msg.complete = ads7846_rx; + ts->msg.context = ts; + + if (request_irq(spi->irq, ads7846_irq, SA_SAMPLE_RANDOM, + dev->bus_id, ts)) { + dev_dbg(dev, "irq %d busy?\n", spi->irq); + input_unregister_device(&ts->input); + kfree(ts); + return -EBUSY; + } + set_irq_type(spi->irq, IRQT_FALLING); + + dev_info(dev, "touchscreen, irq %d\n", spi->irq); + + /* take a first sample, leaving nPENIRQ active; avoid + * the touchscreen, in case it's not connected. + */ + (void) ads7846_read12_ser(dev, + READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); + + /* ads7843/7845 don't have temperature sensors, and + * use the other sensors a bit differently too + */ + if (ts->model == 7846) { + device_create_file(dev, &dev_attr_temp0); + device_create_file(dev, &dev_attr_temp1); + } + if (ts->model != 7845) + device_create_file(dev, &dev_attr_vbatt); + device_create_file(dev, &dev_attr_vaux); + + return 0; +} + +static int __exit ads7846_remove(struct device *dev) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + + ads7846_suspend(dev, PMSG_SUSPEND EXTRA2); + free_irq(ts->spi->irq, ts); + if (ts->irq_disabled) + enable_irq(ts->spi->irq); + + if (ts->model == 7846) { + device_remove_file(dev, &dev_attr_temp0); + device_remove_file(dev, &dev_attr_temp1); + } + if (ts->model != 7845) + device_remove_file(dev, &dev_attr_vbatt); + device_remove_file(dev, &dev_attr_vaux); + + input_unregister_device(&ts->input); + kfree(ts); + + dev_dbg(dev, "unregistered touchscreen\n"); + return 0; +} + +static struct device_driver ads7846_driver = { + .name = "ads7846", + .bus = &spi_bus_type, + .probe = ads7846_probe, + .remove = __exit_p(ads7846_remove), + .suspend = ads7846_suspend, + .resume = ads7846_resume, +}; + +static int __init ads7846_init(void) +{ + /* grr, board-specific init should stay out of drivers!! */ + +#ifdef CONFIG_ARCH_OMAP + if (machine_is_omap_osk()) { + /* GPIO4 = PENIRQ; GPIO6 = BUSY */ + omap_request_gpio(4); + omap_set_gpio_direction(4, 1); + omap_request_gpio(6); + omap_set_gpio_direction(6, 1); + } + // also TI 1510 Innovator, bitbanging through FPGA + // also Nokia 770 + // also Palm Tungsten T2 +#endif + + // PXA: + // also Dell Axim X50 + // also HP iPaq H191x/H192x/H415x/H435x + // also Intel Lubbock (alternate to UCB1400) + // also Sharp Zaurus C7xx, C8xx (corgi/sheperd/husky) + + // also various AMD Au1x00 devel boards + + return driver_register(&ads7846_driver); +} +module_init(ads7846_init); + +static void __exit ads7846_exit(void) +{ + driver_unregister(&ads7846_driver); + +#ifdef CONFIG_ARCH_OMAP + if (machine_is_omap_osk()) { + omap_free_gpio(4); + omap_free_gpio(6); + } +#endif + +} +module_exit(ads7846_exit); + +MODULE_DESCRIPTION("ADS7846 TouchScreen Driver"); +MODULE_LICENSE("GPL"); |
From: David B. <da...@pa...> - 2005-11-23 07:08:47
|
This is the latest refresh of my simple SPI framework. I confess I changed a few lines of code without retesting, so let me know if the change to name devices "spiB.C" (not "spiB.C-drivername") went bad for any reason. I'm sending it with two SPI protocol drivers, ads7846 and dataflash. Those are examples, both of which could stand testing on more hardware. - Dave ======================================== This is the core of a small SPI framework, implementing the model of a queue of messages which complete asynchronously (with thin synchronous wrappers on top). - It's still less than 2KB of ".text" (ARM). If there's got to be a mid-layer for something so simple, that's the right size budget. :) - The guts use board-specific SPI device tables to build the driver model tree. (Hardware probing is rarely an option.) - This version of Kconfig includes no drivers. At this writing there are two known master controller drivers (PXA/SSP, OMAP MicroWire) and three protocol drivers (CS8415a, ADS7846, DataFlash) with LKML mentions of other drivers in development. - No userspace API. There are several implementations to compare. Implement them like any other driver, and bind them with sysfs. The changes from last version posted to LKML (on 11-Nov-2005) are minor, and include: - One bugfix (removes a FIXME), with the visible effect of making device names be "spiB.C" where B is the bus number and C is the chipselect. - The "caller provides DMA mappings" mechanism now has kerneldoc, for DMA drivers that want to be fancy. - Hey, the framework init can be subsys_init. Even though board init logic fires earlier, at arch_init ... since the framework init is for driver support, and the board init support uses static init. - Various additional spec/doc clarifications based on discussions with other folk. It adds a brief "thank you" at the end, for folk who've helped nudge this framework into existence. As I've said before, I think that "protocol tweaking" is the main support that this driver framework will need to evolve. Signed-off-by: David Brownell <dbr...@us...> --- arch/arm/Kconfig | 2 drivers/Kconfig | 2 drivers/Makefile | 1 g26/Documentation/spi/spi-summary | 416 ++++++++++++++++++++++++++++ g26/drivers/spi/Kconfig | 91 ++++++ g26/drivers/spi/Makefile | 23 + g26/drivers/spi/spi.c | 556 ++++++++++++++++++++++++++++++++++++++ g26/include/linux/spi/spi.h | 542 +++++++++++++++++++++++++++++++++++++ 8 files changed, 1633 insertions(+) --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ g26/include/linux/spi/spi.h 2005-11-22 18:40:07.000000000 -0800 @@ -0,0 +1,542 @@ +/* + * Copyright (C) 2005 David Brownell + * + * 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. + */ + +#ifndef __LINUX_SPI_H +#define __LINUX_SPI_H + +/* + * INTERFACES between SPI master drivers and infrastructure + * (There's no SPI slave support for Linux yet...) + * + * A "struct device_driver" for an spi_device uses "spi_bus_type" and + * needs no special API wrappers (much like platform_bus). These drivers + * are bound to devices based on their names (much like platform_bus), + * and are available in dev->driver. + */ +extern struct bus_type spi_bus_type; + +/** + * struct spi_device - Master side proxy for an SPI slave device + * @dev: Driver model representation of the device. + * @master: SPI controller used with the device. + * @max_speed_hz: Maximum clock rate to be used with this chip + * (on this board); may be changed by the device's driver. + * @chip-select: Chipselect, distinguishing chips handled by "master". + * @mode: The spi mode defines how data is clocked out and in. + * This may be changed by the device's driver. + * @bits_per_word: Data transfers involve one or more words; word sizes + * like eight or 12 bits are common. In-memory wordsizes are + * powers of two bytes (e.g. 20 bit samples use 32 bits). + * This may be changed by the device's driver. + * @irq: Negative, or the number passed to request_irq() to receive + * interrupts from this device. + * @controller_state: Controller's runtime state + * @controller_data: Static board-specific definitions for controller, such + * as FIFO initialization parameters; from board_info.controller_data + * + * An spi_device is used to interchange data between an SPI slave + * (usually a discrete chip) and CPU memory. + * + * In "dev", the platform_data is used to hold information about this + * device that's meaningful to the device's protocol driver, but not + * to its controller. One example might be an identifier for a chip + * variant with slightly different functionality. + */ +struct spi_device { + struct device dev; + struct spi_master *master; + u32 max_speed_hz; + u8 chip_select; + u8 mode; +#define SPI_CPHA 0x01 /* clock phase */ +#define SPI_CPOL 0x02 /* clock polarity */ +#define SPI_MODE_0 (0|0) +#define SPI_MODE_1 (0|SPI_CPHA) +#define SPI_MODE_2 (SPI_CPOL|0) +#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA) +#define SPI_CS_HIGH 0x04 /* chipselect active high? */ + u8 bits_per_word; + int irq; + void *controller_state; + const void *controller_data; + const char *modalias; + + // likely need more hooks for more protocol options affecting how + // the controller talks to its chips, like: + // - bit order (default is wordwise msb-first) + // - memory packing (12 bit samples into low bits, others zeroed) + // - priority + // - chipselect delays + // - ... +}; + +static inline struct spi_device *to_spi_device(struct device *dev) +{ + return container_of(dev, struct spi_device, dev); +} + +/* most drivers won't need to care about device refcounting */ +static inline struct spi_device *spi_dev_get(struct spi_device *spi) +{ + return (spi && get_device(&spi->dev)) ? spi : NULL; +} + +static inline void spi_dev_put(struct spi_device *spi) +{ + if (spi) + put_device(&spi->dev); +} + +/* ctldata is for the bus_master driver's runtime state */ +static inline void *spi_get_ctldata(struct spi_device *spi) +{ + return spi->controller_state; +} + +static inline void spi_set_ctldata(struct spi_device *spi, void *state) +{ + spi->controller_state = state; +} + + +struct spi_message; + + +/** + * struct spi_master - interface to SPI master controller + * @cdev: class interface to this driver + * @bus_num: board-specific (and often SOC-specific) identifier for a + * given SPI controller. + * @num_chipselects: chipselects are used to distinguish individual + * SPI slaves, and are numbered from zero to num_chipselects. + * each slave has a chipselect signal, but it's common that not + * every chipselect is connected to a slave. + * @setup: updates the device mode and clocking records used by a + * device's SPI controller; protocol code may call this. + * @transfer: adds a message to the controller's transfer queue. + * @cleanup: frees controller-specific state + * + * Each SPI master controller can communicate with one or more spi_device + * children. These make a small bus, sharing MOSI, MISO and SCK signals + * but not chip select signals. Each device may be configured to use a + * different clock rate, since those shared signals are ignored unless + * the chip is selected. + * + * The driver for an SPI controller manages access to those devices through + * a queue of spi_message transactions, copyin data between CPU memory and + * an SPI slave device). For each such message it queues, it calls the + * message's completion function when the transaction completes. + */ +struct spi_master { + struct class_device cdev; + + /* other than zero (== assign one dynamically), bus_num is fully + * board-specific. usually that simplifies to being SOC-specific. + * example: one SOC has three SPI controllers, numbered 1..3, + * and one board's schematics might show it using SPI-2. software + * would normally use bus_num=2 for that controller. + */ + u16 bus_num; + + /* chipselects will be integral to many controllers; some others + * might use board-specific GPIOs. + */ + u16 num_chipselect; + + /* setup mode and clock, etc (spi driver may call many times) */ + int (*setup)(struct spi_device *spi); + + /* bidirectional bulk transfers + * + * + The transfer() method may not sleep; its main role is + * just to add the message to the queue. + * + For now there's no remove-from-queue operation, or + * any other request management + * + To a given spi_device, message queueing is pure fifo + * + * + The master's main job is to process its message queue, + * selecting a chip then transferring data + * + If there are multiple spi_device children, the i/o queue + * arbitration algorithm is unspecified (round robin, fifo, + * priority, reservations, preemption, etc) + * + * + Chipselect stays active during the entire message + * (unless modified by spi_transfer.cs_change != 0). + * + The message transfers use clock and SPI mode parameters + * previously established by setup() for this device + */ + int (*transfer)(struct spi_device *spi, + struct spi_message *mesg); + + /* called on release() to free memory provided by spi_master */ + void (*cleanup)(const struct spi_device *spi); +}; + +/* the spi driver core manages memory for the spi_master classdev */ +extern struct spi_master * +spi_alloc_master(struct device *host, unsigned size); + +extern int spi_register_master(struct spi_master *master); +extern void spi_unregister_master(struct spi_master *master); + +extern struct spi_master *spi_busnum_to_master(u16 busnum); + +/*---------------------------------------------------------------------------*/ + +/* + * I/O INTERFACE between SPI controller and protocol drivers + * + * Protocol drivers use a queue of spi_messages, each transferring data + * between the controller and memory buffers. + * + * The spi_messages themselves consist of a series of read+write transfer + * segments. Those segments always read the same number of bits as they + * write; but one or the other is easily ignored by passing a null buffer + * pointer. (This is unlike most types of I/O API, because SPI hardware + * is full duplex.) + * + * NOTE: Allocation of spi_transfer and spi_message memory is entirely + * up to the protocol driver, which guarantees the integrity of both (as + * well as the data buffers) for as long as the message is queued. + */ + +/** + * struct spi_transfer - a read/write buffer pair + * @tx_buf: data to be written (dma-safe address), or NULL + * @rx_buf: data to be read (dma-safe address), or NULL + * @tx_dma: DMA address of buffer, if spi_message.is_dma_mapped + * @rx_dma: DMA address of buffer, if spi_message.is_dma_mapped + * @len: size of rx and tx buffers (in bytes) + * @cs_change: affects chipselect after this transfer completes + * @delay_usecs: microseconds to delay after this transfer before + * (optionally) changing the chipselect status, then starting + * the next transfer or completing this spi_message. + * + * SPI transfers always write the same number of bytes as they read. + * Protocol drivers should always provide rx_buf and/or tx_buf. + * In some cases, they may also want to provide DMA addresses for + * the data being transferred; that may reduce overhead, when the + * underlying driver uses dma. + * + * All SPI transfers start with the relevant chipselect active. Drivers + * can change behavior of the chipselect after the transfer finishes + * (including any mandatory delay). The normal behavior is to leave it + * selected, except for the last transfer in a message. Setting cs_change + * allows two additional behavior options: + * + * (i) If the transfer isn't the last one in the message, this flag is + * used to make the chipselect briefly go inactive in the middle of the + * message. Toggling chipselect in this way may be needed to terminate + * a chip command, letting a single spi_message perform all of group of + * chip transactions together. + * + * (ii) When the transfer is the last one in the message, the chip may + * stay selected until the next transfer. This is purely a performance + * hint; the controller driver may need to select a different device + * for the next message. + */ +struct spi_transfer { + /* it's ok if tx_buf == rx_buf (right?) + * for MicroWire, one buffer must be null + * buffers must work with dma_*map_single() calls + */ + const void *tx_buf; + void *rx_buf; + unsigned len; + + dma_addr_t tx_dma; + dma_addr_t rx_dma; + + unsigned cs_change:1; + u16 delay_usecs; +}; + +/** + * struct spi_message - one multi-segment SPI transaction + * @transfers: the segements of the transaction + * @n_transfer: how many segments + * @spi: SPI device to which the transaction is queued + * @is_dma_mapped: if true, the caller provided both dma and cpu virtual + * addresses for each transfer buffer + * @complete: called to report transaction completions + * @context: the argument to complete() when it's called + * @actual_length: how many bytes were transferd + * @status: zero for success, else negative errno + * @queue: for use by whichever driver currently owns the message + * @state: for use by whichever driver currently owns the message + */ +struct spi_message { + struct spi_transfer *transfers; + unsigned n_transfer; + + struct spi_device *spi; + + unsigned is_dma_mapped:1; + + /* REVISIT: we might want a flag affecting the behavior of the + * last transfer ... allowing things like "read 16 bit length L" + * immediately followed by "read L bytes". Basically imposing + * a specific message scheduling algorithm. + * + * Some controller drivers (message-at-a-time queue processing) + * could provide that as their default scheduling algorithm. But + * others (with multi-message pipelines) would need a flag to + * tell them about such special cases. + */ + + /* completion is reported through a callback */ + void FASTCALL((*complete)(void *context)); + void *context; + unsigned actual_length; + int status; + + /* for optional use by whatever driver currently owns the + * spi_message ... between calls to spi_async and then later + * complete(), that's the spi_master controller driver. + */ + struct list_head queue; + void *state; +}; + +/** + * spi_setup -- setup SPI mode and clock rate + * @spi: the device whose settings are being modified + * + * SPI protocol drivers may need to update the transfer mode if the + * device doesn't work with the mode 0 default. They may likewise need + * to update clock rates or word sizes from initial values. This function + * changes those settings, and must be called from a context that can sleep. + */ +static inline int +spi_setup(struct spi_device *spi) +{ + return spi->master->setup(spi); +} + + +/** + * spi_async -- asynchronous SPI transfer + * @spi: device with which data will be exchanged + * @message: describes the data transfers, including completion callback + * + * This call may be used in_irq and other contexts which can't sleep, + * as well as from task contexts which can sleep. + * + * The completion callback is invoked in a context which can't sleep. + * Before that invocation, the value of message->status is undefined. + * When the callback is issued, message->status holds either zero (to + * indicate complete success) or a negative error code. + * + * Note that although all messages to a spi_device are handled in + * FIFO order, messages may go to different devices in other orders. + * Some device might be higher priority, or have various "hard" access + * time requirements, for example. + */ +static inline int +spi_async(struct spi_device *spi, struct spi_message *message) +{ + message->spi = spi; + return spi->master->transfer(spi, message); +} + +/*---------------------------------------------------------------------------*/ + +/* All these synchronous SPI transfer routines are utilities layered + * over the core async transfer primitive. Here, "synchronous" means + * they will sleep uninterruptibly until the async transfer completes. + */ + +extern int spi_sync(struct spi_device *spi, struct spi_message *message); + +/** + * spi_write - SPI synchronous write + * @spi: device to which data will be written + * @buf: data buffer + * @len: data buffer size + * + * This writes the buffer and returns zero or a negative error code. + * Callable only from contexts that can sleep. + */ +static inline int +spi_write(struct spi_device *spi, const u8 *buf, size_t len) +{ + struct spi_transfer t = { + .tx_buf = buf, + .rx_buf = NULL, + .len = len, + .cs_change = 0, + }; + struct spi_message m = { + .transfers = &t, + .n_transfer = 1, + }; + + return spi_sync(spi, &m); +} + +/** + * spi_read - SPI synchronous read + * @spi: device from which data will be read + * @buf: data buffer + * @len: data buffer size + * + * This writes the buffer and returns zero or a negative error code. + * Callable only from contexts that can sleep. + */ +static inline int +spi_read(struct spi_device *spi, u8 *buf, size_t len) +{ + struct spi_transfer t = { + .tx_buf = NULL, + .rx_buf = buf, + .len = len, + .cs_change = 0, + }; + struct spi_message m = { + .transfers = &t, + .n_transfer = 1, + }; + + return spi_sync(spi, &m); +} + +extern int spi_write_then_read(struct spi_device *spi, + const u8 *txbuf, unsigned n_tx, + u8 *rxbuf, unsigned n_rx); + +/** + * spi_w8r8 - SPI synchronous 8 bit write followed by 8 bit read + * @spi: device with which data will be exchanged + * @cmd: command to be written before data is read back + * + * This returns the (unsigned) eight bit number returned by the + * device, or else a negative error code. Callable only from + * contexts that can sleep. + */ +static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd) +{ + ssize_t status; + u8 result; + + status = spi_write_then_read(spi, &cmd, 1, &result, 1); + + /* return negative errno or unsigned value */ + return (status < 0) ? status : result; +} + +/** + * spi_w8r16 - SPI synchronous 8 bit write followed by 16 bit read + * @spi: device with which data will be exchanged + * @cmd: command to be written before data is read back + * + * This returns the (unsigned) sixteen bit number returned by the + * device, or else a negative error code. Callable only from + * contexts that can sleep. + * + * The number is returned in wire-order, which is at least sometimes + * big-endian. + */ +static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd) +{ + ssize_t status; + u16 result; + + status = spi_write_then_read(spi, &cmd, 1, (u8 *) &result, 2); + + /* return negative errno or unsigned value */ + return (status < 0) ? status : result; +} + +/*---------------------------------------------------------------------------*/ + +/* + * INTERFACE between board init code and SPI infrastructure. + * + * No SPI driver ever sees these SPI device table segments, but + * it's how the SPI core (or adapters that get hotplugged) grows + * the driver model tree. + * + * As a rule, SPI devices can't be probed. Instead, board init code + * provides a table listing the devices which are present, with enough + * information to bind and set up the device's driver. There's basic + * support for nonstatic configurations too; enough to handle adding + * parport adapters, or microcontrollers acting as USB-to-SPI bridges. + */ + +/* board-specific information about each SPI device */ +struct spi_board_info { + /* the device name and module name are coupled, like platform_bus; + * "modalias" is normally the driver name. + * + * platform_data goes to spi_device.dev.platform_data, + * controller_data goes to spi_device.platform_data, + * irq is copied too + */ + char modalias[KOBJ_NAME_LEN]; + const void *platform_data; + const void *controller_data; + int irq; + + /* slower signaling on noisy or low voltage boards */ + u32 max_speed_hz; + + + /* bus_num is board specific and matches the bus_num of some + * spi_master that will probably be registered later. + * + * chip_select reflects how this chip is wired to that master; + * it's less than num_chipselect. + */ + u16 bus_num; + u16 chip_select; + + /* ... may need additional spi_device chip config data here. + * avoid stuff protocol drivers can set; but include stuff + * needed to behave without being bound to a driver: + * - chipselect polarity + * - quirks like clock rate mattering when not selected + */ +}; + +#ifdef CONFIG_SPI +extern int +spi_register_board_info(struct spi_board_info const *info, unsigned n); +#else +/* board init code may ignore whether SPI is configured or not */ +static inline int +spi_register_board_info(struct spi_board_info const *info, unsigned n) + { return 0; } +#endif + + +/* If you're hotplugging an adapter with devices (parport, usb, etc) + * use spi_new_device() to describe each device. You can also call + * spi_unregister_device() to get start making that device vanish, + * but normally that would be handled by spi_unregister_master(). + */ +extern struct spi_device * +spi_new_device(struct spi_master *, struct spi_board_info *); + +static inline void +spi_unregister_device(struct spi_device *spi) +{ + if (spi) + device_unregister(&spi->dev); +} + +#endif /* __LINUX_SPI_H */ --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ g26/drivers/spi/spi.c 2005-11-22 18:40:07.000000000 -0800 @@ -0,0 +1,556 @@ +/* + * spi.c - SPI init/core code + * + * Copyright (C) 2005 David Brownell + * + * 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/autoconf.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/cache.h> +#include <linux/spi/spi.h> + + +/* SPI bustype and spi_master class are registered during early boot, + * usually before board init code provides the SPI device tables, and + * are available later when driver init code needs them. + * + * Drivers for SPI devices started out like those for platform bus + * devices. But both have changed in 2.6.15; maybe this should get + * an "spi_driver" structure at some point (not currently needed) + */ +static void spidev_release(struct device *dev) +{ + const struct spi_device *spi = to_spi_device(dev); + + /* spi masters may cleanup for released devices */ + if (spi->master->cleanup) + spi->master->cleanup(spi); + + class_device_put(&spi->master->cdev); + kfree(dev); +} + +static ssize_t +modalias_show(struct device *dev, struct device_attribute *a, char *buf) +{ + const struct spi_device *spi = to_spi_device(dev); + + return snprintf(buf, BUS_ID_SIZE + 1, "%s\n", spi->modalias); +} + +static struct device_attribute spi_dev_attrs[] = { + __ATTR_RO(modalias), + __ATTR_NULL, +}; + +/* modalias support makes "modprobe $MODALIAS" new-style hotplug work, + * and the sysfs version makes coldplug work too. + */ + +static int spi_match_device(struct device *dev, struct device_driver *drv) +{ + const struct spi_device *spi = to_spi_device(dev); + + return strncmp(spi->modalias, drv->name, BUS_ID_SIZE) == 0; +} + +static int spi_hotplug(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + const struct spi_device *spi = to_spi_device(dev); + + envp[0] = buffer; + snprintf(buffer, buffer_size, "MODALIAS=%s", spi->modalias); + envp[1] = NULL; + return 0; +} + +#ifdef CONFIG_PM + +/* 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. + * + * NOTE: the suspend() method for an spi_master controller driver + * should verify that all its child devices are marked as suspended; + * suspend requests delivered through sysfs power/state files don't + * enforce such constraints. + */ +static int spi_suspend(struct device *dev, pm_message_t message) +{ + int value; + + if (!dev->driver || !dev->driver->suspend) + return 0; + + /* suspend will stop irqs and dma; no more i/o */ + value = dev->driver->suspend(dev, message); + if (value == 0) + dev->power.power_state = message; + return value; +} + +static int spi_resume(struct device *dev) +{ + int value; + + if (!dev->driver || !dev->driver->resume) + return 0; + + /* resume may restart the i/o queue */ + value = dev->driver->resume(dev); + if (value == 0) + dev->power.power_state = PMSG_ON; + return value; +} + +#else +#define spi_suspend NULL +#define spi_resume NULL +#endif + +struct bus_type spi_bus_type = { + .name = "spi", + .dev_attrs = spi_dev_attrs, + .match = spi_match_device, + .hotplug = spi_hotplug, + .suspend = spi_suspend, + .resume = spi_resume, +}; +EXPORT_SYMBOL_GPL(spi_bus_type); + +/*-------------------------------------------------------------------------*/ + +/* SPI devices should normally not be created by SPI device drivers; that + * would make them board-specific. Similarly with SPI master drivers. + * Device registration normally goes into like arch/.../mach.../board-YYY.c + * with other readonly (flashable) information about mainboard devices. + */ + +struct boardinfo { + struct list_head list; + unsigned n_board_info; + struct spi_board_info board_info[0]; +}; + +static LIST_HEAD(board_list); +static DECLARE_MUTEX(board_lock); + + +/* On typical mainboards, this is purely internal; and it's not needed + * after board init creates the hard-wired devices. Some development + * platforms may not be able to use spi_register_board_info though, and + * this is exported so that for example a USB or parport based adapter + * driver could add devices (which it would learn about out-of-band). + */ +struct spi_device *__init_or_module +spi_new_device(struct spi_master *master, struct spi_board_info *chip) +{ + struct spi_device *proxy; + struct device *dev = master->cdev.dev; + int status; + + /* NOTE: caller did any chip->bus_num checks necessary */ + + if (!class_device_get(&master->cdev)) + return NULL; + + proxy = kzalloc(sizeof *proxy, GFP_KERNEL); + if (!proxy) { + dev_err(dev, "can't alloc dev for cs%d\n", + chip->chip_select); + goto fail; + } + proxy->master = master; + proxy->chip_select = chip->chip_select; + proxy->max_speed_hz = chip->max_speed_hz; + proxy->irq = chip->irq; + proxy->modalias = chip->modalias; + + snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id, + "%s.%u", master->cdev.class_id, + chip->chip_select); + proxy->dev.parent = dev; + proxy->dev.bus = &spi_bus_type; + proxy->dev.platform_data = (void *) chip->platform_data; + proxy->controller_data = chip->controller_data; + proxy->controller_state = NULL; + proxy->dev.release = spidev_release; + + /* drivers may modify this default i/o setup */ + status = master->setup(proxy); + if (status < 0) { + dev_dbg(dev, "can't %s %s, status %d\n", + "setup", proxy->dev.bus_id, status); + goto fail; + } + + /* driver core catches callers that misbehave by defining + * devices that already exist. + */ + status = device_register(&proxy->dev); + if (status < 0) { + dev_dbg(dev, "can't %s %s, status %d\n", + "add", proxy->dev.bus_id, status); +fail: + class_device_put(&master->cdev); + kfree(proxy); + return NULL; + } + dev_dbg(dev, "registered child %s\n", proxy->dev.bus_id); + return proxy; +} +EXPORT_SYMBOL_GPL(spi_new_device); + +/* + * Board-specific early init code calls this (probably during arch_initcall) + * with segments of the SPI device table. Any device nodes are created later, + * after the relevant parent SPI controller (bus_num) is defined. We keep + * this table of devices forever, so that reloading a controller driver will + * not make Linux forget about these hard-wired devices. + * + * Other code can also call this, e.g. a particular add-on board might provide + * SPI devices through its expansion connector, so code initializing that board + * would naturally declare its SPI devices. + * + * The board info passed can safely be __initdata ... but be careful of + * any embedded pointers (platform_data, etc), they're copied as-is. + */ +int __init +spi_register_board_info(struct spi_board_info const *info, unsigned n) +{ + struct boardinfo *bi; + + bi = kmalloc (sizeof (*bi) + n * sizeof (*info), GFP_KERNEL); + if (!bi) + return -ENOMEM; + bi->n_board_info = n; + memcpy(bi->board_info, info, n * sizeof (*info)); + + down(&board_lock); + list_add_tail(&bi->list, &board_list); + up(&board_lock); + return 0; +} +EXPORT_SYMBOL_GPL(spi_register_board_info); + +/* FIXME someone should add support for a __setup("spi", ...) that + * creates board info from kernel command lines + */ + +static void __init_or_module +scan_boardinfo(struct spi_master *master) +{ + struct boardinfo *bi; + struct device *dev = master->cdev.dev; + + down(&board_lock); + list_for_each_entry(bi, &board_list, list) { + struct spi_board_info *chip = bi->board_info; + unsigned n; + + for (n = bi->n_board_info; n > 0; n--, chip++) { + if (chip->bus_num != master->bus_num) + continue; + /* some controllers only have one chip, so they + * might not use chipselects. otherwise, the + * chipselects are numbered 0..max. + */ + if (chip->chip_select >= master->num_chipselect + && master->num_chipselect) { + dev_dbg(dev, "cs%d > max %d\n", + chip->chip_select, + master->num_chipselect); + continue; + } + (void) spi_new_device(master, chip); + } + } + up(&board_lock); +} + +/*-------------------------------------------------------------------------*/ + +static void spi_master_release(struct class_device *cdev) +{ + struct spi_master *master; + + master = container_of(cdev, struct spi_master, cdev); + put_device(master->cdev.dev); + master->cdev.dev = NULL; + kfree(master); +} + +static struct class spi_master_class = { + .name = "spi_master", + .owner = THIS_MODULE, + .release = spi_master_release, +}; + + +/** + * spi_alloc_master - allocate SPI master controller + * @dev: the controller, possibly using the platform_bus + * @size: how much driver-private data to preallocate; a pointer to this + * memory in the class_data field of the returned class_device + * + * This call is used only by SPI master controller drivers, which are the + * only ones directly touching chip registers. It's how they allocate + * an spi_master structure, prior to calling spi_add_master(). + * + * This must be called from context that can sleep. It returns the SPI + * master structure on success, else NULL. + * + * The caller is responsible for assigning the bus number and initializing + * the master's methods before calling spi_add_master(), or else (on error) + * calling class_device_put() to prevent a memory leak. + */ +struct spi_master * __init_or_module +spi_alloc_master(struct device *dev, unsigned size) +{ + struct spi_master *master; + + master = kzalloc(size + sizeof *master, SLAB_KERNEL); + if (!master) + return NULL; + + master->cdev.class = &spi_master_class; + master->cdev.dev = get_device(dev); + class_set_devdata(&master->cdev, &master[1]); + + return master; +} +EXPORT_SYMBOL_GPL(spi_alloc_master); + +/** + * spi_register_master - register SPI master controller + * @master: initialized master, originally from spi_alloc_master() + * + * SPI master controllers connect to their drivers using some non-SPI bus, + * such as the platform bus. The final stage of probe() in that code + * includes calling spi_register_master() to hook up to this SPI bus glue. + * + * SPI controllers use board specific (often SOC specific) bus numbers, + * and board-specific addressing for SPI devices combines those numbers + * with chip select numbers. Since SPI does not directly support dynamic + * device identification, boards need configuration tables telling which + * chip is at which address. + * + * This must be called from context that can sleep. It returns zero on + * success, else a negative error code (dropping the master's refcount). + */ +int __init_or_module +spi_register_master(struct spi_master *master) +{ + static atomic_t dyn_bus_id = ATOMIC_INIT(0); + struct device *dev = master->cdev.dev; + int status = -ENODEV; + int dynamic = 0; + + /* convention: dynamically assigned bus IDs count down from the max */ + if (master->bus_num == 0) { + master->bus_num = atomic_dec_return(&dyn_bus_id); + dynamic = 0; + } + + /* register the device, then userspace will see it. + * registration fails if the bus ID is in use. + */ + snprintf(master->cdev.class_id, sizeof master->cdev.class_id, + "spi%u", master->bus_num); + status = class_device_register(&master->cdev); + if (status < 0) { + class_device_put(&master->cdev); + goto done; + } + dev_dbg(dev, "registered master %s%s\n", master->cdev.class_id, + dynamic ? " (dynamic)" : ""); + + /* populate children from any spi device tables */ + scan_boardinfo(master); + status = 0; +done: + return status; +} +EXPORT_SYMBOL_GPL(spi_register_master); + + +static int __unregister(struct device *dev, void *unused) +{ + /* note: before about 2.6.14-rc1 this would corrupt memory: */ + device_unregister(dev); + return 0; +} + +/** + * spi_unregister_master - unregister SPI master controller + * @master: the master being unregistered + * + * This call is used only by SPI master controller drivers, which are the + * only ones directly touching chip registers. + * + * This must be called from context that can sleep. + */ +void spi_unregister_master(struct spi_master *master) +{ + class_device_unregister(&master->cdev); + (void) device_for_each_child(master->cdev.dev, NULL, __unregister); +} +EXPORT_SYMBOL_GPL(spi_unregister_master); + +/** + * spi_busnum_to_master - look up master associated with bus_num + * @bus_num: the master's bus number + * + * This call may be used with devices that are registered after + * arch init time. It returns a refcounted pointer to the relevant + * spi_master (which the caller must release), or NULL if there is + * no such master registered. + */ +struct spi_master *spi_busnum_to_master(u16 bus_num) +{ + if (bus_num) { + char name[8]; + struct kobject *bus; + + snprintf(name, sizeof name, "spi%u", bus_num); + bus = kset_find_obj(&spi_master_class.subsys.kset, name); + if (bus) + return container_of(bus, struct spi_master, cdev.kobj); + } + return NULL; +} +EXPORT_SYMBOL_GPL(spi_busnum_to_master); + + +/*-------------------------------------------------------------------------*/ + +/** + * spi_sync - blocking/synchronous SPI data transfers + * @spi: device with which data will be exchanged + * @message: describes the data transfers + * + * This call may only be used from a context that may sleep. The sleep + * is non-interruptible, and has no timeout. Low-overhead controller + * drivers may DMA directly into and out of the message buffers. + * + * Note that the SPI device's chip select is active during the message, + * and then is normally disabled between messages. Drivers for some + * frequently-used devices may want to minimize costs of selecting a chip, + * by leaving it selected in anticipation that the next message will go + * to the same chip. (That may increase power usage.) + * + * The return value is a negative error code if the message could not be + * submitted, else zero. When the value is zero, then message->status is + * also defined: it's the completion code for the transfer, either zero + * or a negative error code from the controller driver. + */ +int spi_sync(struct spi_device *spi, struct spi_message *message) +{ + DECLARE_COMPLETION(done); + int status; + + message->complete = (void (*)(void *)) complete; + message->context = &done; + status = spi_async(spi, message); + if (status == 0) + wait_for_completion(&done); + message->context = NULL; + return status; +} +EXPORT_SYMBOL(spi_sync); + +#define SPI_BUFSIZ (SMP_CACHE_BYTES) + +static u8 *buf; + +/** + * spi_write_then_read - SPI synchronous write followed by read + * @spi: device with which data will be exchanged + * @txbuf: data to be written (need not be dma-safe) + * @n_tx: size of txbuf, in bytes + * @rxbuf: buffer into which data will be read + * @n_rx: size of rxbuf, in bytes (need not be dma-safe) + * + * This performs a half duplex MicroWire style transaction with the + * device, sending txbuf and then reading rxbuf. The return value + * is zero for success, else a negative errno status code. + * + * Parameters to this routine are always copied using a small buffer, + * large transfers should use use spi_{async,sync}() calls with + * dma-safe buffers. + */ +int spi_write_then_read(struct spi_device *spi, + const u8 *txbuf, unsigned n_tx, + u8 *rxbuf, unsigned n_rx) +{ + static DECLARE_MUTEX(lock); + + int status; + struct spi_message message; + struct spi_transfer x[2]; + + /* Use preallocated DMA-safe buffer. We can't avoid copying here, + * (as a pure convenience thing), but we can keep heap costs + * out of the hot path. + */ + if ((n_tx + n_rx) > SPI_BUFSIZ) + return -EINVAL; + + down(&lock); + memset(x, 0, sizeof x); + + memcpy(buf, txbuf, n_tx); + x[0].tx_buf = buf; + x[0].len = n_tx; + + x[1].rx_buf = buf + n_tx; + x[1].len = n_rx; + + /* do the i/o */ + message.transfers = x; + message.n_transfer = ARRAY_SIZE(x); + status = spi_sync(spi, &message); + if (status == 0) { + memcpy(rxbuf, x[1].rx_buf, n_rx); + status = message.status; + } + + up(&lock); + return status; +} +EXPORT_SYMBOL(spi_write_then_read); + +/*-------------------------------------------------------------------------*/ + +static int __init spi_init(void) +{ + buf = kmalloc(SPI_BUFSIZ, SLAB_KERNEL); + if (!buf) + return -ENOMEM; + + bus_register(&spi_bus_type); + class_register(&spi_master_class); + return 0; +} +/* board_info is normally registered in arch_initcall(), + * but even essential drivers wait till later + */ +subsys_initcall(spi_init); + --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ g26/drivers/spi/Kconfig 2005-11-22 18:40:07.000000000 -0800 @@ -0,0 +1,91 @@ +# +# SPI driver configuration +# +# NOTE: the reason this doesn't show SPI slave support is mostly that +# nobody's needed a slave side API yet. The master-role API is not +# fully appropriate there, so it'd need some thought to do well. +# +menu "SPI support" + +# someday this stuff should be set using arch/CPU/PLATFORM/Kconfig +config SPI_ARCH_HAS_MASTER + boolean + default y if ARCH_AT91 + default y if ARCH_OMAP + default y if ARCH_PXA + default y if X86 # devel hack only!! (ICH7 can...) + +config SPI_ARCH_HAS_SLAVE + boolean + default y if ARCH_AT91 + default y if ARCH_OMAP + default y if ARCH_PXA + +config SPI + bool "SPI support" + depends on SPI_ARCH_HAS_MASTER || SPI_ARCH_HAS_SLAVE + help + The "Serial Peripheral Interface" is a low level synchronous + protocol. Chips that support SPI can have data transfer rates + up to several tens of Mbit/sec. Chips are addressed with a + controller and a chipselect. Most SPI slaves don't support + dynamic device discovery; some are even write-only or read-only. + + SPI is widely used by microcontollers to talk with sensors, + eeprom and flash memory, codecs and various other controller + chips, analog to digital (and d-to-a) converters, and more. + MMC and SD cards can be accessed using SPI protocol; and for + DataFlash cards used in MMC sockets, SPI must always be used. + + SPI is one of a family of similar protocols using a four wire + interface (select, clock, data in, data out) including Microwire + (half duplex), SSP, SSI, and PSP. This driver framework should + work with most such devices and controllers. + +config SPI_DEBUG + boolean "Debug support for SPI drivers" + depends on SPI && DEBUG_KERNEL + help + Say "yes" to enable debug messaging (like dev_dbg and pr_debug), + sysfs, and debugfs support in SPI controller and protocol drivers. + +# +# MASTER side ... talking to discrete SPI slave chips including microcontrollers +# + +config SPI_MASTER +# boolean "SPI Master Support" + boolean + default SPI && SPI_ARCH_HAS_MASTER + help + If your system has an master-capable SPI controller (which + provides the clock and chipselect), you can enable that + controller and the protocol drivers for the SPI slave chips + that are connected. + +comment "SPI Master Controller Drivers" + depends on SPI_MASTER + + +# +# Add new SPI master controllers in alphabetical order above this line +# + + +# +# There are lots of SPI device types, with sensors and memory +# being probably the most widely used ones. +# +comment "SPI Protocol Masters" + depends on SPI_MASTER + + +# +# Add new SPI protocol masters in alphabetical order above this line +# + + +# (slave support would go here) + +endmenu # "SPI support" + --- g26.orig/arch/arm/Kconfig 2005-11-22 18:40:01.000000000 -0800 +++ g26/arch/arm/Kconfig 2005-11-22 18:40:07.000000000 -0800 @@ -742,6 +742,8 @@ source "drivers/char/Kconfig" source "drivers/i2c/Kconfig" +source "drivers/spi/Kconfig" + source "drivers/hwmon/Kconfig" #source "drivers/l3/Kconfig" --- g26.orig/drivers/Kconfig 2005-11-22 18:40:01.000000000 -0800 +++ g26/drivers/Kconfig 2005-11-22 18:40:07.000000000 -0800 @@ -44,6 +44,8 @@ source "drivers/char/Kconfig" source "drivers/i2c/Kconfig" +source "drivers/spi/Kconfig" + source "drivers/w1/Kconfig" source "drivers/hwmon/Kconfig" --- g26.orig/drivers/Makefile 2005-11-22 18:40:01.000000000 -0800 +++ g26/drivers/Makefile 2005-11-22 18:40:07.000000000 -0800 @@ -40,6 +40,7 @@ obj-$(CONFIG_FUSION) += message/ obj-$(CONFIG_IEEE1394) += ieee1394/ obj-y += cdrom/ obj-$(CONFIG_MTD) += mtd/ +obj-$(CONFIG_SPI) += spi/ obj-$(CONFIG_PCCARD) += pcmcia/ obj-$(CONFIG_DIO) += dio/ obj-$(CONFIG_SBUS) += sbus/ --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ g26/drivers/spi/Makefile 2005-11-22 18:40:07.000000000 -0800 @@ -0,0 +1,23 @@ +# +# Makefile for kernel SPI drivers. +# + +ifeq ($(CONFIG_SPI_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif + +# small core, mostly translating board-specific +# config declarations into driver model code +obj-$(CONFIG_SPI_MASTER) += spi.o + +# SPI master controller drivers (bus) +# ... add above this line ... + +# SPI protocol drivers (device/link on bus) +# ... add above this line ... + +# SPI slave controller drivers (upstream link) +# ... add above this line ... + +# SPI slave drivers (protocol for that link) +# ... add above this line ... --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ g26/Documentation/spi/spi-summary 2005-11-22 18:40:07.000000000 -0800 @@ -0,0 +1,416 @@ +Overview of Linux kernel SPI support +==================================== + +22-Nov-2005 + +What is SPI? +------------ +The "Serial Peripheral Interface" (SPI) is a four-wire point-to-point +serial link used to connect microcontrollers to sensors and memory. + +The three signal wires hold a clock (SCLK, often on the order of 10 MHz), +and parallel data lines with "Master Out, Slave In" (MOSI) or "Master In, +Slave Out" (MISO) signals. (Other names are also used.) There are four +clocking modes through which data is exchanged; mode-0 and mode-3 are most +commonly used. + +SPI masters may use a "chip select" line to activate a given SPI slave +device, so those three signal wires may be connected to several chips +in parallel. All SPI slaves support chipselects. Some devices have +other signals, often including an interrupt to the master. + +Unlike serial busses like USB or SMBUS, even low level protocols for +SPI slave functions are usually not interoperable between vendors +(except for cases like SPI memory chips). + + - SPI may be used for request/response style device protocols, as with + touchscreen sensors and memory chips. + + - It may also be used to stream data in either direction (half duplex), + or both of them at the same time (full duplex). + + - Some devices may use eight bit words. Others may different word + lengths, such as streams of 12-bit or 20-bit digital samples. + +In the same way, SPI slaves will only rarely support any kind of automatic +discovery/enumeration protocol. The tree of slave devices accessible from +a given SPI master will normally be set up manually, with configuration +tables. + +SPI is only one of the names used by such four-wire protocols, and +most controllers have no problem handling "MicroWire" (think of it as +half-duplex SPI, for request/response protocols), SSP ("Synchronous +Serial Protocol"), PSP ("Programmable Serial Protocol"), and other +related protocols. + +Microcontrollers often support both master and slave sides of the SPI +protocol. This document (and Linux) currently only supports the master +side of SPI interactions. + + +Who uses it? On what kinds of systems? +--------------------------------------- +Linux developers using SPI are probably writing device drivers for embedded +systems boards. SPI is used to control external chips, and it is also a +protocol supported by every MMC or SD memory card. (The older "DataFlash" +cards, predating MMC cards but using the same connectors and card shape, +support only SPI.) Some PC hardware uses SPI flash for BIOS code. + +SPI slave chips range from digital/analog converters used for analog +sensors and codecs, to memory, to peripherals like USB controllers +or Ethernet adapters; and more. + +Most systems using SPI will integrate a few devices on a mainboard. +Some provide SPI links on expansion connectors; in cases where no +dedicated SPI controller exists, GPIO pins can be used to create a +low speed "bitbanging" adapter. Very few systems will "hotplug" an SPI +controller; the reasons to use SPI focus on low cost and simple operation, +and if dynamic reconfiguration is important, USB will often be a more +appropriate low-pincount peripheral bus. + +Many microcontrollers that can run Linux integrate one or more I/O +interfaces with SPI modes. Given SPI support, they could use MMC or SD +cards without needing a special purpose MMC/SD/SDIO controller. + + +How do these driver programming interfaces work? +------------------------------------------------ +The <linux/spi/spi.h> header file includes kerneldoc, as does the +main source code, and you should certainly read that. This is just +an overview, so you get the big picture before the details. + +There are two types of SPI driver, here called: + + Controller drivers ... these are often built in to System-On-Chip + processors, and often support both Master and Slave roles. + These drivers touch hardware registers and may use DMA. + + Protocol drivers ... these pass messages through the controller + driver to communicate with a Slave or Master device on the + other side of an SPI link. + +So for example one protocol driver might talk to the MTD layer to export +data to filesystems stored on SPI flash like DataFlash; and others might +control audio interfaces, present touchscreen sensors as input interfaces, +or monitor temperature and voltage levels during industrial processing. +And those might all be sharing the same controller driver. + +A "struct spi_device" encapsulates the master-side interface between +those two types of driver. At this writing, Linux has no slave side +programming interface. + +There is a minimal core of SPI programming interfaces, focussing on +using driver model to connect controller and protocol drivers using +device tables provided by board specific initialization code. SPI +shows up in sysfs in several locations: + + /sys/devices/.../CTLR/spiB.C ... spi_device for on bus "B", + chipselect C, accessed through CTLR. + + /sys/bus/spi/devices/spiB.C ... symlink to the physical + spiB-C device + + /sys/bus/spi/drivers/D ... driver for one or more spi*.* devices + + /sys/class/spi_master/spiB ... class device for the controller + managing bus "B". All the spiB.* devices share the same + physical SPI bus segment, with SCLK, MOSI, and MISO. + +The basic I/O primitive submits an asynchronous message to an I/O queue +maintained by the controller driver. A completion callback is issued +asynchronously when the data transfer(s) in that message completes. +There are also some simple synchronous wrappers for those calls. + + +How does board-specific init code declare SPI devices? +------------------------------------------------------ +Linux needs several kinds of information to properly configure SPI devices. +That information is normally provided by board-specific code, even for +chips that do support some of automated discovery/enumeration. + +DECLARE CONTROLLERS + +The first kind of information is a list of what SPI controllers exist. +For System-on-Chip (SOC) based boards, these will usually be platform +devices, and the controller may need some platform_data in order to +operate properly. The "struct platform_device" will include resources +like the physical address of the controller's first register and its IRQ. + +Platforms will often abstract the "register SPI controller" operation, +maybe coupling it with code to initialize pin configurations, so that +the arch/.../mach-*/board-*.c files for several boards can all share the +same basic controller setup code. This is because most SOCs have several +SPI-capable controllers, and only the ones actually usable on a given +board should normally be set up and registered. + +So for example arch/.../mach-*/board-*.c files might have code like: + + #include <asm/arch/spi.h> /* for mysoc_spi_data */ + + /* if your mach-* infrastructure doesn't support kernels that can + * run on multiple boards, pdata wouldn't benefit from "__init". + */ + static struct mysoc_spi_data __init pdata = { ... }; + + static __init board_init(void) + { + ... + /* this board only uses SPI controller #2 */ + mysoc_register_spi(2, &pdata); + ... + } + +And SOC-specific utility code might look something like: + + #include <asm/arch/spi.h> + + static struct platform_device spi2 = { ... }; + + void mysoc_register_spi(unsigned n, struct mysoc_spi_data *pdata) + { + struct mysoc_spi_data *pdata2; + + pdata2 = kmalloc(sizeof *pdata2, GFP_KERNEL); + *pdata2 = pdata; + ... + if (n == 2) { + spi2->dev.platform_data = pdata2; + register_platform_device(&spi2); + + /* also: set up pin modes so the spi2 signals are + * visible on the relevant pins ... bootloaders on + * production boards may already have done this, but + * developer boards will often need Linux to do it. + */ + } + ... + } + +Notice how the platform_data for boards may be different, even if the +same SOC controller is used. For example, on one board SPI might use +an external clock, where another derives the SPI clock from current +settings of some master clock. + + +DECLARE SLAVE DEVICES + +The second kind of information is a list of what SPI slave devices exist +on the target board, often with some board-specific data needed for the +driver to work correctly. + +Normally your arch/.../mach-*/board-*.c files would provide a small table +listing the SPI devices on each board. (This would typically be only a +small handful.) That might look like: + + static struct ads7846_platform_data ads_info = { + .vref_delay_usecs = 100, + .x_plate_ohms = 580, + .y_plate_ohms = 410, + }; + + static struct spi_board_info spi_board_info[] __initdata = { + { + .modalias = "ads7846", + .platform_data = &ads_info, + .mode = SPI_MODE_0, + .irq = GPIO_IRQ(31), + .max_speed_hz = 120000 /* max sample rate at 3V */ * 16, + .bus_num = 1, + .chip_select = 0, + }, + }; + +Again, notice how board-specific information is provided; each chip may need +several types. This example shows generic constraints like the fastest SPI +clock to allow (a function of board voltage in this case) or how an IRQ pin +is wired, plus chip-specific constraints like an important delay that's +changed by the capacitance at one pin. + +(There's also "controller_data", information that may be useful to the +controller driver. An example would be peripheral-specific DMA tuning +data or chipselect callbacks. This is stored in spi_device later.) + +The board_info should provide enough information to let the system work +without the chip's driver being loaded. The most troublesome aspect of +that is likely the SPI_CS_HIGH bit in the spi_device.mode field, since +sharing a bus with a device that interprets chipselect "backwards" is +not possible. + +Then your board initialization code would register that table with the SPI +infrastructure, so that it's available later when the SPI master controller +driver is registered: + + spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); + +Like with other static board-specific setup, you won't unregister those. + + +NON-STATIC CONFIGURATIONS + +Developer boards often play by different rules than product boards, and one +example is the potential need to hotplug SPI devices and/or controllers. + +For those cases you might need to use use spi_busnum_to_master() to look +up the spi bus master, and will likely need spi_new_device() to provide the +board info based on the board that was hotplugged. Of course, you'd later +call at least spi_unregister_device() when that board is removed. + + +How do I write an "SPI Protocol Driver"? +---------------------------------------- +All SPI drivers are currently kernel drivers. A userspace driver API +would just be another kernel driver, probably offering some lowlevel +access through aio_read(), aio_write(), and ioctl() calls and using the +standard userspace sysfs mechanisms to bind to a given SPI device. + +SPI protocol drivers are normal device drivers, with no more wrapper +than needed by platform devices: + + static struct device_driver CHIP_driver = { + .name = "CHIP", + .bus = &spi_bus_type, + .probe = CHIP_probe, + .remove = __exit_p(CHIP_remove), + .suspend = CHIP_suspend, + .resume = CHIP_resume, + }; + +The SPI core will autmatically attempt to bind this driver to any SPI +device whose board_info gave a modalias of "CHIP". Your probe() code +might look like this unless you're creating a class_device: + + static int __init CHIP_probe(struct device *dev) + { + struct spi_device *spi = to_spi_device(dev); + struct CHIP *chip; + struct CHIP_platform_data *pdata = dev->platform_data; + + /* get memory for driver's per-chip state */ + chip = kzalloc(sizeof *chip, GFP_KERNEL); + if (!chip) + return -ENOMEM; + dev_set_drvdata(dev, chip); + + ... etc + return 0; + } + +As soon as it enters probe(), the driver may issue I/O requests to +the SPI device using "struct spi_message". When remove() returns, +the driver guarantees that it won't submit any more such messages. + + - An spi_message is a sequence of of protocol operations, executed + as one atomic sequence. SPI driver controls include: + + + when bidirectional reads and writes start ... by how its + sequence of spi_transfer requests is arranged; + + + optionally defining short delays after transfers ... using + the spi_transfer.delay_usecs setting; + + + whether the chipselect becomes inactive after a transfer and + any delay ... by using the spi_transfer.cs_change flag; + + + hinting whether the next message is likely to go to this same + device ... using the spi_transfer.cs_change flag on the last + transfer in that atomic group, and potentially saving costs + for chip deselect and select operations. + + - Follow standard kernel rules, and provide DMA-safe buffers in + your messages. That way controller drivers using DMA aren't forced + to make extra copies unless the hardware requires it (e.g. working + around hardware errata that force the use of bounce buffering). + + If standard dma_map_single() handling of these buffers is inappropriate, + you can use spi_message.is_dma_mapped to tell the controller driver + that you've already provided the relevant DMA addresses. + + - The basic I/O primitive is spi_async(). Async requests may be + issued in any context (irq handler, task, etc) and completion + is reported using a callback provided with the message. + + - There are also synchronous wrappers like spi_sync(), and wrappers + like spi_read(), spi_write(), and spi_write_then_read(). These + may be issued only in contexts that may sleep, and they're all + clean (and small, and "optional") layers over spi_async(). + + - The spi_write_then_read() call, and convenience wrappers around + it, should only be used with small amounts of data where the + cost of an extra copy may be ignored. It's designed to support + common RPC-style requests, such as writing an eight bit command + and reading a sixteen bit response -- spi_w8r16() being one its + wrappers, doing exactly that. + +Some drivers may need to modify spi_device characteristics like the +transfer mode, wordsize, or clock rate. This is done with spi_setup(), +which would normally be called from probe() before the first I/O is +done to the device. + +While "spi_device" would be the bottom boundary of the driver, the +upper boundaries might include sysfs (especially for sensor readings), +the input layer, ALSA, networking, MTD, the character device framework, +or other Linux subsystems. + + +How do I write an "SPI Master Controller Driver"? +------------------------------------------------- +An SPI controller will probably be registered on the platform_bus; write +a driver to bind to the device, whichever bus is involved. + +The main task of this type of driver is to provide an "spi_master". +Use spi_alloc_master() to allocate the master, and class_get_devdata() +to get the driver-private data allocated for that device. + + struct spi_master *master; + struct CONTROLLER *c; + + master = spi_alloc_master(dev, sizeof *c); + if (!master) + return -ENODEV; + + c = class_get_devdata(&master->cdev); + +The driver will initialize the fields of that spi_master, including the +bus number (maybe the same as the platform device ID) and three methods +used to interact with the SPI core and SPI protocol drivers. It will +also initialize its own internal state. + + master->setup(struct spi_device *spi) + This sets up the device clock rate, SPI mode, and word sizes. + Drivers may change the defaults provided by board_info, and then + call spi_setup(spi) to invoke this routine. It may sleep. + + master->transfer(struct spi_device *spi, struct spi_message *message) + This must not sleep. Its responsibility is arrange that the + transfer happens and its complete() callback is issued; the two + will normally happen later, after other transfers complete. + + master->cleanup(struct spi_device *spi) + Your controller driver may use spi_device.controller_state to hold + state it dynamically associates with that device. If you do that, + be sure to provide the cleanup() method to free that state. + +The bulk of the driver will be managing the I/O queue fed by transfer(). + +That queue could be purely conceptual. For example, a driver used only +for low-frequency sensor acess might be fine using synchronous PIO. + +But the queue will probably be very real, using message->queue, PIO, +often DMA (especially if the root filesystem is in SPI flash), and +execution contexts like IRQ handlers, tasklets, or workqueues (such +as keventd). Your driver can be as fancy, or as simple, as you need. + + +THANKS ... [truncated message content] |
From: David B. <da...@pa...> - 2005-11-23 03:24:42
|
On Tuesday 22 November 2005 11:09 am, Greg KH wrote: > On Tue, Nov 22, 2005 at 11:09:58AM +0300, Vitaly Wool wrote: > > Hi Greg, > > > > IMHO your expert opinion on both cores should be very useful for final > > convergence! > > Ok, can both of you resend your patches so I can take a look at them? Done. The core is the first one. - Dave |
From: Greg KH <gr...@kr...> - 2005-11-22 19:37:21
|
On Tue, Nov 22, 2005 at 11:09:58AM +0300, Vitaly Wool wrote: > Hi Greg, > > IMHO your expert opinion on both cores should be very useful for final > convergence! Ok, can both of you resend your patches so I can take a look at them? thanks, greg k-h |
From: Mark U. <bas...@ya...> - 2005-11-22 19:11:26
|
--- Vitaly Wool <vw...@ru...> wrote: > 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. How does this work? As I said before I can see that you can leave a cs active, but how can you make sure the next message is for that SPI device and not for another one? > > > > > > > > >>>- (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. No, but it's a good indication :). > > > > > > > > >>>- (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. Neither proposal is complete. > > > > > > > > >>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. You don't need _as many_ flags as in (B) > > >>>- (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. But suppose you have a SPI controller which has cs lines in the IP block. Are you saying you will write another driver which manipulates the registers in the SPI controller out side of the adapter driver? Some hardware might not support that (i.e. you can't directly controll the cs pin). Every solution will always work better on some hardware then other hardware. The point is to make the solution work on all types hardware, here I think David's solution is better. Mark > > Vitaly > > ___________________________________________________________ Does your mail provider give you FREE antivirus protection? Get Yahoo! Mail http://uk.mail.yahoo.com |
From: Vitaly W. <vw...@ru...> - 2005-11-22 17:03:21
|
David Brownell wrote: >On Monday 21 November 2005 10:44 pm, 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 :) >> >> > >Me too, but it's felt like "the everyone agrees" step has a few holdouts >with more invested in their own approach than in achieving consensus or >in addressing various technical issues that have been raised repeatedly. > > I'm afraid that our understanding of the word "consensus" differs. Have to point out also that I haven't seen adequate answers on the "various thechnical issues" raised by Dmitry or me. Vitaly |
From: David B. <da...@pa...> - 2005-11-22 16:57:35
|
On Monday 21 November 2005 10:44 pm, 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 :) Me too, but it's felt like "the everyone agrees" step has a few holdouts with more invested in their own approach than in achieving consensus or in addressing various technical issues that have been raised repeatedly. I'm not sure I'll want to spend too much more time pushing on this, given that degree of non-cooperation. Unless that changes, I think the only way to get an SPI framework into kernel.org code would be someone just deciding between one of the (two?) alternatives. - Dave |