Merge branch 'tda998x-fixes' of git://ftp.arm.linux.org.uk/~rmk/linux-cubox
[cascardo/linux.git] / arch / x86 / kernel / 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
26 #include <asm/iosf_mbi.h>
27
28 #define PCI_DEVICE_ID_BAYTRAIL          0x0F00
29 #define PCI_DEVICE_ID_QUARK_X1000       0x0958
30
31 static DEFINE_SPINLOCK(iosf_mbi_lock);
32
33 static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset)
34 {
35         return (op << 24) | (port << 16) | (offset << 8) | MBI_ENABLE;
36 }
37
38 static struct pci_dev *mbi_pdev;        /* one mbi device */
39
40 static int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr)
41 {
42         int result;
43
44         if (!mbi_pdev)
45                 return -ENODEV;
46
47         if (mcrx) {
48                 result = pci_write_config_dword(mbi_pdev, MBI_MCRX_OFFSET,
49                                                 mcrx);
50                 if (result < 0)
51                         goto fail_read;
52         }
53
54         result = pci_write_config_dword(mbi_pdev, MBI_MCR_OFFSET, mcr);
55         if (result < 0)
56                 goto fail_read;
57
58         result = pci_read_config_dword(mbi_pdev, MBI_MDR_OFFSET, mdr);
59         if (result < 0)
60                 goto fail_read;
61
62         return 0;
63
64 fail_read:
65         dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result);
66         return result;
67 }
68
69 static int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr)
70 {
71         int result;
72
73         if (!mbi_pdev)
74                 return -ENODEV;
75
76         result = pci_write_config_dword(mbi_pdev, MBI_MDR_OFFSET, mdr);
77         if (result < 0)
78                 goto fail_write;
79
80         if (mcrx) {
81                 result = pci_write_config_dword(mbi_pdev, MBI_MCRX_OFFSET,
82                                                 mcrx);
83                 if (result < 0)
84                         goto fail_write;
85         }
86
87         result = pci_write_config_dword(mbi_pdev, MBI_MCR_OFFSET, mcr);
88         if (result < 0)
89                 goto fail_write;
90
91         return 0;
92
93 fail_write:
94         dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result);
95         return result;
96 }
97
98 int iosf_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr)
99 {
100         u32 mcr, mcrx;
101         unsigned long flags;
102         int ret;
103
104         /*Access to the GFX unit is handled by GPU code */
105         if (port == BT_MBI_UNIT_GFX) {
106                 WARN_ON(1);
107                 return -EPERM;
108         }
109
110         mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
111         mcrx = offset & MBI_MASK_HI;
112
113         spin_lock_irqsave(&iosf_mbi_lock, flags);
114         ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr);
115         spin_unlock_irqrestore(&iosf_mbi_lock, flags);
116
117         return ret;
118 }
119 EXPORT_SYMBOL(iosf_mbi_read);
120
121 int iosf_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr)
122 {
123         u32 mcr, mcrx;
124         unsigned long flags;
125         int ret;
126
127         /*Access to the GFX unit is handled by GPU code */
128         if (port == BT_MBI_UNIT_GFX) {
129                 WARN_ON(1);
130                 return -EPERM;
131         }
132
133         mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
134         mcrx = offset & MBI_MASK_HI;
135
136         spin_lock_irqsave(&iosf_mbi_lock, flags);
137         ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr);
138         spin_unlock_irqrestore(&iosf_mbi_lock, flags);
139
140         return ret;
141 }
142 EXPORT_SYMBOL(iosf_mbi_write);
143
144 int iosf_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask)
145 {
146         u32 mcr, mcrx;
147         u32 value;
148         unsigned long flags;
149         int ret;
150
151         /*Access to the GFX unit is handled by GPU code */
152         if (port == BT_MBI_UNIT_GFX) {
153                 WARN_ON(1);
154                 return -EPERM;
155         }
156
157         mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
158         mcrx = offset & MBI_MASK_HI;
159
160         spin_lock_irqsave(&iosf_mbi_lock, flags);
161
162         /* Read current mdr value */
163         ret = iosf_mbi_pci_read_mdr(mcrx, mcr & MBI_RD_MASK, &value);
164         if (ret < 0) {
165                 spin_unlock_irqrestore(&iosf_mbi_lock, flags);
166                 return ret;
167         }
168
169         /* Apply mask */
170         value &= ~mask;
171         mdr &= mask;
172         value |= mdr;
173
174         /* Write back */
175         ret = iosf_mbi_pci_write_mdr(mcrx, mcr | MBI_WR_MASK, value);
176
177         spin_unlock_irqrestore(&iosf_mbi_lock, flags);
178
179         return ret;
180 }
181 EXPORT_SYMBOL(iosf_mbi_modify);
182
183 bool iosf_mbi_available(void)
184 {
185         /* Mbi isn't hot-pluggable. No remove routine is provided */
186         return mbi_pdev;
187 }
188 EXPORT_SYMBOL(iosf_mbi_available);
189
190 static int iosf_mbi_probe(struct pci_dev *pdev,
191                           const struct pci_device_id *unused)
192 {
193         int ret;
194
195         ret = pci_enable_device(pdev);
196         if (ret < 0) {
197                 dev_err(&pdev->dev, "error: could not enable device\n");
198                 return ret;
199         }
200
201         mbi_pdev = pci_dev_get(pdev);
202         return 0;
203 }
204
205 static DEFINE_PCI_DEVICE_TABLE(iosf_mbi_pci_ids) = {
206         { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_BAYTRAIL) },
207         { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_QUARK_X1000) },
208         { 0, },
209 };
210 MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids);
211
212 static struct pci_driver iosf_mbi_pci_driver = {
213         .name           = "iosf_mbi_pci",
214         .probe          = iosf_mbi_probe,
215         .id_table       = iosf_mbi_pci_ids,
216 };
217
218 static int __init iosf_mbi_init(void)
219 {
220         return pci_register_driver(&iosf_mbi_pci_driver);
221 }
222
223 static void __exit iosf_mbi_exit(void)
224 {
225         pci_unregister_driver(&iosf_mbi_pci_driver);
226         if (mbi_pdev) {
227                 pci_dev_put(mbi_pdev);
228                 mbi_pdev = NULL;
229         }
230 }
231
232 module_init(iosf_mbi_init);
233 module_exit(iosf_mbi_exit);
234
235 MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
236 MODULE_DESCRIPTION("IOSF Mailbox Interface accessor");
237 MODULE_LICENSE("GPL v2");