]>
Commit | Line | Data |
---|---|---|
521a5b21 TP |
1 | /* |
2 | * This file is part of wl1271 | |
3 | * | |
4 | * Copyright (C) 2008-2010 Nokia Corporation | |
5 | * | |
6 | * Contact: Luciano Coelho <luciano.coelho@nokia.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License | |
10 | * version 2 as published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
20 | * 02110-1301 USA | |
21 | * | |
22 | */ | |
23 | ||
24 | #include <linux/module.h> | |
25 | #include <linux/platform_device.h> | |
521a5b21 | 26 | #include <linux/spi/spi.h> |
a390e85c | 27 | #include <linux/interrupt.h> |
521a5b21 | 28 | |
c31be25a | 29 | #include "wlcore.h" |
0f4e3122 | 30 | #include "debug.h" |
521a5b21 | 31 | #include "wl12xx_80211.h" |
00d20100 | 32 | #include "io.h" |
0da13da7 | 33 | #include "tx.h" |
521a5b21 | 34 | |
48a61477 SL |
35 | bool wl1271_set_block_size(struct wl1271 *wl) |
36 | { | |
37 | if (wl->if_ops->set_block_size) { | |
a390e85c | 38 | wl->if_ops->set_block_size(wl->dev, WL12XX_BUS_BLOCK_SIZE); |
48a61477 SL |
39 | return true; |
40 | } | |
41 | ||
42 | return false; | |
43 | } | |
44 | ||
dd5512eb | 45 | void wlcore_disable_interrupts(struct wl1271 *wl) |
54f7e503 | 46 | { |
a390e85c | 47 | disable_irq(wl->irq); |
54f7e503 | 48 | } |
dd5512eb | 49 | EXPORT_SYMBOL_GPL(wlcore_disable_interrupts); |
54f7e503 | 50 | |
b666bb7f IY |
51 | void wlcore_disable_interrupts_nosync(struct wl1271 *wl) |
52 | { | |
53 | disable_irq_nosync(wl->irq); | |
54 | } | |
55 | EXPORT_SYMBOL_GPL(wlcore_disable_interrupts_nosync); | |
56 | ||
dd5512eb | 57 | void wlcore_enable_interrupts(struct wl1271 *wl) |
54f7e503 | 58 | { |
a390e85c | 59 | enable_irq(wl->irq); |
54f7e503 | 60 | } |
dd5512eb | 61 | EXPORT_SYMBOL_GPL(wlcore_enable_interrupts); |
54f7e503 | 62 | |
c24ec83b IY |
63 | void wlcore_synchronize_interrupts(struct wl1271 *wl) |
64 | { | |
65 | synchronize_irq(wl->irq); | |
66 | } | |
67 | EXPORT_SYMBOL_GPL(wlcore_synchronize_interrupts); | |
68 | ||
25a43d78 LC |
69 | int wlcore_translate_addr(struct wl1271 *wl, int addr) |
70 | { | |
71 | struct wlcore_partition_set *part = &wl->curr_part; | |
72 | ||
73 | /* | |
74 | * To translate, first check to which window of addresses the | |
75 | * particular address belongs. Then subtract the starting address | |
76 | * of that window from the address. Then, add offset of the | |
77 | * translated region. | |
78 | * | |
79 | * The translated regions occur next to each other in physical device | |
80 | * memory, so just add the sizes of the preceding address regions to | |
81 | * get the offset to the new region. | |
82 | */ | |
83 | if ((addr >= part->mem.start) && | |
84 | (addr < part->mem.start + part->mem.size)) | |
85 | return addr - part->mem.start; | |
86 | else if ((addr >= part->reg.start) && | |
87 | (addr < part->reg.start + part->reg.size)) | |
88 | return addr - part->reg.start + part->mem.size; | |
89 | else if ((addr >= part->mem2.start) && | |
90 | (addr < part->mem2.start + part->mem2.size)) | |
91 | return addr - part->mem2.start + part->mem.size + | |
92 | part->reg.size; | |
93 | else if ((addr >= part->mem3.start) && | |
94 | (addr < part->mem3.start + part->mem3.size)) | |
95 | return addr - part->mem3.start + part->mem.size + | |
96 | part->reg.size + part->mem2.size; | |
97 | ||
98 | WARN(1, "HW address 0x%x out of range", addr); | |
99 | return 0; | |
100 | } | |
101 | EXPORT_SYMBOL_GPL(wlcore_translate_addr); | |
102 | ||
103 | /* Set the partitions to access the chip addresses | |
521a5b21 TP |
104 | * |
105 | * To simplify driver code, a fixed (virtual) memory map is defined for | |
106 | * register and memory addresses. Because in the chipset, in different stages | |
107 | * of operation, those addresses will move around, an address translation | |
108 | * mechanism is required. | |
109 | * | |
110 | * There are four partitions (three memory and one register partition), | |
111 | * which are mapped to two different areas of the hardware memory. | |
112 | * | |
113 | * Virtual address | |
114 | * space | |
115 | * | |
116 | * | | | |
117 | * ...+----+--> mem.start | |
118 | * Physical address ... | | | |
119 | * space ... | | [PART_0] | |
120 | * ... | | | |
121 | * 00000000 <--+----+... ...+----+--> mem.start + mem.size | |
122 | * | | ... | | | |
123 | * |MEM | ... | | | |
124 | * | | ... | | | |
125 | * mem.size <--+----+... | | {unused area) | |
126 | * | | ... | | | |
127 | * |REG | ... | | | |
128 | * mem.size | | ... | | | |
129 | * + <--+----+... ...+----+--> reg.start | |
130 | * reg.size | | ... | | | |
131 | * |MEM2| ... | | [PART_1] | |
132 | * | | ... | | | |
133 | * ...+----+--> reg.start + reg.size | |
134 | * | | | |
135 | * | |
136 | */ | |
b0f0ad39 IY |
137 | int wlcore_set_partition(struct wl1271 *wl, |
138 | const struct wlcore_partition_set *p) | |
521a5b21 | 139 | { |
b0f0ad39 IY |
140 | int ret; |
141 | ||
521a5b21 | 142 | /* copy partition info */ |
25a43d78 | 143 | memcpy(&wl->curr_part, p, sizeof(*p)); |
521a5b21 | 144 | |
25a43d78 | 145 | wl1271_debug(DEBUG_IO, "mem_start %08X mem_size %08X", |
521a5b21 | 146 | p->mem.start, p->mem.size); |
25a43d78 | 147 | wl1271_debug(DEBUG_IO, "reg_start %08X reg_size %08X", |
521a5b21 | 148 | p->reg.start, p->reg.size); |
25a43d78 | 149 | wl1271_debug(DEBUG_IO, "mem2_start %08X mem2_size %08X", |
521a5b21 | 150 | p->mem2.start, p->mem2.size); |
25a43d78 | 151 | wl1271_debug(DEBUG_IO, "mem3_start %08X mem3_size %08X", |
521a5b21 TP |
152 | p->mem3.start, p->mem3.size); |
153 | ||
b0f0ad39 IY |
154 | ret = wlcore_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start); |
155 | if (ret < 0) | |
156 | goto out; | |
157 | ||
158 | ret = wlcore_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size); | |
159 | if (ret < 0) | |
160 | goto out; | |
161 | ||
162 | ret = wlcore_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start); | |
163 | if (ret < 0) | |
164 | goto out; | |
165 | ||
166 | ret = wlcore_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size); | |
167 | if (ret < 0) | |
168 | goto out; | |
169 | ||
170 | ret = wlcore_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start); | |
171 | if (ret < 0) | |
172 | goto out; | |
173 | ||
174 | ret = wlcore_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size); | |
175 | if (ret < 0) | |
176 | goto out; | |
177 | ||
fb724ed5 EG |
178 | /* We don't need the size of the last partition, as it is |
179 | * automatically calculated based on the total memory size and | |
180 | * the sizes of the previous partitions. | |
181 | */ | |
b0f0ad39 | 182 | ret = wlcore_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start); |
3719c17e SP |
183 | if (ret < 0) |
184 | goto out; | |
185 | ||
b0f0ad39 IY |
186 | out: |
187 | return ret; | |
521a5b21 | 188 | } |
b0f0ad39 | 189 | EXPORT_SYMBOL_GPL(wlcore_set_partition); |
521a5b21 | 190 | |
9b280722 TP |
191 | void wl1271_io_reset(struct wl1271 *wl) |
192 | { | |
77d7d7a3 | 193 | if (wl->if_ops->reset) |
a390e85c | 194 | wl->if_ops->reset(wl->dev); |
9b280722 TP |
195 | } |
196 | ||
197 | void wl1271_io_init(struct wl1271 *wl) | |
198 | { | |
77d7d7a3 | 199 | if (wl->if_ops->init) |
a390e85c | 200 | wl->if_ops->init(wl->dev); |
9b280722 | 201 | } |