diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx/io.c')
-rw-r--r-- | drivers/net/wireless/wl12xx/io.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/drivers/net/wireless/wl12xx/io.c b/drivers/net/wireless/wl12xx/io.c new file mode 100644 index 00000000000..c2da66f4504 --- /dev/null +++ b/drivers/net/wireless/wl12xx/io.c | |||
@@ -0,0 +1,183 @@ | |||
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> | ||
26 | #include <linux/spi/spi.h> | ||
27 | |||
28 | #include "wl12xx.h" | ||
29 | #include "wl12xx_80211.h" | ||
30 | #include "io.h" | ||
31 | #include "tx.h" | ||
32 | |||
33 | #define OCP_CMD_LOOP 32 | ||
34 | |||
35 | #define OCP_CMD_WRITE 0x1 | ||
36 | #define OCP_CMD_READ 0x2 | ||
37 | |||
38 | #define OCP_READY_MASK BIT(18) | ||
39 | #define OCP_STATUS_MASK (BIT(16) | BIT(17)) | ||
40 | |||
41 | #define OCP_STATUS_NO_RESP 0x00000 | ||
42 | #define OCP_STATUS_OK 0x10000 | ||
43 | #define OCP_STATUS_REQ_FAILED 0x20000 | ||
44 | #define OCP_STATUS_RESP_ERROR 0x30000 | ||
45 | |||
46 | bool wl1271_set_block_size(struct wl1271 *wl) | ||
47 | { | ||
48 | if (wl->if_ops->set_block_size) { | ||
49 | wl->if_ops->set_block_size(wl, WL12XX_BUS_BLOCK_SIZE); | ||
50 | return true; | ||
51 | } | ||
52 | |||
53 | return false; | ||
54 | } | ||
55 | |||
56 | void wl1271_disable_interrupts(struct wl1271 *wl) | ||
57 | { | ||
58 | wl->if_ops->disable_irq(wl); | ||
59 | } | ||
60 | |||
61 | void wl1271_enable_interrupts(struct wl1271 *wl) | ||
62 | { | ||
63 | wl->if_ops->enable_irq(wl); | ||
64 | } | ||
65 | |||
66 | /* Set the SPI partitions to access the chip addresses | ||
67 | * | ||
68 | * To simplify driver code, a fixed (virtual) memory map is defined for | ||
69 | * register and memory addresses. Because in the chipset, in different stages | ||
70 | * of operation, those addresses will move around, an address translation | ||
71 | * mechanism is required. | ||
72 | * | ||
73 | * There are four partitions (three memory and one register partition), | ||
74 | * which are mapped to two different areas of the hardware memory. | ||
75 | * | ||
76 | * Virtual address | ||
77 | * space | ||
78 | * | ||
79 | * | | | ||
80 | * ...+----+--> mem.start | ||
81 | * Physical address ... | | | ||
82 | * space ... | | [PART_0] | ||
83 | * ... | | | ||
84 | * 00000000 <--+----+... ...+----+--> mem.start + mem.size | ||
85 | * | | ... | | | ||
86 | * |MEM | ... | | | ||
87 | * | | ... | | | ||
88 | * mem.size <--+----+... | | {unused area) | ||
89 | * | | ... | | | ||
90 | * |REG | ... | | | ||
91 | * mem.size | | ... | | | ||
92 | * + <--+----+... ...+----+--> reg.start | ||
93 | * reg.size | | ... | | | ||
94 | * |MEM2| ... | | [PART_1] | ||
95 | * | | ... | | | ||
96 | * ...+----+--> reg.start + reg.size | ||
97 | * | | | ||
98 | * | ||
99 | */ | ||
100 | int wl1271_set_partition(struct wl1271 *wl, | ||
101 | struct wl1271_partition_set *p) | ||
102 | { | ||
103 | /* copy partition info */ | ||
104 | memcpy(&wl->part, p, sizeof(*p)); | ||
105 | |||
106 | wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", | ||
107 | p->mem.start, p->mem.size); | ||
108 | wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", | ||
109 | p->reg.start, p->reg.size); | ||
110 | wl1271_debug(DEBUG_SPI, "mem2_start %08X mem2_size %08X", | ||
111 | p->mem2.start, p->mem2.size); | ||
112 | wl1271_debug(DEBUG_SPI, "mem3_start %08X mem3_size %08X", | ||
113 | p->mem3.start, p->mem3.size); | ||
114 | |||
115 | /* write partition info to the chipset */ | ||
116 | wl1271_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start); | ||
117 | wl1271_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size); | ||
118 | wl1271_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start); | ||
119 | wl1271_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size); | ||
120 | wl1271_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start); | ||
121 | wl1271_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size); | ||
122 | wl1271_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start); | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | EXPORT_SYMBOL_GPL(wl1271_set_partition); | ||
127 | |||
128 | void wl1271_io_reset(struct wl1271 *wl) | ||
129 | { | ||
130 | if (wl->if_ops->reset) | ||
131 | wl->if_ops->reset(wl); | ||
132 | } | ||
133 | |||
134 | void wl1271_io_init(struct wl1271 *wl) | ||
135 | { | ||
136 | if (wl->if_ops->init) | ||
137 | wl->if_ops->init(wl); | ||
138 | } | ||
139 | |||
140 | void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val) | ||
141 | { | ||
142 | /* write address >> 1 + 0x30000 to OCP_POR_CTR */ | ||
143 | addr = (addr >> 1) + 0x30000; | ||
144 | wl1271_write32(wl, OCP_POR_CTR, addr); | ||
145 | |||
146 | /* write value to OCP_POR_WDATA */ | ||
147 | wl1271_write32(wl, OCP_DATA_WRITE, val); | ||
148 | |||
149 | /* write 1 to OCP_CMD */ | ||
150 | wl1271_write32(wl, OCP_CMD, OCP_CMD_WRITE); | ||
151 | } | ||
152 | |||
153 | u16 wl1271_top_reg_read(struct wl1271 *wl, int addr) | ||
154 | { | ||
155 | u32 val; | ||
156 | int timeout = OCP_CMD_LOOP; | ||
157 | |||
158 | /* write address >> 1 + 0x30000 to OCP_POR_CTR */ | ||
159 | addr = (addr >> 1) + 0x30000; | ||
160 | wl1271_write32(wl, OCP_POR_CTR, addr); | ||
161 | |||
162 | /* write 2 to OCP_CMD */ | ||
163 | wl1271_write32(wl, OCP_CMD, OCP_CMD_READ); | ||
164 | |||
165 | /* poll for data ready */ | ||
166 | do { | ||
167 | val = wl1271_read32(wl, OCP_DATA_READ); | ||
168 | } while (!(val & OCP_READY_MASK) && --timeout); | ||
169 | |||
170 | if (!timeout) { | ||
171 | wl1271_warning("Top register access timed out."); | ||
172 | return 0xffff; | ||
173 | } | ||
174 | |||
175 | /* check data status and return if OK */ | ||
176 | if ((val & OCP_STATUS_MASK) == OCP_STATUS_OK) | ||
177 | return val & 0xffff; | ||
178 | else { | ||
179 | wl1271_warning("Top register access returned error."); | ||
180 | return 0xffff; | ||
181 | } | ||
182 | } | ||
183 | |||