From dde48bc754a6a0e0527681e1c7e6d441721025fd Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Sat, 16 Aug 2014 01:27:33 +0200 Subject: [PATCH] projects/RPi/patches/linux: update RPi support patch, enable optional MMC_BCM2835 and MMC_BCM2835_DMA driver Signed-off-by: Stephan Raue --- projects/RPi/linux/linux.arm.conf | 9 +- .../patches/linux/linux-01-RPi_support.patch | 3620 ++++++++++++++++- 2 files changed, 3494 insertions(+), 135 deletions(-) diff --git a/projects/RPi/linux/linux.arm.conf b/projects/RPi/linux/linux.arm.conf index 754655c337..b59754df87 100644 --- a/projects/RPi/linux/linux.arm.conf +++ b/projects/RPi/linux/linux.arm.conf @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm 3.16.0 Kernel Configuration +# Linux/arm 3.16.1 Kernel Configuration # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -2547,6 +2547,9 @@ CONFIG_MMC_SDHCI_PLTFM=y # CONFIG_MMC_SDHCI_PXAV2 is not set CONFIG_MMC_SDHCI_BCM2708=y CONFIG_MMC_SDHCI_BCM2708_DMA=y +CONFIG_MMC_BCM2835=y +CONFIG_MMC_BCM2835_DMA=y +CONFIG_MMC_PIO_DMA_BARRIER=2 # CONFIG_MMC_SPI is not set # CONFIG_MMC_DW is not set # CONFIG_MMC_VUB300 is not set @@ -2700,9 +2703,9 @@ CONFIG_DMADEVICES=y # CONFIG_DW_DMAC_CORE is not set # CONFIG_DW_DMAC is not set # CONFIG_PL330_DMA is not set -CONFIG_DMA_BCM2708=m +CONFIG_DMA_BCM2708=y CONFIG_DMA_ENGINE=y -CONFIG_DMA_VIRTUAL_CHANNELS=m +CONFIG_DMA_VIRTUAL_CHANNELS=y # # DMA Clients diff --git a/projects/RPi/patches/linux/linux-01-RPi_support.patch b/projects/RPi/patches/linux/linux-01-RPi_support.patch index 2957f02515..a271415dd7 100644 --- a/projects/RPi/patches/linux/linux-01-RPi_support.patch +++ b/projects/RPi/patches/linux/linux-01-RPi_support.patch @@ -1,7 +1,7 @@ -From 6bc5831c3e7b2b70c22ac80371697fc0d7a37f3e Mon Sep 17 00:00:00 2001 +From 839892fb323f851df87d53988607afedf3dd830b Mon Sep 17 00:00:00 2001 From: popcornmix Date: Sun, 12 May 2013 12:24:19 +0100 -Subject: [PATCH 01/69] Main bcm2708 linux port +Subject: [PATCH 01/71] Main bcm2708 linux port Signed-off-by: popcornmix --- @@ -8676,10 +8676,10 @@ index 08abe99..544da76 100644 2.0.3 -From a7620c972ac833ec67feafd57c59a7b9b3244aba Mon Sep 17 00:00:00 2001 +From da440b37d0d7441018381f8444219096b7b0bb1b Mon Sep 17 00:00:00 2001 From: popcornmix Date: Tue, 7 May 2013 22:20:24 +0100 -Subject: [PATCH 02/69] Add quick config. +Subject: [PATCH 02/71] Add quick config. This is designed for quick compiling when developing. No modules are needed and it includes all Pi specific drivers @@ -8895,10 +8895,10 @@ index 0000000..e5efe75 2.0.3 -From c4766da426e90cc933773a962a22948a4ca573f2 Mon Sep 17 00:00:00 2001 +From 902e25dd4eaa08f290dbe28741cfa19f135e9574 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 1 May 2013 19:46:17 +0100 -Subject: [PATCH 03/69] Add dwc_otg driver +Subject: [PATCH 03/71] Add dwc_otg driver Signed-off-by: popcornmix --- @@ -65970,10 +65970,10 @@ index 0000000..cdc9963 2.0.3 -From 53fe7eb309b1efcf96f9010622d698a41d7fe928 Mon Sep 17 00:00:00 2001 +From 6b87ca694e19511ee495315977066f3df32cbadc Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 1 May 2013 19:54:32 +0100 -Subject: [PATCH 04/69] bcm2708 watchdog driver +Subject: [PATCH 04/71] bcm2708 watchdog driver Signed-off-by: popcornmix --- @@ -66404,10 +66404,10 @@ index 0000000..8a27d68 2.0.3 -From 40a7b010c351b4e9b12e1a307a08fc2ed5cc8638 Mon Sep 17 00:00:00 2001 +From 75f85908f5c7346bcfae244d2de54469e7a34b98 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 1 May 2013 19:55:09 +0100 -Subject: [PATCH 05/69] bcm2708 framebuffer driver +Subject: [PATCH 05/71] bcm2708 framebuffer driver Signed-off-by: popcornmix --- @@ -69454,10 +69454,10 @@ index 3c14e43..7626beb 100644 2.0.3 -From d5e4fe4b752393809af327001f3be62c430a169c Mon Sep 17 00:00:00 2001 +From 39f45149b0b1b89e9c6e87d79de37b754008c3d3 Mon Sep 17 00:00:00 2001 From: Harm Hanemaaijer Date: Thu, 20 Jun 2013 20:21:39 +0200 -Subject: [PATCH 06/69] Speed up console framebuffer imageblit function +Subject: [PATCH 06/71] Speed up console framebuffer imageblit function Especially on platforms with a slower CPU but a relatively high framebuffer fill bandwidth, like current ARM devices, the existing @@ -69669,10 +69669,10 @@ index a2bb276..436494f 100644 2.0.3 -From 2319763577aac9e45e0fc3af8c2a51f56e081c08 Mon Sep 17 00:00:00 2001 +From dab41a03d19951dcf0af36647b7bcfc716fa46ce Mon Sep 17 00:00:00 2001 From: Siarhei Siamashka Date: Mon, 17 Jun 2013 13:32:11 +0300 -Subject: [PATCH 07/69] fbdev: add FBIOCOPYAREA ioctl +Subject: [PATCH 07/71] fbdev: add FBIOCOPYAREA ioctl Based on the patch authored by Ali Gholami Rudi at https://lkml.org/lkml/2009/7/13/153 @@ -69768,10 +69768,10 @@ index fb795c3..fa72af0 100644 2.0.3 -From 54bd9aef58aa6e7c33d7047bb2c157c8bc774318 Mon Sep 17 00:00:00 2001 +From 5ca156864b2ac966e3d20aa29a3eabab4b559295 Mon Sep 17 00:00:00 2001 From: Siarhei Siamashka Date: Mon, 17 Jun 2013 16:00:25 +0300 -Subject: [PATCH 08/69] bcm2708_fb: DMA acceleration for fb_copyarea +Subject: [PATCH 08/71] bcm2708_fb: DMA acceleration for fb_copyarea Based on http://www.raspberrypi.org/phpBB3/viewtopic.php?p=62425#p62425 Also used Simon's dmaer_master module as a reference for tweaking DMA @@ -70258,10 +70258,10 @@ index 54cd760..b3b1e04 100644 2.0.3 -From 2e1eeed1dd9c1aba1c0c1a6b4ed1efff86cae8c7 Mon Sep 17 00:00:00 2001 +From 9e7cf5f765bfaf0412918f84f87f507404928531 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Tue, 2 Jul 2013 23:42:01 +0100 -Subject: [PATCH 09/69] bcm2708 vchiq driver +Subject: [PATCH 09/71] bcm2708 vchiq driver Signed-off-by: popcornmix @@ -82813,10 +82813,10 @@ index 0000000..b6bfa21 2.0.3 -From 9712514f82d055918a5ca73f2fb7e3873c9ce40b Mon Sep 17 00:00:00 2001 +From 07e18ad78953ccfa168920bd0cfa95d20d853a0a Mon Sep 17 00:00:00 2001 From: popcornmix Date: Mon, 12 May 2014 15:12:02 +0100 -Subject: [PATCH 10/69] vchiq: Avoid high load when blocked and unkillable +Subject: [PATCH 10/71] vchiq: Avoid high load when blocked and unkillable vchiq: Include SIGSTOP and SIGCONT in list of signals not-masked by vchiq to allow gdb to work --- @@ -82981,10 +82981,10 @@ index c2eefef..05e7979 100644 2.0.3 -From 45c75a9a45e75f750aeff0d08d1396516645d5c6 Mon Sep 17 00:00:00 2001 +From c156c499b73a549d7bff0d3c991494d11108cd6f Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 3 Jul 2013 00:31:47 +0100 -Subject: [PATCH 11/69] cma: Add vc_cma driver to enable use of CMA +Subject: [PATCH 11/71] cma: Add vc_cma driver to enable use of CMA Signed-off-by: popcornmix --- @@ -84276,10 +84276,10 @@ index 0000000..5325832 2.0.3 -From df67b820a9d714d5db7a600bd9792c01036d9126 Mon Sep 17 00:00:00 2001 +From bcd05a294b17945c1992560a04e60d5a75ecd347 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Mon, 26 Mar 2012 22:15:50 +0100 -Subject: [PATCH 12/69] bcm2708: alsa sound driver +Subject: [PATCH 12/71] bcm2708: alsa sound driver Signed-off-by: popcornmix @@ -87070,10 +87070,10 @@ index 0000000..af3e6eb 2.0.3 -From bfb24ef6eda232bcbe30b9a87d8781169aead81a Mon Sep 17 00:00:00 2001 +From afcf0a46aa2ca83398e4be2a0b444dfe82915681 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 3 Jul 2013 00:51:55 +0100 -Subject: [PATCH 13/69] Add hwrng (hardware random number generator) driver +Subject: [PATCH 13/71] Add hwrng (hardware random number generator) driver --- arch/arm/mach-bcm2708/include/mach/platform.h | 1 + @@ -87255,10 +87255,10 @@ index 0000000..340f004 2.0.3 -From 052f1d58548c436f372fe61074f8aa0cb7312631 Mon Sep 17 00:00:00 2001 +From e331619637903fb87cf857a2eca13db59c3fb26e Mon Sep 17 00:00:00 2001 From: Aron Szabo Date: Sat, 16 Jun 2012 12:15:55 +0200 -Subject: [PATCH 14/69] lirc: added support for RaspberryPi GPIO +Subject: [PATCH 14/71] lirc: added support for RaspberryPi GPIO lirc_rpi: Use read_current_timer to determine transmitter delay. Thanks to jjmz and others See: https://github.com/raspberrypi/linux/issues/525 @@ -88003,10 +88003,10 @@ index 0000000..57ffacf 2.0.3 -From 53092d7fda436acb280d02a403c0668a7f56cd8a Mon Sep 17 00:00:00 2001 +From 792da119ecc4c626a203766aaec1e52b2e3a7fab Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 3 Jul 2013 00:49:20 +0100 -Subject: [PATCH 15/69] Add cpufreq driver +Subject: [PATCH 15/71] Add cpufreq driver --- arch/arm/Kconfig | 1 + @@ -88308,10 +88308,10 @@ index 0000000..7bc55bd 2.0.3 -From c37dae10e13ec5f97ff409e76e888d3edd367850 Mon Sep 17 00:00:00 2001 +From 3e47e00f837107aeb880b4ea1712f1a08c27d970 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Tue, 26 Mar 2013 19:24:24 +0000 -Subject: [PATCH 16/69] Added hwmon/thermal driver for reporting core +Subject: [PATCH 16/71] Added hwmon/thermal driver for reporting core temperature. Thanks Dorian --- @@ -88836,10 +88836,10 @@ index 0000000..85fceb5 2.0.3 -From e60f23ce15064909c9ae0d166161d70fd7a1fc04 Mon Sep 17 00:00:00 2001 +From e611450aaf310a866b42c52a34fc5307d4231046 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Tue, 26 Mar 2013 17:26:38 +0000 -Subject: [PATCH 17/69] Allow mac address to be set in smsc95xx +Subject: [PATCH 17/71] Allow mac address to be set in smsc95xx Signed-off-by: popcornmix --- @@ -88933,10 +88933,10 @@ index d07bf4c..5ae60ab 100644 2.0.3 -From 00b4eab6ec2422674dcf87baf73f196e14000fa7 Mon Sep 17 00:00:00 2001 +From cb77c7822b8734fb85c2e0d5fce466b47199969f Mon Sep 17 00:00:00 2001 From: popcornmix Date: Mon, 4 Nov 2013 18:56:10 +0000 -Subject: [PATCH 18/69] Add Chris Boot's i2c and spi drivers. +Subject: [PATCH 18/71] Add Chris Boot's i2c and spi drivers. i2c-bcm2708: fixed baudrate @@ -90320,10 +90320,10 @@ index 0000000..b04a57d 2.0.3 -From 807ad336fb3a9c14bc6b08615f4f32603c89c48e Mon Sep 17 00:00:00 2001 +From b48352e617873908e9eed7c7a0a0274c3154d443 Mon Sep 17 00:00:00 2001 From: cbeytas Date: Mon, 24 Jun 2013 00:05:40 -0400 -Subject: [PATCH 19/69] Perform I2C combined transactions when possible +Subject: [PATCH 19/71] Perform I2C combined transactions when possible Perform I2C combined transactions whenever possible, within the restrictions of the Broadcomm Serial Controller. @@ -90398,10 +90398,10 @@ index f266f10..8750634 100644 2.0.3 -From 0fd73af06f60a6296e4ff5190c5f3eda88580b2f Mon Sep 17 00:00:00 2001 +From a8f2638feb6187f10fe57305790052d7eb5b8864 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 8 May 2013 11:46:50 +0100 -Subject: [PATCH 20/69] enabling the realtime clock 1-wire chip DS1307 and +Subject: [PATCH 20/71] enabling the realtime clock 1-wire chip DS1307 and 1-wire on GPIO4 (as a module) 1-wire: Add support for configuring pin for w1-gpio kernel module @@ -90676,10 +90676,10 @@ index 2820924..fd0550f 100644 2.0.3 -From 410dfdc7595b5cdd5d14d2085ca96cc1556d2dc3 Mon Sep 17 00:00:00 2001 +From bd506122daf1d74245615448bc794c2f64fce271 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Fri, 12 Apr 2013 23:58:47 +0100 -Subject: [PATCH 24/69] config: add missing options from 3.6.y kernel +Subject: [PATCH 24/71] config: add missing options from 3.6.y kernel --- arch/arm/configs/bcmrpi_defconfig | 757 ++++++++++++++++++++++++++++++++------ @@ -91731,10 +91731,10 @@ index 31f5afaa..5b69e83 100644 2.0.3 -From bbd02eefcab6de94a76ff20ae8f41979a224264f Mon Sep 17 00:00:00 2001 +From 81c9c12a18219e4047e0ce94c903a0d71781005d Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 18 Dec 2013 22:16:19 +0000 -Subject: [PATCH 25/69] config: Enable CONFIG_MEMCG, but leave it disabled (due +Subject: [PATCH 25/71] config: Enable CONFIG_MEMCG, but leave it disabled (due to memory cost). Enable with cgroup_enable=memory. --- @@ -91805,10 +91805,10 @@ index 1f14a43..403173e 100644 2.0.3 -From fcdb10ea333e0f0945f9bbad6ea0f660b74000b6 Mon Sep 17 00:00:00 2001 +From 4419c5e5a7d400cdd45bcb500a91a46853aaac51 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 3 Jul 2013 00:46:42 +0100 -Subject: [PATCH 26/69] Add FIQ patch to dwc_otg driver. Enable with +Subject: [PATCH 26/71] Add FIQ patch to dwc_otg driver. Enable with dwc_otg.fiq_fix_enable=1. Should give about 10% more ARM performance. Thanks to Gordon and Costas @@ -95130,10 +95130,10 @@ index 1b1f83c..c8590b5 100644 2.0.3 -From 8c8c4ef61da26e49e22db4625333aef308dfd1dc Mon Sep 17 00:00:00 2001 +From 23139849a415b9f4048fbc2e8f9d9a610cfd6bb6 Mon Sep 17 00:00:00 2001 From: P33M Date: Wed, 19 Mar 2014 12:58:23 +0000 -Subject: [PATCH 27/69] dwc_otg: fiq_fsm: Base commit for driver rewrite +Subject: [PATCH 27/71] dwc_otg: fiq_fsm: Base commit for driver rewrite This commit removes the previous FIQ fixes entirely and adds fiq_fsm. @@ -100032,10 +100032,10 @@ index 5d310df..4b32941 100644 2.0.3 -From 198112683018b1c563861d89649ef42bfa04eda9 Mon Sep 17 00:00:00 2001 +From d4a83bcbcb9da2e2da12b9f6c699c4fbba695f94 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Wed, 30 Jan 2013 12:45:18 +0000 -Subject: [PATCH 28/69] bcm2835: add v4l2 camera device +Subject: [PATCH 28/71] bcm2835: add v4l2 camera device - Supports raw YUV capture, preview, JPEG and H264. - Uses videobuf2 for data transfer, using dma_buf. @@ -106023,10 +106023,10 @@ index 0000000..9d1d11e 2.0.3 -From f9a983d9320bdc8b7d94fdedd59a015456dea8e2 Mon Sep 17 00:00:00 2001 +From ce9099c8d233407393fddc3aac73867919593acf Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 9 Dec 2013 10:58:01 +0000 -Subject: [PATCH 29/69] V4L2: Fixes from 6by9 +Subject: [PATCH 29/71] V4L2: Fixes from 6by9 V4L2: Fix EV values. Add manual shutter speed control @@ -108401,10 +108401,10 @@ index a06fb44..76f249e 100644 2.0.3 -From 19efd180a89088d35c6ead0d351c1878996d9c94 Mon Sep 17 00:00:00 2001 +From c6bb89322561ea50e609a73ee792020429329472 Mon Sep 17 00:00:00 2001 From: Florian Meier Date: Fri, 22 Nov 2013 14:22:53 +0100 -Subject: [PATCH 30/69] dmaengine: Add support for BCM2708 +Subject: [PATCH 30/71] dmaengine: Add support for BCM2708 Add support for DMA controller of BCM2708 as used in the Raspberry Pi. Currently it only supports cyclic DMA. @@ -109044,10 +109044,10 @@ index 0000000..b244293 2.0.3 -From f524482843c30ee526ff78a77bacffb6ebcc67e6 Mon Sep 17 00:00:00 2001 +From 9f7b49b82309f6b788c08ffa9cb7bca2f850c375 Mon Sep 17 00:00:00 2001 From: Florian Meier Date: Fri, 22 Nov 2013 14:33:38 +0100 -Subject: [PATCH 31/69] ASoC: Add support for BCM2708 +Subject: [PATCH 31/71] ASoC: Add support for BCM2708 This driver adds support for digital audio (I2S) for the BCM2708 SoC that is used by the @@ -110045,10 +110045,10 @@ index 0000000..ebaf3d6 2.0.3 -From 19ec42857fcbc3f67c308de4233e73d89238db05 Mon Sep 17 00:00:00 2001 +From f2d01e149898dfc5a6ccfd5007bc0ac07caa156f Mon Sep 17 00:00:00 2001 From: Florian Meier Date: Fri, 22 Nov 2013 14:37:51 +0100 -Subject: [PATCH 32/69] BCM2708: Extend mach header +Subject: [PATCH 32/71] BCM2708: Extend mach header Extend the headers of the mach-bcm2708 in order to support I2S and DMA engine. @@ -110093,10 +110093,10 @@ index 992a630..2e7e1bb 100644 2.0.3 -From 0d4a090b4d5ee5f36d3c44bad0d9de856e63ae65 Mon Sep 17 00:00:00 2001 +From ba0fd48bfe41f4f12ac70dfbba058afa8f3100fd Mon Sep 17 00:00:00 2001 From: Florian Meier Date: Fri, 22 Nov 2013 14:59:51 +0100 -Subject: [PATCH 33/69] ASoC: Add support for PCM5102A codec +Subject: [PATCH 33/71] ASoC: Add support for PCM5102A codec Some definitions to support the PCM5102A codec by Texas Instruments. @@ -110224,10 +110224,10 @@ index 0000000..126f1e9 2.0.3 -From 9c8424c5317a9398179d788c3fc05a0b55905885 Mon Sep 17 00:00:00 2001 +From 5c220e69598ccc5a4331a4807685b2bee0e7531e Mon Sep 17 00:00:00 2001 From: Florian Meier Date: Fri, 22 Nov 2013 19:04:54 +0100 -Subject: [PATCH 34/69] BCM2708: Add I2S support to board file +Subject: [PATCH 34/71] BCM2708: Add I2S support to board file Adds the required initializations for I2S to the board file of mach-bcm2708. @@ -110285,10 +110285,10 @@ index a6eb08e..27e47d5 100644 2.0.3 -From cd2263b1303d573009860feb74caa0faaace47a0 Mon Sep 17 00:00:00 2001 +From 8c4010ef6f120f6f20c3d575600a5b595bbb0994 Mon Sep 17 00:00:00 2001 From: Florian Meier Date: Fri, 22 Nov 2013 19:19:08 +0100 -Subject: [PATCH 35/69] ASoC: Add support for HifiBerry DAC +Subject: [PATCH 35/71] ASoC: Add support for HifiBerry DAC This adds a machine driver for the HifiBerry DAC. It is a sound card that can @@ -110440,10 +110440,10 @@ index 0000000..4b70b45 2.0.3 -From 54ed9b8bcd8f115b0466bb35fae08f7ad27f4f5c Mon Sep 17 00:00:00 2001 +From 64aa80c778a2bc0b03e1039f95960243f4201fd7 Mon Sep 17 00:00:00 2001 From: Florian Meier Date: Fri, 22 Nov 2013 19:21:34 +0100 -Subject: [PATCH 36/69] BCM2708: Add HifiBerry DAC to board file +Subject: [PATCH 36/71] BCM2708: Add HifiBerry DAC to board file This adds the initalization of the HifiBerry DAC to the mach-bcm2708 board file. @@ -110494,10 +110494,10 @@ index 27e47d5..fafd8d0 100644 2.0.3 -From 1ce5835d6d9d66f2d42ecb5035abb40fc471d26e Mon Sep 17 00:00:00 2001 +From 15a789cf4a98f88eb75d553b6caea4f7d1c8de9c Mon Sep 17 00:00:00 2001 From: Florian Meier Date: Fri, 6 Dec 2013 18:55:53 +0100 -Subject: [PATCH 37/69] ASoC: BCM2708: Add 24 bit support +Subject: [PATCH 37/71] ASoC: BCM2708: Add 24 bit support This adds 24 bit support to the I2S driver of the BCM2708. Besides enabling the 24 bit flags, it includes two bug fixes: @@ -110572,10 +110572,10 @@ index ebaf3d6..a179216 100644 2.0.3 -From 51ff7226df47992cc46edd4f3ef71cf9c9b374d7 Mon Sep 17 00:00:00 2001 +From e0716a7fa7874757ff01fac80f62e8c99a56f33c Mon Sep 17 00:00:00 2001 From: Florian Meier Date: Mon, 2 Dec 2013 20:28:22 +0100 -Subject: [PATCH 38/69] BCM2708: Add I2S and DMA support to default config +Subject: [PATCH 38/71] BCM2708: Add I2S and DMA support to default config This commit adds several modules that are needed for I2S support for the Raspberry Pi to the defconfig. @@ -110618,10 +110618,10 @@ index 0c58172..38ccd22 100644 2.0.3 -From 7360c30deb09a0c36473fe741f574560e861633f Mon Sep 17 00:00:00 2001 +From 0b2d13950ced9de365af93a96912f8e55271ecf0 Mon Sep 17 00:00:00 2001 From: Florian Meier Date: Fri, 6 Dec 2013 20:50:28 +0100 -Subject: [PATCH 39/69] ASoC: BCM2708: Add support for RPi-DAC +Subject: [PATCH 39/71] ASoC: BCM2708: Add support for RPi-DAC This adds a machine driver for the RPi-DAC. @@ -110935,10 +110935,10 @@ index 0000000..b4eaa44 2.0.3 -From a36d34e2a8c458ce920925844b3341fcaa53e637 Mon Sep 17 00:00:00 2001 +From 66f98a8406804e24ff9fa194ee21a9b961a53b98 Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Wed, 15 Jan 2014 21:41:23 +0100 -Subject: [PATCH 40/69] ASoC: wm8804: Implement MCLK configuration options, add +Subject: [PATCH 40/71] ASoC: wm8804: Implement MCLK configuration options, add 32bit support WM8804 can run with PLL frequencies of 256xfs and 128xfs for most sample rates. At 192kHz only 128xfs is supported. The existing driver selects 128xfs automatically for some lower samples rates. By using an @@ -110981,10 +110981,10 @@ index d96e5963..9a7a289 100644 2.0.3 -From 62d7db5f8b54792e1445720cc662cf8aba47b49d Mon Sep 17 00:00:00 2001 +From 9903287696cc4f5c070587dd46b2554a3ea771f6 Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Wed, 15 Jan 2014 21:42:08 +0100 -Subject: [PATCH 41/69] ASoC: BCM:Add support for HiFiBerry Digi. Driver is +Subject: [PATCH 41/71] ASoC: BCM:Add support for HiFiBerry Digi. Driver is based on the patched WM8804 driver. Signed-off-by: Daniel Matuschek @@ -111190,10 +111190,10 @@ index 0000000..e4f769d 2.0.3 -From 18d704577cb6dcf4e86d146753c835f01e5f826a Mon Sep 17 00:00:00 2001 +From 9089eb1c8089f27f0d825594aca55203fe4c2b33 Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Thu, 16 Jan 2014 07:26:08 +0100 -Subject: [PATCH 42/69] BCM2708: Added support for HiFiBerry Digi board Board +Subject: [PATCH 42/71] BCM2708: Added support for HiFiBerry Digi board Board initalization by I2C Signed-off-by: Daniel Matuschek @@ -111243,10 +111243,10 @@ index 238f165..139045b 100644 2.0.3 -From 9eef4accbfc135975bdc1371284709ca853d2e3d Mon Sep 17 00:00:00 2001 +From 2ccbdf72d828bd288256a23e79fa2e660bea9599 Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Thu, 16 Jan 2014 07:27:28 +0100 -Subject: [PATCH 43/69] BCM2708: Added HiFiBerry Digi configuration option It +Subject: [PATCH 43/71] BCM2708: Added HiFiBerry Digi configuration option It will be compiled as a module by default. This also includes the WM8804 driver. @@ -111274,10 +111274,10 @@ index 5e049f9..4f6bb90 100644 2.0.3 -From d758dd2a339c020ab12e55155025cb9aaac652a0 Mon Sep 17 00:00:00 2001 +From 8ca488553ca04a621fbee224210872ca64afc55f Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Thu, 16 Jan 2014 07:36:35 +0100 -Subject: [PATCH 44/69] ASoC: wm8804: Set idle_bias_off to false Idle bias has +Subject: [PATCH 44/71] ASoC: wm8804: Set idle_bias_off to false Idle bias has been change to remove warning on driver startup Signed-off-by: Daniel Matuschek @@ -111302,10 +111302,10 @@ index 9a7a289..32e199a 100644 2.0.3 -From 595a565de57c50b73832c62d0fe862ad1f8489d2 Mon Sep 17 00:00:00 2001 +From 658a1d669499b55d8e733ccb470a003e78149e99 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 12 Mar 2014 11:46:34 +0000 -Subject: [PATCH 45/69] ASoc: Don't report S24_LE support, it produces white +Subject: [PATCH 45/71] ASoc: Don't report S24_LE support, it produces white noise with xbmc --- @@ -111343,10 +111343,10 @@ index 126f1e9..7812d34 100644 2.0.3 -From 4a6f55a4abbf3506ca7919d1c95a2be3b952277d Mon Sep 17 00:00:00 2001 +From 5917c8011a856aa57802508ec7df1ec093ae7553 Mon Sep 17 00:00:00 2001 From: Gordon Garrity Date: Sat, 8 Mar 2014 16:56:57 +0000 -Subject: [PATCH 46/69] Add IQaudIO Sound Card support for Raspberry Pi +Subject: [PATCH 46/71] Add IQaudIO Sound Card support for Raspberry Pi --- arch/arm/configs/bcmrpi_defconfig | 1 + @@ -111559,10 +111559,10 @@ index 0000000..8d0e2ae 2.0.3 -From f0483731ee783f911bd4976eb8b186e0494538ea Mon Sep 17 00:00:00 2001 +From 603b762a6a41f0549462638f4f10d6c82f11d3f1 Mon Sep 17 00:00:00 2001 From: Howard Mitchell Date: Wed, 30 Jul 2014 21:43:37 +0100 -Subject: [PATCH 47/69] soc-core: Fix volsw_range funcs so +Subject: [PATCH 47/71] soc-core: Fix volsw_range funcs so SOC_DOUBLE_R_RANGE_TLV works. This is so that the correct range of values as specified @@ -111639,10 +111639,10 @@ index b87d7d8..1f3d03e 100644 2.0.3 -From e242ee32e3c4a87c70242c472fd534b30d096dbb Mon Sep 17 00:00:00 2001 +From 0b5178b68d7c9f41e0b7fa8eb5e2459d1ef80ebd Mon Sep 17 00:00:00 2001 From: Howard Mitchell Date: Fri, 28 Mar 2014 16:40:31 +0000 -Subject: [PATCH 48/69] pcm512x: Use a range macro for Volume and rename to +Subject: [PATCH 48/71] pcm512x: Use a range macro for Volume and rename to PCM. This allows limiting the output gain to avoid clipping in the @@ -111670,10 +111670,10 @@ index 163ec38..1466955 100644 2.0.3 -From b24c3177b927a6c9fdec4bbd6dad6e13deb72bb9 Mon Sep 17 00:00:00 2001 +From b37fad9a65a4e62657fdecb1d5b62b9dd510be90 Mon Sep 17 00:00:00 2001 From: Gordon Hollingworth Date: Fri, 2 May 2014 16:13:59 +0100 -Subject: [PATCH 49/69] Move GPIO setup to hw_params. +Subject: [PATCH 49/71] Move GPIO setup to hw_params. This is used to stop the I2S driver from breaking the GPIO setup for other uses of the PCM interface @@ -111772,10 +111772,10 @@ index b25e158..9976571 100644 2.0.3 -From 643b0d8e1ca78ed65aaba5cbebd56aa4a3cc9d5d Mon Sep 17 00:00:00 2001 +From e5fb8a6be0d14cadcfb602bf22ea8719088c0053 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 18 Jun 2014 13:42:01 +0100 -Subject: [PATCH 50/69] vmstat: Workaround for issue where dirty page count +Subject: [PATCH 50/71] vmstat: Workaround for issue where dirty page count goes negative See: @@ -111805,10 +111805,10 @@ index 82e7db7..f87d16d 100644 2.0.3 -From ca66969e4920c7e099d31327339f5e6a5a51d459 Mon Sep 17 00:00:00 2001 +From ab1b06e206515b2a67e2392aed9a3f87c447fd1a Mon Sep 17 00:00:00 2001 From: P33M Date: Fri, 20 Jun 2014 16:03:12 +0100 -Subject: [PATCH 51/69] dwc_otg: Fix various issues with root port and +Subject: [PATCH 51/71] dwc_otg: Fix various issues with root port and transaction errors Process the host port interrupts correctly (and don't trample them). @@ -111881,10 +111881,10 @@ index d3e2035..6182d3e 100644 2.0.3 -From aa85957ab787ff35e792631430c4ac2438803901 Mon Sep 17 00:00:00 2001 +From 8a3691d7d3bcefd262cdc2c176cf03c8bbcbf58e Mon Sep 17 00:00:00 2001 From: P33M Date: Fri, 20 Jun 2014 17:23:20 +0100 -Subject: [PATCH 52/69] fiq_fsm: Implement hack for Split Interrupt +Subject: [PATCH 52/71] fiq_fsm: Implement hack for Split Interrupt transactions Hubs aren't too picky about which endpoint we send Control type split @@ -111973,10 +111973,10 @@ index daea770..35721e5 100644 2.0.3 -From b0db7da4db5e72e4b971ef4fa41122d547ad629c Mon Sep 17 00:00:00 2001 +From a308c1b04aa2590538f3929459464751af4b1afe Mon Sep 17 00:00:00 2001 From: notro Date: Sun, 6 Jul 2014 12:07:25 +0200 -Subject: [PATCH 53/69] spi-bcm2708: Prepare for Common Clock Framework +Subject: [PATCH 53/71] spi-bcm2708: Prepare for Common Clock Framework migration As part of migrating to use the Common Clock Framework, replace clk_enable() @@ -112024,10 +112024,10 @@ index b04a57d..349d21f 100644 2.0.3 -From 110dc4edc8772481c62f1837cf5a7b9d1d5ffcd1 Mon Sep 17 00:00:00 2001 +From f55cc5f0ad7922b97e46bc594bdf420db2982b04 Mon Sep 17 00:00:00 2001 From: notro Date: Sun, 6 Jul 2014 12:09:30 +0200 -Subject: [PATCH 54/69] BCM2708: Migrate to the Common Clock Framework +Subject: [PATCH 54/71] BCM2708: Migrate to the Common Clock Framework As part of moving towards using Device Tree, the Common Clock Framework has to be used instead of the BCM2708 clock implementation. @@ -112300,10 +112300,10 @@ index 5f9d725..0000000 2.0.3 -From bb8acd988f2d8152964e340ff69df2286360764f Mon Sep 17 00:00:00 2001 +From 79bacbe9b2bc3aa5742e5e636d8750c57bd4abf7 Mon Sep 17 00:00:00 2001 From: notro Date: Wed, 9 Jul 2014 14:46:08 +0200 -Subject: [PATCH 55/69] BCM2708: Add core Device Tree support +Subject: [PATCH 55/71] BCM2708: Add core Device Tree support Add the bare minimum needed to boot BCM2708 from a Device Tree. @@ -112463,10 +112463,10 @@ index 674e5aa..83277d1f 100644 2.0.3 -From 8312254c3a0888c0a85cb5dbf414d458fee5e74a Mon Sep 17 00:00:00 2001 +From 504175cf2dab09ee2d3a38abda8cd14e946d9a8d Mon Sep 17 00:00:00 2001 From: notro Date: Wed, 9 Jul 2014 14:47:48 +0200 -Subject: [PATCH 56/69] BCM2708: armctrl: Add IRQ Device Tree support +Subject: [PATCH 56/71] BCM2708: armctrl: Add IRQ Device Tree support Add Device Tree IRQ support for BCM2708. Usage is the same as for irq-bcm2835. @@ -112633,10 +112633,10 @@ index d4c5333..42f5e1c 100644 2.0.3 -From bd0a5027902f169c9dd9b007ebf5ded0ff126d71 Mon Sep 17 00:00:00 2001 +From 4440f7a92619e12940a7e27f080d11064dc5cc93 Mon Sep 17 00:00:00 2001 From: notro Date: Thu, 10 Jul 2014 13:59:47 +0200 -Subject: [PATCH 57/69] pinctrl: add bcm2708 driver +Subject: [PATCH 57/71] pinctrl: add bcm2708 driver This driver is a verbatim copy of the pinctrl-bcm2835 driver, except for: * changed 2835 to 2708 @@ -113502,10 +113502,10 @@ index 0000000..40d9c86 2.0.3 -From ce7699bc76f43dd06b7c2fd8f7dcf488749b985a Mon Sep 17 00:00:00 2001 +From bef9a66a1a4e952c7d07bf03aa143ed629510152 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Mon, 14 Jul 2014 22:02:09 +0100 -Subject: [PATCH 58/69] hid: Reduce default mouse polling interval to 60Hz +Subject: [PATCH 58/71] hid: Reduce default mouse polling interval to 60Hz Reduces overhead when using X --- @@ -113544,10 +113544,10 @@ index 7b88f4c..016a485 100644 2.0.3 -From 3d10b641de5751000a87e71432e577d2d824e1f2 Mon Sep 17 00:00:00 2001 +From 4a71b12cde3193dbe01492f2f9edff796f2af095 Mon Sep 17 00:00:00 2001 From: notro Date: Fri, 18 Jul 2014 18:15:57 +0200 -Subject: [PATCH 59/69] BCM2708: DT: change 'axi' nodename to 'soc' +Subject: [PATCH 59/71] BCM2708: DT: change 'axi' nodename to 'soc' Change DT node named 'axi' to 'soc' so it matches ARCH_BCM2835. The VC4 bootloader fills in certain properties in the 'axi' subtree, @@ -113575,10 +113575,10 @@ index 3f884b3..e02e67b 100644 2.0.3 -From 49928dfbd6cb8dd00dfc9d3a8f50c819e04b49c5 Mon Sep 17 00:00:00 2001 +From e6c0c0c84ded234045eadb9405266010392b4124 Mon Sep 17 00:00:00 2001 From: notro Date: Sun, 27 Jul 2014 20:12:58 +0200 -Subject: [PATCH 60/69] spi: bcm2708: add device tree support +Subject: [PATCH 60/71] spi: bcm2708: add device tree support Add DT support to driver and add to .dtsi file. Setup pins and spidev in .dts file. @@ -113700,10 +113700,10 @@ index 349d21f..041b5e2 100644 2.0.3 -From a77d4f70ec50b92c8eaedf630da8e0061e28c587 Mon Sep 17 00:00:00 2001 +From bac8143ba837822fe0038a809e23a5c517533c57 Mon Sep 17 00:00:00 2001 From: notro Date: Sun, 27 Jul 2014 20:13:44 +0200 -Subject: [PATCH 61/69] BCM2708: don't register SPI controller when using DT +Subject: [PATCH 61/71] BCM2708: don't register SPI controller when using DT The device for the SPI controller is in the Device Tree. Only register the device when not using DT. @@ -113777,10 +113777,10 @@ index 83277d1f..a4286e9 100644 2.0.3 -From 81c6766ab049b9742d5051c7368530ddd046bd28 Mon Sep 17 00:00:00 2001 +From 9ed17623b1346dddcf60f50a5c11eccd77a1d907 Mon Sep 17 00:00:00 2001 From: notro Date: Sun, 27 Jul 2014 20:14:05 +0200 -Subject: [PATCH 62/69] spi: bcm2835: make driver available on ARCH_BCM2708 +Subject: [PATCH 62/71] spi: bcm2835: make driver available on ARCH_BCM2708 Make this driver available on ARCH_BCM2708 @@ -113806,10 +113806,10 @@ index cb20594..54aac0f 100644 2.0.3 -From dc2e3bb2614efe04e0874e510d73ab0deee576b3 Mon Sep 17 00:00:00 2001 +From 4436b5c4aaa78ab523807228efb9e69dcd13837b Mon Sep 17 00:00:00 2001 From: notro Date: Tue, 29 Jul 2014 11:04:49 +0200 -Subject: [PATCH 63/69] i2c: bcm2708: add device tree support +Subject: [PATCH 63/71] i2c: bcm2708: add device tree support Add DT support to driver and add to .dtsi file. Setup pins in .dts file. @@ -113977,10 +113977,10 @@ index 8750634..728cb69 100644 2.0.3 -From 0c53ad1f44d5a4bd77502d8147dfe6cb5fce1577 Mon Sep 17 00:00:00 2001 +From f32f8d0cc79649f78d6b0d60482af0c0cc7fe53e Mon Sep 17 00:00:00 2001 From: notro Date: Tue, 29 Jul 2014 11:05:18 +0200 -Subject: [PATCH 64/69] bcm2708: don't register i2c controllers when using DT +Subject: [PATCH 64/71] bcm2708: don't register i2c controllers when using DT The devices for the i2c controllers are in the Device Tree. Only register devices when not using DT. @@ -114025,10 +114025,10 @@ index a4286e9..a19f54d 100644 2.0.3 -From ea8bed1550c93b7bf8e67ae6844f4a948904a942 Mon Sep 17 00:00:00 2001 +From 79e54a26c2c8c34bdae2fe2e38670479643e008e Mon Sep 17 00:00:00 2001 From: notro Date: Tue, 29 Jul 2014 11:05:39 +0200 -Subject: [PATCH 65/69] i2c: bcm2835: make driver available on ARCH_BCM2708 +Subject: [PATCH 65/71] i2c: bcm2835: make driver available on ARCH_BCM2708 Make this driver available on ARCH_BCM2708 @@ -114054,10 +114054,10 @@ index 6426811..73c9cfd 100644 2.0.3 -From b86f0c81a89528b7f26ca6c05dfa5b8b2aa2720c Mon Sep 17 00:00:00 2001 +From b4b7637c58d49afd88e39a7a02b99d3b87fb0feb Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Fri, 25 Jul 2014 07:08:09 +0200 -Subject: [PATCH 66/69] Configure GPIOs for I2S based on revision/card settings +Subject: [PATCH 66/71] Configure GPIOs for I2S based on revision/card settings With RPi model B+, assignment of the I2S GPIO pins has changed. This patch uses the board revision to auto-detect the GPIOs used @@ -114215,10 +114215,10 @@ index 0000000..94fed6a 2.0.3 -From 889929e30c5ce608411d40106e04dd120ffffb09 Mon Sep 17 00:00:00 2001 +From ccc6d36d045acb40043635bd123924ec0a470920 Mon Sep 17 00:00:00 2001 From: P33M Date: Thu, 24 Jul 2014 21:24:03 +0100 -Subject: [PATCH 67/69] usb: core: make overcurrent messages more prominent +Subject: [PATCH 67/71] usb: core: make overcurrent messages more prominent Hub overcurrent messages are more serious than "debug". Increase loglevel. --- @@ -114242,10 +114242,10 @@ index 0e950ad..6b004cf 100644 2.0.3 -From 31b51048cc569d29cb2d4dfff91090bd18f9e7e2 Mon Sep 17 00:00:00 2001 +From ff963183dca11df32ba8ef1c1bfbb732f07a50a8 Mon Sep 17 00:00:00 2001 From: Tim Gover Date: Tue, 22 Jul 2014 15:41:04 +0100 -Subject: [PATCH 68/69] vcsm: VideoCore shared memory service for BCM2835 +Subject: [PATCH 68/71] vcsm: VideoCore shared memory service for BCM2835 Add experimental support for the VideoCore shared memory service. This allows user processes to allocate memory from VideoCore's @@ -118581,10 +118581,10 @@ index 0000000..da1c523 2.0.3 -From 0720b9af1c2a0b0a4a167172cc04957176b53700 Mon Sep 17 00:00:00 2001 +From 0765c9fb777f1b40097b74bbaf1944920ceae0d0 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Thu, 7 Aug 2014 02:03:50 +0100 -Subject: [PATCH 69/69] Revert "ARM: dma: Use dma_pfn_offset for dma address +Subject: [PATCH 69/71] Revert "ARM: dma: Use dma_pfn_offset for dma address translation" This reverts commit 6ce0d20016925d031f1e24d64302e4c976d7cec6. @@ -118638,3 +118638,3359 @@ index c45b61a..0af6bd0 100644 -- 2.0.3 + +From e9e133a3ad8d0946a687a3fc0c10aa7648952486 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 8 Aug 2014 16:22:59 +0100 +Subject: [PATCH 70/71] VCHIQ: Make service closure fully synchronous (drv) + +This is one half of a two-part patch, the other half of which is to +the vchiq_lib user library. With these patches, calls to +vchiq_close_service and vchiq_remove_service won't return until any +associated callbacks have been delivered to the callback thread. +--- + .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 122 ++++++++++++++++++--- + .../vc04_services/interface/vchiq_arm/vchiq_cfg.h | 10 +- + .../vc04_services/interface/vchiq_arm/vchiq_core.c | 26 +++++ + .../vc04_services/interface/vchiq_arm/vchiq_core.h | 4 + + .../interface/vchiq_arm/vchiq_ioctl.h | 4 +- + 5 files changed, 149 insertions(+), 17 deletions(-) + +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c +index 99c8967..793e1e9 100644 +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c +@@ -113,13 +113,15 @@ typedef struct user_service_struct { + VCHIQ_SERVICE_T *service; + void *userdata; + VCHIQ_INSTANCE_T instance; +- int is_vchi; +- int dequeue_pending; ++ char is_vchi; ++ char dequeue_pending; ++ char close_pending; + int message_available_pos; + int msg_insert; + int msg_remove; + struct semaphore insert_event; + struct semaphore remove_event; ++ struct semaphore close_event; + VCHIQ_HEADER_T * msg_queue[MSG_QUEUE_SIZE]; + } USER_SERVICE_T; + +@@ -142,6 +144,7 @@ struct vchiq_instance_struct { + int closing; + int pid; + int mark; ++ int use_close_delivered; + + struct list_head bulk_waiter_list; + struct mutex bulk_waiter_list_mutex; +@@ -179,7 +182,9 @@ static const char *const ioctl_names[] = { + "USE_SERVICE", + "RELEASE_SERVICE", + "SET_SERVICE_OPTION", +- "DUMP_PHYS_MEM" ++ "DUMP_PHYS_MEM", ++ "LIB_VERSION", ++ "CLOSE_DELIVERED" + }; + + vchiq_static_assert((sizeof(ioctl_names)/sizeof(ioctl_names[0])) == +@@ -231,10 +236,13 @@ add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason, + completion->service_userdata = user_service->service; + completion->bulk_userdata = bulk_userdata; + +- if (reason == VCHIQ_SERVICE_CLOSED) ++ if (reason == VCHIQ_SERVICE_CLOSED) { + /* Take an extra reference, to be held until + this CLOSED notification is delivered. */ + lock_service(user_service->service); ++ if (instance->use_close_delivered) ++ user_service->close_pending = 1; ++ } + + /* A write barrier is needed here to ensure that the entire completion + record is written out before the insert point. */ +@@ -281,10 +289,10 @@ service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, + return VCHIQ_SUCCESS; + + vchiq_log_trace(vchiq_arm_log_level, +- "service_callback - service %lx(%d), reason %d, header %lx, " ++ "service_callback - service %lx(%d,%p), reason %d, header %lx, " + "instance %lx, bulk_userdata %lx", + (unsigned long)user_service, +- service->localport, ++ service->localport, user_service->userdata, + reason, (unsigned long)header, + (unsigned long)instance, (unsigned long)bulk_userdata); + +@@ -371,10 +379,31 @@ user_service_free(void *userdata) + + /**************************************************************************** + * +-* vchiq_ioctl ++* close_delivered + * + ***************************************************************************/ ++static void close_delivered(USER_SERVICE_T *user_service) ++{ ++ vchiq_log_info(vchiq_arm_log_level, ++ "close_delivered(handle=%x)", ++ user_service->service->handle); ++ ++ WARN_ON(user_service->close_pending == 0); + ++ /* Allow the underlying service to be culled */ ++ unlock_service(user_service->service); ++ ++ /* Wake the user-thread blocked in close_ or remove_service */ ++ up(&user_service->close_event); ++ ++ user_service->close_pending = 0; ++} ++ ++/**************************************************************************** ++* ++* vchiq_ioctl ++* ++***************************************************************************/ + static long + vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + { +@@ -485,14 +514,16 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + user_service->service = service; + user_service->userdata = userdata; + user_service->instance = instance; +- user_service->is_vchi = args.is_vchi; ++ user_service->is_vchi = (args.is_vchi != 0); + user_service->dequeue_pending = 0; ++ user_service->close_pending = 0; + user_service->message_available_pos = + instance->completion_remove - 1; + user_service->msg_insert = 0; + user_service->msg_remove = 0; + sema_init(&user_service->insert_event, 0); + sema_init(&user_service->remove_event, 0); ++ sema_init(&user_service->close_event, 0); + + if (args.is_open) { + status = vchiq_open_service_internal +@@ -526,8 +557,24 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; + + service = find_service_for_instance(instance, handle); +- if (service != NULL) +- status = vchiq_close_service(service->handle); ++ if (service != NULL) { ++ USER_SERVICE_T *user_service = ++ (USER_SERVICE_T *)service->base.userdata; ++ /* close_pending is false on first entry, and when the ++ wait in vchiq_close_service has been interrupted. */ ++ if (!user_service->close_pending) { ++ status = vchiq_close_service(service->handle); ++ if (status != VCHIQ_SUCCESS) ++ break; ++ } ++ ++ /* close_pending is true once the underlying service ++ has been closed until the client library calls the ++ CLOSE_DELIVERED ioctl, signalling close_event. */ ++ if (user_service->close_pending && ++ down_interruptible(&user_service->close_event)) ++ status = VCHIQ_RETRY; ++ } + else + ret = -EINVAL; + } break; +@@ -536,8 +583,24 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; + + service = find_service_for_instance(instance, handle); +- if (service != NULL) +- status = vchiq_remove_service(service->handle); ++ if (service != NULL) { ++ USER_SERVICE_T *user_service = ++ (USER_SERVICE_T *)service->base.userdata; ++ /* close_pending is false on first entry, and when the ++ wait in vchiq_close_service has been interrupted. */ ++ if (!user_service->close_pending) { ++ status = vchiq_remove_service(service->handle); ++ if (status != VCHIQ_SUCCESS) ++ break; ++ } ++ ++ /* close_pending is true once the underlying service ++ has been closed until the client library calls the ++ CLOSE_DELIVERED ioctl, signalling close_event. */ ++ if (user_service->close_pending && ++ down_interruptible(&user_service->close_event)) ++ status = VCHIQ_RETRY; ++ } + else + ret = -EINVAL; + } break; +@@ -804,8 +867,9 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + completion->header = msgbuf; + } + +- if (completion->reason == +- VCHIQ_SERVICE_CLOSED) ++ if ((completion->reason == ++ VCHIQ_SERVICE_CLOSED) && ++ !instance->use_close_delivered) + unlock_service(service); + + if (copy_to_user((void __user *)( +@@ -983,6 +1047,28 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + dump_phys_mem(args.virt_addr, args.num_bytes); + } break; + ++ case VCHIQ_IOC_LIB_VERSION: { ++ unsigned int lib_version = (unsigned int)arg; ++ ++ if (lib_version < VCHIQ_VERSION_MIN) ++ ret = -EINVAL; ++ else if (lib_version >= VCHIQ_VERSION_CLOSE_DELIVERED) ++ instance->use_close_delivered = 1; ++ } break; ++ ++ case VCHIQ_IOC_CLOSE_DELIVERED: { ++ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; ++ ++ service = find_closed_service_for_instance(instance, handle); ++ if (service != NULL) { ++ USER_SERVICE_T *user_service = ++ (USER_SERVICE_T *)service->base.userdata; ++ close_delivered(user_service); ++ } ++ else ++ ret = -EINVAL; ++ } break; ++ + default: + ret = -ENOTTY; + break; +@@ -1170,7 +1256,15 @@ vchiq_release(struct inode *inode, struct file *file) + (MAX_COMPLETIONS - 1)]; + service = completion->service_userdata; + if (completion->reason == VCHIQ_SERVICE_CLOSED) ++ { ++ USER_SERVICE_T *user_service = ++ service->base.userdata; ++ ++ /* Wake any blocked user-thread */ ++ if (instance->use_close_delivered) ++ up(&user_service->close_event); + unlock_service(service); ++ } + instance->completion_remove++; + } + +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h +index 493c86c..c382740 100644 +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h +@@ -1,5 +1,5 @@ + /** +- * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * Copyright (c) 2010-2014 Broadcom. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -36,11 +36,17 @@ + + #define VCHIQ_MAGIC VCHIQ_MAKE_FOURCC('V', 'C', 'H', 'I') + /* The version of VCHIQ - change with any non-trivial change */ +-#define VCHIQ_VERSION 6 ++#define VCHIQ_VERSION 7 + /* The minimum compatible version - update to match VCHIQ_VERSION with any + ** incompatible change */ + #define VCHIQ_VERSION_MIN 3 + ++/* The version that introduced the VCHIQ_IOC_LIB_VERSION ioctl */ ++#define VCHIQ_VERSION_LIB_VERSION 7 ++ ++/* The version that introduced the VCHIQ_IOC_CLOSE_DELIVERED ioctl */ ++#define VCHIQ_VERSION_CLOSE_DELIVERED 7 ++ + #define VCHIQ_MAX_STATES 1 + #define VCHIQ_MAX_SERVICES 4096 + #define VCHIQ_MAX_SLOTS 128 +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c +index 71ed0a5..726107e 100644 +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c +@@ -224,6 +224,31 @@ find_service_for_instance(VCHIQ_INSTANCE_T instance, + } + + VCHIQ_SERVICE_T * ++find_closed_service_for_instance(VCHIQ_INSTANCE_T instance, ++ VCHIQ_SERVICE_HANDLE_T handle) { ++ VCHIQ_SERVICE_T *service; ++ ++ spin_lock(&service_spinlock); ++ service = handle_to_service(handle); ++ if (service && ++ ((service->srvstate == VCHIQ_SRVSTATE_FREE) || ++ (service->srvstate == VCHIQ_SRVSTATE_CLOSED)) && ++ (service->handle == handle) && ++ (service->instance == instance)) { ++ BUG_ON(service->ref_count == 0); ++ service->ref_count++; ++ } else ++ service = NULL; ++ spin_unlock(&service_spinlock); ++ ++ if (!service) ++ vchiq_log_info(vchiq_core_log_level, ++ "Invalid service handle 0x%x", handle); ++ ++ return service; ++} ++ ++VCHIQ_SERVICE_T * + next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance, + int *pidx) + { +@@ -2632,6 +2657,7 @@ vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id) + VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN, service->localport, 0), + &body, 1, sizeof(payload), 1); + if (status == VCHIQ_SUCCESS) { ++ /* Wait for the ACK/NAK */ + if (down_interruptible(&service->remove_event) != 0) { + status = VCHIQ_RETRY; + vchiq_release_service_internal(service); +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h +index 47cdf27..4baf536 100644 +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h +@@ -605,6 +605,10 @@ find_service_for_instance(VCHIQ_INSTANCE_T instance, + VCHIQ_SERVICE_HANDLE_T handle); + + extern VCHIQ_SERVICE_T * ++find_closed_service_for_instance(VCHIQ_INSTANCE_T instance, ++ VCHIQ_SERVICE_HANDLE_T handle); ++ ++extern VCHIQ_SERVICE_T * + next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance, + int *pidx); + +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h +index e248037..6137ae9 100644 +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h +@@ -124,6 +124,8 @@ typedef struct { + _IOW(VCHIQ_IOC_MAGIC, 14, VCHIQ_SET_SERVICE_OPTION_T) + #define VCHIQ_IOC_DUMP_PHYS_MEM \ + _IOW(VCHIQ_IOC_MAGIC, 15, VCHIQ_DUMP_MEM_T) +-#define VCHIQ_IOC_MAX 15 ++#define VCHIQ_IOC_LIB_VERSION _IO(VCHIQ_IOC_MAGIC, 16) ++#define VCHIQ_IOC_CLOSE_DELIVERED _IO(VCHIQ_IOC_MAGIC, 17) ++#define VCHIQ_IOC_MAX 17 + + #endif +-- +2.0.3 + + +From 33f49e8bf532404304798189d98393f6b2b055df Mon Sep 17 00:00:00 2001 +From: gellert +Date: Fri, 15 Aug 2014 15:24:58 +0100 +Subject: [PATCH 71/71] Add new bcm2835_mmc (sdcard) driver and dma driver + +Enable with bcm2708.bcm2835_mmc=1 +--- + arch/arm/configs/bcmrpi_defconfig | 7 +- + arch/arm/mach-bcm2708/bcm2708.c | 35 +- + arch/arm/mach-bcm2708/dma.c | 2 + + arch/arm/mach-bcm2708/include/mach/dma.h | 6 +- + drivers/dma/bcm2708-dmaengine.c | 729 +++++++++++--- + drivers/mmc/host/Kconfig | 29 + + drivers/mmc/host/Makefile | 1 + + drivers/mmc/host/bcm2835-mmc.c | 1537 ++++++++++++++++++++++++++++++ + drivers/mmc/host/sdhci-bcm2708.c | 116 ++- + 9 files changed, 2275 insertions(+), 187 deletions(-) + create mode 100644 drivers/mmc/host/bcm2835-mmc.c + +diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig +index 105bcbe..d0f0781 100644 +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -894,6 +894,9 @@ CONFIG_USB_ISIGHTFW=m + CONFIG_USB_YUREX=m + CONFIG_MMC=y + CONFIG_MMC_BLOCK_MINORS=32 ++CONFIG_MMC_BCM2835=y ++CONFIG_MMC_BCM2835_DMA=y ++CONFIG_MMC_PIO_DMA_BARRIER=2 + CONFIG_MMC_SDHCI=y + CONFIG_MMC_SDHCI_PLTFM=y + CONFIG_MMC_SDHCI_BCM2708=y +@@ -944,9 +947,9 @@ CONFIG_RTC_DRV_DS3234=m + CONFIG_RTC_DRV_PCF2123=m + CONFIG_RTC_DRV_RX4581=m + CONFIG_DMADEVICES=y +-CONFIG_DMA_BCM2708=m ++CONFIG_DMA_BCM2708=y + CONFIG_DMA_ENGINE=y +-CONFIG_DMA_VIRTUAL_CHANNELS=m ++CONFIG_DMA_VIRTUAL_CHANNELS=y + CONFIG_UIO=m + CONFIG_UIO_PDRV_GENIRQ=m + CONFIG_STAGING=y +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index a19f54d..a730d5d 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -92,6 +92,7 @@ static unsigned disk_led_active_low = 1; + static unsigned reboot_part = 0; + static unsigned w1_gpio_pin = W1_GPIO; + static unsigned w1_gpio_pullup = W1_PULLUP; ++static unsigned bcm2835_mmc = 0; + + static void __init bcm2708_init_led(void); + +@@ -442,6 +443,34 @@ struct platform_device bcm2708_emmc_device = { + }; + #endif /* CONFIG_MMC_SDHCI_BCM2708 */ + ++#ifdef CONFIG_MMC_BCM2835 /* Arasan emmc SD (new) */ ++static struct resource bcm2835_emmc_resources[] = { ++ [0] = { ++ .start = EMMC_BASE, ++ .end = EMMC_BASE + SZ_256 - 1, /* we only need this area */ ++ /* the memory map actually makes SZ_4K available */ ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_ARASANSDIO, ++ .end = IRQ_ARASANSDIO, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static u64 bcm2835_emmc_dmamask = 0xffffffffUL; ++ ++struct platform_device bcm2835_emmc_device = { ++ .name = "mmc-bcm2835", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2835_emmc_resources), ++ .resource = bcm2835_emmc_resources, ++ .dev = { ++ .dma_mask = &bcm2835_emmc_dmamask, ++ .coherent_dma_mask = 0xffffffffUL}, ++}; ++#endif /* CONFIG_MMC_BCM2835 */ ++ + static struct resource bcm2708_powerman_resources[] = { + [0] = { + .start = PM_BASE, +@@ -823,7 +852,10 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2708_powerman_device); + + #ifdef CONFIG_MMC_SDHCI_BCM2708 +- bcm_register_device(&bcm2708_emmc_device); ++ if (!bcm2835_mmc) bcm_register_device(&bcm2708_emmc_device); ++#endif ++#ifdef CONFIG_MMC_BCM2835 ++ if (bcm2835_mmc) bcm_register_device(&bcm2835_emmc_device); + #endif + bcm2708_init_led(); + for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) +@@ -1053,3 +1085,4 @@ module_param(disk_led_active_low, uint, 0644); + module_param(reboot_part, uint, 0644); + module_param(w1_gpio_pin, uint, 0644); + module_param(w1_gpio_pullup, uint, 0644); ++module_param(bcm2835_mmc, uint, 0644); +diff --git a/arch/arm/mach-bcm2708/dma.c b/arch/arm/mach-bcm2708/dma.c +index 1da2413..a5e58d1 100644 +--- a/arch/arm/mach-bcm2708/dma.c ++++ b/arch/arm/mach-bcm2708/dma.c +@@ -156,6 +156,8 @@ static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base, + dmaman->chan_available = chans_available; + dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c; /* chans 2 & 3 */ + dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01; /* chan 0 */ ++ dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe; /* chans 1 to 7 */ ++ dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00; /* chans 8 to 14 */ + } + + static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman, +diff --git a/arch/arm/mach-bcm2708/include/mach/dma.h b/arch/arm/mach-bcm2708/include/mach/dma.h +index a4aac4c..d03e7b5 100644 +--- a/arch/arm/mach-bcm2708/include/mach/dma.h ++++ b/arch/arm/mach-bcm2708/include/mach/dma.h +@@ -77,9 +77,13 @@ extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base); + those with higher priority smaller ordinal numbers */ + #define BCM_DMA_FEATURE_FAST_ORD 0 + #define BCM_DMA_FEATURE_BULK_ORD 1 ++#define BCM_DMA_FEATURE_NORMAL_ORD 2 ++#define BCM_DMA_FEATURE_LITE_ORD 3 + #define BCM_DMA_FEATURE_FAST (1< +- * Copyright 2013 ++ * Gellert Weisz ++ * Copyright 2013-2014 + * + * Based on + * OMAP DMAengine support by Russell King +@@ -29,6 +29,7 @@ + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ ++ + #include + #include + #include +@@ -40,21 +41,38 @@ + #include + #include + #include +-#include + +-#include "virt-dma.h" ++#ifndef CONFIG_OF + ++/* dma manager */ + #include +-#include + +-struct bcm2708_dmadev { ++#endif ++ ++#include ++#include ++ ++#include "virt-dma.h" ++ ++ ++struct bcm2835_dmadev { + struct dma_device ddev; + spinlock_t lock; + void __iomem *base; + struct device_dma_parameters dma_parms; + }; + +-struct bcm2708_chan { ++struct bcm2835_dma_cb { ++ uint32_t info; ++ uint32_t src; ++ uint32_t dst; ++ uint32_t length; ++ uint32_t stride; ++ uint32_t next; ++ uint32_t pad[2]; ++}; ++ ++struct bcm2835_chan { + struct virt_dma_chan vc; + struct list_head node; + +@@ -62,48 +80,105 @@ struct bcm2708_chan { + bool cyclic; + + int ch; +- struct bcm2708_desc *desc; ++ struct bcm2835_desc *desc; + + void __iomem *chan_base; + int irq_number; ++ ++ unsigned int dreq; + }; + +-struct bcm2708_desc { ++struct bcm2835_desc { + struct virt_dma_desc vd; + enum dma_transfer_direction dir; + + unsigned int control_block_size; +- struct bcm2708_dma_cb *control_block_base; ++ struct bcm2835_dma_cb *control_block_base; + dma_addr_t control_block_base_phys; + +- unsigned frames; ++ unsigned int frames; + size_t size; + }; + +-#define BCM2708_DMA_DATA_TYPE_S8 1 +-#define BCM2708_DMA_DATA_TYPE_S16 2 +-#define BCM2708_DMA_DATA_TYPE_S32 4 +-#define BCM2708_DMA_DATA_TYPE_S128 16 ++#define BCM2835_DMA_CS 0x00 ++#define BCM2835_DMA_ADDR 0x04 ++#define BCM2835_DMA_SOURCE_AD 0x0c ++#define BCM2835_DMA_DEST_AD 0x10 ++#define BCM2835_DMA_NEXTCB 0x1C ++ ++/* DMA CS Control and Status bits */ ++#define BCM2835_DMA_ACTIVE BIT(0) ++#define BCM2835_DMA_INT BIT(2) ++#define BCM2835_DMA_ISPAUSED BIT(4) /* Pause requested or not active */ ++#define BCM2835_DMA_ISHELD BIT(5) /* Is held by DREQ flow control */ ++#define BCM2835_DMA_ERR BIT(8) ++#define BCM2835_DMA_ABORT BIT(30) /* Stop current CB, go to next, WO */ ++#define BCM2835_DMA_RESET BIT(31) /* WO, self clearing */ ++ ++#define BCM2835_DMA_INT_EN BIT(0) ++#define BCM2835_DMA_D_INC BIT(4) ++#define BCM2835_DMA_D_WIDTH BIT(5) ++#define BCM2835_DMA_D_DREQ BIT(6) ++#define BCM2835_DMA_S_INC BIT(8) ++#define BCM2835_DMA_S_WIDTH BIT(9) ++#define BCM2835_DMA_S_DREQ BIT(10) ++ ++#define BCM2835_DMA_PER_MAP(x) ((x) << 16) ++#define BCM2835_DMA_WAITS(x) (((x)&0x1f) << 21) + +-static inline struct bcm2708_dmadev *to_bcm2708_dma_dev(struct dma_device *d) ++#define SDHCI_BCM_DMA_WAITS 30 /* delays slowing DMA transfers: 0-31 */ ++ ++#define BCM2835_DMA_DATA_TYPE_S8 1 ++#define BCM2835_DMA_DATA_TYPE_S16 2 ++#define BCM2835_DMA_DATA_TYPE_S32 4 ++#define BCM2835_DMA_DATA_TYPE_S128 16 ++ ++#define BCM2835_DMA_BULK_MASK BIT(0) ++#define BCM2835_DMA_FIQ_MASK (BIT(2) | BIT(3)) ++ ++ ++/* Valid only for channels 0 - 14, 15 has its own base address */ ++#define BCM2835_DMA_CHAN(n) ((n) << 8) /* Base address */ ++#define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n)) ++ ++#define MAX_LITE_TRANSFER 32768 ++#define MAX_NORMAL_TRANSFER 1073741824 ++ ++static inline struct bcm2835_dmadev *to_bcm2835_dma_dev(struct dma_device *d) + { +- return container_of(d, struct bcm2708_dmadev, ddev); ++ return container_of(d, struct bcm2835_dmadev, ddev); + } + +-static inline struct bcm2708_chan *to_bcm2708_dma_chan(struct dma_chan *c) ++static inline struct bcm2835_chan *to_bcm2835_dma_chan(struct dma_chan *c) + { +- return container_of(c, struct bcm2708_chan, vc.chan); ++ return container_of(c, struct bcm2835_chan, vc.chan); + } + +-static inline struct bcm2708_desc *to_bcm2708_dma_desc( ++static inline struct bcm2835_desc *to_bcm2835_dma_desc( + struct dma_async_tx_descriptor *t) + { +- return container_of(t, struct bcm2708_desc, vd.tx); ++ return container_of(t, struct bcm2835_desc, vd.tx); + } + +-static void bcm2708_dma_desc_free(struct virt_dma_desc *vd) ++static void dma_dumpregs(struct bcm2835_chan *c) + { +- struct bcm2708_desc *desc = container_of(vd, struct bcm2708_desc, vd); ++ pr_debug("-------------DMA DUMPREGS-------------\n"); ++ pr_debug("CS= %u\n", ++ readl(c->chan_base + BCM2835_DMA_CS)); ++ pr_debug("ADDR= %u\n", ++ readl(c->chan_base + BCM2835_DMA_ADDR)); ++ pr_debug("SOURCE_ADDR= %u\n", ++ readl(c->chan_base + BCM2835_DMA_SOURCE_AD)); ++ pr_debug("DEST_AD= %u\n", ++ readl(c->chan_base + BCM2835_DMA_DEST_AD)); ++ pr_debug("NEXTCB= %u\n", ++ readl(c->chan_base + BCM2835_DMA_NEXTCB)); ++ pr_debug("--------------------------------------\n"); ++} ++ ++static void bcm2835_dma_desc_free(struct virt_dma_desc *vd) ++{ ++ struct bcm2835_desc *desc = container_of(vd, struct bcm2835_desc, vd); + dma_free_coherent(desc->vd.tx.chan->device->dev, + desc->control_block_size, + desc->control_block_base, +@@ -111,10 +186,46 @@ static void bcm2708_dma_desc_free(struct virt_dma_desc *vd) + kfree(desc); + } + +-static void bcm2708_dma_start_desc(struct bcm2708_chan *c) ++static int bcm2835_dma_abort(void __iomem *chan_base) ++{ ++ unsigned long cs; ++ long int timeout = 10000; ++ ++ cs = readl(chan_base + BCM2835_DMA_CS); ++ if (!(cs & BCM2835_DMA_ACTIVE)) ++ return 0; ++ ++ /* Write 0 to the active bit - Pause the DMA */ ++ writel(0, chan_base + BCM2835_DMA_CS); ++ ++ /* Wait for any current AXI transfer to complete */ ++ while ((cs & BCM2835_DMA_ISPAUSED) && --timeout) { ++ cpu_relax(); ++ cs = readl(chan_base + BCM2835_DMA_CS); ++ } ++ ++ /* We'll un-pause when we set of our next DMA */ ++ if (!timeout) ++ return -ETIMEDOUT; ++ ++ if (!(cs & BCM2835_DMA_ACTIVE)) ++ return 0; ++ ++ /* Terminate the control block chain */ ++ writel(0, chan_base + BCM2835_DMA_NEXTCB); ++ ++ /* Abort the whole DMA */ ++ writel(BCM2835_DMA_ABORT | BCM2835_DMA_ACTIVE, ++ chan_base + BCM2835_DMA_CS); ++ ++ return 0; ++} ++ ++ ++static void bcm2835_dma_start_desc(struct bcm2835_chan *c) + { + struct virt_dma_desc *vd = vchan_next_desc(&c->vc); +- struct bcm2708_desc *d; ++ struct bcm2835_desc *d; + + if (!vd) { + c->desc = NULL; +@@ -123,49 +234,62 @@ static void bcm2708_dma_start_desc(struct bcm2708_chan *c) + + list_del(&vd->node); + +- c->desc = d = to_bcm2708_dma_desc(&vd->tx); ++ c->desc = d = to_bcm2835_dma_desc(&vd->tx); ++ ++ writel(d->control_block_base_phys, c->chan_base + BCM2835_DMA_ADDR); ++ writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS); + +- bcm_dma_start(c->chan_base, d->control_block_base_phys); + } + +-static irqreturn_t bcm2708_dma_callback(int irq, void *data) ++static irqreturn_t bcm2835_dma_callback(int irq, void *data) + { +- struct bcm2708_chan *c = data; +- struct bcm2708_desc *d; ++ struct bcm2835_chan *c = data; ++ struct bcm2835_desc *d; + unsigned long flags; + + spin_lock_irqsave(&c->vc.lock, flags); + + /* Acknowledge interrupt */ +- writel(BCM2708_DMA_INT, c->chan_base + BCM2708_DMA_CS); ++ writel(BCM2835_DMA_INT, c->chan_base + BCM2835_DMA_CS); + + d = c->desc; + + if (d) { +- /* TODO Only works for cyclic DMA */ +- vchan_cyclic_callback(&d->vd); +- } ++ if (c->cyclic) { ++ vchan_cyclic_callback(&d->vd); ++ ++ /* Keep the DMA engine running */ ++ writel(BCM2835_DMA_ACTIVE, ++ c->chan_base + BCM2835_DMA_CS); + +- /* Keep the DMA engine running */ +- dsb(); /* ARM synchronization barrier */ +- writel(BCM2708_DMA_ACTIVE, c->chan_base + BCM2708_DMA_CS); ++ } else { ++ vchan_cookie_complete(&c->desc->vd); ++ bcm2835_dma_start_desc(c); ++ } ++ } + + spin_unlock_irqrestore(&c->vc.lock, flags); + + return IRQ_HANDLED; + } + +-static int bcm2708_dma_alloc_chan_resources(struct dma_chan *chan) ++static int bcm2835_dma_alloc_chan_resources(struct dma_chan *chan) + { +- struct bcm2708_chan *c = to_bcm2708_dma_chan(chan); ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ int ret; ++ ++ dev_dbg(c->vc.chan.device->dev, ++ "Allocating DMA channel %d\n", c->ch); + +- return request_irq(c->irq_number, +- bcm2708_dma_callback, 0, "DMA IRQ", c); ++ ret = request_irq(c->irq_number, ++ bcm2835_dma_callback, 0, "DMA IRQ", c); ++ ++ return ret; + } + +-static void bcm2708_dma_free_chan_resources(struct dma_chan *chan) ++static void bcm2835_dma_free_chan_resources(struct dma_chan *chan) + { +- struct bcm2708_chan *c = to_bcm2708_dma_chan(chan); ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); + + vchan_free_chan_resources(&c->vc); + free_irq(c->irq_number, c); +@@ -173,18 +297,18 @@ static void bcm2708_dma_free_chan_resources(struct dma_chan *chan) + dev_dbg(c->vc.chan.device->dev, "Freeing DMA channel %u\n", c->ch); + } + +-static size_t bcm2708_dma_desc_size(struct bcm2708_desc *d) ++static size_t bcm2835_dma_desc_size(struct bcm2835_desc *d) + { + return d->size; + } + +-static size_t bcm2708_dma_desc_size_pos(struct bcm2708_desc *d, dma_addr_t addr) ++static size_t bcm2835_dma_desc_size_pos(struct bcm2835_desc *d, dma_addr_t addr) + { +- unsigned i; ++ unsigned int i; + size_t size; + + for (size = i = 0; i < d->frames; i++) { +- struct bcm2708_dma_cb *control_block = ++ struct bcm2835_dma_cb *control_block = + &d->control_block_base[i]; + size_t this_size = control_block->length; + dma_addr_t dma; +@@ -203,13 +327,15 @@ static size_t bcm2708_dma_desc_size_pos(struct bcm2708_desc *d, dma_addr_t addr) + return size; + } + +-static enum dma_status bcm2708_dma_tx_status(struct dma_chan *chan, ++static enum dma_status bcm2835_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) + { +- struct bcm2708_chan *c = to_bcm2708_dma_chan(chan); ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ struct bcm2835_desc *d; + struct virt_dma_desc *vd; + enum dma_status ret; + unsigned long flags; ++ dma_addr_t pos; + + ret = dma_cookie_status(chan, cookie, txstate); + if (ret == DMA_COMPLETE || !txstate) +@@ -219,19 +345,18 @@ static enum dma_status bcm2708_dma_tx_status(struct dma_chan *chan, + vd = vchan_find_desc(&c->vc, cookie); + if (vd) { + txstate->residue = +- bcm2708_dma_desc_size(to_bcm2708_dma_desc(&vd->tx)); ++ bcm2835_dma_desc_size(to_bcm2835_dma_desc(&vd->tx)); + } else if (c->desc && c->desc->vd.tx.cookie == cookie) { +- struct bcm2708_desc *d = c->desc; +- dma_addr_t pos; ++ d = c->desc; + + if (d->dir == DMA_MEM_TO_DEV) +- pos = readl(c->chan_base + BCM2708_DMA_SOURCE_AD); ++ pos = readl(c->chan_base + BCM2835_DMA_SOURCE_AD); + else if (d->dir == DMA_DEV_TO_MEM) +- pos = readl(c->chan_base + BCM2708_DMA_DEST_AD); ++ pos = readl(c->chan_base + BCM2835_DMA_DEST_AD); + else + pos = 0; + +- txstate->residue = bcm2708_dma_desc_size_pos(d, pos); ++ txstate->residue = bcm2835_dma_desc_size_pos(d, pos); + } else { + txstate->residue = 0; + } +@@ -241,50 +366,50 @@ static enum dma_status bcm2708_dma_tx_status(struct dma_chan *chan, + return ret; + } + +-static void bcm2708_dma_issue_pending(struct dma_chan *chan) ++static void bcm2835_dma_issue_pending(struct dma_chan *chan) + { +- struct bcm2708_chan *c = to_bcm2708_dma_chan(chan); ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); + unsigned long flags; + +- c->cyclic = true; /* Nothing else is implemented */ +- + spin_lock_irqsave(&c->vc.lock, flags); + if (vchan_issue_pending(&c->vc) && !c->desc) +- bcm2708_dma_start_desc(c); ++ bcm2835_dma_start_desc(c); + + spin_unlock_irqrestore(&c->vc.lock, flags); + } + +-static struct dma_async_tx_descriptor *bcm2708_dma_prep_dma_cyclic( ++static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) + { +- struct bcm2708_chan *c = to_bcm2708_dma_chan(chan); ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); + enum dma_slave_buswidth dev_width; +- struct bcm2708_desc *d; ++ struct bcm2835_desc *d; + dma_addr_t dev_addr; +- unsigned es, sync_type; +- unsigned frame; ++ unsigned int es, sync_type; ++ unsigned int frame; + + /* Grab configuration */ ++ if (!is_slave_direction(direction)) { ++ dev_err(chan->device->dev, "%s: bad direction?\n", __func__); ++ return NULL; ++ } ++ + if (direction == DMA_DEV_TO_MEM) { + dev_addr = c->cfg.src_addr; + dev_width = c->cfg.src_addr_width; +- sync_type = BCM2708_DMA_S_DREQ; +- } else if (direction == DMA_MEM_TO_DEV) { ++ sync_type = BCM2835_DMA_S_DREQ; ++ } else { + dev_addr = c->cfg.dst_addr; + dev_width = c->cfg.dst_addr_width; +- sync_type = BCM2708_DMA_D_DREQ; +- } else { +- dev_err(chan->device->dev, "%s: bad direction?\n", __func__); +- return NULL; ++ sync_type = BCM2835_DMA_D_DREQ; + } + + /* Bus width translates to the element size (ES) */ + switch (dev_width) { + case DMA_SLAVE_BUSWIDTH_4_BYTES: +- es = BCM2708_DMA_DATA_TYPE_S32; ++ es = BCM2835_DMA_DATA_TYPE_S32; + break; + default: + return NULL; +@@ -299,7 +424,7 @@ static struct dma_async_tx_descriptor *bcm2708_dma_prep_dma_cyclic( + d->frames = buf_len / period_len; + + /* Allocate memory for control blocks */ +- d->control_block_size = d->frames * sizeof(struct bcm2708_dma_cb); ++ d->control_block_size = d->frames * sizeof(struct bcm2835_dma_cb); + d->control_block_base = dma_zalloc_coherent(chan->device->dev, + d->control_block_size, &d->control_block_base_phys, + GFP_NOWAIT); +@@ -314,22 +439,22 @@ static struct dma_async_tx_descriptor *bcm2708_dma_prep_dma_cyclic( + * for each frame and link them together. + */ + for (frame = 0; frame < d->frames; frame++) { +- struct bcm2708_dma_cb *control_block = ++ struct bcm2835_dma_cb *control_block = + &d->control_block_base[frame]; + + /* Setup adresses */ + if (d->dir == DMA_DEV_TO_MEM) { +- control_block->info = BCM2708_DMA_D_INC; ++ control_block->info = BCM2835_DMA_D_INC; + control_block->src = dev_addr; + control_block->dst = buf_addr + frame * period_len; + } else { +- control_block->info = BCM2708_DMA_S_INC; ++ control_block->info = BCM2835_DMA_S_INC; + control_block->src = buf_addr + frame * period_len; + control_block->dst = dev_addr; + } + + /* Enable interrupt */ +- control_block->info |= BCM2708_DMA_INT_EN; ++ control_block->info |= BCM2835_DMA_INT_EN; + + /* Setup synchronization */ + if (sync_type != 0) +@@ -338,7 +463,7 @@ static struct dma_async_tx_descriptor *bcm2708_dma_prep_dma_cyclic( + /* Setup DREQ channel */ + if (c->cfg.slave_id != 0) + control_block->info |= +- BCM2708_DMA_PER_MAP(c->cfg.slave_id); ++ BCM2835_DMA_PER_MAP(c->cfg.slave_id); + + /* Length of a frame */ + control_block->length = period_len; +@@ -346,18 +471,166 @@ static struct dma_async_tx_descriptor *bcm2708_dma_prep_dma_cyclic( + + /* + * Next block is the next frame. +- * This DMA engine driver currently only supports cyclic DMA. ++ * This function is called on cyclic DMA transfers. + * Therefore, wrap around at number of frames. + */ + control_block->next = d->control_block_base_phys + +- sizeof(struct bcm2708_dma_cb) ++ sizeof(struct bcm2835_dma_cb) + * ((frame + 1) % d->frames); + } + ++ c->cyclic = true; ++ + return vchan_tx_prep(&c->vc, &d->vd, flags); + } + +-static int bcm2708_dma_slave_config(struct bcm2708_chan *c, ++ ++static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg( ++ struct dma_chan *chan, struct scatterlist *sgl, ++ unsigned int sg_len, enum dma_transfer_direction direction, ++ unsigned long flags, void *context) ++{ ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ enum dma_slave_buswidth dev_width; ++ struct bcm2835_desc *d; ++ dma_addr_t dev_addr; ++ struct scatterlist *sgent; ++ unsigned int es, sync_type; ++ unsigned int i, j, splitct, max_size; ++ ++ if (!is_slave_direction(direction)) { ++ dev_err(chan->device->dev, "%s: bad direction?\n", __func__); ++ return NULL; ++ } ++ ++ if (direction == DMA_DEV_TO_MEM) { ++ dev_addr = c->cfg.src_addr; ++ dev_width = c->cfg.src_addr_width; ++ sync_type = BCM2835_DMA_S_DREQ; ++ } else { ++ dev_addr = c->cfg.dst_addr; ++ dev_width = c->cfg.dst_addr_width; ++ sync_type = BCM2835_DMA_D_DREQ; ++ } ++ ++ /* Bus width translates to the element size (ES) */ ++ switch (dev_width) { ++ case DMA_SLAVE_BUSWIDTH_4_BYTES: ++ es = BCM2835_DMA_DATA_TYPE_S32; ++ break; ++ default: ++ return NULL; ++ } ++ ++ /* Now allocate and setup the descriptor. */ ++ d = kzalloc(sizeof(*d), GFP_NOWAIT); ++ if (!d) ++ return NULL; ++ ++ d->dir = direction; ++ ++ if (c->ch >= 8) /* we have a LITE channel */ ++ max_size = MAX_LITE_TRANSFER; ++ else ++ max_size = MAX_NORMAL_TRANSFER; ++ ++ /* We store the length of the SG list in d->frames ++ taking care to account for splitting up transfers ++ too large for a LITE channel */ ++ ++ d->frames = 0; ++ for_each_sg(sgl, sgent, sg_len, i) { ++ uint32_t len = sg_dma_len(sgent); ++ d->frames += 1 + len / max_size; ++ } ++ ++ /* Allocate memory for control blocks */ ++ d->control_block_size = d->frames * sizeof(struct bcm2835_dma_cb); ++ d->control_block_base = dma_zalloc_coherent(chan->device->dev, ++ d->control_block_size, &d->control_block_base_phys, ++ GFP_NOWAIT); ++ ++ if (!d->control_block_base) { ++ kfree(d); ++ return NULL; ++ } ++ ++ /* ++ * Iterate over all SG entries, create a control block ++ * for each frame and link them together. ++ */ ++ ++ /* we count the number of times an SG entry had to be splitct ++ as a result of using a LITE channel */ ++ splitct = 0; ++ ++ for_each_sg(sgl, sgent, sg_len, i) { ++ dma_addr_t addr = sg_dma_address(sgent); ++ uint32_t len = sg_dma_len(sgent); ++ ++ for (j = 0; jcontrol_block_base[i+splitct]; ++ ++ /* Setup adresses */ ++ if (d->dir == DMA_DEV_TO_MEM) { ++ control_block->info = BCM2835_DMA_D_INC | ++ BCM2835_DMA_D_WIDTH | BCM2835_DMA_S_DREQ; ++ control_block->src = dev_addr; ++ control_block->dst = addr + (dma_addr_t)j; ++ } else { ++ control_block->info = BCM2835_DMA_S_INC | ++ BCM2835_DMA_S_WIDTH | BCM2835_DMA_D_DREQ; ++ control_block->src = addr + (dma_addr_t)j; ++ control_block->dst = dev_addr; ++ } ++ ++ /* Common part */ ++ control_block->info |= BCM2835_DMA_WAITS(SDHCI_BCM_DMA_WAITS); ++ ++ /* Enable */ ++ if (i == sg_len-1 && len-j<=max_size) ++ control_block->info |= BCM2835_DMA_INT_EN; ++ ++ /* Setup synchronization */ ++ if (sync_type != 0) ++ control_block->info |= sync_type; ++ ++ /* Setup DREQ channel */ ++ c->dreq = c->cfg.slave_id; /* DREQ loaded from config */ ++ ++ if (c->dreq != 0) ++ control_block->info |= ++ BCM2835_DMA_PER_MAP(c->dreq); ++ ++ /* Length of a frame */ ++ control_block->length = min(len-j, max_size); ++ d->size += control_block->length; ++ ++ /* ++ * Next block is the next frame. ++ */ ++ if (i < sg_len-1 || len-j>max_size) { ++ /* next block is the next frame. */ ++ control_block->next = d->control_block_base_phys + ++ sizeof(struct bcm2835_dma_cb) * (i + splitct + 1); ++ } else { ++ /* next block is empty. */ ++ control_block->next = 0; ++ } ++ ++ if (len-j>max_size) ++ splitct++; ++ } ++ } ++ ++ c->cyclic = false; ++ ++ return vchan_tx_prep(&c->vc, &d->vd, flags); ++} ++ ++static int bcm2835_dma_slave_config(struct bcm2835_chan *c, + struct dma_slave_config *cfg) + { + if ((cfg->direction == DMA_DEV_TO_MEM && +@@ -373,9 +646,9 @@ static int bcm2708_dma_slave_config(struct bcm2708_chan *c, + return 0; + } + +-static int bcm2708_dma_terminate_all(struct bcm2708_chan *c) ++static int bcm2835_dma_terminate_all(struct bcm2835_chan *c) + { +- struct bcm2708_dmadev *d = to_bcm2708_dma_dev(c->vc.chan.device); ++ struct bcm2835_dmadev *d = to_bcm2835_dma_dev(c->vc.chan.device); + unsigned long flags; + int timeout = 10000; + LIST_HEAD(head); +@@ -394,19 +667,18 @@ static int bcm2708_dma_terminate_all(struct bcm2708_chan *c) + */ + if (c->desc) { + c->desc = NULL; +- bcm_dma_abort(c->chan_base); ++ bcm2835_dma_abort(c->chan_base); + + /* Wait for stopping */ +- while (timeout > 0) { +- timeout--; +- if (!(readl(c->chan_base + BCM2708_DMA_CS) & +- BCM2708_DMA_ACTIVE)) ++ while (--timeout) { ++ if (!(readl(c->chan_base + BCM2835_DMA_CS) & ++ BCM2835_DMA_ACTIVE)) + break; + + cpu_relax(); + } + +- if (timeout <= 0) ++ if (!timeout) + dev_err(d->ddev.dev, "DMA transfer could not be terminated\n"); + } + +@@ -417,34 +689,57 @@ static int bcm2708_dma_terminate_all(struct bcm2708_chan *c) + return 0; + } + +-static int bcm2708_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ++static int bcm2835_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) + { +- struct bcm2708_chan *c = to_bcm2708_dma_chan(chan); ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); + + switch (cmd) { + case DMA_SLAVE_CONFIG: +- return bcm2708_dma_slave_config(c, ++ return bcm2835_dma_slave_config(c, + (struct dma_slave_config *)arg); + + case DMA_TERMINATE_ALL: +- return bcm2708_dma_terminate_all(c); ++ return bcm2835_dma_terminate_all(c); + + default: + return -ENXIO; + } + } + +-static int bcm2708_dma_chan_init(struct bcm2708_dmadev *d, void __iomem* chan_base, +- int chan_id, int irq) ++#ifdef CONFIG_OF ++static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id, int irq) + { +- struct bcm2708_chan *c; ++ struct bcm2835_chan *c; + + c = devm_kzalloc(d->ddev.dev, sizeof(*c), GFP_KERNEL); + if (!c) + return -ENOMEM; + +- c->vc.desc_free = bcm2708_dma_desc_free; ++ c->vc.desc_free = bcm2835_dma_desc_free; ++ vchan_init(&c->vc, &d->ddev); ++ INIT_LIST_HEAD(&c->node); ++ ++ d->ddev.chancnt++; ++ ++ c->chan_base = BCM2835_DMA_CHANIO(d->base, chan_id); ++ c->ch = chan_id; ++ c->irq_number = irq; ++ ++ return 0; ++} ++#endif ++ ++static int bcm2708_dma_chan_init(struct bcm2835_dmadev *d, ++ void __iomem *chan_base, int chan_id, int irq) ++{ ++ struct bcm2835_chan *c; ++ ++ c = devm_kzalloc(d->ddev.dev, sizeof(*c), GFP_KERNEL); ++ if (!c) ++ return -ENOMEM; ++ ++ c->vc.desc_free = bcm2835_dma_desc_free; + vchan_init(&c->vc, &d->ddev); + INIT_LIST_HEAD(&c->node); + +@@ -457,30 +752,81 @@ static int bcm2708_dma_chan_init(struct bcm2708_dmadev *d, void __iomem* chan_ba + return 0; + } + +-static void bcm2708_dma_free(struct bcm2708_dmadev *od) ++ ++static void bcm2835_dma_free(struct bcm2835_dmadev *od) + { +- while (!list_empty(&od->ddev.channels)) { +- struct bcm2708_chan *c = list_first_entry(&od->ddev.channels, +- struct bcm2708_chan, vc.chan.device_node); ++ struct bcm2835_chan *c, *next; + ++ list_for_each_entry_safe(c, next, &od->ddev.channels, ++ vc.chan.device_node) { + list_del(&c->vc.chan.device_node); + tasklet_kill(&c->vc.task); + } + } + +-static int bcm2708_dma_probe(struct platform_device *pdev) ++static const struct of_device_id bcm2835_dma_of_match[] = { ++ { .compatible = "brcm,bcm2835-dma", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match); ++ ++#ifdef CONFIG_OF ++static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec, ++ struct of_dma *ofdma) + { +- struct bcm2708_dmadev *od; +- int rc, i; ++ struct bcm2835_dmadev *d = ofdma->of_dma_data; ++ struct dma_chan *chan; ++ ++ chan = dma_get_any_slave_channel(&d->ddev); ++ if (!chan) ++ return NULL; ++ ++ /* Set DREQ from param */ ++ to_bcm2835_dma_chan(chan)->dreq = spec->args[0]; ++ ++ return chan; ++} ++#endif ++ ++static int bcm2835_dma_device_slave_caps(struct dma_chan *dchan, ++ struct dma_slave_caps *caps) ++{ ++ caps->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); ++ caps->dstn_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); ++ caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); ++ caps->cmd_pause = false; ++ caps->cmd_terminate = true; ++ ++ return 0; ++} ++ ++static int bcm2835_dma_probe(struct platform_device *pdev) ++{ ++ struct bcm2835_dmadev *od; ++#ifdef CONFIG_OF ++ struct resource *res; ++ void __iomem *base; ++ uint32_t chans_available; ++#endif ++ int rc; ++ int i; ++ int irq; ++ + + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + ++ /* If CONFIG_OF is selected, device tree is used */ ++ /* hence the difference between probing */ ++ ++#ifndef CONFIG_OF ++ + rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (rc) + return rc; + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + ++ + od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL); + if (!od) + return -ENOMEM; +@@ -488,25 +834,29 @@ static int bcm2708_dma_probe(struct platform_device *pdev) + pdev->dev.dma_parms = &od->dma_parms; + dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF); + ++ + dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); ++ dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask); + dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); +- od->ddev.device_alloc_chan_resources = bcm2708_dma_alloc_chan_resources; +- od->ddev.device_free_chan_resources = bcm2708_dma_free_chan_resources; +- od->ddev.device_tx_status = bcm2708_dma_tx_status; +- od->ddev.device_issue_pending = bcm2708_dma_issue_pending; +- od->ddev.device_prep_dma_cyclic = bcm2708_dma_prep_dma_cyclic; +- od->ddev.device_control = bcm2708_dma_control; ++ od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources; ++ od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources; ++ od->ddev.device_tx_status = bcm2835_dma_tx_status; ++ od->ddev.device_issue_pending = bcm2835_dma_issue_pending; ++ od->ddev.device_slave_caps = bcm2835_dma_device_slave_caps; ++ od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic; ++ od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg; ++ od->ddev.device_control = bcm2835_dma_control; + od->ddev.dev = &pdev->dev; + INIT_LIST_HEAD(&od->ddev.channels); + spin_lock_init(&od->lock); + + platform_set_drvdata(pdev, od); + +- for (i = 0; i < 16; i++) { +- void __iomem* chan_base; +- int chan_id, irq; ++ for (i = 0; i < 5; i++) { ++ void __iomem *chan_base; ++ int chan_id; + +- chan_id = bcm_dma_chan_alloc(BCM_DMA_FEATURE_FAST, ++ chan_id = bcm_dma_chan_alloc(BCM_DMA_FEATURE_LITE, + &chan_base, + &irq); + +@@ -514,38 +864,121 @@ static int bcm2708_dma_probe(struct platform_device *pdev) + break; + + rc = bcm2708_dma_chan_init(od, chan_base, chan_id, irq); +- if (rc) { +- bcm2708_dma_free(od); +- return rc; ++ if (rc) ++ goto err_no_dma; ++ } ++#else ++ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); ++ if (rc) ++ return rc; ++ ++ ++ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL); ++ if (!od) ++ return -ENOMEM; ++ ++ pdev->dev.dma_parms = &od->dma_parms; ++ dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF); ++ ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ od->base = base; ++ ++ ++ dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); ++ dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask); ++ dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); ++ od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources; ++ od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources; ++ od->ddev.device_tx_status = bcm2835_dma_tx_status; ++ od->ddev.device_issue_pending = bcm2835_dma_issue_pending; ++ od->ddev.device_slave_caps = bcm2835_dma_device_slave_caps; ++ od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic; ++ od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg; ++ od->ddev.device_control = bcm2835_dma_control; ++ od->ddev.dev = &pdev->dev; ++ INIT_LIST_HEAD(&od->ddev.channels); ++ spin_lock_init(&od->lock); ++ ++ platform_set_drvdata(pdev, od); ++ ++ ++ /* Request DMA channel mask from device tree */ ++ if (of_property_read_u32(pdev->dev.of_node, ++ "brcm,dma-channel-mask", ++ &chans_available)) { ++ dev_err(&pdev->dev, "Failed to get channel mask\n"); ++ rc = -EINVAL; ++ goto err_no_dma; ++ } ++ ++ ++ /* ++ * Do not use the FIQ and BULK channels, ++ * because they are used by the GPU. ++ */ ++ chans_available &= ~(BCM2835_DMA_FIQ_MASK | BCM2835_DMA_BULK_MASK); ++ ++ ++ for (i = 0; i < pdev->num_resources; i++) { ++ irq = platform_get_irq(pdev, i); ++ if (irq < 0) ++ break; ++ ++ if (chans_available & (1 << i)) { ++ rc = bcm2835_dma_chan_init(od, i, irq); ++ if (rc) ++ goto err_no_dma; + } + } + ++ dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", i); ++ ++ /* Device-tree DMA controller registration */ ++ rc = of_dma_controller_register(pdev->dev.of_node, ++ bcm2835_dma_xlate, od); ++ if (rc) { ++ dev_err(&pdev->dev, "Failed to register DMA controller\n"); ++ goto err_no_dma; ++ } ++#endif ++ + rc = dma_async_device_register(&od->ddev); + if (rc) { + dev_err(&pdev->dev, + "Failed to register slave DMA engine device: %d\n", rc); +- bcm2708_dma_free(od); +- return rc; ++ goto err_no_dma; + } + +- dev_dbg(&pdev->dev, "Load BCM2708 DMA engine driver\n"); ++ dev_info(&pdev->dev, "Load BCM2835 DMA engine driver\n"); + ++ return 0; ++ ++err_no_dma: ++ bcm2835_dma_free(od); + return rc; + } + +-static int bcm2708_dma_remove(struct platform_device *pdev) ++static int bcm2835_dma_remove(struct platform_device *pdev) + { +- struct bcm2708_dmadev *od = platform_get_drvdata(pdev); ++ struct bcm2835_dmadev *od = platform_get_drvdata(pdev); + + dma_async_device_unregister(&od->ddev); +- bcm2708_dma_free(od); ++ bcm2835_dma_free(od); + + return 0; + } + +-static struct platform_driver bcm2708_dma_driver = { +- .probe = bcm2708_dma_probe, +- .remove = bcm2708_dma_remove, ++#ifndef CONFIG_OF ++ ++ ++static struct platform_driver bcm2835_dma_driver = { ++ .probe = bcm2835_dma_probe, ++ .remove = bcm2835_dma_remove, + .driver = { + .name = "bcm2708-dmaengine", + .owner = THIS_MODULE, +@@ -554,35 +987,57 @@ static struct platform_driver bcm2708_dma_driver = { + + static struct platform_device *pdev; + +-static const struct platform_device_info bcm2708_dma_dev_info = { ++static const struct platform_device_info bcm2835_dma_dev_info = { + .name = "bcm2708-dmaengine", + .id = -1, + }; + +-static int bcm2708_dma_init(void) ++static int bcm2835_dma_init(void) + { +- int rc = platform_driver_register(&bcm2708_dma_driver); ++ int rc = platform_driver_register(&bcm2835_dma_driver); + + if (rc == 0) { +- pdev = platform_device_register_full(&bcm2708_dma_dev_info); ++ pdev = platform_device_register_full(&bcm2835_dma_dev_info); + if (IS_ERR(pdev)) { +- platform_driver_unregister(&bcm2708_dma_driver); ++ platform_driver_unregister(&bcm2835_dma_driver); + rc = PTR_ERR(pdev); + } + } + + return rc; + } +-subsys_initcall(bcm2708_dma_init); ++module_init(bcm2835_dma_init); /* preferable to subsys_initcall */ + +-static void __exit bcm2708_dma_exit(void) ++static void __exit bcm2835_dma_exit(void) + { + platform_device_unregister(pdev); +- platform_driver_unregister(&bcm2708_dma_driver); ++ platform_driver_unregister(&bcm2835_dma_driver); + } +-module_exit(bcm2708_dma_exit); ++module_exit(bcm2835_dma_exit); + + MODULE_ALIAS("platform:bcm2708-dma"); + MODULE_DESCRIPTION("BCM2708 DMA engine driver"); + MODULE_AUTHOR("Florian Meier "); + MODULE_LICENSE("GPL v2"); ++ ++ ++#else ++ ++static struct platform_driver bcm2835_dma_driver = { ++ .probe = bcm2835_dma_probe, ++ .remove = bcm2835_dma_remove, ++ .driver = { ++ .name = "bcm2835-dma", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(bcm2835_dma_of_match), ++ }, ++}; ++ ++module_platform_driver(bcm2835_dma_driver); ++ ++MODULE_ALIAS("platform:bcm2835-dma"); ++MODULE_DESCRIPTION("BCM2835 DMA engine driver"); ++MODULE_AUTHOR("Gellert Weisz "); ++MODULE_LICENSE("GPL v2"); ++ ++#endif +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index b152bb3..592dbb2 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -311,6 +311,35 @@ config MMC_MOXART + be found on some embedded hardware such as UC-7112-LX. + If you have a controller with this interface, say Y here. + ++config MMC_BCM2835 ++ tristate "MMC support on BCM2835" ++ depends on MACH_BCM2708 ++ help ++ This selects the MMC Interface on BCM2835. ++ ++ If you have a controller with this interface, say Y or M here. ++ ++ If unsure, say N. ++ ++config MMC_BCM2835_DMA ++ bool "DMA support on BCM2835 Arasan controller" ++ depends on MMC_BCM2835 ++ help ++ Enable DMA support on the Arasan SDHCI controller in Broadcom 2708 ++ based chips. ++ ++ If unsure, say N. ++ ++config MMC_PIO_DMA_BARRIER ++ int "Block count limit for PIO transfers" ++ depends on MMC_BCM2835 && MMC_BCM2835_DMA ++ range 0 256 ++ default 2 ++ help ++ The inclusive limit in bytes under which PIO will be used instead of DMA ++ ++ If unsure, say 2 here. ++ + config MMC_OMAP + tristate "TI OMAP Multimedia Card Interface support" + depends on ARCH_OMAP +diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index 635064e..402ded1 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -17,6 +17,7 @@ obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o + obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o + obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o + obj-$(CONFIG_MMC_SDHCI_BCM2708) += sdhci-bcm2708.o ++obj-$(CONFIG_MMC_BCM2835) += bcm2835-mmc.o + obj-$(CONFIG_MMC_WBSD) += wbsd.o + obj-$(CONFIG_MMC_AU1X) += au1xmmc.o + obj-$(CONFIG_MMC_OMAP) += omap.o +diff --git a/drivers/mmc/host/bcm2835-mmc.c b/drivers/mmc/host/bcm2835-mmc.c +new file mode 100644 +index 0000000..684b872 +--- /dev/null ++++ b/drivers/mmc/host/bcm2835-mmc.c +@@ -0,0 +1,1537 @@ ++/* ++ * BCM2835 MMC host driver. ++ * ++ * Author: Gellert Weisz ++ * Copyright 2014 ++ * ++ * Based on ++ * sdhci-bcm2708.c by Broadcom ++ * sdhci-bcm2835.c by Stephen Warren and Oleksandr Tymoshenko ++ * sdhci.c and sdhci-pci.c by Pierre Ossman ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope 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, see . ++ */ ++ ++#ifndef CONFIG_OF ++ #define BCM2835_CLOCK_FREQ 250000000 ++#endif ++ ++#define DRIVER_NAME "mmc-bcm2835" ++ ++#define DBG(f, x...) \ ++pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x) ++ ++#ifndef CONFIG_MMC_BCM2835_DMA ++ #define FORCE_PIO ++#endif ++ ++ ++/* the inclusive limit in bytes under which PIO will be used instead of DMA */ ++#ifdef CONFIG_MMC_PIO_DMA_BARRIER ++#define PIO_DMA_BARRIER CONFIG_MMC_PIO_DMA_BARRIER ++#else ++#define PIO_DMA_BARRIER 00 ++#endif ++ ++#define MIN_FREQ 400000 ++#define TIMEOUT_VAL 0xE ++#define BCM2835_SDHCI_WRITE_DELAY(f) (((2 * 1000000) / f) + 1) ++ ++/* FIXME: Needs IOMMU support */ ++#define BCM2835_VCMMU_SHIFT (0x7E000000 - 0x20000000) ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sdhci.h" ++ ++ ++struct bcm2835_host { ++ spinlock_t lock; ++ ++ void __iomem *ioaddr; ++ u32 phys_addr; ++ ++ struct mmc_host *mmc; ++ ++ u32 timeout; ++ ++ int clock; /* Current clock speed */ ++ u8 pwr; /* Current voltage */ ++ ++ unsigned int max_clk; /* Max possible freq */ ++ unsigned int timeout_clk; /* Timeout freq (KHz) */ ++ unsigned int clk_mul; /* Clock Muliplier value */ ++ ++ struct tasklet_struct finish_tasklet; /* Tasklet structures */ ++ ++ struct timer_list timer; /* Timer for timeouts */ ++ ++ struct sg_mapping_iter sg_miter; /* SG state for PIO */ ++ unsigned int blocks; /* remaining PIO blocks */ ++ ++ int irq; /* Device IRQ */ ++ ++ ++ u32 ier; /* cached registers */ ++ ++ struct mmc_request *mrq; /* Current request */ ++ struct mmc_command *cmd; /* Current command */ ++ struct mmc_data *data; /* Current data request */ ++ unsigned int data_early:1; /* Data finished before cmd */ ++ ++ wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ ++ ++ u32 thread_isr; ++ ++ u32 shadow; ++ ++ /*DMA part*/ ++ struct dma_chan *dma_chan_rx; /* DMA channel for reads */ ++ struct dma_chan *dma_chan_tx; /* DMA channel for writes */ ++ struct dma_async_tx_descriptor *tx_desc; /* descriptor */ ++ ++ bool have_dma; ++ bool use_dma; ++ /*end of DMA part*/ ++ ++ int max_delay; /* maximum length of time spent waiting */ ++ ++ int flags; /* Host attributes */ ++#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */ ++#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */ ++#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ ++#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ ++#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */ ++#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */ ++#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */ ++#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */ ++#define SDHCI_USE_PLATDMA (1<<12) /* Host uses 3rd party DMA */ ++}; ++ ++ ++static inline void mmc_writel(struct bcm2835_host *host, u32 val, int reg) ++{ ++ writel(val, host->ioaddr + reg); ++ udelay(BCM2835_SDHCI_WRITE_DELAY(max(host->clock, MIN_FREQ))); ++} ++ ++static inline void mmc_raw_writel(struct bcm2835_host *host, u32 val, int reg) ++{ ++ writel(val, host->ioaddr + reg); ++} ++ ++static inline u32 mmc_readl(struct bcm2835_host *host, int reg) ++{ ++ return readl(host->ioaddr + reg); ++} ++ ++static inline void mmc_writew(struct bcm2835_host *host, u16 val, int reg) ++{ ++ u32 oldval = (reg == SDHCI_COMMAND) ? host->shadow : ++ mmc_readl(host, reg & ~3); ++ u32 word_num = (reg >> 1) & 1; ++ u32 word_shift = word_num * 16; ++ u32 mask = 0xffff << word_shift; ++ u32 newval = (oldval & ~mask) | (val << word_shift); ++ ++ if (reg == SDHCI_TRANSFER_MODE) ++ host->shadow = newval; ++ else ++ mmc_writel(host, newval, reg & ~3); ++ ++} ++ ++static inline void mmc_writeb(struct bcm2835_host *host, u8 val, int reg) ++{ ++ u32 oldval = mmc_readl(host, reg & ~3); ++ u32 byte_num = reg & 3; ++ u32 byte_shift = byte_num * 8; ++ u32 mask = 0xff << byte_shift; ++ u32 newval = (oldval & ~mask) | (val << byte_shift); ++ ++ mmc_writel(host, newval, reg & ~3); ++} ++ ++ ++static inline u16 mmc_readw(struct bcm2835_host *host, int reg) ++{ ++ u32 val = mmc_readl(host, (reg & ~3)); ++ u32 word_num = (reg >> 1) & 1; ++ u32 word_shift = word_num * 16; ++ u32 word = (val >> word_shift) & 0xffff; ++ ++ return word; ++} ++ ++static inline u8 mmc_readb(struct bcm2835_host *host, int reg) ++{ ++ u32 val = mmc_readl(host, (reg & ~3)); ++ u32 byte_num = reg & 3; ++ u32 byte_shift = byte_num * 8; ++ u32 byte = (val >> byte_shift) & 0xff; ++ ++ return byte; ++} ++ ++static void sdhci_unsignal_irqs(struct bcm2835_host *host, u32 clear) ++{ ++ u32 ier; ++ ++ ier = mmc_readl(host, SDHCI_SIGNAL_ENABLE); ++ ier &= ~clear; ++ /* change which requests generate IRQs - makes no difference to ++ the content of SDHCI_INT_STATUS, or the need to acknowledge IRQs */ ++ mmc_writel(host, ier, SDHCI_SIGNAL_ENABLE); ++} ++ ++ ++static void sdhci_dumpregs(struct bcm2835_host *host) ++{ ++ pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n", ++ mmc_hostname(host->mmc)); ++ ++ pr_debug(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", ++ mmc_readl(host, SDHCI_DMA_ADDRESS), ++ mmc_readw(host, SDHCI_HOST_VERSION)); ++ pr_debug(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n", ++ mmc_readw(host, SDHCI_BLOCK_SIZE), ++ mmc_readw(host, SDHCI_BLOCK_COUNT)); ++ pr_debug(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n", ++ mmc_readl(host, SDHCI_ARGUMENT), ++ mmc_readw(host, SDHCI_TRANSFER_MODE)); ++ pr_debug(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n", ++ mmc_readl(host, SDHCI_PRESENT_STATE), ++ mmc_readb(host, SDHCI_HOST_CONTROL)); ++ pr_debug(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n", ++ mmc_readb(host, SDHCI_POWER_CONTROL), ++ mmc_readb(host, SDHCI_BLOCK_GAP_CONTROL)); ++ pr_debug(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", ++ mmc_readb(host, SDHCI_WAKE_UP_CONTROL), ++ mmc_readw(host, SDHCI_CLOCK_CONTROL)); ++ pr_debug(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", ++ mmc_readb(host, SDHCI_TIMEOUT_CONTROL), ++ mmc_readl(host, SDHCI_INT_STATUS)); ++ pr_debug(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", ++ mmc_readl(host, SDHCI_INT_ENABLE), ++ mmc_readl(host, SDHCI_SIGNAL_ENABLE)); ++ pr_debug(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", ++ mmc_readw(host, SDHCI_ACMD12_ERR), ++ mmc_readw(host, SDHCI_SLOT_INT_STATUS)); ++ pr_debug(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n", ++ mmc_readl(host, SDHCI_CAPABILITIES), ++ mmc_readl(host, SDHCI_CAPABILITIES_1)); ++ pr_debug(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n", ++ mmc_readw(host, SDHCI_COMMAND), ++ mmc_readl(host, SDHCI_MAX_CURRENT)); ++ pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n", ++ mmc_readw(host, SDHCI_HOST_CONTROL2)); ++ ++ pr_debug(DRIVER_NAME ": ===========================================\n"); ++} ++ ++ ++static void bcm2835_reset(struct bcm2835_host *host, u8 mask) ++{ ++ unsigned long timeout; ++ ++ mmc_writeb(host, mask, SDHCI_SOFTWARE_RESET); ++ ++ if (mask & SDHCI_RESET_ALL) ++ host->clock = 0; ++ ++ /* Wait max 100 ms */ ++ timeout = 100; ++ ++ /* hw clears the bit when it's done */ ++ while (mmc_readb(host, SDHCI_SOFTWARE_RESET) & mask) { ++ if (timeout == 0) { ++ pr_err("%s: Reset 0x%x never completed.\n", ++ mmc_hostname(host->mmc), (int)mask); ++ sdhci_dumpregs(host); ++ return; ++ } ++ timeout--; ++ mdelay(1); ++ } ++ ++ if (100-timeout>10 && 100-timeout>host->max_delay) { ++ host->max_delay = 100-timeout; ++ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); ++ } ++} ++ ++static void bcm2835_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); ++ ++static void bcm2835_init(struct bcm2835_host *host, int soft) ++{ ++ if (soft) ++ bcm2835_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA); ++ else ++ bcm2835_reset(host, SDHCI_RESET_ALL); ++ ++ host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | ++ SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | ++ SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | ++ SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | ++ SDHCI_INT_RESPONSE; ++ ++ mmc_writel(host, host->ier, SDHCI_INT_ENABLE); ++ mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); ++ ++ if (soft) { ++ /* force clock reconfiguration */ ++ host->clock = 0; ++ bcm2835_set_ios(host->mmc, &host->mmc->ios); ++ } ++} ++ ++ ++ ++static void bcm2835_finish_data(struct bcm2835_host *host); ++ ++static void bcm2835_dma_complete(void *param) ++{ ++ struct bcm2835_host *host = param; ++ struct dma_chan *dma_chan; ++ unsigned long flags; ++ u32 dir_data; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ if (host->data && !(host->data->flags & MMC_DATA_WRITE)) { ++ /* otherwise handled in SDHCI IRQ */ ++ dma_chan = host->dma_chan_rx; ++ dir_data = DMA_FROM_DEVICE; ++ ++ dma_unmap_sg(dma_chan->device->dev, ++ host->data->sg, host->data->sg_len, ++ dir_data); ++ ++ bcm2835_finish_data(host); ++ } ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static void bcm2835_read_block_pio(struct bcm2835_host *host) ++{ ++ unsigned long flags; ++ size_t blksize, len, chunk; ++ ++ u32 uninitialized_var(scratch); ++ u8 *buf; ++ ++ blksize = host->data->blksz; ++ chunk = 0; ++ ++ local_irq_save(flags); ++ ++ while (blksize) { ++ if (!sg_miter_next(&host->sg_miter)) ++ BUG(); ++ ++ len = min(host->sg_miter.length, blksize); ++ ++ blksize -= len; ++ host->sg_miter.consumed = len; ++ ++ buf = host->sg_miter.addr; ++ ++ while (len) { ++ if (chunk == 0) { ++ scratch = mmc_readl(host, SDHCI_BUFFER); ++ chunk = 4; ++ } ++ ++ *buf = scratch & 0xFF; ++ ++ buf++; ++ scratch >>= 8; ++ chunk--; ++ len--; ++ } ++ } ++ ++ sg_miter_stop(&host->sg_miter); ++ ++ local_irq_restore(flags); ++} ++ ++static void bcm2835_write_block_pio(struct bcm2835_host *host) ++{ ++ unsigned long flags; ++ size_t blksize, len, chunk; ++ u32 scratch; ++ u8 *buf; ++ ++ blksize = host->data->blksz; ++ chunk = 0; ++ chunk = 0; ++ scratch = 0; ++ ++ local_irq_save(flags); ++ ++ while (blksize) { ++ if (!sg_miter_next(&host->sg_miter)) ++ BUG(); ++ ++ len = min(host->sg_miter.length, blksize); ++ ++ blksize -= len; ++ host->sg_miter.consumed = len; ++ ++ buf = host->sg_miter.addr; ++ ++ while (len) { ++ scratch |= (u32)*buf << (chunk * 8); ++ ++ buf++; ++ chunk++; ++ len--; ++ ++ if ((chunk == 4) || ((len == 0) && (blksize == 0))) { ++ mmc_raw_writel(host, scratch, SDHCI_BUFFER); ++ chunk = 0; ++ scratch = 0; ++ } ++ } ++ } ++ ++ sg_miter_stop(&host->sg_miter); ++ ++ local_irq_restore(flags); ++} ++ ++ ++static void bcm2835_transfer_pio(struct bcm2835_host *host) ++{ ++ u32 mask; ++ ++ BUG_ON(!host->data); ++ ++ if (host->blocks == 0) ++ return; ++ ++ if (host->data->flags & MMC_DATA_READ) ++ mask = SDHCI_DATA_AVAILABLE; ++ else ++ mask = SDHCI_SPACE_AVAILABLE; ++ ++ while (mmc_readl(host, SDHCI_PRESENT_STATE) & mask) { ++ ++ if (host->data->flags & MMC_DATA_READ) ++ bcm2835_read_block_pio(host); ++ else ++ bcm2835_write_block_pio(host); ++ ++ host->blocks--; ++ ++ /* QUIRK used in sdhci.c removes the 'if' */ ++ /* but it seems this is unnecessary */ ++ if (host->blocks == 0) ++ break; ++ ++ ++ } ++} ++ ++ ++static void bcm2835_transfer_dma(struct bcm2835_host *host) ++{ ++ u32 len, dir_data, dir_slave; ++ struct dma_async_tx_descriptor *desc = NULL; ++ struct dma_chan *dma_chan; ++ ++ ++ WARN_ON(!host->data); ++ ++ if (!host->data) ++ return; ++ ++ if (host->blocks == 0) ++ return; ++ ++ if (host->data->flags & MMC_DATA_READ) { ++ dma_chan = host->dma_chan_rx; ++ dir_data = DMA_FROM_DEVICE; ++ dir_slave = DMA_DEV_TO_MEM; ++ } else { ++ dma_chan = host->dma_chan_tx; ++ dir_data = DMA_TO_DEVICE; ++ dir_slave = DMA_MEM_TO_DEV; ++ } ++ ++ BUG_ON(!dma_chan->device); ++ BUG_ON(!dma_chan->device->dev); ++ BUG_ON(!host->data->sg); ++ ++ len = dma_map_sg(dma_chan->device->dev, host->data->sg, ++ host->data->sg_len, dir_data); ++ if (len > 0) { ++ desc = dmaengine_prep_slave_sg(dma_chan, host->data->sg, ++ len, dir_slave, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK); ++ } else { ++ dev_err(mmc_dev(host->mmc), "dma_map_sg returned zero length\n"); ++ } ++ if (desc) { ++ sdhci_unsignal_irqs(host, SDHCI_INT_DATA_AVAIL | ++ SDHCI_INT_SPACE_AVAIL); ++ host->tx_desc = desc; ++ desc->callback = bcm2835_dma_complete; ++ desc->callback_param = host; ++ dmaengine_submit(desc); ++ dma_async_issue_pending(dma_chan); ++ } ++ ++} ++ ++ ++ ++static void bcm2835_set_transfer_irqs(struct bcm2835_host *host) ++{ ++ u32 pio_irqs = SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL; ++ u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR; ++ ++ if (host->use_dma) ++ host->ier = (host->ier & ~pio_irqs) | dma_irqs; ++ else ++ host->ier = (host->ier & ~dma_irqs) | pio_irqs; ++ ++ mmc_writel(host, host->ier, SDHCI_INT_ENABLE); ++ mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); ++} ++ ++ ++static void bcm2835_prepare_data(struct bcm2835_host *host, struct mmc_command *cmd) ++{ ++ u8 count; ++ struct mmc_data *data = cmd->data; ++ ++ WARN_ON(host->data); ++ ++ if (data || (cmd->flags & MMC_RSP_BUSY)) { ++ count = TIMEOUT_VAL; ++ mmc_writeb(host, count, SDHCI_TIMEOUT_CONTROL); ++ } ++ ++ if (!data) ++ return; ++ ++ /* Sanity checks */ ++ BUG_ON(data->blksz * data->blocks > 524288); ++ BUG_ON(data->blksz > host->mmc->max_blk_size); ++ BUG_ON(data->blocks > 65535); ++ ++ host->data = data; ++ host->data_early = 0; ++ host->data->bytes_xfered = 0; ++ ++ ++ if (!(host->flags & SDHCI_REQ_USE_DMA)) { ++ int flags; ++ ++ flags = SG_MITER_ATOMIC; ++ if (host->data->flags & MMC_DATA_READ) ++ flags |= SG_MITER_TO_SG; ++ else ++ flags |= SG_MITER_FROM_SG; ++ sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); ++ host->blocks = data->blocks; ++ } ++ ++ host->use_dma = host->have_dma && data->blocks > PIO_DMA_BARRIER; ++ ++ bcm2835_set_transfer_irqs(host); ++ ++ /* Set the DMA boundary value and block size */ ++ mmc_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, ++ data->blksz), SDHCI_BLOCK_SIZE); ++ mmc_writew(host, data->blocks, SDHCI_BLOCK_COUNT); ++ ++ BUG_ON(!host->data); ++} ++ ++static void bcm2835_set_transfer_mode(struct bcm2835_host *host, ++ struct mmc_command *cmd) ++{ ++ u16 mode; ++ struct mmc_data *data = cmd->data; ++ ++ if (data == NULL) { ++ /* clear Auto CMD settings for no data CMDs */ ++ mode = mmc_readw(host, SDHCI_TRANSFER_MODE); ++ mmc_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 | ++ SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE); ++ return; ++ } ++ ++ WARN_ON(!host->data); ++ ++ mode = SDHCI_TRNS_BLK_CNT_EN; ++ ++ if ((mmc_op_multi(cmd->opcode) || data->blocks > 1)) { ++ mode |= SDHCI_TRNS_MULTI; ++ ++ /* ++ * If we are sending CMD23, CMD12 never gets sent ++ * on successful completion (so no Auto-CMD12). ++ */ ++ if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) ++ mode |= SDHCI_TRNS_AUTO_CMD12; ++ else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) { ++ mode |= SDHCI_TRNS_AUTO_CMD23; ++ mmc_writel(host, host->mrq->sbc->arg, SDHCI_ARGUMENT2); ++ } ++ } ++ ++ if (data->flags & MMC_DATA_READ) ++ mode |= SDHCI_TRNS_READ; ++ if (host->flags & SDHCI_REQ_USE_DMA) ++ mode |= SDHCI_TRNS_DMA; ++ ++ mmc_writew(host, mode, SDHCI_TRANSFER_MODE); ++} ++ ++void bcm2835_send_command(struct bcm2835_host *host, struct mmc_command *cmd) ++{ ++ int flags; ++ u32 mask; ++ unsigned long timeout; ++ ++ WARN_ON(host->cmd); ++ ++ /* Wait max 10 ms */ ++ timeout = 1000; ++ ++ mask = SDHCI_CMD_INHIBIT; ++ if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY)) ++ mask |= SDHCI_DATA_INHIBIT; ++ ++ /* We shouldn't wait for data inihibit for stop commands, even ++ though they might use busy signaling */ ++ if (host->mrq->data && (cmd == host->mrq->data->stop)) ++ mask &= ~SDHCI_DATA_INHIBIT; ++ ++ while (mmc_readl(host, SDHCI_PRESENT_STATE) & mask) { ++ if (timeout == 0) { ++ pr_err("%s: Controller never released inhibit bit(s).\n", ++ mmc_hostname(host->mmc)); ++ sdhci_dumpregs(host); ++ cmd->error = -EIO; ++ tasklet_schedule(&host->finish_tasklet); ++ return; ++ } ++ timeout--; ++ udelay(10); ++ } ++ ++ if ((1000-timeout)/100>1 && (1000-timeout)/100>host->max_delay) { ++ host->max_delay = (1000-timeout)/100; ++ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); ++ } ++ ++ timeout = jiffies; ++#ifdef CONFIG_OF ++ if (!cmd->data && cmd->busy_timeout > 9000) ++ timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; ++ else ++#endif ++ timeout += 10 * HZ; ++ mod_timer(&host->timer, timeout); ++ ++ host->cmd = cmd; ++ ++ bcm2835_prepare_data(host, cmd); ++ ++ mmc_writel(host, cmd->arg, SDHCI_ARGUMENT); ++ ++ bcm2835_set_transfer_mode(host, cmd); ++ ++ if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { ++ pr_err("%s: Unsupported response type!\n", ++ mmc_hostname(host->mmc)); ++ cmd->error = -EINVAL; ++ tasklet_schedule(&host->finish_tasklet); ++ return; ++ } ++ ++ if (!(cmd->flags & MMC_RSP_PRESENT)) ++ flags = SDHCI_CMD_RESP_NONE; ++ else if (cmd->flags & MMC_RSP_136) ++ flags = SDHCI_CMD_RESP_LONG; ++ else if (cmd->flags & MMC_RSP_BUSY) ++ flags = SDHCI_CMD_RESP_SHORT_BUSY; ++ else ++ flags = SDHCI_CMD_RESP_SHORT; ++ ++ if (cmd->flags & MMC_RSP_CRC) ++ flags |= SDHCI_CMD_CRC; ++ if (cmd->flags & MMC_RSP_OPCODE) ++ flags |= SDHCI_CMD_INDEX; ++ ++ if (cmd->data) ++ flags |= SDHCI_CMD_DATA; ++ ++ mmc_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); ++} ++ ++ ++static void bcm2835_finish_data(struct bcm2835_host *host) ++{ ++ struct mmc_data *data; ++ ++ BUG_ON(!host->data); ++ ++ data = host->data; ++ host->data = NULL; ++ ++ if (data->error) ++ data->bytes_xfered = 0; ++ else ++ data->bytes_xfered = data->blksz * data->blocks; ++ ++ /* ++ * Need to send CMD12 if - ++ * a) open-ended multiblock transfer (no CMD23) ++ * b) error in multiblock transfer ++ */ ++ if (data->stop && ++ (data->error || ++ !host->mrq->sbc)) { ++ ++ /* ++ * The controller needs a reset of internal state machines ++ * upon error conditions. ++ */ ++ if (data->error) { ++ bcm2835_reset(host, SDHCI_RESET_CMD); ++ bcm2835_reset(host, SDHCI_RESET_DATA); ++ } ++ ++ bcm2835_send_command(host, data->stop); ++ } else ++ tasklet_schedule(&host->finish_tasklet); ++} ++ ++static void bcm2835_finish_command(struct bcm2835_host *host) ++{ ++ int i; ++ ++ BUG_ON(host->cmd == NULL); ++ ++ if (host->cmd->flags & MMC_RSP_PRESENT) { ++ if (host->cmd->flags & MMC_RSP_136) { ++ /* CRC is stripped so we need to do some shifting. */ ++ for (i = 0; i < 4; i++) { ++ host->cmd->resp[i] = mmc_readl(host, ++ SDHCI_RESPONSE + (3-i)*4) << 8; ++ if (i != 3) ++ host->cmd->resp[i] |= ++ mmc_readb(host, ++ SDHCI_RESPONSE + (3-i)*4-1); ++ } ++ } else { ++ host->cmd->resp[0] = mmc_readl(host, SDHCI_RESPONSE); ++ } ++ } ++ ++ host->cmd->error = 0; ++ ++ /* Finished CMD23, now send actual command. */ ++ if (host->cmd == host->mrq->sbc) { ++ host->cmd = NULL; ++ bcm2835_send_command(host, host->mrq->cmd); ++ } else { ++ ++ /* Processed actual command. */ ++ if (host->data && host->data_early) ++ bcm2835_finish_data(host); ++ ++ if (!host->cmd->data) ++ tasklet_schedule(&host->finish_tasklet); ++ ++ host->cmd = NULL; ++ } ++} ++ ++ ++static void bcm2835_timeout_timer(unsigned long data) ++{ ++ struct bcm2835_host *host; ++ unsigned long flags; ++ ++ host = (struct bcm2835_host *)data; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ if (host->mrq) { ++ pr_err("%s: Timeout waiting for hardware interrupt.\n", ++ mmc_hostname(host->mmc)); ++ sdhci_dumpregs(host); ++ ++ if (host->data) { ++ host->data->error = -ETIMEDOUT; ++ bcm2835_finish_data(host); ++ } else { ++ if (host->cmd) ++ host->cmd->error = -ETIMEDOUT; ++ else ++ host->mrq->cmd->error = -ETIMEDOUT; ++ ++ tasklet_schedule(&host->finish_tasklet); ++ } ++ } ++ ++ mmiowb(); ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++ ++static void mmc_enable_sdio_irq_nolock(struct bcm2835_host *host, int enable) ++{ ++ if (!(host->flags & SDHCI_DEVICE_DEAD)) { ++ if (enable) ++ host->ier |= SDHCI_INT_CARD_INT; ++ else ++ host->ier &= ~SDHCI_INT_CARD_INT; ++ ++ mmc_writel(host, host->ier, SDHCI_INT_ENABLE); ++ mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); ++ mmiowb(); ++ } ++} ++ ++static void mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) ++{ ++ struct bcm2835_host *host = mmc_priv(mmc); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ if (enable) ++ host->flags |= SDHCI_SDIO_IRQ_ENABLED; ++ else ++ host->flags &= ~SDHCI_SDIO_IRQ_ENABLED; ++ ++ mmc_enable_sdio_irq_nolock(host, enable); ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static void mmc_cmd_irq(struct bcm2835_host *host, u32 intmask) ++{ ++ ++ BUG_ON(intmask == 0); ++ ++ if (!host->cmd) { ++ pr_err("%s: Got command interrupt 0x%08x even " ++ "though no command operation was in progress.\n", ++ mmc_hostname(host->mmc), (unsigned)intmask); ++ sdhci_dumpregs(host); ++ return; ++ } ++ ++ if (intmask & SDHCI_INT_TIMEOUT) ++ host->cmd->error = -ETIMEDOUT; ++ else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | ++ SDHCI_INT_INDEX)) { ++ host->cmd->error = -EILSEQ; ++ } ++ ++ if (host->cmd->error) { ++ tasklet_schedule(&host->finish_tasklet); ++ return; ++ } ++ ++ if (intmask & SDHCI_INT_RESPONSE) ++ bcm2835_finish_command(host); ++ ++} ++ ++static void mmc_data_irq(struct bcm2835_host *host, u32 intmask) ++{ ++ struct dma_chan *dma_chan; ++ u32 dir_data; ++ ++ BUG_ON(intmask == 0); ++ ++ if (!host->data) { ++ /* ++ * The "data complete" interrupt is also used to ++ * indicate that a busy state has ended. See comment ++ * above in sdhci_cmd_irq(). ++ */ ++ if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) { ++ if (intmask & SDHCI_INT_DATA_END) { ++ bcm2835_finish_command(host); ++ return; ++ } ++ } ++ ++ pr_debug("%s: Got data interrupt 0x%08x even " ++ "though no data operation was in progress.\n", ++ mmc_hostname(host->mmc), (unsigned)intmask); ++ sdhci_dumpregs(host); ++ ++ return; ++ } ++ ++ if (intmask & SDHCI_INT_DATA_TIMEOUT) ++ host->data->error = -ETIMEDOUT; ++ else if (intmask & SDHCI_INT_DATA_END_BIT) ++ host->data->error = -EILSEQ; ++ else if ((intmask & SDHCI_INT_DATA_CRC) && ++ SDHCI_GET_CMD(mmc_readw(host, SDHCI_COMMAND)) ++ != MMC_BUS_TEST_R) ++ host->data->error = -EILSEQ; ++ ++ if (host->use_dma) { ++ if (host->data->flags & MMC_DATA_WRITE) { ++ /* IRQ handled here */ ++ ++ dma_chan = host->dma_chan_tx; ++ dir_data = DMA_TO_DEVICE; ++ dma_unmap_sg(dma_chan->device->dev, ++ host->data->sg, host->data->sg_len, ++ dir_data); ++ ++ bcm2835_finish_data(host); ++ } ++ ++ } else { ++ if (host->data->error) ++ bcm2835_finish_data(host); ++ else { ++ if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) ++ bcm2835_transfer_pio(host); ++ ++ if (intmask & SDHCI_INT_DATA_END) { ++ if (host->cmd) { ++ /* ++ * Data managed to finish before the ++ * command completed. Make sure we do ++ * things in the proper order. ++ */ ++ host->data_early = 1; ++ } else { ++ bcm2835_finish_data(host); ++ } ++ } ++ } ++ } ++} ++ ++ ++static irqreturn_t mmc_irq(int irq, void *dev_id) ++{ ++ irqreturn_t result = IRQ_NONE; ++ struct bcm2835_host *host = dev_id; ++ u32 intmask, mask, unexpected = 0; ++ int max_loops = 16; ++ int cardint = 0; ++ ++ spin_lock(&host->lock); ++ ++ intmask = mmc_readl(host, SDHCI_INT_STATUS); ++ ++ if (!intmask || intmask == 0xffffffff) { ++ result = IRQ_NONE; ++ goto out; ++ } ++ ++ do { ++ /* Clear selected interrupts. */ ++ mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | ++ SDHCI_INT_BUS_POWER); ++ mmc_writel(host, mask, SDHCI_INT_STATUS); ++ ++ ++ if (intmask & SDHCI_INT_CMD_MASK) ++ mmc_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); ++ ++ if (intmask & SDHCI_INT_DATA_MASK) ++ mmc_data_irq(host, intmask & SDHCI_INT_DATA_MASK); ++ ++ if (intmask & SDHCI_INT_BUS_POWER) ++ pr_err("%s: Card is consuming too much power!\n", ++ mmc_hostname(host->mmc)); ++ ++ if (intmask & SDHCI_INT_CARD_INT) { ++#ifndef CONFIG_OF ++ cardint = 1; ++#else ++ mmc_enable_sdio_irq_nolock(host, false); ++ host->thread_isr |= SDHCI_INT_CARD_INT; ++ result = IRQ_WAKE_THREAD; ++#endif ++ } ++ ++ intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | ++ SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | ++ SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER | ++ SDHCI_INT_CARD_INT); ++ ++ if (intmask) { ++ unexpected |= intmask; ++ mmc_writel(host, intmask, SDHCI_INT_STATUS); ++ } ++ ++ if (result == IRQ_NONE) ++ result = IRQ_HANDLED; ++ ++ intmask = mmc_readl(host, SDHCI_INT_STATUS); ++ } while (intmask && --max_loops); ++out: ++ spin_unlock(&host->lock); ++ ++ if (unexpected) { ++ pr_err("%s: Unexpected interrupt 0x%08x.\n", ++ mmc_hostname(host->mmc), unexpected); ++ sdhci_dumpregs(host); ++ } ++ ++#ifndef CONFIG_OF ++ if (cardint) ++ mmc_signal_sdio_irq(host->mmc); ++#endif ++ ++ return result; ++} ++ ++#ifdef CONFIG_OF ++static irqreturn_t mmc_thread_irq(int irq, void *dev_id) ++{ ++ struct bcm2835_host *host = dev_id; ++ unsigned long flags; ++ u32 isr; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ isr = host->thread_isr; ++ host->thread_isr = 0; ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ if (isr & SDHCI_INT_CARD_INT) { ++ sdio_run_irqs(host->mmc); ++ ++ spin_lock_irqsave(&host->lock, flags); ++ if (host->flags & SDHCI_SDIO_IRQ_ENABLED) ++ mmc_enable_sdio_irq_nolock(host, true); ++ spin_unlock_irqrestore(&host->lock, flags); ++ } ++ ++ return isr ? IRQ_HANDLED : IRQ_NONE; ++} ++#endif ++ ++ ++ ++void bcm2835_set_clock(struct bcm2835_host *host, unsigned int clock) ++{ ++ int div = 0; /* Initialized for compiler warning */ ++ int real_div = div, clk_mul = 1; ++ u16 clk = 0; ++ unsigned long timeout; ++ ++ ++ host->mmc->actual_clock = 0; ++ ++ mmc_writew(host, 0, SDHCI_CLOCK_CONTROL); ++ ++ if (clock == 0) ++ return; ++ ++ /* Version 3.00 divisors must be a multiple of 2. */ ++ if (host->max_clk <= clock) ++ div = 1; ++ else { ++ for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; ++ div += 2) { ++ if ((host->max_clk / div) <= clock) ++ break; ++ } ++ } ++ ++ real_div = div; ++ div >>= 1; ++ ++ if (real_div) ++ host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div; ++ ++ clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; ++ clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) ++ << SDHCI_DIVIDER_HI_SHIFT; ++ clk |= SDHCI_CLOCK_INT_EN; ++ mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); ++ ++ /* Wait max 20 ms */ ++ timeout = 20; ++ while (!((clk = mmc_readw(host, SDHCI_CLOCK_CONTROL)) ++ & SDHCI_CLOCK_INT_STABLE)) { ++ if (timeout == 0) { ++ pr_err("%s: Internal clock never " ++ "stabilised.\n", mmc_hostname(host->mmc)); ++ sdhci_dumpregs(host); ++ return; ++ } ++ timeout--; ++ mdelay(1); ++ } ++ ++ if (20-timeout>10 && 20-timeout>host->max_delay) { ++ host->max_delay = 20-timeout; ++ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); ++ } ++ ++ clk |= SDHCI_CLOCK_CARD_EN; ++ mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); ++} ++ ++static void bcm2835_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct bcm2835_host *host; ++ unsigned long flags; ++ ++ host = mmc_priv(mmc); ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ WARN_ON(host->mrq != NULL); ++ ++ host->mrq = mrq; ++ bcm2835_send_command(host, mrq->cmd); ++ mmiowb(); ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ if (mrq->cmd->data && host->use_dma) { ++ /* DMA transfer starts now, PIO starts after interrupt */ ++ bcm2835_transfer_dma(host); ++ } ++} ++ ++ ++static void bcm2835_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ ++ struct bcm2835_host *host = mmc_priv(mmc); ++ unsigned long flags; ++ u8 ctrl; ++ u16 clk, ctrl_2; ++ ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ if (!ios->clock || ios->clock != host->clock) { ++ bcm2835_set_clock(host, ios->clock); ++ host->clock = ios->clock; ++ } ++ ++ if (host->pwr != SDHCI_POWER_330) { ++ host->pwr = SDHCI_POWER_330; ++ mmc_writeb(host, SDHCI_POWER_330 | SDHCI_POWER_ON, SDHCI_POWER_CONTROL); ++ } ++ ++ ctrl = mmc_readb(host, SDHCI_HOST_CONTROL); ++ ++ /* set bus width */ ++ ctrl &= ~SDHCI_CTRL_8BITBUS; ++ if (ios->bus_width == MMC_BUS_WIDTH_4) ++ ctrl |= SDHCI_CTRL_4BITBUS; ++ else ++ ctrl &= ~SDHCI_CTRL_4BITBUS; ++ ++ ctrl &= ~SDHCI_CTRL_HISPD; /* NO_HISPD_BIT */ ++ ++ ++ mmc_writeb(host, ctrl, SDHCI_HOST_CONTROL); ++ /* ++ * We only need to set Driver Strength if the ++ * preset value enable is not set. ++ */ ++ ctrl_2 = mmc_readw(host, SDHCI_HOST_CONTROL2); ++ ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK; ++ if (ios->drv_type == MMC_SET_DRIVER_TYPE_A) ++ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A; ++ else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C) ++ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C; ++ ++ mmc_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); ++ ++ /* Reset SD Clock Enable */ ++ clk = mmc_readw(host, SDHCI_CLOCK_CONTROL); ++ clk &= ~SDHCI_CLOCK_CARD_EN; ++ mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); ++ ++ /* Re-enable SD Clock */ ++ bcm2835_set_clock(host, host->clock); ++ mmc_writeb(host, ctrl, SDHCI_HOST_CONTROL); ++ ++ mmiowb(); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++ ++static struct mmc_host_ops bcm2835_ops = { ++ .request = bcm2835_request, ++ .set_ios = bcm2835_set_ios, ++ .enable_sdio_irq = mmc_enable_sdio_irq, ++}; ++ ++ ++static void sdhci_tasklet_finish(unsigned long param) ++{ ++ struct bcm2835_host *host; ++ unsigned long flags; ++ struct mmc_request *mrq; ++ ++ host = (struct bcm2835_host *)param; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ /* ++ * If this tasklet gets rescheduled while running, it will ++ * be run again afterwards but without any active request. ++ */ ++ if (!host->mrq) { ++ spin_unlock_irqrestore(&host->lock, flags); ++ return; ++ } ++ ++ del_timer(&host->timer); ++ ++ mrq = host->mrq; ++ ++ /* ++ * The controller needs a reset of internal state machines ++ * upon error conditions. ++ */ ++ if (!(host->flags & SDHCI_DEVICE_DEAD) && ++ ((mrq->cmd && mrq->cmd->error) || ++ (mrq->data && (mrq->data->error || ++ (mrq->data->stop && mrq->data->stop->error))))) { ++ ++ bcm2835_reset(host, SDHCI_RESET_CMD); ++ bcm2835_reset(host, SDHCI_RESET_DATA); ++ } ++ ++ host->mrq = NULL; ++ host->cmd = NULL; ++ host->data = NULL; ++ ++ mmiowb(); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ mmc_request_done(host->mmc, mrq); ++} ++ ++ ++ ++int bcm2835_add_host(struct bcm2835_host *host) ++{ ++ struct mmc_host *mmc; ++ struct dma_slave_config cfg; ++ ++ int ret; ++ ++ mmc = host->mmc; ++ ++ bcm2835_reset(host, SDHCI_RESET_ALL); ++ ++ host->clk_mul = 0; ++ ++ mmc->ops = &bcm2835_ops; ++ mmc->f_max = host->max_clk; ++ mmc->f_max = host->max_clk; ++ mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300; ++ ++ /* SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK */ ++ host->timeout_clk = mmc->f_max / 1000; ++#ifdef CONFIG_OF ++ mmc->max_busy_timeout = (1 << 27) / host->timeout_clk; ++#endif ++ /* host controller capabilities */ ++ mmc->caps = MMC_CAP_CMD23 | MMC_CAP_ERASE | MMC_CAP_NEEDS_POLL | MMC_CAP_SDIO_IRQ | ++ MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_4_BIT_DATA; ++ ++ host->flags = SDHCI_AUTO_CMD23; ++ ++ spin_lock_init(&host->lock); ++ ++ ++#ifdef FORCE_PIO ++ pr_info("Forcing PIO mode\n"); ++ host->have_dma = false; ++#else ++ if (!host->dma_chan_tx || !host->dma_chan_rx || ++ IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) { ++ pr_err("%s: Unable to initialise DMA channels. Falling back to PIO\n", DRIVER_NAME); ++ host->have_dma = false; ++ } else { ++ pr_info("DMA channels allocated for the MMC driver"); ++ host->have_dma = true; ++ ++ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ cfg.slave_id = 11; /* DREQ channel */ ++ ++ cfg.direction = DMA_MEM_TO_DEV; ++ cfg.src_addr = 0; ++ cfg.dst_addr = host->phys_addr + SDHCI_BUFFER; ++ ret = dmaengine_slave_config(host->dma_chan_tx, &cfg); ++ ++ cfg.direction = DMA_DEV_TO_MEM; ++ cfg.src_addr = host->phys_addr + SDHCI_BUFFER; ++ cfg.dst_addr = 0; ++ ret = dmaengine_slave_config(host->dma_chan_rx, &cfg); ++ } ++#endif ++ ++ ++ mmc->max_segs = 128; ++ mmc->max_req_size = 524288; ++ mmc->max_seg_size = mmc->max_req_size; ++ mmc->max_blk_size = 512; ++ mmc->max_blk_count = 65535; ++ ++ /* report supported voltage ranges */ ++ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; ++ ++ tasklet_init(&host->finish_tasklet, ++ sdhci_tasklet_finish, (unsigned long)host); ++ ++ setup_timer(&host->timer, bcm2835_timeout_timer, (unsigned long)host); ++ init_waitqueue_head(&host->buf_ready_int); ++ ++ bcm2835_init(host, 0); ++#ifndef CONFIG_OF ++ ret = request_irq(host->irq, mmc_irq, 0 /*IRQF_SHARED*/, ++ mmc_hostname(mmc), host); ++#else ++ ret = request_threaded_irq(host->irq, mmc_irq, mmc_thread_irq, ++ IRQF_SHARED, mmc_hostname(mmc), host); ++#endif ++ if (ret) { ++ pr_err("%s: Failed to request IRQ %d: %d\n", ++ mmc_hostname(mmc), host->irq, ret); ++ goto untasklet; ++ } ++ ++ mmiowb(); ++ mmc_add_host(mmc); ++ ++ pr_info("Load BCM2835 MMC driver\n"); ++ ++ return 0; ++ ++untasklet: ++ tasklet_kill(&host->finish_tasklet); ++ ++ return ret; ++} ++ ++static int bcm2835_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++#ifdef CONFIG_OF ++ struct device_node *node = dev->of_node; ++ struct clk *clk; ++#endif ++ struct resource *iomem; ++ struct bcm2835_host *host = NULL; ++ ++ int ret; ++ struct mmc_host *mmc; ++ dma_cap_mask_t mask; ++ ++ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!iomem) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ if (resource_size(iomem) < 0x100) ++ dev_err(&pdev->dev, "Invalid iomem size!\n"); ++ ++ mmc = mmc_alloc_host(sizeof(struct bcm2835_host), dev); ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ ++ ++ if (IS_ERR(host)) { ++ ret = PTR_ERR(host); ++ goto err; ++ } ++ ++ host->phys_addr = iomem->start + BCM2835_VCMMU_SHIFT; ++ ++#ifndef CONFIG_OF ++#ifndef FORCE_PIO ++ dma_cap_zero(mask); ++ /* we don't care about the channel, any would work */ ++ dma_cap_set(DMA_SLAVE, mask); ++ ++ host->dma_chan_tx = dma_request_channel(mask, NULL, NULL); ++ host->dma_chan_rx = dma_request_channel(mask, NULL, NULL); ++#endif ++ host->max_clk = BCM2835_CLOCK_FREQ; ++ ++#else ++#ifndef FORCE_PIO ++ host->dma_chan_tx = of_dma_request_slave_channel(node, "tx"); ++ host->dma_chan_rx = of_dma_request_slave_channel(node, "rx"); ++#endif ++ clk = of_clk_get(node, 0); ++ if (IS_ERR(clk)) { ++ dev_err(dev, "get CLOCK failed\n"); ++ ret = PTR_ERR(clk); ++ goto out; ++ } ++ host->max_clk = (clk_get_rate(clk)); ++#endif ++ host->irq = platform_get_irq(pdev, 0); ++ ++ if (!request_mem_region(iomem->start, resource_size(iomem), ++ mmc_hostname(host->mmc))) { ++ dev_err(&pdev->dev, "cannot request region\n"); ++ ret = -EBUSY; ++ goto err_request; ++ } ++ ++ host->ioaddr = ioremap(iomem->start, resource_size(iomem)); ++ if (!host->ioaddr) { ++ dev_err(&pdev->dev, "failed to remap registers\n"); ++ ret = -ENOMEM; ++ goto err_remap; ++ } ++ ++ platform_set_drvdata(pdev, host); ++ ++ ++ if (host->irq <= 0) { ++ dev_err(dev, "get IRQ failed\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ ++#ifndef CONFIG_OF ++ mmc->caps |= MMC_CAP_4_BIT_DATA; ++#else ++ mmc_of_parse(mmc); ++#endif ++ host->timeout = msecs_to_jiffies(1000); ++ spin_lock_init(&host->lock); ++ mmc->ops = &bcm2835_ops; ++ return bcm2835_add_host(host); ++ ++ ++err_remap: ++ release_mem_region(iomem->start, resource_size(iomem)); ++err_request: ++ mmc_free_host(host->mmc); ++err: ++ dev_err(&pdev->dev, "%s failed %d\n", __func__, ret); ++ return ret; ++out: ++ if (mmc) ++ mmc_free_host(mmc); ++ return ret; ++} ++ ++static int bcm2835_remove(struct platform_device *pdev) ++{ ++ struct bcm2835_host *host = platform_get_drvdata(pdev); ++ struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ unsigned long flags; ++ int dead; ++ u32 scratch; ++ ++ dead = 0; ++ scratch = mmc_readl(host, SDHCI_INT_STATUS); ++ if (scratch == (u32)-1) ++ dead = 1; ++ ++ ++ if (dead) { ++ spin_lock_irqsave(&host->lock, flags); ++ ++ host->flags |= SDHCI_DEVICE_DEAD; ++ ++ if (host->mrq) { ++ pr_err("%s: Controller removed during " ++ " transfer!\n", mmc_hostname(host->mmc)); ++ ++ host->mrq->cmd->error = -ENOMEDIUM; ++ tasklet_schedule(&host->finish_tasklet); ++ } ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ } ++ ++ mmc_remove_host(host->mmc); ++ ++ if (!dead) ++ bcm2835_reset(host, SDHCI_RESET_ALL); ++ ++ free_irq(host->irq, host); ++ ++ del_timer_sync(&host->timer); ++ ++ tasklet_kill(&host->finish_tasklet); ++ ++ iounmap(host->ioaddr); ++ release_mem_region(iomem->start, resource_size(iomem)); ++ mmc_free_host(host->mmc); ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++ ++static const struct of_device_id bcm2835_mmc_match[] = { ++ { .compatible = "brcm,bcm2835-mmc" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, bcm2835_mmc_match); ++ ++ ++ ++static struct platform_driver bcm2835_mmc_driver = { ++ .probe = bcm2835_probe, ++ .remove = bcm2835_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = bcm2835_mmc_match, ++ }, ++}; ++module_platform_driver(bcm2835_mmc_driver); ++ ++MODULE_ALIAS("platform:mmc-bcm2835"); ++MODULE_DESCRIPTION("BCM2835 SDHCI driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Gellert Weisz"); +diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c +index 6e777f4..b5f9f162 100644 +--- a/drivers/mmc/host/sdhci-bcm2708.c ++++ b/drivers/mmc/host/sdhci-bcm2708.c +@@ -62,13 +62,16 @@ + #define SOFTWARE_ERASE_TIMEOUT_SEC 30 + + #define SDHCI_BCM_DMA_CHAN 4 /* this default is normally overriden */ +-#define SDHCI_BCM_DMA_WAITS 0 /* delays slowing DMA transfers: 0-31 */ ++#define SDHCI_BCM_DMA_WAITS 1 /* delays slowing DMA transfers: 0-31 */ + /* We are worried that SD card DMA use may be blocking the AXI bus for others */ + + /*! TODO: obtain these from the physical address */ + #define DMA_SDHCI_BASE 0x7e300000 /* EMMC register block on Videocore */ + #define DMA_SDHCI_BUFFER (DMA_SDHCI_BASE + SDHCI_BUFFER) + ++#define MAX_LITE_TRANSFER 32768 ++#define MAX_NORMAL_TRANSFER 1073741824 ++ + #define BCM2708_SDHCI_SLEEP_TIMEOUT 1000 /* msecs */ + + /* Mhz clock that the EMMC core is running at. Should match the platform clockman settings */ +@@ -444,29 +447,40 @@ static void schci_bcm2708_cb_read(struct sdhci_bcm2708_priv *host, + dma_addr_t dma_addr, unsigned len, + int /*bool*/ is_last) + { +- struct bcm2708_dma_cb *cb = &host->cb_base[ix]; +- unsigned char dmawaits = host->dma_waits; +- +- cb->info = BCM2708_DMA_PER_MAP(BCM2708_DMA_DREQ_EMMC) | +- BCM2708_DMA_WAITS(dmawaits) | +- BCM2708_DMA_S_DREQ | +- BCM2708_DMA_D_WIDTH | +- BCM2708_DMA_D_INC; +- cb->src = DMA_SDHCI_BUFFER; /* DATA register DMA address */ +- cb->dst = dma_addr; +- cb->length = len; +- cb->stride = 0; +- +- if (is_last) { +- cb->info |= BCM2708_DMA_INT_EN | +- BCM2708_DMA_WAIT_RESP; +- cb->next = 0; +- } else +- cb->next = host->cb_handle + +- (ix+1)*sizeof(struct bcm2708_dma_cb); ++ struct bcm2708_dma_cb *cb; ++ unsigned char dmawaits = host->dma_waits; ++ unsigned i, max_size; + +- cb->pad[0] = 0; +- cb->pad[1] = 0; ++ if (host->dma_chan >= 8) /* we have a LITE channel */ ++ max_size = MAX_LITE_TRANSFER; ++ else ++ max_size = MAX_NORMAL_TRANSFER; ++ ++ ++ for (i = 0; icb_base[ix+i/max_size]; ++ ++ cb->info = BCM2708_DMA_PER_MAP(BCM2708_DMA_DREQ_EMMC) | ++ BCM2708_DMA_WAITS(dmawaits) | ++ BCM2708_DMA_S_DREQ | ++ BCM2708_DMA_D_WIDTH | ++ BCM2708_DMA_D_INC; ++ cb->src = DMA_SDHCI_BUFFER; /* DATA register DMA address */ ++ cb->dst = dma_addr + (dma_addr_t)i; ++ cb->length = min(len-i, max_size); ++ cb->stride = 0; ++ ++ if (is_last && len-i<=max_size) { ++ cb->info |= BCM2708_DMA_INT_EN | ++ BCM2708_DMA_WAIT_RESP; ++ cb->next = 0; ++ } else ++ cb->next = host->cb_handle + ++ (ix+1 + i/max_size)*sizeof(struct bcm2708_dma_cb); ++ ++ cb->pad[0] = 0; ++ cb->pad[1] = 0; ++ } + } + + static void schci_bcm2708_cb_write(struct sdhci_bcm2708_priv *host, +@@ -475,30 +489,42 @@ static void schci_bcm2708_cb_write(struct sdhci_bcm2708_priv *host, + int /*bool*/ is_last) + { + struct bcm2708_dma_cb *cb = &host->cb_base[ix]; +- unsigned char dmawaits = host->dma_waits; ++ unsigned char dmawaits = host->dma_waits; ++ unsigned i, max_size; ++ ++ if (host->dma_chan >= 8) /* we have a LITE channel */ ++ max_size = MAX_LITE_TRANSFER; ++ else ++ max_size = MAX_NORMAL_TRANSFER; + + /* We can make arbitrarily large writes as long as we specify DREQ to +- pace the delivery of bytes to the Arasan hardware */ +- cb->info = BCM2708_DMA_PER_MAP(BCM2708_DMA_DREQ_EMMC) | +- BCM2708_DMA_WAITS(dmawaits) | +- BCM2708_DMA_D_DREQ | +- BCM2708_DMA_S_WIDTH | +- BCM2708_DMA_S_INC; +- cb->src = dma_addr; +- cb->dst = DMA_SDHCI_BUFFER; /* DATA register DMA address */ +- cb->length = len; +- cb->stride = 0; +- +- if (is_last) { +- cb->info |= BCM2708_DMA_INT_EN | +- BCM2708_DMA_WAIT_RESP; +- cb->next = 0; +- } else +- cb->next = host->cb_handle + +- (ix+1)*sizeof(struct bcm2708_dma_cb); ++ pace the delivery of bytes to the Arasan hardware. However we need ++ to take care when using LITE channels */ ++ ++ for (i = 0; icb_base[ix+i/max_size]; ++ ++ cb->info = BCM2708_DMA_PER_MAP(BCM2708_DMA_DREQ_EMMC) | ++ BCM2708_DMA_WAITS(dmawaits) | ++ BCM2708_DMA_D_DREQ | ++ BCM2708_DMA_S_WIDTH | ++ BCM2708_DMA_S_INC; ++ cb->src = dma_addr + (dma_addr_t)i; ++ cb->dst = DMA_SDHCI_BUFFER; /* DATA register DMA address */ ++ cb->length = min(len-i, max_size); ++ cb->stride = 0; ++ ++ if (is_last && len-i<=max_size) { ++ cb->info |= BCM2708_DMA_INT_EN | ++ BCM2708_DMA_WAIT_RESP; ++ cb->next = 0; ++ } else ++ cb->next = host->cb_handle + ++ (ix+1 + i/max_size)*sizeof(struct bcm2708_dma_cb); + +- cb->pad[0] = 0; +- cb->pad[1] = 0; ++ cb->pad[0] = 0; ++ cb->pad[1] = 0; ++ } + } + + +@@ -1390,5 +1416,3 @@ MODULE_PARM_DESC(emmc_clock_freq, "Specify the speed of emmc clock"); + MODULE_PARM_DESC(sync_after_dma, "Block in driver until dma complete"); + MODULE_PARM_DESC(missing_status, "Use the missing status quirk"); + MODULE_PARM_DESC(extra_messages, "Enable more sdcard warning messages"); +- +- +-- +2.0.3 +