commit b53feb3568ddd6e7b29e0674c7cf3e4f64c33aab Merge: d063edb... 82de618... 94a73cb... Author: Arnd Bergmann Date: Mon Jul 9 21:04:34 2007 +0200 Merge branches 'cell-merge' and 'ps3-linux' into ps3-merge Conflicts: MAINTAINERS commit 94a73cb88a970b281e5de0f73dfbfa66f80e6c59 Merge: 2b83d1b... 7dcca30... Author: Geoff Levand Date: Mon Jul 9 09:00:28 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 2b83d1b820ef5450e1516b9735008e0cc6ee4ab8 Merge: 8c687bb... d23cf67... Author: Geoff Levand Date: Sat Jul 7 16:13:04 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 8c687bbf02847a281d0f2bc4c5b8663a7408ed1c Author: Geoff Levand Date: Sat Jul 7 16:06:51 2007 -0700 [PS3] Merge in Geert's LOOP_CLR_FD fix. commit 612fa4f0ee10b8db3a6ca65aab77f6447c1cd0b4 Author: Geoff Levand Date: Sat Jul 7 16:02:23 2007 -0700 [PS3] Merge in Mokuno-san's zd1211rw patch and gelic fixes. commit d063edbd95fe02de86f456d064366d992d118536 Merge: 9b6d717... 15a822e... Author: Arnd Bergmann Date: Fri Jul 6 19:09:10 2007 +0200 Merge branch 'cell-merge' into ps3-merge Conflicts: arch/powerpc/boot/Makefile arch/powerpc/boot/of.c arch/powerpc/configs/ps3_defconfig arch/powerpc/platforms/cell/spu_base.c arch/powerpc/platforms/ps3/Kconfig arch/powerpc/platforms/ps3/htab.c arch/powerpc/platforms/ps3/setup.c drivers/ps3/sys-manager.c drivers/video/ps3fb.c include/asm-powerpc/spu.h commit 9b6d71714188351f89ec9c279c5c77601743224e Author: Geoff Levand Date: Tue Jul 3 15:56:03 2007 -0700 [PS3] Rebase ps3-zimage-zerocopy.diff to for-2.6.23. commit 5f1ef2b1f11675ae194471ce51450e3b8ba56223 Merge: a44dc27... 872aad4... Author: Geoff Levand Date: Tue Jul 3 13:27:41 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit a44dc274819e57e75ab831a92af3026581a31781 Author: Geoff Levand Date: Sat Jun 23 13:27:42 2007 -0700 [PS3] Merge in Geert's ps3fb debug fix. commit db795a5473f671dc3c7575e9e5e13c11a759704f Author: Geoff Levand Date: Sat Jun 23 13:22:57 2007 -0700 [PS3] Merge in sound, storage, gelic cleanups. commit c51aee0f0227fc13c73d876638f616c992417968 Author: Geoff Levand Date: Mon Jun 18 17:14:01 2007 -0700 [PS3] Minor change to ps3-zimage-zerocopy.diff. commit efd51b8b63b5acfe59d467c221f2750eda9194f7 Author: Geoff Levand Date: Mon Jun 18 15:40:52 2007 -0700 [PS3] Review updates for ps3-zimage-zerocopy.diff. commit 011d8e31f6c45b9d71213adc866ac17763d3ff79 Merge: 2e17a6c... fa490cf... Author: Geoff Levand Date: Mon Jun 18 12:32:11 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 2e17a6c2b4130a9b11608dd6eb598c3cdf7df821 Author: Geoff Levand Date: Mon Jun 18 12:30:56 2007 -0700 [PS3] Merge in Geert's storage cleanups. commit cbbd64bb6b7a014c8792735d4eebca35980a5e4c Author: Geoff Levand Date: Mon Jun 18 12:28:35 2007 -0700 [PS3] Merge in Mokuno-san's gelic cleanups. commit 722fd51edd5b114eb3932a1be059a8b922d4f8a4 Author: Geoff Levand Date: Fri Jun 15 12:41:56 2007 -0700 [PS3] Geert's patch cleanups for 2.6.23. commit 1f662e465259291f7c5070fe44cf9e9f080c10dc Author: Geoff Levand Date: Thu Jun 14 20:14:59 2007 -0700 [PS3] Fix bug in wrapper dd call. commit 297362ab0f06a107291d82f4a0871f08fe44cad4 Author: Geoff Levand Date: Thu Jun 14 18:10:22 2007 -0700 [PS3] Quiet dd. commit 8a6330b5b9bb13dba52626dd8202e9a18753184a Merge: a7816e3... 0127d6d... Author: Geoff Levand Date: Thu Jun 14 17:07:47 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit a7816e37f8225575ae16e99187a75952d1daa0d8 Author: Geoff Levand Date: Thu Jun 14 17:06:25 2007 -0700 [PS3] Cleanups from checkpatch.pl. commit 923c3c750637e21a73bedba208b7ef8dd1207cb7 Author: Geoff Levand Date: Thu Jun 14 14:11:55 2007 -0700 [PS3] Mark ps3_register_devices() helpers __init. commit f5f9f3b53e94b1b4b56a303f5a45439ce6843dc2 Author: Geoff Levand Date: Thu Jun 14 13:39:05 2007 -0700 [PS3] Minor patch updates for 2.6.23 review. commit 59ba982d5a6b6a411f060cf2e271db0e571a7c37 Author: Geoff Levand Date: Thu Jun 14 11:25:17 2007 -0700 [PS3] Update ps3-use-ioremap-flags.diff. commit e100640872cdd3fb473f2ae3251fe667213af7e8 Author: Geoff Levand Date: Thu Jun 14 10:20:44 2007 -0700 [PS3] Update ps3-legacy-bootloader-hack.diff to match ps3.dts. commit dfa66a2680e912b416747a5dbb3eaf9d5b300830 Author: Geoff Levand Date: Thu Jun 14 09:51:16 2007 -0700 [PS3] Merge in Geert's storage updates. commit 430e7a9cc8f440002e95383b4c97f2c81b75ddd0 Author: Geoff Levand Date: Thu Jun 14 09:45:45 2007 -0700 [PS3] Add ps3-zimage-verbose.diff. commit bffe809aa0ac35088ca40ce1e7098bf4872324e5 Merge: 0b4e4f4... eedab66... Author: Geoff Levand Date: Wed Jun 13 18:27:10 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 0b4e4f42b1ff39a580168467f7d85e074277cf54 Author: Geoff Levand Date: Wed Jun 13 18:26:15 2007 -0700 [PS3] Update ps3.dts commit 0308ad025e6e018e782f6cef4ede91f1d5195e63 Author: Geoff Levand Date: Wed Jun 13 15:29:33 2007 -0700 [PS3] Merge in Geert's ioremap and ps3rom cleanups. commit f781400c1fc16306a59905decf404030d810d4d3 Author: Geoff Levand Date: Wed Jun 13 15:17:24 2007 -0700 [PS3] Merge in Mokuno-san's gelic cleanups. commit 94be1f2e8885baac0cf0f47da6077422bb1d9683 Author: Geoff Levand Date: Wed Jun 13 15:08:49 2007 -0700 [PS3] Update ps3-inline-DBG.diff. commit 962db7d0e67a7d91c91ee886088267bdb94b0f4e Merge: 141dee9... a0e1d1d... Author: Geoff Levand Date: Wed Jun 13 15:05:12 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 141dee97b9c712596498e79942c3435fdfd1bcda Merge: e2185e0... 99f9f3d... Author: Geoff Levand Date: Tue Jun 12 13:23:43 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit e2185e0439ad25fc409e44372fb465139c65f7ad Author: Geoff Levand Date: Tue Jun 12 13:23:08 2007 -0700 [PS3] Prepare patches for 2.6.23. commit 6cada2140e40e7b61f646bcd3ef2f129d7af5ff4 Author: Geoff Levand Date: Tue Jun 12 10:08:12 2007 -0700 [PS3] Merge in Geert's storage cleanups. commit c6fa88dcf1726b49c6a6582e75b86ec281f3f992 Author: Geoff Levand Date: Tue Jun 12 09:15:35 2007 -0700 [PS3] Prepare patches for 2.6.23. commit 1efe4517e2cab598b9a70839fbc141ce3f95f12c Author: Geoff Levand Date: Mon Jun 11 12:10:09 2007 -0700 [PS3] Minor cleanup to vuart drivers. commit 5a4f50e4e9c704a713717f77ca3b91f2f38d4661 Author: Geoff Levand Date: Mon Jun 11 10:05:56 2007 -0700 [PS3] Add ps3-inline-DBG.diff. commit 2764f682b01e6c298fbb41f539c0e672e02898f5 Author: Geoff Levand Date: Mon Jun 11 09:30:39 2007 -0700 [PS3] Merge in Geert's storage and av cleanups. commit 815e388cc0b25f113859c5938f623c388d6c603e Author: Geoff Levand Date: Sat Jun 9 17:12:42 2007 -0700 [PS3] Add ps3-use-clear_bit.diff. commit 37f350a16d892a6cbb1c4dc9031d4bc1376635c3 Author: Geoff Levand Date: Sat Jun 9 16:23:08 2007 -0700 [PS3] Add routine ps3_shutdown_IRQ(). commit c5f0051d823b85f917da9ff29d0ebf7d827b004b Merge: 3c87568... 845a2fd... Author: Geoff Levand Date: Fri Jun 8 21:49:49 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 3c875688d193d544bf5dc5637e9ce41f42b672bf Author: Geoff Levand Date: Fri Jun 8 21:48:27 2007 -0700 [PS3] Zero copy bootwrapper. commit a9b189ef174412e4f04c618aa2324bf953fc992f Author: Geoff Levand Date: Fri Jun 8 10:34:09 2007 -0700 [PS3] Merge in Geert's disk barrier updates. commit 6eb7c5c3bce29807da0786dae7b473fbc6885f95 Author: Geoff Levand Date: Fri Jun 8 10:30:07 2007 -0700 [PS3] Add ps3stor-fix-multi-plat.diff. commit 23d7c0b1e63911edadf8bff868d8c33540fb2581 Author: Geoff Levand Date: Fri Jun 8 10:27:20 2007 -0700 [PS3] Bootwrapper cleanup. commit 77c71b3d04c18749c7d39b9e83c3d26d3eebad6b Author: Geoff Levand Date: Thu Jun 7 12:21:02 2007 -0700 [PS3] Update defconfigs. commit 35ec22744ba49b7455205f12a167b52976cce091 Author: Geoff Levand Date: Thu Jun 7 10:30:44 2007 -0700 [PS3] Merge in Geert's fbcon unbinding fixes. commit a44791c282de6abf4a56d927130ce7699f5b84e5 Merge: 6a6e952... 7244d54... Author: Geoff Levand Date: Thu Jun 7 10:26:24 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 6a6e952b773f421d399e1991ec45cb3178e21987 Author: Geoff Levand Date: Thu Jun 7 10:24:54 2007 -0700 [PS3] Add gelic SET_NETDEV_DEV. commit e32bd6ae7cc4583d67cf000ff32a7517bf5126c6 Author: Geoff Levand Date: Wed Jun 6 20:02:43 2007 -0700 [PS3] Updates for 2.6.23 round one review. commit eb8203a40126a650178cb1b302751a506e4db130 Author: Geoff Levand Date: Wed Jun 6 13:43:38 2007 -0700 [PS3] Add ps3-use-ioremap-flags.diff. commit 679229bb09fcadf5a15fc6a7daa1c64ab6d88e4e Author: Geoff Levand Date: Wed Jun 6 11:34:37 2007 -0700 [PS3] Remove ps3-hack-fbcon-shutdown.diff. commit cb1fdc91bbae667b7bf2c34e61f60b347a06c0bd Author: Geoff Levand Date: Wed Jun 6 10:30:46 2007 -0700 [PS3] Merge in Geert's vuart fix. commit 1ea2fee4aa49c4684ccde228aa622380cd843d39 Author: Geoff Levand Date: Tue Jun 5 18:42:43 2007 -0700 [PS3] Prepare patches for 2.6.23. commit 06c1c1af99ea78ccd5a205ce224b5207c408e541 Merge: 3e9a662... 5ecd310... Author: Geoff Levand Date: Tue Jun 5 09:06:23 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 3e9a6624746cf93f747e482050b422f91e35b427 Author: Geoff Levand Date: Tue Jun 5 08:58:44 2007 -0700 [PS3] Merge in Geert's storage updates. commit 4aae404f868a7f205e29e03838d010dc42d0b28f Author: Geoff Levand Date: Tue Jun 5 08:48:08 2007 -0700 [PS3] Merge in Mokuno-san's firmware version re-work. commit 228dae20becf855380c597515b9dca824635a97d Author: Geoff Levand Date: Mon Jun 4 20:45:34 2007 -0700 [PS3] Device probe rework. commit 1304f77fde8b0c5814c6b5909c5cb6768c434b3c Merge: 68f0578... c1a13ff... Author: Geoff Levand Date: Mon Jun 4 13:44:56 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 68f05783af029b865311eca69677ea0c0b71a710 Author: Geoff Levand Date: Fri Jun 1 18:52:59 2007 -0700 [PS3] Fix zImage.initrd build. commit 8fef261b351650c357099bb985e0ff433f1c810d Author: Geoff Levand Date: Fri Jun 1 10:10:29 2007 -0700 [PS3] Merge in Geert's av and storage updates. commit a32aeec72ba5bddd50ebfe2a0f4eebd1a8f8e323 Author: Geoff Levand Date: Fri Jun 1 09:37:07 2007 -0700 [PS3] Merge in Mokuno-san's gelic cleanups. commit d901afff38571464ede919734f632afc7a157543 Merge: 4aa7d84... f285e3d... Author: Geoff Levand Date: Fri Jun 1 09:31:23 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 4aa7d847791c46b5077ffc50afda64251074d6ef Author: Geoff Levand Date: Thu May 31 21:17:49 2007 -0700 [PS3] Merge in Mokuno-san's gelic cleanups. commit ba5415b48e1890841d969e1e99960d30549c1367 Merge: 1a49fb7... 184b812... Author: Geoff Levand Date: Thu May 31 21:16:28 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 1a49fb77ee1d220d0ea79dc6758066e6fae97f21 Merge: 267a31a... f54496f... Author: Geoff Levand Date: Wed May 30 10:27:36 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 267a31a33690614cf6679b1a1a046bbd13b65295 Author: Geoff Levand Date: Wed May 30 10:26:35 2007 -0700 [PS3] Fix cell-add-spu-shutdown-method.diff. commit 5ca29317db42720ef181bf7c0b7d98cfe37e5e32 Merge: 5e229cf... 7f397dc... Author: Geoff Levand Date: Wed May 30 08:24:54 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 5e229cf188b1e18ba03c4eda2626b6ad100539fe Author: Geoff Levand Date: Wed May 30 08:24:02 2007 -0700 [PS3] Merge in Mokuno-san's gelic and sound cleanups. commit e851abb926fdefcf363545fdcd6032c1f29b881d Merge: 4d4cfd5... 6e98ee7... Author: Geoff Levand Date: Tue May 29 17:13:34 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 4d4cfd5a29fd01dc6b4cdaab7ab26bb3516172ee Author: Geoff Levand Date: Tue May 29 17:12:35 2007 -0700 [PS3] Make ps3_gelic module. commit d885ccadce075689057bf68d277dd4c744bf99a2 Author: Geoff Levand Date: Tue May 29 12:36:19 2007 -0700 [PS3] Merge in Geert's build fixes. commit 763bf92d3f9616048cf107af7a0eacbe8736ed91 Author: Geoff Levand Date: Tue May 29 12:09:59 2007 -0700 [PS3] Update defconfigs, add ps3_interrupt-c-uses-get_hard_smp_processor_id.diff. commit c67a055e4be81fefa5db7f3d1f3f915d9d42e9b8 Author: Geoff Levand Date: Sat May 26 14:51:15 2007 -0700 [PS3] Update defconfigs. commit ebd9fc7e47bff8c2e195b5f67e2ccebaa6c8d1f9 Merge: 4cd2f29... c420bc9... Author: Geoff Levand Date: Sat May 26 13:00:54 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 4cd2f290e4b45018fec1a9a8734a1735b1a3922c Author: Geoff Levand Date: Sat May 26 12:56:15 2007 -0700 [PS3] Make vuart loadable. commit 1ff2c56e2f2b388a8427632934a2df7ee7b8486e Merge: 58add21... 1c1ee4c... Author: Geoff Levand Date: Fri May 25 11:08:42 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 58add21543a55da200efede0e3dcc0cb840a24e7 Author: Geoff Levand Date: Fri May 25 11:07:42 2007 -0700 [PS3] Merge in Geert's system bus and storage updates. commit e33ce865bd05fc899f4ebd034b22562e1255372e Merge: 3fae0a9... d333fc8... Author: Geoff Levand Date: Thu May 24 10:44:48 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 3fae0a9f93ef40a9fdf4415a651e9907d4935e93 Author: Geoff Levand Date: Thu May 24 10:43:39 2007 -0700 [PS3] Merge in Geert's fb and storage updates. commit 488ca6076ac84c0ab1ac1236dec5a57513bf132f Author: Geoff Levand Date: Wed May 23 21:15:41 2007 -0700 [PS3] Loadable ps3av fixes. commit 4c1a6a88fb304fb99ee235fabc751542f14fa0f1 Merge: ee20397... 4598c95... Author: Geoff Levand Date: Wed May 23 13:21:33 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit ee203977a92d79288778c7423652e6b29866edbb Author: Geoff Levand Date: Wed May 23 13:20:43 2007 -0700 [PS3] Gelic and sorage cleanups. commit 2471e15f3d389dd6bc2d009f266e5d95076ef531 Merge: cb2ef45... ad9ddd6... Author: Geoff Levand Date: Tue May 22 18:19:46 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit cb2ef45fbd1f76c95c30e292624d8ef5dfa70d67 Author: Geoff Levand Date: Tue May 22 18:17:43 2007 -0700 [PS3] Make sound loadable. commit 8f6f3fe99e25bd7cfb3bc981b1dcd32666c82327 Merge: 7d99d81... cdb7532... Author: Geoff Levand Date: Tue May 22 18:04:53 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 7d99d814ad51ce744c37a6b13edb0ee194e7101b Author: Geoff Levand Date: Tue May 22 09:37:42 2007 -0700 [PS3] Pulled in Geert's system-bus fixes. commit c2c2e867d038df406bdbda23932b55fea7139cba Author: Geoff Levand Date: Tue May 22 09:27:01 2007 -0700 [PS3] Gelic cleanups. commit 998e39a32c3b41408631225725e92fa3ebd77b64 Merge: e5de73b... efa5bf1... Author: Geoff Levand Date: Mon May 21 19:40:50 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit e5de73bcabcd4a55c5bf482c6f6b0076fe1e55cb Author: Geoff Levand Date: Mon May 21 19:38:04 2007 -0700 [PS3] Make ps3fb loadable. commit e33c2b59326d92a7dbf9b5d12136353c907b6f63 Author: Geoff Levand Date: Sat May 19 15:08:34 2007 -0700 [PS3] Consolidate system-bus patches. commit da4eeb0513095730e3c232fd29012d25eaf5601e Merge: b605d77... 55b637c... Author: Geoff Levand Date: Sat May 19 09:07:47 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit b605d7707c99f6966a972ac7f51b5559de69682a Author: Geoff Levand Date: Sat May 19 09:06:53 2007 -0700 [PS3] Cleanup as-module patches. commit b40b4bf38ac1df07337e4d04a56040a8a5e0963f Merge: 031077a... 18963c0... Author: Geoff Levand Date: Fri May 18 21:09:05 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 031077a35f4039bbe163d554a05c7280bb35c129 Author: Geoff Levand Date: Fri May 18 21:08:02 2007 -0700 [PS3] Gelic cleanup. commit a10375c93276d6117d22fd878904d96ad796561e Author: Geoff Levand Date: Fri May 18 21:04:34 2007 -0700 [PS3] Make sys-manager loadable. commit dd98e6e77ffeaaf16c284024aa482fb9e909f86a Author: Geoff Levand Date: Thu May 17 10:47:47 2007 -0700 [PS3] Update defconfigs. commit b449b94fd311502ba4665e17a82348aa2c5a2428 Merge: 44ff250... 0479ea0... Author: Geoff Levand Date: Thu May 17 10:00:29 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 44ff250438d7d85e865c64f40670fec65607e70d Author: Geoff Levand Date: Thu May 17 09:59:19 2007 -0700 [PS3] Gelic cleanups. commit 2b73caaff26c5b88922f57ad46e0df51bd94a8b4 Author: Geoff Levand Date: Wed May 16 19:10:54 2007 -0700 [PS3] Fix ps3_dma_region free bug. commit b206d620230348da14b0cf07481f1fe6b850bb59 Merge: a150edd... 7b104bc... Author: Geoff Levand Date: Wed May 16 11:32:28 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit a150edddcdf596f101aaf5e1290f52259f766e67 Author: Geoff Levand Date: Wed May 16 11:31:37 2007 -0700 [PS3] Depreciate old storage driver. commit 59164abbe67338297504b9b0e8d69aeb839580e2 Author: Geoff Levand Date: Wed May 16 11:19:18 2007 -0700 [PS3] Fix no driver on reboot crash. commit cb9cb6e0972246ef98bdb01bb985ccf47f566af7 Merge: 54c8491... 0560551... Author: Geoff Levand Date: Tue May 15 18:20:29 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 54c84914357a11090864fa5d901676552912e0dc Author: Geoff Levand Date: Tue May 15 18:19:48 2007 -0700 [PS3] Storage cleanups. commit 1f3422ec0c3d9dcd6b9726b09f6a2b6731e3a6f7 Author: Geoff Levand Date: Tue May 15 18:19:05 2007 -0700 [PS3] Storage cleanups. commit d900e954a2cd8407877fd81783992717f732cafc Merge: db8286d... faa8b6c... Author: Geoff Levand Date: Mon May 14 18:53:38 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit db8286d1faec3914080180613d51f590feeb35c7 Author: Geoff Levand Date: Mon May 14 13:17:28 2007 -0700 [PS3] Fix reset. commit 1df2054c1272601c801ce23d81685583a0e62b6a Merge: f5f90dc... 705962c... Author: Geoff Levand Date: Mon May 14 12:00:40 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit f5f90dc8cfd1d7fa4629db52d7c110f16f15d2a7 Author: Geoff Levand Date: Mon May 14 11:58:47 2007 -0700 [PS3] Storage cleanups. commit 0b57c7dd2be632aee1791f9b286a337fd308ff0e Author: Geoff Levand Date: Sat May 12 17:26:47 2007 -0700 [PS3] Force button poweroff. commit 36faddaf1139ee1ab038e7ab8c1168a34a2d31b3 Author: Geoff Levand Date: Sat May 12 15:15:54 2007 -0700 [PS3] Fix power button. commit b85273b4a10dd2550c817abf026ea06bf3c2cf44 Merge: 2654b7b... f1d1a84... Author: Geoff Levand Date: Sat May 12 13:20:15 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 2654b7b119f78645c1a1c8aec661c827a9d5a4a5 Author: Geoff Levand Date: Sat May 12 13:17:33 2007 -0700 [PS3] Storage driver work. commit afdb121091c2122032b347e611716e1d569c0ad4 Merge: 04efa7d... 8df767d... Author: Geoff Levand Date: Sat May 12 11:23:41 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 04efa7de5bb64eade4e525622e79331a7945e9eb Author: Geoff Levand Date: Sat May 12 10:54:34 2007 -0700 [PS3] Update defconfig. commit 138516853504710107e002d37e14d4da060dd588 Merge: dc68d9f... 0a3fd05... Author: Geoff Levand Date: Fri May 11 18:29:04 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit dc68d9fc0e9aec4b7e70436a30327110d5ad46f5 Author: Geoff Levand Date: Fri May 11 18:08:06 2007 -0700 [PS3] Hack fbcon to work with sys_reboot(). commit 9d174cc5739a0b76e21679c3c5f4ec60069dac0f Merge: b4b7070... 1f8a6b6... Author: Geoff Levand Date: Fri May 11 11:47:31 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit b4b70705f609bb0242c7eb1a702f93d659a7bc7e Author: Geoff Levand Date: Fri May 11 11:46:26 2007 -0700 [PS3] Fix compile warnings. commit a5367b72833206afaa6427740350278fd02892b3 Merge: 773e1f9... a9deecb... Author: Geoff Levand Date: Thu May 10 19:03:18 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 773e1f9be1683e930b68dc98da86f4c5843f698b Author: Geoff Levand Date: Thu May 10 18:53:53 2007 -0700 [PS3] Fix sys_reboot() and NFS bugs. commit 59f2bfefbe7f6c9f9038eba937c173d609f1a1e1 Merge: 6d60a36... e991084... Author: Geoff Levand Date: Thu May 10 10:14:08 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 6d60a36829f47e7addf75875af8d5f966212610a Author: Geoff Levand Date: Thu May 10 10:12:30 2007 -0700 [PS3] Gelic and storage cleanups. commit 72202dfcd57ef610a3ff70651d94c56540452dbd Author: Geoff Levand Date: Wed May 9 15:03:34 2007 -0700 [PS3] Move otheros.bld to arch/powerpc/boot. commit 1365389ca1721a98a39efca4735a57e2463e533f Author: Geoff Levand Date: Wed May 9 14:59:55 2007 -0700 [PS3] Update defconfig. commit 300168f6604b112666260d05067c8220cef3ec90 Merge: 23ac012... 38cb162... Author: Geoff Levand Date: Wed May 9 14:17:08 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 23ac012a12df6c7cd0f20a69b75a18512fdcd266 Merge: d75f2f6... ba7cc09... Author: Geoff Levand Date: Wed May 9 13:28:29 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit d75f2f6056e1aa1626e5a83576949ba664c44baf Merge: 8005882... de372ec... Author: Geoff Levand Date: Wed May 9 11:26:44 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 800588203a9779842ccfde4e6d5a7b6d48b3d4ce Merge: 2858acb... 36f021b... Author: Geoff Levand Date: Wed May 9 11:22:02 2007 -0700 [PS3] Storage and logo updates. commit 2858acb95eff3d3b5d64d4c4f99ebe368373c0c4 Author: Geoff Levand Date: Mon May 7 16:06:41 2007 -0700 [PS3] Add fix-bt-build.diff. commit 30f0022bb7bc62cd70ccaa734d7c322a15489885 Merge: 2ca56a9... a989705... Author: Geoff Levand Date: Mon May 7 13:15:59 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 2ca56a90087fd8b830bd549d158bcd35e2ef3941 Merge: 75a0629... e3ebadd... Author: Geoff Levand Date: Mon May 7 11:25:26 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 75a0629c6796a7d19302a2dbfd4149d64532bb16 Author: Geoff Levand Date: Mon May 7 11:23:37 2007 -0700 [PS3] Add ps3-bt-event-filter.diff. commit 22a8c2a136b045d0a910a401cc7166eca9e4c91e Author: Geoff Levand Date: Mon May 7 10:33:03 2007 -0700 [PS3] Update defconfig. commit b0ba0f93ecd908047bf771bf8ab7896ef5c9b16f Merge: 0de4ce3... 1570077... Author: Geoff Levand Date: Mon May 7 10:04:03 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 0de4ce3baf1f80823087877563263f8b75d95b25 Author: Geoff Levand Date: Sat May 5 16:47:35 2007 -0700 [PS3] Kexec device work. commit ea96f0cb44574246e140cfa3e4ed145976f9ab01 Author: Geoff Levand Date: Thu May 3 13:35:49 2007 -0700 [PS3] Rework kexec support. commit 766339edf275f63edcb116c5e262a4efa1574942 Author: Geoff Levand Date: Wed May 2 10:32:53 2007 -0700 [PS3] Add gelic wireless. commit 52b6991082e330cd8a22e6f031b043a03b91955f Merge: e124e98... dc87c39... Author: Geoff Levand Date: Tue May 1 14:56:16 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit e124e9806c31f43b807d6072729372a3666af2ba Author: Geoff Levand Date: Tue May 1 14:55:01 2007 -0700 [PS3] Consolidate system bus rework into ps3-system-bus-rework.diff. commit 1302d2cac1bfb7422e07924a0029e6b9e26aa486 Author: Geoff Levand Date: Mon Apr 30 18:49:23 2007 -0700 [PS3] Consolodate dma rework into ps3-dma-rework.diff. commit eee29202b7ec21a8cc8b1097f5ca79bf220b3672 Merge: e8ca730... 40caf5e... Author: Geoff Levand Date: Mon Apr 30 14:47:01 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit e8ca730c25f8dcfa4cc7f2ea12e6bd99cbbd2e92 Merge: b7ba38a... cd9bb7e... Author: Geoff Levand Date: Mon Apr 30 12:14:10 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit b7ba38a4c6b4c0f4744972812c1f6dc5020cbe95 Merge: e7d08bc... de46c33... Author: Geoff Levand Date: Thu Apr 26 11:22:14 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit e7d08bc313a2bf79c2cb1257ef39a052a12b6a9c Author: Geoff Levand Date: Mon Apr 23 11:28:28 2007 -0700 [PS3] Update ps3_defconfig. commit 11ec8b59a644c38ed67bf40dace09002d24bda97 Merge: 3c8a5e7... c445a31... Author: Geoff Levand Date: Mon Apr 23 11:25:58 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 3c8a5e780bd25d473a6b95f8b2f2d121f72c56dd Merge: d7d05e5... 0f85102... Author: Geoff Levand Date: Sat Apr 21 10:08:45 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit d7d05e5b044a4944a82b93d7871e3a2c6c171ab3 Author: Geoff Levand Date: Sat Apr 21 10:08:14 2007 -0700 [PS3] New storage drivers. commit 8c1099f5198da6c3457dc9c229df7121afec0b81 Author: Geoff Levand Date: Wed Apr 18 11:45:33 2007 -0700 [PS3] Add firmware_has_feature to device-init, gelic. commit 07ac87e7cf9ab0718d17c8860000d7e8e45028cd Author: Geoff Levand Date: Wed Apr 18 10:03:47 2007 -0700 [PS3] Storage bus updates. commit 18278abdffc0ff08b917d0432987e22d72e58eb7 Merge: dc1edea... 80d74d5... Author: Geoff Levand Date: Wed Apr 18 09:59:46 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit dc1edea7fea646004c8e936471ad51a7344b0173 Author: Geoff Levand Date: Tue Apr 17 10:30:02 2007 -0700 [PS3] Set CONFIG_EXT2_FS=m. commit 8de6c99e8b80bae80fd90f2b94e7af7bdb14ad78 Merge: a925275... 6f29e35... Author: Geoff Levand Date: Tue Apr 17 09:41:40 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit a9252752b565f66ce963f1ff334b05c4b244f6ea Author: Geoff Levand Date: Mon Apr 16 19:06:31 2007 -0700 [PS3] Add powerpc-allow-duplicate-lmb_reserve.diff, powerpc-automatically-lmb_reserve-initrd.diff. commit cae5fba839e83542743e07cadbf1776b915603fa Author: Geoff Levand Date: Mon Apr 16 17:06:36 2007 -0700 [PS3] Add ps3-system-bus-add-modinfo-attribute.diff. commit 0c92104ac543af3e90401709bb834664bd300c56 Author: Geoff Levand Date: Mon Apr 16 15:58:45 2007 -0700 [PS3] Add ps3-gelic-fix-ipv6.diff. commit f3fdde9f726c9007c322070d82906dfe4a571432 Author: Geoff Levand Date: Mon Apr 16 15:45:45 2007 -0700 [PS3] Add cell-usb-workaround-ehci-iso.diff. commit b5b87adf8568c9d1663a2e17bdc2dc9cbeabcd93 Merge: 1afbb30... 94a0550... Author: Geoff Levand Date: Mon Apr 16 14:14:24 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 1afbb30c9383c9465cad83ad3eecf1f38aa65b9c Author: Geoff Levand Date: Mon Apr 16 14:10:11 2007 -0700 [PS3] Add boot mem to device tree. commit 13a91094f8a29f74374658cac7d07f0ad0e387da Author: Geoff Levand Date: Wed Apr 11 19:56:16 2007 -0700 [PS3] Added ps3-legacy-bootloader-hack.diff. commit 32b7b302184e12c63cc702747f926cd80b03c27a Merge: 2df3f01... 5ab7ffe... Author: Geoff Levand Date: Wed Apr 11 09:27:43 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 2df3f01790148d3c9e205ea31d8d80aeee3b7591 Author: Geoff Levand Date: Wed Apr 11 09:26:03 2007 -0700 [PS3] Fix bounce buffer for ATAPI. commit 5a3638c4e30757969cbfcab41255fe021923b9fa Author: Geoff Levand Date: Tue Apr 10 19:07:39 2007 -0700 [PS3] Fix CD playback. commit 3ad9f76ecc0b387bfa5c0e74c95bcac65d02703a Author: Geoff Levand Date: Tue Apr 10 17:49:37 2007 -0700 [PS3] zImage work. Changed makefile target names. Note the file name changes: zImage.ps3 -> vmlinux.strip zImage.ps3.wrap -> zImage.ps3 zImage.ps3.rom.gz -> otheros.bld commit 1cedbf505f4f38fc30279302e1792dddbb614540 Merge: 2bdf986... 90f30ec... Author: Geoff Levand Date: Tue Apr 10 16:24:25 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 2bdf98610323099fa26b185a538bdcaacc195aea Author: Geoff Levand Date: Sat Apr 7 16:41:00 2007 -0700 [PS3] Add ps3-check-legacy-ioport.diff, ps3-gelic-fix-as-module.diff, ps3-system-bus-uevent.diff. commit dc41a7414189b43747100a50c5155d82846227bb Author: Geoff Levand Date: Fri Apr 6 10:32:19 2007 -0700 [PS3] Add HV call to local_irq_restore(). commit b9c6d9aba996b732ea0a5788a930ac2e901b3e6b Author: Geoff Levand Date: Thu Apr 5 20:12:49 2007 -0700 [PS3] Correct generated file names to vmlinux.strip, otheros.bld. commit d23783f820b33c305ce32d53011a76198fa06879 Author: Geoff Levand Date: Thu Apr 5 19:45:13 2007 -0700 [PS3] Pass kexec cmdline to new kernel. commit 7470360fc51def8f39ece4b36c193486f982e4ed Author: Geoff Levand Date: Wed Apr 4 19:28:14 2007 -0700 [PS3] Set CONFIG_DEBUG_SLAB=n, disable ps3-use-handle-level-irq.diff. commit 12ecedbe013d04bc6775e64f5188c269bfecd199 Author: Geoff Levand Date: Wed Apr 4 15:37:47 2007 -0700 [PS3] Text cleanups. commit ce6ca928988e76409da393ee0ec4c4c28cd9d065 Author: Geoff Levand Date: Tue Apr 3 19:13:28 2007 -0700 [PS3] New slowdown fix. commit 8d84882087f005564a9305af5161471bfc021839 Author: Geoff Levand Date: Thu Mar 29 11:29:42 2007 -0700 [PS3] Remove -x from the boot wrapper target make rule. commit 4bc6e7ca36cc2a649441d61d775b6cc06123b2da Merge: 1a8860e... 399afa4... Author: Geoff Levand Date: Thu Mar 29 10:39:12 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 1a8860e910d0b13b9f5e5d9091fa26ecf45c58f5 Author: Geoff Levand Date: Thu Mar 29 10:36:52 2007 -0700 [PS3] Fix sound under-run. commit 844a0e4d177f6d7388cd2ce3c01404d90dd5b676 Author: Geoff Levand Date: Wed Mar 28 19:37:38 2007 -0700 [PS3] Add ps3-fix-slowdown-bug.diff. commit 912d311ddfe221c4a333d7df6a746470c8a6c51c Merge: 80c2fb3... 28defbe... Author: Geoff Levand Date: Wed Mar 28 11:09:02 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 80c2fb3efdd4280ae74d44973be6a1c88930a198 Merge: 94dde7c... d459094... Author: Geoff Levand Date: Tue Mar 27 10:29:27 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 94dde7c76e10dc06979ea7b5b05f05c2c3248813 Merge: 6b96860... 703071b... Author: Geoff Levand Date: Mon Mar 26 17:12:07 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 6b9686006fce686a39c24599fb3b0c33f320b7ae Author: Geoff Levand Date: Mon Mar 26 17:09:13 2007 -0700 [PS3] Enable CONFIG_BLK_DEV_LOOP. commit 809b239c3114a72cb936050243e0d5a59b515333 Author: Geoff Levand Date: Mon Mar 26 16:46:53 2007 -0700 [PS3] Sound, irq, storage, htab work. commit c2c0e3bf8337b0d6ccf9fc01193d47ecbfe2a454 Merge: 73fcb27... e0f2e3a... Author: Geoff Levand Date: Mon Mar 26 12:08:57 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 73fcb27cc21edcbdbd7f62d8e517d24d59d90e52 Author: Geoff Levand Date: Fri Mar 23 17:09:44 2007 -0700 [PS3] Added ps3-replace-irq-alloc-free.diff. commit 51172fabf32ae89d1aef977f8b04c2441ee6db5f Author: Geoff Levand Date: Fri Mar 23 12:10:21 2007 -0700 [PS3] Storage bus dma updates. commit a5714431e0d5bf147dcdc887ce9377352cf000dc Merge: 79615f9... e585bef... Author: Geoff Levand Date: Fri Mar 23 10:50:14 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 79615f93ba0b4e9d9124042b545b9aedbf56b125 Merge: 0a95ffe... 1299809... Author: Geoff Levand Date: Thu Mar 22 20:25:43 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 0a95ffe1aadfa6b3739cbf6887376fb652ec9554 Author: Geoff Levand Date: Thu Mar 22 20:24:31 2007 -0700 [PS3] Add spufs-always-release-mapping-lock.diff, spufs-fix-ctx-lifetimes.diff. commit 9b8ccc6298f40ae45e0b1888852f118090e41583 Author: Geoff Levand Date: Tue Mar 20 12:52:56 2007 -0700 [PS3] Remove bootwrapper stack. commit 4196ee40bc2d503995d812c5df448a179b512c49 Merge: b8f5bdb... 0a14fe6... Author: Geoff Levand Date: Tue Mar 20 08:55:49 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit b8f5bdbe87a6735581cdd45327f0fc0ecb54009d Author: Geoff Levand Date: Tue Mar 20 08:50:27 2007 -0700 [PS3] Add spufs-avoid-accessing-to-released-inode.diff. commit c2bc2b4bbd0d821f8041db70a827f06e23e3d147 Author: Geoff Levand Date: Tue Mar 20 07:50:43 2007 -0700 [PS3] Remove mode:1080p from ps3_defconfig. commit 6e295fe4c62f1d0831db5df52580ce1a8be0d653 Author: Geoff Levand Date: Fri Mar 16 13:29:54 2007 -0700 [PS3] Add support for non-OF SMP kernel entry. commit aa7309dcb189d1e3c10808fb8b4e3fe6225647cd Merge: 0e360a2... db98e0b... Author: Geoff Levand Date: Fri Mar 16 13:27:06 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 0e360a2391768a677b6cf306c621fcf832dd79ec Merge: 6a692ae... 8ce5e3e... Author: Geoff Levand Date: Thu Mar 15 10:45:59 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 6a692aee74db1bb37bbd8e0db498c81dd040f380 Merge: 3a4a77b... 4e337ad... Author: Geoff Levand Date: Wed Mar 14 19:07:23 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 3a4a77ba921630b718197b4b5650d987830f0b19 Author: Geoff Levand Date: Wed Mar 14 18:56:13 2007 -0700 [PS3] zImage: move smp_secondary_hold to crt0. commit d84a9ef7c6fdbef2d26d6b14213d0e65c8100a71 Author: Geoff Levand Date: Wed Mar 14 12:10:34 2007 -0700 [PS3] Fixes for SDK 2.0 toolchain. commit 09fa242c897f7f75295d2e8e0c89c56846a53eb4 Author: Geoff Levand Date: Tue Mar 13 19:10:38 2007 -0700 [PS3] Storage cleanups, defconfig updates. commit f6b737d6d912b27b9d11c0bdc6674568eaf6df09 Merge: b5285fa... baab108... Author: Geoff Levand Date: Tue Mar 13 19:02:33 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit b5285fa05037ad7677ccbb557710e313dcfdf194 Author: Geoff Levand Date: Tue Mar 13 18:58:17 2007 -0700 [PS3] Update defconfigs. commit aa9d3764c72f16c5ab1859ac095fe7378ee897bc Author: Geoff Levand Date: Mon Mar 12 15:54:10 2007 -0700 [PS3] Add missing ps3-wip/ps3stor_flash_write.diff. commit cf72dd0e73c31c0f39a29d00f83fa839de5a215a Author: Geoff Levand Date: Mon Mar 12 15:37:07 2007 -0700 [PS3] Use named video modes in defconfigs. commit 08c2a72b4073ee6a2f4a526dc48d05a8ba088a33 Merge: 1fe8afe... be52146... Author: Geoff Levand Date: Mon Mar 12 15:06:17 2007 -0700 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 1fe8afeb10f495bdd5c3f2be47e97269617a33cd Author: Geoff Levand Date: Mon Mar 12 14:43:26 2007 -0700 [PS3] Update defconfigs for FC6. commit 043f44e57c9ea8fc2db1bdcc3c085ad8fd95411c Author: Geoff Levand Date: Wed Mar 7 16:28:33 2007 -0800 [PS3] Update defconfigs. commit a115bf4ccfb781769b0ab684947eb721089d657e Merge: 3365970... 4559214... Author: Geoff Levand Date: Wed Mar 7 14:30:30 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 33659702d882bdd8fbf637d10c95c37d95c63657 Author: Geoff Levand Date: Wed Mar 7 14:22:38 2007 -0800 [PS3] Remove fb mode in CONFIG_CMDLINE. commit 0362e77def69ae3672b2a9843b524a61e9e9f9f7 Merge: 3e8c763... 20b0f65... Author: Geoff Levand Date: Mon Mar 5 08:46:01 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 3e8c763a49a291f82d68f80ba85c77df712badb5 Author: Geoff Levand Date: Mon Feb 26 08:29:03 2007 -0800 [PS3] Config fixes. commit 0475979cff9292a832d2560aff441f906764ab63 Author: Geoff Levand Date: Wed Feb 21 16:57:17 2007 -0800 [PS3] Storage updates. commit 2e5a666c3416c6dbdae8e78dffda2b93a3533d71 Merge: 0ed510d... 9654640... Author: Geoff Levand Date: Wed Feb 21 16:51:58 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 0ed510d5d9948ebd1a1ff899ad4a89e07ff5710e Author: Geoff Levand Date: Wed Feb 21 16:49:27 2007 -0800 [PS3] zImage cleanups. commit 2eeae9fd4301c752b132f6850f58d3ddb269f0bb Author: Geoff Levand Date: Wed Feb 21 07:40:26 2007 -0800 [PS3] Update defconfig. commit c119587e7d5a852a480512c5efcc2673dae49816 Author: Geoff Levand Date: Wed Feb 21 07:21:21 2007 -0800 [PS3] Correct accidental reversals by commit d06b73895a71d17a08f5760640bf84a70e28f commit c7b879682c1ac16c8c6af5c18bafeb478d7dfc75 Merge: 6266067... c8f71b0... Author: Geoff Levand Date: Wed Feb 21 06:31:13 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 626606742a83670e394fec6e165843b57a2497e2 Author: Geoff Levand Date: Tue Feb 20 16:59:04 2007 -0800 [PS3] More bootwrapper work. commit 9703708b09a9c3f6c82c04171284e1fb82560624 Merge: 3811f6d... e696268... Author: Geoff Levand Date: Tue Feb 20 13:39:20 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 3811f6da3092d2c700d9732d358bc4b9b9a43618 Author: Geoff Levand Date: Tue Feb 20 13:38:11 2007 -0800 [PS3] Start storage bus, ps3fbd fix. commit 2b669b067393d071f46ad17c4f1c0353fa6c826d Merge: 03aa884... 1ca9492... Author: Geoff Levand Date: Tue Feb 20 09:18:56 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 03aa884deb7af667d24e24c90b13b84b1bbbdadf Merge: 726389a... c9ce228... Author: Geoff Levand Date: Mon Feb 19 16:30:27 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 726389a7ecd5993b02e59d5b11a0bdfa1eac8fd2 Author: Geoff Levand Date: Mon Feb 19 16:25:16 2007 -0800 [PS3] Merge in new bootwrapper patches. commit 5e08ca0adb73da4a929cdb7bc6a12a20f6004e21 Author: Geoff Levand Date: Mon Feb 19 11:14:19 2007 -0800 [PS3] Update defconfigs. commit 4f8ac9854a9aba26947047c1af44d745d23518ca Author: Geoff Levand Date: Mon Feb 19 10:26:58 2007 -0800 [PS3] Storage driver work. commit 1edee1e641a4d949f6011bfe6372c6c3e977915c Merge: 2a7b2dd... 8a03d9a... Author: Geoff Levand Date: Fri Feb 16 10:30:27 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 2a7b2dd0473a56e88c30c11f180c8b9a9762773e Author: Geoff Levand Date: Fri Feb 16 10:28:10 2007 -0800 [PS3] Add DABR support. commit 8530bd9eda590a8f42cc845c0596c08d30520414 Merge: 6030cd0... 724339d... Author: Geoff Levand Date: Thu Feb 15 09:24:50 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 6030cd0105e5a2f949ff41ce11801b0d1f9dce5b Author: Geoff Levand Date: Thu Feb 15 09:13:45 2007 -0800 [PS3] Bootwrapper and framebuffer updates. commit 2b3d94e0248451c808dd7b187fee9ca5ff5ce893 Merge: cc02843... 86a71db... Author: Geoff Levand Date: Wed Feb 14 12:07:51 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit cc02843052654f9e65a746b856db599398541ff6 Merge: c5fd63d... ec2f9d1... Author: Geoff Levand Date: Mon Feb 12 17:41:23 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit c5fd63d013e0fa699dbf2cc2a533f353c52c0285 Author: Geoff Levand Date: Mon Feb 12 17:00:45 2007 -0800 [PS3] Bootwrapper re-work and logo fixups. commit c810f94ebf018b8eec845d6e329d9e2cd6ad2741 Author: Geoff Levand Date: Sat Feb 10 11:10:54 2007 -0800 [PS3] Re-work of ps3-bootwrapper.diff. commit 677935656560d919c9b35d4939f0d04a9f881a37 Author: Geoff Levand Date: Fri Feb 9 14:05:46 2007 -0800 [PS3] Add Add vdso-bug-fix.diff. commit f071f0230b4e5c062ed3899adacf2f00fc1a67e2 Author: Geoff Levand Date: Fri Feb 9 12:20:20 2007 -0800 [PS3] Add spufs-clear-class-0-interrupt.diff, spufs-wrap-master-run-bit.diff. commit 5267f86f91aacc6272bc3cb2edfdb979edbe3ac7 Merge: e318ec2... 6410610... Author: Geoff Levand Date: Fri Feb 9 10:35:21 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit e318ec27a7080b77f459267a33facdb2a0ae6793 Merge: d96197d... 5986a2e... Author: Geoff Levand Date: Thu Feb 8 13:22:43 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit d96197dd114e9b6d4ccc668a18108a4a746d7138 Author: Geoff Levand Date: Wed Feb 7 12:50:08 2007 -0800 Revert merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit a239914ca14f112c1719b95f934970fece47a1f1 Merge: 6f32573... dda2ac1... Author: Geoff Levand Date: Tue Feb 6 15:25:20 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 Conflicts: drivers/usb/input/hid-core.c include/linux/hid.h commit 6f3257305e4783a7808bde0abf66207abe0ac9f4 Author: Geoff Levand Date: Tue Feb 6 15:11:53 2007 -0800 [PS3] Prepare sys-manager patches for submission. commit e5d35f2329377c85713ad5f6277a04b85fa0de87 Author: Geoff Levand Date: Mon Feb 5 20:05:32 2007 -0800 [PS3] Sys-manager cleanups. commit 54ce7999c20aa584a5f5cb0fcca6ffb91daee90e Merge: c409171... 62d0cfc... Author: Geoff Levand Date: Mon Feb 5 11:01:04 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit c409171b40ac28cf8a1ff8e239da98cf43ce101f Author: Geoff Levand Date: Fri Feb 2 21:48:00 2007 -0800 [PS3] SPE logo, sys-manager cleanups. commit 3062566e9cf3663966145e2c90d69e0e2d7c89bd Merge: 658d581... f56df2f... Author: Geoff Levand Date: Wed Jan 31 10:13:14 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 658d58198fff520b6194a13c7b4aac2d0112cf7b Author: Geoff Levand Date: Tue Jan 30 14:44:45 2007 -0800 [PS3] Finish system bus move. commit 433fc01d9391acb5bfce1b873512b2fc6b40d8ac Author: Geoff Levand Date: Tue Jan 30 11:27:16 2007 -0800 [PS3] More av and fb prepearations for 2nd round 2.6.21 submission. commit 4183fb7b4d26a03cb33f5fd612236b76a1633e08 Merge: 7cf624f... 8c8c4ba... Author: Geoff Levand Date: Tue Jan 30 11:20:51 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 7cf624fa89027019ab1c02cf1ba581666361f04c Author: Geoff Levand Date: Mon Jan 29 19:45:41 2007 -0800 [PS3] Fixed ps3_map_sg, enabled CONFIG_USB_STORAGE. commit ea215db2828238d71527ad355e30249d1fafc7fe Author: Geoff Levand Date: Mon Jan 29 17:43:24 2007 -0800 [PS3] Move system-bus to platform directory. commit 931c24f504d441267a1583c5d57396d5502c75dd Author: Geoff Levand Date: Mon Jan 29 13:49:19 2007 -0800 [PS3] Prepare av and fb patches for 2nd round 2.6.21 submission. commit 24570acc069527b5196cff1b1ff899057f2e80aa Merge: 73e9fe9... 5263bf6... Author: Geoff Levand Date: Mon Jan 29 10:30:42 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 73e9fe9d5a712b3e2b6716f4d4ef826cc2b6d633 Author: Geoff Levand Date: Fri Jan 26 19:48:03 2007 -0800 [PS3] Prepared patches for 2.6.21 submission. commit 5e85e745e620bbf49cc190ff1d7eb4b3471225fe Merge: a1aa2ba... 7d620a4... Author: Geoff Levand Date: Fri Jan 26 19:41:06 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit a1aa2ba1381e088edebef974b38ba76c6d78135d Author: Geoff Levand Date: Thu Jan 25 14:13:52 2007 -0800 [PS3] Prepare av and fb patches for 2.6.21 submission. commit d336162bb7134de8267fafdfffdd5a964fc4aa0c Merge: e53ab43... 99abfea... Author: Geoff Levand Date: Thu Jan 25 10:53:04 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit e53ab435f120729a7c27e1dfedc59b20d9618c0b Author: Geoff Levand Date: Wed Jan 24 19:04:46 2007 -0800 [PS3] Prepare patches for 2.6.21 submission. commit 8af3f93efbe5c811b9ccb35a2e3726b7a159164a Author: Geoff Levand Date: Tue Jan 23 15:32:56 2007 -0800 [PS3] Export ps3_get_firmware_version(). commit a75940981b791995d2b373741038bdbf2a4c381e Merge: 9962d40... 99ddcc7... Author: Geoff Levand Date: Tue Jan 23 15:22:53 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 9962d4037f17e89f79f45ee32f82f89cb674609f Merge: 4e70aee... 9554317... Author: Geoff Levand Date: Tue Jan 23 13:01:01 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 commit 4e70aeef91c1f4001b5587657366ab0149e09c98 Author: Geoff Levand Date: Tue Jan 23 12:25:21 2007 -0800 [PS3] Cleanup fb patches. commit ddb1dee94f8eae280d174b0109312a7bb06c1030 Merge: cf834ce... 2596627... Author: Geoff Levand Date: Mon Jan 22 18:35:20 2007 -0800 Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 Conflicts: arch/powerpc/configs/ps3_defconfig commit cf834cefdd7451d69362b5ffa7dc69106337ceb5 Author: Geoff Levand Date: Mon Jan 22 18:15:43 2007 -0800 [PS3] Import ps3-linux-patches-efc1ddc7203ae9a2418e03a9dbbafb260f4fe2a3. Index: linux-2.6/Documentation/block/barrier.txt =================================================================== --- linux-2.6.orig/Documentation/block/barrier.txt +++ linux-2.6/Documentation/block/barrier.txt @@ -82,23 +82,12 @@ including draining and flushing. typedef void (prepare_flush_fn)(request_queue_t *q, struct request *rq); int blk_queue_ordered(request_queue_t *q, unsigned ordered, - prepare_flush_fn *prepare_flush_fn, - unsigned gfp_mask); - -int blk_queue_ordered_locked(request_queue_t *q, unsigned ordered, - prepare_flush_fn *prepare_flush_fn, - unsigned gfp_mask); - -The only difference between the two functions is whether or not the -caller is holding q->queue_lock on entry. The latter expects the -caller is holding the lock. + prepare_flush_fn *prepare_flush_fn); @q : the queue in question @ordered : the ordered mode the driver/device supports @prepare_flush_fn : this function should prepare @rq such that it flushes cache to physical medium when executed -@gfp_mask : gfp_mask used when allocating data structures - for ordered processing For example, SCSI disk driver's prepare_flush_fn looks like the following. @@ -106,9 +95,10 @@ following. static void sd_prepare_flush(request_queue_t *q, struct request *rq) { memset(rq->cmd, 0, sizeof(rq->cmd)); - rq->flags |= REQ_BLOCK_PC; + rq->cmd_type = REQ_TYPE_BLOCK_PC; rq->timeout = SD_TIMEOUT; rq->cmd[0] = SYNCHRONIZE_CACHE; + rq->cmd_len = 10; } The following seven ordered modes are supported. The following table Index: linux-2.6/MAINTAINERS =================================================================== --- linux-2.6.orig/MAINTAINERS +++ linux-2.6/MAINTAINERS @@ -2930,6 +2930,13 @@ M: mikpe@it.uu.se L: linux-ide@vger.kernel.org S: Maintained +PS3 NETWORK SUPPORT +P: Masakazu Mokuno +M: mokuno@sm.sony.co.jp +L: netdev@vger.kernel.org +L: cbe-oss-dev@ozlabs.org +S: Supported + PS3 PLATFORM SUPPORT P: Geoff Levand M: geoffrey.levand@am.sony.com Index: linux-2.6/arch/powerpc/boot/Makefile =================================================================== --- linux-2.6.orig/arch/powerpc/boot/Makefile +++ linux-2.6/arch/powerpc/boot/Makefile @@ -17,6 +17,7 @@ # CROSS32_COMPILE is setup as a prefix just like CROSS_COMPILE # in the toplevel makefile. + all: $(obj)/zImage BOOTCFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ @@ -46,7 +47,8 @@ src-wlib := string.S crt0.S stdio.c main gunzip_util.c elf_util.c $(zlib) devtree.c oflib.c ofconsole.c \ 44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c cuboot.c src-plat := of.c cuboot-83xx.c cuboot-85xx.c holly.c \ - cuboot-ebony.c treeboot-ebony.c prpmc2800.c + cuboot-ebony.c treeboot-ebony.c prpmc2800.c \ + ps3-head.S ps3-hvcall.S ps3.c src-boot := $(src-wlib) $(src-plat) empty.c src-boot := $(addprefix $(obj)/, $(src-boot)) @@ -75,11 +77,11 @@ $(addprefix $(obj)/,$(zliblinuxheader)): $(obj)/empty.c: @touch $@ -$(obj)/zImage.lds $(obj)/zImage.coff.lds: $(obj)/%: $(srctree)/$(src)/%.S +$(obj)/zImage.lds $(obj)/zImage.coff.lds $(obj)/zImage.ps3.lds: $(obj)/%: $(srctree)/$(src)/%.S @cp $< $@ clean-files := $(zlib) $(zlibheader) $(zliblinuxheader) \ - empty.c zImage.coff.lds zImage.lds + empty.c zImage zImage.coff.lds zImage.ps3.lds zImage.lds quiet_cmd_bootcc = BOOTCC $@ cmd_bootcc = $(CROSS32CC) -Wp,-MD,$(depfile) $(BOOTCFLAGS) -c -o $@ $< @@ -102,7 +104,7 @@ hostprogs-y := addnote addRamDisk hack-c targets += $(patsubst $(obj)/%,%,$(obj-boot) wrapper.a) extra-y := $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ - $(obj)/zImage.lds $(obj)/zImage.coff.lds + $(obj)/zImage.lds $(obj)/zImage.coff.lds $(obj)/zImage.ps3.lds wrapper :=$(srctree)/$(src)/wrapper wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree) \ @@ -119,10 +121,15 @@ CROSSWRAP := -C "$(CROSS_COMPILE)" endif endif +ifneq ($(KBUILD_VERBOSE),0) +verbose_shell := -x +endif + # args (to if_changed): 1 = (this rule), 2 = platform, 3 = dts 4=dtb 5=initrd quiet_cmd_wrap = WRAP $@ - cmd_wrap =$(CONFIG_SHELL) $(wrapper) -c -o $@ -p $2 $(CROSSWRAP) \ - $(if $3, -s $3)$(if $4, -d $4)$(if $5, -i $5) vmlinux + cmd_wrap =$(CONFIG_SHELL) $(verbose_shell) $(wrapper) -c -o $@ -p $2 \ + $(CROSSWRAP) $(if $3, -s $3)$(if $4, -d $4)$(if $5, -i $5) \ + vmlinux image-$(CONFIG_PPC_PSERIES) += zImage.pseries image-$(CONFIG_PPC_MAPLE) += zImage.pseries @@ -179,11 +186,12 @@ $(obj)/zImage.%: vmlinux $(wrapperbits) $(obj)/zImage.iseries: vmlinux $(STRIP) -s -R .comment $< -o $@ -$(obj)/zImage.ps3: vmlinux - $(STRIP) -s -R .comment $< -o $@ +$(obj)/zImage.ps3: vmlinux $(wrapper) $(wrapperbits) $(srctree)/$(src)/dts/ps3.dts + $(STRIP) -s -R .comment $< -o vmlinux.strip + $(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,) -$(obj)/zImage.initrd.ps3: vmlinux - @echo " WARNING zImage.initrd.ps3 not supported (yet)" +$(obj)/zImage.initrd.ps3: vmlinux $(wrapper) $(wrapperbits) $(srctree)/$(src)/dts/ps3.dts $(obj)/ramdisk.image.gz + $(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,$(obj)/ramdisk.image.gz) $(obj)/uImage: vmlinux $(wrapperbits) $(call if_changed,wrap,uboot) @@ -206,7 +214,8 @@ install: $(CONFIGURE) $(addprefix $(obj) sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)" $< # anything not in $(targets) -clean-files += $(image-) $(initrd-) zImage zImage.initrd cuImage.* treeImage.* +clean-files += $(image-) $(initrd-) zImage zImage.initrd cuImage.* treeImage.* \ + otheros.bld # clean up files cached by wrapper clean-kernel := vmlinux.strip vmlinux.bin Index: linux-2.6/arch/powerpc/boot/ps3-head.S =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/boot/ps3-head.S @@ -0,0 +1,80 @@ +/* + * PS3 bootwrapper entry. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * 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; version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ppc_asm.h" + + .text + +/* + * __system_reset_overlay - The PS3 first stage entry. + * + * The bootwraper build script copies the 0x100 bytes at symbol + * __system_reset_overlay to offset 0x100 of the rom image. + * + * The PS3 has a single processor with two threads. + */ + + .globl __system_reset_overlay +__system_reset_overlay: + + /* Switch to 32-bit mode. */ + + mfmsr r9 + clrldi r9,r9,1 + mtmsrd r9 + nop + + /* Get thread number in r3 and branch. */ + + mfspr r3, 0x88 + cntlzw. r3, r3 + li r4, 0 + li r5, 0 + beq 1f + + /* Secondary goes to __secondary_hold in kernel. */ + + li r4, 0x60 + mtctr r4 + bctr + + /* Primary delays then goes to _zimage_start in wrapper. */ +1: + or 31, 31, 31 /* db16cyc */ + or 31, 31, 31 /* db16cyc */ + + lis r4, _zimage_start@ha + addi r4, r4, _zimage_start@l + mtctr r4 + bctr + +/* + * __system_reset_kernel - Place holder for the kernel reset vector. + * + * The bootwrapper build script copies 0x100 bytes from offset 0x100 + * of the rom image to the symbol __system_reset_kernel. At runtime + * the bootwrapper program copies the 0x100 bytes at __system_reset_kernel + * to ram address 0x100. This symbol must occupy 0x100 bytes. + */ + + .globl __system_reset_kernel +__system_reset_kernel: + + . = __system_reset_kernel + 0x100 Index: linux-2.6/arch/powerpc/boot/ps3-hvcall.S =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/boot/ps3-hvcall.S @@ -0,0 +1,184 @@ +/* + * PS3 bootwrapper hvcalls. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * 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; version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ppc_asm.h" + +/* + * The PS3 hypervisor uses a 64 bit "C" language calling convention. + * The routines here marshal arguments between the 32 bit wrapper + * program and the 64 bit hvcalls. + * + * wrapper lv1 + * 32-bit (h,l) 64-bit + * + * 1: r3,r4 <-> r3 + * 2: r5,r6 <-> r4 + * 3: r7,r8 <-> r5 + * 4: r9,r10 <-> r6 + * 5: 8(r1),12(r1) <-> r7 + * 6: 16(r1),20(r1) <-> r8 + * 7: 24(r1),28(r1) <-> r9 + * 8: 32(r1),36(r1) <-> r10 + * + */ + +.macro GLOBAL name + .section ".text" + .balign 4 + .globl \name +\name: +.endm + +.macro NO_SUPPORT name + GLOBAL \name + b ps3_no_support +.endm + +.macro HVCALL num + li r11, \num + .long 0x44000022 + extsw r3, r3 +.endm + +.macro SAVE_LR offset=4 + mflr r0 + stw r0, \offset(r1) +.endm + +.macro LOAD_LR offset=4 + lwz r0, \offset(r1) + mtlr r0 +.endm + +.macro LOAD_64_REG target,high,low + sldi r11, \high, 32 + or \target, r11, \low +.endm + +.macro LOAD_64_STACK target,offset + ld \target, \offset(r1) +.endm + +.macro LOAD_R3 + LOAD_64_REG r3,r3,r4 +.endm + +.macro LOAD_R4 + LOAD_64_REG r4,r5,r6 +.endm + +.macro LOAD_R5 + LOAD_64_REG r5,r7,r8 +.endm + +.macro LOAD_R6 + LOAD_64_REG r6,r9,r10 +.endm + +.macro LOAD_R7 + LOAD_64_STACK r7,8 +.endm + +.macro LOAD_R8 + LOAD_64_STACK r8,16 +.endm + +.macro LOAD_R9 + LOAD_64_STACK r9,24 +.endm + +.macro LOAD_R10 + LOAD_64_STACK r10,32 +.endm + +.macro LOAD_REGS_0 + stwu 1,-16(1) + stw 3, 8(1) +.endm + +.macro LOAD_REGS_5 + LOAD_R3 + LOAD_R4 + LOAD_R5 + LOAD_R6 + LOAD_R7 +.endm + +.macro LOAD_REGS_6 + LOAD_REGS_5 + LOAD_R8 +.endm + +.macro LOAD_REGS_8 + LOAD_REGS_6 + LOAD_R9 + LOAD_R10 +.endm + +.macro STORE_REGS_0_1 + lwz r11, 8(r1) + std r4, 0(r11) + mr r4, r3 + li r3, 0 + addi r1,r1,16 +.endm + +.macro STORE_REGS_5_2 + lwz r11, 16(r1) + std r4, 0(r11) + lwz r11, 24(r1) + std r5, 0(r11) +.endm + +.macro STORE_REGS_6_1 + lwz r11, 24(r1) + std r4, 0(r11) +.endm + +GLOBAL lv1_get_logical_ppe_id + SAVE_LR + LOAD_REGS_0 + HVCALL 69 + STORE_REGS_0_1 + LOAD_LR + blr + +GLOBAL lv1_get_logical_partition_id + SAVE_LR + LOAD_REGS_0 + HVCALL 74 + STORE_REGS_0_1 + LOAD_LR + blr + +GLOBAL lv1_get_repository_node_value + SAVE_LR + LOAD_REGS_5 + HVCALL 91 + STORE_REGS_5_2 + LOAD_LR + blr + +GLOBAL lv1_panic + SAVE_LR + LOAD_REGS_8 + HVCALL 255 + LOAD_LR + blr Index: linux-2.6/arch/powerpc/boot/ps3.c =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/boot/ps3.c @@ -0,0 +1,161 @@ +/* + * PS3 bootwrapper support. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * 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; version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include "types.h" +#include "elf.h" +#include "string.h" +#include "stdio.h" +#include "page.h" +#include "ops.h" + +extern s64 lv1_panic(u64 in_1); +extern s64 lv1_get_logical_partition_id(u64 *out_1); +extern s64 lv1_get_logical_ppe_id(u64 *out_1); +extern s64 lv1_get_repository_node_value(u64 in_1, u64 in_2, u64 in_3, + u64 in_4, u64 in_5, u64 *out_1, u64 *out_2); + +#ifdef DEBUG +#define DBG(fmt...) printf(fmt) +#else +static inline int __attribute__ ((format (printf, 1, 2))) DBG( + const char *fmt, ...) {return 0;} +#endif + +BSS_STACK(4096); + +/* A buffer that may be edited by tools operating on a zImage binary so as to + * edit the command line passed to vmlinux (by setting /chosen/bootargs). + * The buffer is put in it's own section so that tools may locate it easier. + */ +static char cmdline[COMMAND_LINE_SIZE] + __attribute__((__section__("__builtin_cmdline"))); + +static void prep_cmdline(void *chosen) +{ + if (cmdline[0] == '\0') + getprop(chosen, "bootargs", cmdline, COMMAND_LINE_SIZE-1); + else + setprop_str(chosen, "bootargs", cmdline); + + printf("cmdline: '%s'\n", cmdline); +} + +static void ps3_console_write(const char *buf, int len) +{ +} + +static void ps3_exit(void) +{ + printf("ps3_exit\n"); + + /* lv1_panic will shutdown the lpar. */ + + lv1_panic(0); /* zero = do not reboot */ + while (1); +} + +static int ps3_repository_read_rm_size(u64 *rm_size) +{ + s64 result; + u64 lpar_id; + u64 ppe_id; + u64 v2; + + result = lv1_get_logical_partition_id(&lpar_id); + + if (result) + return -1; + + result = lv1_get_logical_ppe_id(&ppe_id); + + if (result) + return -1; + + /* + * n1: 0000000062690000 : ....bi.. + * n2: 7075000000000000 : pu...... + * n3: 0000000000000001 : ........ + * n4: 726d5f73697a6500 : rm_size. + */ + + result = lv1_get_repository_node_value(lpar_id, 0x0000000062690000ULL, + 0x7075000000000000ULL, ppe_id, 0x726d5f73697a6500ULL, rm_size, + &v2); + + printf("%s:%d: ppe_id %lu \n", __func__, __LINE__, + (unsigned long)ppe_id); + printf("%s:%d: lpar_id %lu \n", __func__, __LINE__, + (unsigned long)lpar_id); + printf("%s:%d: rm_size %llxh \n", __func__, __LINE__, *rm_size); + + return result ? -1 : 0; +} + +void ps3_copy_vectors(void) +{ + extern char __system_reset_kernel[]; + + memcpy((void *)0x100, __system_reset_kernel, 0x100); + flush_cache((void *)0x100, 0x100); +} + +void platform_init(void) +{ + extern char _end[]; + extern char _dtb_start[]; + extern char _initrd_start[]; + extern char _initrd_end[]; + const u32 heapsize = 0x1000000 - (u32)_end; /* 16MiB */ + void *chosen; + unsigned long ft_addr; + u64 rm_size; + + console_ops.write = ps3_console_write; + platform_ops.exit = ps3_exit; + + printf("\n-- PS3 bootwrapper --\n"); + + simple_alloc_init(_end, heapsize, 32, 64); + ft_init(_dtb_start, 0, 4); + + chosen = finddevice("/chosen"); + + ps3_repository_read_rm_size(&rm_size); + dt_fixup_memory(0, rm_size); + + if (_initrd_end > _initrd_start) { + setprop_val(chosen, "linux,initrd-start", (u32)(_initrd_start)); + setprop_val(chosen, "linux,initrd-end", (u32)(_initrd_end)); + } + + prep_cmdline(chosen); + + ft_addr = dt_ops.finalize(); + + ps3_copy_vectors(); + + printf(" flat tree at 0x%lx\n\r", ft_addr); + + ((kernel_entry_t)0)(ft_addr, 0, NULL); + + ps3_exit(); +} Index: linux-2.6/arch/powerpc/boot/wrapper =================================================================== --- linux-2.6.orig/arch/powerpc/boot/wrapper +++ linux-2.6/arch/powerpc/boot/wrapper @@ -144,6 +144,15 @@ miboot|uboot) cuboot*) gzip= ;; +ps3) + platformo="$object/ps3-head.o $object/ps3-hvcall.o $object/ps3.o" + lds=$object/zImage.ps3.lds + gzip= + ext=bin + objflags="-O binary --set-section-flags=.bss=contents,alloc,load,data" + ksection=.kernel:vmlinux.bin + isection=.kernel:initrd + ;; esac vmz="$tmpdir/`basename \"$kernel\"`.$ext" @@ -207,6 +216,7 @@ fi if [ "$platform" != "miboot" ]; then ${CROSS}ld -m elf32ppc -T $lds -o "$ofile" \ + -Map "$ofile.map" \ $platformo $tmp $object/wrapper.a rm $tmp fi @@ -239,4 +249,50 @@ treeboot*) fi exit 0 ;; +ps3) + # The ps3's loader supports loading gzipped binary images from flash + # rom to addr zero. The loader enters the image at addr 0x100. A + # bootwrapper overlay is used to arrange for the kernel to be loaded + # to addr zero and to have a suitable bootwrapper entry at 0x100. + # To construct the rom image, 0x100 bytes from offset 0x100 in the + # kernel is copied to the bootwrapper symbol __system_reset_kernel. + # The 0x100 bytes at the bootwrapper symbol __system_reset_overlay is + # then copied to offset 0x100. At runtime the bootwrapper program + # copies the 0x100 bytes at __system_reset_kernel to addr 0x100. + + system_reset_overlay=0x`${CROSS}nm "$ofile" \ + | grep ' __system_reset_overlay$' \ + | cut -d' ' -f1` + system_reset_overlay=`printf "%d" $system_reset_overlay` + system_reset_kernel=0x`${CROSS}nm "$ofile" \ + | grep ' __system_reset_kernel$' \ + | cut -d' ' -f1` + system_reset_kernel=`printf "%d" $system_reset_kernel` + overlay_dest="256" + overlay_size="256" + + rm -f "$object/otheros.bld" + + ${CROSS}objcopy -O binary "$ofile" "$ofile.bin" + + msg=$(dd if="$ofile.bin" of="$ofile.bin" conv=notrunc \ + skip=$overlay_dest seek=$system_reset_kernel \ + count=$overlay_size bs=1 2>&1) + + if [ $? -ne "0" ]; then + echo $msg + exit 1 + fi + + msg=$(dd if="$ofile.bin" of="$ofile.bin" conv=notrunc \ + skip=$system_reset_overlay seek=$overlay_dest \ + count=$overlay_size bs=1 2>&1) + + if [ $? -ne "0" ]; then + echo $msg + exit 2 + fi + + gzip --force -9 --stdout "$ofile.bin" > "$object/otheros.bld" + ;; esac Index: linux-2.6/arch/powerpc/boot/zImage.ps3.lds.S =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/boot/zImage.ps3.lds.S @@ -0,0 +1,50 @@ +OUTPUT_ARCH(powerpc:common) +ENTRY(_zimage_start) +EXTERN(_zimage_start) +SECTIONS +{ + _vmlinux_start = .; + .kernel:vmlinux.bin : { *(.kernel:vmlinux.bin) } + _vmlinux_end = .; + + . = ALIGN(4096); + _dtb_start = .; + .kernel:dtb : { *(.kernel:dtb) } + _dtb_end = .; + + . = ALIGN(4096); + _initrd_start = .; + .kernel:initrd : { *(.kernel:initrd) } + _initrd_end = .; + + _start = .; + .text : + { + *(.text) + *(.fixup) + } + _etext = .; + . = ALIGN(4096); + .data : + { + *(.rodata*) + *(.data*) + *(.sdata*) + __got2_start = .; + *(.got2) + __got2_end = .; + } + + . = ALIGN(4096); + _edata = .; + + . = ALIGN(4096); + __bss_start = .; + .bss : + { + *(.sbss) + *(.bss) + } + . = ALIGN(4096); + _end = . ; +} Index: linux-2.6/arch/powerpc/configs/ps3_defconfig =================================================================== --- linux-2.6.orig/arch/powerpc/configs/ps3_defconfig +++ linux-2.6/arch/powerpc/configs/ps3_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.22-rc4 -# Thu Jun 14 19:52:01 2007 +# Linux kernel version: 2.6.22-rc7 +# Tue Jul 3 13:17:45 2007 # CONFIG_PPC64=y CONFIG_64BIT=y @@ -485,6 +485,7 @@ CONFIG_NETDEVICES=y CONFIG_MII=m CONFIG_NETDEV_1000=y CONFIG_GELIC_NET=y +CONFIG_GELIC_WIRELESS=y # CONFIG_NETDEV_10000 is not set # @@ -709,6 +710,8 @@ CONFIG_SOUND=y # Advanced Linux Sound Architecture # CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y # CONFIG_SND_SEQUENCER is not set # CONFIG_SND_MIXER_OSS is not set # CONFIG_SND_PCM_OSS is not set @@ -735,6 +738,12 @@ CONFIG_SND_VERBOSE_PROCFS=y # # +# ALSA PowerPC devices +# +CONFIG_SND_PS3=y +CONFIG_SND_PS3_DEFAULT_START_DELAY=2000 + +# # USB devices # # CONFIG_SND_USB_AUDIO is not set Index: linux-2.6/arch/powerpc/kernel/head_64.S =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/head_64.S +++ linux-2.6/arch/powerpc/kernel/head_64.S @@ -1695,9 +1695,11 @@ _GLOBAL(__start_initialization_multiplat 2: /* Switch off MMU if not already */ - LOAD_REG_IMMEDIATE(r4, .__after_prom_start - KERNELBASE) + LOAD_REG_IMMEDIATE(r4, __mmu_off_return - KERNELBASE) add r4,r4,r30 bl .__mmu_off +__mmu_off_return: + b .__after_prom_start _STATIC(__boot_from_prom) Index: linux-2.6/arch/powerpc/kernel/prom.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/prom.c +++ linux-2.6/arch/powerpc/kernel/prom.c @@ -939,6 +939,12 @@ static int __init early_init_dt_scan_mem size = 0x80000000ul - base; } #endif +#ifdef CONFIG_PPC_PS3 + /* temporary hack for the legacy bootloader */ + if (of_flat_dt_is_compatible(of_get_flat_dt_root(), "PS3PF")) { + size = 0x8000000; + } +#endif lmb_add(base, size); } return 0; Index: linux-2.6/arch/powerpc/kernel/prom_init.c =================================================================== --- linux-2.6.orig/arch/powerpc/kernel/prom_init.c +++ linux-2.6/arch/powerpc/kernel/prom_init.c @@ -44,10 +44,7 @@ #include #include -#ifdef CONFIG_LOGO_LINUX_CLUT224 #include -extern const struct linux_logo logo_linux_clut224; -#endif /* * Properties whose value is longer than this get excluded from our Index: linux-2.6/arch/powerpc/platforms/cell/spu_base.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spu_base.c +++ linux-2.6/arch/powerpc/platforms/cell/spu_base.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -236,27 +237,34 @@ static irqreturn_t spu_irq_class_0(int irq, void *data) { struct spu *spu; + unsigned long stat, mask; spu = data; - spu->class_0_pending = 1; + + mask = spu_int_mask_get(spu, 0); + stat = spu_int_stat_get(spu, 0); + stat &= mask; + + spin_lock(&spu->register_lock); + spu->class_0_pending |= stat; + spin_unlock(&spu->register_lock); + spu->stop_callback(spu); + spu_int_stat_clear(spu, 0, stat); + return IRQ_HANDLED; } int spu_irq_class_0_bottom(struct spu *spu) { - unsigned long stat, mask; unsigned long flags; - - spu->class_0_pending = 0; + unsigned long stat; spin_lock_irqsave(&spu->register_lock, flags); - mask = spu_int_mask_get(spu, 0); - stat = spu_int_stat_get(spu, 0); - - stat &= mask; + stat = spu->class_0_pending; + spu->class_0_pending = 0; if (stat & 1) /* invalid DMA alignment */ __spu_trap_dma_align(spu); @@ -781,12 +789,15 @@ static int __init init_spu_base(void) ret = spu_enumerate_spus(create_spu); - if (ret) { + if (ret < 0) { printk(KERN_WARNING "%s: Error initializing spus\n", __FUNCTION__); goto out_unregister_sysdev_class; } + if (ret > 0) + fb_append_extra_logo(&logo_spe_clut224, ret); + mutex_lock(&spu_full_list_mutex); xmon_register_spus(&spu_full_list); #ifdef CONFIG_KEXEC Index: linux-2.6/arch/powerpc/platforms/cell/spu_manage.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spu_manage.c +++ linux-2.6/arch/powerpc/platforms/cell/spu_manage.c @@ -35,6 +35,7 @@ #include #include +#include "spufs/spufs.h" #include "interrupt.h" struct device_node *spu_devnode(struct spu *spu) @@ -279,6 +280,7 @@ static int __init of_enumerate_spus(int { int ret; struct device_node *node; + unsigned int n = 0; ret = -ENODEV; for (node = of_find_node_by_type(NULL, "spe"); @@ -289,8 +291,9 @@ static int __init of_enumerate_spus(int __FUNCTION__, node->name); break; } + n++; } - return ret; + return ret ? ret : n; } static int __init of_create_spu(struct spu *spu, void *data) @@ -359,8 +362,22 @@ static int of_destroy_spu(struct spu *sp return 0; } +static int enable_spu_by_master_run(struct spu_context *ctx) +{ + ctx->ops->master_start(ctx); + return 0; +} + +static int disable_spu_by_master_run(struct spu_context *ctx) +{ + ctx->ops->master_stop(ctx); + return 0; +} + const struct spu_management_ops spu_management_of_ops = { .enumerate_spus = of_enumerate_spus, .create_spu = of_create_spu, .destroy_spu = of_destroy_spu, + .enable_spu = enable_spu_by_master_run, + .disable_spu = disable_spu_by_master_run, }; Index: linux-2.6/arch/powerpc/platforms/cell/spufs/backing_ops.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/backing_ops.c +++ linux-2.6/arch/powerpc/platforms/cell/spufs/backing_ops.c @@ -284,6 +284,11 @@ static void spu_backing_runcntl_write(st spin_unlock(&ctx->csa.register_lock); } +static void spu_backing_runcntl_stop(struct spu_context *ctx) +{ + spu_backing_runcntl_write(ctx, SPU_RUNCNTL_STOP); +} + static void spu_backing_master_start(struct spu_context *ctx) { struct spu_state *csa = &ctx->csa; @@ -380,6 +385,7 @@ struct spu_context_ops spu_backing_ops = .get_ls = spu_backing_get_ls, .runcntl_read = spu_backing_runcntl_read, .runcntl_write = spu_backing_runcntl_write, + .runcntl_stop = spu_backing_runcntl_stop, .master_start = spu_backing_master_start, .master_stop = spu_backing_master_stop, .set_mfc_query = spu_backing_set_mfc_query, Index: linux-2.6/arch/powerpc/platforms/cell/spufs/hw_ops.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/hw_ops.c +++ linux-2.6/arch/powerpc/platforms/cell/spufs/hw_ops.c @@ -220,6 +220,15 @@ static void spu_hw_runcntl_write(struct spin_unlock_irq(&ctx->spu->register_lock); } +static void spu_hw_runcntl_stop(struct spu_context *ctx) +{ + spin_lock_irq(&ctx->spu->register_lock); + out_be32(&ctx->spu->problem->spu_runcntl_RW, SPU_RUNCNTL_STOP); + while(in_be32(&ctx->spu->problem->spu_status_R) & SPU_STATUS_RUNNING) + cpu_relax(); + spin_unlock_irq(&ctx->spu->register_lock); +} + static void spu_hw_master_start(struct spu_context *ctx) { struct spu *spu = ctx->spu; @@ -321,6 +330,7 @@ struct spu_context_ops spu_hw_ops = { .get_ls = spu_hw_get_ls, .runcntl_read = spu_hw_runcntl_read, .runcntl_write = spu_hw_runcntl_write, + .runcntl_stop = spu_hw_runcntl_stop, .master_start = spu_hw_master_start, .master_stop = spu_hw_master_stop, .set_mfc_query = spu_hw_set_mfc_query, Index: linux-2.6/arch/powerpc/platforms/cell/spufs/run.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/run.c +++ linux-2.6/arch/powerpc/platforms/cell/spufs/run.c @@ -310,7 +310,7 @@ long spufs_run_spu(struct file *file, st if (mutex_lock_interruptible(&ctx->run_mutex)) return -ERESTARTSYS; - ctx->ops->master_start(ctx); + spu_enable_spu(ctx); ctx->event_return = 0; spu_acquire(ctx); @@ -384,7 +384,7 @@ long spufs_run_spu(struct file *file, st (ctx->state == SPU_STATE_RUNNABLE)) ctx->stats.libassist++; - ctx->ops->master_stop(ctx); + spu_disable_spu(ctx); ret = spu_run_fini(ctx, npc, &status); spu_yield(ctx); Index: linux-2.6/arch/powerpc/platforms/cell/spufs/spufs.h =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/cell/spufs/spufs.h +++ linux-2.6/arch/powerpc/platforms/cell/spufs/spufs.h @@ -184,6 +184,7 @@ struct spu_context_ops { char*(*get_ls) (struct spu_context * ctx); u32 (*runcntl_read) (struct spu_context * ctx); void (*runcntl_write) (struct spu_context * ctx, u32 data); + void (*runcntl_stop) (struct spu_context * ctx); void (*master_start) (struct spu_context * ctx); void (*master_stop) (struct spu_context * ctx); int (*set_mfc_query)(struct spu_context * ctx, u32 mask, u32 mode); Index: linux-2.6/arch/powerpc/platforms/ps3/Kconfig =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/ps3/Kconfig +++ linux-2.6/arch/powerpc/platforms/ps3/Kconfig @@ -102,4 +102,40 @@ config PS3_STORAGE depends on PPC_PS3 tristate +config PS3_DISK + tristate "PS3 Disk Storage Driver" + depends on PPC_PS3 && BLOCK + select PS3_STORAGE + help + Include support for the PS3 Disk Storage. + + This support is required to access the PS3 hard disk. + In general, all users will say Y or M. + +config PS3_ROM + tristate "PS3 BD/DVD/CD-ROM Storage Driver" + depends on PPC_PS3 && SCSI + select PS3_STORAGE + help + Include support for the PS3 ROM Storage. + + This support is required to access the PS3 BD/DVD/CD-ROM drive. + In general, all users will say Y or M. + Also make sure to say Y or M to "SCSI CDROM support" later. + +config PS3_FLASH + tristate "PS3 FLASH ROM Storage Driver" + depends on PPC_PS3 + select PS3_STORAGE + help + Include support for the PS3 FLASH ROM Storage. + + This support is required to access the PS3 FLASH ROM, which + contains the boot loader and some boot options. + In general, all users will say Y or M. + + As this driver needs a fixed buffer of 256 KiB of memory, it can + be disabled on the kernel command line using "ps3flash=off", to + not allocate this fixed buffer. + endmenu Index: linux-2.6/arch/powerpc/platforms/ps3/mm.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/ps3/mm.c +++ linux-2.6/arch/powerpc/platforms/ps3/mm.c @@ -1213,8 +1213,6 @@ void __init ps3_mm_init(void) BUG_ON(map.rm.base); BUG_ON(!map.rm.size); - lmb_add(map.rm.base, map.rm.size); - lmb_analyze(); /* arrange to do this in ps3_mm_add_memory */ ps3_mm_region_create(&map.r1, map.total - map.rm.size); Index: linux-2.6/arch/powerpc/platforms/ps3/setup.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/ps3/setup.c +++ linux-2.6/arch/powerpc/platforms/ps3/setup.c @@ -222,7 +222,9 @@ static int __init ps3_probe(void) DBG(" -> %s:%d\n", __func__, __LINE__); dt_root = of_get_flat_dt_root(); - if (!of_flat_dt_is_compatible(dt_root, "sony,ps3")) + if (!of_flat_dt_is_compatible(dt_root, "sony,ps3") + /* temporary hack for the legacy bootloader */ + && !of_flat_dt_is_compatible(dt_root, "PS3PF")) return 0; powerpc_firmware_features |= FW_FEATURE_PS3_POSSIBLE; Index: linux-2.6/arch/powerpc/platforms/ps3/spu.c =================================================================== --- linux-2.6.orig/arch/powerpc/platforms/ps3/spu.c +++ linux-2.6/arch/powerpc/platforms/ps3/spu.c @@ -28,6 +28,7 @@ #include #include +#include "../cell/spufs/spufs.h" #include "platform.h" /* spu_management_ops */ @@ -405,17 +406,32 @@ static int __init ps3_enumerate_spus(int } } - if (result) + if (result) { printk(KERN_WARNING "%s:%d: Error initializing spus\n", __func__, __LINE__); + return result; + } - return result; + return num_resource_id; +} + +static int ps3_enable_spu(struct spu_context *ctx) +{ + return -ENOSYS; +} + +static int ps3_disable_spu(struct spu_context *ctx) +{ + ctx->ops->runcntl_stop(ctx); + return -ENOSYS; } const struct spu_management_ops spu_management_ps3_ops = { .enumerate_spus = ps3_enumerate_spus, .create_spu = ps3_create_spu, .destroy_spu = ps3_destroy_spu, + .enable_spu = ps3_enable_spu, + .disable_spu = ps3_disable_spu, }; /* spu_priv1_ops */ Index: linux-2.6/arch/powerpc/platforms/ps3/system-bus-rework.txt =================================================================== --- /dev/null +++ linux-2.6/arch/powerpc/platforms/ps3/system-bus-rework.txt @@ -0,0 +1,82 @@ +* Status of the system-bus re-work + +o=working +x=not working +NA=no support planned + 1st shut 2nd insmod rmmod + boot down boot + +CONFIG_USB_EHCI_HCD o o o o o +CONFIG_USB_OHCI_HCD o o o o o +CONFIG_GELIC_NET o o o o o +CONFIG_FB_PS3 o o(2) o o x(3) +CONFIG_SND_PS3 o o o o o +CONFIG_PS3_PS3AV o o o o o +CONFIG_PS3_SYS_MANAGER o o o o NA(1) +CONFIG_PS3_VUART o o o o o +CONFIG_PS3_FLASH o o o o o +CONFIG_PS3_DISK o o o o o +CONFIG_PS3_ROM o o o o o +CONFIG_PS3_STORAGE o o o o o +CONFIG_SPU_FS o o o o o + +-- commands -- + +64 bit kexec o +32 bit kexec x(4) +reboot: o +halt o +shutdown o +poweroff o +power button o + +-- notes -- + +(1) loaded as 'permanent'. +(2) fbcon trouble, temp fix: ps3-hack-fbcon-shutdown.diff +(3) need to fix fbcon to support remove +(4) not working, WIP + +-------------------------------------------------------------------------------- +* Bus layout + +system-bus --+-- sb -------+-- usb0 --+-- ehci0 + | | +-- ohci0 + | | + | +-- usb1 --+-- ehci1 + | | +-- ohci1 + | | + | +-- gelic + | + +-- storage --+-- disk + | +-- flash + | +-- rom + | + +-- ioc0 -----+-- gfx + | +-- sound + | + +-- vuart ----+-- av_settings + +-- system_manager + + +bus | bus | in | +name | num | repo | +----------+------+------+--------------------------------------- +sb | 04h | yes | +storage | 05h | yes | +ioc0 | 81h | no | +vuart | 82h | no | +----------+------+------+--------------------------------------- + +device | type | irq | dma | mmio | +--------------+--------+------+-----+------+-------------------------- +usb0 sb_04 +usb1 sb_04 +gelic sb_03 +disk st_00 +flash st_0e +rom st_05 +gfx +sound +av_settings vu_00 +system_manager vu_02 Index: linux-2.6/drivers/block/Makefile =================================================================== --- linux-2.6.orig/drivers/block/Makefile +++ linux-2.6/drivers/block/Makefile @@ -29,3 +29,4 @@ obj-$(CONFIG_VIODASD) += viodasd.o obj-$(CONFIG_BLK_DEV_SX8) += sx8.o obj-$(CONFIG_BLK_DEV_UB) += ub.o +obj-$(CONFIG_PS3_DISK) += ps3disk.o Index: linux-2.6/drivers/block/ps3disk.c =================================================================== --- /dev/null +++ linux-2.6/drivers/block/ps3disk.c @@ -0,0 +1,623 @@ +/* + * PS3 Disk Storage Driver + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * 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; version 2 of the License. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +#include +#include +#include + + +#define DEVICE_NAME "ps3disk" + +#define BOUNCE_SIZE (64*1024) + +#define PS3DISK_MAX_DISKS 16 +#define PS3DISK_MINORS 16 + +#define KERNEL_SECTOR_SIZE 512 + + +#define PS3DISK_NAME "ps3d%c" + + +struct ps3disk_private { + spinlock_t lock; /* Request queue spinlock */ + struct request_queue *queue; + struct gendisk *gendisk; + unsigned int blocking_factor; + struct request *req; + u64 raw_capacity; + unsigned char model[ATA_ID_PROD_LEN+1]; +}; +#define ps3disk_priv(dev) ((dev)->sbd.core.driver_data) + + +#define LV1_STORAGE_SEND_ATA_COMMAND (2) +#define LV1_STORAGE_ATA_HDDOUT (0x23) + +struct lv1_ata_cmnd_block { + u16 features; + u16 sector_count; + u16 LBA_low; + u16 LBA_mid; + u16 LBA_high; + u8 device; + u8 command; + u32 is_ext; + u32 proto; + u32 in_out; + u32 size; + u64 buffer; + u32 arglen; +}; + +enum lv1_ata_proto { + NON_DATA_PROTO = 0, + PIO_DATA_IN_PROTO = 1, + PIO_DATA_OUT_PROTO = 2, + DMA_PROTO = 3 +}; + +enum lv1_ata_in_out { + DIR_WRITE = 0, /* memory -> device */ + DIR_READ = 1 /* device -> memory */ +}; + +static int ps3disk_major; + + +static struct block_device_operations ps3disk_fops = { + .owner = THIS_MODULE, +}; + + +static void ps3disk_scatter_gather(struct ps3_storage_device *dev, + struct request *req, int gather) +{ + unsigned int sectors = 0, offset = 0; + struct bio *bio; + sector_t sector; + struct bio_vec *bvec; + unsigned int i = 0, j; + size_t size; + void *buf; + + rq_for_each_bio(bio, req) { + sector = bio->bi_sector; + dev_dbg(&dev->sbd.core, + "%s:%u: bio %u: %u segs %u sectors from %lu\n", + __func__, __LINE__, i, bio_segments(bio), + bio_sectors(bio), sector); + bio_for_each_segment(bvec, bio, j) { + size = bio_cur_sectors(bio)*KERNEL_SECTOR_SIZE; + buf = __bio_kmap_atomic(bio, j, KM_USER0); + if (gather) + memcpy(dev->bounce_buf+offset, buf, size); + else + memcpy(buf, dev->bounce_buf+offset, size); + offset += size; + __bio_kunmap_atomic(bio, KM_USER0); + } + sectors += bio_sectors(bio); + i++; + } +} + +static int ps3disk_submit_request_sg(struct ps3_storage_device *dev, + struct request *req) +{ + struct ps3disk_private *priv = ps3disk_priv(dev); + int write = rq_data_dir(req), res; + const char *op = write ? "write" : "read"; + u64 start_sector, sectors; + unsigned int region_id = dev->regions[dev->region_idx].id; + +#ifdef DEBUG + unsigned int n = 0; + struct bio *bio; + rq_for_each_bio(bio, req) + n++; + dev_dbg(&dev->sbd.core, + "%s:%u: %s req has %u bios for %lu sectors %lu hard sectors\n", + __func__, __LINE__, op, n, req->nr_sectors, + req->hard_nr_sectors); +#endif + + start_sector = req->sector*priv->blocking_factor; + sectors = req->nr_sectors*priv->blocking_factor; + dev_dbg(&dev->sbd.core, "%s:%u: %s %lu sectors starting at %lu\n", + __func__, __LINE__, op, sectors, start_sector); + + if (write) { + ps3disk_scatter_gather(dev, req, 1); + + res = lv1_storage_write(dev->sbd.dev_id, region_id, + start_sector, sectors, 0, + dev->bounce_lpar, &dev->tag); + } else { + res = lv1_storage_read(dev->sbd.dev_id, region_id, + start_sector, sectors, 0, + dev->bounce_lpar, &dev->tag); + } + if (res) { + dev_err(&dev->sbd.core, "%s:%u: %s failed %d\n", __func__, + __LINE__, op, res); + end_request(req, 0); + return 0; + } + + priv->req = req; + return 1; +} + +static int ps3disk_submit_flush_request(struct ps3_storage_device *dev, + struct request *req) +{ + struct ps3disk_private *priv = ps3disk_priv(dev); + u64 res; + + dev_dbg(&dev->sbd.core, "%s:%u: flush request\n", __func__, __LINE__); + + res = lv1_storage_send_device_command(dev->sbd.dev_id, + LV1_STORAGE_ATA_HDDOUT, 0, 0, 0, + 0, &dev->tag); + if (res) { + dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%lx\n", + __func__, __LINE__, res); + end_request(req, 0); + return 0; + } + + priv->req = req; + return 1; +} + +static void ps3disk_do_request(struct ps3_storage_device *dev, + request_queue_t *q) +{ + struct request *req; + + dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__); + + while ((req = elv_next_request(q))) { + if (blk_fs_request(req)) { + if (ps3disk_submit_request_sg(dev, req)) + break; + } else if (req->cmd_type == REQ_TYPE_FLUSH) { + if (ps3disk_submit_flush_request(dev, req)) + break; + } else { + blk_dump_rq_flags(req, DEVICE_NAME " bad request"); + end_request(req, 0); + continue; + } + } +} + +static void ps3disk_request(request_queue_t *q) +{ + struct ps3_storage_device *dev = q->queuedata; + struct ps3disk_private *priv = ps3disk_priv(dev); + + if (priv->req) { + dev_dbg(&dev->sbd.core, "%s:%u busy\n", __func__, __LINE__); + return; + } + + ps3disk_do_request(dev, q); +} + +static irqreturn_t ps3disk_interrupt(int irq, void *data) +{ + struct ps3_storage_device *dev = data; + struct ps3disk_private *priv; + struct request *req; + int res, read, uptodate; + u64 tag, status; + unsigned long num_sectors; + const char *op; + + res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status); + + if (tag != dev->tag) + dev_err(&dev->sbd.core, + "%s:%u: tag mismatch, got %lx, expected %lx\n", + __func__, __LINE__, tag, dev->tag); + + if (res) { + dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%lx\n", + __func__, __LINE__, res, status); + return IRQ_HANDLED; + } + + priv = ps3disk_priv(dev); + req = priv->req; + if (!req) { + dev_dbg(&dev->sbd.core, + "%s:%u non-block layer request completed\n", __func__, + __LINE__); + dev->lv1_status = status; + complete(&dev->done); + return IRQ_HANDLED; + } + + if (req->cmd_type == REQ_TYPE_FLUSH) { + read = 0; + num_sectors = req->hard_cur_sectors; + op = "flush"; + } else { + read = !rq_data_dir(req); + num_sectors = req->nr_sectors; + op = read ? "read" : "write"; + } + if (status) { + dev_dbg(&dev->sbd.core, "%s:%u: %s failed 0x%lx\n", __func__, + __LINE__, op, status); + uptodate = 0; + } else { + dev_dbg(&dev->sbd.core, "%s:%u: %s completed\n", __func__, + __LINE__, op); + uptodate = 1; + if (read) + ps3disk_scatter_gather(dev, req, 0); + } + + spin_lock(&priv->lock); + if (!end_that_request_first(req, uptodate, num_sectors)) { + add_disk_randomness(req->rq_disk); + blkdev_dequeue_request(req); + end_that_request_last(req, uptodate); + } + priv->req = NULL; + ps3disk_do_request(dev, priv->queue); + spin_unlock(&priv->lock); + + return IRQ_HANDLED; +} + +static int ps3disk_sync_cache(struct ps3_storage_device *dev) +{ + u64 res; + + dev_dbg(&dev->sbd.core, "%s:%u: sync cache\n", __func__, __LINE__); + + res = ps3stor_send_command(dev, LV1_STORAGE_ATA_HDDOUT, 0, 0, 0, 0); + if (res) { + dev_err(&dev->sbd.core, "%s:%u: sync cache failed 0x%lx\n", + __func__, __LINE__, res); + return -EIO; + } + return 0; +} + + +/* ATA helpers copied from drivers/ata/libata-core.c */ + +static void swap_buf_le16(u16 *buf, unsigned int buf_words) +{ +#ifdef __BIG_ENDIAN + unsigned int i; + + for (i = 0; i < buf_words; i++) + buf[i] = le16_to_cpu(buf[i]); +#endif /* __BIG_ENDIAN */ +} + +static u64 ata_id_n_sectors(const u16 *id) +{ + if (ata_id_has_lba(id)) { + if (ata_id_has_lba48(id)) + return ata_id_u64(id, 100); + else + return ata_id_u32(id, 60); + } else { + if (ata_id_current_chs_valid(id)) + return ata_id_u32(id, 57); + else + return id[1] * id[3] * id[6]; + } +} + +static void ata_id_string(const u16 *id, unsigned char *s, unsigned int ofs, + unsigned int len) +{ + unsigned int c; + + while (len > 0) { + c = id[ofs] >> 8; + *s = c; + s++; + + c = id[ofs] & 0xff; + *s = c; + s++; + + ofs++; + len -= 2; + } +} + +static void ata_id_c_string(const u16 *id, unsigned char *s, unsigned int ofs, + unsigned int len) +{ + unsigned char *p; + + WARN_ON(!(len & 1)); + + ata_id_string(id, s, ofs, len - 1); + + p = s + strnlen(s, len - 1); + while (p > s && p[-1] == ' ') + p--; + *p = '\0'; +} + +static int ps3disk_identify(struct ps3_storage_device *dev) +{ + struct ps3disk_private *priv = ps3disk_priv(dev); + struct lv1_ata_cmnd_block ata_cmnd; + u16 *id = dev->bounce_buf; + u64 res; + + dev_dbg(&dev->sbd.core, "%s:%u: identify disk\n", __func__, __LINE__); + + memset(&ata_cmnd, 0, sizeof(struct lv1_ata_cmnd_block)); + ata_cmnd.command = ATA_CMD_ID_ATA; + ata_cmnd.sector_count = 1; + ata_cmnd.size = ata_cmnd.arglen = ATA_ID_WORDS * 2; + ata_cmnd.buffer = dev->bounce_lpar; + ata_cmnd.proto = PIO_DATA_IN_PROTO; + ata_cmnd.in_out = DIR_READ; + + res = ps3stor_send_command(dev, LV1_STORAGE_SEND_ATA_COMMAND, + ps3_mm_phys_to_lpar(__pa(&ata_cmnd)), + sizeof(ata_cmnd), ata_cmnd.buffer, + ata_cmnd.arglen); + if (res) { + dev_err(&dev->sbd.core, "%s:%u: identify disk failed 0x%lx\n", + __func__, __LINE__, res); + return -EIO; + } + + swap_buf_le16(id, ATA_ID_WORDS); + + /* All we're interested in are raw capacity and model name */ + priv->raw_capacity = ata_id_n_sectors(id); + ata_id_c_string(id, priv->model, ATA_ID_PROD, sizeof(priv->model)); + return 0; +} + +static void ps3disk_prepare_flush(request_queue_t *q, struct request *req) +{ + struct ps3_storage_device *dev = q->queuedata; + + dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__); + + memset(req->cmd, 0, sizeof(req->cmd)); + req->cmd_type = REQ_TYPE_FLUSH; +} + +static int ps3disk_issue_flush(request_queue_t *q, struct gendisk *gendisk, + sector_t *sector) +{ + struct ps3_storage_device *dev = q->queuedata; + struct request *req; + int res; + + dev_dbg(&dev->sbd.core, "%s:%u\n", __func__, __LINE__); + + req = blk_get_request(q, WRITE, __GFP_WAIT); + ps3disk_prepare_flush(q, req); + res = blk_execute_rq(q, gendisk, req, 0); + if (res) + dev_err(&dev->sbd.core, "%s:%u: flush request failed %d\n", + __func__, __LINE__, res); + blk_put_request(req); + return res; +} + + +static unsigned long ps3disk_mask; + +static int __devinit ps3disk_probe(struct ps3_system_bus_device *_dev) +{ + struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); + struct ps3disk_private *priv; + int error; + unsigned int devidx; + struct request_queue *queue; + struct gendisk *gendisk; + + if (dev->blk_size < KERNEL_SECTOR_SIZE) { + dev_err(&dev->sbd.core, + "%s:%u: cannot handle block size %lu\n", __func__, + __LINE__, dev->blk_size); + return -EINVAL; + } + + BUILD_BUG_ON(PS3DISK_MAX_DISKS > BITS_PER_LONG); + devidx = find_first_zero_bit(&ps3disk_mask, PS3DISK_MAX_DISKS); + if (devidx >= PS3DISK_MAX_DISKS) { + dev_err(&dev->sbd.core, "%s:%u: Too many disks\n", __func__, + __LINE__); + return -ENOSPC; + } + __set_bit(devidx, &ps3disk_mask); + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + error = -ENOMEM; + goto fail; + } + + ps3disk_priv(dev) = priv; + spin_lock_init(&priv->lock); + + dev->bounce_size = BOUNCE_SIZE; + dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA); + if (!dev->bounce_buf) { + error = -ENOMEM; + goto fail_free_priv; + } + + error = ps3stor_setup(dev, ps3disk_interrupt); + if (error) + goto fail_free_bounce; + + ps3disk_identify(dev); + + queue = blk_init_queue(ps3disk_request, &priv->lock); + if (!queue) { + dev_err(&dev->sbd.core, "%s:%u: blk_init_queue failed\n", + __func__, __LINE__); + error = -ENOMEM; + goto fail_teardown; + } + + priv->queue = queue; + queue->queuedata = dev; + + blk_queue_bounce_limit(queue, BLK_BOUNCE_HIGH); + + blk_queue_max_sectors(queue, dev->bounce_size/KERNEL_SECTOR_SIZE); + blk_queue_segment_boundary(queue, -1UL); + blk_queue_dma_alignment(queue, dev->blk_size-1); + blk_queue_hardsect_size(queue, dev->blk_size); + + blk_queue_issue_flush_fn(queue, ps3disk_issue_flush); + blk_queue_ordered(queue, QUEUE_ORDERED_DRAIN_FLUSH, + ps3disk_prepare_flush); + + blk_queue_max_phys_segments(queue, -1); + blk_queue_max_hw_segments(queue, -1); + blk_queue_max_segment_size(queue, dev->bounce_size); + + gendisk = alloc_disk(PS3DISK_MINORS); + if (!gendisk) { + dev_err(&dev->sbd.core, "%s:%u: alloc_disk failed\n", __func__, + __LINE__); + error = -ENOMEM; + goto fail_cleanup_queue; + } + + priv->gendisk = gendisk; + gendisk->major = ps3disk_major; + gendisk->first_minor = devidx * PS3DISK_MINORS; + gendisk->fops = &ps3disk_fops; + gendisk->queue = queue; + gendisk->private_data = dev; + gendisk->driverfs_dev = &dev->sbd.core; + snprintf(gendisk->disk_name, sizeof(gendisk->disk_name), PS3DISK_NAME, + devidx+'a'); + priv->blocking_factor = dev->blk_size/KERNEL_SECTOR_SIZE; + set_capacity(gendisk, + dev->regions[dev->region_idx].size*priv->blocking_factor); + + dev_info(&dev->sbd.core, + "%s is a %s (%lu MiB total, %lu MiB for OtherOS)\n", + gendisk->disk_name, priv->model, priv->raw_capacity >> 11, + get_capacity(gendisk) >> 11); + + add_disk(gendisk); + return 0; + +fail_cleanup_queue: + blk_cleanup_queue(queue); +fail_teardown: + ps3stor_teardown(dev); +fail_free_bounce: + kfree(dev->bounce_buf); +fail_free_priv: + kfree(priv); + ps3disk_priv(dev) = NULL; +fail: + __clear_bit(devidx, &ps3disk_mask); + return error; +} + +static int ps3disk_remove(struct ps3_system_bus_device *_dev) +{ + struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); + struct ps3disk_private *priv = ps3disk_priv(dev); + + __clear_bit(priv->gendisk->first_minor / PS3DISK_MINORS, + &ps3disk_mask); + del_gendisk(priv->gendisk); + blk_cleanup_queue(priv->queue); + put_disk(priv->gendisk); + dev_notice(&dev->sbd.core, "Synchronizing disk cache\n"); + ps3disk_sync_cache(dev); + ps3stor_teardown(dev); + kfree(dev->bounce_buf); + kfree(priv); + ps3disk_priv(dev) = NULL; + return 0; +} + +static struct ps3_system_bus_driver ps3disk = { + .match_id = PS3_MATCH_ID_STOR_DISK, + .core.name = DEVICE_NAME, + .core.owner = THIS_MODULE, + .probe = ps3disk_probe, + .remove = ps3disk_remove, + .shutdown = ps3disk_remove, +}; + + +static int __init ps3disk_init(void) +{ + int error; + + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ENODEV; + + error = register_blkdev(0, DEVICE_NAME); + if (error <= 0) { + printk(KERN_ERR "%s:%u: register_blkdev failed %d\n", __func__, + __LINE__, error); + return error; + } + ps3disk_major = error; + + pr_info("%s:%u: registered block device major %d\n", __func__, + __LINE__, ps3disk_major); + + error = ps3_system_bus_driver_register(&ps3disk); + if (error) + unregister_blkdev(ps3disk_major, DEVICE_NAME); + + return error; +} + +static void __exit ps3disk_exit(void) +{ + ps3_system_bus_driver_unregister(&ps3disk); + unregister_blkdev(ps3disk_major, DEVICE_NAME); +} + +module_init(ps3disk_init); +module_exit(ps3disk_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PS3 Disk Storage Driver"); +MODULE_AUTHOR("Sony Corporation"); +MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_DISK); Index: linux-2.6/drivers/char/Makefile =================================================================== --- linux-2.6.orig/drivers/char/Makefile +++ linux-2.6/drivers/char/Makefile @@ -104,6 +104,8 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi/ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o obj-$(CONFIG_TCG_TPM) += tpm/ +obj-$(CONFIG_PS3_FLASH) += ps3flash.o + # Files generated that shall be removed upon make clean clean-files := consolemap_deftbl.c defkeymap.c Index: linux-2.6/drivers/char/ps3flash.c =================================================================== --- /dev/null +++ linux-2.6/drivers/char/ps3flash.c @@ -0,0 +1,429 @@ +/* + * PS3 FLASH ROM Storage Driver + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * 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; version 2 of the License. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include + +#include +#include + + +#define DEVICE_NAME "ps3flash" + +#define FLASH_BLOCK_SIZE (256*1024) + + +struct ps3flash_private { + struct mutex mutex; /* Bounce buffer mutex */ +}; +#define ps3flash_priv(dev) ((dev)->sbd.core.driver_data) + +static struct ps3_storage_device *ps3flash_dev; + +static ssize_t ps3flash_read_write_sectors(struct ps3_storage_device *dev, + u64 lpar, u64 start_sector, + u64 sectors, int write) +{ + u64 res = ps3stor_read_write_sectors(dev, lpar, start_sector, sectors, + write); + if (res) { + dev_err(&dev->sbd.core, "%s:%u: %s failed 0x%lx\n", __func__, + __LINE__, write ? "write" : "read", res); + return -EIO; + } + return sectors; +} + +static ssize_t ps3flash_read_sectors(struct ps3_storage_device *dev, + u64 start_sector, u64 sectors, + unsigned int sector_offset) +{ + u64 max_sectors, lpar; + + max_sectors = dev->bounce_size / dev->blk_size; + if (sectors > max_sectors) { + dev_dbg(&dev->sbd.core, "%s:%u Limiting sectors to %lu\n", + __func__, __LINE__, max_sectors); + sectors = max_sectors; + } + + lpar = dev->bounce_lpar + sector_offset * dev->blk_size; + return ps3flash_read_write_sectors(dev, lpar, start_sector, sectors, + 0); +} + +static ssize_t ps3flash_write_chunk(struct ps3_storage_device *dev, + u64 start_sector) +{ + u64 sectors = dev->bounce_size / dev->blk_size; + return ps3flash_read_write_sectors(dev, dev->bounce_lpar, start_sector, + sectors, 1); +} + +static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin) +{ + struct ps3_storage_device *dev = ps3flash_dev; + u64 size = dev->regions[dev->region_idx].size*dev->blk_size; + + switch (origin) { + case 1: + offset += file->f_pos; + break; + case 2: + offset += size; + break; + } + if (offset < 0) + return -EINVAL; + + file->f_pos = offset; + return file->f_pos; +} + +static ssize_t ps3flash_read(struct file *file, char __user *buf, size_t count, + loff_t *pos) +{ + struct ps3_storage_device *dev = ps3flash_dev; + struct ps3flash_private *priv = ps3flash_priv(dev); + u64 size, start_sector, end_sector, offset; + ssize_t sectors_read; + size_t remaining, n; + + dev_dbg(&dev->sbd.core, + "%s:%u: Reading %zu bytes at position %lld to user 0x%p\n", + __func__, __LINE__, count, *pos, buf); + + size = dev->regions[dev->region_idx].size*dev->blk_size; + if (*pos >= size || !count) + return 0; + + if (*pos+count > size) { + dev_dbg(&dev->sbd.core, + "%s:%u Truncating count from %zu to %llu\n", __func__, + __LINE__, count, size - *pos); + count = size - *pos; + } + + start_sector = do_div_llr(*pos, dev->blk_size, &offset); + end_sector = DIV_ROUND_UP(*pos+count, dev->blk_size); + + remaining = count; + do { + mutex_lock(&priv->mutex); + + sectors_read = ps3flash_read_sectors(dev, start_sector, + end_sector-start_sector, + 0); + if (sectors_read < 0) { + mutex_unlock(&priv->mutex); + return sectors_read; + } + + n = min(remaining, sectors_read*dev->blk_size-offset); + dev_dbg(&dev->sbd.core, + "%s:%u: copy %lu bytes from 0x%p to user 0x%p\n", + __func__, __LINE__, n, dev->bounce_buf+offset, buf); + if (copy_to_user(buf, dev->bounce_buf+offset, n)) { + mutex_unlock(&priv->mutex); + return -EFAULT; + } + + mutex_unlock(&priv->mutex); + + *pos += n; + buf += n; + remaining -= n; + start_sector += sectors_read; + offset = 0; + } while (remaining > 0); + + return count; +} + +static ssize_t ps3flash_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct ps3_storage_device *dev = ps3flash_dev; + struct ps3flash_private *priv = ps3flash_priv(dev); + u64 size, chunk_sectors, start_write_sector, end_write_sector, + end_read_sector, start_read_sector, head, tail, offset; + ssize_t res; + size_t remaining, n; + unsigned int sec_off; + + dev_dbg(&dev->sbd.core, + "%s:%u: Writing %zu bytes at position %lld from user 0x%p\n", + __func__, __LINE__, count, *pos, buf); + + size = dev->regions[dev->region_idx].size*dev->blk_size; + if (*pos >= size || !count) + return 0; + + if (*pos+count > size) { + dev_dbg(&dev->sbd.core, + "%s:%u Truncating count from %zu to %llu\n", __func__, + __LINE__, count, size - *pos); + count = size - *pos; + } + + chunk_sectors = dev->bounce_size / dev->blk_size; + + start_write_sector = do_div_llr(*pos, dev->bounce_size, &offset) * + chunk_sectors; + end_write_sector = DIV_ROUND_UP(*pos+count, dev->bounce_size) * + chunk_sectors; + + end_read_sector = DIV_ROUND_UP(*pos, dev->blk_size); + start_read_sector = (*pos+count) / dev->blk_size; + + /* + * As we have to write in 256 KiB chunks, while we can read in blk_size + * (usually 512 bytes) chunks, we perform the following steps: + * 1. Read from start_write_sector to end_read_sector ("head") + * 2. Read from start_read_sector to end_write_sector ("tail") + * 3. Copy data to buffer + * 4. Write from start_write_sector to end_write_sector + * All of this is complicated by using only one 256 KiB bounce buffer. + */ + + head = end_read_sector-start_write_sector; + tail = end_write_sector-start_read_sector; + + remaining = count; + do { + mutex_lock(&priv->mutex); + + if (end_read_sector >= start_read_sector) { + /* Merge head and tail */ + dev_dbg(&dev->sbd.core, + "Merged head and tail: %lu sectors at %lu\n", + chunk_sectors, start_write_sector); + res = ps3flash_read_sectors(dev, start_write_sector, + chunk_sectors, 0); + if (res < 0) + goto fail; + } else { + if (head) { + /* Read head */ + dev_dbg(&dev->sbd.core, + "head: %lu sectors at %lu\n", head, + start_write_sector); + res = ps3flash_read_sectors(dev, + start_write_sector, + head, 0); + if (res < 0) + goto fail; + } + if (start_read_sector < + start_write_sector+chunk_sectors) { + /* Read tail */ + dev_dbg(&dev->sbd.core, + "tail: %lu sectors at %lu\n", tail, + start_read_sector); + sec_off = start_read_sector-start_write_sector; + res = ps3flash_read_sectors(dev, + start_read_sector, + tail, sec_off); + if (res < 0) + goto fail; + } + } + + n = min(remaining, dev->bounce_size-offset); + dev_dbg(&dev->sbd.core, + "%s:%u: copy %lu bytes from user 0x%p to 0x%p\n", + __func__, __LINE__, n, buf, dev->bounce_buf+offset); + if (copy_from_user(dev->bounce_buf+offset, buf, n)) { + res = -EFAULT; + goto fail; + } + + res = ps3flash_write_chunk(dev, start_write_sector); + if (res < 0) + goto fail; + + mutex_unlock(&priv->mutex); + + *pos += n; + buf += n; + remaining -= n; + start_write_sector += chunk_sectors; + head = 0; + offset = 0; + } while (remaining > 0); + + return count; + +fail: + mutex_unlock(&priv->mutex); + return res; +} + + +static irqreturn_t ps3flash_interrupt(int irq, void *data) +{ + struct ps3_storage_device *dev = data; + int res; + u64 tag, status; + + res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status); + + if (tag != dev->tag) + dev_err(&dev->sbd.core, + "%s:%u: tag mismatch, got %lx, expected %lx\n", + __func__, __LINE__, tag, dev->tag); + + if (res) { + dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%lx\n", + __func__, __LINE__, res, status); + } else { + dev->lv1_status = status; + complete(&dev->done); + } + return IRQ_HANDLED; +} + + +static const struct file_operations ps3flash_fops = { + .owner = THIS_MODULE, + .llseek = ps3flash_llseek, + .read = ps3flash_read, + .write = ps3flash_write, +}; + +static struct miscdevice ps3flash_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = DEVICE_NAME, + .fops = &ps3flash_fops, +}; + +static int __devinit ps3flash_probe(struct ps3_system_bus_device *_dev) +{ + struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); + struct ps3flash_private *priv; + int error; + unsigned long tmp; + + tmp = dev->regions[dev->region_idx].start*dev->blk_size; + if (tmp % FLASH_BLOCK_SIZE) { + dev_err(&dev->sbd.core, + "%s:%u region start %lu is not aligned\n", __func__, + __LINE__, tmp); + return -EINVAL; + } + tmp = dev->regions[dev->region_idx].size*dev->blk_size; + if (tmp % FLASH_BLOCK_SIZE) { + dev_err(&dev->sbd.core, + "%s:%u region size %lu is not aligned\n", __func__, + __LINE__, tmp); + return -EINVAL; + } + + /* use static buffer, kmalloc cannot allocate 256 KiB */ + if (!ps3flash_bounce_buffer.address) + return -ENODEV; + + if (ps3flash_dev) { + dev_err(&dev->sbd.core, + "Only one FLASH device is supported\n"); + return -EBUSY; + } + + ps3flash_dev = dev; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + error = -ENOMEM; + goto fail; + } + + ps3flash_priv(dev) = priv; + mutex_init(&priv->mutex); + + dev->bounce_size = ps3flash_bounce_buffer.size; + dev->bounce_buf = ps3flash_bounce_buffer.address; + + error = ps3stor_setup(dev, ps3flash_interrupt); + if (error) + goto fail_free_priv; + + ps3flash_misc.parent = &dev->sbd.core; + error = misc_register(&ps3flash_misc); + if (error) { + dev_err(&dev->sbd.core, "%s:%u: misc_register failed %d\n", + __func__, __LINE__, error); + goto fail_teardown; + } + + dev_info(&dev->sbd.core, "%s:%u: registered misc device %d\n", + __func__, __LINE__, ps3flash_misc.minor); + return 0; + +fail_teardown: + ps3stor_teardown(dev); +fail_free_priv: + kfree(priv); + ps3flash_priv(dev) = NULL; +fail: + ps3flash_dev = NULL; + return error; +} + +static int ps3flash_remove(struct ps3_system_bus_device *_dev) +{ + struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); + + misc_deregister(&ps3flash_misc); + ps3stor_teardown(dev); + kfree(ps3flash_priv(dev)); + ps3flash_priv(dev) = NULL; + ps3flash_dev = NULL; + return 0; +} + + +static struct ps3_system_bus_driver ps3flash = { + .match_id = PS3_MATCH_ID_STOR_FLASH, + .core.name = DEVICE_NAME, + .core.owner = THIS_MODULE, + .probe = ps3flash_probe, + .remove = ps3flash_remove, + .shutdown = ps3flash_remove, +}; + + +static int __init ps3flash_init(void) +{ + return ps3_system_bus_driver_register(&ps3flash); +} + +static void __exit ps3flash_exit(void) +{ + ps3_system_bus_driver_unregister(&ps3flash); +} + +module_init(ps3flash_init); +module_exit(ps3flash_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PS3 FLASH ROM Storage Driver"); +MODULE_AUTHOR("Sony Corporation"); +MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_FLASH); Index: linux-2.6/drivers/char/vt.c =================================================================== --- linux-2.6.orig/drivers/char/vt.c +++ linux-2.6/drivers/char/vt.c @@ -2989,8 +2989,7 @@ static int con_is_graphics(const struct return retval; } -static int unbind_con_driver(const struct consw *csw, int first, int last, - int deflt) +int unbind_con_driver(const struct consw *csw, int first, int last, int deflt) { struct module *owner = csw->owner; const struct consw *defcsw = NULL; Index: linux-2.6/drivers/net/Kconfig =================================================================== --- linux-2.6.orig/drivers/net/Kconfig +++ linux-2.6/drivers/net/Kconfig @@ -2264,6 +2264,24 @@ config TSI108_ETH To compile this driver as a module, choose M here: the module will be called tsi108_eth. +config GELIC_NET + tristate "PS3 Gigabit Ethernet driver" + depends on PPC_PS3 + help + This driver supports the network device on the PS3 game + console. This driver has built-in support for Ethernet. + + To compile this driver as a module, choose M here: the + module will be called ps3_gelic. + +config GELIC_WIRELESS + bool "PS3 Wireless support" + depends on GELIC_NET + select WIRELESS_EXT + help + This option enables the wireless networking support of + the PS3 network driver. + config GIANFAR tristate "Gianfar Ethernet" depends on 85xx || 83xx || PPC_86xx Index: linux-2.6/drivers/net/Makefile =================================================================== --- linux-2.6.orig/drivers/net/Makefile +++ linux-2.6/drivers/net/Makefile @@ -60,6 +60,9 @@ obj-$(CONFIG_TIGON3) += tg3.o obj-$(CONFIG_BNX2) += bnx2.o spidernet-y += spider_net.o spider_net_ethtool.o obj-$(CONFIG_SPIDER_NET) += spidernet.o sungem_phy.o +obj-$(CONFIG_GELIC_NET) += ps3_gelic.o +gelic_wireless-$(CONFIG_GELIC_WIRELESS) += ps3_gelic_wireless.o +ps3_gelic-objs += ps3_gelic_net.o $(gelic_wireless-y) obj-$(CONFIG_TC35815) += tc35815.o obj-$(CONFIG_SKGE) += skge.o obj-$(CONFIG_SKY2) += sky2.o Index: linux-2.6/drivers/net/ps3_gelic_net.c =================================================================== --- /dev/null +++ linux-2.6/drivers/net/ps3_gelic_net.c @@ -0,0 +1,1600 @@ +/* + * PS3 gelic network driver. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2006, 2007 Sony Corporation + * + * This file is based on: spider_net.c + * + * (C) Copyright IBM Corp. 2005 + * + * Authors : Utz Bacher + * Jens Osterkamp + * + * 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, 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. + */ + +#undef DEBUG + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ps3_gelic_net.h" + +#define DRV_NAME "Gelic Network Driver" +#define DRV_VERSION "1.0" + +MODULE_AUTHOR("SCE Inc."); +MODULE_DESCRIPTION("Gelic Network driver"); +MODULE_LICENSE("GPL"); + +static inline struct device *ctodev(struct gelic_net_card *card) +{ + return &card->dev->core; +} +static inline unsigned int bus_id(struct gelic_net_card *card) +{ + return card->dev->bus_id; +} +static inline unsigned int dev_id(struct gelic_net_card *card) +{ + return card->dev->dev_id; +} + +/* set irq_mask */ +static int gelic_net_set_irq_mask(struct gelic_net_card *card, u64 mask) +{ + int status; + + status = lv1_net_set_interrupt_mask(bus_id(card), dev_id(card), + mask, 0); + if (status) + dev_info(ctodev(card), + "lv1_net_set_interrupt_mask failed %d\n", status); + return status; +} +static inline void gelic_net_rx_irq_on(struct gelic_net_card *card) +{ + gelic_net_set_irq_mask(card, card->ghiintmask | GELIC_NET_RXINT); +} +static inline void gelic_net_rx_irq_off(struct gelic_net_card *card) +{ + gelic_net_set_irq_mask(card, card->ghiintmask & ~GELIC_NET_RXINT); +} +/** + * gelic_net_get_descr_status -- returns the status of a descriptor + * @descr: descriptor to look at + * + * returns the status as in the dmac_cmd_status field of the descriptor + */ +static enum gelic_net_descr_status +gelic_net_get_descr_status(struct gelic_net_descr *descr) +{ + u32 cmd_status; + + cmd_status = descr->dmac_cmd_status; + cmd_status >>= GELIC_NET_DESCR_IND_PROC_SHIFT; + return cmd_status; +} + +/** + * gelic_net_set_descr_status -- sets the status of a descriptor + * @descr: descriptor to change + * @status: status to set in the descriptor + * + * changes the status to the specified value. Doesn't change other bits + * in the status + */ +static void gelic_net_set_descr_status(struct gelic_net_descr *descr, + enum gelic_net_descr_status status) +{ + u32 cmd_status; + + /* read the status */ + cmd_status = descr->dmac_cmd_status; + /* clean the upper 4 bits */ + cmd_status &= GELIC_NET_DESCR_IND_PROC_MASKO; + /* add the status to it */ + cmd_status |= ((u32)status) << GELIC_NET_DESCR_IND_PROC_SHIFT; + /* and write it back */ + descr->dmac_cmd_status = cmd_status; + /* + * dma_cmd_status field is used to indicate whether the descriptor + * is valid or not. + * Usually caller of this function wants to inform that to the + * hardware, so we assure here the hardware sees the change. + */ + wmb(); +} + +/** + * gelic_net_free_chain - free descriptor chain + * @card: card structure + * @descr_in: address of desc + */ +static void gelic_net_free_chain(struct gelic_net_card *card, + struct gelic_net_descr *descr_in) +{ + struct gelic_net_descr *descr; + + for (descr = descr_in; descr && descr->bus_addr; descr = descr->next) { + dma_unmap_single(ctodev(card), descr->bus_addr, + GELIC_NET_DESCR_SIZE, DMA_BIDIRECTIONAL); + descr->bus_addr = 0; + } +} + +/** + * gelic_net_init_chain - links descriptor chain + * @card: card structure + * @chain: address of chain + * @start_descr: address of descriptor array + * @no: number of descriptors + * + * we manage a circular list that mirrors the hardware structure, + * except that the hardware uses bus addresses. + * + * returns 0 on success, <0 on failure + */ +static int gelic_net_init_chain(struct gelic_net_card *card, + struct gelic_net_descr_chain *chain, + struct gelic_net_descr *start_descr, int no) +{ + int i; + struct gelic_net_descr *descr; + + descr = start_descr; + memset(descr, 0, sizeof(*descr) * no); + + /* set up the hardware pointers in each descriptor */ + for (i = 0; i < no; i++, descr++) { + gelic_net_set_descr_status(descr, GELIC_NET_DESCR_NOT_IN_USE); + descr->bus_addr = + dma_map_single(ctodev(card), descr, + GELIC_NET_DESCR_SIZE, + DMA_BIDIRECTIONAL); + + if (!descr->bus_addr) + goto iommu_error; + + descr->next = descr + 1; + descr->prev = descr - 1; + } + /* make them as ring */ + (descr - 1)->next = start_descr; + start_descr->prev = (descr - 1); + + /* chain bus addr of hw descriptor */ + descr = start_descr; + for (i = 0; i < no; i++, descr++) { + descr->next_descr_addr = descr->next->bus_addr; + } + + chain->head = start_descr; + chain->tail = start_descr; + + /* do not chain last hw descriptor */ + (descr - 1)->next_descr_addr = 0; + + return 0; + +iommu_error: + for (i--, descr--; 0 <= i; i--, descr--) + if (descr->bus_addr) + dma_unmap_single(ctodev(card), descr->bus_addr, + GELIC_NET_DESCR_SIZE, + DMA_BIDIRECTIONAL); + return -ENOMEM; +} + +/** + * gelic_net_prepare_rx_descr - reinitializes a rx descriptor + * @card: card structure + * @descr: descriptor to re-init + * + * return 0 on succes, <0 on failure + * + * allocates a new rx skb, iommu-maps it and attaches it to the descriptor. + * Activate the descriptor state-wise + */ +static int gelic_net_prepare_rx_descr(struct gelic_net_card *card, + struct gelic_net_descr *descr) +{ + int offset; + unsigned int bufsize; + + if (gelic_net_get_descr_status(descr) != GELIC_NET_DESCR_NOT_IN_USE) { + dev_info(ctodev(card), "%s: ERROR status \n", __func__); + } + /* we need to round up the buffer size to a multiple of 128 */ + bufsize = ALIGN(GELIC_NET_MAX_MTU, GELIC_NET_RXBUF_ALIGN); + + /* and we need to have it 128 byte aligned, therefore we allocate a + * bit more */ + descr->skb = netdev_alloc_skb(card->netdev, + bufsize + GELIC_NET_RXBUF_ALIGN - 1); + if (!descr->skb) { + descr->buf_addr = 0; /* tell DMAC don't touch memory */ + dev_info(ctodev(card), + "%s:allocate skb failed !!\n", __func__); + return -ENOMEM; + } + descr->buf_size = bufsize; + descr->dmac_cmd_status = 0; + descr->result_size = 0; + descr->valid_size = 0; + descr->data_error = 0; + + offset = ((unsigned long)descr->skb->data) & + (GELIC_NET_RXBUF_ALIGN - 1); + if (offset) + skb_reserve(descr->skb, GELIC_NET_RXBUF_ALIGN - offset); + /* io-mmu-map the skb */ + descr->buf_addr = dma_map_single(ctodev(card), descr->skb->data, + GELIC_NET_MAX_MTU, + DMA_FROM_DEVICE); + if (!descr->buf_addr) { + dev_kfree_skb_any(descr->skb); + descr->skb = NULL; + dev_info(ctodev(card), + "%s:Could not iommu-map rx buffer\n", __func__); + gelic_net_set_descr_status(descr, GELIC_NET_DESCR_NOT_IN_USE); + return -ENOMEM; + } else { + gelic_net_set_descr_status(descr, GELIC_NET_DESCR_CARDOWNED); + return 0; + } +} + +/** + * gelic_net_release_rx_chain - free all skb of rx descr + * @card: card structure + * + */ +static void gelic_net_release_rx_chain(struct gelic_net_card *card) +{ + struct gelic_net_descr *descr = card->rx_chain.head; + + do { + if (descr->skb) { + dma_unmap_single(ctodev(card), + descr->buf_addr, + descr->skb->len, + DMA_FROM_DEVICE); + descr->buf_addr = 0; + dev_kfree_skb_any(descr->skb); + descr->skb = NULL; + descr->dmac_cmd_status = GELIC_NET_DESCR_NOT_IN_USE; + } + descr = descr->next; + } while (descr != card->rx_chain.head); +} + +/** + * gelic_net_fill_rx_chain - fills descriptors/skbs in the rx chains + * @card: card structure + * + * fills all descriptors in the rx chain: allocates skbs + * and iommu-maps them. + * returns 0 on success, <0 on failure + */ +static int gelic_net_fill_rx_chain(struct gelic_net_card *card) +{ + struct gelic_net_descr *descr = card->rx_chain.head; + int ret; + + do { + if (!descr->skb) { + ret = gelic_net_prepare_rx_descr(card, descr); + if (ret) + goto rewind; + } + descr = descr->next; + } while (descr != card->rx_chain.head); + + return 0; +rewind: + gelic_net_release_rx_chain(card); + return ret; +} + +/** + * gelic_net_alloc_rx_skbs - allocates rx skbs in rx descriptor chains + * @card: card structure + * + * returns 0 on success, <0 on failure + */ +static int gelic_net_alloc_rx_skbs(struct gelic_net_card *card) +{ + struct gelic_net_descr_chain *chain; + int ret; + chain = &card->rx_chain; + ret = gelic_net_fill_rx_chain(card); + chain->head = card->rx_top->prev; /* point to the last */ + return ret; +} + +/** + * gelic_net_release_tx_descr - processes a used tx descriptor + * @card: card structure + * @descr: descriptor to release + * + * releases a used tx descriptor (unmapping, freeing of skb) + */ +static void gelic_net_release_tx_descr(struct gelic_net_card *card, + struct gelic_net_descr *descr) +{ + struct sk_buff *skb; + + + if (descr->data_status & (1 << GELIC_NET_TXDESC_TAIL)) { + /* 2nd descriptor */ + skb = descr->skb; + dma_unmap_single(ctodev(card), descr->buf_addr, skb->len, + DMA_TO_DEVICE); + dev_kfree_skb_any(skb); + } else { + dma_unmap_single(ctodev(card), descr->buf_addr, + descr->buf_size, DMA_TO_DEVICE); + } + + descr->buf_addr = 0; + descr->buf_size = 0; + descr->next_descr_addr = 0; + descr->result_size = 0; + descr->valid_size = 0; + descr->data_status = 0; + descr->data_error = 0; + descr->skb = NULL; + + /* set descr status */ + descr->dmac_cmd_status = GELIC_NET_DMAC_CMDSTAT_NOT_IN_USE; +} + +/** + * gelic_net_release_tx_chain - processes sent tx descriptors + * @card: adapter structure + * @stop: net_stop sequence + * + * releases the tx descriptors that gelic has finished with + */ +static void gelic_net_release_tx_chain(struct gelic_net_card *card, int stop) +{ + struct gelic_net_descr_chain *tx_chain; + enum gelic_net_descr_status status; + int release = 0; + + for (tx_chain = &card->tx_chain; + tx_chain->head != tx_chain->tail && tx_chain->tail; + tx_chain->tail = tx_chain->tail->next) { + status = gelic_net_get_descr_status(tx_chain->tail); + switch (status) { + case GELIC_NET_DESCR_RESPONSE_ERROR: + case GELIC_NET_DESCR_PROTECTION_ERROR: + case GELIC_NET_DESCR_FORCE_END: + if (printk_ratelimit()) + dev_info(ctodev(card), + "%s: forcing end of tx descriptor " \ + "with status %x\n", + __func__, status); + card->netdev_stats.tx_dropped++; + break; + + case GELIC_NET_DESCR_COMPLETE: + card->netdev_stats.tx_packets++; + card->netdev_stats.tx_bytes += + tx_chain->tail->skb->len; + break; + + case GELIC_NET_DESCR_CARDOWNED: + /* pending tx request */ + default: + /* any other value (== GELIC_NET_DESCR_NOT_IN_USE) */ + goto out; + } + gelic_net_release_tx_descr(card, tx_chain->tail); + release = 1; + } +out: + if (!stop && release) + netif_wake_queue(card->netdev); +} + +/** + * gelic_net_set_multi - sets multicast addresses and promisc flags + * @netdev: interface device structure + * + * gelic_net_set_multi configures multicast addresses as needed for the + * netdev interface. It also sets up multicast, allmulti and promisc + * flags appropriately + */ +static void gelic_net_set_multi(struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + struct dev_mc_list *mc; + unsigned int i; + uint8_t *p; + u64 addr; + int status; + + /* clear all multicast address */ + status = lv1_net_remove_multicast_address(bus_id(card), dev_id(card), + 0, 1); + if (status) + dev_err(ctodev(card), + "lv1_net_remove_multicast_address failed %d\n", + status); + /* set broadcast address */ + status = lv1_net_add_multicast_address(bus_id(card), dev_id(card), + GELIC_NET_BROADCAST_ADDR, 0); + if (status) + dev_err(ctodev(card), + "lv1_net_add_multicast_address failed, %d\n", + status); + + if (netdev->flags & IFF_ALLMULTI + || netdev->mc_count > GELIC_NET_MC_COUNT_MAX) { /* list max */ + status = lv1_net_add_multicast_address(bus_id(card), + dev_id(card), + 0, 1); + if (status) + dev_err(ctodev(card), + "lv1_net_add_multicast_address failed, %d\n", + status); + return; + } + + /* set multicast address */ + for (mc = netdev->mc_list; mc; mc = mc->next) { + addr = 0; + p = mc->dmi_addr; + for (i = 0; i < ETH_ALEN; i++) { + addr <<= 8; + addr |= *p++; + } + status = lv1_net_add_multicast_address(bus_id(card), + dev_id(card), + addr, 0); + if (status) + dev_err(ctodev(card), + "lv1_net_add_multicast_address failed, %d\n", + status); + } +} + +/** + * gelic_net_enable_rxdmac - enables the receive DMA controller + * @card: card structure + * + * gelic_net_enable_rxdmac enables the DMA controller by setting RX_DMA_EN + * in the GDADMACCNTR register + */ +static inline void gelic_net_enable_rxdmac(struct gelic_net_card *card) +{ + int status; + + status = lv1_net_start_rx_dma(bus_id(card), dev_id(card), + card->rx_chain.tail->bus_addr, 0); + if (status) + dev_info(ctodev(card), + "lv1_net_start_rx_dma failed, status=%d\n", status); +} + +/** + * gelic_net_disable_rxdmac - disables the receive DMA controller + * @card: card structure + * + * gelic_net_disable_rxdmac terminates processing on the DMA controller by + * turing off DMA and issueing a force end + */ +static inline void gelic_net_disable_rxdmac(struct gelic_net_card *card) +{ + int status; + + /* this hvc blocks until the DMA in progress really stopped */ + status = lv1_net_stop_rx_dma(bus_id(card), dev_id(card), 0); + if (status) + dev_err(ctodev(card), + "lv1_net_stop_rx_dma faild, %d\n", status); +} + +/** + * gelic_net_disable_txdmac - disables the transmit DMA controller + * @card: card structure + * + * gelic_net_disable_txdmac terminates processing on the DMA controller by + * turing off DMA and issueing a force end + */ +static inline void gelic_net_disable_txdmac(struct gelic_net_card *card) +{ + int status; + + /* this hvc blocks until the DMA in progress really stopped */ + status = lv1_net_stop_tx_dma(bus_id(card), dev_id(card), 0); + if (status) + dev_err(ctodev(card), + "lv1_net_stop_tx_dma faild, status=%d\n", status); +} + +/** + * gelic_net_stop - called upon ifconfig down + * @netdev: interface device structure + * + * always returns 0 + */ +static int gelic_net_stop(struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + +#ifdef CONFIG_GELIC_WIRELESS + gelicw_down(netdev); +#endif + netif_poll_disable(netdev); + netif_stop_queue(netdev); + + /* turn off DMA, force end */ + gelic_net_disable_rxdmac(card); + gelic_net_disable_txdmac(card); + + gelic_net_set_irq_mask(card, 0); + + /* disconnect event port */ + free_irq(card->netdev->irq, card->netdev); + ps3_sb_event_receive_port_destroy(card->dev, card->netdev->irq); + card->netdev->irq = NO_IRQ; + + netif_carrier_off(netdev); + + /* release chains */ + gelic_net_release_tx_chain(card, 1); + gelic_net_release_rx_chain(card); + + gelic_net_free_chain(card, card->tx_top); + gelic_net_free_chain(card, card->rx_top); + + return 0; +} + +/** + * gelic_net_get_next_tx_descr - returns the next available tx descriptor + * @card: device structure to get descriptor from + * + * returns the address of the next descriptor, or NULL if not available. + */ +static struct gelic_net_descr * +gelic_net_get_next_tx_descr(struct gelic_net_card *card) +{ + if (!card->tx_chain.head) + return NULL; + /* see if we can two consecutive free descrs */ + if (card->tx_chain.tail != card->tx_chain.head->next && + gelic_net_get_descr_status(card->tx_chain.head) == + GELIC_NET_DESCR_NOT_IN_USE && + card->tx_chain.tail != card->tx_chain.head->next->next && + gelic_net_get_descr_status(card->tx_chain.head->next) == + GELIC_NET_DESCR_NOT_IN_USE ) + return card->tx_chain.head; + else + return NULL; + +} + +/** + * gelic_net_set_txdescr_cmdstat - sets the tx descriptor command field + * @descr: descriptor structure to fill out + * @skb: packet to consider + * @middle: middle of frame + * + * fills out the command and status field of the descriptor structure, + * depending on hardware checksum settings. This function assumes a wmb() + * has executed before. + */ +static void gelic_net_set_txdescr_cmdstat(struct gelic_net_descr *descr, + struct sk_buff *skb, int middle) +{ + u32 eofr; + + if (middle) + eofr = 0; + else + eofr = GELIC_NET_DMAC_CMDSTAT_END_FRAME; + + if (skb->ip_summed != CHECKSUM_PARTIAL) + descr->dmac_cmd_status = GELIC_NET_DMAC_CMDSTAT_NOCS | eofr; + else { + /* is packet ip? + * if yes: tcp? udp? */ + if (skb->protocol == htons(ETH_P_IP)) { + if (ip_hdr(skb)->protocol == IPPROTO_TCP) + descr->dmac_cmd_status = + GELIC_NET_DMAC_CMDSTAT_TCPCS | eofr; + else if (ip_hdr(skb)->protocol == IPPROTO_UDP) + descr->dmac_cmd_status = + GELIC_NET_DMAC_CMDSTAT_UDPCS | eofr; + else /* + * the stack should checksum non-tcp and non-udp + * packets on his own: NETIF_F_IP_CSUM + */ + descr->dmac_cmd_status = + GELIC_NET_DMAC_CMDSTAT_NOCS | eofr; + } + } +} + +/** + * gelic_net_prepare_tx_descr_v - get dma address of skb_data + * @card: card structure + * @descr: descriptor structure + * @skb: packet to use + * + * returns 0 on success, <0 on failure. + * + */ +static int gelic_net_prepare_tx_descr_v(struct gelic_net_card *card, + struct gelic_net_descr *descr, + struct sk_buff *skb) +{ + dma_addr_t buf[2]; + unsigned int vlan_len; + + if (skb->len < GELIC_NET_VLAN_POS) + return -EINVAL; + + memcpy(&descr->vlan, skb->data, GELIC_NET_VLAN_POS); + if (card->vlan_index != -1) { + descr->vlan.h_vlan_proto = htons(ETH_P_8021Q); /* vlan 0x8100*/ + descr->vlan.h_vlan_TCI = htons(card->vlan_id[card->vlan_index]); + vlan_len = GELIC_NET_VLAN_POS + VLAN_HLEN; /* VLAN_HLEN=4 */ + } else + vlan_len = GELIC_NET_VLAN_POS; /* no vlan tag */ + + /* first descr */ + buf[0] = dma_map_single(ctodev(card), &descr->vlan, + vlan_len, DMA_TO_DEVICE); + + if (!buf[0]) { + dev_err(ctodev(card), + "dma map 1 failed (%p, %i). Dropping packet\n", + skb->data, vlan_len); + return -ENOMEM; + } + + descr->buf_addr = buf[0]; + descr->buf_size = vlan_len; + descr->skb = skb; /* not used */ + descr->data_status = 0; + gelic_net_set_txdescr_cmdstat(descr, skb, 1); /* not the frame end */ + + /* second descr */ + card->tx_chain.head = card->tx_chain.head->next; + descr->next_descr_addr = descr->next->bus_addr; + descr = descr->next; + if (gelic_net_get_descr_status(descr) != GELIC_NET_DESCR_NOT_IN_USE) + /* XXX will be removed */ + dev_err(ctodev(card), "descr is not free!\n"); + + buf[1] = dma_map_single(ctodev(card), skb->data + GELIC_NET_VLAN_POS, + skb->len - GELIC_NET_VLAN_POS, + DMA_TO_DEVICE); + + if (!buf[1]) { + dev_err(ctodev(card), + "dma map 2 failed (%p, %i). Dropping packet\n", + skb->data + GELIC_NET_VLAN_POS, + skb->len - GELIC_NET_VLAN_POS); + dma_unmap_single(ctodev(card), buf[0], vlan_len, + DMA_TO_DEVICE); + return -ENOMEM; + } + + descr->buf_addr = buf[1]; + descr->buf_size = skb->len - GELIC_NET_VLAN_POS; + descr->skb = skb; + descr->data_status = 0; + descr->next_descr_addr = 0; /* terminate hw descr */ + gelic_net_set_txdescr_cmdstat(descr, skb, 0); + + return 0; +} + +/** + * gelic_net_kick_txdma - enables TX DMA processing + * @card: card structure + * @descr: descriptor address to enable TX processing at + * + */ +static int gelic_net_kick_txdma(struct gelic_net_card *card, + struct gelic_net_descr *descr) +{ + int status = -ENXIO; + int count = 10; + + if (card->tx_dma_progress) + return 0; + + if (gelic_net_get_descr_status(descr) == GELIC_NET_DESCR_CARDOWNED) { + card->tx_dma_progress = 1; + /* sometimes we need retry here */ + while (count--) { + status = lv1_net_start_tx_dma(bus_id(card), + dev_id(card), + descr->bus_addr, 0); + if (!status) + break; + } + if (!count) + dev_info(ctodev(card), "lv1_net_start_txdma failed," \ + "status=%d %#lx\n", + status, card->irq_status); + } + return status; +} + +/** + * gelic_net_xmit - transmits a frame over the device + * @skb: packet to send out + * @netdev: interface device structure + * + * returns 0 on success, <0 on failure + */ +static int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + struct gelic_net_descr *descr = NULL; + int result; + unsigned long flags; + + spin_lock_irqsave(&card->tx_dma_lock, flags); + + gelic_net_release_tx_chain(card, 0); + if (!skb) + goto kick; + descr = gelic_net_get_next_tx_descr(card); + if (!descr) { + netif_stop_queue(netdev); + spin_unlock_irqrestore(&card->tx_dma_lock, flags); + return NETDEV_TX_BUSY; + } + result = gelic_net_prepare_tx_descr_v(card, descr, skb); + + if (result) + goto error; + + card->tx_chain.head = card->tx_chain.head->next; + + if (descr->prev) + descr->prev->next_descr_addr = descr->bus_addr; +kick: + /* + * as hardware descriptor is modified in the above lines, + * ensure that the hardware sees it + */ + wmb(); + if (gelic_net_kick_txdma(card, card->tx_chain.tail)) + goto error; + + netdev->trans_start = jiffies; + spin_unlock_irqrestore(&card->tx_dma_lock, flags); + return NETDEV_TX_OK; + +error: + card->netdev_stats.tx_dropped++; + spin_unlock_irqrestore(&card->tx_dma_lock, flags); + return NETDEV_TX_LOCKED; +} + +/** + * gelic_net_pass_skb_up - takes an skb from a descriptor and passes it on + * @descr: descriptor to process + * @card: card structure + * + * iommu-unmaps the skb, fills out skb structure and passes the data to the + * stack. The descriptor state is not changed. + */ +static void gelic_net_pass_skb_up(struct gelic_net_descr *descr, + struct gelic_net_card *card) +{ + struct sk_buff *skb; + struct net_device *netdev; + u32 data_status, data_error; + + data_status = descr->data_status; + data_error = descr->data_error; + netdev = card->netdev; + /* unmap skb buffer */ + skb = descr->skb; + dma_unmap_single(ctodev(card), descr->buf_addr, GELIC_NET_MAX_MTU, + DMA_FROM_DEVICE); + + skb_put(skb, descr->valid_size? descr->valid_size : descr->result_size); + if (!descr->valid_size) + dev_info(ctodev(card), "buffer full %x %x %x\n", + descr->result_size, descr->buf_size, + descr->dmac_cmd_status); + + descr->skb = NULL; + /* + * the card put 2 bytes vlan tag in front + * of the ethernet frame + */ + skb_pull(skb, 2); + skb->protocol = eth_type_trans(skb, netdev); + + /* checksum offload */ + if (card->rx_csum) { + if ((data_status & GELIC_NET_DATA_STATUS_CHK_MASK) && + (!(data_error & GELIC_NET_DATA_ERROR_CHK_MASK))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; + } else + skb->ip_summed = CHECKSUM_NONE; + + /* update netdevice statistics */ + card->netdev_stats.rx_packets++; + card->netdev_stats.rx_bytes += skb->len; + + /* pass skb up to stack */ + netif_receive_skb(skb); +} + +/** + * gelic_net_decode_one_descr - processes an rx descriptor + * @card: card structure + * + * returns 1 if a packet has been sent to the stack, otherwise 0 + * + * processes an rx descriptor by iommu-unmapping the data buffer and passing + * the packet up to the stack + */ +static int gelic_net_decode_one_descr(struct gelic_net_card *card) +{ + enum gelic_net_descr_status status; + struct gelic_net_descr_chain *chain = &card->rx_chain; + struct gelic_net_descr *descr = chain->tail; + int dmac_chain_ended; + + status = gelic_net_get_descr_status(descr); + /* is this descriptor terminated with next_descr == NULL? */ + dmac_chain_ended = + descr->dmac_cmd_status & GELIC_NET_DMAC_CMDSTAT_RXDCEIS; + + if (status == GELIC_NET_DESCR_CARDOWNED) + return 0; + + if (status == GELIC_NET_DESCR_NOT_IN_USE) { + dev_dbg(ctodev(card), "dormant descr? %p\n", descr); + return 0; + } + + if ((status == GELIC_NET_DESCR_RESPONSE_ERROR) || + (status == GELIC_NET_DESCR_PROTECTION_ERROR) || + (status == GELIC_NET_DESCR_FORCE_END)) { + dev_info(ctodev(card), "dropping RX descriptor with state %x\n", + status); + card->netdev_stats.rx_dropped++; + goto refill; + } + + if ((status != GELIC_NET_DESCR_COMPLETE) && + (status != GELIC_NET_DESCR_FRAME_END)) { + dev_dbg(ctodev(card), "RX descriptor with state %x\n", + status); + goto refill; + } + + /* ok, we've got a packet in descr */ + gelic_net_pass_skb_up(descr, card); /* 1: skb_up sccess */ + +refill: + descr->next_descr_addr = 0; /* unlink the descr */ + + /* change the descriptor state: */ + gelic_net_set_descr_status(descr, GELIC_NET_DESCR_NOT_IN_USE); + + /* refill one desc + * FIXME: this can fail, but for now, just leave this + * descriptor without skb + */ + gelic_net_prepare_rx_descr(card, descr); + chain->head = descr; + chain->tail = descr->next; + descr->prev->next_descr_addr = descr->bus_addr; + + if (dmac_chain_ended) { + gelic_net_enable_rxdmac(card); + dev_dbg(ctodev(card), "reenable rx dma\n"); + } + + return 1; +} + +/** + * gelic_net_poll - NAPI poll function called by the stack to return packets + * @netdev: interface device structure + * @budget: number of packets we can pass to the stack at most + * + * returns 0 if no more packets available to the driver/stack. Returns 1, + * if the quota is exceeded, but the driver has still packets. + * + */ +static int gelic_net_poll(struct net_device *netdev, int *budget) +{ + struct gelic_net_card *card = netdev_priv(netdev); + int packets_to_do, packets_done = 0; + int no_more_packets = 0; + + packets_to_do = min(*budget, netdev->quota); + + while (packets_to_do) { + if (gelic_net_decode_one_descr(card)) { + packets_done++; + packets_to_do--; + } else { + /* no more packets for the stack */ + no_more_packets = 1; + break; + } + } + netdev->quota -= packets_done; + *budget -= packets_done; + if (no_more_packets) { + netif_rx_complete(netdev); + gelic_net_rx_irq_on(card); + return 0; + } else + return 1; +} + +/** + * gelic_net_get_stats - get interface statistics + * @netdev: interface device structure + * + * returns the interface statistics residing in the gelic_net_card struct + */ +static struct net_device_stats *gelic_net_get_stats(struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + + return &card->netdev_stats; +} + +/** + * gelic_net_change_mtu - changes the MTU of an interface + * @netdev: interface device structure + * @new_mtu: new MTU value + * + * returns 0 on success, <0 on failure + */ +static int gelic_net_change_mtu(struct net_device *netdev, int new_mtu) +{ + /* no need to re-alloc skbs or so -- the max mtu is about 2.3k + * and mtu is outbound only anyway */ + if ((new_mtu < GELIC_NET_MIN_MTU) || + (new_mtu > GELIC_NET_MAX_MTU)) { + return -EINVAL; + } + netdev->mtu = new_mtu; + return 0; +} + +/** + * gelic_net_interrupt - event handler for gelic_net + */ +static irqreturn_t gelic_net_interrupt(int irq, void *ptr) +{ + unsigned long flags; + struct net_device *netdev = ptr; + struct gelic_net_card *card = netdev_priv(netdev); + u64 status; + + status = card->irq_status; + + if (!status) + return IRQ_NONE; + + if (status & GELIC_NET_RXINT) { + gelic_net_rx_irq_off(card); + netif_rx_schedule(netdev); + } + + if (status & GELIC_NET_TXINT) { + spin_lock_irqsave(&card->tx_dma_lock, flags); + card->tx_dma_progress = 0; + spin_unlock_irqrestore(&card->tx_dma_lock, flags); + /* start pending DMA */ + gelic_net_xmit(NULL, netdev); + } +#ifdef CONFIG_GELIC_WIRELESS + gelicw_interrupt(netdev, status); +#endif + return IRQ_HANDLED; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/** + * gelic_net_poll_controller - artificial interrupt for netconsole etc. + * @netdev: interface device structure + * + * see Documentation/networking/netconsole.txt + */ +static void gelic_net_poll_controller(struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + + gelic_net_set_irq_mask(card, 0); + gelic_net_interrupt(netdev->irq, netdev); + gelic_net_set_irq_mask(card, card->ghiintmask); +} +#endif /* CONFIG_NET_POLL_CONTROLLER */ + +/** + * gelic_net_open_device - open device and map dma region + * @card: card structure + */ +static int gelic_net_open_device(struct gelic_net_card *card) +{ + int result; + + result = ps3_sb_event_receive_port_setup(card->dev, PS3_BINDING_CPU_ANY, + &card->netdev->irq); + + if (result) { + dev_info(ctodev(card), + "%s:%d: gelic_net_open_device failed (%d)\n", + __func__, __LINE__, result); + result = -EPERM; + goto fail_alloc_irq; + } + + result = request_irq(card->netdev->irq, gelic_net_interrupt, + IRQF_DISABLED, "gelic network", card->netdev); + + if (result) { + dev_info(ctodev(card), "%s:%d: request_irq failed (%d)\n", + __func__, __LINE__, result); + goto fail_request_irq; + } + + return 0; + +fail_request_irq: + ps3_sb_event_receive_port_destroy(card->dev, card->netdev->irq); + card->netdev->irq = NO_IRQ; +fail_alloc_irq: + return result; +} + + +/** + * gelic_net_open - called upon ifonfig up + * @netdev: interface device structure + * + * returns 0 on success, <0 on failure + * + * gelic_net_open allocates all the descriptors and memory needed for + * operation, sets up multicast list and enables interrupts + */ +static int gelic_net_open(struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + + dev_dbg(ctodev(card), " -> %s:%d\n", __func__, __LINE__); + + gelic_net_open_device(card); + + if (gelic_net_init_chain(card, &card->tx_chain, + card->descr, GELIC_NET_TX_DESCRIPTORS)) + goto alloc_tx_failed; + if (gelic_net_init_chain(card, &card->rx_chain, + card->descr + GELIC_NET_RX_DESCRIPTORS, + GELIC_NET_RX_DESCRIPTORS)) + goto alloc_rx_failed; + + /* head of chain */ + card->tx_top = card->tx_chain.head; + card->rx_top = card->rx_chain.head; + dev_dbg(ctodev(card), "descr rx %p, tx %p, size %#lx, num %#x\n", + card->rx_top, card->tx_top, sizeof(struct gelic_net_descr), + GELIC_NET_RX_DESCRIPTORS); + /* allocate rx skbs */ + if (gelic_net_alloc_rx_skbs(card)) + goto alloc_skbs_failed; + + card->tx_dma_progress = 0; + card->ghiintmask = GELIC_NET_RXINT | GELIC_NET_TXINT; +#ifdef CONFIG_GELIC_WIRELESS + card->ghiintmask |= GELICW_DEVICE_CMD_COMP | GELICW_DEVICE_EVENT_RECV; +#endif + gelic_net_set_irq_mask(card, card->ghiintmask); + gelic_net_enable_rxdmac(card); + + netif_start_queue(netdev); + netif_carrier_on(netdev); + netif_poll_enable(netdev); +#ifdef CONFIG_GELIC_WIRELESS + gelicw_up(netdev); +#endif + + return 0; + +alloc_skbs_failed: + gelic_net_free_chain(card, card->rx_top); +alloc_rx_failed: + gelic_net_free_chain(card, card->tx_top); +alloc_tx_failed: + return -ENOMEM; +} + +#ifdef GELIC_NET_ETHTOOL +static void gelic_net_get_drvinfo (struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + strncpy(info->driver, DRV_NAME, sizeof(info->driver) - 1); + strncpy(info->version, DRV_VERSION, sizeof(info->version) - 1); +} + +static int gelic_net_get_settings(struct net_device *netdev, + struct ethtool_cmd *cmd) +{ + struct gelic_net_card *card = netdev_priv(netdev); + int status; + u64 v1, v2; + int speed, duplex; + + speed = duplex = -1; + status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_NET_GET_ETH_PORT_STATUS, GELIC_NET_PORT, 0, 0, + &v1, &v2); + if (status) { + /* link down */ + } else { + if (v1 & GELIC_NET_FULL_DUPLEX) { + duplex = DUPLEX_FULL; + } else { + duplex = DUPLEX_HALF; + } + + if (v1 & GELIC_NET_SPEED_10 ) { + speed = SPEED_10; + } else if (v1 & GELIC_NET_SPEED_100) { + speed = SPEED_100; + } else if (v1 & GELIC_NET_SPEED_1000) { + speed = SPEED_1000; + } + } + cmd->supported = SUPPORTED_TP | SUPPORTED_Autoneg | + SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full; + cmd->advertising = cmd->supported; + cmd->speed = speed; + cmd->duplex = duplex; + cmd->autoneg = AUTONEG_ENABLE; /* always enabled */ + cmd->port = PORT_TP; + + return 0; +} + +static u32 gelic_net_get_link(struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + int status; + u64 v1, v2; + int link; + + status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_NET_GET_ETH_PORT_STATUS, GELIC_NET_PORT, 0, 0, + &v1, &v2); + if (status) + return 0; /* link down */ + + if (v1 & GELIC_NET_LINK_UP) + link = 1; + else + link = 0; +#ifdef CONFIG_GELIC_WIRELESS + /* (v1 & GELIC_NET_LINK_UP) is always 0 in wireless mode */ + if (gelicw_is_associated(netdev)) { + link = 1; + } +#endif + return link; +} + +static int gelic_net_nway_reset(struct net_device *netdev) +{ + if (netif_running(netdev)) { + gelic_net_stop(netdev); + gelic_net_open(netdev); + } + return 0; +} + +static u32 gelic_net_get_tx_csum(struct net_device *netdev) +{ + return (netdev->features & NETIF_F_IP_CSUM) != 0; +} + +static int gelic_net_set_tx_csum(struct net_device *netdev, u32 data) +{ + if (data) + netdev->features |= NETIF_F_IP_CSUM; + else + netdev->features &= ~NETIF_F_IP_CSUM; + + return 0; +} + +static u32 gelic_net_get_rx_csum(struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + + return card->rx_csum; +} + +static int gelic_net_set_rx_csum(struct net_device *netdev, u32 data) +{ + struct gelic_net_card *card = netdev_priv(netdev); + + card->rx_csum = data; + return 0; +} + +static struct ethtool_ops gelic_net_ethtool_ops = { + .get_drvinfo = gelic_net_get_drvinfo, + .get_settings = gelic_net_get_settings, + .get_link = gelic_net_get_link, + .nway_reset = gelic_net_nway_reset, + .get_tx_csum = gelic_net_get_tx_csum, + .set_tx_csum = gelic_net_set_tx_csum, + .get_rx_csum = gelic_net_get_rx_csum, + .set_rx_csum = gelic_net_set_rx_csum, +}; +#endif + +/** + * gelic_net_tx_timeout_task - task scheduled by the watchdog timeout + * function (to be called not under interrupt status) + * @work: work is context of tx timout task + * + * called as task when tx hangs, resets interface (if interface is up) + */ +static void gelic_net_tx_timeout_task(struct work_struct *work) +{ + struct gelic_net_card *card = + container_of(work, struct gelic_net_card, tx_timeout_task); + struct net_device *netdev = card->netdev; + + dev_info(ctodev(card), "%s:Timed out. Restarting... \n", __func__); + + if (!(netdev->flags & IFF_UP)) + goto out; + + netif_device_detach(netdev); + gelic_net_stop(netdev); + + gelic_net_open(netdev); + netif_device_attach(netdev); + +out: + atomic_dec(&card->tx_timeout_task_counter); +} + +/** + * gelic_net_tx_timeout - called when the tx timeout watchdog kicks in. + * @netdev: interface device structure + * + * called, if tx hangs. Schedules a task that resets the interface + */ +static void gelic_net_tx_timeout(struct net_device *netdev) +{ + struct gelic_net_card *card; + + card = netdev_priv(netdev); + atomic_inc(&card->tx_timeout_task_counter); + if (netdev->flags & IFF_UP) + schedule_work(&card->tx_timeout_task); + else + atomic_dec(&card->tx_timeout_task_counter); +} + +/** + * gelic_net_setup_netdev_ops - initialization of net_device operations + * @netdev: net_device structure + * + * fills out function pointers in the net_device structure + */ +static void gelic_net_setup_netdev_ops(struct net_device *netdev) +{ + netdev->open = &gelic_net_open; + netdev->stop = &gelic_net_stop; + netdev->hard_start_xmit = &gelic_net_xmit; + netdev->get_stats = &gelic_net_get_stats; + netdev->set_multicast_list = &gelic_net_set_multi; + netdev->change_mtu = &gelic_net_change_mtu; + /* tx watchdog */ + netdev->tx_timeout = &gelic_net_tx_timeout; + netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT; + /* NAPI */ + netdev->poll = &gelic_net_poll; + netdev->weight = GELIC_NET_NAPI_WEIGHT; +#ifdef GELIC_NET_ETHTOOL + netdev->ethtool_ops = &gelic_net_ethtool_ops; +#endif +} + +/** + * gelic_net_setup_netdev - initialization of net_device + * @card: card structure + * + * Returns 0 on success or <0 on failure + * + * gelic_net_setup_netdev initializes the net_device structure + **/ +static int gelic_net_setup_netdev(struct gelic_net_card *card) +{ + struct net_device *netdev = card->netdev; + struct sockaddr addr; + unsigned int i; + int status; + u64 v1, v2; + + SET_MODULE_OWNER(netdev); + SET_NETDEV_DEV(netdev, &card->dev->core); + spin_lock_init(&card->tx_dma_lock); + + card->rx_csum = GELIC_NET_RX_CSUM_DEFAULT; + + gelic_net_setup_netdev_ops(netdev); + + netdev->features = NETIF_F_IP_CSUM; + + status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_NET_GET_MAC_ADDRESS, + 0, 0, 0, &v1, &v2); + if (status || !is_valid_ether_addr((u8 *)&v1)) { + dev_info(ctodev(card), + "%s:lv1_net_control GET_MAC_ADDR failed %d\n", + __func__, status); + return -EINVAL; + } + v1 <<= 16; + memcpy(addr.sa_data, &v1, ETH_ALEN); + memcpy(netdev->dev_addr, addr.sa_data, ETH_ALEN); + dev_info(ctodev(card), "MAC addr %02x:%02x:%02x:%02x:%02x:%02x\n", + netdev->dev_addr[0], netdev->dev_addr[1], + netdev->dev_addr[2], netdev->dev_addr[3], + netdev->dev_addr[4], netdev->dev_addr[5]); + + card->vlan_index = -1; /* no vlan */ + for (i = 0; i < GELIC_NET_VLAN_MAX; i++) { + status = lv1_net_control(bus_id(card), dev_id(card), + GELIC_NET_GET_VLAN_ID, + i + 1, /* index; one based */ + 0, 0, &v1, &v2); + if (status == GELIC_NET_VLAN_NO_ENTRY) { + dev_dbg(ctodev(card), + "GELIC_VLAN_ID no entry:%d, VLAN disabled\n", + status); + card->vlan_id[i] = 0; + } else if (status) { + dev_dbg(ctodev(card), + "%s:GELIC_NET_VLAN_ID faild, status=%d\n", + __func__, status); + card->vlan_id[i] = 0; + } else { + card->vlan_id[i] = (u32)v1; + dev_dbg(ctodev(card), "vlan_id:%d, %lx\n", i, v1); + } + } + if (card->vlan_id[GELIC_NET_VLAN_WIRED - 1]) + card->vlan_index = GELIC_NET_VLAN_WIRED - 1; +#ifdef CONFIG_GELIC_WIRELESS + card->w.card = card; + /* init wireless extension */ + /* No wireless vlan_index:-1 */ + gelicw_setup_netdev(netdev, card->vlan_index); +#endif + status = register_netdev(netdev); + if (status) { + dev_err(ctodev(card), "%s:Couldn't register net_device: %d\n", + __func__, status); + return status; + } + + return 0; +} + +/** + * gelic_net_alloc_card - allocates net_device and card structure + * + * returns the card structure or NULL in case of errors + * + * the card and net_device structures are linked to each other + */ +static struct gelic_net_card *gelic_net_alloc_card(void) +{ + struct net_device *netdev; + struct gelic_net_card *card; + size_t alloc_size; + + alloc_size = sizeof (*card) + + sizeof (struct gelic_net_descr) * GELIC_NET_RX_DESCRIPTORS + + sizeof (struct gelic_net_descr) * GELIC_NET_TX_DESCRIPTORS; + /* + * we assume private data is allocated 32 bytes (or more) aligned + * so that gelic_net_descr should be 32 bytes aligned. + * Current alloc_etherdev() does do it because NETDEV_ALIGN + * is 32. + * check this assumption here. + */ + BUILD_BUG_ON(NETDEV_ALIGN < 32); + BUILD_BUG_ON(offsetof(struct gelic_net_card, irq_status) % 8); + BUILD_BUG_ON(offsetof(struct gelic_net_card, descr) % 32); + + netdev = alloc_etherdev(alloc_size); + if (!netdev) + return NULL; + + card = netdev_priv(netdev); + card->netdev = netdev; + INIT_WORK(&card->tx_timeout_task, gelic_net_tx_timeout_task); + init_waitqueue_head(&card->waitq); + atomic_set(&card->tx_timeout_task_counter, 0); + + return card; +} + +/** + * ps3_gelic_driver_probe - add a device to the control of this driver + */ +static int ps3_gelic_driver_probe (struct ps3_system_bus_device *dev) +{ + struct gelic_net_card *card = gelic_net_alloc_card(); + int result; + + if (!card) { + dev_info(&dev->core, "gelic_net_alloc_card failed\n"); + result = -ENOMEM; + goto fail_alloc_card; + } + + ps3_system_bus_set_driver_data(dev, card); + card->dev = dev; + + result = ps3_open_hv_device(dev); + + if (result) { + dev_dbg(&dev->core, "ps3_open_hv_device failed\n"); + goto fail_open; + } + + result = ps3_dma_region_create(dev->d_region); + + if (result) { + dev_dbg(&dev->core, "ps3_dma_region_create failed(%d)\n", + result); + BUG_ON("check region type"); + goto fail_dma_region; + } + + result = lv1_net_set_interrupt_status_indicator(bus_id(card), + dev_id(card), + ps3_mm_phys_to_lpar(__pa(&card->irq_status)), + 0); + + if (result) { + dev_dbg(&dev->core, + "lv1_net_set_interrupt_status_indicator failed: %s\n", + ps3_result(result)); + result = -EIO; + goto fail_status_indicator; + } + + result = gelic_net_setup_netdev(card); + + if (result) { + dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: " + "(%d)\n", __func__, __LINE__, result); + goto fail_setup_netdev; + } + + return 0; + +fail_setup_netdev: + lv1_net_set_interrupt_status_indicator(bus_id(card), + bus_id(card), + 0 , 0); +fail_status_indicator: + ps3_dma_region_free(dev->d_region); +fail_dma_region: + ps3_close_hv_device(dev); +fail_open: + ps3_system_bus_set_driver_data(dev, NULL); + free_netdev(card->netdev); +fail_alloc_card: + return result; +} + +/** + * ps3_gelic_driver_remove - remove a device from the control of this driver + */ + +static int ps3_gelic_driver_remove (struct ps3_system_bus_device *dev) +{ + struct gelic_net_card *card = ps3_system_bus_get_driver_data(dev); + +#ifdef CONFIG_GELIC_WIRELESS + gelicw_remove(card->netdev); +#endif + wait_event(card->waitq, + atomic_read(&card->tx_timeout_task_counter) == 0); + + lv1_net_set_interrupt_status_indicator(bus_id(card), dev_id(card), + 0 , 0); + + unregister_netdev(card->netdev); + free_netdev(card->netdev); + + ps3_system_bus_set_driver_data(dev, NULL); + + ps3_dma_region_free(dev->d_region); + + ps3_close_hv_device(dev); + + return 0; +} + +static struct ps3_system_bus_driver ps3_gelic_driver = { + .match_id = PS3_MATCH_ID_GELIC, + .probe = ps3_gelic_driver_probe, + .remove = ps3_gelic_driver_remove, + .shutdown = ps3_gelic_driver_remove, + .core.name = "ps3_gelic_driver", + .core.owner = THIS_MODULE, +}; + +static int __init ps3_gelic_driver_init (void) +{ + return firmware_has_feature(FW_FEATURE_PS3_LV1) + ? ps3_system_bus_driver_register(&ps3_gelic_driver) + : -ENODEV; +} + +static void __exit ps3_gelic_driver_exit (void) +{ + ps3_system_bus_driver_unregister(&ps3_gelic_driver); +} + +module_init (ps3_gelic_driver_init); +module_exit (ps3_gelic_driver_exit); + +MODULE_ALIAS(PS3_MODULE_ALIAS_GELIC); + Index: linux-2.6/drivers/net/ps3_gelic_net.h =================================================================== --- /dev/null +++ linux-2.6/drivers/net/ps3_gelic_net.h @@ -0,0 +1,465 @@ +/* + * PS3 Platfom gelic network driver. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2006, 2007 Sony Corporation. + * + * This file is based on: spider_net.h + * + * (C) Copyright IBM Corp. 2005 + * + * Authors : Utz Bacher + * Jens Osterkamp + * + * 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, 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 _GELIC_NET_H +#define _GELIC_NET_H + +#include +#include + +#define GELIC_NET_DRV_NAME "Gelic Network Driver" +#define GELIC_NET_DRV_VERSION "1.0" + +#define GELIC_NET_ETHTOOL /* use ethtool */ + +/* ioctl */ +#define GELIC_NET_GET_MODE (SIOCDEVPRIVATE + 0) +#define GELIC_NET_SET_MODE (SIOCDEVPRIVATE + 1) + +/* descriptors */ +#define GELIC_NET_RX_DESCRIPTORS 128 /* num of descriptors */ +#define GELIC_NET_TX_DESCRIPTORS 128 /* num of descriptors */ + +#define GELIC_NET_MAX_MTU 2308 +#define GELIC_NET_MIN_MTU 64 +#define GELIC_NET_RXBUF_ALIGN 128 +#define GELIC_NET_RX_CSUM_DEFAULT 1 /* hw chksum */ +#define GELIC_NET_WATCHDOG_TIMEOUT 5*HZ +#define GELIC_NET_NAPI_WEIGHT (GELIC_NET_RX_DESCRIPTORS) +#define GELIC_NET_BROADCAST_ADDR 0xffffffffffffL +#define GELIC_NET_VLAN_POS (VLAN_ETH_ALEN * 2) +#define GELIC_NET_VLAN_MAX 4 +#define GELIC_NET_MC_COUNT_MAX 32 /* multicast address list */ + +enum gelic_net_int0_status { + GELIC_NET_GDTDCEINT = 24, + GELIC_NET_GRFANMINT = 28, +}; + +/* GHIINT1STS bits */ +enum gelic_net_int1_status { + GELIC_NET_GDADCEINT = 14, +}; + +/* interrupt mask */ +#define GELIC_NET_TXINT (1L << (GELIC_NET_GDTDCEINT + 32)) + +#define GELIC_NET_RXINT0 (1L << (GELIC_NET_GRFANMINT + 32)) +#define GELIC_NET_RXINT1 (1L << GELIC_NET_GDADCEINT) +#define GELIC_NET_RXINT (GELIC_NET_RXINT0 | GELIC_NET_RXINT1) + + /* RX descriptor data_status bits */ +#define GELIC_NET_RXDMADU 0x80000000 /* destination MAC addr unknown */ +#define GELIC_NET_RXLSTFBF 0x40000000 /* last frame buffer */ +#define GELIC_NET_RXIPCHK 0x20000000 /* IP checksum performed */ +#define GELIC_NET_RXTCPCHK 0x10000000 /* TCP/UDP checksup performed */ +#define GELIC_NET_RXIPSPKT 0x08000000 /* IPsec packet */ +#define GELIC_NET_RXIPSAHPRT 0x04000000 /* IPsec AH protocol performed */ +#define GELIC_NET_RXIPSESPPRT 0x02000000 /* IPsec ESP protocol performed */ +#define GELIC_NET_RXSESPAH 0x01000000 /* + * IPsec ESP protocol auth + * performed + */ + +#define GELIC_NET_RXWTPKT 0x00C00000 /* + * wakeup trigger packet + * 01: Magic Packet (TM) + * 10: ARP packet + * 11: Multicast MAC addr + */ +#define GELIC_NET_RXVLNPKT 0x00200000 /* VLAN packet */ +/* bit 20..16 reserved */ +#define GELIC_NET_RXRECNUM 0x0000ff00 /* reception receipt number */ +/* bit 7..0 reserved */ + +#define GELIC_NET_TXDESC_TAIL 0 +#define GELIC_NET_DATA_STATUS_CHK_MASK (GELIC_NET_RXIPCHK | GELIC_NET_RXTCPCHK) + +/* RX descriptor data_error bits */ +/* bit 31 reserved */ +#define GELIC_NET_RXALNERR 0x40000000 /* alignement error 10/100M */ +#define GELIC_NET_RXOVERERR 0x20000000 /* oversize error */ +#define GELIC_NET_RXRNTERR 0x10000000 /* Runt error */ +#define GELIC_NET_RXIPCHKERR 0x08000000 /* IP checksum error */ +#define GELIC_NET_RXTCPCHKERR 0x04000000 /* TCP/UDP checksum error */ +#define GELIC_NET_RXUMCHSP 0x02000000 /* unmatched sp on sp */ +#define GELIC_NET_RXUMCHSPI 0x01000000 /* unmatched SPI on SAD */ +#define GELIC_NET_RXUMCHSAD 0x00800000 /* unmatched SAD */ +#define GELIC_NET_RXIPSAHERR 0x00400000 /* auth error on AH protocol + * processing */ +#define GELIC_NET_RXIPSESPAHERR 0x00200000 /* auth error on ESP protocol + * processing */ +#define GELIC_NET_RXDRPPKT 0x00100000 /* drop packet */ +#define GELIC_NET_RXIPFMTERR 0x00080000 /* IP packet format error */ +/* bit 18 reserved */ +#define GELIC_NET_RXDATAERR 0x00020000 /* IP packet format error */ +#define GELIC_NET_RXCALERR 0x00010000 /* cariier extension length + * error */ +#define GELIC_NET_RXCREXERR 0x00008000 /* carrier extention error */ +#define GELIC_NET_RXMLTCST 0x00004000 /* multicast address frame */ +/* bit 13..0 reserved */ +#define GELIC_NET_DATA_ERROR_CHK_MASK \ + (GELIC_NET_RXIPCHKERR | GELIC_NET_RXTCPCHKERR) + + +/* tx descriptor command and status */ +#define GELIC_NET_DMAC_CMDSTAT_NOCS 0xa0080000 /* middle of frame */ +#define GELIC_NET_DMAC_CMDSTAT_TCPCS 0xa00a0000 +#define GELIC_NET_DMAC_CMDSTAT_UDPCS 0xa00b0000 +#define GELIC_NET_DMAC_CMDSTAT_END_FRAME 0x00040000 /* end of frame */ + +#define GELIC_NET_DMAC_CMDSTAT_RXDCEIS 0x00000002 /* descriptor chain end + * interrupt status */ + +#define GELIC_NET_DMAC_CMDSTAT_CHAIN_END 0x00000002 /* RXDCEIS:DMA stopped */ +#define GELIC_NET_DMAC_CMDSTAT_NOT_IN_USE 0xb0000000 +#define GELIC_NET_DESCR_IND_PROC_SHIFT 28 +#define GELIC_NET_DESCR_IND_PROC_MASKO 0x0fffffff + + +enum gelic_net_descr_status { + GELIC_NET_DESCR_COMPLETE = 0x00, /* used in rx and tx */ + GELIC_NET_DESCR_RESPONSE_ERROR = 0x01, /* used in rx and tx */ + GELIC_NET_DESCR_PROTECTION_ERROR = 0x02, /* used in rx and tx */ + GELIC_NET_DESCR_FRAME_END = 0x04, /* used in rx */ + GELIC_NET_DESCR_FORCE_END = 0x05, /* used in rx and tx */ + GELIC_NET_DESCR_CARDOWNED = 0x0a, /* used in rx and tx */ + GELIC_NET_DESCR_NOT_IN_USE /* any other value */ +}; +/* for lv1_net_control */ +#define GELIC_NET_GET_MAC_ADDRESS 0x0000000000000001 +#define GELIC_NET_GET_ETH_PORT_STATUS 0x0000000000000002 +#define GELIC_NET_SET_NEGOTIATION_MODE 0x0000000000000003 +#define GELIC_NET_GET_VLAN_ID 0x0000000000000004 + +#define GELIC_NET_LINK_UP 0x0000000000000001 +#define GELIC_NET_FULL_DUPLEX 0x0000000000000002 +#define GELIC_NET_AUTO_NEG 0x0000000000000004 +#define GELIC_NET_SPEED_10 0x0000000000000010 +#define GELIC_NET_SPEED_100 0x0000000000000020 +#define GELIC_NET_SPEED_1000 0x0000000000000040 + +#define GELIC_NET_VLAN_ALL 0x0000000000000001 +#define GELIC_NET_VLAN_WIRED 0x0000000000000002 +#define GELIC_NET_VLAN_WIRELESS 0x0000000000000003 +#define GELIC_NET_VLAN_PSP 0x0000000000000004 +#define GELIC_NET_VLAN_PORT0 0x0000000000000010 +#define GELIC_NET_VLAN_PORT1 0x0000000000000011 +#define GELIC_NET_VLAN_PORT2 0x0000000000000012 +#define GELIC_NET_VLAN_DAEMON_CLIENT_BSS 0x0000000000000013 +#define GELIC_NET_VLAN_LIBERO_CLIENT_BSS 0x0000000000000014 +#define GELIC_NET_VLAN_NO_ENTRY -6 + +#define GELIC_NET_PORT 2 /* for port status */ + +/* wireless */ +#define GELICW_WIRELESS_NOT_EXIST 0 +#define GELICW_WIRELESS_SUPPORTED 1 +#define GELICW_WIRELESS_ON 2 +#define GELICW_WIRELESS_SHUTDOWN 3 +/* state */ +#define GELICW_STATE_DOWN 0 +#define GELICW_STATE_UP 1 +#define GELICW_STATE_SCANNING 2 +#define GELICW_STATE_SCAN_DONE 3 +#define GELICW_STATE_ASSOCIATED 4 + +/* cmd_send_flg */ +#define GELICW_CMD_SEND_NONE 0x00 +#define GELICW_CMD_SEND_COMMON 0x01 +#define GELICW_CMD_SEND_ENCODE 0x02 +#define GELICW_CMD_SEND_SCAN 0x04 +#define GELICW_CMD_SEND_ALL (GELICW_CMD_SEND_COMMON \ + | GELICW_CMD_SEND_ENCODE \ + | GELICW_CMD_SEND_SCAN) + +#define GELICW_SCAN_INTERVAL (HZ) + +#ifdef DEBUG +#define CH_INFO_FAIL 0x0600 /* debug */ +#else +#define CH_INFO_FAIL 0 +#endif + + +/* net_control command */ +#define GELICW_SET_PORT 3 /* control Ether port */ +#define GELICW_GET_INFO 6 /* get supported channels */ +#define GELICW_SET_CMD 9 /* set configuration */ +#define GELICW_GET_RES 10 /* get command response */ +#define GELICW_GET_EVENT 11 /* get event from device */ +/* net_control command data buffer */ +#define GELICW_DATA_BUF_SIZE 0x1000 + +/* GELICW_SET_CMD params */ +#define GELICW_CMD_START 1 +#define GELICW_CMD_STOP 2 +#define GELICW_CMD_SCAN 3 +#define GELICW_CMD_GET_SCAN 4 +#define GELICW_CMD_SET_CONFIG 5 +#define GELICW_CMD_GET_CONFIG 6 +#define GELICW_CMD_SET_WEP 7 +#define GELICW_CMD_GET_WEP 8 +#define GELICW_CMD_SET_WPA 9 +#define GELICW_CMD_GET_WPA 10 +#define GELICW_CMD_GET_RSSI 11 + +/* GELICW_SET_PORT params */ +#define GELICW_ETHER_PORT 2 +#define GELICW_PORT_DOWN 0 /* Ether port off */ +#define GELICW_PORT_UP 4 /* Ether port on (auto neg) */ + +/* interrupt status bit */ +#define GELICW_DEVICE_CMD_COMP (1UL << 31) +#define GELICW_DEVICE_EVENT_RECV (1UL << 30) + +/* GELICW_GET_EVENT ID */ +#define GELICW_EVENT_UNKNOWN 0x00 +#define GELICW_EVENT_DEVICE_READY 0x01 +#define GELICW_EVENT_SCAN_COMPLETED 0x02 +#define GELICW_EVENT_DEAUTH 0x04 +#define GELICW_EVENT_BEACON_LOST 0x08 +#define GELICW_EVENT_CONNECTED 0x10 +#define GELICW_EVENT_WPA_CONNECTED 0x20 +#define GELICW_EVENT_WPA_ERROR 0x40 +#define GELICW_EVENT_NO_ENTRY (-6) + +#define MAX_IW_PRIV_SIZE 32 + +/* structure of data buffer for lv1_net_contol */ +/* wep_config: sec */ +#define GELICW_WEP_SEC_NONE 0 +#define GELICW_WEP_SEC_40BIT 1 +#define GELICW_WEP_SEC_104BIT 2 +struct wep_config { + u16 sec; + u8 key[4][16]; +} __attribute__ ((packed)); + +/* wpa_config: sec */ +#define GELICW_WPA_SEC_NONE 0 +#define GELICW_WPA_SEC_TKIP 1 +#define GELICW_WPA_SEC_AES 2 +/* wpa_config: psk_type */ +#define GELICW_PSK_PASSPHRASE 0 +#define GELICW_PSK_64HEX 1 +struct wpa_config { + u16 sec; + u16 psk_type; + u8 psk_material[64]; /* key */ +} __attribute__ ((packed)); + +/* common_config: bss_type */ +#define GELICW_BSS_INFRA 0 +#define GELICW_BSS_ADHOC 1 +/* common_config: auth_method */ +#define GELICW_AUTH_OPEN 0 +#define GELICW_AUTH_SHARED 1 +/* common_config: op_mode */ +#define GELICW_OP_MODE_11BG 0 +#define GELICW_OP_MODE_11B 1 +#define GELICW_OP_MODE_11G 2 +struct common_config { + u16 scan_index; /* index of scan_desc list */ + u16 bss_type; + u16 auth_method; + u16 op_mode; +} __attribute__ ((packed)); + +/* scan_descriptor: security */ +#define GELICW_SEC_TYPE_NONE 0x0000 +#define GELICW_SEC_TYPE_WEP 0x0100 +#define GELICW_SEC_TYPE_WEP40 0x0101 +#define GELICW_SEC_TYPE_WEP104 0x0102 +#define GELICW_SEC_TYPE_TKIP 0x0201 +#define GELICW_SEC_TYPE_AES 0x0202 +#define GELICW_SEC_TYPE_WEP_MASK 0xFF00 +struct scan_desc { + u16 size; + u16 rssi; + u16 channel; + u16 beacon_period; + u16 capability; + u16 security; + u64 bssid; + u8 essid[32]; + u8 rate[16]; + u8 ext_rate[16]; + u32 reserved1; + u32 reserved2; + u32 reserved3; + u32 reserved4; +} __attribute__ ((packed)); + +/* rssi_descriptor */ +struct rssi_desc { + u16 rssi; /* max rssi = 100 */ +} __attribute__ ((packed)); + + +struct gelicw_bss { + u8 bssid[ETH_ALEN]; + u8 channel; + u8 mode; + u8 essid_len; + u8 essid[IW_ESSID_MAX_SIZE + 1]; /* null terminated for debug msg */ + + u16 capability; + u16 beacon_interval; + + u8 rates_len; + u8 rates[MAX_RATES_LENGTH]; + u8 rates_ex_len; + u8 rates_ex[MAX_RATES_EX_LENGTH]; + u8 rssi; + + /* scan results have sec_info instead of rsn_ie or wpa_ie */ + u16 sec_info; +}; + +/* max station count of station list which hvc returns */ +#define MAX_SCAN_BSS (16) + +struct gelic_wireless { + struct gelic_net_card *card; + struct completion cmd_done, rssi_done; + struct work_struct work_event, work_start_done; + struct delayed_work work_rssi, work_scan_all, work_scan_essid; + struct delayed_work work_common, work_encode; + struct delayed_work work_start, work_stop, work_roam; + wait_queue_head_t waitq_cmd, waitq_scan; + + u64 cmd_tag, cmd_id; + u8 cmd_send_flg; + + struct iw_public_data wireless_data; + void *data_buf; /* data buffer for lv1_net_control */ + + u8 wireless; /* wireless support */ + u8 state; + u8 scan_all; /* essid scan or all scan */ + u8 essid_search; /* essid background scan */ + u8 is_assoc; + + u16 ch_info; /* supoprted channels */ + u8 wireless_mode; /* 11b/g */ + u8 channel; /* current ch */ + u8 iw_mode; /* INFRA or Ad-hoc */ + u8 rssi; + u8 essid_len; + u8 essid[IW_ESSID_MAX_SIZE + 1]; /* null terminated for debug msg */ + u8 nick[IW_ESSID_MAX_SIZE + 1]; + u8 bssid[ETH_ALEN]; + + u8 key_index; + u8 key[WEP_KEYS][IW_ENCODING_TOKEN_MAX]; /* 4 * 64byte */ + u8 key_len[WEP_KEYS]; + u8 key_alg; /* key algorithm */ + u8 auth_mode; /* authenticaton mode */ + + u8 bss_index; /* current bss in bss_list */ + u8 num_bss_list; + u8 bss_key_alg; /* key alg of bss */ + u8 wap_bssid[ETH_ALEN]; + unsigned long last_scan; /* last scan time */ + struct gelicw_bss current_bss; + struct gelicw_bss bss_list[MAX_SCAN_BSS]; +}; + +/* size of hardware part of gelic descriptor */ +#define GELIC_NET_DESCR_SIZE (32) +struct gelic_net_descr { + /* as defined by the hardware */ + u32 buf_addr; + u32 buf_size; + u32 next_descr_addr; + u32 dmac_cmd_status; + u32 result_size; + u32 valid_size; /* all zeroes for tx */ + u32 data_status; + u32 data_error; /* all zeroes for tx */ + + /* used in the driver */ + struct sk_buff *skb; + dma_addr_t bus_addr; + struct gelic_net_descr *next; + struct gelic_net_descr *prev; + struct vlan_ethhdr vlan; +} __attribute__((aligned(32))); + +struct gelic_net_descr_chain { + /* we walk from tail to head */ + struct gelic_net_descr *head; + struct gelic_net_descr *tail; +}; + +struct gelic_net_card { + struct net_device *netdev; + /* + * hypervisor requires irq_status should be + * 8 bytes aligned, but u64 member is + * always disposed in that manner + */ + u64 irq_status; + u64 ghiintmask; + + struct ps3_system_bus_device *dev; + u32 vlan_id[GELIC_NET_VLAN_MAX]; + int vlan_index; + + struct gelic_net_descr_chain tx_chain; + struct gelic_net_descr_chain rx_chain; + /* gurad dmac descriptor chain*/ + spinlock_t chain_lock; + + struct net_device_stats netdev_stats; + int rx_csum; + /* guard tx_dma_progress */ + spinlock_t tx_dma_lock; + int tx_dma_progress; + + struct work_struct tx_timeout_task; + atomic_t tx_timeout_task_counter; + wait_queue_head_t waitq; + + struct gelic_net_descr *tx_top, *rx_top; +#ifdef CONFIG_GELIC_WIRELESS + struct gelic_wireless w; +#endif + struct gelic_net_descr descr[0]; +}; + + +extern unsigned long p_to_lp(long pa); +extern int gelicw_setup_netdev(struct net_device *netdev, int wi); +extern void gelicw_up(struct net_device *netdev); +extern int gelicw_down(struct net_device *netdev); +extern void gelicw_remove(struct net_device *netdev); +extern void gelicw_interrupt(struct net_device *netdev, u64 status); +extern int gelicw_is_associated(struct net_device *netdev); + +#endif /* _GELIC_NET_H */ Index: linux-2.6/drivers/net/ps3_gelic_wireless.c =================================================================== --- /dev/null +++ linux-2.6/drivers/net/ps3_gelic_wireless.c @@ -0,0 +1,2125 @@ +/* + * gelic_wireless.c: wireless extension for gelic_net + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2006, 2007 Sony Corporation + * + * 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; version 2 of the License. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include + +#include "ps3_gelic_net.h" + +static struct iw_handler_def gelicw_handler_def; + +/* + * Data tables + */ +static const u32 freq_list[] = { + 2412, 2417, 2422, 2427, 2432, + 2437, 2442, 2447, 2452, 2457, + 2462, 2467, 2472, 2484 }; + +static const u32 bitrate_list[] = { + 1000000, 2000000, 5500000, 11000000, /* 11b */ + 6000000, 9000000, 12000000, 18000000, + 24000000, 36000000, 48000000, 54000000 +}; +#define GELICW_NUM_11B_BITRATES 4 /* 802.11b: 1 ~ 11 Mb/s */ + +static inline struct device * ntodev(struct net_device *netdev) +{ + return &((struct gelic_net_card *)netdev_priv(netdev))->dev->core; +} + +static inline struct device * wtodev(struct gelic_wireless *w) +{ + return &w->card->dev->core; +} +static inline struct gelic_wireless *gelicw_priv(struct net_device *netdev) +{ + struct gelic_net_card *card = netdev_priv(netdev); + return &card->w; +} +static inline unsigned int bus_id(struct gelic_wireless *w) +{ + return w->card->dev->bus_id; +} +static inline unsigned int dev_id(struct gelic_wireless *w) +{ + return w->card->dev->dev_id; +} + +/* control wired or wireless */ +static void gelicw_vlan_mode(struct net_device *netdev, int mode) +{ + struct gelic_net_card *card = netdev_priv(netdev); + + if ((mode < GELIC_NET_VLAN_WIRED) || + (mode > GELIC_NET_VLAN_WIRELESS)) + return; + + card->vlan_index = mode - 1; +} + +/* wireless_send_event */ +static void notify_assoc_event(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + union iwreq_data wrqu; + + cancel_delayed_work(&w->work_scan_all); + cancel_delayed_work(&w->work_scan_essid); + + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + if (w->state < GELICW_STATE_ASSOCIATED) { + dev_dbg(ntodev(netdev), "notify disassociated\n"); + w->is_assoc = 0; + memset(w->bssid, 0, ETH_ALEN); + } else { + dev_dbg(ntodev(netdev), "notify associated\n"); + w->channel = w->current_bss.channel; + w->is_assoc = 1; + memcpy(w->bssid, w->current_bss.bssid, ETH_ALEN); + } + memcpy(wrqu.ap_addr.sa_data, w->bssid, ETH_ALEN); + wireless_send_event(netdev, SIOCGIWAP, &wrqu, NULL); +} + +/* association/disassociation */ +static void gelicw_assoc(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + if (w->state == GELICW_STATE_ASSOCIATED) + return; + schedule_delayed_work(&w->work_start, 0); +} + +static int gelicw_disassoc(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + memset(w->bssid, 0, ETH_ALEN); /* clear current bssid */ + if (w->state < GELICW_STATE_ASSOCIATED) + return 0; + + schedule_delayed_work(&w->work_stop, 0); + w->state = GELICW_STATE_SCAN_DONE; + /* notify disassociation */ + notify_assoc_event(netdev); + + return 0; +} + +static void gelicw_reassoc(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + if (w->cmd_send_flg != GELICW_CMD_SEND_ALL) + return; + + if (!gelicw_disassoc(netdev)) + gelicw_assoc(netdev); +} + + +/* + * lv1_net_control command + */ +/* control Ether port */ +static int gelicw_cmd_set_port(struct net_device *netdev, int mode) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + u64 tag, val; + int status; + + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_SET_PORT, GELICW_ETHER_PORT, mode, 0, + &tag, &val); + if (status) + dev_dbg(ntodev(netdev), "GELICW_SET_PORT failed:%d\n", status); + return status; +} + +/* check support channels */ +static int gelicw_cmd_get_ch_info(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + u64 ch_info, val; + int status; + + if (w->state < GELICW_STATE_UP) + return -EIO; + + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_GET_INFO, 0, 0 , 0, + &ch_info, &val); + if (status) { + dev_dbg(ntodev(netdev), "GELICW_GET_INFO failed:%d\n", status); + w->ch_info = CH_INFO_FAIL; + } else { + dev_dbg(ntodev(netdev), "ch_info:%lx val:%lx\n", ch_info, val); + w->ch_info = ch_info >> 48; /* MSB 16bit shows supported channnels */ + } + return status; +} + + +/* + * lv1_net_control GELICW_SET_CMD command + * queued using schedule_work() + */ +/* association */ +static void gelicw_cmd_start(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + u64 val; + int status; + + if (w->state < GELICW_STATE_SCAN_DONE) + return; + + dev_dbg(ntodev(netdev), "GELICW_CMD_START\n"); + w->cmd_id = GELICW_CMD_START; + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_SET_CMD, w->cmd_id, 0, 0, + &w->cmd_tag, &val); + if (status) { + w->cmd_tag = 0; + dev_dbg(ntodev(netdev), "GELICW_CMD_START failed:%d\n", status); + } +} + +/* association done */ +static void gelicw_cmd_start_done(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + u64 res, val; + int status; + + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_GET_RES, w->cmd_tag, 0, 0, + &res, &val); + w->cmd_tag = 0; + wake_up_interruptible(&w->waitq_cmd); + + if (status || res) + dev_dbg(ntodev(netdev), "GELICW_CMD_START res:%d,%ld\n", status, res); +} + +/* disassociation */ +static void gelicw_cmd_stop(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + u64 res, val; + int status; + + if (w->state < GELICW_STATE_SCAN_DONE) + return; + + dev_dbg(ntodev(netdev), "GELICW_CMD_STOP\n"); + init_completion(&w->cmd_done); + w->cmd_id = GELICW_CMD_STOP; + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_SET_CMD, w->cmd_id, 0, 0, + &w->cmd_tag, &val); + if (status) { + w->cmd_tag = 0; + dev_dbg(ntodev(netdev), "GELICW_CMD_STOP failed:%d\n", status); + return; + } + wait_for_completion_interruptible(&w->cmd_done); + + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_GET_RES, w->cmd_tag, 0, 0, + &res, &val); + w->cmd_tag = 0; + if (status || res) { + dev_dbg(ntodev(netdev), "GELICW_CMD_STOP res:%d,%ld\n", status, res); + return; + } +} + +/* get rssi */ +static void gelicw_cmd_rssi(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + u64 lpar, res, val; + int status; + struct rssi_desc *rssi; + + if (w->state < GELICW_STATE_ASSOCIATED) { + w->rssi = 0; + complete(&w->rssi_done); + return; + } + + lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf)); + init_completion(&w->cmd_done); + w->cmd_id = GELICW_CMD_GET_RSSI; + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_SET_CMD, w->cmd_id, 0, 0, + &w->cmd_tag, &val); + if (status) { + w->cmd_tag = 0; + w->rssi = 0; + dev_dbg(ntodev(netdev), "GELICW_CMD_GET_RSSI failed:%d\n", status); + } else { + wait_for_completion_interruptible(&w->cmd_done); + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_GET_RES, w->cmd_tag, lpar, sizeof(*rssi), + &res, &val); + w->cmd_tag = 0; + if (status || res) { + w->rssi = 0; + dev_dbg(ntodev(netdev), "GELICW_CMD_GET_RSSI res:%d,%ld\n", status, res); + } + rssi = w->data_buf; + w->rssi = rssi->rssi; + dev_dbg(ntodev(netdev), "GELICW_CMD_GET_RSSI:%d\n", rssi->rssi); + } + + complete(&w->rssi_done); +} + +/* set common configuration */ +static int gelicw_cmd_common(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + u64 lpar, res, val; + int status; + struct common_config *config; + + if (w->state < GELICW_STATE_SCAN_DONE) + return -EIO; + + lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf)); + config = w->data_buf; + config->scan_index = w->bss_index; + config->bss_type = (w->iw_mode == IW_MODE_ADHOC) ? + GELICW_BSS_ADHOC : GELICW_BSS_INFRA; + config->auth_method = (w->auth_mode == IW_AUTH_ALG_SHARED_KEY) ? + GELICW_AUTH_SHARED : GELICW_AUTH_OPEN; + switch (w->wireless_mode) { + case IEEE_B: + config->op_mode = GELICW_OP_MODE_11B; + break; + case IEEE_G: + config->op_mode = GELICW_OP_MODE_11G; + break; + case IEEE_B | IEEE_G: + default: + /* default 11bg mode */ + config->op_mode = GELICW_OP_MODE_11BG; + break; + } + + dev_dbg(ntodev(netdev), "GELICW_CMD_SET_CONFIG: index:%d type:%d auth:%d mode:%d\n",\ + config->scan_index, config->bss_type,\ + config->auth_method,config->op_mode); + init_completion(&w->cmd_done); + w->cmd_id = GELICW_CMD_SET_CONFIG; + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_SET_CMD, w->cmd_id, + lpar, sizeof(struct common_config), + &w->cmd_tag, &val); + if (status) { + w->cmd_tag = 0; + dev_dbg(ntodev(netdev), "GELICW_CMD_SET_CONFIG failed:%d\n", status); + return status; + } + wait_for_completion_interruptible(&w->cmd_done); + + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_GET_RES, w->cmd_tag, 0, 0, + &res, &val); + w->cmd_tag = 0; + if (status || res) { + dev_dbg(ntodev(netdev), "GELICW_CMD_SET_CONFIG res:%d,%ld\n", status, res); + return -EFAULT; + } + + w->cmd_send_flg |= GELICW_CMD_SEND_COMMON; + + return 0; +} + +#define h2i(c) (isdigit(c) ? c - '0' : toupper(c) - 'A' + 10) +/* send WEP/WPA configuration */ +static int gelicw_cmd_encode(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + u64 res, val, lpar; + int status; + struct wep_config *config; + struct wpa_config *wpa_config; + + u8 *key, key_len; + + if (w->state < GELICW_STATE_SCAN_DONE) + return -EIO; + + lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf)); + + if (w->key_alg == IW_ENCODE_ALG_WEP || + w->key_alg == IW_ENCODE_ALG_NONE) { + /* WEP */ + config = w->data_buf; + memset(config, 0, sizeof(struct wep_config)); + + /* check key len */ + key_len = w->key_len[w->key_index]; + key = w->key[w->key_index]; + if (w->key_alg == IW_ENCODE_ALG_NONE) + config->sec = GELICW_WEP_SEC_NONE; + else + config->sec = (key_len == 5) ? GELICW_WEP_SEC_40BIT : + GELICW_WEP_SEC_104BIT; + /* copy key */ + memcpy(config->key[w->key_index], key, key_len); + + /* send wep config */ + dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WEP\n"); + init_completion(&w->cmd_done); + w->cmd_id = GELICW_CMD_SET_WEP; + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_SET_CMD, w->cmd_id, + lpar, sizeof(struct wep_config), + &w->cmd_tag, &val); + if (status) { + w->cmd_tag = 0; + dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WEP failed:%d\n", status); + goto err; + } + wait_for_completion_interruptible(&w->cmd_done); + + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_GET_RES, w->cmd_tag, 0, 0, + &res, &val); + w->cmd_tag = 0; + if (status || res) { + dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WEP res:%d,%ld\n", status, res); + status = -EFAULT; + goto err; + } + } else { + /* WPA */ + wpa_config = w->data_buf; + memset(wpa_config, 0, sizeof(struct wpa_config)); + + switch (w->key_alg) { + case IW_ENCODE_ALG_TKIP: + wpa_config->sec = GELICW_WPA_SEC_TKIP; + break; + default: + case IW_ENCODE_ALG_CCMP: + wpa_config->sec = GELICW_WPA_SEC_AES; + break; + } + /* check key len */ + key = w->key[w->key_index]; + key_len = w->key_len[w->key_index]; + + if (key_len > 64) key_len = 64; + if (key_len != 64) { + /* if key_len isn't 64byte ,it should be passphrase */ + /* pass phrase */ + memcpy(wpa_config->psk_material, key, key_len); + wpa_config->psk_type = GELICW_PSK_PASSPHRASE; + } else { + int i; + /* 64 hex */ + for (i = 0; i < 32; i++) + wpa_config->psk_material[i] = + h2i(key[2 * i]) * 16 + + h2i(key[2 * i + 1]); + wpa_config->psk_type = GELICW_PSK_64HEX; + } + + /* send wpa config */ + dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WPA:type:%d\n", wpa_config->psk_type); + init_completion(&w->cmd_done); + w->cmd_id = GELICW_CMD_SET_WPA; + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_SET_CMD, w->cmd_id, + lpar, sizeof(struct wpa_config), + &w->cmd_tag, &val); + if (status) { + w->cmd_tag = 0; + dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WPA failed:%d\n", status); + goto err; + } + wait_for_completion_interruptible(&w->cmd_done); + + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_GET_RES, w->cmd_tag, 0, 0, &res, &val); + w->cmd_tag = 0; + if (status || res) { + dev_dbg(ntodev(netdev), "GELICW_CMD_SET_WPA res:%d,%ld\n", status, res); + status = -EFAULT; + goto err; + } + } + + w->cmd_send_flg |= GELICW_CMD_SEND_ENCODE; + /* (re)associate */ + gelicw_reassoc(netdev); + + return 0; +err: + gelicw_disassoc(netdev); + return status; +} + +static int gelicw_is_ap_11b(struct gelicw_bss *list) +{ + if (list->rates_len + list->rates_ex_len == GELICW_NUM_11B_BITRATES) + return 1; + else + return 0; +} + +/* get scan results */ +static int gelicw_cmd_get_scan(struct gelic_wireless *w) +{ + u64 lpar, res, val; + int status; + struct scan_desc *desc; + int i, j; + u8 *p; + + + /* get scan */ + dev_dbg(wtodev(w), "GELICW_CMD_GET_SCAN\n"); + init_completion(&w->cmd_done); + w->cmd_id = GELICW_CMD_GET_SCAN; + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_SET_CMD, w->cmd_id, 0, 0, + &w->cmd_tag, &val); + if (status) { + w->cmd_tag = 0; + dev_dbg(wtodev(w), "GELICW_CMD_GET_SCAN failed:%d\n", status); + return status; + } + wait_for_completion_interruptible(&w->cmd_done); + + lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf)); + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_GET_RES, w->cmd_tag, lpar, PAGE_SIZE, + &res, &val); + w->cmd_tag = 0; + if (status || res) { + dev_dbg(wtodev(w), "GELICW_CMD_GET_SCAN res:%d,%ld\n", + status, res); + return -EFAULT; + } + + desc = w->data_buf; + for (i = 0; + i < val / sizeof(struct scan_desc) && i < MAX_SCAN_BSS; + i++) { + struct gelicw_bss *bss = &w->bss_list[i]; + + bss->rates_len = 0; + for (j = 0; j < MAX_RATES_LENGTH; j++) + if (desc[i].rate[j]) + bss->rates[bss->rates_len++] = desc[i].rate[j]; + bss->rates_ex_len = 0; + for (j = 0; j < MAX_RATES_EX_LENGTH; j++) + if (desc[i].ext_rate[j]) + bss->rates_ex[bss->rates_ex_len++] + = desc[i].ext_rate[j]; + + if (desc[i].capability & 0x3) { + if (desc[i].capability & 0x1) + bss->mode = IW_MODE_INFRA; + else + bss->mode = IW_MODE_ADHOC; + } + bss->channel = desc[i].channel; + bss->essid_len = strnlen(desc[i].essid, IW_ESSID_MAX_SIZE); + bss->rssi = (u8)desc[i].rssi; + bss->capability = desc[i].capability; + bss->beacon_interval = desc[i].beacon_period; + memset(bss->essid, 0, sizeof(bss->essid)); + memcpy(bss->essid, desc[i].essid, bss->essid_len); + p = (u8 *)&desc[i].bssid; + memcpy(bss->bssid, &p[2], ETH_ALEN);/* bssid:64bit in desc */ + bss->sec_info = desc[i].security; + } + w->num_bss_list = i; + + if (w->num_bss_list) + return 0; /* ap found */ + else + return -1; /* no ap found */ +} + +/* search bssid in bss list */ +static int gelicw_search_bss_list(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + int i; + int check_bss = 0, check_11g = 0, found_bss, found_11g; + + if (!w->num_bss_list) + return -1; /* no bss list */ + + if (memcmp(off, w->wap_bssid, ETH_ALEN)) + check_bss = 1; + + /* wireless op_mode seems not working with CMD_SET_CONFIG */ + if (w->wireless_mode == IEEE_G) + check_11g = 1; + + if (!check_bss && !check_11g) + return 0; /* no check bssid, wmode */ + + for (i = 0; i < w->num_bss_list; i++) { + found_bss = found_11g = 1; + if (check_bss && + memcmp(w->bss_list[i].bssid, w->wap_bssid, ETH_ALEN)) + found_bss = 0; /* not found */ + + if (check_11g && + gelicw_is_ap_11b(&w->bss_list[i])) + found_11g = 0; /* not found */ + + if (found_bss && found_11g) + break; + } + + if (i == w->num_bss_list) + return -1; /* not found */ + else + return i; +} + +/* scan done */ +static void gelicw_scan_complete(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + int res; + int bss_index; + + /* get scan results */ + res = gelicw_cmd_get_scan(w); + if (w->is_assoc) + w->state = GELICW_STATE_ASSOCIATED; + else + w->state = GELICW_STATE_SCAN_DONE; + + if (res) { + /* No AP found */ + if (!w->scan_all) { + /* no specified AP */ + gelicw_disassoc(netdev); + /* rescan */ + if (w->essid_search && w->essid_len) + schedule_delayed_work(&w->work_scan_essid, + GELICW_SCAN_INTERVAL); + return; + } + } + + if (w->scan_all) { + /* all params should be set again after scan */ + w->cmd_send_flg = 0; + return; + } + + bss_index = gelicw_search_bss_list(netdev); + if (bss_index < 0) { + /* no wap_bssid in bss_list */ + if (w->essid_search && w->essid_len) + schedule_delayed_work(&w->work_scan_essid, + GELICW_SCAN_INTERVAL); + return; + } + w->bss_index = (u8)bss_index; + w->current_bss = w->bss_list[w->bss_index]; + + /* essid search complete */ + w->essid_search = 0; + w->cmd_send_flg |= GELICW_CMD_SEND_SCAN; + + /* (re)connect to AP */ + if (w->is_assoc) { + /* notify disassociation */ + w->state = GELICW_STATE_SCAN_DONE; + notify_assoc_event(netdev); + } + schedule_delayed_work(&w->work_common, 0); + schedule_delayed_work(&w->work_encode, 0); +} + +/* start scan */ +static int gelicw_cmd_set_scan(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + u64 res, val, lpar; + int status; + u8 *p; + + if (w->state < GELICW_STATE_UP) { + w->scan_all = 0; + return -EIO; + } + if (!w->scan_all && !w->essid_len) + return -EINVAL; /* unsupported essid ANY */ + + /* set device wired to wireless when essid is set */ + if (!w->scan_all && w->wireless < GELICW_WIRELESS_ON) { + gelicw_vlan_mode(netdev, GELIC_NET_VLAN_WIRELESS); + gelicw_cmd_set_port(netdev, GELICW_PORT_DOWN); + w->wireless = GELICW_WIRELESS_ON; + } + + p = w->data_buf; + lpar = ps3_mm_phys_to_lpar(__pa(w->data_buf)); + + /* avoid frequent scanning */ + if (!w->essid_search && /* background scan off */ + w->scan_all && + (time_before64(get_jiffies_64(), w->last_scan + 5 * HZ))) + return 0; + + w->bss_key_alg = IW_ENCODE_ALG_NONE; + + init_completion(&w->cmd_done); + w->state = GELICW_STATE_SCANNING; + w->cmd_id = GELICW_CMD_SCAN; + + if (w->scan_all) { + /* scan all ch */ + dev_dbg(ntodev(netdev), "GELICW_CMD_SCAN all\n"); + w->last_scan = get_jiffies_64(); /* last scan time */ + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_SET_CMD, w->cmd_id, 0, 0, + &w->cmd_tag, &val); + } else { + /* scan essid */ + memset(p, 0, 32); + memcpy(p, w->essid, w->essid_len); + dev_dbg(ntodev(netdev), "GELICW_CMD_SCAN essid\n"); + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_SET_CMD, w->cmd_id, lpar, 32, + &w->cmd_tag, &val); + } + + if (status) { + w->cmd_tag = 0; + dev_dbg(ntodev(netdev), "GELICW_CMD_SCAN failed:%d\n", status); + return status; + } + wait_for_completion_interruptible(&w->cmd_done); + + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_GET_RES, w->cmd_tag, 0, 0, + &res, &val); + w->cmd_tag = 0; + if (status || res) { + dev_dbg(ntodev(netdev), "GELICW_CMD_SCAN res:%d,%ld\n", status, res); + return -EFAULT; + } + + return 0; +} + +static void gelicw_send_common_config(struct net_device *netdev, + u8 *cur, u8 mode) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + if (*cur != mode) { + *cur = mode; + if (w->state < GELICW_STATE_SCAN_DONE) + return; + + if (!(w->cmd_send_flg & GELICW_CMD_SEND_SCAN) && + w->essid_len) + /* scan essid and set other params */ + schedule_delayed_work(&w->work_scan_essid, 0); + else { + schedule_delayed_work(&w->work_common, 0); + if (w->cmd_send_flg + & GELICW_CMD_SEND_ENCODE) + /* (re)send encode key */ + schedule_delayed_work(&w->work_encode, 0); + } + } +} + + +/* + * work queue + */ +static void gelicw_work_rssi(struct work_struct *work) +{ + struct gelic_wireless *w = + container_of(work, struct gelic_wireless, work_rssi.work); + struct net_device *netdev = w->card->netdev; + + if (w->cmd_tag) { + schedule_delayed_work(&w->work_rssi, HZ / 5); + return; + } + + gelicw_cmd_rssi(netdev); +} + +static void gelicw_work_scan_all(struct work_struct *work) +{ + struct gelic_wireless *w = + container_of(work, struct gelic_wireless, work_scan_all.work); + struct net_device *netdev = w->card->netdev; + + if (w->cmd_tag || w->state == GELICW_STATE_SCANNING) { + schedule_delayed_work(&w->work_scan_all, HZ / 5); + return; + } + + w->scan_all = 1; + gelicw_cmd_set_scan(netdev); +} + +static void gelicw_work_scan_essid(struct work_struct *work) +{ + struct gelic_wireless *w = + container_of(work, struct gelic_wireless, work_scan_essid.work); + struct net_device *netdev = w->card->netdev; + + if (w->cmd_tag || w->scan_all || w->state == GELICW_STATE_SCANNING) { + schedule_delayed_work(&w->work_scan_essid, HZ / 5); + return; + } + w->bss_index = 0; + w->scan_all = 0; + gelicw_cmd_set_scan(netdev); +} + +static void gelicw_work_common(struct work_struct *work) +{ + struct gelic_wireless *w = + container_of(work, struct gelic_wireless, work_common.work); + struct net_device *netdev = w->card->netdev; + + if (w->cmd_tag) { + schedule_delayed_work(&w->work_common, HZ / 5); + return; + } + gelicw_cmd_common(netdev); +} + +static void gelicw_work_encode(struct work_struct *work) +{ + struct gelic_wireless *w = + container_of(work, struct gelic_wireless, work_encode.work); + struct net_device *netdev = w->card->netdev; + + if (w->cmd_tag) { + schedule_delayed_work(&w->work_encode, HZ / 5); + return; + } + gelicw_cmd_encode(netdev); +} + +static void gelicw_work_start(struct work_struct *work) +{ + struct gelic_wireless *w = + container_of(work, struct gelic_wireless, work_start.work); + struct net_device *netdev = w->card->netdev; + + if (w->cmd_tag) { + schedule_delayed_work(&w->work_start, HZ / 5); + return; + } + gelicw_cmd_start(netdev); +} + +static void gelicw_work_start_done(struct work_struct *work) +{ + struct gelic_wireless *w = + container_of(work, struct gelic_wireless, work_start_done); + struct net_device *netdev = w->card->netdev; + + gelicw_cmd_start_done(netdev); +} + +static void gelicw_work_stop(struct work_struct *work) +{ + struct gelic_wireless *w = + container_of(work, struct gelic_wireless, work_stop.work); + struct net_device *netdev = w->card->netdev; + + if (w->cmd_tag) { + schedule_delayed_work(&w->work_stop, HZ / 5); + return; + } + gelicw_cmd_stop(netdev); +} + +static void gelicw_work_roam(struct work_struct *work) +{ + struct gelic_wireless *w = + container_of(work, struct gelic_wireless, work_roam.work); + struct net_device *netdev = w->card->netdev; + + if (w->cmd_tag || w->scan_all || w->state == GELICW_STATE_SCANNING) { + schedule_delayed_work(&w->work_roam, HZ / 5); + return; + } + gelicw_cmd_stop(netdev); + w->bss_index = 0; + w->scan_all = 0; + gelicw_cmd_set_scan(netdev); +} + +/* + * Event handler + */ +#define GELICW_EVENT_LOOP_MAX 16 +static void gelicw_event(struct work_struct *work) +{ + struct gelic_wireless *w = + container_of(work, struct gelic_wireless, work_event); + struct net_device *netdev = w->card->netdev; + u64 event_type, val; + int i, status; + + for (i = 0; i < GELICW_EVENT_LOOP_MAX; i++) { + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_GET_EVENT, 0, 0 , 0, + &event_type, &val); + if (status == GELICW_EVENT_NO_ENTRY) + /* got all events */ + break; + else if (status){ + dev_dbg(ntodev(netdev), "GELICW_GET_EVENT failed:%d\n", status); + return; + } + switch(event_type) { + case GELICW_EVENT_DEVICE_READY: + dev_dbg(ntodev(netdev), " GELICW_EVENT_DEVICE_READY\n"); + break; + case GELICW_EVENT_SCAN_COMPLETED: + dev_dbg(ntodev(netdev), " GELICW_EVENT_SCAN_COMPLETED\n"); + gelicw_scan_complete(netdev); + break; + case GELICW_EVENT_BEACON_LOST: + dev_dbg(ntodev(netdev), " GELICW_EVENT_BEACON_LOST\n"); + w->state = GELICW_STATE_SCAN_DONE; + notify_assoc_event(netdev); + /* roaming */ + w->essid_search = 1; + schedule_delayed_work(&w->work_roam, 0); + break; + case GELICW_EVENT_CONNECTED: + { + u16 ap_sec; + dev_dbg(ntodev(netdev), " GELICW_EVENT_CONNECTED\n"); + /* this event ocuured with any key_alg */ + ap_sec = w->current_bss.sec_info; + if (w->key_alg == IW_ENCODE_ALG_NONE) { + /* no encryption */ + if (ap_sec == 0) { + w->state = GELICW_STATE_ASSOCIATED; + notify_assoc_event(netdev); + } + } else if (w->key_alg == IW_ENCODE_ALG_WEP){ + if ((ap_sec & GELICW_SEC_TYPE_WEP_MASK) + == GELICW_SEC_TYPE_WEP) { + /* wep */ + w->state = GELICW_STATE_ASSOCIATED; + notify_assoc_event(netdev); + } + } + break; + } + case GELICW_EVENT_WPA_CONNECTED: + dev_dbg(ntodev(netdev), " GELICW_EVENT_WPA_CONNECTED\n"); + w->state = GELICW_STATE_ASSOCIATED; + notify_assoc_event(netdev); + break; + case GELICW_EVENT_WPA_ERROR: + dev_dbg(ntodev(netdev), " GELICW_EVENT_WPA_ERROR\n"); + break; + default: + dev_dbg(ntodev(netdev), " GELICW_EVENT_UNKNOWN\n"); + break; + } + } +} + +static void gelicw_clear_event(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + u64 event_type, val; + int i, status; + + for (i = 0; i < GELICW_EVENT_LOOP_MAX; i++) { + status = lv1_net_control(bus_id(w), dev_id(w), + GELICW_GET_EVENT, 0, 0 , 0, + &event_type, &val); + if (status) + return;/* got all events */ + + switch(event_type) { + case GELICW_EVENT_SCAN_COMPLETED: + w->state = GELICW_STATE_SCAN_DONE; + wake_up_interruptible(&w->waitq_scan); + break; + default: + break; + } + } +} + +/* + * gelic_net support function + */ +static void gelicw_clear_params(struct gelic_wireless *w) +{ + int i; + + /* clear status */ + w->state = GELICW_STATE_DOWN; + w->cmd_send_flg = 0; + w->scan_all = 0; + w->is_assoc = 0; + w->essid_search = 0; + w->cmd_tag = 0; + w->cmd_id = 0; + w->last_scan = 0; + + /* default mode and settings */ + w->essid_len = 0; + w->essid[0] = '\0'; + w->nick[0] = '\0'; + w->iw_mode = IW_MODE_INFRA; + w->auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; + w->wireless_mode = IEEE_B | IEEE_G; + w->bss_index = 0; + memset(w->bssid, 0, ETH_ALEN); + memset(w->wap_bssid, 0, ETH_ALEN); + + /* init key */ + w->key_index = 0; + for (i = 0; i < WEP_KEYS; i++) { + w->key[i][0] = '\0'; + w->key_len[i] = 0; + } + w->key_alg = IW_ENCODE_ALG_NONE; + w->bss_key_alg = IW_ENCODE_ALG_NONE; +} + +int gelicw_setup_netdev(struct net_device *netdev, int wi) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + if (wi < 0) { + /* PS3 low model has no wireless */ + dev_info(ntodev(netdev), "No wireless dvice in this system\n"); + w->wireless = 0; + return 0; + } + /* version check */ + if (ps3_compare_firmware_version(1, 6, 0) < 0) { + dev_info(ntodev(netdev), + "firmware is too old for wireless.\n"); + w->wireless = 0; + return 0; + } + /* we need 4K aligned, 16 units of scan_desc sized */ + BUILD_BUG_ON(PAGE_SIZE < sizeof(struct scan_desc) * MAX_SCAN_BSS); + w->data_buf = (void *)get_zeroed_page(GFP_KERNEL); + if (!w->data_buf) { + w->wireless = 0; + dev_info(ntodev(netdev), "%s:get_page failed\n", __func__); + return -ENOMEM; + } + + w->wireless = GELICW_WIRELESS_SUPPORTED; + + w->ch_info = 0; + w->channel = 0; + netdev->wireless_data = &w->wireless_data; + netdev->wireless_handlers = &gelicw_handler_def; + INIT_WORK(&w->work_event, gelicw_event); + INIT_WORK(&w->work_start_done, gelicw_work_start_done); + INIT_DELAYED_WORK(&w->work_rssi, gelicw_work_rssi); + INIT_DELAYED_WORK(&w->work_scan_all, gelicw_work_scan_all); + INIT_DELAYED_WORK(&w->work_scan_essid, gelicw_work_scan_essid); + INIT_DELAYED_WORK(&w->work_common, gelicw_work_common); + INIT_DELAYED_WORK(&w->work_encode, gelicw_work_encode); + INIT_DELAYED_WORK(&w->work_start, gelicw_work_start); + INIT_DELAYED_WORK(&w->work_stop, gelicw_work_stop); + INIT_DELAYED_WORK(&w->work_roam, gelicw_work_roam); + init_waitqueue_head(&w->waitq_cmd); + init_waitqueue_head(&w->waitq_scan); + + gelicw_clear_params(w); + + return 0; +} + +void gelicw_up(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + if (!w->wireless) + return; + + dev_dbg(ntodev(netdev), "gelicw_up\n"); + if (w->state < GELICW_STATE_UP) + w->state = GELICW_STATE_UP; + + /* start essid scanning */ + if (w->essid_len) + schedule_delayed_work(&w->work_scan_essid, 0); +} + +int gelicw_down(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + if (!w->wireless || w->state == GELICW_STATE_DOWN) + return 0; + + dev_dbg(ntodev(netdev), "gelicw_down\n"); + w->wireless = GELICW_WIRELESS_SHUTDOWN; + flush_scheduled_work(); + + /* check cmd_tag of CMD_START */ + if (w->cmd_id == GELICW_CMD_START) + wait_event_interruptible(w->waitq_cmd, !w->cmd_tag); + /* wait scan done */ + if (w->state == GELICW_STATE_SCANNING) { + wait_event_interruptible(w->waitq_scan, + w->state != GELICW_STATE_SCANNING); + gelicw_cmd_get_scan(w); + } + + gelicw_cmd_stop(netdev); + if (w->is_assoc) { + w->state = GELICW_STATE_DOWN; + notify_assoc_event(netdev); + } + gelicw_clear_params(w); + + /* set device wireless to wired */ + gelicw_vlan_mode(netdev, GELIC_NET_VLAN_WIRED); + gelicw_cmd_set_port(netdev, GELICW_PORT_UP); + w->wireless = GELICW_WIRELESS_SUPPORTED; + + return 0; +} + +void gelicw_remove(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + if (!w->wireless) + return; + + dev_dbg(ntodev(netdev), "gelicw_remove\n"); + gelicw_down(netdev); + w->wireless = 0; + netdev->wireless_handlers = NULL; + free_page((unsigned long)w->data_buf); +} + +void gelicw_interrupt(struct net_device *netdev, u64 status) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + if (!w->wireless) + return; + + if (status & GELICW_DEVICE_CMD_COMP) { + dev_dbg(ntodev(netdev), "GELICW_DEVICE_CMD_COMP\n"); + if (w->cmd_id == GELICW_CMD_START) + schedule_work(&w->work_start_done); + else + complete(&w->cmd_done); + } + if (status & GELICW_DEVICE_EVENT_RECV) { + dev_dbg(ntodev(netdev), "GELICW_DEVICE_EVENT_RECV\n"); + if (w->wireless == GELICW_WIRELESS_SHUTDOWN) + gelicw_clear_event(netdev); + else + schedule_work(&w->work_event); + } +} + +int gelicw_is_associated(struct net_device *netdev) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + if (!w->wireless) + return 0; + + return w->is_assoc; +} + + +/* + * Wireless externsions + */ +static int gelicw_get_name(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + dev_dbg(ntodev(netdev), "wx: get_name\n"); + if (w->state < GELICW_STATE_UP) { + strcpy(wrqu->name, "radio off"); + return 0; + } + + if (w->wireless_mode == IEEE_B || + (w->is_assoc && gelicw_is_ap_11b(&w->current_bss))) + strcpy(wrqu->name, "IEEE 802.11b"); + else { + switch (w->wireless_mode) { + case IEEE_G: + strcpy(wrqu->name, "IEEE 802.11g"); + break; + case IEEE_B | IEEE_G: + default: + strcpy(wrqu->name, "IEEE 802.11bg"); + break; + } + } + + return 0; +} + +static int gelicw_set_freq(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + struct iw_freq *fwrq = &wrqu->freq; + int ch; + + dev_dbg(ntodev(netdev), "wx: set_freq e:%d m:%d\n", fwrq->e, fwrq->m); + if (w->is_assoc || w->state < GELICW_STATE_UP) + return 0; + + /* this setting has no effect for INFRA mode */ + if (fwrq->e == 1) { + u32 f = fwrq->m / 100000; + int i; + for (i = 0; i < ARRAY_SIZE(freq_list); i++) + if (freq_list[i] == f) + break; + if (i == ARRAY_SIZE(freq_list)) + return -EINVAL; + fwrq->m = i + 1; /* ch number */ + fwrq->e = 0; + } + if (fwrq->e > 0) + return -EINVAL; + + ch = fwrq->m; + if (ch < 1) + w->channel = 0; /* auto */ + else if (ch > ARRAY_SIZE(freq_list)) + return -EINVAL; + else { + /* check supported channnel */ + if (!w->ch_info) + gelicw_cmd_get_ch_info(netdev); + if (w->ch_info & (1 << (ch - 1))) + w->channel = ch; + else + return -EINVAL; + } + dev_dbg(ntodev(netdev), " set cnannel: %d\n", w->channel); + + return 0; +} + +static int gelicw_get_freq(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + dev_dbg(ntodev(netdev), "wx: get_freq:%d\n", w->channel); + if (w->channel == 0) + wrqu->freq.m = 0; + else + wrqu->freq.m = freq_list[w->channel - 1] * 100000; + wrqu->freq.e = 1; + + return 0; +} + +static int gelicw_set_mode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + int mode = wrqu->mode; + u8 iw_mode = IW_MODE_INFRA; + + dev_dbg(ntodev(netdev), "wx: set_mode:%x\n",mode); + switch (mode) { + case IW_MODE_ADHOC: + dev_dbg(ntodev(netdev), "IW_MODE_ADHOC\n"); + iw_mode = mode; + return -EOPNOTSUPP; /* adhoc not supported */ + case IW_MODE_INFRA: + default: + dev_dbg(ntodev(netdev), "IW_MODE_INFRA\n"); + iw_mode = IW_MODE_INFRA; + break; + } + + /* send common config */ + gelicw_send_common_config(netdev, &w->iw_mode, iw_mode); + + return 0; +} + +static int gelicw_get_mode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + dev_dbg(ntodev(netdev), "wx: get_mode\n"); + wrqu->mode = w->iw_mode; + + return 0; +} + +static inline int gelicw_qual2level(int qual) +{ + return (qual * 4 - 820)/10; /* FIXME: dummy */ +} + +static int gelicw_get_range(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + struct iw_range *range = (struct iw_range *)extra; + int num_ch, i; + + dev_dbg(ntodev(netdev), "wx: get_range\n"); + wrqu->data.length = sizeof(*range); + memset(range, 0, sizeof(*range)); + + /* wireless extension */ + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 19; + + /* supported bitrates */ + if (w->wireless_mode == IEEE_B) + range->num_bitrates = GELICW_NUM_11B_BITRATES; + else + range->num_bitrates = ARRAY_SIZE(bitrate_list); + range->throughput = bitrate_list[range->num_bitrates -1] / 2; /* half */ + for (i = 0; i < range->num_bitrates; i++) + range->bitrate[i] = bitrate_list[i]; + + range->max_qual.qual = 100; /* relative value */ + range->max_qual.level = 0; + range->avg_qual.qual = 50; + range->avg_qual.level = 0; + range->sensitivity = 0; + + /* encryption capabilities */ + range->encoding_size[0] = 5; /* 40bit WEP */ + range->encoding_size[1] = 13; /* 104bit WEP */ + range->encoding_size[2] = 64; /* WPA-PSK */ + range->num_encoding_sizes = 3; + range->max_encoding_tokens = WEP_KEYS; + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; + + /* freq */ + if (!w->ch_info) + gelicw_cmd_get_ch_info(netdev); /* get supported freq */ + + num_ch = 0; + for (i = 0; i < ARRAY_SIZE(freq_list); i++) + if (w->ch_info & (1 << i)) { + range->freq[num_ch].i = i + 1; + range->freq[num_ch].m = freq_list[i] * 100000; + range->freq[num_ch].e = 1; + if (++num_ch == IW_MAX_FREQUENCIES) + break; + } + + range->num_channels = num_ch; + range->num_frequency = num_ch; + + /* event capabilities */ + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWAP)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + + return 0; +} + +static int gelicw_set_wap(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + static const u8 any[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + dev_dbg(ntodev(netdev), "wx: set_wap\n"); + if (wrqu->ap_addr.sa_family != ARPHRD_ETHER) + return -EINVAL; + + if (!memcmp(any, wrqu->ap_addr.sa_data, ETH_ALEN) || + !memcmp(off, wrqu->ap_addr.sa_data, ETH_ALEN)) { + if (!memcmp(off, w->wap_bssid, ETH_ALEN)) + return 0; /* ap off, no change */ + else { + memset(w->wap_bssid, 0, ETH_ALEN); + /* start scan */ + } + } else if (!memcmp(w->wap_bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) + /* no change */ + return 0; + else if (!memcmp(w->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) { + /* current bss */ + memcpy(w->wap_bssid, wrqu->ap_addr.sa_data, ETH_ALEN); + return 0; + } else + memcpy(w->wap_bssid, wrqu->ap_addr.sa_data, ETH_ALEN); + + /* start scan */ + if (w->essid_len && w->state >= GELICW_STATE_SCAN_DONE) { + gelicw_disassoc(netdev); + /* scan essid */ + cancel_delayed_work(&w->work_scan_all); + cancel_delayed_work(&w->work_scan_essid); + w->essid_search = 1; + schedule_delayed_work(&w->work_scan_essid, 0); + } + + return 0; +} + +static int gelicw_get_wap(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + dev_dbg(ntodev(netdev), "wx: get_wap\n"); + wrqu->ap_addr.sa_family = ARPHRD_ETHER; + memcpy(wrqu->ap_addr.sa_data, w->bssid, ETH_ALEN); + + return 0; +} + +static int gelicw_set_scan(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + dev_dbg(ntodev(netdev), "wx: set_scan\n"); + if (w->state < GELICW_STATE_UP) + return -EIO; + + /* cancel scan */ + cancel_delayed_work(&w->work_scan_all); + cancel_delayed_work(&w->work_scan_essid); + + schedule_delayed_work(&w->work_scan_all, 0); + + return 0; +} + +#define MAX_CUSTOM_LEN 64 +static char *gelicw_translate_scan(struct net_device *netdev, + char *start, char *stop, + struct gelicw_bss *list) +{ + char custom[MAX_CUSTOM_LEN]; + struct iw_event iwe; + int i; + char *p, *current_val; + + /* BSSID */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, list->bssid, ETH_ALEN); + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN); + + /* ESSID */ + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + iwe.u.data.length = list->essid_len; + start = iwe_stream_add_point(start, stop, &iwe, list->essid); + + /* protocol name */ + iwe.cmd = SIOCGIWNAME; + if (gelicw_is_ap_11b(list)) + snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11b"); + else + snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11bg"); + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN); + + /* MODE */ + iwe.cmd = SIOCGIWMODE; + iwe.u.mode = list->mode; + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN); + + /* FREQ */ + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = list->channel; + iwe.u.freq.e = 0; + iwe.u.freq.i = 0; + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN); + + /* ENCODE */ + iwe.cmd = SIOCGIWENCODE; + if (list->capability & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + start = iwe_stream_add_point(start, stop, &iwe, list->essid); + + /* QUAL */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | + IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID; + iwe.u.qual.qual = list->rssi; + iwe.u.qual.level = gelicw_qual2level(list->rssi); + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN); + + /* RATE */ + current_val = start + IW_EV_LCP_LEN; + iwe.cmd = SIOCGIWRATE; + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + for (i = 0; i < list->rates_len; i++) { + iwe.u.bitrate.value = ((list->rates[i] & 0x7f) * 500000); + current_val = iwe_stream_add_value(start, current_val, stop, + &iwe, IW_EV_PARAM_LEN); + } + for (i = 0; i < list->rates_ex_len; i++) { + iwe.u.bitrate.value = ((list->rates_ex[i] & 0x7f) * 500000); + current_val = iwe_stream_add_value(start, current_val, stop, + &iwe, IW_EV_PARAM_LEN); + } + if ((current_val - start) > IW_EV_LCP_LEN) + start = current_val; + + /* Extra */ + /* BEACON */ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + p = custom; + p += snprintf(p, MAX_CUSTOM_LEN, "bcn_int=%d", list->beacon_interval); + iwe.u.data.length = p - custom; + start = iwe_stream_add_point(start, stop, &iwe, custom); + + /* AP security */ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + p = custom; + p += snprintf(p, MAX_CUSTOM_LEN, "ap_sec=%04X", list->sec_info); + iwe.u.data.length = p - custom; + start = iwe_stream_add_point(start, stop, &iwe, custom); + + return start; +} + +static int gelicw_get_scan(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + int i; + char *ev = extra; + char *stop = ev + wrqu->data.length; + + dev_dbg(ntodev(netdev), "wx: get_scan \n"); + switch (w->state) { + case GELICW_STATE_DOWN: + case GELICW_STATE_UP: + return 0; /* no scan results */ + case GELICW_STATE_SCANNING: + return -EAGAIN; /* now scanning */ + case GELICW_STATE_SCAN_DONE: + if (!w->scan_all) /* essid scan */ + return -EAGAIN; + break; + default: + break; + } + + w->scan_all = 0; + for (i = 0; i < w->num_bss_list; i++) + ev = gelicw_translate_scan(netdev, ev, stop, &w->bss_list[i]); + wrqu->data.length = ev - extra; + wrqu->data.flags = 0; + + /* start background scan */ + if (w->essid_search) + schedule_delayed_work(&w->work_scan_essid, + GELICW_SCAN_INTERVAL); + + return 0; +} + +static int gelicw_set_essid(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + u16 length = 0; + + dev_dbg(ntodev(netdev), "wx:set_essid\n"); + /* cancel scan */ + w->essid_search = 0; + cancel_delayed_work(&w->work_scan_all); + cancel_delayed_work(&w->work_scan_essid); + + if (wrqu->essid.flags && wrqu->essid.length) + length = wrqu->essid.length; + + if (length == 0) { + /* essid ANY scan not supported */ + dev_dbg(ntodev(netdev), "ESSID ANY\n"); + w->essid_len = 0; /* clear essid */ + w->essid[0] = '\0'; + return 0; + } else { + /* check essid */ + if (length > IW_ESSID_MAX_SIZE) + return -EINVAL; + if (w->essid_len == length && + !strncmp(w->essid, extra, length)) { + /* same essid */ + if (w->is_assoc) + return 0; + } else { + /* set new essid */ + w->essid_len = length; + memcpy(w->essid, extra, length); + } + } + /* start essid scan */ + w->essid_search = 1; + schedule_delayed_work(&w->work_scan_essid, 0); + + return 0; +} + +static int gelicw_get_essid(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + dev_dbg(ntodev(netdev), "wx:get_essid\n"); + if (w->essid_len) { + memcpy(extra, w->essid, w->essid_len); + wrqu->essid.length = w->essid_len; + wrqu->essid.flags = 1; + } else { + wrqu->essid.length = 0; + wrqu->essid.flags = 0; + } + + return 0; +} + +static int gelicw_set_nick(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + u32 len = wrqu->data.length; + + dev_dbg(ntodev(netdev), "wx:set_nick\n"); + if (len > IW_ESSID_MAX_SIZE) + return -EINVAL; + + memset(w->nick, 0, sizeof(w->nick)); + memcpy(w->nick, extra, len); + + return 0; +} + +static int gelicw_get_nick(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + dev_dbg(ntodev(netdev), "wx:get_nick\n"); + wrqu->data.length = strlen(w->nick); + memcpy(extra, w->nick, wrqu->data.length); + wrqu->data.flags = 1; + + return 0; +} + +static int gelicw_set_rate(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + dev_dbg(ntodev(netdev), "wx:set_rate:%d\n", wrqu->bitrate.value); + if (wrqu->bitrate.value == -1) + return 0; /* auto rate only */ + + return -EOPNOTSUPP; +} + +static int gelicw_get_rate(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + + dev_dbg(ntodev(netdev), "wx:get_rate\n"); + + if (w->wireless_mode == IEEE_B || + (w->is_assoc && gelicw_is_ap_11b(&w->current_bss))) + wrqu->bitrate.value = bitrate_list[GELICW_NUM_11B_BITRATES -1]; + else + wrqu->bitrate.value = bitrate_list[ARRAY_SIZE(bitrate_list) -1]; + + wrqu->bitrate.fixed = 0; + + return 0; +} + +static int gelicw_set_encode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + struct iw_point *enc = &wrqu->encoding; + int i, index, key_index; + + dev_dbg(ntodev(netdev), "wx:set_encode: flags:%x\n", enc->flags ); + index = enc->flags & IW_ENCODE_INDEX; + if (index < 0 || index > WEP_KEYS) + return -EINVAL; + index--; + + if (enc->length > IW_ENCODING_TOKEN_MAX) + return -EINVAL; + + if (index != -1) + w->key_index = index; + key_index = w->key_index; + + if (enc->flags & IW_ENCODE_DISABLED) { + /* disable encryption */ + if (index == -1) { + /* disable all */ + w->key_alg = IW_ENCODE_ALG_NONE; + for (i = 0; i < WEP_KEYS; i++) + w->key_len[i] = 0; + } else + w->key_len[key_index] = 0; + } else if (enc->flags & IW_ENCODE_NOKEY) { + /* key not changed */ + if (w->key_alg == IW_ENCODE_ALG_NONE) + w->key_alg = IW_ENCODE_ALG_WEP; /* default wep */ + } else { + /* enable encryption */ + w->key_len[key_index] = enc->length; + if (w->key_alg == IW_ENCODE_ALG_NONE) + w->key_alg = IW_ENCODE_ALG_WEP; /* default wep */ + memcpy(w->key[key_index], extra, w->key_len[key_index]); + } + dev_dbg(ntodev(netdev), "key %d len:%d alg:%x\n",\ + key_index, w->key_len[key_index], w->key_alg); + + if (w->state >= GELICW_STATE_SCAN_DONE && + w->cmd_send_flg == 0 && w->essid_len) + /* scan essid and set other params */ + schedule_delayed_work(&w->work_scan_essid, 0); + else + schedule_delayed_work(&w->work_encode, 0); + + return 0; +} + +static int gelicw_get_encode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + struct iw_point *enc = &wrqu->encoding; + int index, key_index; + + dev_dbg(ntodev(netdev), "wx:get_encode\n"); + index = enc->flags & IW_ENCODE_INDEX; + if (index < 0 || index > WEP_KEYS) + return -EINVAL; + + index--; + key_index = (index == -1 ? w->key_index : index); + enc->flags = key_index + 1; + + if (w->key_alg == IW_ENCODE_ALG_NONE || !w->key_len[key_index]) { + /* no encryption */ + enc->flags |= IW_ENCODE_DISABLED; + enc->length = 0; + } else { + enc->flags |= IW_ENCODE_NOKEY; + enc->length = w->key_len[key_index]; + memset(extra, 0, w->key_len[key_index]); + } + + return 0; +} + +static int gelicw_set_auth(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + struct iw_param *param = &wrqu->param; + int value = param->value; + int ret = 0; + + dev_dbg(ntodev(netdev), "wx:set_auth:%x\n", param->flags & IW_AUTH_INDEX); + switch(param->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + case IW_AUTH_TKIP_COUNTERMEASURES: + case IW_AUTH_DROP_UNENCRYPTED: + case IW_AUTH_WPA_ENABLED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_ROAMING_CONTROL: + case IW_AUTH_PRIVACY_INVOKED: + /* ignore */ + dev_dbg(ntodev(netdev), "IW_AUTH(%x)\n", param->flags & IW_AUTH_INDEX); + break; + case IW_AUTH_80211_AUTH_ALG: + dev_dbg(ntodev(netdev), "IW_AUTH_80211_AUTH_ALG:\n"); + if (value & IW_AUTH_ALG_SHARED_KEY) + w->auth_mode = IW_AUTH_ALG_SHARED_KEY; + else if (value & IW_AUTH_ALG_OPEN_SYSTEM) + w->auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; + else + ret = -EINVAL; + break; + default: + dev_dbg(ntodev(netdev), "IW_AUTH_UNKNOWN flags:%x\n", param->flags); + ret = -EOPNOTSUPP; + } + + return ret; +} + +static int gelicw_get_auth(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + struct iw_param *param = &wrqu->param; + + dev_dbg(ntodev(netdev), "wx:get_auth\n"); + switch(param->flags & IW_AUTH_INDEX) { + case IW_AUTH_80211_AUTH_ALG: + param->value = w->auth_mode; + break; + case IW_AUTH_WPA_ENABLED: + if ((w->key_alg & IW_ENCODE_ALG_TKIP) || + (w->key_alg & IW_ENCODE_ALG_CCMP)) + param->value = 1; + else + param->value = 0; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int gelicw_set_encodeext(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + struct iw_point *enc = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int i, index, key_index; + + dev_dbg(ntodev(netdev), "wx:set_encodeext\n"); + index = enc->flags & IW_ENCODE_INDEX; + if (index < 0 || index > WEP_KEYS) + return -EINVAL; + + index--; + if (ext->key_len > IW_ENCODING_TOKEN_MAX) + return -EINVAL; + + if (index != -1) + w->key_index = index; + key_index = w->key_index; + + if (enc->flags & IW_ENCODE_DISABLED) { + /* disable encryption */ + if (index == -1) { + /* disable all */ + w->key_alg = IW_ENCODE_ALG_NONE; + for (i = 0; i < WEP_KEYS; i++) + w->key_len[i] = 0; + } else + w->key_len[key_index] = 0; + } else if (enc->flags & IW_ENCODE_NOKEY) + /* key not changed */ + w->key_alg = ext->alg; + else { + w->key_len[key_index] = ext->key_len; + w->key_alg = ext->alg; + if (w->key_alg != IW_ENCODE_ALG_NONE && w->key_len[key_index]) + memcpy(w->key[key_index], ext->key, w->key_len[key_index]); + } + dev_dbg(ntodev(netdev), "key %d len:%d alg:%x\n",\ + key_index, w->key_len[key_index], w->key_alg); + + if (w->state >= GELICW_STATE_SCAN_DONE && + w->cmd_send_flg == 0 && w->essid_len) + /* scan essid and set other params */ + schedule_delayed_work(&w->work_scan_essid, 0); + else + schedule_delayed_work(&w->work_encode, 0); + + return 0; +} + +static int gelicw_get_encodeext(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + struct iw_point *enc = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int index, key_index, key_len; + + dev_dbg(ntodev(netdev), "wx:get_encodeext\n"); + key_len = enc->length - sizeof(*ext); + if (key_len < 0) + return -EINVAL; + + index = enc->flags & IW_ENCODE_INDEX; + if (index < 0 || index > WEP_KEYS) + return -EINVAL; + + index--; + key_index = (index == -1 ? w->key_index : index); + + memset(ext, 0, sizeof(*ext)); + enc->flags = key_index + 1; + + if (w->key_alg == IW_ENCODE_ALG_NONE || !w->key_len[key_index]) { + /* no encryption */ + enc->flags |= IW_ENCODE_DISABLED; + ext->alg = IW_ENCODE_ALG_NONE; + ext->key_len = 0; + } else { + enc->flags |= IW_ENCODE_NOKEY; + ext->alg = w->key_alg; + ext->key_len = w->key_len[key_index]; + } + + return 0; +} + +/* + * wireless stats + */ +static struct iw_statistics *gelicw_get_wireless_stats(struct net_device *netdev) +{ + static struct iw_statistics wstats; + struct gelic_wireless *w = gelicw_priv(netdev); + + dev_dbg(ntodev(netdev), "wx:wireless_stats\n"); + if (w->state < GELICW_STATE_ASSOCIATED) { + wstats.qual.updated = IW_QUAL_QUAL_UPDATED | + IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID; + wstats.qual.qual = 0; + wstats.qual.level = 0; + return &wstats; + } + init_completion(&w->rssi_done); + schedule_delayed_work(&w->work_rssi, 0); + + wait_for_completion_interruptible(&w->rssi_done); + wstats.qual.updated = IW_QUAL_QUAL_UPDATED | + IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID; + wstats.qual.qual = w->rssi; + wstats.qual.level = gelicw_qual2level(w->rssi); + + return &wstats; +} + +/* + * private handler + */ +static int gelicw_priv_set_alg_mode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + int mode = *(int *)extra; + + dev_dbg(ntodev(netdev), "wx:priv_set_alg\n"); + switch (mode) { + case IW_ENCODE_ALG_NONE: + case IW_ENCODE_ALG_WEP: + case IW_ENCODE_ALG_TKIP: + case IW_ENCODE_ALG_CCMP: + break; + default: + return -EINVAL; + } + /* send common config */ + gelicw_send_common_config(netdev, &w->key_alg, (u8)mode); + + return 0; +} + +static int gelicw_priv_get_alg_mode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct gelic_wireless *w = gelicw_priv(netdev); + char *p; + + dev_dbg(ntodev(netdev), "wx:priv_get_alg\n"); + switch (w->key_alg) { + case IW_ENCODE_ALG_NONE: + strncpy(extra, "OFF", MAX_IW_PRIV_SIZE); + break; + case IW_ENCODE_ALG_WEP: + strncpy(extra, "WEP", MAX_IW_PRIV_SIZE); + break; + case IW_ENCODE_ALG_TKIP: + strncpy(extra, "TKIP", MAX_IW_PRIV_SIZE); + break; + case IW_ENCODE_ALG_CCMP: + strncpy(extra, "AES-CCMP", MAX_IW_PRIV_SIZE); + break; + default: + break; + } + p = extra + strlen(extra); + + if (w->key_alg == IW_ENCODE_ALG_TKIP || + w->key_alg == IW_ENCODE_ALG_CCMP) { + if (w->key_len[w->key_index] == 64) /* current key index */ + strncpy(p, " hex", MAX_IW_PRIV_SIZE); + else + strncpy(p, " passphrase", MAX_IW_PRIV_SIZE); + } + wrqu->data.length = strlen(extra); + + return 0; +} + + +/* + * Wireless handlers + */ +static const iw_handler gelicw_handler[] = +{ + [IW_IOCTL_IDX(SIOCGIWNAME)] = gelicw_get_name, + [IW_IOCTL_IDX(SIOCSIWFREQ)] = gelicw_set_freq, + [IW_IOCTL_IDX(SIOCGIWFREQ)] = gelicw_get_freq, + [IW_IOCTL_IDX(SIOCSIWMODE)] = gelicw_set_mode, + [IW_IOCTL_IDX(SIOCGIWMODE)] = gelicw_get_mode, + [IW_IOCTL_IDX(SIOCGIWRANGE)] = gelicw_get_range, + [IW_IOCTL_IDX(SIOCSIWAP)] = gelicw_set_wap, + [IW_IOCTL_IDX(SIOCGIWAP)] = gelicw_get_wap, + [IW_IOCTL_IDX(SIOCSIWSCAN)] = gelicw_set_scan, + [IW_IOCTL_IDX(SIOCGIWSCAN)] = gelicw_get_scan, + [IW_IOCTL_IDX(SIOCSIWESSID)] = gelicw_set_essid, + [IW_IOCTL_IDX(SIOCGIWESSID)] = gelicw_get_essid, + [IW_IOCTL_IDX(SIOCSIWNICKN)] = gelicw_set_nick, + [IW_IOCTL_IDX(SIOCGIWNICKN)] = gelicw_get_nick, + [IW_IOCTL_IDX(SIOCSIWRATE)] = gelicw_set_rate, + [IW_IOCTL_IDX(SIOCGIWRATE)] = gelicw_get_rate, + [IW_IOCTL_IDX(SIOCSIWENCODE)] = gelicw_set_encode, + [IW_IOCTL_IDX(SIOCGIWENCODE)] = gelicw_get_encode, + [IW_IOCTL_IDX(SIOCSIWAUTH)] = gelicw_set_auth, + [IW_IOCTL_IDX(SIOCGIWAUTH)] = gelicw_get_auth, + [IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = gelicw_set_encodeext, + [IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = gelicw_get_encodeext, +}; + +/* + * Private wireless handlers + */ +enum { + GELICW_PRIV_SET_AUTH = SIOCIWFIRSTPRIV, + GELICW_PRIV_GET_AUTH +}; + +static struct iw_priv_args gelicw_private_args[] = { + { + .cmd = GELICW_PRIV_SET_AUTH, + .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + .name = "set_alg" + }, + { + .cmd = GELICW_PRIV_GET_AUTH, + .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_IW_PRIV_SIZE, + .name = "get_alg" + }, +}; + +static const iw_handler gelicw_private_handler[] = +{ + gelicw_priv_set_alg_mode, + gelicw_priv_get_alg_mode, +}; + +static struct iw_handler_def gelicw_handler_def = +{ + .num_standard = ARRAY_SIZE(gelicw_handler), + .num_private = ARRAY_SIZE(gelicw_private_handler), + .num_private_args = ARRAY_SIZE(gelicw_private_args), + .standard = (iw_handler *)gelicw_handler, + .private = (iw_handler *)gelicw_private_handler, + .private_args = (struct iw_priv_args *)gelicw_private_args, + .get_wireless_stats = gelicw_get_wireless_stats +}; Index: linux-2.6/drivers/ps3/sys-manager.c =================================================================== --- linux-2.6.orig/drivers/ps3/sys-manager.c +++ linux-2.6/drivers/ps3/sys-manager.c @@ -422,7 +422,7 @@ static int ps3_sys_manager_handle_event( __func__, __LINE__); ps3_sm_force_power_off = 1; /* - * A memory barrier is use here to sync memory since + * A memory barrier is used here to sync memory since * ps3_sys_manager_final_restart() could be called on * another cpu. */ @@ -438,7 +438,7 @@ static int ps3_sys_manager_handle_event( __func__, __LINE__); ps3_sm_force_power_off = 0; /* - * A memory barrier is use here to sync memory since + * A memory barrier is used here to sync memory since * ps3_sys_manager_final_restart() could be called on * another cpu. */ @@ -622,7 +622,7 @@ static void ps3_sys_manager_final_restar ps3_vuart_cancel_async(dev); ps3_sys_manager_send_attr(dev, 0); - ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_LPAR_REBOOT, + ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_REBOOT, PS3_SM_WAKE_DEFAULT); ps3_sys_manager_send_request_shutdown(dev); Index: linux-2.6/drivers/scsi/Makefile =================================================================== --- linux-2.6.orig/drivers/scsi/Makefile +++ linux-2.6/drivers/scsi/Makefile @@ -132,6 +132,7 @@ obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi/ obj-$(CONFIG_SCSI_IBMVSCSIS) += ibmvscsi/ obj-$(CONFIG_SCSI_HPTIOP) += hptiop.o obj-$(CONFIG_SCSI_STEX) += stex.o +obj-$(CONFIG_PS3_ROM) += ps3rom.o obj-$(CONFIG_ARM) += arm/ Index: linux-2.6/drivers/scsi/ps3rom.c =================================================================== --- /dev/null +++ linux-2.6/drivers/scsi/ps3rom.c @@ -0,0 +1,537 @@ +/* + * PS3 BD/DVD/CD-ROM Storage Driver + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * 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; version 2 of the License. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + + +#define DEVICE_NAME "ps3rom" + +#define BOUNCE_SIZE (64*1024) + +#define PS3ROM_MAX_SECTORS (BOUNCE_SIZE / CD_FRAMESIZE) + + +struct ps3rom_private { + struct ps3_storage_device *dev; + struct scsi_cmnd *curr_cmd; +}; +#define ps3rom_priv(dev) ((dev)->sbd.core.driver_data) + + +#define LV1_STORAGE_SEND_ATAPI_COMMAND (1) + +struct lv1_atapi_cmnd_block { + u8 pkt[32]; /* packet command block */ + u32 pktlen; /* should be 12 for ATAPI 8020 */ + u32 blocks; + u32 block_size; + u32 proto; /* transfer mode */ + u32 in_out; /* transfer direction */ + u64 buffer; /* parameter except command block */ + u32 arglen; /* length above */ +}; + +enum lv1_atapi_proto { + NON_DATA_PROTO = 0, + PIO_DATA_IN_PROTO = 1, + PIO_DATA_OUT_PROTO = 2, + DMA_PROTO = 3 +}; + +enum lv1_atapi_in_out { + DIR_WRITE = 0, /* memory -> device */ + DIR_READ = 1 /* device -> memory */ +}; + + +static int ps3rom_slave_configure(struct scsi_device *scsi_dev) +{ + struct ps3rom_private *priv = shost_priv(scsi_dev->host); + struct ps3_storage_device *dev = priv->dev; + + dev_dbg(&dev->sbd.core, "%s:%u: id %u, lun %u, channel %u\n", __func__, + __LINE__, scsi_dev->id, scsi_dev->lun, scsi_dev->channel); + + /* + * ATAPI SFF8020 devices use MODE_SENSE_10, + * so we can prohibit MODE_SENSE_6 + */ + scsi_dev->use_10_for_ms = 1; + + /* we don't support {READ,WRITE}_6 */ + scsi_dev->use_10_for_rw = 1; + + return 0; +} + +/* + * copy data from device into scatter/gather buffer + */ +static int fill_from_dev_buffer(struct scsi_cmnd *cmd, const void *buf) +{ + int k, req_len, act_len, len, active; + void *kaddr; + struct scatterlist *sgpnt; + unsigned int buflen; + + buflen = cmd->request_bufflen; + if (!buflen) + return 0; + + if (!cmd->request_buffer) + return -1; + + sgpnt = cmd->request_buffer; + active = 1; + for (k = 0, req_len = 0, act_len = 0; k < cmd->use_sg; ++k, ++sgpnt) { + if (active) { + kaddr = kmap_atomic(sgpnt->page, KM_USER0); + if (!kaddr) + return -1; + len = sgpnt->length; + if ((req_len + len) > buflen) { + active = 0; + len = buflen - req_len; + } + memcpy(kaddr + sgpnt->offset, buf + req_len, len); + kunmap_atomic(kaddr, KM_USER0); + act_len += len; + } + req_len += sgpnt->length; + } + cmd->resid = req_len - act_len; + return 0; +} + +/* + * copy data from scatter/gather into device's buffer + */ +static int fetch_to_dev_buffer(struct scsi_cmnd *cmd, void *buf) +{ + int k, req_len, len, fin; + void *kaddr; + struct scatterlist *sgpnt; + unsigned int buflen; + + buflen = cmd->request_bufflen; + if (!buflen) + return 0; + + if (!cmd->request_buffer) + return -1; + + sgpnt = cmd->request_buffer; + for (k = 0, req_len = 0, fin = 0; k < cmd->use_sg; ++k, ++sgpnt) { + kaddr = kmap_atomic(sgpnt->page, KM_USER0); + if (!kaddr) + return -1; + len = sgpnt->length; + if ((req_len + len) > buflen) { + len = buflen - req_len; + fin = 1; + } + memcpy(buf + req_len, kaddr + sgpnt->offset, len); + kunmap_atomic(kaddr, KM_USER0); + if (fin) + return req_len + len; + req_len += sgpnt->length; + } + return req_len; +} + +static int ps3rom_atapi_request(struct ps3_storage_device *dev, + struct scsi_cmnd *cmd) +{ + struct lv1_atapi_cmnd_block atapi_cmnd; + unsigned char opcode = cmd->cmnd[0]; + int res; + u64 lpar; + + dev_dbg(&dev->sbd.core, "%s:%u: send ATAPI command 0x%02x\n", __func__, + __LINE__, opcode); + + memset(&atapi_cmnd, 0, sizeof(struct lv1_atapi_cmnd_block)); + memcpy(&atapi_cmnd.pkt, cmd->cmnd, 12); + atapi_cmnd.pktlen = 12; + atapi_cmnd.block_size = 1; /* transfer size is block_size * blocks */ + atapi_cmnd.blocks = atapi_cmnd.arglen = cmd->request_bufflen; + atapi_cmnd.buffer = dev->bounce_lpar; + + switch (cmd->sc_data_direction) { + case DMA_FROM_DEVICE: + if (cmd->request_bufflen >= CD_FRAMESIZE) + atapi_cmnd.proto = DMA_PROTO; + else + atapi_cmnd.proto = PIO_DATA_IN_PROTO; + atapi_cmnd.in_out = DIR_READ; + break; + + case DMA_TO_DEVICE: + if (cmd->request_bufflen >= CD_FRAMESIZE) + atapi_cmnd.proto = DMA_PROTO; + else + atapi_cmnd.proto = PIO_DATA_OUT_PROTO; + atapi_cmnd.in_out = DIR_WRITE; + res = fetch_to_dev_buffer(cmd, dev->bounce_buf); + if (res < 0) + return DID_ERROR << 16; + break; + + default: + atapi_cmnd.proto = NON_DATA_PROTO; + break; + } + + lpar = ps3_mm_phys_to_lpar(__pa(&atapi_cmnd)); + res = lv1_storage_send_device_command(dev->sbd.dev_id, + LV1_STORAGE_SEND_ATAPI_COMMAND, + lpar, sizeof(atapi_cmnd), + atapi_cmnd.buffer, + atapi_cmnd.arglen, &dev->tag); + if (res == LV1_DENIED_BY_POLICY) { + dev_dbg(&dev->sbd.core, + "%s:%u: ATAPI command 0x%02x denied by policy\n", + __func__, __LINE__, opcode); + return DID_ERROR << 16; + } + + if (res) { + dev_err(&dev->sbd.core, + "%s:%u: ATAPI command 0x%02x failed %d\n", __func__, + __LINE__, opcode, res); + return DID_ERROR << 16; + } + + return 0; +} + +static inline unsigned int srb10_lba(const struct scsi_cmnd *cmd) +{ + return cmd->cmnd[2] << 24 | cmd->cmnd[3] << 16 | cmd->cmnd[4] << 8 | + cmd->cmnd[5]; +} + +static inline unsigned int srb10_len(const struct scsi_cmnd *cmd) +{ + return cmd->cmnd[7] << 8 | cmd->cmnd[8]; +} + +static int ps3rom_read_request(struct ps3_storage_device *dev, + struct scsi_cmnd *cmd, u32 start_sector, + u32 sectors) +{ + int res; + + dev_dbg(&dev->sbd.core, "%s:%u: read %u sectors starting at %u\n", + __func__, __LINE__, sectors, start_sector); + + res = lv1_storage_read(dev->sbd.dev_id, + dev->regions[dev->region_idx].id, start_sector, + sectors, 0, dev->bounce_lpar, &dev->tag); + if (res) { + dev_err(&dev->sbd.core, "%s:%u: read failed %d\n", __func__, + __LINE__, res); + return DID_ERROR << 16; + } + + return 0; +} + +static int ps3rom_write_request(struct ps3_storage_device *dev, + struct scsi_cmnd *cmd, u32 start_sector, + u32 sectors) +{ + int res; + + dev_dbg(&dev->sbd.core, "%s:%u: write %u sectors starting at %u\n", + __func__, __LINE__, sectors, start_sector); + + res = fetch_to_dev_buffer(cmd, dev->bounce_buf); + if (res < 0) + return DID_ERROR << 16; + + res = lv1_storage_write(dev->sbd.dev_id, + dev->regions[dev->region_idx].id, start_sector, + sectors, 0, dev->bounce_lpar, &dev->tag); + if (res) { + dev_err(&dev->sbd.core, "%s:%u: write failed %d\n", __func__, + __LINE__, res); + return DID_ERROR << 16; + } + + return 0; +} + +static int ps3rom_queuecommand(struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *)) +{ + struct ps3rom_private *priv = shost_priv(cmd->device->host); + struct ps3_storage_device *dev = priv->dev; + unsigned char opcode; + int res; + +#ifdef DEBUG + scsi_print_command(cmd); +#endif + + priv->curr_cmd = cmd; + cmd->scsi_done = done; + + opcode = cmd->cmnd[0]; + /* + * While we can submit READ/WRITE SCSI commands as ATAPI commands, + * it's recommended for various reasons (performance, error handling, + * ...) to use lv1_storage_{read,write}() instead + */ + switch (opcode) { + case READ_10: + res = ps3rom_read_request(dev, cmd, srb10_lba(cmd), + srb10_len(cmd)); + break; + + case WRITE_10: + res = ps3rom_write_request(dev, cmd, srb10_lba(cmd), + srb10_len(cmd)); + break; + + default: + res = ps3rom_atapi_request(dev, cmd); + break; + } + + if (res) { + memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); + cmd->result = res; + cmd->sense_buffer[0] = 0x70; + cmd->sense_buffer[2] = ILLEGAL_REQUEST; + priv->curr_cmd = NULL; + cmd->scsi_done(cmd); + } + + return 0; +} + +static int decode_lv1_status(u64 status, unsigned char *sense_key, + unsigned char *asc, unsigned char *ascq) +{ + if (((status >> 24) & 0xff) != SAM_STAT_CHECK_CONDITION) + return -1; + + *sense_key = (status >> 16) & 0xff; + *asc = (status >> 8) & 0xff; + *ascq = status & 0xff; + return 0; +} + +static irqreturn_t ps3rom_interrupt(int irq, void *data) +{ + struct ps3_storage_device *dev = data; + struct Scsi_Host *host; + struct ps3rom_private *priv; + struct scsi_cmnd *cmd; + int res; + u64 tag, status; + unsigned char sense_key, asc, ascq; + + res = lv1_storage_get_async_status(dev->sbd.dev_id, &tag, &status); + /* + * status = -1 may mean that ATAPI transport completed OK, but + * ATAPI command itself resulted CHECK CONDITION + * so, upper layer should issue REQUEST_SENSE to check the sense data + */ + + if (tag != dev->tag) + dev_err(&dev->sbd.core, + "%s:%u: tag mismatch, got %lx, expected %lx\n", + __func__, __LINE__, tag, dev->tag); + + if (res) { + dev_err(&dev->sbd.core, "%s:%u: res=%d status=0x%lx\n", + __func__, __LINE__, res, status); + return IRQ_HANDLED; + } + + host = ps3rom_priv(dev); + priv = shost_priv(host); + cmd = priv->curr_cmd; + + if (!status) { + /* OK, completed */ + if (cmd->sc_data_direction == DMA_FROM_DEVICE) { + res = fill_from_dev_buffer(cmd, dev->bounce_buf); + if (res) { + cmd->result = DID_ERROR << 16; + goto done; + } + } + cmd->result = DID_OK << 16; + goto done; + } + + if (cmd->cmnd[0] == REQUEST_SENSE) { + /* SCSI spec says request sense should never get error */ + dev_err(&dev->sbd.core, "%s:%u: end error without autosense\n", + __func__, __LINE__); + cmd->result = DID_ERROR << 16 | SAM_STAT_CHECK_CONDITION; + goto done; + } + + if (decode_lv1_status(status, &sense_key, &asc, &ascq)) { + cmd->result = DID_ERROR << 16; + goto done; + } + + cmd->sense_buffer[0] = 0x70; + cmd->sense_buffer[2] = sense_key; + cmd->sense_buffer[7] = 16 - 6; + cmd->sense_buffer[12] = asc; + cmd->sense_buffer[13] = ascq; + cmd->result = SAM_STAT_CHECK_CONDITION; + +done: + priv->curr_cmd = NULL; + cmd->scsi_done(cmd); + return IRQ_HANDLED; +} + +static struct scsi_host_template ps3rom_host_template = { + .name = DEVICE_NAME, + .slave_configure = ps3rom_slave_configure, + .queuecommand = ps3rom_queuecommand, + .can_queue = 1, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 1, + .emulated = 1, /* only sg driver uses this */ + .max_sectors = PS3ROM_MAX_SECTORS, + .use_clustering = ENABLE_CLUSTERING, + .module = THIS_MODULE, +}; + + +static int __devinit ps3rom_probe(struct ps3_system_bus_device *_dev) +{ + struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); + int error; + struct Scsi_Host *host; + struct ps3rom_private *priv; + + if (dev->blk_size != CD_FRAMESIZE) { + dev_err(&dev->sbd.core, + "%s:%u: cannot handle block size %lu\n", __func__, + __LINE__, dev->blk_size); + return -EINVAL; + } + + dev->bounce_size = BOUNCE_SIZE; + dev->bounce_buf = kmalloc(BOUNCE_SIZE, GFP_DMA); + if (!dev->bounce_buf) + return -ENOMEM; + + error = ps3stor_setup(dev, ps3rom_interrupt); + if (error) + goto fail_free_bounce; + + host = scsi_host_alloc(&ps3rom_host_template, + sizeof(struct ps3rom_private)); + if (!host) { + dev_err(&dev->sbd.core, "%s:%u: scsi_host_alloc failed\n", + __func__, __LINE__); + goto fail_teardown; + } + + priv = shost_priv(host); + ps3rom_priv(dev) = host; + priv->dev = dev; + + /* One device/LUN per SCSI bus */ + host->max_id = 1; + host->max_lun = 1; + + error = scsi_add_host(host, &dev->sbd.core); + if (error) { + dev_err(&dev->sbd.core, "%s:%u: scsi_host_alloc failed %d\n", + __func__, __LINE__, error); + error = -ENODEV; + goto fail_host_put; + } + + scsi_scan_host(host); + return 0; + +fail_host_put: + scsi_host_put(host); + ps3rom_priv(dev) = NULL; +fail_teardown: + ps3stor_teardown(dev); +fail_free_bounce: + kfree(dev->bounce_buf); + return error; +} + +static int ps3rom_remove(struct ps3_system_bus_device *_dev) +{ + struct ps3_storage_device *dev = to_ps3_storage_device(&_dev->core); + struct Scsi_Host *host = ps3rom_priv(dev); + + scsi_remove_host(host); + ps3stor_teardown(dev); + scsi_host_put(host); + ps3rom_priv(dev) = NULL; + kfree(dev->bounce_buf); + return 0; +} + +static struct ps3_system_bus_driver ps3rom = { + .match_id = PS3_MATCH_ID_STOR_ROM, + .core.name = DEVICE_NAME, + .core.owner = THIS_MODULE, + .probe = ps3rom_probe, + .remove = ps3rom_remove +}; + + +static int __init ps3rom_init(void) +{ + return ps3_system_bus_driver_register(&ps3rom); +} + +static void __exit ps3rom_exit(void) +{ + ps3_system_bus_driver_unregister(&ps3rom); +} + +module_init(ps3rom_init); +module_exit(ps3rom_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PS3 BD/DVD/CD-ROM Storage Driver"); +MODULE_AUTHOR("Sony Corporation"); +MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_ROM); Index: linux-2.6/drivers/usb/host/ehci-hcd.c =================================================================== --- linux-2.6.orig/drivers/usb/host/ehci-hcd.c +++ linux-2.6/drivers/usb/host/ehci-hcd.c @@ -41,10 +41,6 @@ #include #include #include -#ifdef CONFIG_PPC_PS3 -#include -#endif - /*-------------------------------------------------------------------------*/ @@ -937,7 +933,7 @@ MODULE_LICENSE ("GPL"); #ifdef CONFIG_PPC_PS3 #include "ehci-ps3.c" -#define PS3_SYSTEM_BUS_DRIVER ps3_ehci_sb_driver +#define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver #endif #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ @@ -971,18 +967,15 @@ static int __init ehci_hcd_init(void) #endif #ifdef PS3_SYSTEM_BUS_DRIVER - if (firmware_has_feature(FW_FEATURE_PS3_LV1)) { - retval = ps3_system_bus_driver_register( - &PS3_SYSTEM_BUS_DRIVER); - if (retval < 0) { + retval = ps3_ehci_driver_register(&PS3_SYSTEM_BUS_DRIVER); + if (retval < 0) { #ifdef PLATFORM_DRIVER - platform_driver_unregister(&PLATFORM_DRIVER); + platform_driver_unregister(&PLATFORM_DRIVER); #endif #ifdef PCI_DRIVER - pci_unregister_driver(&PCI_DRIVER); + pci_unregister_driver(&PCI_DRIVER); #endif - return retval; - } + return retval; } #endif @@ -999,8 +992,7 @@ static void __exit ehci_hcd_cleanup(void pci_unregister_driver(&PCI_DRIVER); #endif #ifdef PS3_SYSTEM_BUS_DRIVER - if (firmware_has_feature(FW_FEATURE_PS3_LV1)) - ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); + ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif } module_exit(ehci_hcd_cleanup); Index: linux-2.6/drivers/usb/host/ehci-ps3.c =================================================================== --- linux-2.6.orig/drivers/usb/host/ehci-ps3.c +++ linux-2.6/drivers/usb/host/ehci-ps3.c @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include static int ps3_ehci_hc_reset(struct usb_hcd *hcd) @@ -73,7 +74,7 @@ static const struct hc_driver ps3_ehci_h #endif }; -static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev) +static int ps3_ehci_probe(struct ps3_system_bus_device *dev) { int result; struct usb_hcd *hcd; @@ -85,13 +86,30 @@ static int ps3_ehci_sb_probe(struct ps3_ goto fail_start; } + result = ps3_open_hv_device(dev); + + if (result) { + dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed\n", + __func__, __LINE__); + goto fail_open; + } + + result = ps3_dma_region_create(dev->d_region); + + if (result) { + dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: " + "(%d)\n", __func__, __LINE__, result); + BUG_ON("check region type"); + goto fail_dma_region; + } + result = ps3_mmio_region_create(dev->m_region); if (result) { dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n", __func__, __LINE__); result = -EPERM; - goto fail_mmio; + goto fail_mmio_region; } dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__, @@ -120,6 +138,11 @@ static int ps3_ehci_sb_probe(struct ps3_ hcd->rsrc_start = dev->m_region->lpar_addr; hcd->rsrc_len = dev->m_region->len; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) + dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n", + __func__, __LINE__); + hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len); if (!hcd->regs) { @@ -153,34 +176,73 @@ static int ps3_ehci_sb_probe(struct ps3_ fail_add_hcd: iounmap(hcd->regs); fail_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); fail_create_hcd: ps3_io_irq_destroy(virq); fail_irq: ps3_free_mmio_region(dev->m_region); -fail_mmio: +fail_mmio_region: + ps3_dma_region_free(dev->d_region); +fail_dma_region: + ps3_close_hv_device(dev); +fail_open: fail_start: return result; } -static int ps3_ehci_sb_remove(struct ps3_system_bus_device *dev) +static int ps3_ehci_remove(struct ps3_system_bus_device *dev) { + unsigned int tmp; struct usb_hcd *hcd = (struct usb_hcd *)ps3_system_bus_get_driver_data(dev); - usb_put_hcd(hcd); + BUG_ON(!hcd); + + dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs); + dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq); + + tmp = hcd->irq; + + usb_remove_hcd(hcd); + ps3_system_bus_set_driver_data(dev, NULL); + BUG_ON(!hcd->regs); + iounmap(hcd->regs); + + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + ps3_io_irq_destroy(tmp); + ps3_free_mmio_region(dev->m_region); + + ps3_dma_region_free(dev->d_region); + ps3_close_hv_device(dev); + return 0; } -MODULE_ALIAS("ps3-ehci"); +static int ps3_ehci_driver_register(struct ps3_system_bus_driver *drv) +{ + return firmware_has_feature(FW_FEATURE_PS3_LV1) + ? ps3_system_bus_driver_register(drv) + : 0; +} + +static void ps3_ehci_driver_unregister(struct ps3_system_bus_driver *drv) +{ + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) + ps3_system_bus_driver_unregister(drv); +} + +MODULE_ALIAS(PS3_MODULE_ALIAS_EHCI); -static struct ps3_system_bus_driver ps3_ehci_sb_driver = { +static struct ps3_system_bus_driver ps3_ehci_driver = { + .core.name = "ps3-ehci-driver", + .core.owner = THIS_MODULE, .match_id = PS3_MATCH_ID_EHCI, - .core = { - .name = "ps3-ehci-driver", - }, - .probe = ps3_ehci_sb_probe, - .remove = ps3_ehci_sb_remove, + .probe = ps3_ehci_probe, + .remove = ps3_ehci_remove, + .shutdown = ps3_ehci_remove, }; Index: linux-2.6/drivers/usb/host/ehci-sched.c =================================================================== --- linux-2.6.orig/drivers/usb/host/ehci-sched.c +++ linux-2.6/drivers/usb/host/ehci-sched.c @@ -21,6 +21,10 @@ /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_PPC_PS3 +#include +#endif + /* * EHCI scheduled transaction support: interrupt, iso, split iso * These are called "periodic" transactions in the EHCI spec. @@ -1168,8 +1172,21 @@ itd_urb_transaction ( if (likely (!list_empty(&stream->free_list))) { itd = list_entry (stream->free_list.prev, struct ehci_itd, itd_list); - list_del (&itd->itd_list); - itd_dma = itd->itd_dma; +#ifdef CONFIG_PPC_PS3 + /* Fix for Cell SCC ISO transfer (PS3 Bluetooth). */ + if (firmware_has_feature(FW_FEATURE_PS3_LV1) + && itd->frame == ((ehci_readl(ehci, + &ehci->regs->frame_index) >> 3) + % ehci->periodic_size)) + itd = NULL; + else { + list_del (&itd->itd_list); + itd_dma = itd->itd_dma; + } +#else + list_del (&itd->itd_list); + itd_dma = itd->itd_dma; +#endif } else itd = NULL; @@ -1784,8 +1801,21 @@ sitd_urb_transaction ( if (!list_empty(&stream->free_list)) { sitd = list_entry (stream->free_list.prev, struct ehci_sitd, sitd_list); - list_del (&sitd->sitd_list); - sitd_dma = sitd->sitd_dma; +#ifdef CONFIG_PPC_PS3 + /* Fix for Cell SCC ISO transfer (PS3 Bluetooth). */ + if (firmware_has_feature(FW_FEATURE_PS3_LV1) + && sitd->frame == ((ehci_readl(ehci, + &ehci->regs->frame_index) >> 3) + % ehci->periodic_size)) + sitd = NULL; + else { + list_del (&sitd->sitd_list); + sitd_dma = sitd->sitd_dma; + } +#else + list_del (&sitd->sitd_list); + sitd_dma = sitd->sitd_dma; +#endif } else sitd = NULL; Index: linux-2.6/drivers/usb/host/ohci-hcd.c =================================================================== --- linux-2.6.orig/drivers/usb/host/ohci-hcd.c +++ linux-2.6/drivers/usb/host/ohci-hcd.c @@ -41,9 +41,6 @@ #include #include #include -#ifdef CONFIG_PPC_PS3 -#include -#endif #include "../core/hcd.h" @@ -917,7 +914,7 @@ MODULE_LICENSE ("GPL"); #ifdef CONFIG_PPC_PS3 #include "ohci-ps3.c" -#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_sb_driver +#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver #endif #if !defined(PCI_DRIVER) && \ @@ -940,12 +937,9 @@ static int __init ohci_hcd_mod_init(void sizeof (struct ed), sizeof (struct td)); #ifdef PS3_SYSTEM_BUS_DRIVER - if (firmware_has_feature(FW_FEATURE_PS3_LV1)) { - retval = ps3_system_bus_driver_register( - &PS3_SYSTEM_BUS_DRIVER); - if (retval < 0) - goto error_ps3; - } + retval = ps3_ohci_driver_register(&PS3_SYSTEM_BUS_DRIVER); + if (retval < 0) + goto error_ps3; #endif #ifdef PLATFORM_DRIVER @@ -991,8 +985,7 @@ static int __init ohci_hcd_mod_init(void error_platform: #endif #ifdef PS3_SYSTEM_BUS_DRIVER - if (firmware_has_feature(FW_FEATURE_PS3_LV1)) - ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); + ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); error_ps3: #endif return retval; @@ -1014,8 +1007,7 @@ static void __exit ohci_hcd_mod_exit(voi platform_driver_unregister(&PLATFORM_DRIVER); #endif #ifdef PS3_SYSTEM_BUS_DRIVER - if (firmware_has_feature(FW_FEATURE_PS3_LV1)) - ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); + ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif } module_exit(ohci_hcd_mod_exit); Index: linux-2.6/drivers/usb/host/ohci-ps3.c =================================================================== --- linux-2.6.orig/drivers/usb/host/ohci-ps3.c +++ linux-2.6/drivers/usb/host/ohci-ps3.c @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include static int ps3_ohci_hc_reset(struct usb_hcd *hcd) @@ -75,7 +76,7 @@ static const struct hc_driver ps3_ohci_h #endif }; -static int ps3_ohci_sb_probe(struct ps3_system_bus_device *dev) +static int ps3_ohci_probe(struct ps3_system_bus_device *dev) { int result; struct usb_hcd *hcd; @@ -87,13 +88,31 @@ static int ps3_ohci_sb_probe(struct ps3_ goto fail_start; } + result = ps3_open_hv_device(dev); + + if (result) { + dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed: %s\n", + __func__, __LINE__, ps3_result(result)); + result = -EPERM; + goto fail_open; + } + + result = ps3_dma_region_create(dev->d_region); + + if (result) { + dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: " + "(%d)\n", __func__, __LINE__, result); + BUG_ON("check region type"); + goto fail_dma_region; + } + result = ps3_mmio_region_create(dev->m_region); if (result) { dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n", __func__, __LINE__); result = -EPERM; - goto fail_mmio; + goto fail_mmio_region; } dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__, @@ -122,6 +141,11 @@ static int ps3_ohci_sb_probe(struct ps3_ hcd->rsrc_start = dev->m_region->lpar_addr; hcd->rsrc_len = dev->m_region->len; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) + dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n", + __func__, __LINE__); + hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len); if (!hcd->regs) { @@ -155,34 +179,73 @@ static int ps3_ohci_sb_probe(struct ps3_ fail_add_hcd: iounmap(hcd->regs); fail_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); fail_create_hcd: ps3_io_irq_destroy(virq); fail_irq: ps3_free_mmio_region(dev->m_region); -fail_mmio: +fail_mmio_region: + ps3_dma_region_free(dev->d_region); +fail_dma_region: + ps3_close_hv_device(dev); +fail_open: fail_start: return result; } -static int ps3_ohci_sb_remove (struct ps3_system_bus_device *dev) +static int ps3_ohci_remove (struct ps3_system_bus_device *dev) { + unsigned int tmp; struct usb_hcd *hcd = (struct usb_hcd *)ps3_system_bus_get_driver_data(dev); - usb_put_hcd(hcd); + BUG_ON(!hcd); + + dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs); + dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq); + + tmp = hcd->irq; + + usb_remove_hcd(hcd); + ps3_system_bus_set_driver_data(dev, NULL); + BUG_ON(!hcd->regs); + iounmap(hcd->regs); + + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + ps3_io_irq_destroy(tmp); + ps3_free_mmio_region(dev->m_region); + + ps3_dma_region_free(dev->d_region); + ps3_close_hv_device(dev); + return 0; } -MODULE_ALIAS("ps3-ohci"); +static int ps3_ohci_driver_register(struct ps3_system_bus_driver *drv) +{ + return firmware_has_feature(FW_FEATURE_PS3_LV1) + ? ps3_system_bus_driver_register(drv) + : 0; +} + +static void ps3_ohci_driver_unregister(struct ps3_system_bus_driver *drv) +{ + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) + ps3_system_bus_driver_unregister(drv); +} + +MODULE_ALIAS(PS3_MODULE_ALIAS_OHCI); -static struct ps3_system_bus_driver ps3_ohci_sb_driver = { +static struct ps3_system_bus_driver ps3_ohci_driver = { + .core.name = "ps3-ohci-driver", + .core.owner = THIS_MODULE, .match_id = PS3_MATCH_ID_OHCI, - .core = { - .name = "ps3-ohci-driver", - }, - .probe = ps3_ohci_sb_probe, - .remove = ps3_ohci_sb_remove, + .probe = ps3_ohci_probe, + .remove = ps3_ohci_remove, + .shutdown = ps3_ohci_remove, }; Index: linux-2.6/drivers/video/Kconfig =================================================================== --- linux-2.6.orig/drivers/video/Kconfig +++ linux-2.6/drivers/video/Kconfig @@ -1796,6 +1796,7 @@ config FB_PS3 select FB_SYS_COPYAREA select FB_SYS_IMAGEBLIT select FB_SYS_FOPS + select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE ---help--- Include support for the virtual frame buffer in the PS3 platform. Index: linux-2.6/drivers/video/console/fbcon.c =================================================================== --- linux-2.6.orig/drivers/video/console/fbcon.c +++ linux-2.6/drivers/video/console/fbcon.c @@ -2937,6 +2937,45 @@ static int fbcon_mode_deleted(struct fb_ return found; } +#ifdef CONFIG_VT_HW_CONSOLE_BINDING +static int fbcon_unbind(void) +{ + int ret; + + ret = unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, + fbcon_is_default); + return ret; +} +#else +static int fbcon_unbind(void) +{ + return -EINVAL; +} +#endif /* CONFIG_VT_HW_CONSOLE_BINDING */ + +static int fbcon_fb_unbind(int idx) +{ + int i, new_idx = -1, ret = 0; + + for (i = first_fb_vc; i <= last_fb_vc; i++) { + if (con2fb_map[i] != idx && + con2fb_map[i] != -1) { + new_idx = i; + break; + } + } + + if (new_idx != -1) { + for (i = first_fb_vc; i <= last_fb_vc; i++) { + if (con2fb_map[i] == idx) + set_con2fb_map(i, new_idx, 0); + } + } else + ret = fbcon_unbind(); + + return ret; +} + static int fbcon_fb_unregistered(int idx) { int i; @@ -3114,6 +3153,9 @@ static int fbcon_event_notify(struct not mode = event->data; ret = fbcon_mode_deleted(info, mode); break; + case FB_EVENT_FB_UNBIND: + ret = fbcon_fb_unbind(info->node); + break; case FB_EVENT_FB_REGISTERED: ret = fbcon_fb_registered(info->node); break; Index: linux-2.6/drivers/video/fbmem.c =================================================================== --- linux-2.6.orig/drivers/video/fbmem.c +++ linux-2.6/drivers/video/fbmem.c @@ -318,6 +318,13 @@ static struct logo_data { const struct linux_logo *logo; } fb_logo __read_mostly; +#define FB_LOGO_EX_NUM_MAX 10 +static struct logo_data_extra { + const struct linux_logo *logo; + unsigned int n; +} fb_logo_ex[FB_LOGO_EX_NUM_MAX]; +static unsigned int fb_logo_ex_num; + static void fb_rotate_logo_ud(const u8 *in, u8 *out, u32 width, u32 height) { u32 size = width * height, i; @@ -411,10 +418,22 @@ static void fb_do_show_logo(struct fb_in } } +#ifdef CONFIG_FB +void fb_append_extra_logo(const struct linux_logo *logo, unsigned int n) +{ + if (!n || fb_logo_ex_num == FB_LOGO_EX_NUM_MAX) + return; + + fb_logo_ex[fb_logo_ex_num].logo = logo; + fb_logo_ex[fb_logo_ex_num].n = n; + fb_logo_ex_num++; +} +#endif + int fb_prepare_logo(struct fb_info *info, int rotate) { int depth = fb_get_color_depth(&info->var, &info->fix); - int yres; + unsigned int yres, height, i; memset(&fb_logo, 0, sizeof(struct logo_data)); @@ -474,25 +493,41 @@ int fb_prepare_logo(struct fb_info *info fb_logo.depth = 4; else fb_logo.depth = 1; - return fb_logo.logo->height; + + /* FIXME: logo_ex supports only truecolor fb. */ + if (info->fix.visual != FB_VISUAL_TRUECOLOR) + fb_logo_ex_num = 0; + + height = fb_logo.logo->height; + for (i = 0; i < fb_logo_ex_num; i++) { + height += fb_logo_ex[i].logo->height; + if (height > yres) { + height -= fb_logo_ex[i].logo->height; + fb_logo_ex_num = i; + break; + } + } + return height; } -int fb_show_logo(struct fb_info *info, int rotate) +static int fb_show_logo_line(struct fb_info *info, int rotate, + const struct linux_logo *logo, int y, + unsigned int n) { u32 *palette = NULL, *saved_pseudo_palette = NULL; unsigned char *logo_new = NULL, *logo_rotate = NULL; struct fb_image image; /* Return if the frame buffer is not mapped or suspended */ - if (fb_logo.logo == NULL || info->state != FBINFO_STATE_RUNNING || + if (logo == NULL || info->state != FBINFO_STATE_RUNNING || info->flags & FBINFO_MODULE) return 0; image.depth = 8; - image.data = fb_logo.logo->data; + image.data = logo->data; if (fb_logo.needs_cmapreset) - fb_set_logocmap(info, fb_logo.logo); + fb_set_logocmap(info, logo); if (fb_logo.needs_truepalette || fb_logo.needs_directpalette) { @@ -501,17 +536,16 @@ int fb_show_logo(struct fb_info *info, i return 0; if (fb_logo.needs_truepalette) - fb_set_logo_truepalette(info, fb_logo.logo, palette); + fb_set_logo_truepalette(info, logo, palette); else - fb_set_logo_directpalette(info, fb_logo.logo, palette); + fb_set_logo_directpalette(info, logo, palette); saved_pseudo_palette = info->pseudo_palette; info->pseudo_palette = palette; } if (fb_logo.depth <= 4) { - logo_new = kmalloc(fb_logo.logo->width * fb_logo.logo->height, - GFP_KERNEL); + logo_new = kmalloc(logo->width * logo->height, GFP_KERNEL); if (logo_new == NULL) { kfree(palette); if (saved_pseudo_palette) @@ -519,29 +553,44 @@ int fb_show_logo(struct fb_info *info, i return 0; } image.data = logo_new; - fb_set_logo(info, fb_logo.logo, logo_new, fb_logo.depth); + fb_set_logo(info, logo, logo_new, fb_logo.depth); } image.dx = 0; - image.dy = 0; - image.width = fb_logo.logo->width; - image.height = fb_logo.logo->height; + image.dy = y; + image.width = logo->width; + image.height = logo->height; if (rotate) { - logo_rotate = kmalloc(fb_logo.logo->width * - fb_logo.logo->height, GFP_KERNEL); + logo_rotate = kmalloc(logo->width * + logo->height, GFP_KERNEL); if (logo_rotate) fb_rotate_logo(info, logo_rotate, &image, rotate); } - fb_do_show_logo(info, &image, rotate, num_online_cpus()); + fb_do_show_logo(info, &image, rotate, n); kfree(palette); if (saved_pseudo_palette != NULL) info->pseudo_palette = saved_pseudo_palette; kfree(logo_new); kfree(logo_rotate); - return fb_logo.logo->height; + return logo->height; +} + +int fb_show_logo(struct fb_info *info, int rotate) +{ + int y, i; + + y = fb_show_logo_line(info, rotate, fb_logo.logo, 0, + num_online_cpus()); + + for (i = 0; i < fb_logo_ex_num; i++) { + y += fb_show_logo_line(info, rotate, + fb_logo_ex[i].logo, y, fb_logo_ex[i].n); + } + + return y; } #else int fb_prepare_logo(struct fb_info *info, int rotate) { return 0; } @@ -1388,17 +1437,34 @@ register_framebuffer(struct fb_info *fb_ * * Returns negative errno on error, or zero for success. * + * This function will also notify the framebuffer console + * to release the driver. + * + * This is meant to be called within a driver's module_exit() + * function. If this is called outside module_exit(), ensure + * that the driver implements fb_open() and fb_release() to + * check that no processes are using the device. */ int unregister_framebuffer(struct fb_info *fb_info) { struct fb_event event; - int i; + int i, ret = 0; i = fb_info->node; - if (!registered_fb[i]) - return -EINVAL; + if (!registered_fb[i]) { + ret = -EINVAL; + goto done; + } + + event.info = fb_info; + ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event); + + if (ret) { + ret = -EINVAL; + goto done; + } if (fb_info->pixmap.addr && (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) @@ -1410,7 +1476,8 @@ unregister_framebuffer(struct fb_info *f device_destroy(fb_class, MKDEV(FB_MAJOR, i)); event.info = fb_info; fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event); - return 0; +done: + return ret; } /** Index: linux-2.6/drivers/video/logo/Makefile =================================================================== --- linux-2.6.orig/drivers/video/logo/Makefile +++ linux-2.6/drivers/video/logo/Makefile @@ -14,6 +14,8 @@ obj-$(CONFIG_LOGO_SUPERH_VGA16) += logo obj-$(CONFIG_LOGO_SUPERH_CLUT224) += logo_superh_clut224.o obj-$(CONFIG_LOGO_M32R_CLUT224) += logo_m32r_clut224.o +obj-$(CONFIG_SPU_BASE) += logo_spe_clut224.o + # How to generate logo's # Use logo-cfiles to retrieve list of .c files to be built Index: linux-2.6/drivers/video/logo/logo.c =================================================================== --- linux-2.6.orig/drivers/video/logo/logo.c +++ linux-2.6/drivers/video/logo/logo.c @@ -21,19 +21,6 @@ #include #endif -extern const struct linux_logo logo_linux_mono; -extern const struct linux_logo logo_linux_vga16; -extern const struct linux_logo logo_linux_clut224; -extern const struct linux_logo logo_dec_clut224; -extern const struct linux_logo logo_mac_clut224; -extern const struct linux_logo logo_parisc_clut224; -extern const struct linux_logo logo_sgi_clut224; -extern const struct linux_logo logo_sun_clut224; -extern const struct linux_logo logo_superh_mono; -extern const struct linux_logo logo_superh_vga16; -extern const struct linux_logo logo_superh_clut224; -extern const struct linux_logo logo_m32r_clut224; - const struct linux_logo *fb_find_logo(int depth) { Index: linux-2.6/drivers/video/logo/logo_spe_clut224.ppm =================================================================== --- /dev/null +++ linux-2.6/drivers/video/logo/logo_spe_clut224.ppm @@ -0,0 +1,283 @@ +P3 +40 40 +255 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 6 6 6 +15 15 15 21 21 21 19 19 19 14 14 14 6 6 6 2 2 2 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 2 2 2 21 21 21 55 55 55 +56 56 56 54 54 54 53 53 53 60 60 60 56 56 56 25 25 25 +6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 2 2 2 27 27 27 62 62 62 17 17 19 +2 2 6 2 2 6 2 2 6 2 2 6 16 16 18 57 57 57 +45 45 45 8 8 8 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 16 16 16 62 62 62 8 8 10 2 2 6 +2 2 6 2 2 6 2 2 6 12 12 14 67 67 67 16 16 17 +45 45 45 41 41 41 4 4 4 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 2 2 2 35 35 35 40 40 40 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 15 15 17 70 70 70 27 27 27 +3 3 6 62 62 62 20 20 20 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 4 4 4 58 58 58 12 12 14 2 2 6 2 2 6 +2 2 6 2 2 6 2 2 6 4 4 7 4 4 7 2 2 6 +2 2 6 34 34 36 40 40 40 3 3 3 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 7 7 7 64 64 64 2 2 6 5 5 5 17 17 17 +3 3 6 2 2 6 2 2 6 15 15 15 21 21 21 7 7 10 +2 2 6 8 8 10 62 62 62 6 6 6 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 7 7 7 66 66 66 5 5 8 122 122 122 122 122 122 +9 9 11 3 3 6 104 96 81 179 179 179 122 122 122 13 13 13 +2 2 6 2 2 6 67 67 67 10 10 10 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 7 7 7 65 65 65 41 41 43 152 149 142 192 191 189 +48 48 49 23 23 24 228 210 210 86 86 86 192 191 189 59 59 61 +2 2 6 2 2 6 64 64 64 14 14 14 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 7 7 7 66 66 66 59 59 59 59 59 61 86 86 86 +99 84 50 78 66 28 152 149 142 5 5 8 122 122 122 104 96 81 +2 2 6 2 2 6 67 67 67 14 14 14 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 5 5 5 63 63 63 24 24 24 152 149 142 175 122 13 +238 184 12 220 170 13 226 181 52 112 86 32 194 165 151 46 46 47 +2 2 6 2 2 6 65 65 65 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 5 5 5 59 59 59 21 21 21 175 122 13 231 174 11 +240 192 13 237 183 61 240 192 13 240 192 13 234 179 16 81 64 9 +2 2 6 2 2 6 63 63 63 25 25 25 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 5 5 5 54 54 54 51 48 39 189 138 9 238 184 12 +240 192 13 240 192 13 240 192 13 215 161 11 207 152 19 81 64 9 +16 16 18 5 5 8 40 40 40 44 44 44 4 4 4 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 5 5 5 59 59 59 27 27 27 126 107 64 187 136 12 +220 170 13 201 147 20 189 138 9 198 154 46 199 182 125 70 70 70 +27 27 27 104 96 81 12 12 14 70 70 70 16 16 16 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 17 17 17 70 70 70 12 12 12 168 168 168 174 135 135 +175 122 13 175 122 13 178 151 83 192 191 189 233 233 233 179 179 179 +3 3 6 29 29 31 3 3 6 41 41 41 44 44 44 5 5 5 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +8 8 8 53 53 53 44 44 44 59 59 59 238 238 238 192 191 189 +192 191 189 192 191 189 221 205 205 240 240 240 253 253 253 253 253 253 +70 70 70 2 2 6 2 2 6 5 5 8 67 67 67 22 22 22 +2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 5 +38 38 38 56 56 56 7 7 9 221 205 205 253 253 253 233 233 233 +221 205 205 233 233 233 251 251 251 253 253 253 253 253 253 253 253 253 +192 191 189 2 2 6 2 2 6 2 2 6 25 25 25 64 64 64 +15 15 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 27 27 27 +66 66 66 7 7 9 86 86 86 252 252 252 253 253 253 253 253 253 +252 252 252 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +244 244 244 19 19 21 2 2 6 2 2 6 2 2 6 38 38 38 +54 54 54 10 10 10 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 14 14 14 62 62 62 +10 10 12 3 3 6 122 122 122 235 235 235 251 251 251 248 248 248 +235 235 235 248 248 248 252 252 252 246 246 246 233 233 233 237 228 228 +223 207 207 70 70 70 2 2 6 2 2 6 2 2 6 2 2 6 +46 46 47 38 38 38 4 4 4 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 2 2 2 33 33 33 44 44 44 +4 4 7 9 9 11 168 168 168 240 240 240 252 252 252 252 252 252 +246 246 246 253 253 253 253 253 253 251 251 251 245 241 241 233 233 233 +221 205 205 192 191 189 29 29 31 27 27 27 9 9 12 2 2 6 +3 3 6 65 65 65 15 15 15 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 6 6 6 59 59 59 19 19 21 +24 24 24 86 86 86 249 249 249 253 253 253 253 253 253 253 253 253 +253 253 253 228 210 210 241 230 230 253 253 253 253 253 253 253 253 253 +251 251 251 228 210 210 152 149 142 5 5 8 27 27 27 4 4 7 +2 2 6 46 46 47 34 34 34 2 2 2 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 16 16 16 67 67 67 19 19 21 +12 12 14 223 207 207 254 20 20 254 20 20 253 127 127 242 223 223 +254 20 20 253 127 127 254 48 48 242 223 223 254 86 86 254 20 20 +254 20 20 253 137 137 233 233 233 32 32 32 35 35 35 23 23 24 +2 2 6 15 15 15 60 60 60 6 6 6 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 4 4 4 38 38 38 48 48 49 22 22 22 +86 86 86 253 253 253 254 20 20 241 230 230 227 216 186 253 137 137 +253 137 137 253 253 253 253 137 137 253 137 137 254 48 48 253 253 253 +253 253 253 253 253 253 253 253 253 62 62 62 2 2 6 23 23 24 +2 2 6 2 2 6 62 62 62 17 17 17 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 14 14 14 70 70 70 14 14 14 16 16 18 +179 179 179 253 253 253 227 216 186 254 48 48 240 219 160 253 127 127 +254 20 20 253 137 137 254 86 86 231 203 141 254 20 20 254 20 20 +253 137 137 253 253 253 253 253 253 104 96 81 2 2 6 23 23 24 +2 2 6 2 2 6 46 46 47 27 27 27 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 4 4 4 39 39 39 42 42 43 19 19 21 13 13 13 +228 210 210 242 223 223 253 253 253 242 223 223 253 127 127 253 127 127 +253 127 127 253 127 127 253 137 137 253 253 253 254 48 48 253 253 253 +228 210 210 253 253 253 253 253 253 122 122 122 2 2 6 19 19 19 +2 2 6 2 2 6 39 39 39 38 38 38 3 3 3 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 8 8 8 60 60 60 3 3 6 33 33 33 38 38 38 +253 137 137 254 86 86 253 137 137 254 86 86 253 137 137 209 197 168 +253 127 127 253 253 253 253 253 253 253 253 253 253 127 127 254 86 86 +254 86 86 253 137 137 253 253 253 122 122 122 2 2 6 17 17 17 +2 2 6 2 2 6 34 34 36 42 42 43 3 3 3 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 13 13 13 59 59 59 2 2 6 9 9 12 56 56 56 +252 252 252 240 219 160 253 137 137 240 219 160 253 253 253 237 228 228 +254 86 86 253 253 253 253 253 253 253 253 253 253 253 253 242 223 223 +227 216 186 249 249 249 253 253 253 122 122 122 16 16 17 17 17 17 +12 12 14 3 3 6 39 39 39 38 38 38 3 3 3 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 2 2 +5 5 5 22 22 22 104 96 81 187 136 12 207 152 19 51 48 39 +221 205 205 253 253 253 253 253 253 253 253 253 253 253 253 240 240 240 +250 247 243 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 250 247 243 240 219 160 99 84 50 5 5 8 2 2 6 +7 7 9 46 46 47 58 58 58 35 35 35 3 3 3 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 8 8 8 33 33 33 +58 58 58 86 86 86 170 136 53 239 182 13 246 190 14 220 170 13 +44 38 29 179 179 179 253 253 253 253 253 253 253 253 253 240 240 240 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +253 253 253 240 219 160 240 192 13 112 86 32 2 2 6 2 2 6 +3 3 6 41 33 20 220 170 13 53 53 53 4 4 4 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 2 2 2 32 32 32 150 116 44 +215 161 11 215 161 11 228 170 11 245 188 14 246 190 14 246 190 14 +187 136 12 9 9 11 122 122 122 251 251 251 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +248 248 248 211 196 135 239 182 13 175 122 13 6 5 6 2 2 6 +16 14 12 187 136 12 238 184 12 84 78 65 10 10 10 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 4 4 4 53 53 53 207 152 19 +242 185 13 245 188 14 246 190 14 246 190 14 246 190 14 246 190 14 +240 192 13 81 64 9 2 2 6 86 86 86 244 244 244 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +233 233 233 199 182 125 231 174 11 207 152 19 175 122 13 175 122 13 +201 147 20 239 182 13 244 187 14 150 116 44 35 35 35 6 6 6 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 5 5 5 53 53 53 201 147 20 +242 185 13 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 220 170 13 13 11 10 2 2 6 152 149 142 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +235 235 235 199 182 125 228 170 11 234 177 12 226 168 11 226 168 11 +234 177 12 246 190 14 246 190 14 234 179 16 126 107 64 36 36 36 +6 6 6 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 3 3 3 48 48 49 189 142 35 +242 185 13 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 140 112 39 36 36 36 192 191 189 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 253 +192 191 189 112 86 32 226 168 11 244 187 14 244 187 14 244 187 14 +245 188 14 246 190 14 246 190 14 246 190 14 242 185 13 150 116 44 +27 27 27 2 2 2 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 6 6 6 58 58 58 189 142 35 +239 182 13 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 239 188 14 209 197 168 253 253 253 253 253 253 +253 253 253 253 253 253 253 253 253 253 253 253 252 252 252 168 168 168 +16 16 18 97 67 8 228 170 11 245 188 14 246 190 14 246 190 14 +246 190 14 246 190 14 246 190 14 246 190 14 244 187 14 198 154 46 +35 35 35 3 3 3 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 13 13 13 84 78 65 215 161 11 +244 187 14 246 190 14 246 190 14 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 238 184 12 187 136 12 168 168 168 244 244 244 +253 253 253 252 252 252 240 240 240 179 179 179 67 67 67 2 2 6 +2 2 6 97 67 8 228 170 11 246 190 14 246 190 14 246 190 14 +246 190 14 246 190 14 245 188 14 234 177 12 189 142 35 86 77 61 +16 16 16 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 13 13 13 103 92 56 207 152 19 +228 170 11 234 177 12 239 182 13 242 186 14 245 188 14 246 190 14 +246 190 14 246 190 14 239 182 13 189 138 9 41 33 20 10 10 12 +30 30 31 23 23 24 5 5 8 2 2 6 2 2 6 2 2 6 +4 4 6 112 86 32 215 161 11 245 188 14 246 190 14 245 188 14 +239 182 13 228 170 11 189 142 35 104 96 81 48 48 49 17 17 17 +2 2 2 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 5 5 5 39 39 39 103 92 56 +141 109 44 175 122 13 187 136 12 189 138 9 207 152 19 228 170 11 +239 182 13 239 182 13 215 161 11 175 122 13 41 33 20 2 2 6 +15 15 17 20 20 22 20 20 22 20 20 22 20 20 22 8 8 10 +4 4 6 97 67 8 189 138 9 231 174 11 239 182 13 226 168 11 +189 138 9 126 107 64 59 59 59 21 21 21 5 5 5 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 5 5 5 17 17 17 +34 34 34 57 57 57 84 78 65 103 92 56 125 101 41 140 112 39 +175 122 13 175 122 13 175 122 13 97 67 8 72 67 58 84 78 65 +60 60 60 56 56 56 56 56 56 56 56 56 57 57 57 65 65 65 +86 86 86 95 73 34 175 122 13 187 136 12 187 136 12 175 122 13 +103 92 56 41 41 41 10 10 10 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +2 2 2 4 4 4 12 12 12 24 24 24 40 40 40 70 70 70 +86 77 61 95 73 34 88 72 41 72 67 58 36 36 36 10 10 10 +5 5 5 5 5 5 5 5 5 4 4 4 5 5 5 6 6 6 +22 22 22 61 61 59 88 72 41 112 86 32 112 86 32 84 78 65 +32 32 32 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 3 3 3 10 10 10 +21 21 21 33 33 33 31 31 31 16 16 16 2 2 2 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +2 2 2 12 12 12 30 30 31 40 40 40 32 32 32 16 16 16 +2 2 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 Index: linux-2.6/drivers/video/ps3fb.c =================================================================== --- linux-2.6.orig/drivers/video/ps3fb.c +++ linux-2.6/drivers/video/ps3fb.c @@ -48,12 +48,6 @@ #define DEVICE_NAME "ps3fb" -#ifdef PS3FB_DEBUG -#define DPRINTK(fmt, args...) printk("%s: " fmt, __func__ , ##args) -#else -#define DPRINTK(fmt, args...) -#endif - #define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC 0x101 #define L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP 0x102 #define L1GPU_CONTEXT_ATTRIBUTE_FB_SETUP 0x600 @@ -316,7 +310,7 @@ static int ps3fb_get_res_table(u32 xres, f = ps3fb_res[i].type; if (!x) { - DPRINTK("ERROR: ps3fb_get_res_table()\n"); + pr_debug("ERROR: ps3fb_get_res_table()\n"); return -1; } @@ -357,11 +351,11 @@ static unsigned int ps3fb_find_mode(cons /* Full broadcast modes have the full mode bit set */ mode = i > 12 ? (i - 12) | PS3FB_FULL_MODE_BIT : i + 1; - DPRINTK("ps3fb_find_mode: mode %u\n", mode); + pr_debug("ps3fb_find_mode: mode %u\n", mode); return mode; } - DPRINTK("ps3fb_find_mode: mode not found\n"); + pr_debug("ps3fb_find_mode: mode not found\n"); return 0; } @@ -384,7 +378,7 @@ static const struct fb_videomode *ps3fb_ return &ps3fb_modedb[mode - 1]; } -static int ps3fb_sync(u32 frame) +static int ps3fb_sync(struct fb_info *info, u32 frame) { int i, status; u32 xres, yres; @@ -395,8 +389,8 @@ static int ps3fb_sync(u32 frame) yres = ps3fb_res[i].yres; if (frame > ps3fb.num_frames - 1) { - printk(KERN_WARNING "%s: invalid frame number (%u)\n", - __func__, frame); + dev_warn(info->device, "%s: invalid frame number (%u)\n", + __func__, frame); return -EINVAL; } offset = xres * yres * BPP * frame; @@ -409,26 +403,26 @@ static int ps3fb_sync(u32 frame) (xres << 16) | yres, xres * BPP); /* line_length */ if (status) - printk(KERN_ERR - "%s: lv1_gpu_context_attribute FB_BLIT failed: %d\n", - __func__, status); + dev_err(info->device, + "%s: lv1_gpu_context_attribute FB_BLIT failed: %d\n", + __func__, status); #ifdef HEAD_A status = lv1_gpu_context_attribute(ps3fb.context_handle, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 0, offset, 0, 0); if (status) - printk(KERN_ERR - "%s: lv1_gpu_context_attribute FLIP failed: %d\n", - __func__, status); + dev_err(info->device, + "%s: lv1_gpu_context_attribute FLIP failed: %d\n", + __func__, status); #endif #ifdef HEAD_B status = lv1_gpu_context_attribute(ps3fb.context_handle, L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_FLIP, 1, offset, 0, 0); if (status) - printk(KERN_ERR - "%s: lv1_gpu_context_attribute FLIP failed: %d\n", - __func__, status); + dev_err(info->device, + "%s: lv1_gpu_context_attribute FLIP failed: %d\n", + __func__, status); #endif return 0; } @@ -445,7 +439,7 @@ static int ps3fb_release(struct fb_info if (atomic_dec_and_test(&ps3fb.f_count)) { if (atomic_read(&ps3fb.ext_flip)) { atomic_set(&ps3fb.ext_flip, 0); - ps3fb_sync(0); /* single buffer */ + ps3fb_sync(info, 0); /* single buffer */ } } return 0; @@ -465,8 +459,10 @@ static int ps3fb_check_var(struct fb_var int mode; int i; - DPRINTK("var->xres:%u info->var.xres:%u\n", var->xres, info->var.xres); - DPRINTK("var->yres:%u info->var.yres:%u\n", var->yres, info->var.yres); + dev_dbg(info->device, "var->xres:%u info->var.xres:%u\n", var->xres, + info->var.xres); + dev_dbg(info->device, "var->yres:%u info->var.yres:%u\n", var->yres, + info->var.yres); /* FIXME For now we do exact matches only */ mode = ps3fb_find_mode(var, &line_length); @@ -487,7 +483,8 @@ static int ps3fb_check_var(struct fb_var /* Virtual screen and panning are not supported */ if (var->xres_virtual > var->xres || var->yres_virtual > var->yres || var->xoffset || var->yoffset) { - DPRINTK("Virtual screen and panning are not supported\n"); + dev_dbg(info->device, + "Virtual screen and panning are not supported\n"); return -EINVAL; } @@ -502,7 +499,7 @@ static int ps3fb_check_var(struct fb_var var->blue.length > 8 || var->transp.length > 8 || var->red.msb_right || var->green.msb_right || var->blue.msb_right || var->transp.msb_right || var->nonstd) { - DPRINTK("We support ARGB8888 only\n"); + dev_dbg(info->device, "We support ARGB8888 only\n"); return -EINVAL; } @@ -522,14 +519,14 @@ static int ps3fb_check_var(struct fb_var /* Rotation is not supported */ if (var->rotate) { - DPRINTK("Rotation is not supported\n"); + dev_dbg(info->device, "Rotation is not supported\n"); return -EINVAL; } /* Memory limit */ i = ps3fb_get_res_table(var->xres, var->yres); if (ps3fb_res[i].xres*ps3fb_res[i].yres*BPP > ps3fb_videomemory.size) { - DPRINTK("Not enough memory\n"); + dev_dbg(info->device, "Not enough memory\n"); return -ENOMEM; } @@ -550,7 +547,7 @@ static int ps3fb_set_par(struct fb_info unsigned long offset; static int first = 1; - DPRINTK("xres:%d xv:%d yres:%d yv:%d clock:%d\n", + dev_dbg(info->device, "xres:%d xv:%d yres:%d yv:%d clock:%d\n", info->var.xres, info->var.xres_virtual, info->var.yres, info->var.yres_virtual, info->var.pixclock); i = ps3fb_get_res_table(info->var.xres, info->var.yres); @@ -624,8 +621,8 @@ static int ps3fb_mmap(struct fb_info *in size, vma->vm_page_prot)) return -EAGAIN; - printk(KERN_DEBUG "ps3fb: mmap framebuffer P(%lx)->V(%lx)\n", offset, - vma->vm_start); + dev_dbg(info->device, "ps3fb: mmap framebuffer P(%lx)->V(%lx)\n", + offset, vma->vm_start); return 0; } @@ -637,7 +634,7 @@ static int ps3fb_blank(int blank, struct { int retval; - DPRINTK("%s: blank:%d\n", __func__, blank); + dev_dbg(info->device, "%s: blank:%d\n", __func__, blank); switch (blank) { case FB_BLANK_POWERDOWN: case FB_BLANK_HSYNC_SUSPEND: @@ -706,7 +703,7 @@ static int ps3fb_ioctl(struct fb_info *i case FBIOGET_VBLANK: { struct fb_vblank vblank; - DPRINTK("FBIOGET_VBLANK:\n"); + dev_dbg(info->device, "FBIOGET_VBLANK:\n"); retval = ps3fb_get_vblank(&vblank); if (retval) break; @@ -719,7 +716,7 @@ static int ps3fb_ioctl(struct fb_info *i case FBIO_WAITFORVSYNC: { u32 crt; - DPRINTK("FBIO_WAITFORVSYNC:\n"); + dev_dbg(info->device, "FBIO_WAITFORVSYNC:\n"); if (get_user(crt, (u32 __user *) arg)) break; @@ -740,7 +737,7 @@ static int ps3fb_ioctl(struct fb_info *i if (id > 0) val = (val & ~PS3AV_MODE_MASK) | id; } - DPRINTK("PS3FB_IOCTL_SETMODE:%x\n", val); + dev_dbg(info->device, "PS3FB_IOCTL_SETMODE:%x\n", val); retval = -EINVAL; old_mode = ps3fb_mode; ps3fb_mode = val; @@ -763,7 +760,7 @@ static int ps3fb_ioctl(struct fb_info *i case PS3FB_IOCTL_GETMODE: val = ps3av_get_mode(); - DPRINTK("PS3FB_IOCTL_GETMODE:%x\n", val); + dev_dbg(info->device, "PS3FB_IOCTL_GETMODE:%x\n", val); if (!copy_to_user(argp, &val, sizeof(val))) retval = 0; break; @@ -772,7 +769,7 @@ static int ps3fb_ioctl(struct fb_info *i { struct ps3fb_ioctl_res res; int i = ps3fb.res_index; - DPRINTK("PS3FB_IOCTL_SCREENINFO:\n"); + dev_dbg(info->device, "PS3FB_IOCTL_SCREENINFO:\n"); res.xres = ps3fb_res[i].xres; res.yres = ps3fb_res[i].yres; res.xoff = ps3fb_res[i].xoff; @@ -784,13 +781,13 @@ static int ps3fb_ioctl(struct fb_info *i } case PS3FB_IOCTL_ON: - DPRINTK("PS3FB_IOCTL_ON:\n"); + dev_dbg(info->device, "PS3FB_IOCTL_ON:\n"); atomic_inc(&ps3fb.ext_flip); retval = 0; break; case PS3FB_IOCTL_OFF: - DPRINTK("PS3FB_IOCTL_OFF:\n"); + dev_dbg(info->device, "PS3FB_IOCTL_OFF:\n"); atomic_dec_if_positive(&ps3fb.ext_flip); retval = 0; break; @@ -799,8 +796,8 @@ static int ps3fb_ioctl(struct fb_info *i if (copy_from_user(&val, argp, sizeof(val))) break; - DPRINTK("PS3FB_IOCTL_FSEL:%d\n", val); - retval = ps3fb_sync(val); + dev_dbg(info->device, "PS3FB_IOCTL_FSEL:%d\n", val); + retval = ps3fb_sync(info, val); break; default: @@ -812,12 +809,14 @@ static int ps3fb_ioctl(struct fb_info *i static int ps3fbd(void *arg) { + struct fb_info *info = arg; + while (!kthread_should_stop()) { try_to_freeze(); set_current_state(TASK_INTERRUPTIBLE); if (ps3fb.is_kicked) { ps3fb.is_kicked = 0; - ps3fb_sync(0); /* single buffer */ + ps3fb_sync(info, 0); /* single buffer */ } schedule(); } @@ -826,14 +825,15 @@ static int ps3fbd(void *arg) static irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr) { + struct ps3_system_bus_device *dev = ptr; u64 v1; int status; struct display_head *head = &ps3fb.dinfo->display_head[1]; status = lv1_gpu_context_intr(ps3fb.context_handle, &v1); if (status) { - printk(KERN_ERR "%s: lv1_gpu_context_intr failed: %d\n", - __func__, status); + dev_err(&dev->core, "%s: lv1_gpu_context_intr failed: %d\n", + __func__, status); return IRQ_NONE; } @@ -857,31 +857,32 @@ static int ps3fb_vsync_settings(struct g { int error; - DPRINTK("version_driver:%x\n", dinfo->version_driver); - DPRINTK("irq outlet:%x\n", dinfo->irq.irq_outlet); - DPRINTK("version_gpu:%x memory_size:%x ch:%x core_freq:%d mem_freq:%d\n", + dev_dbg(&dev->core, "version_driver:%x\n", dinfo->version_driver); + dev_dbg(&dev->core, "irq outlet:%x\n", dinfo->irq.irq_outlet); + dev_dbg(&dev->core, + "version_gpu:%x memory_size:%x ch:%x core_freq:%d mem_freq:%d\n", dinfo->version_gpu, dinfo->memory_size, dinfo->hardware_channel, dinfo->nvcore_frequency/1000000, dinfo->memory_frequency/1000000); if (dinfo->version_driver != GPU_DRIVER_INFO_VERSION) { - printk(KERN_ERR "%s: version_driver err:%x\n", __func__, - dinfo->version_driver); + dev_err(&dev->core, "%s: version_driver err:%x\n", __func__, + dinfo->version_driver); return -EINVAL; } error = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet, &ps3fb.irq_no); if (error) { - printk(KERN_ERR "%s: ps3_alloc_irq failed %d\n", __func__, - error); + dev_err(&dev->core, "%s: ps3_alloc_irq failed %d\n", __func__, + error); return error; } error = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt, IRQF_DISABLED, DEVICE_NAME, dev); if (error) { - printk(KERN_ERR "%s: request_irq failed %d\n", __func__, - error); + dev_err(&dev->core, "%s: request_irq failed %d\n", __func__, + error); ps3_irq_plug_destroy(ps3fb.irq_no); return error; } @@ -891,18 +892,19 @@ static int ps3fb_vsync_settings(struct g return 0; } -static int ps3fb_xdr_settings(u64 xdr_lpar) +static int ps3fb_xdr_settings(u64 xdr_lpar, struct ps3_system_bus_device *dev) { int status; status = lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, xdr_lpar, ps3fb_videomemory.size, 0); if (status) { - printk(KERN_ERR "%s: lv1_gpu_context_iomap failed: %d\n", - __func__, status); + dev_err(&dev->core, "%s: lv1_gpu_context_iomap failed: %d\n", + __func__, status); return -ENXIO; } - DPRINTK("video:%p xdr_ea:%p ioif:%lx lpar:%lx phys:%lx size:%lx\n", + dev_dbg(&dev->core, + "video:%p xdr_ea:%p ioif:%lx lpar:%lx phys:%lx size:%lx\n", ps3fb_videomemory.address, ps3fb.xdr_ea, GPU_IOIF, xdr_lpar, virt_to_abs(ps3fb.xdr_ea), ps3fb_videomemory.size); @@ -911,9 +913,9 @@ static int ps3fb_xdr_settings(u64 xdr_lp xdr_lpar, ps3fb_videomemory.size, GPU_IOIF, 0); if (status) { - printk(KERN_ERR - "%s: lv1_gpu_context_attribute FB_SETUP failed: %d\n", - __func__, status); + dev_err(&dev->core, + "%s: lv1_gpu_context_attribute FB_SETUP failed: %d\n", + __func__, status); return -ENXIO; } return 0; @@ -943,7 +945,7 @@ static struct fb_fix_screeninfo ps3fb_fi .accel = FB_ACCEL_NONE, }; -static int ps3fb_set_sync(void) +static int ps3fb_set_sync(struct ps3_system_bus_device *dev) { int status; @@ -952,8 +954,8 @@ static int ps3fb_set_sync(void) L1GPU_CONTEXT_ATTRIBUTE_DISPLAY_SYNC, 0, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); if (status) { - printk(KERN_ERR "%s: lv1_gpu_context_attribute DISPLAY_SYNC " - "failed: %d\n", __func__, status); + dev_err(&dev->core, "%s: lv1_gpu_context_attribute " + "DISPLAY_SYNC failed: %d\n", __func__, status); return -1; } #endif @@ -963,8 +965,8 @@ static int ps3fb_set_sync(void) 1, L1GPU_DISPLAY_SYNC_VSYNC, 0, 0); if (status) { - printk(KERN_ERR "%s: lv1_gpu_context_attribute DISPLAY_MODE " - "failed: %d\n", __func__, status); + dev_err(&dev->core, "%s: lv1_gpu_context_attribute " + "DISPLAY_SYNC failed: %d\n", __func__, status); return -1; } #endif @@ -988,18 +990,19 @@ static int __devinit ps3fb_probe(struct status = ps3_open_hv_device(dev); if (status) { - printk(KERN_ERR "%s: ps3_open_hv_device failed\n", __func__); + dev_err(&dev->core, "%s: ps3_open_hv_device failed\n", + __func__); goto err; } if (!ps3fb_mode) ps3fb_mode = ps3av_get_mode(); - DPRINTK("ps3av_mode:%d\n", ps3fb_mode); + dev_dbg(&dev->core, "ps3av_mode:%d\n", ps3fb_mode); if (ps3fb_mode > 0 && !ps3av_video_mode2res(ps3fb_mode, &xres, &yres)) { ps3fb.res_index = ps3fb_get_res_table(xres, yres); - DPRINTK("res_index:%d\n", ps3fb.res_index); + dev_dbg(&dev->core, "res_index:%d\n", ps3fb.res_index); } else ps3fb.res_index = GPU_RES_INDEX; @@ -1008,32 +1011,33 @@ static int __devinit ps3fb_probe(struct init_waitqueue_head(&ps3fb.wait_vsync); ps3fb.num_frames = 1; - ps3fb_set_sync(); + ps3fb_set_sync(dev); /* get gpu context handle */ status = lv1_gpu_memory_allocate(DDR_SIZE, 0, 0, 0, 0, &ps3fb.memory_handle, &ddr_lpar); if (status) { - printk(KERN_ERR "%s: lv1_gpu_memory_allocate failed: %d\n", - __func__, status); + dev_err(&dev->core, "%s: lv1_gpu_memory_allocate failed: %d\n", + __func__, status); goto err; } - DPRINTK("ddr:lpar:0x%lx\n", ddr_lpar); + dev_dbg(&dev->core, "ddr:lpar:0x%lx\n", ddr_lpar); status = lv1_gpu_context_allocate(ps3fb.memory_handle, 0, &ps3fb.context_handle, &lpar_dma_control, &lpar_driver_info, &lpar_reports, &lpar_reports_size); if (status) { - printk(KERN_ERR "%s: lv1_gpu_context_attribute failed: %d\n", - __func__, status); + dev_err(&dev->core, + "%s: lv1_gpu_context_attribute failed: %d\n", __func__, + status); goto err_gpu_memory_free; } /* vsync interrupt */ ps3fb.dinfo = ioremap(lpar_driver_info, 128 * 1024); if (!ps3fb.dinfo) { - printk(KERN_ERR "%s: ioremap failed\n", __func__); + dev_err(&dev->core, "%s: ioremap failed\n", __func__); goto err_gpu_context_free; } @@ -1044,7 +1048,7 @@ static int __devinit ps3fb_probe(struct /* xdr frame buffer */ ps3fb.xdr_ea = ps3fb_videomemory.address; xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb.xdr_ea)); - retval = ps3fb_xdr_settings(xdr_lpar); + retval = ps3fb_xdr_settings(xdr_lpar, dev); if (retval) goto err_free_irq; @@ -1087,9 +1091,9 @@ static int __devinit ps3fb_probe(struct dev->core.driver_data = info; - printk(KERN_INFO - "fb%d: PS3 frame buffer device, using %ld KiB of video memory\n", - info->node, ps3fb_videomemory.size >> 10); + dev_info(info->device, "%s %s, using %lu KiB of video memory\n", + dev_driver_string(info->dev), info->dev->bus_id, + ps3fb_videomemory.size >> 10); task = kthread_run(ps3fbd, info, DEVICE_NAME); if (IS_ERR(task)) { @@ -1126,7 +1130,7 @@ static int ps3fb_shutdown(struct ps3_sys int status; struct fb_info *info = dev->core.driver_data; - DPRINTK(" -> %s:%d\n", __func__, __LINE__); + dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); ps3fb_flip_ctl(0, &ps3fb); /* flip off */ ps3fb.dinfo->irq.mask = 0; @@ -1151,14 +1155,16 @@ static int ps3fb_shutdown(struct ps3_sys status = lv1_gpu_context_free(ps3fb.context_handle); if (status) - DPRINTK("lv1_gpu_context_free failed: %d\n", status); + dev_dbg(&dev->core, "lv1_gpu_context_free failed: %d\n", + status); status = lv1_gpu_memory_free(ps3fb.memory_handle); if (status) - DPRINTK("lv1_gpu_memory_free failed: %d\n", status); + dev_dbg(&dev->core, "lv1_gpu_memory_free failed: %d\n", + status); ps3_close_hv_device(dev); - DPRINTK(" <- %s:%d\n", __func__, __LINE__); + dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); return 0; } @@ -1211,9 +1217,9 @@ static int __init ps3fb_init(void) static void __exit ps3fb_exit(void) { - DPRINTK(" -> %s:%d\n", __func__, __LINE__); + pr_debug(" -> %s:%d\n", __func__, __LINE__); ps3_system_bus_driver_unregister(&ps3fb_driver); - DPRINTK(" <- %s:%d\n", __func__, __LINE__); + pr_debug(" <- %s:%d\n", __func__, __LINE__); } module_init(ps3fb_init); Index: linux-2.6/fs/compat_ioctl.c =================================================================== --- linux-2.6.orig/fs/compat_ioctl.c +++ linux-2.6/fs/compat_ioctl.c @@ -63,6 +63,7 @@ #include #include #include +#include #include #include @@ -3489,6 +3490,9 @@ HANDLE_IOCTL(LPSETTIMEOUT, lp_timeout_tr IGNORE_IOCTL(VFAT_IOCTL_READDIR_BOTH32) IGNORE_IOCTL(VFAT_IOCTL_READDIR_SHORT32) + +/* loop */ +IGNORE_IOCTL(LOOP_CLR_FD) }; #define IOCTL_HASHSIZE 256 Index: linux-2.6/include/asm-powerpc/Kbuild =================================================================== --- linux-2.6.orig/include/asm-powerpc/Kbuild +++ linux-2.6/include/asm-powerpc/Kbuild @@ -34,6 +34,7 @@ unifdef-y += elf.h unifdef-y += nvram.h unifdef-y += param.h unifdef-y += posix_types.h +unifdef-y += ps3av.h unifdef-y += ptrace.h unifdef-y += seccomp.h unifdef-y += signal.h Index: linux-2.6/include/asm-powerpc/ps3av.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/ps3av.h +++ linux-2.6/include/asm-powerpc/ps3av.h @@ -324,6 +324,7 @@ #define PS3AV_MODE_RGB 0x0020 +#ifdef __KERNEL__ /** command packet structure **/ struct ps3av_send_hdr { u16 version; @@ -723,4 +724,5 @@ extern void ps3av_register_flip_ctl(void void *flip_data); extern void ps3av_flip_ctl(int on); +#endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PS3AV_H_ */ Index: linux-2.6/include/asm-powerpc/spu.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/spu.h +++ linux-2.6/include/asm-powerpc/spu.h @@ -136,6 +136,7 @@ struct spu { u64 flags; u64 dar; u64 dsisr; + u64 class_0_pending; size_t ls_size; unsigned int slb_replace; struct mm_struct *mm; @@ -144,7 +145,6 @@ struct spu { unsigned long long timestamp; pid_t pid; pid_t tgid; - int class_0_pending; spinlock_t register_lock; void (* wbox_callback)(struct spu *spu); Index: linux-2.6/include/asm-powerpc/spu_priv1.h =================================================================== --- linux-2.6.orig/include/asm-powerpc/spu_priv1.h +++ linux-2.6/include/asm-powerpc/spu_priv1.h @@ -24,6 +24,7 @@ #include struct spu; +struct spu_context; /* access to priv1 registers */ @@ -178,6 +179,8 @@ struct spu_management_ops { int (*enumerate_spus)(int (*fn)(void *data)); int (*create_spu)(struct spu *spu, void *data); int (*destroy_spu)(struct spu *spu); + int (*enable_spu)(struct spu_context *ctx); + int (*disable_spu)(struct spu_context *ctx); }; extern const struct spu_management_ops* spu_management_ops; @@ -200,6 +203,18 @@ spu_destroy_spu (struct spu *spu) return spu_management_ops->destroy_spu(spu); } +static inline int +spu_enable_spu (struct spu_context *ctx) +{ + return spu_management_ops->enable_spu(ctx); +} + +static inline int +spu_disable_spu (struct spu_context *ctx) +{ + return spu_management_ops->disable_spu(ctx); +} + /* * The declarations folowing are put here for convenience * and only intended to be used by the platform setup code. Index: linux-2.6/include/linux/fb.h =================================================================== --- linux-2.6.orig/include/linux/fb.h +++ linux-2.6/include/linux/fb.h @@ -529,6 +529,8 @@ struct fb_cursor_user { #define FB_EVENT_CONBLANK 0x0C /* Get drawing requirements */ #define FB_EVENT_GET_REQ 0x0D +/* Unbind from the console if possible */ +#define FB_EVENT_FB_UNBIND 0x0E struct fb_event { struct fb_info *info; Index: linux-2.6/include/linux/linux_logo.h =================================================================== --- linux-2.6.orig/include/linux/linux_logo.h +++ linux-2.6/include/linux/linux_logo.h @@ -32,6 +32,28 @@ struct linux_logo { const unsigned char *data; }; +extern const struct linux_logo logo_linux_mono; +extern const struct linux_logo logo_linux_vga16; +extern const struct linux_logo logo_linux_clut224; +extern const struct linux_logo logo_dec_clut224; +extern const struct linux_logo logo_mac_clut224; +extern const struct linux_logo logo_parisc_clut224; +extern const struct linux_logo logo_sgi_clut224; +extern const struct linux_logo logo_sun_clut224; +extern const struct linux_logo logo_superh_mono; +extern const struct linux_logo logo_superh_vga16; +extern const struct linux_logo logo_superh_clut224; +extern const struct linux_logo logo_m32r_clut224; +extern const struct linux_logo logo_spe_clut224; + extern const struct linux_logo *fb_find_logo(int depth); +#if defined(CONFIG_LOGO) && defined(CONFIG_FB) +extern void fb_append_extra_logo(const struct linux_logo *logo, + unsigned int n); +#else +static inline void fb_append_extra_logo(const struct linux_logo *logo, + unsigned int n) +{} +#endif #endif /* _LINUX_LINUX_LOGO_H */ Index: linux-2.6/include/linux/vt_kern.h =================================================================== --- linux-2.6.orig/include/linux/vt_kern.h +++ linux-2.6/include/linux/vt_kern.h @@ -75,6 +75,8 @@ int con_copy_unimap(struct vc_data *dst_ int vt_waitactive(int vt); void change_console(struct vc_data *new_vc); void reset_vc(struct vc_data *vc); +extern int unbind_con_driver(const struct consw *csw, int first, int last, + int deflt); /* * vc_screen.c shares this temporary buffer with the console write code so that Index: linux-2.6/include/net/bluetooth/hci.h =================================================================== --- linux-2.6.orig/include/net/bluetooth/hci.h +++ linux-2.6/include/net/bluetooth/hci.h @@ -251,6 +251,12 @@ struct hci_cp_set_event_flt { __u8 condition[0]; } __attribute__ ((packed)); +struct hci_cp_set_event_flt_conn { + __u8 flt_type; + __u8 cond_type; + __u8 condition; +} __attribute__ ((packed)); + /* Filter types */ #define HCI_FLT_CLEAR_ALL 0x00 #define HCI_FLT_INQ_RESULT 0x01 Index: linux-2.6/include/scsi/scsi_host.h =================================================================== --- linux-2.6.orig/include/scsi/scsi_host.h +++ linux-2.6/include/scsi/scsi_host.h @@ -677,6 +677,10 @@ struct Scsi_Host { #define shost_printk(prefix, shost, fmt, a...) \ dev_printk(prefix, &(shost)->shost_gendev, fmt, ##a) +static inline void *shost_priv(struct Scsi_Host *shost) +{ + return (void *)shost->hostdata; +} int scsi_is_host_device(const struct device *); Index: linux-2.6/net/bluetooth/hci_core.c =================================================================== --- linux-2.6.orig/net/bluetooth/hci_core.c +++ linux-2.6/net/bluetooth/hci_core.c @@ -46,6 +46,9 @@ #include #include +#if defined(CONFIG_PPC_PS3) +#include +#endif #ifndef CONFIG_BT_HCI_CORE_DEBUG #undef BT_DBG @@ -239,6 +242,22 @@ static void hci_init_req(struct hci_dev hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, sizeof(cp), &cp); } +#if defined(CONFIG_PPC_PS3) + /* + * The PS3 built-in bluetooth host controler doesn't support the + * HCI_FLT_CLEAR_ALL command properly. Use the HCI_FLT_CONN_SETUP + * command to clear the event filter. + */ + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) { + struct hci_cp_set_event_flt_conn cp; + cp.flt_type = HCI_FLT_CONN_SETUP; + cp.cond_type = 0; /* all devices */ + cp.condition = 1; /* auto accept is off */ + hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, sizeof(cp), + &cp); + } +#endif + /* Page timeout ~20 secs */ param = cpu_to_le16(0x8000); hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_PG_TIMEOUT, 2, ¶m); Index: linux-2.6/scripts/pnmtologo.c =================================================================== --- linux-2.6.orig/scripts/pnmtologo.c +++ linux-2.6/scripts/pnmtologo.c @@ -244,7 +244,7 @@ static void write_header(void) static void write_footer(void) { fputs("\n};\n\n", out); - fprintf(out, "struct linux_logo %s __initdata = {\n", logoname); + fprintf(out, "const struct linux_logo %s __initdata = {\n", logoname); fprintf(out, " .type\t= %s,\n", logo_types[logo_type]); fprintf(out, " .width\t= %d,\n", logo_width); fprintf(out, " .height\t= %d,\n", logo_height); Index: linux-2.6/sound/ppc/Kconfig =================================================================== --- linux-2.6.orig/sound/ppc/Kconfig +++ linux-2.6/sound/ppc/Kconfig @@ -33,3 +33,23 @@ config SND_POWERMAC_AUTO_DRC option. endmenu + +menu "ALSA PowerPC devices" + depends on SND!=n && ( PPC64 || PPC32 ) + +config SND_PS3 + tristate "PS3 Audio support" + depends on SND && PS3_PS3AV + select SND_PCM + default m + help + Say Y here to include support for audio on the PS3 + + To compile this driver as a module, choose M here: the module + will be called snd_ps3. + +config SND_PS3_DEFAULT_START_DELAY + int "Startup delay time in ms" + depends on SND_PS3 + default "2000" +endmenu Index: linux-2.6/sound/ppc/Makefile =================================================================== --- linux-2.6.orig/sound/ppc/Makefile +++ linux-2.6/sound/ppc/Makefile @@ -6,4 +6,5 @@ snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o # Toplevel Module Dependency -obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o +obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o +obj-$(CONFIG_SND_PS3) += snd_ps3.o Index: linux-2.6/sound/ppc/snd_ps3.c =================================================================== --- /dev/null +++ linux-2.6/sound/ppc/snd_ps3.c @@ -0,0 +1,1127 @@ +/* + * Audio support for PS3 + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * All rights reserved. + * Copyright 2006, 2007 Sony Corporation + * + * 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; version 2 of the Licence. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "snd_ps3_reg.h" +#include "snd_ps3.h" + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PS3 sound driver"); +MODULE_AUTHOR("Sony Computer Entertainment Inc."); + +/* module entries */ +static int __init snd_ps3_init(void); +static void __exit snd_ps3_exit(void); + +/* ALSA snd driver ops */ +static int snd_ps3_pcm_open(struct snd_pcm_substream *substream); +static int snd_ps3_pcm_close(struct snd_pcm_substream *substream); +static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream); +static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream, + int cmd); +static snd_pcm_uframes_t snd_ps3_pcm_pointer(struct snd_pcm_substream + *substream); +static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params); +static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream); + + +/* ps3_system_bus_driver entries */ +static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev); +static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev); + +/* address setup */ +static int snd_ps3_map_mmio(void); +static void snd_ps3_unmap_mmio(void); +static int snd_ps3_allocate_irq(void); +static void snd_ps3_free_irq(void); +static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start); + +/* interrupt handler */ +static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id); + + +/* set sampling rate/format */ +static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream); +/* take effect parameter change */ +static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card); +/* initialize avsetting and take it effect */ +static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card); +/* setup dma */ +static int snd_ps3_program_dma(struct snd_ps3_card_info *card, + enum snd_ps3_dma_filltype filltype); +static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card); + +static dma_addr_t v_to_bus(struct snd_ps3_card_info *, void *vaddr, int ch); + + +module_init(snd_ps3_init); +module_exit(snd_ps3_exit); + +/* + * global + */ +static struct snd_ps3_card_info the_card; + +static int snd_ps3_start_delay = CONFIG_SND_PS3_DEFAULT_START_DELAY; + +module_param_named(start_delay, snd_ps3_start_delay, uint, 0644); +MODULE_PARM_DESC(start_delay, "time to insert silent data in milisec"); + +static int index = SNDRV_DEFAULT_IDX1; +static char *id = SNDRV_DEFAULT_STR1; + +module_param(index, int, 0444); +MODULE_PARM_DESC(index, "Index value for PS3 soundchip."); +module_param(id, charp, 0444); +MODULE_PARM_DESC(id, "ID string for PS3 soundchip."); + + +/* + * PS3 audio register access + */ +static inline u32 read_reg(unsigned int reg) +{ + return in_be32(the_card.mapped_mmio_vaddr + reg); +} +static inline void write_reg(unsigned int reg, u32 val) +{ + out_be32(the_card.mapped_mmio_vaddr + reg, val); +} +static inline void update_reg(unsigned int reg, u32 or_val) +{ + u32 newval = read_reg(reg) | or_val; + write_reg(reg, newval); +} +static inline void update_mask_reg(unsigned int reg, u32 mask, u32 or_val) +{ + u32 newval = (read_reg(reg) & mask) | or_val; + write_reg(reg, newval); +} + +/* + * ALSA defs + */ +const static struct snd_pcm_hardware snd_ps3_pcm_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_NONINTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = (SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE), + .rates = (SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000), + .rate_min = 44100, + .rate_max = 96000, + + .channels_min = 2, /* stereo only */ + .channels_max = 2, + + .buffer_bytes_max = PS3_AUDIO_FIFO_SIZE * 64, + + /* interrupt by four stages */ + .period_bytes_min = PS3_AUDIO_FIFO_STAGE_SIZE * 4, + .period_bytes_max = PS3_AUDIO_FIFO_STAGE_SIZE * 4, + + .periods_min = 16, + .periods_max = 32, /* buffer_size_max/ period_bytes_max */ + + .fifo_size = PS3_AUDIO_FIFO_SIZE +}; + +static struct snd_pcm_ops snd_ps3_pcm_spdif_ops = +{ + .open = snd_ps3_pcm_open, + .close = snd_ps3_pcm_close, + .prepare = snd_ps3_pcm_prepare, + .ioctl = snd_pcm_lib_ioctl, + .trigger = snd_ps3_pcm_trigger, + .pointer = snd_ps3_pcm_pointer, + .hw_params = snd_ps3_pcm_hw_params, + .hw_free = snd_ps3_pcm_hw_free +}; + +static int snd_ps3_verify_dma_stop(struct snd_ps3_card_info *card, + int count, int force_stop) +{ + int dma_ch, done, retries, stop_forced = 0; + uint32_t status; + + for (dma_ch = 0; dma_ch < 8; dma_ch ++) { + retries = count; + do { + status = read_reg(PS3_AUDIO_KICK(dma_ch)) & + PS3_AUDIO_KICK_STATUS_MASK; + switch (status) { + case PS3_AUDIO_KICK_STATUS_DONE: + case PS3_AUDIO_KICK_STATUS_NOTIFY: + case PS3_AUDIO_KICK_STATUS_CLEAR: + case PS3_AUDIO_KICK_STATUS_ERROR: + done = 1; + break; + default: + done = 0; + udelay(10); + } + } while (!done && --retries); + if (!retries && force_stop) { + pr_info("%s: DMA ch %d is not stopped.", + __func__, dma_ch); + /* last resort. force to stop dma. + * NOTE: this cause DMA done interrupts + */ + update_reg(PS3_AUDIO_CONFIG, PS3_AUDIO_CONFIG_CLEAR); + stop_forced = 1; + } + } + return stop_forced; +} + +/* + * wait for all dma is done. + * NOTE: caller should reset card->running before call. + * If not, the interrupt handler will re-start DMA, + * then DMA is never stopped. + */ +static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card) +{ + int stop_forced; + /* + * wait for the last dma is done + */ + + /* + * expected maximum DMA done time is 5.7ms + something (DMA itself). + * 5.7ms is from 16bit/sample 2ch 44.1Khz; the time next + * DMA kick event would occur. + */ + stop_forced = snd_ps3_verify_dma_stop(card, 700, 1); + + /* + * clear outstanding interrupts. + */ + update_reg(PS3_AUDIO_INTR_0, 0); + update_reg(PS3_AUDIO_AX_IS, 0); + + /* + *revert CLEAR bit since it will not reset automatically after DMA stop + */ + if (stop_forced) + update_mask_reg(PS3_AUDIO_CONFIG, ~PS3_AUDIO_CONFIG_CLEAR, 0); + /* ensure the hardware sees changes */ + wmb(); +} + +static void snd_ps3_kick_dma(struct snd_ps3_card_info *card) +{ + + update_reg(PS3_AUDIO_KICK(0), PS3_AUDIO_KICK_REQUEST); + /* ensure the hardware sees the change */ + wmb(); +} + +/* + * convert virtual addr to ioif bus addr. + */ +static dma_addr_t v_to_bus(struct snd_ps3_card_info *card, + void * paddr, + int ch) +{ + return card->dma_start_bus_addr[ch] + + (paddr - card->dma_start_vaddr[ch]); +}; + + +/* + * increment ring buffer pointer. + * NOTE: caller must hold write spinlock + */ +static void snd_ps3_bump_buffer(struct snd_ps3_card_info *card, + enum snd_ps3_ch ch, size_t byte_count, + int stage) +{ + if (!stage) + card->dma_last_transfer_vaddr[ch] = + card->dma_next_transfer_vaddr[ch]; + card->dma_next_transfer_vaddr[ch] += byte_count; + if ((card->dma_start_vaddr[ch] + (card->dma_buffer_size / 2)) <= + card->dma_next_transfer_vaddr[ch]) { + card->dma_next_transfer_vaddr[ch] = card->dma_start_vaddr[ch]; + } +} +/* + * setup dmac to send data to audio and attenuate samples on the ring buffer + */ +static int snd_ps3_program_dma(struct snd_ps3_card_info *card, + enum snd_ps3_dma_filltype filltype) +{ + /* this dmac does not support over 4G */ + uint32_t dma_addr; + int fill_stages, dma_ch, stage; + enum snd_ps3_ch ch; + uint32_t ch0_kick_event = 0; /* initialize to mute gcc */ + void *start_vaddr; + unsigned long irqsave; + int silent = 0; + + switch (filltype) { + case SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL: + silent = 1; + /* intentionally fall thru */ + case SND_PS3_DMA_FILLTYPE_FIRSTFILL: + ch0_kick_event = PS3_AUDIO_KICK_EVENT_ALWAYS; + break; + + case SND_PS3_DMA_FILLTYPE_SILENT_RUNNING: + silent = 1; + /* intentionally fall thru */ + case SND_PS3_DMA_FILLTYPE_RUNNING: + ch0_kick_event = PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY; + break; + } + + snd_ps3_verify_dma_stop(card, 700, 0); + fill_stages = 4; + spin_lock_irqsave(&card->dma_lock, irqsave); + for (ch = 0; ch < 2; ch++) { + start_vaddr = card->dma_next_transfer_vaddr[0]; + for (stage = 0; stage < fill_stages; stage ++) { + dma_ch = stage * 2 + ch; + if (silent) + dma_addr = card->null_buffer_start_dma_addr; + else + dma_addr = + v_to_bus(card, + card->dma_next_transfer_vaddr[ch], + ch); + + write_reg(PS3_AUDIO_SOURCE(dma_ch), + (PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY | + dma_addr)); + + /* dst: fixed to 3wire#0 */ + if (ch == 0) + write_reg(PS3_AUDIO_DEST(dma_ch), + (PS3_AUDIO_DEST_TARGET_AUDIOFIFO | + PS3_AUDIO_AO_3W_LDATA(0))); + else + write_reg(PS3_AUDIO_DEST(dma_ch), + (PS3_AUDIO_DEST_TARGET_AUDIOFIFO | + PS3_AUDIO_AO_3W_RDATA(0))); + + /* count always 1 DMA block (1/2 stage = 128 bytes) */ + write_reg(PS3_AUDIO_DMASIZE(dma_ch), 0); + /* bump pointer if needed */ + if (!silent) + snd_ps3_bump_buffer(card, ch, + PS3_AUDIO_DMAC_BLOCK_SIZE, + stage); + + /* kick event */ + if (dma_ch == 0) + write_reg(PS3_AUDIO_KICK(dma_ch), + ch0_kick_event); + else + write_reg(PS3_AUDIO_KICK(dma_ch), + PS3_AUDIO_KICK_EVENT_AUDIO_DMA(dma_ch + - 1) | + PS3_AUDIO_KICK_REQUEST); + } + } + /* ensure the hardware sees the change */ + wmb(); + spin_unlock_irqrestore(&card->dma_lock, irqsave); + + return 0; +} + +/* + * audio mute on/off + * mute_on : 0 output enabled + * 1 mute + */ +static int snd_ps3_mute(int mute_on) +{ + return ps3av_audio_mute(mute_on); +} + +/* + * PCM operators + */ +static int snd_ps3_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + int pcm_index; + + pcm_index = substream->pcm->device; + /* to retrieve substream/runtime in interrupt handler */ + card->substream = substream; + + runtime->hw = snd_ps3_pcm_hw; + + card->start_delay = snd_ps3_start_delay; + + /* mute off */ + snd_ps3_mute(0); /* this function sleep */ + + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + PS3_AUDIO_FIFO_STAGE_SIZE * 4 * 2); + return 0; +}; + +static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + size_t size; + + /* alloc transport buffer */ + size = params_buffer_bytes(hw_params); + snd_pcm_lib_malloc_pages(substream, size); + return 0; +}; + +static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream, + unsigned int delay_ms) +{ + int ret; + int rate ; + + rate = substream->runtime->rate; + ret = snd_pcm_format_size(substream->runtime->format, + rate * delay_ms / 1000) + * substream->runtime->channels; + + pr_debug(KERN_ERR "%s: time=%d rate=%d bytes=%ld, frames=%d, ret=%d\n", + __func__, + delay_ms, + rate, + snd_pcm_format_size(substream->runtime->format, rate), + rate * delay_ms / 1000, + ret); + + return ret; +}; + +static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + unsigned long irqsave; + + if (!snd_ps3_set_avsetting(substream)) { + /* some parameter changed */ + write_reg(PS3_AUDIO_AX_IE, + PS3_AUDIO_AX_IE_ASOBEIE(0) | + PS3_AUDIO_AX_IE_ASOBUIE(0)); + /* + * let SPDIF device re-lock with SPDIF signal, + * start with some silence + */ + card->silent = snd_ps3_delay_to_bytes(substream, + card->start_delay) / + (PS3_AUDIO_FIFO_STAGE_SIZE * 4); /* every 4 times */ + } + + /* restart ring buffer pointer */ + spin_lock_irqsave(&card->dma_lock, irqsave); + { + card->dma_buffer_size = runtime->dma_bytes; + + card->dma_last_transfer_vaddr[SND_PS3_CH_L] = + card->dma_next_transfer_vaddr[SND_PS3_CH_L] = + card->dma_start_vaddr[SND_PS3_CH_L] = + runtime->dma_area; + card->dma_start_bus_addr[SND_PS3_CH_L] = runtime->dma_addr; + + card->dma_last_transfer_vaddr[SND_PS3_CH_R] = + card->dma_next_transfer_vaddr[SND_PS3_CH_R] = + card->dma_start_vaddr[SND_PS3_CH_R] = + runtime->dma_area + (runtime->dma_bytes / 2); + card->dma_start_bus_addr[SND_PS3_CH_R] = + runtime->dma_addr + (runtime->dma_bytes / 2); + + pr_debug("%s: vaddr=%p bus=%#lx\n", __func__, + card->dma_start_vaddr[SND_PS3_CH_L], + card->dma_start_bus_addr[SND_PS3_CH_L]); + + } + spin_unlock_irqrestore(&card->dma_lock, irqsave); + + /* ensure the hardware sees the change */ + mb(); + + return 0; +}; + +static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* clear outstanding interrupts */ + update_reg(PS3_AUDIO_AX_IS, 0); + + spin_lock(&card->dma_lock); + { + card->running = 1; + } + spin_unlock(&card->dma_lock); + + snd_ps3_program_dma(card, + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); + snd_ps3_kick_dma(card); + while (read_reg(PS3_AUDIO_KICK(7)) & + PS3_AUDIO_KICK_STATUS_MASK) { + udelay(1); + } + snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_RUNNING); + snd_ps3_kick_dma(card); + break; + + case SNDRV_PCM_TRIGGER_STOP: + spin_lock(&card->dma_lock); + { + card->running = 0; + } + spin_unlock(&card->dma_lock); + snd_ps3_wait_for_dma_stop(card); + break; + default: + break; + + } + + return ret; +}; + +/* + * report current pointer + */ +static snd_pcm_uframes_t snd_ps3_pcm_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + size_t bytes; + snd_pcm_uframes_t ret; + + spin_lock(&card->dma_lock); + { + bytes = (size_t)(card->dma_last_transfer_vaddr[SND_PS3_CH_L] - + card->dma_start_vaddr[SND_PS3_CH_L]); + } + spin_unlock(&card->dma_lock); + + ret = bytes_to_frames(substream->runtime, bytes * 2); + + return ret; +}; + +static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream) +{ + int ret; + ret = snd_pcm_lib_free_pages(substream); + return ret; +}; + +static int snd_ps3_pcm_close(struct snd_pcm_substream *substream) +{ + /* mute on */ + snd_ps3_mute(1); + return 0; +}; + +static void snd_ps3_audio_fixup(struct snd_ps3_card_info *card) +{ + /* + * avsetting driver seems to never change the followings + * so, init them here once + */ + + /* no dma interrupt needed */ + write_reg(PS3_AUDIO_INTR_EN_0, 0); + + /* use every 4 buffer empty interrupt */ + update_mask_reg(PS3_AUDIO_AX_IC, + PS3_AUDIO_AX_IC_AASOIMD_MASK, + PS3_AUDIO_AX_IC_AASOIMD_EVERY4); + + /* enable 3wire clocks */ + update_mask_reg(PS3_AUDIO_AO_3WMCTRL, + ~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED | + PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED), + 0); + update_reg(PS3_AUDIO_AO_3WMCTRL, + PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT); +} + +/* + * av setting + * NOTE: calling this function may generate audio interrupt. + */ +static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card) +{ + int ret, retries, i; + pr_debug("%s: start\n", __func__); + + ret = ps3av_set_audio_mode(card->avs.avs_audio_ch, + card->avs.avs_audio_rate, + card->avs.avs_audio_width, + card->avs.avs_audio_format, + card->avs.avs_audio_source); + /* + * Reset the following unwanted settings: + */ + + /* disable all 3wire buffers */ + update_mask_reg(PS3_AUDIO_AO_3WMCTRL, + ~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) | + PS3_AUDIO_AO_3WMCTRL_ASOEN(1) | + PS3_AUDIO_AO_3WMCTRL_ASOEN(2) | + PS3_AUDIO_AO_3WMCTRL_ASOEN(3)), + 0); + wmb(); /* ensure the hardware sees the change */ + /* wait for actually stopped */ + retries = 1000; + while ((read_reg(PS3_AUDIO_AO_3WMCTRL) & + (PS3_AUDIO_AO_3WMCTRL_ASORUN(0) | + PS3_AUDIO_AO_3WMCTRL_ASORUN(1) | + PS3_AUDIO_AO_3WMCTRL_ASORUN(2) | + PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) && + --retries) { + udelay(1); + } + + /* reset buffer pointer */ + for (i = 0; i < 4; i++) { + update_reg(PS3_AUDIO_AO_3WCTRL(i), + PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET); + udelay(10); + } + wmb(); /* ensure the hardware actually start resetting */ + + /* enable 3wire#0 buffer */ + update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0)); + + + /* In 24bit mode,ALSA inserts a zero byte at first byte of per sample */ + update_mask_reg(PS3_AUDIO_AO_3WCTRL(0), + ~PS3_AUDIO_AO_3WCTRL_ASODF, + PS3_AUDIO_AO_3WCTRL_ASODF_LSB); + update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0), + ~PS3_AUDIO_AO_SPDCTRL_SPODF, + PS3_AUDIO_AO_SPDCTRL_SPODF_LSB); + /* ensure all the setting above is written back to register */ + wmb(); + /* avsetting driver altered AX_IE, caller must reset it if you want */ + pr_debug("%s: end\n", __func__); + return ret; +} + +static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card) +{ + int ret; + pr_debug("%s: start\n", __func__); + card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2; + card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; + card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; + card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM; + card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL; + + ret = snd_ps3_change_avsetting(card); + + snd_ps3_audio_fixup(card); + + /* to start to generate SPDIF signal, fill data */ + snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); + snd_ps3_kick_dma(card); + pr_debug("%s: end\n", __func__); + return ret; +} + +/* + * set sampling rate according to the substream + */ +static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream) +{ + struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); + struct snd_ps3_avsetting_info avs; + + avs = card->avs; + + pr_debug("%s: called freq=%d width=%d\n", __func__, + substream->runtime->rate, + snd_pcm_format_width(substream->runtime->format)); + + pr_debug("%s: before freq=%d width=%d\n", __func__, + card->avs.avs_audio_rate, card->avs.avs_audio_width); + + /* sample rate */ + switch (substream->runtime->rate) { + case 44100: + avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K; + break; + case 48000: + avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; + break; + case 88200: + avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K; + break; + case 96000: + avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K; + break; + default: + pr_info("%s: invalid rate %d\n", __func__, + substream->runtime->rate); + return 1; + } + + /* width */ + switch (snd_pcm_format_width(substream->runtime->format)) { + case 16: + avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; + break; + case 24: + avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24; + break; + default: + pr_info("%s: invalid width %d\n", __func__, + snd_pcm_format_width(substream->runtime->format)); + return 1; + } + + if ((card->avs.avs_audio_width != avs.avs_audio_width) || + (card->avs.avs_audio_rate != avs.avs_audio_rate)) { + card->avs = avs; + snd_ps3_change_avsetting(card); + + pr_debug("%s: after freq=%d width=%d\n", __func__, + card->avs.avs_audio_rate, card->avs.avs_audio_width); + + return 0; + } else + return 1; +} + + + +static int snd_ps3_map_mmio(void) +{ + the_card.mapped_mmio_vaddr = + ioremap(the_card.ps3_dev->m_region->bus_addr, + the_card.ps3_dev->m_region->len); + + if (!the_card.mapped_mmio_vaddr) { + pr_info("%s: ioremap 0 failed p=%#lx l=%#lx \n", + __func__, the_card.ps3_dev->m_region->lpar_addr, + the_card.ps3_dev->m_region->len); + return -ENXIO; + } + + return 0; +}; + +static void snd_ps3_unmap_mmio(void) +{ + iounmap(the_card.mapped_mmio_vaddr); + the_card.mapped_mmio_vaddr = NULL; +} + +static int snd_ps3_allocate_irq(void) +{ + int ret; + u64 lpar_addr, lpar_size; + u64 __iomem *mapped; + + /* FIXME: move this to device_init (H/W probe) */ + + /* get irq outlet */ + ret = lv1_gpu_device_map(1, &lpar_addr, &lpar_size); + if (ret) { + pr_info("%s: device map 1 failed %d\n", __func__, + ret); + return -ENXIO; + } + + mapped = ioremap(lpar_addr, lpar_size); + if (!mapped) { + pr_info("%s: ioremap 1 failed \n", __func__); + return -ENXIO; + } + + the_card.audio_irq_outlet = in_be64(mapped); + + iounmap(mapped); + ret = lv1_gpu_device_unmap(1); + if (ret) + pr_info("%s: unmap 1 failed\n", __func__); + + /* irq */ + ret = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, + the_card.audio_irq_outlet, + &the_card.irq_no); + if (ret) { + pr_info("%s:ps3_alloc_irq failed (%d)\n", __func__, ret); + return ret; + } + + ret = request_irq(the_card.irq_no, snd_ps3_interrupt, IRQF_DISABLED, + SND_PS3_DRIVER_NAME, &the_card); + if (ret) { + pr_info("%s: request_irq failed (%d)\n", __func__, ret); + goto cleanup_irq; + } + + return 0; + + cleanup_irq: + ps3_irq_plug_destroy(the_card.irq_no); + return ret; +}; + +static void snd_ps3_free_irq(void) +{ + free_irq(the_card.irq_no, &the_card); + ps3_irq_plug_destroy(the_card.irq_no); +} + +static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start) +{ + uint64_t val; + int ret; + + val = (ioaddr_start & (0x0fUL << 32)) >> (32 - 20) | + (0x03UL << 24) | + (0x0fUL << 12) | + (PS3_AUDIO_IOID); + + ret = lv1_gpu_attribute(0x100, 0x007, val, 0, 0); + if (ret) + pr_info("%s: gpu_attribute failed %d\n", __func__, + ret); +} + +static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev) +{ + int ret; + u64 lpar_addr, lpar_size; + + BUG_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1)); + BUG_ON(dev->match_id != PS3_MATCH_ID_SOUND); + + the_card.ps3_dev = dev; + + ret = ps3_open_hv_device(dev); + + if (ret) + return -ENXIO; + + /* setup MMIO */ + ret = lv1_gpu_device_map(2, &lpar_addr, &lpar_size); + if (ret) { + pr_info("%s: device map 2 failed %d\n", __func__, ret); + goto clean_open; + } + ps3_mmio_region_init(dev, dev->m_region, lpar_addr, lpar_size, + PAGE_SHIFT); + + ret = snd_ps3_map_mmio(); + if (ret) + goto clean_dev_map; + + /* setup DMA area */ + ps3_dma_region_init(dev, dev->d_region, + PAGE_SHIFT, /* use system page size */ + 0, /* dma type; not used */ + NULL, + _ALIGN_UP(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE)); + dev->d_region->ioid = PS3_AUDIO_IOID; + + ret = ps3_dma_region_create(dev->d_region); + if (ret) { + pr_info("%s: region_create\n", __func__); + goto clean_mmio; + } + + snd_ps3_audio_set_base_addr(dev->d_region->bus_addr); + + /* CONFIG_SND_PS3_DEFAULT_START_DELAY */ + the_card.start_delay = snd_ps3_start_delay; + + /* irq */ + if (snd_ps3_allocate_irq()) { + ret = -ENXIO; + goto clean_dma_region; + } + + /* create card instance */ + the_card.card = snd_card_new(index, id, THIS_MODULE, 0); + if (!the_card.card) { + ret = -ENXIO; + goto clean_irq; + } + + strcpy(the_card.card->driver, "PS3"); + strcpy(the_card.card->shortname, "PS3"); + strcpy(the_card.card->longname, "PS3 sound"); + /* create PCM devices instance */ + /* NOTE:this driver works assuming pcm:substream = 1:1 */ + ret = snd_pcm_new(the_card.card, + "SPDIF", + 0, /* instance index, will be stored pcm.device*/ + 1, /* output substream */ + 0, /* input substream */ + &(the_card.pcm)); + if (ret) + goto clean_card; + + the_card.pcm->private_data = &the_card; + strcpy(the_card.pcm->name, "SPDIF"); + + /* set pcm ops */ + snd_pcm_set_ops(the_card.pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_ps3_pcm_spdif_ops); + + the_card.pcm->info_flags = SNDRV_PCM_INFO_NONINTERLEAVED; + /* pre-alloc PCM DMA buffer*/ + ret = snd_pcm_lib_preallocate_pages_for_all(the_card.pcm, + SNDRV_DMA_TYPE_DEV, + &dev->core, + SND_PS3_PCM_PREALLOC_SIZE, + SND_PS3_PCM_PREALLOC_SIZE); + if (ret < 0) { + pr_info("%s: prealloc failed\n", __func__); + goto clean_card; + } + + /* + * allocate null buffer + * its size should be lager than PS3_AUDIO_FIFO_STAGE_SIZE * 2 + * PAGE_SIZE is enogh + */ + if (!(the_card.null_buffer_start_vaddr = + dma_alloc_coherent(&the_card.ps3_dev->core, + PAGE_SIZE, + &the_card.null_buffer_start_dma_addr, + GFP_KERNEL))) { + pr_info("%s: nullbuffer alloc failed\n", __func__); + goto clean_preallocate; + } + pr_debug("%s: null vaddr=%p dma=%#lx\n", __func__, + the_card.null_buffer_start_vaddr, + the_card.null_buffer_start_dma_addr); + /* set default sample rate/word width */ + snd_ps3_init_avsetting(&the_card); + + /* register the card */ + ret = snd_card_register(the_card.card); + if (ret < 0) + goto clean_dma_map; + + pr_info("%s started. start_delay=%dms\n", + the_card.card->longname, the_card.start_delay); + return 0; + +clean_dma_map: + dma_free_coherent(&the_card.ps3_dev->core, + PAGE_SIZE, + the_card.null_buffer_start_vaddr, + the_card.null_buffer_start_dma_addr); +clean_preallocate: + snd_pcm_lib_preallocate_free_for_all(the_card.pcm); +clean_card: + snd_card_free(the_card.card); +clean_irq: + snd_ps3_free_irq(); +clean_dma_region: + ps3_dma_region_free(dev->d_region); +clean_mmio: + snd_ps3_unmap_mmio(); +clean_dev_map: + lv1_gpu_device_unmap(2); +clean_open: + ps3_close_hv_device(dev); + /* + * there is no destructor function to pcm. + * midlayer automatically releases if the card removed + */ + return ret; +}; /* snd_ps3_probe */ + +/* called when module removal */ +static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev) +{ + int ret; + pr_info("%s:start id=%d\n", __func__, dev->match_id); + if (dev->match_id != PS3_MATCH_ID_SOUND) + return -ENXIO; + + /* + * ctl and preallocate buffer will be freed in + * snd_card_free + */ + ret = snd_card_free(the_card.card); + if (ret) + pr_info("%s: ctl freecard=%d\n", __func__, ret); + + dma_free_coherent(&dev->core, + PAGE_SIZE, + the_card.null_buffer_start_vaddr, + the_card.null_buffer_start_dma_addr); + + ps3_dma_region_free(dev->d_region); + + snd_ps3_free_irq(); + snd_ps3_unmap_mmio(); + + lv1_gpu_device_unmap(2); + ps3_close_hv_device(dev); + pr_info("%s:end id=%d\n", __func__, dev->match_id); + return 0; +} /* snd_ps3_remove */ + +static struct ps3_system_bus_driver snd_ps3_bus_driver_info = { + .match_id = PS3_MATCH_ID_SOUND, + .probe = snd_ps3_driver_probe, + .remove = snd_ps3_driver_remove, + .shutdown = snd_ps3_driver_remove, + .core = { + .name = SND_PS3_DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + + +/* + * Interrupt handler + */ +static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id) +{ + + uint32_t port_intr; + int underflow_occured = 0; + struct snd_ps3_card_info *card = dev_id; + + if (!card->running) { + update_reg(PS3_AUDIO_AX_IS, 0); + update_reg(PS3_AUDIO_INTR_0, 0); + return IRQ_HANDLED; + } + + port_intr = read_reg(PS3_AUDIO_AX_IS); + /* + *serial buffer empty detected (every 4 times), + *program next dma and kick it + */ + if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) { + write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0)); + if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) { + write_reg(PS3_AUDIO_AX_IS, port_intr); + underflow_occured = 1; + } + if (card->silent) { + /* we are still in silent time */ + snd_ps3_program_dma(card, + (underflow_occured) ? + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL : + SND_PS3_DMA_FILLTYPE_SILENT_RUNNING); + snd_ps3_kick_dma(card); + card->silent --; + } else { + snd_ps3_program_dma(card, + (underflow_occured) ? + SND_PS3_DMA_FILLTYPE_FIRSTFILL : + SND_PS3_DMA_FILLTYPE_RUNNING); + snd_ps3_kick_dma(card); + snd_pcm_period_elapsed(card->substream); + } + } else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) { + write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0)); + /* + * serial out underflow, but buffer empty not detected. + * in this case, fill fifo with 0 to recover. After + * filling dummy data, serial automatically start to + * consume them and then will generate normal buffer + * empty interrupts. + * If both buffer underflow and buffer empty are occured, + * it is better to do nomal data transfer than empty one + */ + snd_ps3_program_dma(card, + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); + snd_ps3_kick_dma(card); + snd_ps3_program_dma(card, + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); + snd_ps3_kick_dma(card); + } + /* clear interrupt cause */ + return IRQ_HANDLED; +}; + +/* + * module/subsystem initialize/terminate + */ +static int __init snd_ps3_init(void) +{ + int ret; + + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ENXIO; + + memset(&the_card, 0, sizeof(the_card)); + spin_lock_init(&the_card.dma_lock); + + /* register systembus DRIVER, this calls our probe() func */ + ret = ps3_system_bus_driver_register(&snd_ps3_bus_driver_info); + + return ret; +} + +static void __exit snd_ps3_exit(void) +{ + ps3_system_bus_driver_unregister(&snd_ps3_bus_driver_info); +} + +MODULE_ALIAS(PS3_MODULE_ALIAS_SOUND); Index: linux-2.6/sound/ppc/snd_ps3.h =================================================================== --- /dev/null +++ linux-2.6/sound/ppc/snd_ps3.h @@ -0,0 +1,135 @@ +/* + * Audio support for PS3 + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * All rights reserved. + * Copyright 2006, 2007 Sony Corporation + * + * 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; version 2 of the Licence. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(_SND_PS3_H_) +#define _SND_PS3_H_ + +#include + +#define SND_PS3_DRIVER_NAME "snd_ps3" + +enum snd_ps3_out_channel { + SND_PS3_OUT_SPDIF_0, + SND_PS3_OUT_SPDIF_1, + SND_PS3_OUT_SERIAL_0, + SND_PS3_OUT_DEVS +}; + +enum snd_ps3_dma_filltype { + SND_PS3_DMA_FILLTYPE_FIRSTFILL, + SND_PS3_DMA_FILLTYPE_RUNNING, + SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL, + SND_PS3_DMA_FILLTYPE_SILENT_RUNNING +}; + +enum snd_ps3_ch { + SND_PS3_CH_L = 0, + SND_PS3_CH_R = 1, + SND_PS3_CH_MAX = 2 +}; + +struct snd_ps3_avsetting_info { + uint32_t avs_audio_ch; /* fixed */ + uint32_t avs_audio_rate; + uint32_t avs_audio_width; + uint32_t avs_audio_format; /* fixed */ + uint32_t avs_audio_source; /* fixed */ +}; +/* + * PS3 audio 'card' instance + * there should be only ONE hardware. + */ +struct snd_ps3_card_info { + struct ps3_system_bus_device *ps3_dev; + struct snd_card *card; + + struct snd_pcm *pcm; + struct snd_pcm_substream *substream; + + /* hvc info */ + u64 audio_lpar_addr; + u64 audio_lpar_size; + + /* registers */ + void __iomem *mapped_mmio_vaddr; + + /* irq */ + u64 audio_irq_outlet; + unsigned int irq_no; + + /* remember avsetting */ + struct snd_ps3_avsetting_info avs; + + /* dma buffer management */ + spinlock_t dma_lock; + /* dma_lock start */ + void * dma_start_vaddr[2]; /* 0 for L, 1 for R */ + dma_addr_t dma_start_bus_addr[2]; + size_t dma_buffer_size; + void * dma_last_transfer_vaddr[2]; + void * dma_next_transfer_vaddr[2]; + int silent; + /* dma_lock end */ + + int running; + + /* null buffer */ + void *null_buffer_start_vaddr; + dma_addr_t null_buffer_start_dma_addr; + + /* start delay */ + unsigned int start_delay; + +}; + + +/* PS3 audio DMAC block size in bytes */ +#define PS3_AUDIO_DMAC_BLOCK_SIZE (128) +/* one stage (stereo) of audio FIFO in bytes */ +#define PS3_AUDIO_FIFO_STAGE_SIZE (256) +/* how many stages the fifo have */ +#define PS3_AUDIO_FIFO_STAGE_COUNT (8) +/* fifo size 128 bytes * 8 stages * stereo (2ch) */ +#define PS3_AUDIO_FIFO_SIZE \ + (PS3_AUDIO_FIFO_STAGE_SIZE * PS3_AUDIO_FIFO_STAGE_COUNT) + +/* PS3 audio DMAC max block count in one dma shot = 128 (0x80) blocks*/ +#define PS3_AUDIO_DMAC_MAX_BLOCKS (PS3_AUDIO_DMASIZE_BLOCKS_MASK + 1) + +#define PS3_AUDIO_NORMAL_DMA_START_CH (0) +#define PS3_AUDIO_NORMAL_DMA_COUNT (8) +#define PS3_AUDIO_NULL_DMA_START_CH \ + (PS3_AUDIO_NORMAL_DMA_START_CH + PS3_AUDIO_NORMAL_DMA_COUNT) +#define PS3_AUDIO_NULL_DMA_COUNT (2) + +#define SND_PS3_MAX_VOL (0x0F) +#define SND_PS3_MIN_VOL (0x00) +#define SND_PS3_MIN_ATT SND_PS3_MIN_VOL +#define SND_PS3_MAX_ATT SND_PS3_MAX_VOL + +#define SND_PS3_PCM_PREALLOC_SIZE \ + (PS3_AUDIO_DMAC_BLOCK_SIZE * PS3_AUDIO_DMAC_MAX_BLOCKS * 4) + +#define SND_PS3_DMA_REGION_SIZE \ + (SND_PS3_PCM_PREALLOC_SIZE + PAGE_SIZE) + +#define PS3_AUDIO_IOID (1UL) + +#endif /* _SND_PS3_H_ */ Index: linux-2.6/sound/ppc/snd_ps3_reg.h =================================================================== --- /dev/null +++ linux-2.6/sound/ppc/snd_ps3_reg.h @@ -0,0 +1,891 @@ +/* + * Audio support for PS3 + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2006, 2007 Sony Corporation + * All rights reserved. + * + * 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; version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * interrupt / configure registers + */ + +#define PS3_AUDIO_INTR_0 (0x00000100) +#define PS3_AUDIO_INTR_EN_0 (0x00000140) +#define PS3_AUDIO_CONFIG (0x00000200) + +/* + * DMAC registers + * n:0..9 + */ +#define PS3_AUDIO_DMAC_REGBASE(x) (0x0000210 + 0x20 * (x)) + +#define PS3_AUDIO_KICK(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x00) +#define PS3_AUDIO_SOURCE(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x04) +#define PS3_AUDIO_DEST(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x08) +#define PS3_AUDIO_DMASIZE(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x0C) + +/* + * mute control + */ +#define PS3_AUDIO_AX_MCTRL (0x00004000) +#define PS3_AUDIO_AX_ISBP (0x00004004) +#define PS3_AUDIO_AX_AOBP (0x00004008) +#define PS3_AUDIO_AX_IC (0x00004010) +#define PS3_AUDIO_AX_IE (0x00004014) +#define PS3_AUDIO_AX_IS (0x00004018) + +/* + * three wire serial + * n:0..3 + */ +#define PS3_AUDIO_AO_MCTRL (0x00006000) +#define PS3_AUDIO_AO_3WMCTRL (0x00006004) + +#define PS3_AUDIO_AO_3WCTRL(n) (0x00006200 + 0x200 * (n)) + +/* + * S/PDIF + * n:0..1 + * x:0..11 + * y:0..5 + */ +#define PS3_AUDIO_AO_SPD_REGBASE(n) (0x00007200 + 0x200 * (n)) + +#define PS3_AUDIO_AO_SPDCTRL(n) \ + (PS3_AUDIO_AO_SPD_REGBASE(n) + 0x00) +#define PS3_AUDIO_AO_SPDUB(n, x) \ + (PS3_AUDIO_AO_SPD_REGBASE(n) + 0x04 + 0x04 * (x)) +#define PS3_AUDIO_AO_SPDCS(n, y) \ + (PS3_AUDIO_AO_SPD_REGBASE(n) + 0x34 + 0x04 * (y)) + + +/* + PS3_AUDIO_INTR_0 register tells an interrupt handler which audio + DMA channel triggered the interrupt. The interrupt status for a channel + can be cleared by writing a '1' to the corresponding bit. A new interrupt + cannot be generated until the previous interrupt has been cleared. + + Note that the status reported by PS3_AUDIO_INTR_0 is independent of the + value of PS3_AUDIO_INTR_EN_0. + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C| INTR_0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +#define PS3_AUDIO_INTR_0_CHAN(n) (1 << ((n) * 2)) +#define PS3_AUDIO_INTR_0_CHAN9 PS3_AUDIO_INTR_0_CHAN(9) +#define PS3_AUDIO_INTR_0_CHAN8 PS3_AUDIO_INTR_0_CHAN(8) +#define PS3_AUDIO_INTR_0_CHAN7 PS3_AUDIO_INTR_0_CHAN(7) +#define PS3_AUDIO_INTR_0_CHAN6 PS3_AUDIO_INTR_0_CHAN(6) +#define PS3_AUDIO_INTR_0_CHAN5 PS3_AUDIO_INTR_0_CHAN(5) +#define PS3_AUDIO_INTR_0_CHAN4 PS3_AUDIO_INTR_0_CHAN(4) +#define PS3_AUDIO_INTR_0_CHAN3 PS3_AUDIO_INTR_0_CHAN(3) +#define PS3_AUDIO_INTR_0_CHAN2 PS3_AUDIO_INTR_0_CHAN(2) +#define PS3_AUDIO_INTR_0_CHAN1 PS3_AUDIO_INTR_0_CHAN(1) +#define PS3_AUDIO_INTR_0_CHAN0 PS3_AUDIO_INTR_0_CHAN(0) + +/* + The PS3_AUDIO_INTR_EN_0 register specifies which DMA channels can generate + an interrupt to the PU. Each bit of PS3_AUDIO_INTR_EN_0 is ANDed with the + corresponding bit in PS3_AUDIO_INTR_0. The resulting bits are OR'd together + to generate the Audio interrupt. + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C| INTR_EN_0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + + Bit assignments are same as PS3_AUDIO_INTR_0 +*/ + +/* + PS3_AUDIO_CONFIG + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 C|0 0 0 0 0 0 0 0| CONFIG + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +*/ + +/* The CLEAR field cancels all pending transfers, and stops any running DMA + transfers. Any interrupts associated with the canceled transfers + will occur as if the transfer had finished. + Since this bit is designed to recover from DMA related issues + which are caused by unpredictable situations, it is prefered to wait + for normal DMA transfer end without using this bit. +*/ +#define PS3_AUDIO_CONFIG_CLEAR (1 << 8) /* RWIVF */ + +/* + PS3_AUDIO_AX_MCTRL: Audio Port Mute Control Register + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|A|A|A|0 0 0 0 0 0 0|S|S|A|A|A|A| AX_MCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* 3 Wire Audio Serial Output Channel Mutes (0..3) */ +#define PS3_AUDIO_AX_MCTRL_ASOMT(n) (1 << (3 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_ASO3MT (1 << 0) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_ASO2MT (1 << 1) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_ASO1MT (1 << 2) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_ASO0MT (1 << 3) /* RWIVF */ + +/* S/PDIF mutes (0,1)*/ +#define PS3_AUDIO_AX_MCTRL_SPOMT(n) (1 << (5 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_SPO1MT (1 << 4) /* RWIVF */ +#define PS3_AUDIO_AX_MCTRL_SPO0MT (1 << 5) /* RWIVF */ + +/* All 3 Wire Serial Outputs Mute */ +#define PS3_AUDIO_AX_MCTRL_AASOMT (1 << 13) /* RWIVF */ + +/* All S/PDIF Mute */ +#define PS3_AUDIO_AX_MCTRL_ASPOMT (1 << 14) /* RWIVF */ + +/* All Audio Outputs Mute */ +#define PS3_AUDIO_AX_MCTRL_AAOMT (1 << 15) /* RWIVF */ + +/* + S/PDIF Outputs Buffer Read/Write Pointer Register + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|0|SPO0B|0|SPO1B|0 0 0 0 0 0 0 0|0|SPO0B|0|SPO1B| AX_ISBP + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +*/ +/* + S/PDIF Output Channel Read Buffer Numbers + Buffer number is value of field. + Indicates current read access buffer ID from Audio Data + Transfer controller of S/PDIF Output +*/ + +#define PS3_AUDIO_AX_ISBP_SPOBRN_MASK(n) (0x7 << 4 * (1 - (n))) /* R-IUF */ +#define PS3_AUDIO_AX_ISBP_SPO1BRN_MASK (0x7 << 0) /* R-IUF */ +#define PS3_AUDIO_AX_ISBP_SPO0BRN_MASK (0x7 << 4) /* R-IUF */ + +/* +S/PDIF Output Channel Buffer Write Numbers +Indicates current write access buffer ID from bus master. +*/ +#define PS3_AUDIO_AX_ISBP_SPOBWN_MASK(n) (0x7 << 4 * (5 - (n))) /* R-IUF */ +#define PS3_AUDIO_AX_ISBP_SPO1BWN_MASK (0x7 << 16) /* R-IUF */ +#define PS3_AUDIO_AX_ISBP_SPO0BWN_MASK (0x7 << 20) /* R-IUF */ + +/* + 3 Wire Audio Serial Outputs Buffer Read/Write + Pointer Register + Buffer number is value of field + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0|ASO0B|0|ASO1B|0|ASO2B|0|ASO3B|0|ASO0B|0|ASO1B|0|ASO2B|0|ASO3B| AX_AOBP + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* +3 Wire Audio Serial Output Channel Buffer Read Numbers +Indicates current read access buffer Id from Audio Data Transfer +Controller of 3 Wire Audio Serial Output Channels +*/ +#define PS3_AUDIO_AX_AOBP_ASOBRN_MASK(n) (0x7 << 4 * (3 - (n))) /* R-IUF */ + +#define PS3_AUDIO_AX_AOBP_ASO3BRN_MASK (0x7 << 0) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO2BRN_MASK (0x7 << 4) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO1BRN_MASK (0x7 << 8) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO0BRN_MASK (0x7 << 12) /* R-IUF */ + +/* +3 Wire Audio Serial Output Channel Buffer Write Numbers +Indicates current write access buffer ID from bus master. +*/ +#define PS3_AUDIO_AX_AOBP_ASOBWN_MASK(n) (0x7 << 4 * (7 - (n))) /* R-IUF */ + +#define PS3_AUDIO_AX_AOBP_ASO3BWN_MASK (0x7 << 16) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO2BWN_MASK (0x7 << 20) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO1BWN_MASK (0x7 << 24) /* R-IUF */ +#define PS3_AUDIO_AX_AOBP_ASO0BWN_MASK (0x7 << 28) /* R-IUF */ + + + +/* +Audio Port Interrupt Condition Register +For the fields in this register, the following values apply: +0 = Interrupt is generated every interrupt event. +1 = Interrupt is generated every 2 interrupt events. +2 = Interrupt is generated every 4 interrupt events. +3 = Reserved + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|0 0|SPO|0 0|SPO|0 0|AAS|0 0 0 0 0 0 0 0 0 0 0 0| AX_IC + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +/* +All 3-Wire Audio Serial Outputs Interrupt Mode +Configures the Interrupt and Signal Notification +condition of all 3-wire Audio Serial Outputs. +*/ +#define PS3_AUDIO_AX_IC_AASOIMD_MASK (0x3 << 12) /* RWIVF */ +#define PS3_AUDIO_AX_IC_AASOIMD_EVERY1 (0x0 << 12) /* RWI-V */ +#define PS3_AUDIO_AX_IC_AASOIMD_EVERY2 (0x1 << 12) /* RW--V */ +#define PS3_AUDIO_AX_IC_AASOIMD_EVERY4 (0x2 << 12) /* RW--V */ + +/* +S/PDIF Output Channel Interrupt Modes +Configures the Interrupt and signal Notification +conditions of S/PDIF output channels. +*/ +#define PS3_AUDIO_AX_IC_SPO1IMD_MASK (0x3 << 16) /* RWIVF */ +#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY1 (0x0 << 16) /* RWI-V */ +#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY2 (0x1 << 16) /* RW--V */ +#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY4 (0x2 << 16) /* RW--V */ + +#define PS3_AUDIO_AX_IC_SPO0IMD_MASK (0x3 << 20) /* RWIVF */ +#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY1 (0x0 << 20) /* RWI-V */ +#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY2 (0x1 << 20) /* RW--V */ +#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY4 (0x2 << 20) /* RW--V */ + +/* +Audio Port interrupt Enable Register +Configures whether to enable or disable each Interrupt Generation. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|S|S|0 0|A|A|A|A|0 0 0 0|S|S|0 0|S|S|0 0|A|A|A|A| AX_IE + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +*/ + +/* +3 Wire Audio Serial Output Channel Buffer Underflow +Interrupt Enables +Select enable/disable of Buffer Underflow Interrupts for +3-Wire Audio Serial Output Channels +DISABLED=Interrupt generation disabled. +*/ +#define PS3_AUDIO_AX_IE_ASOBUIE(n) (1 << (3 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO3BUIE (1 << 0) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO2BUIE (1 << 1) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO1BUIE (1 << 2) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO0BUIE (1 << 3) /* RWIVF */ + +/* S/PDIF Output Channel Buffer Underflow Interrupt Enables */ + +#define PS3_AUDIO_AX_IE_SPOBUIE(n) (1 << (7 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO1BUIE (1 << 6) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO0BUIE (1 << 7) /* RWIVF */ + +/* S/PDIF Output Channel One Block Transfer Completion Interrupt Enables */ + +#define PS3_AUDIO_AX_IE_SPOBTCIE(n) (1 << (11 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO1BTCIE (1 << 10) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO0BTCIE (1 << 11) /* RWIVF */ + +/* 3-Wire Audio Serial Output Channel Buffer Empty Interrupt Enables */ + +#define PS3_AUDIO_AX_IE_ASOBEIE(n) (1 << (19 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO3BEIE (1 << 16) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO2BEIE (1 << 17) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO1BEIE (1 << 18) /* RWIVF */ +#define PS3_AUDIO_AX_IE_ASO0BEIE (1 << 19) /* RWIVF */ + +/* S/PDIF Output Channel Buffer Empty Interrupt Enables */ + +#define PS3_AUDIO_AX_IE_SPOBEIE(n) (1 << (23 - (n))) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO1BEIE (1 << 22) /* RWIVF */ +#define PS3_AUDIO_AX_IE_SPO0BEIE (1 << 23) /* RWIVF */ + +/* +Audio Port Interrupt Status Register +Indicates Interrupt status, which interrupt has occured, and can clear +each interrupt in this register. +Writing 1b to a field containing 1b clears field and de-asserts interrupt. +Writing 0b to a field has no effect. +Field vaules are the following: +0 - Interrupt hasn't occured. +1 - Interrupt has occured. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0|S|S|0 0|A|A|A|A|0 0 0 0|S|S|0 0|S|S|0 0|A|A|A|A| AX_IS + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + + Bit assignment are same as AX_IE +*/ + +/* +Audio Output Master Control Register +Configures Master Clock and other master Audio Output Settings + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0|SCKSE|0|SCKSE| MR0 | MR1 |MCL|MCL|0 0 0 0|0 0 0 0 0 0 0 0| AO_MCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* +MCLK Output Control +Controls mclko[1] output. +0 - Disable output (fixed at High) +1 - Output clock produced by clock selected +with scksel1 by mr1 +2 - Reserved +3 - Reserved +*/ + +#define PS3_AUDIO_AO_MCTRL_MCLKC1_MASK (0x3 << 12) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_MCLKC1_DISABLED (0x0 << 12) /* RWI-V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC1_ENABLED (0x1 << 12) /* RW--V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC1_RESVD2 (0x2 << 12) /* RW--V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC1_RESVD3 (0x3 << 12) /* RW--V */ + +/* +MCLK Output Control +Controls mclko[0] output. +0 - Disable output (fixed at High) +1 - Output clock produced by clock selected +with SCKSEL0 by MR0 +2 - Reserved +3 - Reserved +*/ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_MASK (0x3 << 14) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_DISABLED (0x0 << 14) /* RWI-V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_ENABLED (0x1 << 14) /* RW--V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_RESVD2 (0x2 << 14) /* RW--V */ +#define PS3_AUDIO_AO_MCTRL_MCLKC0_RESVD3 (0x3 << 14) /* RW--V */ +/* +Master Clock Rate 1 +Sets the divide ration of Master Clock1 (clock output from +mclko[1] for the input clock selected by scksel1. +*/ +#define PS3_AUDIO_AO_MCTRL_MR1_MASK (0xf << 16) +#define PS3_AUDIO_AO_MCTRL_MR1_DEFAULT (0x0 << 16) /* RWI-V */ +/* +Master Clock Rate 0 +Sets the divide ratio of Master Clock0 (clock output from +mclko[0] for the input clock selected by scksel0). +*/ +#define PS3_AUDIO_AO_MCTRL_MR0_MASK (0xf << 20) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_MR0_DEFAULT (0x0 << 20) /* RWI-V */ +/* +System Clock Select 0/1 +Selects the system clock to be used as Master Clock 0/1 +Input the system clock that is appropriate for the sampling +rate. +*/ +#define PS3_AUDIO_AO_MCTRL_SCKSEL1_MASK (0x7 << 24) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_SCKSEL1_DEFAULT (0x2 << 24) /* RWI-V */ + +#define PS3_AUDIO_AO_MCTRL_SCKSEL0_MASK (0x7 << 28) /* RWIVF */ +#define PS3_AUDIO_AO_MCTRL_SCKSEL0_DEFAULT (0x2 << 28) /* RWI-V */ + + +/* +3-Wire Audio Output Master Control Register +Configures clock, 3-Wire Audio Serial Output Enable, and +other 3-Wire Audio Serial Output Master Settings + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |A|A|A|A|0 0 0|A| ASOSR |0 0 0 0|A|A|A|A|A|A|0|1|0 0 0 0 0 0 0 0| AO_3WMCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + + +/* +LRCKO Polarity +0 - Reserved +1 - default +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASOPLRCK (1 << 8) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT (1 << 8) /* RW--V */ + +/* LRCK Output Disable */ + +#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD (1 << 10) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_ENABLED (0 << 10) /* RW--V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED (1 << 10) /* RWI-V */ + +/* Bit Clock Output Disable */ + +#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD (1 << 11) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_ENABLED (0 << 11) /* RW--V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED (1 << 11) /* RWI-V */ + +/* +3-Wire Audio Serial Output Channel 0-3 Operational +Status. Each bit becomes 1 after each 3-Wire Audio +Serial Output Channel N is in action by setting 1 to +asoen. +Each bit becomes 0 after each 3-Wire Audio Serial Output +Channel N is out of action by setting 0 to asoen. +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASORUN(n) (1 << (15 - (n))) /* R-IVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(n) (0 << (15 - (n))) /* R-I-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(n) (1 << (15 - (n))) /* R---V */ +#define PS3_AUDIO_AO_3WMCTRL_ASORUN0 \ + PS3_AUDIO_AO_3WMCTRL_ASORUN(0) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN0_STOPPED \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(0) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN0_RUNNING \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(0) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN1 \ + PS3_AUDIO_AO_3WMCTRL_ASORUN(1) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN1_STOPPED \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(1) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN1_RUNNING \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(1) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN2 \ + PS3_AUDIO_AO_3WMCTRL_ASORUN(2) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN2_STOPPED \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(2) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN2_RUNNING \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(2) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN3 \ + PS3_AUDIO_AO_3WMCTRL_ASORUN(3) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN3_STOPPED \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(3) +#define PS3_AUDIO_AO_3WMCTRL_ASORUN3_RUNNING \ + PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(3) + +/* +Sampling Rate +Specifies the divide ratio of the bit clock (clock output +from bclko) used by the 3-wire Audio Output Clock, whcih +is applied to the master clock selected by mcksel. +Data output is synchronized with this clock. +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_MASK (0xf << 20) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV2 (0x1 << 20) /* RWI-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV4 (0x2 << 20) /* RW--V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV8 (0x4 << 20) /* RW--V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV12 (0x6 << 20) /* RW--V */ + +/* +Master Clock Select +0 - Master Clock 0 +1 - Master Clock 1 +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL (1 << 24) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL_CLK0 (0 << 24) /* RWI-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL_CLK1 (1 << 24) /* RW--V */ + +/* +Enables and disables 4ch 3-Wire Audio Serial Output +operation. Each Bit from 0 to 3 corresponds to an +output channel, which means that each output channel +can be enabled or disabled individually. When +multiple channels are enabled at the same time, output +operations are performed in synchronization. +Bit 0 - Output Channel 0 (SDOUT[0]) +Bit 1 - Output Channel 1 (SDOUT[1]) +Bit 2 - Output Channel 2 (SDOUT[2]) +Bit 3 - Output Channel 3 (SDOUT[3]) +*/ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN(n) (1 << (31 - (n))) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(n) (0 << (31 - (n))) /* RWI-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(n) (1 << (31 - (n))) /* RW--V */ + +#define PS3_AUDIO_AO_3WMCTRL_ASOEN0 \ + PS3_AUDIO_AO_3WMCTRL_ASOEN(0) /* RWIVF */ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN0_DISABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(0) /* RWI-V */ +#define PS3_AUDIO_AO_3WMCTRL_ASOEN0_ENABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(0) /* RW--V */ +#define PS3_AUDIO_A1_3WMCTRL_ASOEN0 \ + PS3_AUDIO_AO_3WMCTRL_ASOEN(1) /* RWIVF */ +#define PS3_AUDIO_A1_3WMCTRL_ASOEN0_DISABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(1) /* RWI-V */ +#define PS3_AUDIO_A1_3WMCTRL_ASOEN0_ENABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(1) /* RW--V */ +#define PS3_AUDIO_A2_3WMCTRL_ASOEN0 \ + PS3_AUDIO_AO_3WMCTRL_ASOEN(2) /* RWIVF */ +#define PS3_AUDIO_A2_3WMCTRL_ASOEN0_DISABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(2) /* RWI-V */ +#define PS3_AUDIO_A2_3WMCTRL_ASOEN0_ENABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(2) /* RW--V */ +#define PS3_AUDIO_A3_3WMCTRL_ASOEN0 \ + PS3_AUDIO_AO_3WMCTRL_ASOEN(3) /* RWIVF */ +#define PS3_AUDIO_A3_3WMCTRL_ASOEN0_DISABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(3) /* RWI-V */ +#define PS3_AUDIO_A3_3WMCTRL_ASOEN0_ENABLED \ + PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(3) /* RW--V */ + +/* +3-Wire Audio Serial output Channel 0-3 Control Register +Configures settings for 3-Wire Serial Audio Output Channel 0-3 + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|A|0 0 0 0|A|0|ASO|0 0 0|0|0|0|0|0| AO_3WCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +*/ +/* +Data Bit Mode +Specifies the number of data bits +0 - 16 bits +1 - reserved +2 - 20 bits +3 - 24 bits +*/ +#define PS3_AUDIO_AO_3WCTRL_ASODB_MASK (0x3 << 8) /* RWIVF */ +#define PS3_AUDIO_AO_3WCTRL_ASODB_16BIT (0x0 << 8) /* RWI-V */ +#define PS3_AUDIO_AO_3WCTRL_ASODB_RESVD (0x1 << 8) /* RWI-V */ +#define PS3_AUDIO_AO_3WCTRL_ASODB_20BIT (0x2 << 8) /* RW--V */ +#define PS3_AUDIO_AO_3WCTRL_ASODB_24BIT (0x3 << 8) /* RW--V */ +/* +Data Format Mode +Specifies the data format where (LSB side or MSB) the data(in 20 bit +or 24 bit resolution mode) is put in a 32 bit field. +0 - Data put on LSB side +1 - Data put on MSB side +*/ +#define PS3_AUDIO_AO_3WCTRL_ASODF (1 << 11) /* RWIVF */ +#define PS3_AUDIO_AO_3WCTRL_ASODF_LSB (0 << 11) /* RWI-V */ +#define PS3_AUDIO_AO_3WCTRL_ASODF_MSB (1 << 11) /* RW--V */ +/* +Buffer Reset +Performs buffer reset. Writing 1 to this bit initializes the +corresponding 3-Wire Audio Output buffers(both L and R). +*/ +#define PS3_AUDIO_AO_3WCTRL_ASOBRST (1 << 16) /* CWIVF */ +#define PS3_AUDIO_AO_3WCTRL_ASOBRST_IDLE (0 << 16) /* -WI-V */ +#define PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET (1 << 16) /* -W--T */ + +/* +S/PDIF Audio Output Channel 0/1 Control Register +Configures settings for S/PDIF Audio Output Channel 0/1. + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |S|0 0 0|S|0 0|S| SPOSR |0 0|SPO|0 0 0 0|S|0|SPO|0 0 0 0 0 0 0|S| AO_SPDCTRL + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +/* +Buffer reset. Writing 1 to this bit initializes the +corresponding S/PDIF output buffer pointer. +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOBRST (1 << 0) /* CWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOBRST_IDLE (0 << 0) /* -WI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOBRST_RESET (1 << 0) /* -W--T */ + +/* +Data Bit Mode +Specifies number of data bits +0 - 16 bits +1 - Reserved +2 - 20 bits +3 - 24 bits +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_MASK (0x3 << 8) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_16BIT (0x0 << 8) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_RESVD (0x1 << 8) /* RW--V */ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_20BIT (0x2 << 8) /* RW--V */ +#define PS3_AUDIO_AO_SPDCTRL_SPODB_24BIT (0x3 << 8) /* RW--V */ +/* +Data format Mode +Specifies the data format, where (LSB side or MSB) +the data(in 20 or 24 bit resolution) is put in the +32 bit field. +0 - LSB Side +1 - MSB Side +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPODF (1 << 11) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPODF_LSB (0 << 11) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPODF_MSB (1 << 11) /* RW--V */ +/* +Source Select +Specifies the source of the S/PDIF output. When 0, output +operation is controlled by 3wen[0] of AO_3WMCTRL register. +The SR must have the same setting as the a0_3wmctrl reg. +0 - 3-Wire Audio OUT Ch0 Buffer +1 - S/PDIF buffer +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOSS_MASK (0x3 << 16) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSS_3WEN (0x0 << 16) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSS_SPDIF (0x1 << 16) /* RW--V */ +/* +Sampling Rate +Specifies the divide ratio of the bit clock (clock output +from bclko) used by the S/PDIF Output Clock, which +is applied to the master clock selected by mcksel. +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR (0xf << 20) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV2 (0x1 << 20) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV4 (0x2 << 20) /* RW--V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV8 (0x4 << 20) /* RW--V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV12 (0x6 << 20) /* RW--V */ +/* +Master Clock Select +0 - Master Clock 0 +1 - Master Clock 1 +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL (1 << 24) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL_CLK0 (0 << 24) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL_CLK1 (1 << 24) /* RW--V */ + +/* +S/PDIF Output Channel Operational Status +This bit becomes 1 after S/PDIF Output Channel is in +action by setting 1 to spoen. This bit becomes 0 +after S/PDIF Output Channel is out of action by setting +0 to spoen. +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPORUN (1 << 27) /* R-IVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPORUN_STOPPED (0 << 27) /* R-I-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPORUN_RUNNING (1 << 27) /* R---V */ + +/* +S/PDIF Audio Output Channel Output Enable +Enables and disables output operation. This bit is used +only when sposs = 1 +*/ +#define PS3_AUDIO_AO_SPDCTRL_SPOEN (1 << 31) /* RWIVF */ +#define PS3_AUDIO_AO_SPDCTRL_SPOEN_DISABLED (0 << 31) /* RWI-V */ +#define PS3_AUDIO_AO_SPDCTRL_SPOEN_ENABLED (1 << 31) /* RW--V */ + +/* +S/PDIF Audio Output Channel Channel Status +Setting Registers. +Configures channel status bit settings for each block +(192 bits). +Output is performed from the MSB(AO_SPDCS0 register bit 31). +The same value is added for subframes within the same frame. + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + | SPOCS | AO_SPDCS + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + +S/PDIF Audio Output Channel User Bit Setting +Configures user bit settings for each block (384 bits). +Output is performed from the MSB(ao_spdub0 register bit 31). + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + | SPOUB | AO_SPDUB + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +/***************************************************************************** + * + * DMAC register + * + *****************************************************************************/ +/* +The PS3_AUDIO_KICK register is used to initiate a DMA transfer and monitor +its status + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0|STATU|0 0 0| EVENT |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|R| KICK + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ +/* +The REQUEST field is written to ACTIVE to initiate a DMA request when EVENT +occurs. +It will return to the DONE state when the request is completed. +The registers for a DMA channel should only be written if REQUEST is IDLE. +*/ + +#define PS3_AUDIO_KICK_REQUEST (1 << 0) /* RWIVF */ +#define PS3_AUDIO_KICK_REQUEST_IDLE (0 << 0) /* RWI-V */ +#define PS3_AUDIO_KICK_REQUEST_ACTIVE (1 << 0) /* -W--T */ + +/* + *The EVENT field is used to set the event in which + *the DMA request becomes active. + */ +#define PS3_AUDIO_KICK_EVENT_MASK (0x1f << 16) /* RWIVF */ +#define PS3_AUDIO_KICK_EVENT_ALWAYS (0x00 << 16) /* RWI-V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY (0x01 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT0_UNDERFLOW (0x02 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT1_EMPTY (0x03 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT1_UNDERFLOW (0x04 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT2_EMPTY (0x05 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT2_UNDERFLOW (0x06 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT3_EMPTY (0x07 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SERIALOUT3_UNDERFLOW (0x08 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF0_BLOCKTRANSFERCOMPLETE \ + (0x09 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF0_UNDERFLOW (0x0A << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF0_EMPTY (0x0B << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF1_BLOCKTRANSFERCOMPLETE \ + (0x0C << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF1_UNDERFLOW (0x0D << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_SPDIF1_EMPTY (0x0E << 16) /* RW--V */ + +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA(n) \ + ((0x13 + (n)) << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA0 (0x13 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA1 (0x14 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA2 (0x15 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA3 (0x16 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA4 (0x17 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA5 (0x18 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA6 (0x19 << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA7 (0x1A << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA8 (0x1B << 16) /* RW--V */ +#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA9 (0x1C << 16) /* RW--V */ + +/* +The STATUS field can be used to monitor the progress of a DMA request. +DONE indicates the previous request has completed. +EVENT indicates that the DMA engine is waiting for the EVENT to occur. +PENDING indicates that the DMA engine has not started processing this +request, but the EVENT has occured. +DMA indicates that the data transfer is in progress. +NOTIFY indicates that the notifier signalling end of transfer is being written. +CLEAR indicated that the previous transfer was cleared. +ERROR indicates the previous transfer requested an unsupported +source/destination combination. +*/ + +#define PS3_AUDIO_KICK_STATUS_MASK (0x7 << 24) /* R-IVF */ +#define PS3_AUDIO_KICK_STATUS_DONE (0x0 << 24) /* R-I-V */ +#define PS3_AUDIO_KICK_STATUS_EVENT (0x1 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_PENDING (0x2 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_DMA (0x3 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_NOTIFY (0x4 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_CLEAR (0x5 << 24) /* R---V */ +#define PS3_AUDIO_KICK_STATUS_ERROR (0x6 << 24) /* R---V */ + +/* +The PS3_AUDIO_SOURCE register specifies the source address for transfers. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + | START |0 0 0 0 0|TAR| SOURCE + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* +The Audio DMA engine uses 128-byte transfers, thus the address must be aligned +to a 128 byte boundary. The low seven bits are assumed to be 0. +*/ + +#define PS3_AUDIO_SOURCE_START_MASK (0x01FFFFFF << 7) /* RWIUF */ + +/* +The TARGET field specifies the memory space containing the source address. +*/ + +#define PS3_AUDIO_SOURCE_TARGET_MASK (3 << 0) /* RWIVF */ +#define PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY (2 << 0) /* RW--V */ + +/* +The PS3_AUDIO_DEST register specifies the destination address for transfers. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + | START |0 0 0 0 0|TAR| DEST + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + +/* +The Audio DMA engine uses 128-byte transfers, thus the address must be aligned +to a 128 byte boundary. The low seven bits are assumed to be 0. +*/ + +#define PS3_AUDIO_DEST_START_MASK (0x01FFFFFF << 7) /* RWIUF */ + +/* +The TARGET field specifies the memory space containing the destination address +AUDIOFIFO = Audio WriteData FIFO, +*/ + +#define PS3_AUDIO_DEST_TARGET_MASK (3 << 0) /* RWIVF */ +#define PS3_AUDIO_DEST_TARGET_AUDIOFIFO (1 << 0) /* RW--V */ + +/* +PS3_AUDIO_DMASIZE specifies the number of 128-byte blocks + 1 to transfer. +So a value of 0 means 128-bytes will get transfered. + + + 31 24 23 16 15 8 7 0 + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ + |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0| BLOCKS | DMASIZE + +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+ +*/ + + +#define PS3_AUDIO_DMASIZE_BLOCKS_MASK (0x7f << 0) /* RWIUF */ + +/* + * source/destination address for internal fifos + */ +#define PS3_AUDIO_AO_3W_LDATA(n) (0x1000 + (0x100 * (n))) +#define PS3_AUDIO_AO_3W_RDATA(n) (0x1080 + (0x100 * (n))) + +#define PS3_AUDIO_AO_SPD_DATA(n) (0x2000 + (0x400 * (n))) + + +/* + * field attiribute + * + * Read + * ' ' = Other Information + * '-' = Field is part of a write-only register + * 'C' = Value read is always the same, constant value line follows (C) + * 'R' = Value is read + * + * Write + * ' ' = Other Information + * '-' = Must not be written (D), value ignored when written (R,A,F) + * 'W' = Can be written + * + * Internal State + * ' ' = Other Information + * '-' = No internal state + * 'X' = Internal state, initial value is unknown + * 'I' = Internal state, initial value is known and follows (I) + * + * Declaration/Size + * ' ' = Other Information + * '-' = Does Not Apply + * 'V' = Type is void + * 'U' = Type is unsigned integer + * 'S' = Type is signed integer + * 'F' = Type is IEEE floating point + * '1' = Byte size (008) + * '2' = Short size (016) + * '3' = Three byte size (024) + * '4' = Word size (032) + * '8' = Double size (064) + * + * Define Indicator + * ' ' = Other Information + * 'D' = Device + * 'M' = Memory + * 'R' = Register + * 'A' = Array of Registers + * 'F' = Field + * 'V' = Value + * 'T' = Task + */ +