diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/staging/ath6kl/hif | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/staging/ath6kl/hif')
4 files changed, 1884 insertions, 0 deletions
diff --git a/drivers/staging/ath6kl/hif/common/hif_sdio_common.h b/drivers/staging/ath6kl/hif/common/hif_sdio_common.h new file mode 100644 index 00000000000..93a2adceca3 --- /dev/null +++ b/drivers/staging/ath6kl/hif/common/hif_sdio_common.h | |||
@@ -0,0 +1,87 @@ | |||
1 | //------------------------------------------------------------------------------ | ||
2 | // Copyright (c) 2009-2010 Atheros Corporation. All rights reserved. | ||
3 | // | ||
4 | // | ||
5 | // Permission to use, copy, modify, and/or distribute this software for any | ||
6 | // purpose with or without fee is hereby granted, provided that the above | ||
7 | // copyright notice and this permission notice appear in all copies. | ||
8 | // | ||
9 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
10 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
11 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
12 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
13 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
14 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
15 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
16 | // | ||
17 | // | ||
18 | //------------------------------------------------------------------------------ | ||
19 | //============================================================================== | ||
20 | // common header file for HIF modules designed for SDIO | ||
21 | // | ||
22 | // Author(s): ="Atheros" | ||
23 | //============================================================================== | ||
24 | |||
25 | #ifndef HIF_SDIO_COMMON_H_ | ||
26 | #define HIF_SDIO_COMMON_H_ | ||
27 | |||
28 | /* SDIO manufacturer ID and Codes */ | ||
29 | #define MANUFACTURER_ID_AR6002_BASE 0x200 | ||
30 | #define MANUFACTURER_ID_AR6003_BASE 0x300 | ||
31 | #define MANUFACTURER_ID_AR6K_BASE_MASK 0xFF00 | ||
32 | #define FUNCTION_CLASS 0x0 | ||
33 | #define MANUFACTURER_CODE 0x271 /* Atheros */ | ||
34 | |||
35 | /* Mailbox address in SDIO address space */ | ||
36 | #define HIF_MBOX_BASE_ADDR 0x800 | ||
37 | #define HIF_MBOX_WIDTH 0x800 | ||
38 | #define HIF_MBOX_START_ADDR(mbox) \ | ||
39 | ( HIF_MBOX_BASE_ADDR + mbox * HIF_MBOX_WIDTH) | ||
40 | |||
41 | #define HIF_MBOX_END_ADDR(mbox) \ | ||
42 | (HIF_MBOX_START_ADDR(mbox) + HIF_MBOX_WIDTH - 1) | ||
43 | |||
44 | /* extended MBOX address for larger MBOX writes to MBOX 0*/ | ||
45 | #define HIF_MBOX0_EXTENDED_BASE_ADDR 0x2800 | ||
46 | #define HIF_MBOX0_EXTENDED_WIDTH_AR6002 (6*1024) | ||
47 | #define HIF_MBOX0_EXTENDED_WIDTH_AR6003 (18*1024) | ||
48 | |||
49 | /* version 1 of the chip has only a 12K extended mbox range */ | ||
50 | #define HIF_MBOX0_EXTENDED_BASE_ADDR_AR6003_V1 0x4000 | ||
51 | #define HIF_MBOX0_EXTENDED_WIDTH_AR6003_V1 (12*1024) | ||
52 | |||
53 | /* GMBOX addresses */ | ||
54 | #define HIF_GMBOX_BASE_ADDR 0x7000 | ||
55 | #define HIF_GMBOX_WIDTH 0x4000 | ||
56 | |||
57 | /* for SDIO we recommend a 128-byte block size */ | ||
58 | #define HIF_DEFAULT_IO_BLOCK_SIZE 128 | ||
59 | |||
60 | /* set extended MBOX window information for SDIO interconnects */ | ||
61 | static INLINE void SetExtendedMboxWindowInfo(u16 Manfid, struct hif_device_mbox_info *pInfo) | ||
62 | { | ||
63 | switch (Manfid & MANUFACTURER_ID_AR6K_BASE_MASK) { | ||
64 | case MANUFACTURER_ID_AR6002_BASE : | ||
65 | /* MBOX 0 has an extended range */ | ||
66 | pInfo->MboxProp[0].ExtendedAddress = HIF_MBOX0_EXTENDED_BASE_ADDR; | ||
67 | pInfo->MboxProp[0].ExtendedSize = HIF_MBOX0_EXTENDED_WIDTH_AR6002; | ||
68 | break; | ||
69 | case MANUFACTURER_ID_AR6003_BASE : | ||
70 | /* MBOX 0 has an extended range */ | ||
71 | pInfo->MboxProp[0].ExtendedAddress = HIF_MBOX0_EXTENDED_BASE_ADDR_AR6003_V1; | ||
72 | pInfo->MboxProp[0].ExtendedSize = HIF_MBOX0_EXTENDED_WIDTH_AR6003_V1; | ||
73 | pInfo->GMboxAddress = HIF_GMBOX_BASE_ADDR; | ||
74 | pInfo->GMboxSize = HIF_GMBOX_WIDTH; | ||
75 | break; | ||
76 | default: | ||
77 | A_ASSERT(false); | ||
78 | break; | ||
79 | } | ||
80 | } | ||
81 | |||
82 | /* special CCCR (func 0) registers */ | ||
83 | |||
84 | #define CCCR_SDIO_IRQ_MODE_REG 0xF0 /* interrupt mode register */ | ||
85 | #define SDIO_IRQ_MODE_ASYNC_4BIT_IRQ (1 << 0) /* mode to enable special 4-bit interrupt assertion without clock*/ | ||
86 | |||
87 | #endif /*HIF_SDIO_COMMON_H_*/ | ||
diff --git a/drivers/staging/ath6kl/hif/sdio/linux_sdio/include/hif_internal.h b/drivers/staging/ath6kl/hif/sdio/linux_sdio/include/hif_internal.h new file mode 100644 index 00000000000..ed7ad4786f5 --- /dev/null +++ b/drivers/staging/ath6kl/hif/sdio/linux_sdio/include/hif_internal.h | |||
@@ -0,0 +1,131 @@ | |||
1 | //------------------------------------------------------------------------------ | ||
2 | // <copyright file="hif_internal.h" company="Atheros"> | ||
3 | // Copyright (c) 2004-2010 Atheros Corporation. All rights reserved. | ||
4 | // | ||
5 | // | ||
6 | // Permission to use, copy, modify, and/or distribute this software for any | ||
7 | // purpose with or without fee is hereby granted, provided that the above | ||
8 | // copyright notice and this permission notice appear in all copies. | ||
9 | // | ||
10 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
11 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
12 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
13 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
14 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
15 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
16 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
17 | // | ||
18 | // | ||
19 | //------------------------------------------------------------------------------ | ||
20 | //============================================================================== | ||
21 | // internal header file for hif layer | ||
22 | // | ||
23 | // Author(s): ="Atheros" | ||
24 | //============================================================================== | ||
25 | #ifndef _HIF_INTERNAL_H_ | ||
26 | #define _HIF_INTERNAL_H_ | ||
27 | |||
28 | #include "a_config.h" | ||
29 | #include "athdefs.h" | ||
30 | #include "a_osapi.h" | ||
31 | #include "hif.h" | ||
32 | #include "../../../common/hif_sdio_common.h" | ||
33 | #include <linux/scatterlist.h> | ||
34 | #define HIF_LINUX_MMC_SCATTER_SUPPORT | ||
35 | |||
36 | #define BUS_REQUEST_MAX_NUM 64 | ||
37 | |||
38 | #define SDIO_CLOCK_FREQUENCY_DEFAULT 25000000 | ||
39 | #define SDWLAN_ENABLE_DISABLE_TIMEOUT 20 | ||
40 | #define FLAGS_CARD_ENAB 0x02 | ||
41 | #define FLAGS_CARD_IRQ_UNMSK 0x04 | ||
42 | |||
43 | #define HIF_MBOX_BLOCK_SIZE HIF_DEFAULT_IO_BLOCK_SIZE | ||
44 | #define HIF_MBOX0_BLOCK_SIZE 1 | ||
45 | #define HIF_MBOX1_BLOCK_SIZE HIF_MBOX_BLOCK_SIZE | ||
46 | #define HIF_MBOX2_BLOCK_SIZE HIF_MBOX_BLOCK_SIZE | ||
47 | #define HIF_MBOX3_BLOCK_SIZE HIF_MBOX_BLOCK_SIZE | ||
48 | |||
49 | typedef struct bus_request { | ||
50 | struct bus_request *next; /* link list of available requests */ | ||
51 | struct bus_request *inusenext; /* link list of in use requests */ | ||
52 | struct semaphore sem_req; | ||
53 | u32 address; /* request data */ | ||
54 | u8 *buffer; | ||
55 | u32 length; | ||
56 | u32 request; | ||
57 | void *context; | ||
58 | int status; | ||
59 | struct hif_scatter_req_priv *pScatterReq; /* this request is a scatter request */ | ||
60 | } BUS_REQUEST; | ||
61 | |||
62 | struct hif_device { | ||
63 | struct sdio_func *func; | ||
64 | spinlock_t asynclock; | ||
65 | struct task_struct* async_task; /* task to handle async commands */ | ||
66 | struct semaphore sem_async; /* wake up for async task */ | ||
67 | int async_shutdown; /* stop the async task */ | ||
68 | struct completion async_completion; /* thread completion */ | ||
69 | BUS_REQUEST *asyncreq; /* request for async tasklet */ | ||
70 | BUS_REQUEST *taskreq; /* async tasklet data */ | ||
71 | spinlock_t lock; | ||
72 | BUS_REQUEST *s_busRequestFreeQueue; /* free list */ | ||
73 | BUS_REQUEST busRequest[BUS_REQUEST_MAX_NUM]; /* available bus requests */ | ||
74 | void *claimedContext; | ||
75 | HTC_CALLBACKS htcCallbacks; | ||
76 | u8 *dma_buffer; | ||
77 | struct dl_list ScatterReqHead; /* scatter request list head */ | ||
78 | bool scatter_enabled; /* scatter enabled flag */ | ||
79 | bool is_suspend; | ||
80 | bool is_disabled; | ||
81 | atomic_t irqHandling; | ||
82 | HIF_DEVICE_POWER_CHANGE_TYPE powerConfig; | ||
83 | const struct sdio_device_id *id; | ||
84 | }; | ||
85 | |||
86 | #define HIF_DMA_BUFFER_SIZE (32 * 1024) | ||
87 | #define CMD53_FIXED_ADDRESS 1 | ||
88 | #define CMD53_INCR_ADDRESS 2 | ||
89 | |||
90 | BUS_REQUEST *hifAllocateBusRequest(struct hif_device *device); | ||
91 | void hifFreeBusRequest(struct hif_device *device, BUS_REQUEST *busrequest); | ||
92 | void AddToAsyncList(struct hif_device *device, BUS_REQUEST *busrequest); | ||
93 | |||
94 | #ifdef HIF_LINUX_MMC_SCATTER_SUPPORT | ||
95 | |||
96 | #define MAX_SCATTER_REQUESTS 4 | ||
97 | #define MAX_SCATTER_ENTRIES_PER_REQ 16 | ||
98 | #define MAX_SCATTER_REQ_TRANSFER_SIZE 32*1024 | ||
99 | |||
100 | struct hif_scatter_req_priv { | ||
101 | struct hif_scatter_req *pHifScatterReq; /* HIF scatter request with allocated entries */ | ||
102 | struct hif_device *device; /* this device */ | ||
103 | BUS_REQUEST *busrequest; /* request associated with request */ | ||
104 | /* scatter list for linux */ | ||
105 | struct scatterlist sgentries[MAX_SCATTER_ENTRIES_PER_REQ]; | ||
106 | }; | ||
107 | |||
108 | #define ATH_DEBUG_SCATTER ATH_DEBUG_MAKE_MODULE_MASK(0) | ||
109 | |||
110 | int SetupHIFScatterSupport(struct hif_device *device, struct hif_device_scatter_support_info *pInfo); | ||
111 | void CleanupHIFScatterResources(struct hif_device *device); | ||
112 | int DoHifReadWriteScatter(struct hif_device *device, BUS_REQUEST *busrequest); | ||
113 | |||
114 | #else // HIF_LINUX_MMC_SCATTER_SUPPORT | ||
115 | |||
116 | static inline int SetupHIFScatterSupport(struct hif_device *device, struct hif_device_scatter_support_info *pInfo) | ||
117 | { | ||
118 | return A_ENOTSUP; | ||
119 | } | ||
120 | |||
121 | static inline int DoHifReadWriteScatter(struct hif_device *device, BUS_REQUEST *busrequest) | ||
122 | { | ||
123 | return A_ENOTSUP; | ||
124 | } | ||
125 | |||
126 | #define CleanupHIFScatterResources(d) { } | ||
127 | |||
128 | #endif // HIF_LINUX_MMC_SCATTER_SUPPORT | ||
129 | |||
130 | #endif // _HIF_INTERNAL_H_ | ||
131 | |||
diff --git a/drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif.c b/drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif.c new file mode 100644 index 00000000000..5f5d67720fa --- /dev/null +++ b/drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif.c | |||
@@ -0,0 +1,1273 @@ | |||
1 | //------------------------------------------------------------------------------ | ||
2 | // <copyright file="hif.c" company="Atheros"> | ||
3 | // Copyright (c) 2004-2010 Atheros Corporation. All rights reserved. | ||
4 | // | ||
5 | // | ||
6 | // Permission to use, copy, modify, and/or distribute this software for any | ||
7 | // purpose with or without fee is hereby granted, provided that the above | ||
8 | // copyright notice and this permission notice appear in all copies. | ||
9 | // | ||
10 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
11 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
12 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
13 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
14 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
15 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
16 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
17 | // | ||
18 | // | ||
19 | //------------------------------------------------------------------------------ | ||
20 | //============================================================================== | ||
21 | // HIF layer reference implementation for Linux Native MMC stack | ||
22 | // | ||
23 | // Author(s): ="Atheros" | ||
24 | //============================================================================== | ||
25 | #include <linux/mmc/card.h> | ||
26 | #include <linux/mmc/mmc.h> | ||
27 | #include <linux/mmc/host.h> | ||
28 | #include <linux/mmc/sdio_func.h> | ||
29 | #include <linux/mmc/sdio_ids.h> | ||
30 | #include <linux/mmc/sdio.h> | ||
31 | #include <linux/mmc/sd.h> | ||
32 | #include <linux/kthread.h> | ||
33 | |||
34 | /* by default setup a bounce buffer for the data packets, if the underlying host controller driver | ||
35 | does not use DMA you may be able to skip this step and save the memory allocation and transfer time */ | ||
36 | #define HIF_USE_DMA_BOUNCE_BUFFER 1 | ||
37 | #include "hif_internal.h" | ||
38 | #define ATH_MODULE_NAME hif | ||
39 | #include "a_debug.h" | ||
40 | #include "hw/mbox_host_reg.h" | ||
41 | |||
42 | #if HIF_USE_DMA_BOUNCE_BUFFER | ||
43 | /* macro to check if DMA buffer is WORD-aligned and DMA-able. Most host controllers assume the | ||
44 | * buffer is DMA'able and will bug-check otherwise (i.e. buffers on the stack). | ||
45 | * virt_addr_valid check fails on stack memory. | ||
46 | */ | ||
47 | #define BUFFER_NEEDS_BOUNCE(buffer) (((unsigned long)(buffer) & 0x3) || !virt_addr_valid((buffer))) | ||
48 | #else | ||
49 | #define BUFFER_NEEDS_BOUNCE(buffer) (false) | ||
50 | #endif | ||
51 | |||
52 | /* ATHENV */ | ||
53 | #if defined(CONFIG_PM) | ||
54 | #define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev) | ||
55 | #define to_sdio_driver(d) container_of(d, struct sdio_driver, drv) | ||
56 | #endif /* CONFIG_PM */ | ||
57 | static void delHifDevice(struct hif_device * device); | ||
58 | static int Func0_CMD52WriteByte(struct mmc_card *card, unsigned int address, unsigned char byte); | ||
59 | static int Func0_CMD52ReadByte(struct mmc_card *card, unsigned int address, unsigned char *byte); | ||
60 | |||
61 | static int hifEnableFunc(struct hif_device *device, struct sdio_func *func); | ||
62 | static int hifDisableFunc(struct hif_device *device, struct sdio_func *func); | ||
63 | OSDRV_CALLBACKS osdrvCallbacks; | ||
64 | |||
65 | int reset_sdio_on_unload = 0; | ||
66 | module_param(reset_sdio_on_unload, int, 0644); | ||
67 | |||
68 | extern u32 nohifscattersupport; | ||
69 | |||
70 | static struct hif_device *ath6kl_alloc_hifdev(struct sdio_func *func) | ||
71 | { | ||
72 | struct hif_device *hifdevice; | ||
73 | |||
74 | hifdevice = kzalloc(sizeof(struct hif_device), GFP_KERNEL); | ||
75 | |||
76 | #if HIF_USE_DMA_BOUNCE_BUFFER | ||
77 | hifdevice->dma_buffer = kmalloc(HIF_DMA_BUFFER_SIZE, GFP_KERNEL); | ||
78 | #endif | ||
79 | hifdevice->func = func; | ||
80 | hifdevice->powerConfig = HIF_DEVICE_POWER_UP; | ||
81 | sdio_set_drvdata(func, hifdevice); | ||
82 | |||
83 | return hifdevice; | ||
84 | } | ||
85 | |||
86 | static struct hif_device *ath6kl_get_hifdev(struct sdio_func *func) | ||
87 | { | ||
88 | return (struct hif_device *) sdio_get_drvdata(func); | ||
89 | } | ||
90 | |||
91 | static const struct sdio_device_id ath6kl_hifdev_ids[] = { | ||
92 | { SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6002_BASE | 0x0)) }, | ||
93 | { SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6002_BASE | 0x1)) }, | ||
94 | { SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x0)) }, | ||
95 | { SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1)) }, | ||
96 | { /* null */ }, | ||
97 | }; | ||
98 | |||
99 | MODULE_DEVICE_TABLE(sdio, ath6kl_hifdev_ids); | ||
100 | |||
101 | static int ath6kl_hifdev_probe(struct sdio_func *func, | ||
102 | const struct sdio_device_id *id) | ||
103 | { | ||
104 | int ret; | ||
105 | struct hif_device *device; | ||
106 | int count; | ||
107 | |||
108 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, | ||
109 | ("ath6kl: Function: 0x%X, Vendor ID: 0x%X, " | ||
110 | "Device ID: 0x%X, block size: 0x%X/0x%X\n", | ||
111 | func->num, func->vendor, func->device, | ||
112 | func->max_blksize, func->cur_blksize)); | ||
113 | |||
114 | ath6kl_alloc_hifdev(func); | ||
115 | device = ath6kl_get_hifdev(func); | ||
116 | |||
117 | device->id = id; | ||
118 | device->is_disabled = true; | ||
119 | |||
120 | spin_lock_init(&device->lock); | ||
121 | spin_lock_init(&device->asynclock); | ||
122 | |||
123 | DL_LIST_INIT(&device->ScatterReqHead); | ||
124 | |||
125 | /* Try to allow scatter unless globally overridden */ | ||
126 | if (!nohifscattersupport) | ||
127 | device->scatter_enabled = true; | ||
128 | |||
129 | A_MEMZERO(device->busRequest, sizeof(device->busRequest)); | ||
130 | |||
131 | for (count = 0; count < BUS_REQUEST_MAX_NUM; count++) { | ||
132 | sema_init(&device->busRequest[count].sem_req, 0); | ||
133 | hifFreeBusRequest(device, &device->busRequest[count]); | ||
134 | } | ||
135 | |||
136 | sema_init(&device->sem_async, 0); | ||
137 | |||
138 | ret = hifEnableFunc(device, func); | ||
139 | |||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | static void ath6kl_hifdev_remove(struct sdio_func *func) | ||
144 | { | ||
145 | int status = 0; | ||
146 | struct hif_device *device; | ||
147 | |||
148 | device = ath6kl_get_hifdev(func); | ||
149 | if (device->claimedContext != NULL) | ||
150 | status = osdrvCallbacks. | ||
151 | deviceRemovedHandler(device->claimedContext, device); | ||
152 | |||
153 | if (device->is_disabled) | ||
154 | device->is_disabled = false; | ||
155 | else | ||
156 | status = hifDisableFunc(device, func); | ||
157 | |||
158 | CleanupHIFScatterResources(device); | ||
159 | |||
160 | delHifDevice(device); | ||
161 | } | ||
162 | |||
163 | #if defined(CONFIG_PM) | ||
164 | static int ath6kl_hifdev_suspend(struct device *dev) | ||
165 | { | ||
166 | struct sdio_func *func = dev_to_sdio_func(dev); | ||
167 | int status = 0; | ||
168 | struct hif_device *device; | ||
169 | |||
170 | device = ath6kl_get_hifdev(func); | ||
171 | |||
172 | if (device && device->claimedContext && | ||
173 | osdrvCallbacks.deviceSuspendHandler) { | ||
174 | /* set true first for PowerStateChangeNotify(..) */ | ||
175 | device->is_suspend = true; | ||
176 | status = osdrvCallbacks. | ||
177 | deviceSuspendHandler(device->claimedContext); | ||
178 | if (status) | ||
179 | device->is_suspend = false; | ||
180 | } | ||
181 | |||
182 | CleanupHIFScatterResources(device); | ||
183 | |||
184 | switch (status) { | ||
185 | case 0: | ||
186 | return 0; | ||
187 | case A_EBUSY: | ||
188 | /* Hack for kernel in order to support deep sleep and wow */ | ||
189 | return -EBUSY; | ||
190 | default: | ||
191 | return -1; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | static int ath6kl_hifdev_resume(struct device *dev) | ||
196 | { | ||
197 | struct sdio_func *func = dev_to_sdio_func(dev); | ||
198 | int status = 0; | ||
199 | struct hif_device *device; | ||
200 | |||
201 | device = ath6kl_get_hifdev(func); | ||
202 | if (device && device->claimedContext && | ||
203 | osdrvCallbacks.deviceSuspendHandler) { | ||
204 | status = osdrvCallbacks. | ||
205 | deviceResumeHandler(device->claimedContext); | ||
206 | if (status == 0) | ||
207 | device->is_suspend = false; | ||
208 | } | ||
209 | |||
210 | return status; | ||
211 | } | ||
212 | |||
213 | static const struct dev_pm_ops ath6kl_hifdev_pmops = { | ||
214 | .suspend = ath6kl_hifdev_suspend, | ||
215 | .resume = ath6kl_hifdev_resume, | ||
216 | }; | ||
217 | #endif /* CONFIG_PM */ | ||
218 | |||
219 | static struct sdio_driver ath6kl_hifdev_driver = { | ||
220 | .name = "ath6kl_hifdev", | ||
221 | .id_table = ath6kl_hifdev_ids, | ||
222 | .probe = ath6kl_hifdev_probe, | ||
223 | .remove = ath6kl_hifdev_remove, | ||
224 | #if defined(CONFIG_PM) | ||
225 | .drv = { | ||
226 | .pm = &ath6kl_hifdev_pmops, | ||
227 | }, | ||
228 | #endif | ||
229 | }; | ||
230 | |||
231 | /* make sure we only unregister when registered. */ | ||
232 | static int registered = 0; | ||
233 | |||
234 | extern u32 onebitmode; | ||
235 | extern u32 busspeedlow; | ||
236 | extern u32 debughif; | ||
237 | |||
238 | static void ResetAllCards(void); | ||
239 | |||
240 | #ifdef DEBUG | ||
241 | |||
242 | ATH_DEBUG_INSTANTIATE_MODULE_VAR(hif, | ||
243 | "hif", | ||
244 | "(Linux MMC) Host Interconnect Framework", | ||
245 | ATH_DEBUG_MASK_DEFAULTS, | ||
246 | 0, | ||
247 | NULL); | ||
248 | |||
249 | #endif | ||
250 | |||
251 | |||
252 | /* ------ Functions ------ */ | ||
253 | int HIFInit(OSDRV_CALLBACKS *callbacks) | ||
254 | { | ||
255 | int r; | ||
256 | AR_DEBUG_ASSERT(callbacks != NULL); | ||
257 | |||
258 | A_REGISTER_MODULE_DEBUG_INFO(hif); | ||
259 | |||
260 | /* store the callback handlers */ | ||
261 | osdrvCallbacks = *callbacks; | ||
262 | |||
263 | /* Register with bus driver core */ | ||
264 | registered = 1; | ||
265 | |||
266 | r = sdio_register_driver(&ath6kl_hifdev_driver); | ||
267 | if (r < 0) | ||
268 | return r; | ||
269 | |||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int | ||
274 | __HIFReadWrite(struct hif_device *device, | ||
275 | u32 address, | ||
276 | u8 *buffer, | ||
277 | u32 length, | ||
278 | u32 request, | ||
279 | void *context) | ||
280 | { | ||
281 | u8 opcode; | ||
282 | int status = 0; | ||
283 | int ret; | ||
284 | u8 *tbuffer; | ||
285 | bool bounced = false; | ||
286 | |||
287 | AR_DEBUG_ASSERT(device != NULL); | ||
288 | AR_DEBUG_ASSERT(device->func != NULL); | ||
289 | |||
290 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Device: 0x%p, buffer:0x%p (addr:0x%X)\n", | ||
291 | device, buffer, address)); | ||
292 | |||
293 | do { | ||
294 | if (request & HIF_EXTENDED_IO) { | ||
295 | //AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Command type: CMD53\n")); | ||
296 | } else { | ||
297 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, | ||
298 | ("AR6000: Invalid command type: 0x%08x\n", request)); | ||
299 | status = A_EINVAL; | ||
300 | break; | ||
301 | } | ||
302 | |||
303 | if (request & HIF_BLOCK_BASIS) { | ||
304 | /* round to whole block length size */ | ||
305 | length = (length / HIF_MBOX_BLOCK_SIZE) * HIF_MBOX_BLOCK_SIZE; | ||
306 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, | ||
307 | ("AR6000: Block mode (BlockLen: %d)\n", | ||
308 | length)); | ||
309 | } else if (request & HIF_BYTE_BASIS) { | ||
310 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, | ||
311 | ("AR6000: Byte mode (BlockLen: %d)\n", | ||
312 | length)); | ||
313 | } else { | ||
314 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, | ||
315 | ("AR6000: Invalid data mode: 0x%08x\n", request)); | ||
316 | status = A_EINVAL; | ||
317 | break; | ||
318 | } | ||
319 | |||
320 | #if 0 | ||
321 | /* useful for checking register accesses */ | ||
322 | if (length & 0x3) { | ||
323 | A_PRINTF(KERN_ALERT"AR6000: HIF (%s) is not a multiple of 4 bytes, addr:0x%X, len:%d\n", | ||
324 | request & HIF_WRITE ? "write":"read", address, length); | ||
325 | } | ||
326 | #endif | ||
327 | |||
328 | if (request & HIF_WRITE) { | ||
329 | if ((address >= HIF_MBOX_START_ADDR(0)) && | ||
330 | (address <= HIF_MBOX_END_ADDR(3))) | ||
331 | { | ||
332 | |||
333 | AR_DEBUG_ASSERT(length <= HIF_MBOX_WIDTH); | ||
334 | |||
335 | /* | ||
336 | * Mailbox write. Adjust the address so that the last byte | ||
337 | * falls on the EOM address. | ||
338 | */ | ||
339 | address += (HIF_MBOX_WIDTH - length); | ||
340 | } | ||
341 | } | ||
342 | |||
343 | if (request & HIF_FIXED_ADDRESS) { | ||
344 | opcode = CMD53_FIXED_ADDRESS; | ||
345 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Address mode: Fixed 0x%X\n", address)); | ||
346 | } else if (request & HIF_INCREMENTAL_ADDRESS) { | ||
347 | opcode = CMD53_INCR_ADDRESS; | ||
348 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Address mode: Incremental 0x%X\n", address)); | ||
349 | } else { | ||
350 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, | ||
351 | ("AR6000: Invalid address mode: 0x%08x\n", request)); | ||
352 | status = A_EINVAL; | ||
353 | break; | ||
354 | } | ||
355 | |||
356 | if (request & HIF_WRITE) { | ||
357 | #if HIF_USE_DMA_BOUNCE_BUFFER | ||
358 | if (BUFFER_NEEDS_BOUNCE(buffer)) { | ||
359 | AR_DEBUG_ASSERT(device->dma_buffer != NULL); | ||
360 | tbuffer = device->dma_buffer; | ||
361 | /* copy the write data to the dma buffer */ | ||
362 | AR_DEBUG_ASSERT(length <= HIF_DMA_BUFFER_SIZE); | ||
363 | memcpy(tbuffer, buffer, length); | ||
364 | bounced = true; | ||
365 | } else { | ||
366 | tbuffer = buffer; | ||
367 | } | ||
368 | #else | ||
369 | tbuffer = buffer; | ||
370 | #endif | ||
371 | if (opcode == CMD53_FIXED_ADDRESS) { | ||
372 | ret = sdio_writesb(device->func, address, tbuffer, length); | ||
373 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: writesb ret=%d address: 0x%X, len: %d, 0x%X\n", | ||
374 | ret, address, length, *(int *)tbuffer)); | ||
375 | } else { | ||
376 | ret = sdio_memcpy_toio(device->func, address, tbuffer, length); | ||
377 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: writeio ret=%d address: 0x%X, len: %d, 0x%X\n", | ||
378 | ret, address, length, *(int *)tbuffer)); | ||
379 | } | ||
380 | } else if (request & HIF_READ) { | ||
381 | #if HIF_USE_DMA_BOUNCE_BUFFER | ||
382 | if (BUFFER_NEEDS_BOUNCE(buffer)) { | ||
383 | AR_DEBUG_ASSERT(device->dma_buffer != NULL); | ||
384 | AR_DEBUG_ASSERT(length <= HIF_DMA_BUFFER_SIZE); | ||
385 | tbuffer = device->dma_buffer; | ||
386 | bounced = true; | ||
387 | } else { | ||
388 | tbuffer = buffer; | ||
389 | } | ||
390 | #else | ||
391 | tbuffer = buffer; | ||
392 | #endif | ||
393 | if (opcode == CMD53_FIXED_ADDRESS) { | ||
394 | ret = sdio_readsb(device->func, tbuffer, address, length); | ||
395 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: readsb ret=%d address: 0x%X, len: %d, 0x%X\n", | ||
396 | ret, address, length, *(int *)tbuffer)); | ||
397 | } else { | ||
398 | ret = sdio_memcpy_fromio(device->func, tbuffer, address, length); | ||
399 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: readio ret=%d address: 0x%X, len: %d, 0x%X\n", | ||
400 | ret, address, length, *(int *)tbuffer)); | ||
401 | } | ||
402 | #if HIF_USE_DMA_BOUNCE_BUFFER | ||
403 | if (bounced) { | ||
404 | /* copy the read data from the dma buffer */ | ||
405 | memcpy(buffer, tbuffer, length); | ||
406 | } | ||
407 | #endif | ||
408 | } else { | ||
409 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, | ||
410 | ("AR6000: Invalid direction: 0x%08x\n", request)); | ||
411 | status = A_EINVAL; | ||
412 | break; | ||
413 | } | ||
414 | |||
415 | if (ret) { | ||
416 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, | ||
417 | ("AR6000: SDIO bus operation failed! MMC stack returned : %d \n", ret)); | ||
418 | status = A_ERROR; | ||
419 | } | ||
420 | } while (false); | ||
421 | |||
422 | return status; | ||
423 | } | ||
424 | |||
425 | void AddToAsyncList(struct hif_device *device, BUS_REQUEST *busrequest) | ||
426 | { | ||
427 | unsigned long flags; | ||
428 | BUS_REQUEST *async; | ||
429 | BUS_REQUEST *active; | ||
430 | |||
431 | spin_lock_irqsave(&device->asynclock, flags); | ||
432 | active = device->asyncreq; | ||
433 | if (active == NULL) { | ||
434 | device->asyncreq = busrequest; | ||
435 | device->asyncreq->inusenext = NULL; | ||
436 | } else { | ||
437 | for (async = device->asyncreq; | ||
438 | async != NULL; | ||
439 | async = async->inusenext) { | ||
440 | active = async; | ||
441 | } | ||
442 | active->inusenext = busrequest; | ||
443 | busrequest->inusenext = NULL; | ||
444 | } | ||
445 | spin_unlock_irqrestore(&device->asynclock, flags); | ||
446 | } | ||
447 | |||
448 | |||
449 | /* queue a read/write request */ | ||
450 | int | ||
451 | HIFReadWrite(struct hif_device *device, | ||
452 | u32 address, | ||
453 | u8 *buffer, | ||
454 | u32 length, | ||
455 | u32 request, | ||
456 | void *context) | ||
457 | { | ||
458 | int status = 0; | ||
459 | BUS_REQUEST *busrequest; | ||
460 | |||
461 | |||
462 | AR_DEBUG_ASSERT(device != NULL); | ||
463 | AR_DEBUG_ASSERT(device->func != NULL); | ||
464 | |||
465 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Device: %p addr:0x%X\n", device,address)); | ||
466 | |||
467 | do { | ||
468 | if ((request & HIF_ASYNCHRONOUS) || (request & HIF_SYNCHRONOUS)){ | ||
469 | /* serialize all requests through the async thread */ | ||
470 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Execution mode: %s\n", | ||
471 | (request & HIF_ASYNCHRONOUS)?"Async":"Synch")); | ||
472 | busrequest = hifAllocateBusRequest(device); | ||
473 | if (busrequest == NULL) { | ||
474 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, | ||
475 | ("AR6000: no async bus requests available (%s, addr:0x%X, len:%d) \n", | ||
476 | request & HIF_READ ? "READ":"WRITE", address, length)); | ||
477 | return A_ERROR; | ||
478 | } | ||
479 | busrequest->address = address; | ||
480 | busrequest->buffer = buffer; | ||
481 | busrequest->length = length; | ||
482 | busrequest->request = request; | ||
483 | busrequest->context = context; | ||
484 | |||
485 | AddToAsyncList(device, busrequest); | ||
486 | |||
487 | if (request & HIF_SYNCHRONOUS) { | ||
488 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: queued sync req: 0x%lX\n", (unsigned long)busrequest)); | ||
489 | |||
490 | /* wait for completion */ | ||
491 | up(&device->sem_async); | ||
492 | if (down_interruptible(&busrequest->sem_req) != 0) { | ||
493 | /* interrupted, exit */ | ||
494 | return A_ERROR; | ||
495 | } else { | ||
496 | int status = busrequest->status; | ||
497 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: sync return freeing 0x%lX: 0x%X\n", | ||
498 | (unsigned long)busrequest, busrequest->status)); | ||
499 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: freeing req: 0x%X\n", (unsigned int)request)); | ||
500 | hifFreeBusRequest(device, busrequest); | ||
501 | return status; | ||
502 | } | ||
503 | } else { | ||
504 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: queued async req: 0x%lX\n", (unsigned long)busrequest)); | ||
505 | up(&device->sem_async); | ||
506 | return A_PENDING; | ||
507 | } | ||
508 | } else { | ||
509 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, | ||
510 | ("AR6000: Invalid execution mode: 0x%08x\n", (unsigned int)request)); | ||
511 | status = A_EINVAL; | ||
512 | break; | ||
513 | } | ||
514 | } while(0); | ||
515 | |||
516 | return status; | ||
517 | } | ||
518 | /* thread to serialize all requests, both sync and async */ | ||
519 | static int async_task(void *param) | ||
520 | { | ||
521 | struct hif_device *device; | ||
522 | BUS_REQUEST *request; | ||
523 | int status; | ||
524 | unsigned long flags; | ||
525 | |||
526 | device = (struct hif_device *)param; | ||
527 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async task\n")); | ||
528 | set_current_state(TASK_INTERRUPTIBLE); | ||
529 | while(!device->async_shutdown) { | ||
530 | /* wait for work */ | ||
531 | if (down_interruptible(&device->sem_async) != 0) { | ||
532 | /* interrupted, exit */ | ||
533 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async task interrupted\n")); | ||
534 | break; | ||
535 | } | ||
536 | if (device->async_shutdown) { | ||
537 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async task stopping\n")); | ||
538 | break; | ||
539 | } | ||
540 | /* we want to hold the host over multiple cmds if possible, but holding the host blocks card interrupts */ | ||
541 | sdio_claim_host(device->func); | ||
542 | spin_lock_irqsave(&device->asynclock, flags); | ||
543 | /* pull the request to work on */ | ||
544 | while (device->asyncreq != NULL) { | ||
545 | request = device->asyncreq; | ||
546 | if (request->inusenext != NULL) { | ||
547 | device->asyncreq = request->inusenext; | ||
548 | } else { | ||
549 | device->asyncreq = NULL; | ||
550 | } | ||
551 | spin_unlock_irqrestore(&device->asynclock, flags); | ||
552 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async_task processing req: 0x%lX\n", (unsigned long)request)); | ||
553 | |||
554 | if (request->pScatterReq != NULL) { | ||
555 | A_ASSERT(device->scatter_enabled); | ||
556 | /* this is a queued scatter request, pass the request to scatter routine which | ||
557 | * executes it synchronously, note, no need to free the request since scatter requests | ||
558 | * are maintained on a separate list */ | ||
559 | status = DoHifReadWriteScatter(device,request); | ||
560 | } else { | ||
561 | /* call HIFReadWrite in sync mode to do the work */ | ||
562 | status = __HIFReadWrite(device, request->address, request->buffer, | ||
563 | request->length, request->request & ~HIF_SYNCHRONOUS, NULL); | ||
564 | if (request->request & HIF_ASYNCHRONOUS) { | ||
565 | void *context = request->context; | ||
566 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async_task freeing req: 0x%lX\n", (unsigned long)request)); | ||
567 | hifFreeBusRequest(device, request); | ||
568 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async_task completion routine req: 0x%lX\n", (unsigned long)request)); | ||
569 | device->htcCallbacks.rwCompletionHandler(context, status); | ||
570 | } else { | ||
571 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: async_task upping req: 0x%lX\n", (unsigned long)request)); | ||
572 | request->status = status; | ||
573 | up(&request->sem_req); | ||
574 | } | ||
575 | } | ||
576 | spin_lock_irqsave(&device->asynclock, flags); | ||
577 | } | ||
578 | spin_unlock_irqrestore(&device->asynclock, flags); | ||
579 | sdio_release_host(device->func); | ||
580 | } | ||
581 | |||
582 | complete_and_exit(&device->async_completion, 0); | ||
583 | return 0; | ||
584 | } | ||
585 | |||
586 | static s32 IssueSDCommand(struct hif_device *device, u32 opcode, u32 arg, u32 flags, u32 *resp) | ||
587 | { | ||
588 | struct mmc_command cmd; | ||
589 | s32 err; | ||
590 | struct mmc_host *host; | ||
591 | struct sdio_func *func; | ||
592 | |||
593 | func = device->func; | ||
594 | host = func->card->host; | ||
595 | |||
596 | memset(&cmd, 0, sizeof(struct mmc_command)); | ||
597 | cmd.opcode = opcode; | ||
598 | cmd.arg = arg; | ||
599 | cmd.flags = flags; | ||
600 | err = mmc_wait_for_cmd(host, &cmd, 3); | ||
601 | |||
602 | if ((!err) && (resp)) { | ||
603 | *resp = cmd.resp[0]; | ||
604 | } | ||
605 | |||
606 | return err; | ||
607 | } | ||
608 | |||
609 | int ReinitSDIO(struct hif_device *device) | ||
610 | { | ||
611 | s32 err; | ||
612 | struct mmc_host *host; | ||
613 | struct mmc_card *card; | ||
614 | struct sdio_func *func; | ||
615 | u8 cmd52_resp; | ||
616 | u32 clock; | ||
617 | |||
618 | func = device->func; | ||
619 | card = func->card; | ||
620 | host = card->host; | ||
621 | |||
622 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +ReinitSDIO \n")); | ||
623 | sdio_claim_host(func); | ||
624 | |||
625 | do { | ||
626 | if (!device->is_suspend) { | ||
627 | u32 resp; | ||
628 | u16 rca; | ||
629 | u32 i; | ||
630 | int bit = fls(host->ocr_avail) - 1; | ||
631 | /* emulate the mmc_power_up(...) */ | ||
632 | host->ios.vdd = bit; | ||
633 | host->ios.chip_select = MMC_CS_DONTCARE; | ||
634 | host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; | ||
635 | host->ios.power_mode = MMC_POWER_UP; | ||
636 | host->ios.bus_width = MMC_BUS_WIDTH_1; | ||
637 | host->ios.timing = MMC_TIMING_LEGACY; | ||
638 | host->ops->set_ios(host, &host->ios); | ||
639 | /* | ||
640 | * This delay should be sufficient to allow the power supply | ||
641 | * to reach the minimum voltage. | ||
642 | */ | ||
643 | msleep(2); | ||
644 | |||
645 | host->ios.clock = host->f_min; | ||
646 | host->ios.power_mode = MMC_POWER_ON; | ||
647 | host->ops->set_ios(host, &host->ios); | ||
648 | |||
649 | /* | ||
650 | * This delay must be at least 74 clock sizes, or 1 ms, or the | ||
651 | * time required to reach a stable voltage. | ||
652 | */ | ||
653 | msleep(2); | ||
654 | |||
655 | /* Issue CMD0. Goto idle state */ | ||
656 | host->ios.chip_select = MMC_CS_HIGH; | ||
657 | host->ops->set_ios(host, &host->ios); | ||
658 | msleep(1); | ||
659 | err = IssueSDCommand(device, MMC_GO_IDLE_STATE, 0, (MMC_RSP_NONE | MMC_CMD_BC), NULL); | ||
660 | host->ios.chip_select = MMC_CS_DONTCARE; | ||
661 | host->ops->set_ios(host, &host->ios); | ||
662 | msleep(1); | ||
663 | host->use_spi_crc = 0; | ||
664 | |||
665 | if (err) { | ||
666 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD0 failed : %d \n",err)); | ||
667 | break; | ||
668 | } | ||
669 | |||
670 | if (!host->ocr) { | ||
671 | /* Issue CMD5, arg = 0 */ | ||
672 | err = IssueSDCommand(device, SD_IO_SEND_OP_COND, 0, (MMC_RSP_R4 | MMC_CMD_BCR), &resp); | ||
673 | if (err) { | ||
674 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD5 failed : %d \n",err)); | ||
675 | break; | ||
676 | } | ||
677 | host->ocr = resp; | ||
678 | } | ||
679 | |||
680 | /* Issue CMD5, arg = ocr. Wait till card is ready */ | ||
681 | for (i=0;i<100;i++) { | ||
682 | err = IssueSDCommand(device, SD_IO_SEND_OP_COND, host->ocr, (MMC_RSP_R4 | MMC_CMD_BCR), &resp); | ||
683 | if (err) { | ||
684 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD5 failed : %d \n",err)); | ||
685 | break; | ||
686 | } | ||
687 | if (resp & MMC_CARD_BUSY) { | ||
688 | break; | ||
689 | } | ||
690 | msleep(10); | ||
691 | } | ||
692 | |||
693 | if ((i == 100) || (err)) { | ||
694 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: card in not ready : %d %d \n",i,err)); | ||
695 | break; | ||
696 | } | ||
697 | |||
698 | /* Issue CMD3, get RCA */ | ||
699 | err = IssueSDCommand(device, SD_SEND_RELATIVE_ADDR, 0, MMC_RSP_R6 | MMC_CMD_BCR, &resp); | ||
700 | if (err) { | ||
701 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD3 failed : %d \n",err)); | ||
702 | break; | ||
703 | } | ||
704 | rca = resp >> 16; | ||
705 | host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; | ||
706 | host->ops->set_ios(host, &host->ios); | ||
707 | |||
708 | /* Issue CMD7, select card */ | ||
709 | err = IssueSDCommand(device, MMC_SELECT_CARD, (rca << 16), MMC_RSP_R1 | MMC_CMD_AC, NULL); | ||
710 | if (err) { | ||
711 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD7 failed : %d \n",err)); | ||
712 | break; | ||
713 | } | ||
714 | } | ||
715 | |||
716 | /* Enable high speed */ | ||
717 | if (card->host->caps & MMC_CAP_SD_HIGHSPEED) { | ||
718 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("ReinitSDIO: Set high speed mode\n")); | ||
719 | err = Func0_CMD52ReadByte(card, SDIO_CCCR_SPEED, &cmd52_resp); | ||
720 | if (err) { | ||
721 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD52 read to CCCR speed register failed : %d \n",err)); | ||
722 | card->state &= ~MMC_STATE_HIGHSPEED; | ||
723 | /* no need to break */ | ||
724 | } else { | ||
725 | err = Func0_CMD52WriteByte(card, SDIO_CCCR_SPEED, (cmd52_resp | SDIO_SPEED_EHS)); | ||
726 | if (err) { | ||
727 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD52 write to CCCR speed register failed : %d \n",err)); | ||
728 | break; | ||
729 | } | ||
730 | mmc_card_set_highspeed(card); | ||
731 | host->ios.timing = MMC_TIMING_SD_HS; | ||
732 | host->ops->set_ios(host, &host->ios); | ||
733 | } | ||
734 | } | ||
735 | |||
736 | /* Set clock */ | ||
737 | if (mmc_card_highspeed(card)) { | ||
738 | clock = 50000000; | ||
739 | } else { | ||
740 | clock = card->cis.max_dtr; | ||
741 | } | ||
742 | |||
743 | if (clock > host->f_max) { | ||
744 | clock = host->f_max; | ||
745 | } | ||
746 | host->ios.clock = clock; | ||
747 | host->ops->set_ios(host, &host->ios); | ||
748 | |||
749 | |||
750 | if (card->host->caps & MMC_CAP_4_BIT_DATA) { | ||
751 | /* CMD52: Set bus width & disable card detect resistor */ | ||
752 | err = Func0_CMD52WriteByte(card, SDIO_CCCR_IF, SDIO_BUS_CD_DISABLE | SDIO_BUS_WIDTH_4BIT); | ||
753 | if (err) { | ||
754 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("ReinitSDIO: CMD52 to set bus mode failed : %d \n",err)); | ||
755 | break; | ||
756 | } | ||
757 | host->ios.bus_width = MMC_BUS_WIDTH_4; | ||
758 | host->ops->set_ios(host, &host->ios); | ||
759 | } | ||
760 | } while (0); | ||
761 | |||
762 | sdio_release_host(func); | ||
763 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: -ReinitSDIO \n")); | ||
764 | |||
765 | return (err) ? A_ERROR : 0; | ||
766 | } | ||
767 | |||
768 | int | ||
769 | PowerStateChangeNotify(struct hif_device *device, HIF_DEVICE_POWER_CHANGE_TYPE config) | ||
770 | { | ||
771 | int status = 0; | ||
772 | #if defined(CONFIG_PM) | ||
773 | struct sdio_func *func = device->func; | ||
774 | int old_reset_val; | ||
775 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +PowerStateChangeNotify %d\n", config)); | ||
776 | switch (config) { | ||
777 | case HIF_DEVICE_POWER_DOWN: | ||
778 | case HIF_DEVICE_POWER_CUT: | ||
779 | old_reset_val = reset_sdio_on_unload; | ||
780 | reset_sdio_on_unload = 1; | ||
781 | status = hifDisableFunc(device, func); | ||
782 | reset_sdio_on_unload = old_reset_val; | ||
783 | if (!device->is_suspend) { | ||
784 | struct mmc_host *host = func->card->host; | ||
785 | host->ios.clock = 0; | ||
786 | host->ios.vdd = 0; | ||
787 | host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; | ||
788 | host->ios.chip_select = MMC_CS_DONTCARE; | ||
789 | host->ios.power_mode = MMC_POWER_OFF; | ||
790 | host->ios.bus_width = MMC_BUS_WIDTH_1; | ||
791 | host->ios.timing = MMC_TIMING_LEGACY; | ||
792 | host->ops->set_ios(host, &host->ios); | ||
793 | } | ||
794 | break; | ||
795 | case HIF_DEVICE_POWER_UP: | ||
796 | if (device->powerConfig == HIF_DEVICE_POWER_CUT) { | ||
797 | status = ReinitSDIO(device); | ||
798 | } | ||
799 | if (status == 0) { | ||
800 | status = hifEnableFunc(device, func); | ||
801 | } | ||
802 | break; | ||
803 | } | ||
804 | device->powerConfig = config; | ||
805 | |||
806 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: -PowerStateChangeNotify\n")); | ||
807 | #endif | ||
808 | return status; | ||
809 | } | ||
810 | |||
811 | int | ||
812 | HIFConfigureDevice(struct hif_device *device, HIF_DEVICE_CONFIG_OPCODE opcode, | ||
813 | void *config, u32 configLen) | ||
814 | { | ||
815 | u32 count; | ||
816 | int status = 0; | ||
817 | |||
818 | switch(opcode) { | ||
819 | case HIF_DEVICE_GET_MBOX_BLOCK_SIZE: | ||
820 | ((u32 *)config)[0] = HIF_MBOX0_BLOCK_SIZE; | ||
821 | ((u32 *)config)[1] = HIF_MBOX1_BLOCK_SIZE; | ||
822 | ((u32 *)config)[2] = HIF_MBOX2_BLOCK_SIZE; | ||
823 | ((u32 *)config)[3] = HIF_MBOX3_BLOCK_SIZE; | ||
824 | break; | ||
825 | |||
826 | case HIF_DEVICE_GET_MBOX_ADDR: | ||
827 | for (count = 0; count < 4; count ++) { | ||
828 | ((u32 *)config)[count] = HIF_MBOX_START_ADDR(count); | ||
829 | } | ||
830 | |||
831 | if (configLen >= sizeof(struct hif_device_mbox_info)) { | ||
832 | SetExtendedMboxWindowInfo((u16)device->func->device, | ||
833 | (struct hif_device_mbox_info *)config); | ||
834 | } | ||
835 | |||
836 | break; | ||
837 | case HIF_DEVICE_GET_IRQ_PROC_MODE: | ||
838 | *((HIF_DEVICE_IRQ_PROCESSING_MODE *)config) = HIF_DEVICE_IRQ_SYNC_ONLY; | ||
839 | break; | ||
840 | case HIF_CONFIGURE_QUERY_SCATTER_REQUEST_SUPPORT: | ||
841 | if (!device->scatter_enabled) { | ||
842 | return A_ENOTSUP; | ||
843 | } | ||
844 | status = SetupHIFScatterSupport(device, (struct hif_device_scatter_support_info *)config); | ||
845 | if (status) { | ||
846 | device->scatter_enabled = false; | ||
847 | } | ||
848 | break; | ||
849 | case HIF_DEVICE_GET_OS_DEVICE: | ||
850 | /* pass back a pointer to the SDIO function's "dev" struct */ | ||
851 | ((struct hif_device_os_device_info *)config)->pOSDevice = &device->func->dev; | ||
852 | break; | ||
853 | case HIF_DEVICE_POWER_STATE_CHANGE: | ||
854 | status = PowerStateChangeNotify(device, *(HIF_DEVICE_POWER_CHANGE_TYPE *)config); | ||
855 | break; | ||
856 | default: | ||
857 | AR_DEBUG_PRINTF(ATH_DEBUG_WARN, | ||
858 | ("AR6000: Unsupported configuration opcode: %d\n", opcode)); | ||
859 | status = A_ERROR; | ||
860 | } | ||
861 | |||
862 | return status; | ||
863 | } | ||
864 | |||
865 | void | ||
866 | HIFShutDownDevice(struct hif_device *device) | ||
867 | { | ||
868 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +HIFShutDownDevice\n")); | ||
869 | if (device != NULL) { | ||
870 | AR_DEBUG_ASSERT(device->func != NULL); | ||
871 | } else { | ||
872 | /* since we are unloading the driver anyways, reset all cards in case the SDIO card | ||
873 | * is externally powered and we are unloading the SDIO stack. This avoids the problem when | ||
874 | * the SDIO stack is reloaded and attempts are made to re-enumerate a card that is already | ||
875 | * enumerated */ | ||
876 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: HIFShutDownDevice, resetting\n")); | ||
877 | ResetAllCards(); | ||
878 | |||
879 | /* Unregister with bus driver core */ | ||
880 | if (registered) { | ||
881 | registered = 0; | ||
882 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, | ||
883 | ("AR6000: Unregistering with the bus driver\n")); | ||
884 | sdio_unregister_driver(&ath6kl_hifdev_driver); | ||
885 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, | ||
886 | ("AR6000: Unregistered\n")); | ||
887 | } | ||
888 | } | ||
889 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: -HIFShutDownDevice\n")); | ||
890 | } | ||
891 | |||
892 | static void | ||
893 | hifIRQHandler(struct sdio_func *func) | ||
894 | { | ||
895 | int status; | ||
896 | struct hif_device *device; | ||
897 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +hifIRQHandler\n")); | ||
898 | |||
899 | device = ath6kl_get_hifdev(func); | ||
900 | atomic_set(&device->irqHandling, 1); | ||
901 | /* release the host during ints so we can pick it back up when we process cmds */ | ||
902 | sdio_release_host(device->func); | ||
903 | status = device->htcCallbacks.dsrHandler(device->htcCallbacks.context); | ||
904 | sdio_claim_host(device->func); | ||
905 | atomic_set(&device->irqHandling, 0); | ||
906 | AR_DEBUG_ASSERT(status == 0 || status == A_ECANCELED); | ||
907 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: -hifIRQHandler\n")); | ||
908 | } | ||
909 | |||
910 | /* handle HTC startup via thread*/ | ||
911 | static int startup_task(void *param) | ||
912 | { | ||
913 | struct hif_device *device; | ||
914 | |||
915 | device = (struct hif_device *)param; | ||
916 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: call HTC from startup_task\n")); | ||
917 | /* start up inform DRV layer */ | ||
918 | if ((osdrvCallbacks.deviceInsertedHandler(osdrvCallbacks.context,device)) != 0) { | ||
919 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Device rejected\n")); | ||
920 | } | ||
921 | return 0; | ||
922 | } | ||
923 | |||
924 | #if defined(CONFIG_PM) | ||
925 | static int enable_task(void *param) | ||
926 | { | ||
927 | struct hif_device *device; | ||
928 | device = (struct hif_device *)param; | ||
929 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: call from resume_task\n")); | ||
930 | |||
931 | /* start up inform DRV layer */ | ||
932 | if (device && | ||
933 | device->claimedContext && | ||
934 | osdrvCallbacks.devicePowerChangeHandler && | ||
935 | osdrvCallbacks.devicePowerChangeHandler(device->claimedContext, HIF_DEVICE_POWER_UP) != 0) | ||
936 | { | ||
937 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: Device rejected\n")); | ||
938 | } | ||
939 | |||
940 | return 0; | ||
941 | } | ||
942 | #endif | ||
943 | |||
944 | void | ||
945 | HIFAckInterrupt(struct hif_device *device) | ||
946 | { | ||
947 | AR_DEBUG_ASSERT(device != NULL); | ||
948 | |||
949 | /* Acknowledge our function IRQ */ | ||
950 | } | ||
951 | |||
952 | void | ||
953 | HIFUnMaskInterrupt(struct hif_device *device) | ||
954 | { | ||
955 | int ret; | ||
956 | |||
957 | AR_DEBUG_ASSERT(device != NULL); | ||
958 | AR_DEBUG_ASSERT(device->func != NULL); | ||
959 | |||
960 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: HIFUnMaskInterrupt\n")); | ||
961 | |||
962 | /* Register the IRQ Handler */ | ||
963 | sdio_claim_host(device->func); | ||
964 | ret = sdio_claim_irq(device->func, hifIRQHandler); | ||
965 | sdio_release_host(device->func); | ||
966 | AR_DEBUG_ASSERT(ret == 0); | ||
967 | } | ||
968 | |||
969 | void HIFMaskInterrupt(struct hif_device *device) | ||
970 | { | ||
971 | int ret; | ||
972 | AR_DEBUG_ASSERT(device != NULL); | ||
973 | AR_DEBUG_ASSERT(device->func != NULL); | ||
974 | |||
975 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: HIFMaskInterrupt\n")); | ||
976 | |||
977 | /* Mask our function IRQ */ | ||
978 | sdio_claim_host(device->func); | ||
979 | while (atomic_read(&device->irqHandling)) { | ||
980 | sdio_release_host(device->func); | ||
981 | schedule_timeout(HZ/10); | ||
982 | sdio_claim_host(device->func); | ||
983 | } | ||
984 | ret = sdio_release_irq(device->func); | ||
985 | sdio_release_host(device->func); | ||
986 | AR_DEBUG_ASSERT(ret == 0); | ||
987 | } | ||
988 | |||
989 | BUS_REQUEST *hifAllocateBusRequest(struct hif_device *device) | ||
990 | { | ||
991 | BUS_REQUEST *busrequest; | ||
992 | unsigned long flag; | ||
993 | |||
994 | /* Acquire lock */ | ||
995 | spin_lock_irqsave(&device->lock, flag); | ||
996 | |||
997 | /* Remove first in list */ | ||
998 | if((busrequest = device->s_busRequestFreeQueue) != NULL) | ||
999 | { | ||
1000 | device->s_busRequestFreeQueue = busrequest->next; | ||
1001 | } | ||
1002 | /* Release lock */ | ||
1003 | spin_unlock_irqrestore(&device->lock, flag); | ||
1004 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: hifAllocateBusRequest: 0x%p\n", busrequest)); | ||
1005 | return busrequest; | ||
1006 | } | ||
1007 | |||
1008 | void | ||
1009 | hifFreeBusRequest(struct hif_device *device, BUS_REQUEST *busrequest) | ||
1010 | { | ||
1011 | unsigned long flag; | ||
1012 | |||
1013 | AR_DEBUG_ASSERT(busrequest != NULL); | ||
1014 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: hifFreeBusRequest: 0x%p\n", busrequest)); | ||
1015 | /* Acquire lock */ | ||
1016 | spin_lock_irqsave(&device->lock, flag); | ||
1017 | |||
1018 | |||
1019 | /* Insert first in list */ | ||
1020 | busrequest->next = device->s_busRequestFreeQueue; | ||
1021 | busrequest->inusenext = NULL; | ||
1022 | device->s_busRequestFreeQueue = busrequest; | ||
1023 | |||
1024 | /* Release lock */ | ||
1025 | spin_unlock_irqrestore(&device->lock, flag); | ||
1026 | } | ||
1027 | |||
1028 | static int hifDisableFunc(struct hif_device *device, struct sdio_func *func) | ||
1029 | { | ||
1030 | int ret; | ||
1031 | int status = 0; | ||
1032 | |||
1033 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +hifDisableFunc\n")); | ||
1034 | device = ath6kl_get_hifdev(func); | ||
1035 | if (!IS_ERR(device->async_task)) { | ||
1036 | init_completion(&device->async_completion); | ||
1037 | device->async_shutdown = 1; | ||
1038 | up(&device->sem_async); | ||
1039 | wait_for_completion(&device->async_completion); | ||
1040 | device->async_task = NULL; | ||
1041 | } | ||
1042 | /* Disable the card */ | ||
1043 | sdio_claim_host(device->func); | ||
1044 | ret = sdio_disable_func(device->func); | ||
1045 | if (ret) { | ||
1046 | status = A_ERROR; | ||
1047 | } | ||
1048 | |||
1049 | if (reset_sdio_on_unload) { | ||
1050 | /* reset the SDIO interface. This is useful in automated testing where the card | ||
1051 | * does not need to be removed at the end of the test. It is expected that the user will | ||
1052 | * also unload/reload the host controller driver to force the bus driver to re-enumerate the slot */ | ||
1053 | AR_DEBUG_PRINTF(ATH_DEBUG_WARN, ("AR6000: reseting SDIO card back to uninitialized state \n")); | ||
1054 | |||
1055 | /* NOTE : sdio_f0_writeb() cannot be used here, that API only allows access | ||
1056 | * to undefined registers in the range of: 0xF0-0xFF */ | ||
1057 | |||
1058 | ret = Func0_CMD52WriteByte(device->func->card, SDIO_CCCR_ABORT, (1 << 3)); | ||
1059 | if (ret) { | ||
1060 | status = A_ERROR; | ||
1061 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("AR6000: reset failed : %d \n",ret)); | ||
1062 | } | ||
1063 | } | ||
1064 | |||
1065 | sdio_release_host(device->func); | ||
1066 | |||
1067 | if (status == 0) { | ||
1068 | device->is_disabled = true; | ||
1069 | } | ||
1070 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: -hifDisableFunc\n")); | ||
1071 | |||
1072 | return status; | ||
1073 | } | ||
1074 | |||
1075 | static int hifEnableFunc(struct hif_device *device, struct sdio_func *func) | ||
1076 | { | ||
1077 | struct task_struct* pTask; | ||
1078 | const char *taskName = NULL; | ||
1079 | int (*taskFunc)(void *) = NULL; | ||
1080 | int ret = 0; | ||
1081 | |||
1082 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: +hifEnableFunc\n")); | ||
1083 | device = ath6kl_get_hifdev(func); | ||
1084 | |||
1085 | if (device->is_disabled) { | ||
1086 | /* enable the SDIO function */ | ||
1087 | sdio_claim_host(func); | ||
1088 | |||
1089 | if ((device->id->device & MANUFACTURER_ID_AR6K_BASE_MASK) >= MANUFACTURER_ID_AR6003_BASE) { | ||
1090 | /* enable 4-bit ASYNC interrupt on AR6003 or later devices */ | ||
1091 | ret = Func0_CMD52WriteByte(func->card, CCCR_SDIO_IRQ_MODE_REG, SDIO_IRQ_MODE_ASYNC_4BIT_IRQ); | ||
1092 | if (ret) { | ||
1093 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("AR6000: failed to enable 4-bit ASYNC IRQ mode %d \n",ret)); | ||
1094 | sdio_release_host(func); | ||
1095 | return ret; | ||
1096 | } | ||
1097 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: 4-bit ASYNC IRQ mode enabled\n")); | ||
1098 | } | ||
1099 | /* give us some time to enable, in ms */ | ||
1100 | func->enable_timeout = 100; | ||
1101 | ret = sdio_enable_func(func); | ||
1102 | if (ret) { | ||
1103 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: %s(), Unable to enable AR6K: 0x%X\n", | ||
1104 | __FUNCTION__, ret)); | ||
1105 | sdio_release_host(func); | ||
1106 | return ret; | ||
1107 | } | ||
1108 | ret = sdio_set_block_size(func, HIF_MBOX_BLOCK_SIZE); | ||
1109 | sdio_release_host(func); | ||
1110 | if (ret) { | ||
1111 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: %s(), Unable to set block size 0x%x AR6K: 0x%X\n", | ||
1112 | __FUNCTION__, HIF_MBOX_BLOCK_SIZE, ret)); | ||
1113 | return ret; | ||
1114 | } | ||
1115 | device->is_disabled = false; | ||
1116 | /* create async I/O thread */ | ||
1117 | if (!device->async_task) { | ||
1118 | device->async_shutdown = 0; | ||
1119 | device->async_task = kthread_create(async_task, | ||
1120 | (void *)device, | ||
1121 | "AR6K Async"); | ||
1122 | if (IS_ERR(device->async_task)) { | ||
1123 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: %s(), to create async task\n", __FUNCTION__)); | ||
1124 | return -ENOMEM; | ||
1125 | } | ||
1126 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: start async task\n")); | ||
1127 | wake_up_process(device->async_task ); | ||
1128 | } | ||
1129 | } | ||
1130 | |||
1131 | if (!device->claimedContext) { | ||
1132 | taskFunc = startup_task; | ||
1133 | taskName = "AR6K startup"; | ||
1134 | ret = 0; | ||
1135 | #if defined(CONFIG_PM) | ||
1136 | } else { | ||
1137 | taskFunc = enable_task; | ||
1138 | taskName = "AR6K enable"; | ||
1139 | ret = -ENOMEM; | ||
1140 | #endif /* CONFIG_PM */ | ||
1141 | } | ||
1142 | /* create resume thread */ | ||
1143 | pTask = kthread_create(taskFunc, (void *)device, taskName); | ||
1144 | if (IS_ERR(pTask)) { | ||
1145 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("AR6000: %s(), to create enabel task\n", __FUNCTION__)); | ||
1146 | return -ENOMEM; | ||
1147 | } | ||
1148 | wake_up_process(pTask); | ||
1149 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: -hifEnableFunc\n")); | ||
1150 | |||
1151 | /* task will call the enable func, indicate pending */ | ||
1152 | return ret; | ||
1153 | } | ||
1154 | |||
1155 | /* | ||
1156 | * This should be moved to AR6K HTC layer. | ||
1157 | */ | ||
1158 | int hifWaitForPendingRecv(struct hif_device *device) | ||
1159 | { | ||
1160 | s32 cnt = 10; | ||
1161 | u8 host_int_status; | ||
1162 | int status = 0; | ||
1163 | |||
1164 | do { | ||
1165 | while (atomic_read(&device->irqHandling)) { | ||
1166 | /* wait until irq handler finished all the jobs */ | ||
1167 | schedule_timeout(HZ/10); | ||
1168 | } | ||
1169 | /* check if there is any pending irq due to force done */ | ||
1170 | host_int_status = 0; | ||
1171 | status = HIFReadWrite(device, HOST_INT_STATUS_ADDRESS, | ||
1172 | (u8 *)&host_int_status, sizeof(host_int_status), | ||
1173 | HIF_RD_SYNC_BYTE_INC, NULL); | ||
1174 | host_int_status = !status ? (host_int_status & (1 << 0)) : 0; | ||
1175 | if (host_int_status) { | ||
1176 | schedule(); /* schedule for next dsrHandler */ | ||
1177 | } | ||
1178 | } while (host_int_status && --cnt > 0); | ||
1179 | |||
1180 | if (host_int_status && cnt == 0) { | ||
1181 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, | ||
1182 | ("AR6000: %s(), Unable clear up pending IRQ before the system suspended\n", __FUNCTION__)); | ||
1183 | } | ||
1184 | |||
1185 | return 0; | ||
1186 | } | ||
1187 | |||
1188 | static void | ||
1189 | delHifDevice(struct hif_device * device) | ||
1190 | { | ||
1191 | AR_DEBUG_ASSERT(device!= NULL); | ||
1192 | AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("AR6000: delHifDevice; 0x%p\n", device)); | ||
1193 | kfree(device->dma_buffer); | ||
1194 | kfree(device); | ||
1195 | } | ||
1196 | |||
1197 | static void ResetAllCards(void) | ||
1198 | { | ||
1199 | } | ||
1200 | |||
1201 | void HIFClaimDevice(struct hif_device *device, void *context) | ||
1202 | { | ||
1203 | device->claimedContext = context; | ||
1204 | } | ||
1205 | |||
1206 | void HIFReleaseDevice(struct hif_device *device) | ||
1207 | { | ||
1208 | device->claimedContext = NULL; | ||
1209 | } | ||
1210 | |||
1211 | int HIFAttachHTC(struct hif_device *device, HTC_CALLBACKS *callbacks) | ||
1212 | { | ||
1213 | if (device->htcCallbacks.context != NULL) { | ||
1214 | /* already in use! */ | ||
1215 | return A_ERROR; | ||
1216 | } | ||
1217 | device->htcCallbacks = *callbacks; | ||
1218 | return 0; | ||
1219 | } | ||
1220 | |||
1221 | void HIFDetachHTC(struct hif_device *device) | ||
1222 | { | ||
1223 | A_MEMZERO(&device->htcCallbacks,sizeof(device->htcCallbacks)); | ||
1224 | } | ||
1225 | |||
1226 | #define SDIO_SET_CMD52_ARG(arg,rw,func,raw,address,writedata) \ | ||
1227 | (arg) = (((rw) & 1) << 31) | \ | ||
1228 | (((func) & 0x7) << 28) | \ | ||
1229 | (((raw) & 1) << 27) | \ | ||
1230 | (1 << 26) | \ | ||
1231 | (((address) & 0x1FFFF) << 9) | \ | ||
1232 | (1 << 8) | \ | ||
1233 | ((writedata) & 0xFF) | ||
1234 | |||
1235 | #define SDIO_SET_CMD52_READ_ARG(arg,func,address) \ | ||
1236 | SDIO_SET_CMD52_ARG(arg,0,(func),0,address,0x00) | ||
1237 | #define SDIO_SET_CMD52_WRITE_ARG(arg,func,address,value) \ | ||
1238 | SDIO_SET_CMD52_ARG(arg,1,(func),0,address,value) | ||
1239 | |||
1240 | static int Func0_CMD52WriteByte(struct mmc_card *card, unsigned int address, unsigned char byte) | ||
1241 | { | ||
1242 | struct mmc_command ioCmd; | ||
1243 | unsigned long arg; | ||
1244 | |||
1245 | memset(&ioCmd,0,sizeof(ioCmd)); | ||
1246 | SDIO_SET_CMD52_WRITE_ARG(arg,0,address,byte); | ||
1247 | ioCmd.opcode = SD_IO_RW_DIRECT; | ||
1248 | ioCmd.arg = arg; | ||
1249 | ioCmd.flags = MMC_RSP_R5 | MMC_CMD_AC; | ||
1250 | |||
1251 | return mmc_wait_for_cmd(card->host, &ioCmd, 0); | ||
1252 | } | ||
1253 | |||
1254 | static int Func0_CMD52ReadByte(struct mmc_card *card, unsigned int address, unsigned char *byte) | ||
1255 | { | ||
1256 | struct mmc_command ioCmd; | ||
1257 | unsigned long arg; | ||
1258 | s32 err; | ||
1259 | |||
1260 | memset(&ioCmd,0,sizeof(ioCmd)); | ||
1261 | SDIO_SET_CMD52_READ_ARG(arg,0,address); | ||
1262 | ioCmd.opcode = SD_IO_RW_DIRECT; | ||
1263 | ioCmd.arg = arg; | ||
1264 | ioCmd.flags = MMC_RSP_R5 | MMC_CMD_AC; | ||
1265 | |||
1266 | err = mmc_wait_for_cmd(card->host, &ioCmd, 0); | ||
1267 | |||
1268 | if ((!err) && (byte)) { | ||
1269 | *byte = ioCmd.resp[0] & 0xFF; | ||
1270 | } | ||
1271 | |||
1272 | return err; | ||
1273 | } | ||
diff --git a/drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c b/drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c new file mode 100644 index 00000000000..7516d913dab --- /dev/null +++ b/drivers/staging/ath6kl/hif/sdio/linux_sdio/src/hif_scatter.c | |||
@@ -0,0 +1,393 @@ | |||
1 | //------------------------------------------------------------------------------ | ||
2 | // Copyright (c) 2009-2010 Atheros Corporation. All rights reserved. | ||
3 | // | ||
4 | // | ||
5 | // Permission to use, copy, modify, and/or distribute this software for any | ||
6 | // purpose with or without fee is hereby granted, provided that the above | ||
7 | // copyright notice and this permission notice appear in all copies. | ||
8 | // | ||
9 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
10 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
11 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
12 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
13 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
14 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
15 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
16 | // | ||
17 | // | ||
18 | //------------------------------------------------------------------------------ | ||
19 | //============================================================================== | ||
20 | // HIF scatter implementation | ||
21 | // | ||
22 | // Author(s): ="Atheros" | ||
23 | //============================================================================== | ||
24 | |||
25 | #include <linux/mmc/card.h> | ||
26 | #include <linux/mmc/host.h> | ||
27 | #include <linux/mmc/sdio_func.h> | ||
28 | #include <linux/mmc/sdio_ids.h> | ||
29 | #include <linux/mmc/sdio.h> | ||
30 | #include <linux/kthread.h> | ||
31 | #include "hif_internal.h" | ||
32 | #define ATH_MODULE_NAME hif | ||
33 | #include "a_debug.h" | ||
34 | |||
35 | #ifdef HIF_LINUX_MMC_SCATTER_SUPPORT | ||
36 | |||
37 | #define _CMD53_ARG_READ 0 | ||
38 | #define _CMD53_ARG_WRITE 1 | ||
39 | #define _CMD53_ARG_BLOCK_BASIS 1 | ||
40 | #define _CMD53_ARG_FIXED_ADDRESS 0 | ||
41 | #define _CMD53_ARG_INCR_ADDRESS 1 | ||
42 | |||
43 | #define SDIO_SET_CMD53_ARG(arg,rw,func,mode,opcode,address,bytes_blocks) \ | ||
44 | (arg) = (((rw) & 1) << 31) | \ | ||
45 | (((func) & 0x7) << 28) | \ | ||
46 | (((mode) & 1) << 27) | \ | ||
47 | (((opcode) & 1) << 26) | \ | ||
48 | (((address) & 0x1FFFF) << 9) | \ | ||
49 | ((bytes_blocks) & 0x1FF) | ||
50 | |||
51 | static void FreeScatterReq(struct hif_device *device, struct hif_scatter_req *pReq) | ||
52 | { | ||
53 | unsigned long flag; | ||
54 | |||
55 | spin_lock_irqsave(&device->lock, flag); | ||
56 | |||
57 | DL_ListInsertTail(&device->ScatterReqHead, &pReq->ListLink); | ||
58 | |||
59 | spin_unlock_irqrestore(&device->lock, flag); | ||
60 | |||
61 | } | ||
62 | |||
63 | static struct hif_scatter_req *AllocScatterReq(struct hif_device *device) | ||
64 | { | ||
65 | struct dl_list *pItem; | ||
66 | unsigned long flag; | ||
67 | |||
68 | spin_lock_irqsave(&device->lock, flag); | ||
69 | |||
70 | pItem = DL_ListRemoveItemFromHead(&device->ScatterReqHead); | ||
71 | |||
72 | spin_unlock_irqrestore(&device->lock, flag); | ||
73 | |||
74 | if (pItem != NULL) { | ||
75 | return A_CONTAINING_STRUCT(pItem, struct hif_scatter_req, ListLink); | ||
76 | } | ||
77 | |||
78 | return NULL; | ||
79 | } | ||
80 | |||
81 | /* called by async task to perform the operation synchronously using direct MMC APIs */ | ||
82 | int DoHifReadWriteScatter(struct hif_device *device, BUS_REQUEST *busrequest) | ||
83 | { | ||
84 | int i; | ||
85 | u8 rw; | ||
86 | u8 opcode; | ||
87 | struct mmc_request mmcreq; | ||
88 | struct mmc_command cmd; | ||
89 | struct mmc_data data; | ||
90 | struct hif_scatter_req_priv *pReqPriv; | ||
91 | struct hif_scatter_req *pReq; | ||
92 | int status = 0; | ||
93 | struct scatterlist *pSg; | ||
94 | |||
95 | pReqPriv = busrequest->pScatterReq; | ||
96 | |||
97 | A_ASSERT(pReqPriv != NULL); | ||
98 | |||
99 | pReq = pReqPriv->pHifScatterReq; | ||
100 | |||
101 | memset(&mmcreq, 0, sizeof(struct mmc_request)); | ||
102 | memset(&cmd, 0, sizeof(struct mmc_command)); | ||
103 | memset(&data, 0, sizeof(struct mmc_data)); | ||
104 | |||
105 | data.blksz = HIF_MBOX_BLOCK_SIZE; | ||
106 | data.blocks = pReq->TotalLength / HIF_MBOX_BLOCK_SIZE; | ||
107 | |||
108 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: (%s) Address: 0x%X, (BlockLen: %d, BlockCount: %d) , (tot:%d,sg:%d)\n", | ||
109 | (pReq->Request & HIF_WRITE) ? "WRITE":"READ", pReq->Address, data.blksz, data.blocks, | ||
110 | pReq->TotalLength,pReq->ValidScatterEntries)); | ||
111 | |||
112 | if (pReq->Request & HIF_WRITE) { | ||
113 | rw = _CMD53_ARG_WRITE; | ||
114 | data.flags = MMC_DATA_WRITE; | ||
115 | } else { | ||
116 | rw = _CMD53_ARG_READ; | ||
117 | data.flags = MMC_DATA_READ; | ||
118 | } | ||
119 | |||
120 | if (pReq->Request & HIF_FIXED_ADDRESS) { | ||
121 | opcode = _CMD53_ARG_FIXED_ADDRESS; | ||
122 | } else { | ||
123 | opcode = _CMD53_ARG_INCR_ADDRESS; | ||
124 | } | ||
125 | |||
126 | /* fill SG entries */ | ||
127 | pSg = pReqPriv->sgentries; | ||
128 | sg_init_table(pSg, pReq->ValidScatterEntries); | ||
129 | |||
130 | /* assemble SG list */ | ||
131 | for (i = 0 ; i < pReq->ValidScatterEntries ; i++, pSg++) { | ||
132 | /* setup each sg entry */ | ||
133 | if ((unsigned long)pReq->ScatterList[i].pBuffer & 0x3) { | ||
134 | /* note some scatter engines can handle unaligned buffers, print this | ||
135 | * as informational only */ | ||
136 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, | ||
137 | ("HIF: (%s) Scatter Buffer is unaligned 0x%lx\n", | ||
138 | pReq->Request & HIF_WRITE ? "WRITE":"READ", | ||
139 | (unsigned long)pReq->ScatterList[i].pBuffer)); | ||
140 | } | ||
141 | |||
142 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, (" %d: Addr:0x%lX, Len:%d \n", | ||
143 | i,(unsigned long)pReq->ScatterList[i].pBuffer,pReq->ScatterList[i].Length)); | ||
144 | |||
145 | sg_set_buf(pSg, pReq->ScatterList[i].pBuffer, pReq->ScatterList[i].Length); | ||
146 | } | ||
147 | /* set scatter-gather table for request */ | ||
148 | data.sg = pReqPriv->sgentries; | ||
149 | data.sg_len = pReq->ValidScatterEntries; | ||
150 | /* set command argument */ | ||
151 | SDIO_SET_CMD53_ARG(cmd.arg, | ||
152 | rw, | ||
153 | device->func->num, | ||
154 | _CMD53_ARG_BLOCK_BASIS, | ||
155 | opcode, | ||
156 | pReq->Address, | ||
157 | data.blocks); | ||
158 | |||
159 | cmd.opcode = SD_IO_RW_EXTENDED; | ||
160 | cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; | ||
161 | |||
162 | mmcreq.cmd = &cmd; | ||
163 | mmcreq.data = &data; | ||
164 | |||
165 | mmc_set_data_timeout(&data, device->func->card); | ||
166 | /* synchronous call to process request */ | ||
167 | mmc_wait_for_req(device->func->card->host, &mmcreq); | ||
168 | |||
169 | if (cmd.error) { | ||
170 | status = A_ERROR; | ||
171 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("HIF-SCATTER: cmd error: %d \n",cmd.error)); | ||
172 | } | ||
173 | |||
174 | if (data.error) { | ||
175 | status = A_ERROR; | ||
176 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("HIF-SCATTER: data error: %d \n",data.error)); | ||
177 | } | ||
178 | |||
179 | if (status) { | ||
180 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("HIF-SCATTER: FAILED!!! (%s) Address: 0x%X, Block mode (BlockLen: %d, BlockCount: %d)\n", | ||
181 | (pReq->Request & HIF_WRITE) ? "WRITE":"READ",pReq->Address, data.blksz, data.blocks)); | ||
182 | } | ||
183 | |||
184 | /* set completion status, fail or success */ | ||
185 | pReq->CompletionStatus = status; | ||
186 | |||
187 | if (pReq->Request & HIF_ASYNCHRONOUS) { | ||
188 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: async_task completion routine req: 0x%lX (%d)\n",(unsigned long)busrequest, status)); | ||
189 | /* complete the request */ | ||
190 | A_ASSERT(pReq->CompletionRoutine != NULL); | ||
191 | pReq->CompletionRoutine(pReq); | ||
192 | } else { | ||
193 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER async_task upping busrequest : 0x%lX (%d)\n", (unsigned long)busrequest,status)); | ||
194 | /* signal wait */ | ||
195 | up(&busrequest->sem_req); | ||
196 | } | ||
197 | |||
198 | return status; | ||
199 | } | ||
200 | |||
201 | /* callback to issue a read-write scatter request */ | ||
202 | static int HifReadWriteScatter(struct hif_device *device, struct hif_scatter_req *pReq) | ||
203 | { | ||
204 | int status = A_EINVAL; | ||
205 | u32 request = pReq->Request; | ||
206 | struct hif_scatter_req_priv *pReqPriv = (struct hif_scatter_req_priv *)pReq->HIFPrivate[0]; | ||
207 | |||
208 | do { | ||
209 | |||
210 | A_ASSERT(pReqPriv != NULL); | ||
211 | |||
212 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: total len: %d Scatter Entries: %d\n", | ||
213 | pReq->TotalLength, pReq->ValidScatterEntries)); | ||
214 | |||
215 | if (!(request & HIF_EXTENDED_IO)) { | ||
216 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, | ||
217 | ("HIF-SCATTER: Invalid command type: 0x%08x\n", request)); | ||
218 | break; | ||
219 | } | ||
220 | |||
221 | if (!(request & (HIF_SYNCHRONOUS | HIF_ASYNCHRONOUS))) { | ||
222 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, | ||
223 | ("HIF-SCATTER: Invalid execution mode: 0x%08x\n", request)); | ||
224 | break; | ||
225 | } | ||
226 | |||
227 | if (!(request & HIF_BLOCK_BASIS)) { | ||
228 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, | ||
229 | ("HIF-SCATTER: Invalid data mode: 0x%08x\n", request)); | ||
230 | break; | ||
231 | } | ||
232 | |||
233 | if (pReq->TotalLength > MAX_SCATTER_REQ_TRANSFER_SIZE) { | ||
234 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, | ||
235 | ("HIF-SCATTER: Invalid length: %d \n", pReq->TotalLength)); | ||
236 | break; | ||
237 | } | ||
238 | |||
239 | if (pReq->TotalLength == 0) { | ||
240 | A_ASSERT(false); | ||
241 | break; | ||
242 | } | ||
243 | |||
244 | /* add bus request to the async list for the async I/O thread to process */ | ||
245 | AddToAsyncList(device, pReqPriv->busrequest); | ||
246 | |||
247 | if (request & HIF_SYNCHRONOUS) { | ||
248 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: queued sync req: 0x%lX\n", (unsigned long)pReqPriv->busrequest)); | ||
249 | /* signal thread and wait */ | ||
250 | up(&device->sem_async); | ||
251 | if (down_interruptible(&pReqPriv->busrequest->sem_req) != 0) { | ||
252 | AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,("HIF-SCATTER: interrupted! \n")); | ||
253 | /* interrupted, exit */ | ||
254 | status = A_ERROR; | ||
255 | break; | ||
256 | } else { | ||
257 | status = pReq->CompletionStatus; | ||
258 | } | ||
259 | } else { | ||
260 | AR_DEBUG_PRINTF(ATH_DEBUG_SCATTER, ("HIF-SCATTER: queued async req: 0x%lX\n", (unsigned long)pReqPriv->busrequest)); | ||
261 | /* wake thread, it will process and then take care of the async callback */ | ||
262 | up(&device->sem_async); | ||
263 | status = 0; | ||
264 | } | ||
265 | |||
266 | } while (false); | ||
267 | |||
268 | if (status && (request & HIF_ASYNCHRONOUS)) { | ||
269 | pReq->CompletionStatus = status; | ||
270 | pReq->CompletionRoutine(pReq); | ||
271 | status = 0; | ||
272 | } | ||
273 | |||
274 | return status; | ||
275 | } | ||
276 | |||
277 | /* setup of HIF scatter resources */ | ||
278 | int SetupHIFScatterSupport(struct hif_device *device, struct hif_device_scatter_support_info *pInfo) | ||
279 | { | ||
280 | int status = A_ERROR; | ||
281 | int i; | ||
282 | struct hif_scatter_req_priv *pReqPriv; | ||
283 | BUS_REQUEST *busrequest; | ||
284 | |||
285 | do { | ||
286 | |||
287 | /* check if host supports scatter requests and it meets our requirements */ | ||
288 | if (device->func->card->host->max_segs < MAX_SCATTER_ENTRIES_PER_REQ) { | ||
289 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HIF-SCATTER : host only supports scatter of : %d entries, need: %d \n", | ||
290 | device->func->card->host->max_segs, MAX_SCATTER_ENTRIES_PER_REQ)); | ||
291 | status = A_ENOTSUP; | ||
292 | break; | ||
293 | } | ||
294 | |||
295 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("HIF-SCATTER Enabled: max scatter req : %d entries: %d \n", | ||
296 | MAX_SCATTER_REQUESTS, MAX_SCATTER_ENTRIES_PER_REQ)); | ||
297 | |||
298 | for (i = 0; i < MAX_SCATTER_REQUESTS; i++) { | ||
299 | /* allocate the private request blob */ | ||
300 | pReqPriv = (struct hif_scatter_req_priv *)A_MALLOC(sizeof(struct hif_scatter_req_priv)); | ||
301 | if (NULL == pReqPriv) { | ||
302 | break; | ||
303 | } | ||
304 | A_MEMZERO(pReqPriv, sizeof(struct hif_scatter_req_priv)); | ||
305 | /* save the device instance*/ | ||
306 | pReqPriv->device = device; | ||
307 | /* allocate the scatter request */ | ||
308 | pReqPriv->pHifScatterReq = (struct hif_scatter_req *)A_MALLOC(sizeof(struct hif_scatter_req) + | ||
309 | (MAX_SCATTER_ENTRIES_PER_REQ - 1) * (sizeof(struct hif_scatter_item))); | ||
310 | |||
311 | if (NULL == pReqPriv->pHifScatterReq) { | ||
312 | kfree(pReqPriv); | ||
313 | break; | ||
314 | } | ||
315 | /* just zero the main part of the scatter request */ | ||
316 | A_MEMZERO(pReqPriv->pHifScatterReq, sizeof(struct hif_scatter_req)); | ||
317 | /* back pointer to the private struct */ | ||
318 | pReqPriv->pHifScatterReq->HIFPrivate[0] = pReqPriv; | ||
319 | /* allocate a bus request for this scatter request */ | ||
320 | busrequest = hifAllocateBusRequest(device); | ||
321 | if (NULL == busrequest) { | ||
322 | kfree(pReqPriv->pHifScatterReq); | ||
323 | kfree(pReqPriv); | ||
324 | break; | ||
325 | } | ||
326 | /* assign the scatter request to this bus request */ | ||
327 | busrequest->pScatterReq = pReqPriv; | ||
328 | /* point back to the request */ | ||
329 | pReqPriv->busrequest = busrequest; | ||
330 | /* add it to the scatter pool */ | ||
331 | FreeScatterReq(device,pReqPriv->pHifScatterReq); | ||
332 | } | ||
333 | |||
334 | if (i != MAX_SCATTER_REQUESTS) { | ||
335 | status = A_NO_MEMORY; | ||
336 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("HIF-SCATTER : failed to alloc scatter resources !\n")); | ||
337 | break; | ||
338 | } | ||
339 | |||
340 | /* set scatter function pointers */ | ||
341 | pInfo->pAllocateReqFunc = AllocScatterReq; | ||
342 | pInfo->pFreeReqFunc = FreeScatterReq; | ||
343 | pInfo->pReadWriteScatterFunc = HifReadWriteScatter; | ||
344 | pInfo->MaxScatterEntries = MAX_SCATTER_ENTRIES_PER_REQ; | ||
345 | pInfo->MaxTransferSizePerScatterReq = MAX_SCATTER_REQ_TRANSFER_SIZE; | ||
346 | |||
347 | status = 0; | ||
348 | |||
349 | } while (false); | ||
350 | |||
351 | if (status) { | ||
352 | CleanupHIFScatterResources(device); | ||
353 | } | ||
354 | |||
355 | return status; | ||
356 | } | ||
357 | |||
358 | /* clean up scatter support */ | ||
359 | void CleanupHIFScatterResources(struct hif_device *device) | ||
360 | { | ||
361 | struct hif_scatter_req_priv *pReqPriv; | ||
362 | struct hif_scatter_req *pReq; | ||
363 | |||
364 | /* empty the free list */ | ||
365 | |||
366 | while (1) { | ||
367 | |||
368 | pReq = AllocScatterReq(device); | ||
369 | |||
370 | if (NULL == pReq) { | ||
371 | break; | ||
372 | } | ||
373 | |||
374 | pReqPriv = (struct hif_scatter_req_priv *)pReq->HIFPrivate[0]; | ||
375 | A_ASSERT(pReqPriv != NULL); | ||
376 | |||
377 | if (pReqPriv->busrequest != NULL) { | ||
378 | pReqPriv->busrequest->pScatterReq = NULL; | ||
379 | /* free bus request */ | ||
380 | hifFreeBusRequest(device, pReqPriv->busrequest); | ||
381 | pReqPriv->busrequest = NULL; | ||
382 | } | ||
383 | |||
384 | if (pReqPriv->pHifScatterReq != NULL) { | ||
385 | kfree(pReqPriv->pHifScatterReq); | ||
386 | pReqPriv->pHifScatterReq = NULL; | ||
387 | } | ||
388 | |||
389 | kfree(pReqPriv); | ||
390 | } | ||
391 | } | ||
392 | |||
393 | #endif // HIF_LINUX_MMC_SCATTER_SUPPORT | ||