Merge branch 'tunnels'
[linux.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");