1  // SPDX-License-Identifier: GPL-2.0-only
2  /*
3   * This file is part of wl1271
4   *
5   * Copyright (C) 2008-2010 Nokia Corporation
6   *
7   * Contact: Luciano Coelho <luciano.coelho@nokia.com>
8   */
9  
10  #include <linux/module.h>
11  #include <linux/platform_device.h>
12  #include <linux/spi/spi.h>
13  #include <linux/interrupt.h>
14  
15  #include "wlcore.h"
16  #include "debug.h"
17  #include "wl12xx_80211.h"
18  #include "io.h"
19  #include "tx.h"
20  
wl1271_set_block_size(struct wl1271 * wl)21  bool wl1271_set_block_size(struct wl1271 *wl)
22  {
23  	if (wl->if_ops->set_block_size) {
24  		wl->if_ops->set_block_size(wl->dev, WL12XX_BUS_BLOCK_SIZE);
25  		return true;
26  	}
27  
28  	return false;
29  }
30  
wlcore_disable_interrupts(struct wl1271 * wl)31  void wlcore_disable_interrupts(struct wl1271 *wl)
32  {
33  	disable_irq(wl->irq);
34  }
35  EXPORT_SYMBOL_GPL(wlcore_disable_interrupts);
36  
wlcore_disable_interrupts_nosync(struct wl1271 * wl)37  void wlcore_disable_interrupts_nosync(struct wl1271 *wl)
38  {
39  	disable_irq_nosync(wl->irq);
40  }
41  EXPORT_SYMBOL_GPL(wlcore_disable_interrupts_nosync);
42  
wlcore_enable_interrupts(struct wl1271 * wl)43  void wlcore_enable_interrupts(struct wl1271 *wl)
44  {
45  	enable_irq(wl->irq);
46  }
47  EXPORT_SYMBOL_GPL(wlcore_enable_interrupts);
48  
wlcore_synchronize_interrupts(struct wl1271 * wl)49  void wlcore_synchronize_interrupts(struct wl1271 *wl)
50  {
51  	synchronize_irq(wl->irq);
52  }
53  EXPORT_SYMBOL_GPL(wlcore_synchronize_interrupts);
54  
wlcore_translate_addr(struct wl1271 * wl,int addr)55  int wlcore_translate_addr(struct wl1271 *wl, int addr)
56  {
57  	struct wlcore_partition_set *part = &wl->curr_part;
58  
59  	/*
60  	 * To translate, first check to which window of addresses the
61  	 * particular address belongs. Then subtract the starting address
62  	 * of that window from the address. Then, add offset of the
63  	 * translated region.
64  	 *
65  	 * The translated regions occur next to each other in physical device
66  	 * memory, so just add the sizes of the preceding address regions to
67  	 * get the offset to the new region.
68  	 */
69  	if ((addr >= part->mem.start) &&
70  	    (addr < part->mem.start + part->mem.size))
71  		return addr - part->mem.start;
72  	else if ((addr >= part->reg.start) &&
73  		 (addr < part->reg.start + part->reg.size))
74  		return addr - part->reg.start + part->mem.size;
75  	else if ((addr >= part->mem2.start) &&
76  		 (addr < part->mem2.start + part->mem2.size))
77  		return addr - part->mem2.start + part->mem.size +
78  			part->reg.size;
79  	else if ((addr >= part->mem3.start) &&
80  		 (addr < part->mem3.start + part->mem3.size))
81  		return addr - part->mem3.start + part->mem.size +
82  			part->reg.size + part->mem2.size;
83  
84  	WARN(1, "HW address 0x%x out of range", addr);
85  	return 0;
86  }
87  EXPORT_SYMBOL_GPL(wlcore_translate_addr);
88  
89  /* Set the partitions to access the chip addresses
90   *
91   * To simplify driver code, a fixed (virtual) memory map is defined for
92   * register and memory addresses. Because in the chipset, in different stages
93   * of operation, those addresses will move around, an address translation
94   * mechanism is required.
95   *
96   * There are four partitions (three memory and one register partition),
97   * which are mapped to two different areas of the hardware memory.
98   *
99   *                                Virtual address
100   *                                     space
101   *
102   *                                    |    |
103   *                                 ...+----+--> mem.start
104   *          Physical address    ...   |    |
105   *               space       ...      |    | [PART_0]
106   *                        ...         |    |
107   *  00000000  <--+----+...         ...+----+--> mem.start + mem.size
108   *               |    |         ...   |    |
109   *               |MEM |      ...      |    |
110   *               |    |   ...         |    |
111   *  mem.size  <--+----+...            |    | {unused area)
112   *               |    |   ...         |    |
113   *               |REG |      ...      |    |
114   *  mem.size     |    |         ...   |    |
115   *      +     <--+----+...         ...+----+--> reg.start
116   *  reg.size     |    |   ...         |    |
117   *               |MEM2|      ...      |    | [PART_1]
118   *               |    |         ...   |    |
119   *                                 ...+----+--> reg.start + reg.size
120   *                                    |    |
121   *
122   */
wlcore_set_partition(struct wl1271 * wl,const struct wlcore_partition_set * p)123  int wlcore_set_partition(struct wl1271 *wl,
124  			 const struct wlcore_partition_set *p)
125  {
126  	int ret;
127  
128  	/* copy partition info */
129  	memcpy(&wl->curr_part, p, sizeof(*p));
130  
131  	wl1271_debug(DEBUG_IO, "mem_start %08X mem_size %08X",
132  		     p->mem.start, p->mem.size);
133  	wl1271_debug(DEBUG_IO, "reg_start %08X reg_size %08X",
134  		     p->reg.start, p->reg.size);
135  	wl1271_debug(DEBUG_IO, "mem2_start %08X mem2_size %08X",
136  		     p->mem2.start, p->mem2.size);
137  	wl1271_debug(DEBUG_IO, "mem3_start %08X mem3_size %08X",
138  		     p->mem3.start, p->mem3.size);
139  
140  	ret = wlcore_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start);
141  	if (ret < 0)
142  		goto out;
143  
144  	ret = wlcore_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size);
145  	if (ret < 0)
146  		goto out;
147  
148  	ret = wlcore_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start);
149  	if (ret < 0)
150  		goto out;
151  
152  	ret = wlcore_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size);
153  	if (ret < 0)
154  		goto out;
155  
156  	ret = wlcore_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start);
157  	if (ret < 0)
158  		goto out;
159  
160  	ret = wlcore_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size);
161  	if (ret < 0)
162  		goto out;
163  
164  	/* wl12xx only: We don't need the size of the last partition,
165  	 * as it is automatically calculated based on the total memory
166  	 * size and the sizes of the previous partitions.
167  	 *
168  	 * wl18xx re-defines the HW_PART3 addresses for logger over
169  	 * SDIO support. wl12xx is expecting the write to
170  	 * HW_PART3_START_ADDR at offset 24. This creates conflict
171  	 * between the addresses.
172  	 * In order to fix this the expected value is written to
173  	 * HW_PART3_SIZE_ADDR instead which is at offset 24 after changes.
174  	 */
175  	ret = wlcore_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start);
176  	if (ret < 0)
177  		goto out;
178  
179  	ret = wlcore_raw_write32(wl, HW_PART3_SIZE_ADDR, p->mem3.size);
180  	if (ret < 0)
181  		goto out;
182  
183  out:
184  	return ret;
185  }
186  EXPORT_SYMBOL_GPL(wlcore_set_partition);
187  
wl1271_io_reset(struct wl1271 * wl)188  void wl1271_io_reset(struct wl1271 *wl)
189  {
190  	if (wl->if_ops->reset)
191  		wl->if_ops->reset(wl->dev);
192  }
193  
wl1271_io_init(struct wl1271 * wl)194  void wl1271_io_init(struct wl1271 *wl)
195  {
196  	if (wl->if_ops->init)
197  		wl->if_ops->init(wl->dev);
198  }
199