2 * Intel Wireless Multicomm 3200 WiFi driver
4 * Copyright (C) 2009 Intel Corporation. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
16 * * Neither the name of Intel Corporation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * Intel Corporation <ilw@linux.intel.com>
34 * Samuel Ortiz <samuel.ortiz@intel.com>
35 * Zhu Yi <yi.zhu@intel.com>
39 #include <linux/kernel.h>
40 #include <linux/slab.h>
47 static struct iwm_eeprom_entry eeprom_map
[] = {
49 {"Signature", IWM_EEPROM_SIG_OFF
, IWM_EEPROM_SIG_LEN
},
51 [IWM_EEPROM_VERSION
] =
52 {"Version", IWM_EEPROM_VERSION_OFF
, IWM_EEPROM_VERSION_LEN
},
54 [IWM_EEPROM_OEM_HW_VERSION
] =
55 {"OEM HW version", IWM_EEPROM_OEM_HW_VERSION_OFF
,
56 IWM_EEPROM_OEM_HW_VERSION_LEN
},
58 [IWM_EEPROM_MAC_VERSION
] =
59 {"MAC version", IWM_EEPROM_MAC_VERSION_OFF
, IWM_EEPROM_MAC_VERSION_LEN
},
61 [IWM_EEPROM_CARD_ID
] =
62 {"Card ID", IWM_EEPROM_CARD_ID_OFF
, IWM_EEPROM_CARD_ID_LEN
},
64 [IWM_EEPROM_RADIO_CONF
] =
65 {"Radio config", IWM_EEPROM_RADIO_CONF_OFF
, IWM_EEPROM_RADIO_CONF_LEN
},
67 [IWM_EEPROM_SKU_CAP
] =
68 {"SKU capabilities", IWM_EEPROM_SKU_CAP_OFF
, IWM_EEPROM_SKU_CAP_LEN
},
70 [IWM_EEPROM_FAT_CHANNELS_CAP
] =
71 {"HT channels capabilities", IWM_EEPROM_FAT_CHANNELS_CAP_OFF
,
72 IWM_EEPROM_FAT_CHANNELS_CAP_LEN
},
74 [IWM_EEPROM_CALIB_RXIQ_OFFSET
] =
75 {"RX IQ offset", IWM_EEPROM_CALIB_RXIQ_OFF
, IWM_EEPROM_INDIRECT_LEN
},
77 [IWM_EEPROM_CALIB_RXIQ
] =
78 {"Calib RX IQ", 0, IWM_EEPROM_CALIB_RXIQ_LEN
},
82 static int iwm_eeprom_read(struct iwm_priv
*iwm
, u8 eeprom_id
)
85 u32 entry_size
, chunk_size
, data_offset
= 0, addr_offset
= 0;
87 struct iwm_udma_wifi_cmd udma_cmd
;
88 struct iwm_umac_cmd umac_cmd
;
89 struct iwm_umac_cmd_eeprom_proxy eeprom_cmd
;
91 if (eeprom_id
> (IWM_EEPROM_LAST
- 1))
94 entry_size
= eeprom_map
[eeprom_id
].length
;
96 if (eeprom_id
>= IWM_EEPROM_INDIRECT_DATA
) {
98 u32 off_id
= eeprom_id
- IWM_EEPROM_INDIRECT_DATA
+
99 IWM_EEPROM_INDIRECT_OFFSET
;
101 eeprom_map
[eeprom_id
].offset
=
102 *(u16
*)(iwm
->eeprom
+ eeprom_map
[off_id
].offset
) << 1;
105 addr
= eeprom_map
[eeprom_id
].offset
;
108 udma_cmd
.credit_group
= 0x4;
109 udma_cmd
.ra_tid
= UMAC_HDI_ACT_TBL_IDX_HOST_CMD
;
110 udma_cmd
.lmac_offset
= 0;
112 umac_cmd
.id
= UMAC_CMD_OPCODE_EEPROM_PROXY
;
115 while (entry_size
> 0) {
116 chunk_size
= min_t(u32
, entry_size
, IWM_MAX_EEPROM_DATA_LEN
);
118 eeprom_cmd
.hdr
.type
=
119 cpu_to_le32(IWM_UMAC_CMD_EEPROM_TYPE_READ
);
120 eeprom_cmd
.hdr
.offset
= cpu_to_le32(addr
+ addr_offset
);
121 eeprom_cmd
.hdr
.len
= cpu_to_le32(chunk_size
);
123 ret
= iwm_hal_send_umac_cmd(iwm
, &udma_cmd
,
124 &umac_cmd
, &eeprom_cmd
,
125 sizeof(struct iwm_umac_cmd_eeprom_proxy
));
127 IWM_ERR(iwm
, "Couldn't read eeprom\n");
131 ret
= iwm_notif_handle(iwm
, UMAC_CMD_OPCODE_EEPROM_PROXY
,
134 IWM_ERR(iwm
, "Did not get any eeprom answer\n");
138 data_offset
+= chunk_size
;
139 addr_offset
+= chunk_size
;
140 entry_size
-= chunk_size
;
146 u8
*iwm_eeprom_access(struct iwm_priv
*iwm
, u8 eeprom_id
)
149 return ERR_PTR(-ENODEV
);
151 return iwm
->eeprom
+ eeprom_map
[eeprom_id
].offset
;
154 int iwm_eeprom_fat_channels(struct iwm_priv
*iwm
)
156 struct wiphy
*wiphy
= iwm_to_wiphy(iwm
);
157 struct ieee80211_supported_band
*band
;
160 channels
= (u16
*)iwm_eeprom_access(iwm
, IWM_EEPROM_FAT_CHANNELS_CAP
);
161 if (IS_ERR(channels
))
162 return PTR_ERR(channels
);
164 band
= wiphy
->bands
[IEEE80211_BAND_2GHZ
];
165 band
->ht_cap
.ht_supported
= true;
167 for (i
= 0; i
< IWM_EEPROM_FAT_CHANNELS_24
; i
++)
168 if (!(channels
[i
] & IWM_EEPROM_FAT_CHANNEL_ENABLED
))
169 band
->ht_cap
.ht_supported
= false;
171 band
= wiphy
->bands
[IEEE80211_BAND_5GHZ
];
172 band
->ht_cap
.ht_supported
= true;
173 for (i
= IWM_EEPROM_FAT_CHANNELS_24
; i
< IWM_EEPROM_FAT_CHANNELS
; i
++)
174 if (!(channels
[i
] & IWM_EEPROM_FAT_CHANNEL_ENABLED
))
175 band
->ht_cap
.ht_supported
= false;
180 u32
iwm_eeprom_wireless_mode(struct iwm_priv
*iwm
)
183 u32 wireless_mode
= 0;
185 sku_cap
= *((u16
*)iwm_eeprom_access(iwm
, IWM_EEPROM_SKU_CAP
));
187 if (sku_cap
& IWM_EEPROM_SKU_CAP_BAND_24GHZ
)
188 wireless_mode
|= WIRELESS_MODE_11G
;
190 if (sku_cap
& IWM_EEPROM_SKU_CAP_BAND_52GHZ
)
191 wireless_mode
|= WIRELESS_MODE_11A
;
193 if (sku_cap
& IWM_EEPROM_SKU_CAP_11N_ENABLE
)
194 wireless_mode
|= WIRELESS_MODE_11N
;
196 return wireless_mode
;
200 int iwm_eeprom_init(struct iwm_priv
*iwm
)
205 iwm
->eeprom
= kzalloc(IWM_EEPROM_LEN
, GFP_KERNEL
);
209 for (i
= IWM_EEPROM_FIRST
; i
< IWM_EEPROM_LAST
; i
++) {
210 ret
= iwm_eeprom_read(iwm
, i
);
212 IWM_ERR(iwm
, "Couldn't read eeprom entry #%d: %s\n",
213 i
, eeprom_map
[i
].name
);
218 IWM_DBG_BOOT(iwm
, DBG
, "EEPROM dump:\n");
219 for (i
= IWM_EEPROM_FIRST
; i
< IWM_EEPROM_LAST
; i
++) {
221 sprintf(name
, "%s: ", eeprom_map
[i
].name
);
223 IWM_HEXDUMP(iwm
, DBG
, BOOT
, name
,
224 iwm
->eeprom
+ eeprom_map
[i
].offset
,
225 eeprom_map
[i
].length
);
231 void iwm_eeprom_exit(struct iwm_priv
*iwm
)