28fb290ca3250f1b6dfda9d9c484137511759d57
[firefly-linux-kernel-4.4.55.git] / arch / x86 / platform / intel / iosf_mbi.c
1 /*
2  * 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 access to this interface for those platforms that can
18  * enumerate the device using PCI.
19  */
20
21 #include <linux/module.h>
22 #include <linux/init.h>
23 #include <linux/spinlock.h>
24 #include <linux/pci.h>
25 #include <linux/debugfs.h>
26 #include <linux/capability.h>
27
28 #include <asm/iosf_mbi.h>
29
30 #define PCI_DEVICE_ID_BAYTRAIL          0x0F00
31 #define PCI_DEVICE_ID_BRASWELL          0x2280
32 #define PCI_DEVICE_ID_QUARK_X1000       0x0958
33
34 static struct pci_dev *mbi_pdev;
35 static DEFINE_SPINLOCK(iosf_mbi_lock);
36
37 static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset)
38 {
39         return (op << 24) | (port << 16) | (offset << 8) | MBI_ENABLE;
40 }
41
42 static int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr)
43 {
44         int result;
45
46         if (!mbi_pdev)
47                 return -ENODEV;
48
49         if (mcrx) {
50                 result = pci_write_config_dword(mbi_pdev, MBI_MCRX_OFFSET,
51                                                 mcrx);
52                 if (result < 0)
53                         goto fail_read;
54         }
55
56         result = pci_write_config_dword(mbi_pdev, MBI_MCR_OFFSET, mcr);
57         if (result < 0)
58                 goto fail_read;
59
60         result = pci_read_config_dword(mbi_pdev, MBI_MDR_OFFSET, mdr);
61         if (result < 0)
62                 goto fail_read;
63
64         return 0;
65
66 fail_read:
67         dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result);
68         return result;
69 }
70
71 static int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr)
72 {
73         int result;
74
75         if (!mbi_pdev)
76                 return -ENODEV;
77
78         result = pci_write_config_dword(mbi_pdev, MBI_MDR_OFFSET, mdr);
79         if (result < 0)
80                 goto fail_write;
81
82         if (mcrx) {
83                 result = pci_write_config_dword(mbi_pdev, MBI_MCRX_OFFSET,
84                                                 mcrx);
85                 if (result < 0)
86                         goto fail_write;
87         }
88
89         result = pci_write_config_dword(mbi_pdev, MBI_MCR_OFFSET, mcr);
90         if (result < 0)
91                 goto fail_write;
92
93         return 0;
94
95 fail_write:
96         dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result);
97         return result;
98 }
99
100 int iosf_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr)
101 {
102         u32 mcr, mcrx;
103         unsigned long flags;
104         int ret;
105
106         /* Access to the GFX unit is handled by GPU code */
107         if (port == BT_MBI_UNIT_GFX) {
108                 WARN_ON(1);
109                 return -EPERM;
110         }
111
112         mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
113         mcrx = offset & MBI_MASK_HI;
114
115         spin_lock_irqsave(&iosf_mbi_lock, flags);
116         ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr);
117         spin_unlock_irqrestore(&iosf_mbi_lock, flags);
118
119         return ret;
120 }
121 EXPORT_SYMBOL(iosf_mbi_read);
122
123 int iosf_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr)
124 {
125         u32 mcr, mcrx;
126         unsigned long flags;
127         int ret;
128
129         /* Access to the GFX unit is handled by GPU code */
130         if (port == BT_MBI_UNIT_GFX) {
131                 WARN_ON(1);
132                 return -EPERM;
133         }
134
135         mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
136         mcrx = offset & MBI_MASK_HI;
137
138         spin_lock_irqsave(&iosf_mbi_lock, flags);
139         ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr);
140         spin_unlock_irqrestore(&iosf_mbi_lock, flags);
141
142         return ret;
143 }
144 EXPORT_SYMBOL(iosf_mbi_write);
145
146 int iosf_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask)
147 {
148         u32 mcr, mcrx;
149         u32 value;
150         unsigned long flags;
151         int ret;
152
153         /* Access to the GFX unit is handled by GPU code */
154         if (port == BT_MBI_UNIT_GFX) {
155                 WARN_ON(1);
156                 return -EPERM;
157         }
158
159         mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
160         mcrx = offset & MBI_MASK_HI;
161
162         spin_lock_irqsave(&iosf_mbi_lock, flags);
163
164         /* Read current mdr value */
165         ret = iosf_mbi_pci_read_mdr(mcrx, mcr & MBI_RD_MASK, &value);
166         if (ret < 0) {
167                 spin_unlock_irqrestore(&iosf_mbi_lock, flags);
168                 return ret;
169         }
170
171         /* Apply mask */
172         value &= ~mask;
173         mdr &= mask;
174         value |= mdr;
175
176         /* Write back */
177         ret = iosf_mbi_pci_write_mdr(mcrx, mcr | MBI_WR_MASK, value);
178
179         spin_unlock_irqrestore(&iosf_mbi_lock, flags);
180
181         return ret;
182 }
183 EXPORT_SYMBOL(iosf_mbi_modify);
184
185 bool iosf_mbi_available(void)
186 {
187         /* Mbi isn't hot-pluggable. No remove routine is provided */
188         return mbi_pdev;
189 }
190 EXPORT_SYMBOL(iosf_mbi_available);
191
192 #ifdef CONFIG_IOSF_MBI_DEBUG
193 static u32      dbg_mdr;
194 static u32      dbg_mcr;
195 static u32      dbg_mcrx;
196
197 static int mcr_get(void *data, u64 *val)
198 {
199         *val = *(u32 *)data;
200         return 0;
201 }
202
203 static int mcr_set(void *data, u64 val)
204 {
205         u8 command = ((u32)val & 0xFF000000) >> 24,
206            port    = ((u32)val & 0x00FF0000) >> 16,
207            offset  = ((u32)val & 0x0000FF00) >> 8;
208         int err;
209
210         *(u32 *)data = val;
211
212         if (!capable(CAP_SYS_RAWIO))
213                 return -EACCES;
214
215         if (command & 1u)
216                 err = iosf_mbi_write(port,
217                                command,
218                                dbg_mcrx | offset,
219                                dbg_mdr);
220         else
221                 err = iosf_mbi_read(port,
222                               command,
223                               dbg_mcrx | offset,
224                               &dbg_mdr);
225
226         return err;
227 }
228 DEFINE_SIMPLE_ATTRIBUTE(iosf_mcr_fops, mcr_get, mcr_set , "%llx\n");
229
230 static struct dentry *iosf_dbg;
231
232 static void iosf_sideband_debug_init(void)
233 {
234         struct dentry *d;
235
236         iosf_dbg = debugfs_create_dir("iosf_sb", NULL);
237         if (IS_ERR_OR_NULL(iosf_dbg))
238                 return;
239
240         /* mdr */
241         d = debugfs_create_x32("mdr", 0660, iosf_dbg, &dbg_mdr);
242         if (!d)
243                 goto cleanup;
244
245         /* mcrx */
246         d = debugfs_create_x32("mcrx", 0660, iosf_dbg, &dbg_mcrx);
247         if (!d)
248                 goto cleanup;
249
250         /* mcr - initiates mailbox tranaction */
251         d = debugfs_create_file("mcr", 0660, iosf_dbg, &dbg_mcr, &iosf_mcr_fops);
252         if (!d)
253                 goto cleanup;
254
255         return;
256
257 cleanup:
258         debugfs_remove_recursive(d);
259 }
260
261 static void iosf_debugfs_init(void)
262 {
263         iosf_sideband_debug_init();
264 }
265
266 static void iosf_debugfs_remove(void)
267 {
268         debugfs_remove_recursive(iosf_dbg);
269 }
270 #else
271 static inline void iosf_debugfs_init(void) { }
272 static inline void iosf_debugfs_remove(void) { }
273 #endif /* CONFIG_IOSF_MBI_DEBUG */
274
275 static int iosf_mbi_probe(struct pci_dev *pdev,
276                           const struct pci_device_id *unused)
277 {
278         int ret;
279
280         ret = pci_enable_device(pdev);
281         if (ret < 0) {
282                 dev_err(&pdev->dev, "error: could not enable device\n");
283                 return ret;
284         }
285
286         mbi_pdev = pci_dev_get(pdev);
287         return 0;
288 }
289
290 static const struct pci_device_id iosf_mbi_pci_ids[] = {
291         { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_BAYTRAIL) },
292         { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_BRASWELL) },
293         { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_QUARK_X1000) },
294         { 0, },
295 };
296 MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids);
297
298 static struct pci_driver iosf_mbi_pci_driver = {
299         .name           = "iosf_mbi_pci",
300         .probe          = iosf_mbi_probe,
301         .id_table       = iosf_mbi_pci_ids,
302 };
303
304 static int __init iosf_mbi_init(void)
305 {
306         iosf_debugfs_init();
307
308         return pci_register_driver(&iosf_mbi_pci_driver);
309 }
310
311 static void __exit iosf_mbi_exit(void)
312 {
313         iosf_debugfs_remove();
314
315         pci_unregister_driver(&iosf_mbi_pci_driver);
316         pci_dev_put(mbi_pdev);
317         mbi_pdev = NULL;
318 }
319
320 module_init(iosf_mbi_init);
321 module_exit(iosf_mbi_exit);
322
323 MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
324 MODULE_DESCRIPTION("IOSF Mailbox Interface accessor");
325 MODULE_LICENSE("GPL v2");