License cleanup: add SPDX GPL-2.0 license identifier to files with no license
[linux-block.git] / drivers / staging / media / atomisp / platform / intel-mid / intel_mid_pcihelpers.c
CommitLineData
b2441318 1// SPDX-License-Identifier: GPL-2.0
a49d2536
AC
2#include <linux/export.h>
3#include <linux/pci.h>
4#include <linux/pm_qos.h>
5#include <linux/delay.h>
6
7/* G-Min addition: "platform_is()" lives in intel_mid_pm.h in the MCG
8 * tree, but it's just platform ID info and we don't want to pull in
8c7e9a73
AS
9 * the whole SFI-based PM architecture.
10 */
a49d2536
AC
11#define INTEL_ATOM_MRST 0x26
12#define INTEL_ATOM_MFLD 0x27
13#define INTEL_ATOM_CLV 0x35
14#define INTEL_ATOM_MRFLD 0x4a
15#define INTEL_ATOM_BYT 0x37
16#define INTEL_ATOM_MOORFLD 0x5a
17#define INTEL_ATOM_CHT 0x4c
18/* synchronization for sharing the I2C controller */
19#define PUNIT_PORT 0x04
20#define PUNIT_DOORBELL_OPCODE (0xE0)
21#define PUNIT_DOORBELL_REG (0x0)
22#ifndef CSTATE_EXIT_LATENCY
23#define CSTATE_EXIT_LATENCY_C1 1
24#endif
25static inline int platform_is(u8 model)
26{
b739d024 27 return (boot_cpu_data.x86_model == model);
a49d2536
AC
28}
29
25016567 30#include "../../include/asm/intel_mid_pcihelpers.h"
a49d2536
AC
31
32/* Unified message bus read/write operation */
33static DEFINE_SPINLOCK(msgbus_lock);
34
35static struct pci_dev *pci_root;
36static struct pm_qos_request pm_qos;
a49d2536
AC
37
38#define DW_I2C_NEED_QOS (platform_is(INTEL_ATOM_BYT))
39
40static int intel_mid_msgbus_init(void)
41{
42 pci_root = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
43 if (!pci_root) {
44 pr_err("%s: Error: msgbus PCI handle NULL\n", __func__);
45 return -ENODEV;
46 }
47
48 if (DW_I2C_NEED_QOS) {
49 pm_qos_add_request(&pm_qos,
50 PM_QOS_CPU_DMA_LATENCY,
51 PM_QOS_DEFAULT_VALUE);
52 }
53 return 0;
54}
55fs_initcall(intel_mid_msgbus_init);
56
57u32 intel_mid_msgbus_read32_raw(u32 cmd)
58{
59 unsigned long irq_flags;
60 u32 data;
61
62 spin_lock_irqsave(&msgbus_lock, irq_flags);
63 pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_CTRL_REG, cmd);
64 pci_read_config_dword(pci_root, PCI_ROOT_MSGBUS_DATA_REG, &data);
65 spin_unlock_irqrestore(&msgbus_lock, irq_flags);
66
67 return data;
68}
69EXPORT_SYMBOL(intel_mid_msgbus_read32_raw);
70
71/*
72 * GU: this function is only used by the VISA and 'VXD' drivers.
73 */
74u32 intel_mid_msgbus_read32_raw_ext(u32 cmd, u32 cmd_ext)
75{
76 unsigned long irq_flags;
77 u32 data;
78
79 spin_lock_irqsave(&msgbus_lock, irq_flags);
80 pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_CTRL_EXT_REG, cmd_ext);
81 pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_CTRL_REG, cmd);
82 pci_read_config_dword(pci_root, PCI_ROOT_MSGBUS_DATA_REG, &data);
83 spin_unlock_irqrestore(&msgbus_lock, irq_flags);
84
85 return data;
86}
87EXPORT_SYMBOL(intel_mid_msgbus_read32_raw_ext);
88
89void intel_mid_msgbus_write32_raw(u32 cmd, u32 data)
90{
91 unsigned long irq_flags;
92
93 spin_lock_irqsave(&msgbus_lock, irq_flags);
94 pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_DATA_REG, data);
95 pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_CTRL_REG, cmd);
96 spin_unlock_irqrestore(&msgbus_lock, irq_flags);
97}
98EXPORT_SYMBOL(intel_mid_msgbus_write32_raw);
99
100/*
101 * GU: this function is only used by the VISA and 'VXD' drivers.
102 */
103void intel_mid_msgbus_write32_raw_ext(u32 cmd, u32 cmd_ext, u32 data)
104{
105 unsigned long irq_flags;
106
107 spin_lock_irqsave(&msgbus_lock, irq_flags);
108 pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_DATA_REG, data);
109 pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_CTRL_EXT_REG, cmd_ext);
110 pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_CTRL_REG, cmd);
111 spin_unlock_irqrestore(&msgbus_lock, irq_flags);
112}
113EXPORT_SYMBOL(intel_mid_msgbus_write32_raw_ext);
114
115u32 intel_mid_msgbus_read32(u8 port, u32 addr)
116{
117 unsigned long irq_flags;
118 u32 data;
119 u32 cmd;
120 u32 cmdext;
121
122 cmd = (PCI_ROOT_MSGBUS_READ << 24) | (port << 16) |
123 ((addr & 0xff) << 8) | PCI_ROOT_MSGBUS_DWORD_ENABLE;
124 cmdext = addr & 0xffffff00;
125
126 spin_lock_irqsave(&msgbus_lock, irq_flags);
127
128 if (cmdext) {
129 /* This resets to 0 automatically, no need to write 0 */
130 pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_CTRL_EXT_REG,
131 cmdext);
132 }
133
134 pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_CTRL_REG, cmd);
135 pci_read_config_dword(pci_root, PCI_ROOT_MSGBUS_DATA_REG, &data);
136 spin_unlock_irqrestore(&msgbus_lock, irq_flags);
137
138 return data;
139}
a49d2536 140EXPORT_SYMBOL(intel_mid_msgbus_read32);
8c7e9a73 141
a49d2536
AC
142void intel_mid_msgbus_write32(u8 port, u32 addr, u32 data)
143{
144 unsigned long irq_flags;
145 u32 cmd;
146 u32 cmdext;
147
148 cmd = (PCI_ROOT_MSGBUS_WRITE << 24) | (port << 16) |
149 ((addr & 0xFF) << 8) | PCI_ROOT_MSGBUS_DWORD_ENABLE;
150 cmdext = addr & 0xffffff00;
151
152 spin_lock_irqsave(&msgbus_lock, irq_flags);
153 pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_DATA_REG, data);
154
155 if (cmdext) {
156 /* This resets to 0 automatically, no need to write 0 */
157 pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_CTRL_EXT_REG,
158 cmdext);
159 }
160
161 pci_write_config_dword(pci_root, PCI_ROOT_MSGBUS_CTRL_REG, cmd);
162 spin_unlock_irqrestore(&msgbus_lock, irq_flags);
163}
164EXPORT_SYMBOL(intel_mid_msgbus_write32);
165
166/* called only from where is later then fs_initcall */
167u32 intel_mid_soc_stepping(void)
168{
169 return pci_root->revision;
170}
171EXPORT_SYMBOL(intel_mid_soc_stepping);
172
173static bool is_south_complex_device(struct pci_dev *dev)
174{
8c7e9a73
AS
175 unsigned int base_class = dev->class >> 16;
176 unsigned int sub_class = (dev->class & SUB_CLASS_MASK) >> 8;
a49d2536
AC
177
178 /* other than camera, pci bridges and display,
179 * everything else are south complex devices.
180 */
181 if (((base_class == PCI_BASE_CLASS_MULTIMEDIA) &&
182 (sub_class == ISP_SUB_CLASS)) ||
183 (base_class == PCI_BASE_CLASS_BRIDGE) ||
184 ((base_class == PCI_BASE_CLASS_DISPLAY) && !sub_class))
185 return false;
186 else
187 return true;
188}
189
190/* In BYT platform, d3_delay for internal south complex devices,
191 * they are not subject to 10 ms d3 to d0 delay required by pci spec.
192 */
193static void pci_d3_delay_fixup(struct pci_dev *dev)
194{
195 if (platform_is(INTEL_ATOM_BYT) ||
196 platform_is(INTEL_ATOM_CHT)) {
197 /* All internal devices are in bus 0. */
198 if (dev->bus->number == 0 && is_south_complex_device(dev)) {
199 dev->d3_delay = INTERNAL_PCI_PM_D3_WAIT;
200 dev->d3cold_delay = INTERNAL_PCI_PM_D3_WAIT;
201 }
202 }
203}
204DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_d3_delay_fixup);
205
206#define PUNIT_SEMAPHORE (platform_is(INTEL_ATOM_BYT) ? 0x7 : 0x10E)
207#define GET_SEM() (intel_mid_msgbus_read32(PUNIT_PORT, PUNIT_SEMAPHORE) & 0x1)
208
209static void reset_semaphore(void)
210{
211 u32 data;
212
213 data = intel_mid_msgbus_read32(PUNIT_PORT, PUNIT_SEMAPHORE);
214 smp_mb();
215 data = data & 0xfffffffc;
216 intel_mid_msgbus_write32(PUNIT_PORT, PUNIT_SEMAPHORE, data);
217 smp_mb();
218
219}
220
221int intel_mid_dw_i2c_acquire_ownership(void)
222{
223 u32 ret = 0;
224 u32 data = 0; /* data sent to PUNIT */
225 u32 cmd;
226 u32 cmdext;
227 int timeout = 1000;
228
229 if (DW_I2C_NEED_QOS)
230 pm_qos_update_request(&pm_qos, CSTATE_EXIT_LATENCY_C1 - 1);
231
232 /*
233 * We need disable irq. Otherwise, the main thread
234 * might be preempted and the other thread jumps to
235 * disable irq for a long time. Another case is
236 * some irq handlers might trigger power voltage change
237 */
238 BUG_ON(irqs_disabled());
239 local_irq_disable();
240
241 /* host driver writes 0x2 to side band register 0x7 */
242 intel_mid_msgbus_write32(PUNIT_PORT, PUNIT_SEMAPHORE, 0x2);
243 smp_mb();
244
245 /* host driver sends 0xE0 opcode to PUNIT and writes 0 register */
246 cmd = (PUNIT_DOORBELL_OPCODE << 24) | (PUNIT_PORT << 16) |
247 ((PUNIT_DOORBELL_REG & 0xFF) << 8) | PCI_ROOT_MSGBUS_DWORD_ENABLE;
248 cmdext = PUNIT_DOORBELL_REG & 0xffffff00;
249
250 if (cmdext)
251 intel_mid_msgbus_write32_raw_ext(cmd, cmdext, data);
252 else
253 intel_mid_msgbus_write32_raw(cmd, data);
254
255 /* host driver waits for bit 0 to be set in side band 0x7 */
256 while (GET_SEM() != 0x1) {
257 udelay(100);
258 timeout--;
259 if (timeout <= 0) {
260 pr_err("Timeout: semaphore timed out, reset sem\n");
261 ret = -ETIMEDOUT;
262 reset_semaphore();
263 /*Delay 1ms in case race with punit*/
264 udelay(1000);
265 if (GET_SEM() != 0) {
266 /*Reset again as kernel might race with punit*/
267 reset_semaphore();
268 }
269 pr_err("PUNIT SEM: %d\n",
270 intel_mid_msgbus_read32(PUNIT_PORT,
271 PUNIT_SEMAPHORE));
272 local_irq_enable();
273
274 if (DW_I2C_NEED_QOS) {
275 pm_qos_update_request(&pm_qos,
276 PM_QOS_DEFAULT_VALUE);
277 }
278
279 return ret;
280 }
281 }
282 smp_mb();
283
284 return ret;
285}
286EXPORT_SYMBOL(intel_mid_dw_i2c_acquire_ownership);
287
288int intel_mid_dw_i2c_release_ownership(void)
289{
290 reset_semaphore();
291 local_irq_enable();
292
293 if (DW_I2C_NEED_QOS)
294 pm_qos_update_request(&pm_qos, PM_QOS_DEFAULT_VALUE);
295
296 return 0;
297}
298EXPORT_SYMBOL(intel_mid_dw_i2c_release_ownership);