1b7d5e03cSMatthew Dillon /*
2b7d5e03cSMatthew Dillon * Copyright (c) 2013 Qualcomm Atheros, Inc.
3b7d5e03cSMatthew Dillon *
4b7d5e03cSMatthew Dillon * Permission to use, copy, modify, and/or distribute this software for any
5b7d5e03cSMatthew Dillon * purpose with or without fee is hereby granted, provided that the above
6b7d5e03cSMatthew Dillon * copyright notice and this permission notice appear in all copies.
7b7d5e03cSMatthew Dillon *
8b7d5e03cSMatthew Dillon * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9b7d5e03cSMatthew Dillon * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10b7d5e03cSMatthew Dillon * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11b7d5e03cSMatthew Dillon * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12b7d5e03cSMatthew Dillon * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13b7d5e03cSMatthew Dillon * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14b7d5e03cSMatthew Dillon * PERFORMANCE OF THIS SOFTWARE.
15b7d5e03cSMatthew Dillon */
16b7d5e03cSMatthew Dillon
17b7d5e03cSMatthew Dillon #include "opt_ah.h"
18b7d5e03cSMatthew Dillon
19b7d5e03cSMatthew Dillon #include "ah.h"
20b7d5e03cSMatthew Dillon #include "ah_internal.h"
21b7d5e03cSMatthew Dillon
22b7d5e03cSMatthew Dillon #include "ar9300/ar9300.h"
23b7d5e03cSMatthew Dillon #include "ar9300/ar9300reg.h"
24b7d5e03cSMatthew Dillon #include "ar9300/ar9300phy.h"
25b7d5e03cSMatthew Dillon
26b7d5e03cSMatthew Dillon /* chansel table, used by Hornet and Poseidon */
27b7d5e03cSMatthew Dillon static const u_int32_t ar9300_chansel_xtal_25M[] = {
28b7d5e03cSMatthew Dillon 0x101479e, /* Freq 2412 - (128 << 17) + 83870 */
29b7d5e03cSMatthew Dillon 0x101d027, /* Freq 2417 - (128 << 17) + 118823 */
30b7d5e03cSMatthew Dillon 0x10258af, /* Freq 2422 - (129 << 17) + 22703 */
31b7d5e03cSMatthew Dillon 0x102e138, /* Freq 2427 - (129 << 17) + 57656 */
32b7d5e03cSMatthew Dillon 0x10369c0, /* Freq 2432 - (129 << 17) + 92608 */
33b7d5e03cSMatthew Dillon 0x103f249, /* Freq 2437 - (129 << 17) + 127561 */
34b7d5e03cSMatthew Dillon 0x1047ad1, /* Freq 2442 - (130 << 17) + 31441 */
35b7d5e03cSMatthew Dillon 0x105035a, /* Freq 2447 - (130 << 17) + 66394 */
36b7d5e03cSMatthew Dillon 0x1058be2, /* Freq 2452 - (130 << 17) + 101346 */
37b7d5e03cSMatthew Dillon 0x106146b, /* Freq 2457 - (131 << 17) + 5227 */
38b7d5e03cSMatthew Dillon 0x1069cf3, /* Freq 2462 - (131 << 17) + 40179 */
39b7d5e03cSMatthew Dillon 0x107257c, /* Freq 2467 - (131 << 17) + 75132 */
40b7d5e03cSMatthew Dillon 0x107ae04, /* Freq 2472 - (131 << 17) + 110084 */
41b7d5e03cSMatthew Dillon 0x108f5b2, /* Freq 2484 - (132 << 17) + 62898 */
42b7d5e03cSMatthew Dillon };
43b7d5e03cSMatthew Dillon
44b7d5e03cSMatthew Dillon static const u_int32_t ar9300_chansel_xtal_40M[] = {
45b7d5e03cSMatthew Dillon 0xa0ccbe, /* Freq 2412 - (80 << 17) + 52414 */
46b7d5e03cSMatthew Dillon 0xa12213, /* Freq 2417 - (80 << 17) + 74259 */
47b7d5e03cSMatthew Dillon 0xa17769, /* Freq 2422 - (80 << 17) + 96105 */
48b7d5e03cSMatthew Dillon 0xa1ccbe, /* Freq 2427 - (80 << 17) + 117950 */
49b7d5e03cSMatthew Dillon 0xa22213, /* Freq 2432 - (81 << 17) + 8723 */
50b7d5e03cSMatthew Dillon 0xa27769, /* Freq 2437 - (81 << 17) + 30569 */
51b7d5e03cSMatthew Dillon 0xa2ccbe, /* Freq 2442 - (81 << 17) + 52414 */
52b7d5e03cSMatthew Dillon 0xa32213, /* Freq 2447 - (81 << 17) + 74259 */
53b7d5e03cSMatthew Dillon 0xa37769, /* Freq 2452 - (81 << 17) + 96105 */
54b7d5e03cSMatthew Dillon 0xa3ccbe, /* Freq 2457 - (81 << 17) + 117950 */
55b7d5e03cSMatthew Dillon 0xa42213, /* Freq 2462 - (82 << 17) + 8723 */
56b7d5e03cSMatthew Dillon 0xa47769, /* Freq 2467 - (82 << 17) + 30569 */
57b7d5e03cSMatthew Dillon 0xa4ccbe, /* Freq 2472 - (82 << 17) + 52414 */
58b7d5e03cSMatthew Dillon 0xa5998b, /* Freq 2484 - (82 << 17) + 104843 */
59b7d5e03cSMatthew Dillon };
60b7d5e03cSMatthew Dillon
61b7d5e03cSMatthew Dillon
62b7d5e03cSMatthew Dillon /*
63b7d5e03cSMatthew Dillon * Take the MHz channel value and set the Channel value
64b7d5e03cSMatthew Dillon *
65b7d5e03cSMatthew Dillon * ASSUMES: Writes enabled to analog bus
66b7d5e03cSMatthew Dillon *
67b7d5e03cSMatthew Dillon * Actual Expression,
68b7d5e03cSMatthew Dillon *
69b7d5e03cSMatthew Dillon * For 2GHz channel,
70b7d5e03cSMatthew Dillon * Channel Frequency = (3/4) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^17)
71b7d5e03cSMatthew Dillon * (freq_ref = 40MHz)
72b7d5e03cSMatthew Dillon *
73b7d5e03cSMatthew Dillon * For 5GHz channel,
74b7d5e03cSMatthew Dillon * Channel Frequency = (3/2) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^10)
75b7d5e03cSMatthew Dillon * (freq_ref = 40MHz/(24>>amode_ref_sel))
76b7d5e03cSMatthew Dillon *
77b7d5e03cSMatthew Dillon * For 5GHz channels which are 5MHz spaced,
78b7d5e03cSMatthew Dillon * Channel Frequency = (3/2) * freq_ref * (chansel[8:0] + chanfrac[16:0]/2^17)
79b7d5e03cSMatthew Dillon * (freq_ref = 40MHz)
80b7d5e03cSMatthew Dillon */
81b7d5e03cSMatthew Dillon static HAL_BOOL
ar9300_set_channel(struct ath_hal * ah,struct ieee80211_channel * chan)82b7d5e03cSMatthew Dillon ar9300_set_channel(struct ath_hal *ah, struct ieee80211_channel *chan)
83b7d5e03cSMatthew Dillon {
84b7d5e03cSMatthew Dillon u_int16_t b_mode, frac_mode = 0, a_mode_ref_sel = 0;
85b7d5e03cSMatthew Dillon u_int32_t freq, channel_sel, reg32;
86b7d5e03cSMatthew Dillon u_int8_t clk_25mhz = AH9300(ah)->clk_25mhz;
87b7d5e03cSMatthew Dillon CHAN_CENTERS centers;
88b7d5e03cSMatthew Dillon int load_synth_channel;
89*a20e5e51SMatthew Dillon #ifdef AH_DEBUG_ALQ
90b7d5e03cSMatthew Dillon HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
91*a20e5e51SMatthew Dillon #endif
92b7d5e03cSMatthew Dillon
93b7d5e03cSMatthew Dillon /*
94b7d5e03cSMatthew Dillon * Put this behind AH_DEBUG_ALQ for now until the Hornet
95b7d5e03cSMatthew Dillon * channel_sel code below is made to work.
96b7d5e03cSMatthew Dillon */
97b7d5e03cSMatthew Dillon #ifdef AH_DEBUG_ALQ
98b7d5e03cSMatthew Dillon OS_MARK(ah, AH_MARK_SETCHANNEL, ichan->channel);
99b7d5e03cSMatthew Dillon #endif
100b7d5e03cSMatthew Dillon
101b7d5e03cSMatthew Dillon ar9300_get_channel_centers(ah, chan, ¢ers);
102b7d5e03cSMatthew Dillon freq = centers.synth_center;
103b7d5e03cSMatthew Dillon
104b7d5e03cSMatthew Dillon if (freq < 4800) { /* 2 GHz, fractional mode */
105b7d5e03cSMatthew Dillon b_mode = 1; /* 2 GHz */
106b7d5e03cSMatthew Dillon
107b7d5e03cSMatthew Dillon if (AR_SREV_HORNET(ah)) {
108b7d5e03cSMatthew Dillon #if 0
109b7d5e03cSMatthew Dillon u_int32_t ichan =
110b7d5e03cSMatthew Dillon ieee80211_mhz2ieee(ah, chan->ic_freq, chan->ic_flags);
111b7d5e03cSMatthew Dillon HALASSERT(ichan > 0 && ichan <= 14);
112b7d5e03cSMatthew Dillon if (clk_25mhz) {
113b7d5e03cSMatthew Dillon channel_sel = ar9300_chansel_xtal_25M[ichan - 1];
114b7d5e03cSMatthew Dillon } else {
115b7d5e03cSMatthew Dillon channel_sel = ar9300_chansel_xtal_40M[ichan - 1];
116b7d5e03cSMatthew Dillon }
117b7d5e03cSMatthew Dillon #endif
118b7d5e03cSMatthew Dillon uint32_t i;
119b7d5e03cSMatthew Dillon
120*a20e5e51SMatthew Dillon /*
121*a20e5e51SMatthew Dillon * Pay close attention to this bit!
122*a20e5e51SMatthew Dillon *
123*a20e5e51SMatthew Dillon * We need to map the actual desired synth frequency to
124*a20e5e51SMatthew Dillon * one of the channel select array entries.
125*a20e5e51SMatthew Dillon *
126*a20e5e51SMatthew Dillon * For HT20, it'll align with the channel we select.
127*a20e5e51SMatthew Dillon *
128*a20e5e51SMatthew Dillon * For HT40 though it won't - the centre frequency
129*a20e5e51SMatthew Dillon * will not be the frequency of chan->ic_freq or ichan->freq;
130*a20e5e51SMatthew Dillon * it needs to be whatever frequency maps to 'freq'.
131*a20e5e51SMatthew Dillon */
132*a20e5e51SMatthew Dillon i = ath_hal_mhz2ieee_2ghz(ah, freq);
133b7d5e03cSMatthew Dillon HALASSERT(i > 0 && i <= 14);
134b7d5e03cSMatthew Dillon if (clk_25mhz) {
135b7d5e03cSMatthew Dillon channel_sel = ar9300_chansel_xtal_25M[i - 1];
136b7d5e03cSMatthew Dillon } else {
137b7d5e03cSMatthew Dillon channel_sel = ar9300_chansel_xtal_40M[i - 1];
138b7d5e03cSMatthew Dillon }
139b7d5e03cSMatthew Dillon } else if (AR_SREV_POSEIDON(ah) || AR_SREV_APHRODITE(ah)) {
140b7d5e03cSMatthew Dillon u_int32_t channel_frac;
141b7d5e03cSMatthew Dillon /*
142b7d5e03cSMatthew Dillon * freq_ref = (40 / (refdiva >> a_mode_ref_sel));
143b7d5e03cSMatthew Dillon * (where refdiva = 1 and amoderefsel = 0)
144b7d5e03cSMatthew Dillon * ndiv = ((chan_mhz * 4) / 3) / freq_ref;
145b7d5e03cSMatthew Dillon * chansel = int(ndiv), chanfrac = (ndiv - chansel) * 0x20000
146b7d5e03cSMatthew Dillon */
147b7d5e03cSMatthew Dillon channel_sel = (freq * 4) / 120;
148b7d5e03cSMatthew Dillon channel_frac = (((freq * 4) % 120) * 0x20000) / 120;
149b7d5e03cSMatthew Dillon channel_sel = (channel_sel << 17) | (channel_frac);
150*a20e5e51SMatthew Dillon } else if (AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah) || AR_SREV_HONEYBEE(ah)) {
151b7d5e03cSMatthew Dillon u_int32_t channel_frac;
152b7d5e03cSMatthew Dillon if (clk_25mhz) {
153b7d5e03cSMatthew Dillon /*
154b7d5e03cSMatthew Dillon * freq_ref = (50 / (refdiva >> a_mode_ref_sel));
155b7d5e03cSMatthew Dillon * (where refdiva = 1 and amoderefsel = 0)
156b7d5e03cSMatthew Dillon * ndiv = ((chan_mhz * 4) / 3) / freq_ref;
157b7d5e03cSMatthew Dillon * chansel = int(ndiv), chanfrac = (ndiv - chansel) * 0x20000
158b7d5e03cSMatthew Dillon */
159*a20e5e51SMatthew Dillon if (AR_SREV_SCORPION(ah) || AR_SREV_HONEYBEE(ah)) {
160b7d5e03cSMatthew Dillon /* Doubler is off for Scorpion */
161b7d5e03cSMatthew Dillon channel_sel = (freq * 4) / 75;
162b7d5e03cSMatthew Dillon channel_frac = (((freq * 4) % 75) * 0x20000) / 75;
163b7d5e03cSMatthew Dillon } else {
164b7d5e03cSMatthew Dillon channel_sel = (freq * 2) / 75;
165b7d5e03cSMatthew Dillon channel_frac = (((freq * 2) % 75) * 0x20000) / 75;
166b7d5e03cSMatthew Dillon }
167b7d5e03cSMatthew Dillon } else {
168b7d5e03cSMatthew Dillon /*
169b7d5e03cSMatthew Dillon * freq_ref = (50 / (refdiva >> a_mode_ref_sel));
170b7d5e03cSMatthew Dillon * (where refdiva = 1 and amoderefsel = 0)
171b7d5e03cSMatthew Dillon * ndiv = ((chan_mhz * 4) / 3) / freq_ref;
172b7d5e03cSMatthew Dillon * chansel = int(ndiv), chanfrac = (ndiv - chansel) * 0x20000
173b7d5e03cSMatthew Dillon */
174b7d5e03cSMatthew Dillon if (AR_SREV_SCORPION(ah)) {
175b7d5e03cSMatthew Dillon /* Doubler is off for Scorpion */
176b7d5e03cSMatthew Dillon channel_sel = (freq * 4) / 120;
177b7d5e03cSMatthew Dillon channel_frac = (((freq * 4) % 120) * 0x20000) / 120;
178b7d5e03cSMatthew Dillon } else {
179b7d5e03cSMatthew Dillon channel_sel = (freq * 2) / 120;
180b7d5e03cSMatthew Dillon channel_frac = (((freq * 2) % 120) * 0x20000) / 120;
181b7d5e03cSMatthew Dillon }
182b7d5e03cSMatthew Dillon }
183b7d5e03cSMatthew Dillon channel_sel = (channel_sel << 17) | (channel_frac);
184b7d5e03cSMatthew Dillon } else {
185b7d5e03cSMatthew Dillon channel_sel = CHANSEL_2G(freq);
186b7d5e03cSMatthew Dillon }
187b7d5e03cSMatthew Dillon } else {
188b7d5e03cSMatthew Dillon b_mode = 0; /* 5 GHz */
189b7d5e03cSMatthew Dillon if ((AR_SREV_WASP(ah) || AR_SREV_SCORPION(ah)) && clk_25mhz){
190b7d5e03cSMatthew Dillon u_int32_t channel_frac;
191b7d5e03cSMatthew Dillon /*
192b7d5e03cSMatthew Dillon * freq_ref = (50 / (refdiva >> amoderefsel));
193b7d5e03cSMatthew Dillon * (refdiva = 1, amoderefsel = 0)
194b7d5e03cSMatthew Dillon * ndiv = ((chan_mhz * 2) / 3) / freq_ref;
195b7d5e03cSMatthew Dillon * chansel = int(ndiv), chanfrac = (ndiv - chansel) * 0x20000
196b7d5e03cSMatthew Dillon */
197b7d5e03cSMatthew Dillon channel_sel = freq / 75 ;
198b7d5e03cSMatthew Dillon channel_frac = ((freq % 75) * 0x20000) / 75;
199b7d5e03cSMatthew Dillon channel_sel = (channel_sel << 17) | (channel_frac);
200b7d5e03cSMatthew Dillon } else {
201b7d5e03cSMatthew Dillon channel_sel = CHANSEL_5G(freq);
202b7d5e03cSMatthew Dillon /* Doubler is ON, so, divide channel_sel by 2. */
203b7d5e03cSMatthew Dillon channel_sel >>= 1;
204b7d5e03cSMatthew Dillon }
205b7d5e03cSMatthew Dillon }
206b7d5e03cSMatthew Dillon
207b7d5e03cSMatthew Dillon
208b7d5e03cSMatthew Dillon /* Enable fractional mode for all channels */
209b7d5e03cSMatthew Dillon frac_mode = 1;
210b7d5e03cSMatthew Dillon a_mode_ref_sel = 0;
211b7d5e03cSMatthew Dillon load_synth_channel = 0;
212b7d5e03cSMatthew Dillon
213b7d5e03cSMatthew Dillon reg32 = (b_mode << 29);
214b7d5e03cSMatthew Dillon OS_REG_WRITE(ah, AR_PHY_SYNTH_CONTROL, reg32);
215b7d5e03cSMatthew Dillon
216b7d5e03cSMatthew Dillon /* Enable Long shift Select for Synthesizer */
217b7d5e03cSMatthew Dillon OS_REG_RMW_FIELD(ah,
218b7d5e03cSMatthew Dillon AR_PHY_65NM_CH0_SYNTH4, AR_PHY_SYNTH4_LONG_SHIFT_SELECT, 1);
219b7d5e03cSMatthew Dillon
220b7d5e03cSMatthew Dillon /* program synth. setting */
221b7d5e03cSMatthew Dillon reg32 =
222b7d5e03cSMatthew Dillon (channel_sel << 2) |
223b7d5e03cSMatthew Dillon (a_mode_ref_sel << 28) |
224b7d5e03cSMatthew Dillon (frac_mode << 30) |
225b7d5e03cSMatthew Dillon (load_synth_channel << 31);
226b7d5e03cSMatthew Dillon if (IEEE80211_IS_CHAN_QUARTER(chan)) {
227b7d5e03cSMatthew Dillon reg32 += CHANSEL_5G_DOT5MHZ;
228b7d5e03cSMatthew Dillon }
229b7d5e03cSMatthew Dillon OS_REG_WRITE(ah, AR_PHY_65NM_CH0_SYNTH7, reg32);
230b7d5e03cSMatthew Dillon /* Toggle Load Synth channel bit */
231b7d5e03cSMatthew Dillon load_synth_channel = 1;
232b7d5e03cSMatthew Dillon reg32 |= load_synth_channel << 31;
233b7d5e03cSMatthew Dillon OS_REG_WRITE(ah, AR_PHY_65NM_CH0_SYNTH7, reg32);
234b7d5e03cSMatthew Dillon
235b7d5e03cSMatthew Dillon
236b7d5e03cSMatthew Dillon AH_PRIVATE(ah)->ah_curchan = chan;
237b7d5e03cSMatthew Dillon
238b7d5e03cSMatthew Dillon return AH_TRUE;
239b7d5e03cSMatthew Dillon }
240b7d5e03cSMatthew Dillon
241b7d5e03cSMatthew Dillon
242b7d5e03cSMatthew Dillon #if 0
243b7d5e03cSMatthew Dillon static HAL_BOOL
244b7d5e03cSMatthew Dillon ar9300_get_chip_power_limits(struct ath_hal *ah, HAL_CHANNEL *chans,
245b7d5e03cSMatthew Dillon u_int32_t nchans)
246b7d5e03cSMatthew Dillon {
247b7d5e03cSMatthew Dillon int i;
248b7d5e03cSMatthew Dillon
249b7d5e03cSMatthew Dillon for (i = 0; i < nchans; i++) {
250b7d5e03cSMatthew Dillon chans[i].max_tx_power = AR9300_MAX_RATE_POWER;
251b7d5e03cSMatthew Dillon chans[i].min_tx_power = AR9300_MAX_RATE_POWER;
252b7d5e03cSMatthew Dillon }
253b7d5e03cSMatthew Dillon return AH_TRUE;
254b7d5e03cSMatthew Dillon }
255b7d5e03cSMatthew Dillon #endif
256b7d5e03cSMatthew Dillon
257b7d5e03cSMatthew Dillon /* XXX FreeBSD */
258b7d5e03cSMatthew Dillon static HAL_BOOL
ar9300_get_chip_power_limits(struct ath_hal * ah,struct ieee80211_channel * chan)259b7d5e03cSMatthew Dillon ar9300_get_chip_power_limits(struct ath_hal *ah,
260b7d5e03cSMatthew Dillon struct ieee80211_channel *chan)
261b7d5e03cSMatthew Dillon {
262b7d5e03cSMatthew Dillon /* XXX ? */
263b7d5e03cSMatthew Dillon chan->ic_minpower = 0;
264b7d5e03cSMatthew Dillon chan->ic_maxpower = AR9300_MAX_RATE_POWER;
265b7d5e03cSMatthew Dillon
266b7d5e03cSMatthew Dillon return AH_TRUE;
267b7d5e03cSMatthew Dillon }
268b7d5e03cSMatthew Dillon
269b7d5e03cSMatthew Dillon HAL_BOOL
ar9300_rf_attach(struct ath_hal * ah,HAL_STATUS * status)270b7d5e03cSMatthew Dillon ar9300_rf_attach(struct ath_hal *ah, HAL_STATUS *status)
271b7d5e03cSMatthew Dillon {
272b7d5e03cSMatthew Dillon struct ath_hal_9300 *ahp = AH9300(ah);
273b7d5e03cSMatthew Dillon
274b7d5e03cSMatthew Dillon ahp->ah_rf_hal.set_channel = ar9300_set_channel;
275b7d5e03cSMatthew Dillon ahp->ah_rf_hal.get_chip_power_lim = ar9300_get_chip_power_limits;
276b7d5e03cSMatthew Dillon
277b7d5e03cSMatthew Dillon *status = HAL_OK;
278b7d5e03cSMatthew Dillon
279b7d5e03cSMatthew Dillon return AH_TRUE;
280b7d5e03cSMatthew Dillon }
281