]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blob - drivers/platform/x86/intel_baytrail.c
Merge branch 'acpi-config'
[mirror_ubuntu-hirsute-kernel.git] / drivers / platform / x86 / intel_baytrail.c
1 /*
2 * Baytrail IOSF-SB MailBox Interface Driver
3 * Copyright (c) 2013, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 *
15 * The IOSF-SB is a fabric bus available on Atom based SOC's that uses a
16 * mailbox interface (MBI) to communicate with mutiple devices. This
17 * driver implements BayTrail-specific access to this interface.
18 */
19
20 #include <linux/module.h>
21 #include <linux/init.h>
22 #include <linux/spinlock.h>
23 #include <linux/pci.h>
24
25 #include "intel_baytrail.h"
26
27 static DEFINE_SPINLOCK(iosf_mbi_lock);
28
29 static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset)
30 {
31 return (op << 24) | (port << 16) | (offset << 8) | BT_MBI_ENABLE;
32 }
33
34 static struct pci_dev *mbi_pdev; /* one mbi device */
35
36 /* Hold lock before calling */
37 static int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr)
38 {
39 int result;
40
41 if (!mbi_pdev)
42 return -ENODEV;
43
44 if (mcrx) {
45 result = pci_write_config_dword(mbi_pdev,
46 BT_MBI_MCRX_OFFSET, mcrx);
47 if (result < 0)
48 goto iosf_mbi_read_err;
49 }
50
51 result = pci_write_config_dword(mbi_pdev,
52 BT_MBI_MCR_OFFSET, mcr);
53 if (result < 0)
54 goto iosf_mbi_read_err;
55
56 result = pci_read_config_dword(mbi_pdev,
57 BT_MBI_MDR_OFFSET, mdr);
58 if (result < 0)
59 goto iosf_mbi_read_err;
60
61 return 0;
62
63 iosf_mbi_read_err:
64 dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n",
65 result);
66 return result;
67 }
68
69 /* Hold lock before calling */
70 static int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr)
71 {
72 int result;
73
74 if (!mbi_pdev)
75 return -ENODEV;
76
77 result = pci_write_config_dword(mbi_pdev,
78 BT_MBI_MDR_OFFSET, mdr);
79 if (result < 0)
80 goto iosf_mbi_write_err;
81
82 if (mcrx) {
83 result = pci_write_config_dword(mbi_pdev,
84 BT_MBI_MCRX_OFFSET, mcrx);
85 if (result < 0)
86 goto iosf_mbi_write_err;
87 }
88
89 result = pci_write_config_dword(mbi_pdev,
90 BT_MBI_MCR_OFFSET, mcr);
91 if (result < 0)
92 goto iosf_mbi_write_err;
93
94 return 0;
95
96 iosf_mbi_write_err:
97 dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n",
98 result);
99 return result;
100 }
101
102 int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr)
103 {
104 u32 mcr, mcrx;
105 unsigned long flags;
106 int ret;
107
108 /*Access to the GFX unit is handled by GPU code */
109 BUG_ON(port == BT_MBI_UNIT_GFX);
110
111 mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
112 mcrx = offset & BT_MBI_MASK_HI;
113
114 spin_lock_irqsave(&iosf_mbi_lock, flags);
115 ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr);
116 spin_unlock_irqrestore(&iosf_mbi_lock, flags);
117
118 return ret;
119 }
120 EXPORT_SYMBOL(bt_mbi_read);
121
122 int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr)
123 {
124 u32 mcr, mcrx;
125 unsigned long flags;
126 int ret;
127
128 /*Access to the GFX unit is handled by GPU code */
129 BUG_ON(port == BT_MBI_UNIT_GFX);
130
131 mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
132 mcrx = offset & BT_MBI_MASK_HI;
133
134 spin_lock_irqsave(&iosf_mbi_lock, flags);
135 ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr);
136 spin_unlock_irqrestore(&iosf_mbi_lock, flags);
137
138 return ret;
139 }
140 EXPORT_SYMBOL(bt_mbi_write);
141
142 int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask)
143 {
144 u32 mcr, mcrx;
145 u32 value;
146 unsigned long flags;
147 int ret;
148
149 /*Access to the GFX unit is handled by GPU code */
150 BUG_ON(port == BT_MBI_UNIT_GFX);
151
152 mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
153 mcrx = offset & BT_MBI_MASK_HI;
154
155 spin_lock_irqsave(&iosf_mbi_lock, flags);
156
157 /* Read current mdr value */
158 ret = iosf_mbi_pci_read_mdr(mcrx, mcr & BT_MBI_RD_MASK, &value);
159 if (ret < 0) {
160 spin_unlock_irqrestore(&iosf_mbi_lock, flags);
161 return ret;
162 }
163
164 /* Apply mask */
165 value &= ~mask;
166 mdr &= mask;
167 value |= mdr;
168
169 /* Write back */
170 ret = iosf_mbi_pci_write_mdr(mcrx, mcr | BT_MBI_WR_MASK, value);
171
172 spin_unlock_irqrestore(&iosf_mbi_lock, flags);
173
174 return ret;
175 }
176 EXPORT_SYMBOL(bt_mbi_modify);
177
178 static int iosf_mbi_probe(struct pci_dev *pdev,
179 const struct pci_device_id *unused)
180 {
181 int ret;
182
183 ret = pci_enable_device(pdev);
184 if (ret < 0) {
185 dev_err(&pdev->dev, "error: could not enable device\n");
186 return ret;
187 }
188
189 mbi_pdev = pci_dev_get(pdev);
190 return 0;
191 }
192
193 static DEFINE_PCI_DEVICE_TABLE(iosf_mbi_pci_ids) = {
194 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F00) },
195 { 0, },
196 };
197 MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids);
198
199 static struct pci_driver iosf_mbi_pci_driver = {
200 .name = "iosf_mbi_pci",
201 .probe = iosf_mbi_probe,
202 .id_table = iosf_mbi_pci_ids,
203 };
204
205 static int __init bt_mbi_init(void)
206 {
207 return pci_register_driver(&iosf_mbi_pci_driver);
208 }
209
210 static void __exit bt_mbi_exit(void)
211 {
212 pci_unregister_driver(&iosf_mbi_pci_driver);
213 if (mbi_pdev) {
214 pci_dev_put(mbi_pdev);
215 mbi_pdev = NULL;
216 }
217 }
218
219 module_init(bt_mbi_init);
220 module_exit(bt_mbi_exit);
221
222 MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
223 MODULE_DESCRIPTION("BayTrail Mailbox Interface accessor");
224 MODULE_LICENSE("GPL v2");