Merge branch 'for-3.17/core' of git://git.kernel.dk/linux-block
[deliverable/linux.git] / arch / x86 / kernel / iosf_mbi.c
CommitLineData
46184415
DB
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
04725ad5
OBL
28#define PCI_DEVICE_ID_BAYTRAIL 0x0F00
29#define PCI_DEVICE_ID_QUARK_X1000 0x0958
30
46184415
DB
31static DEFINE_SPINLOCK(iosf_mbi_lock);
32
33static 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
38static struct pci_dev *mbi_pdev; /* one mbi device */
39
40static 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
64fail_read:
65 dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result);
66 return result;
67}
68
69static 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
93fail_write:
94 dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result);
95 return result;
96}
97
98int 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}
119EXPORT_SYMBOL(iosf_mbi_read);
120
121int 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}
142EXPORT_SYMBOL(iosf_mbi_write);
143
144int 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}
181EXPORT_SYMBOL(iosf_mbi_modify);
182
6b8f0c87
DB
183bool iosf_mbi_available(void)
184{
185 /* Mbi isn't hot-pluggable. No remove routine is provided */
186 return mbi_pdev;
187}
188EXPORT_SYMBOL(iosf_mbi_available);
189
46184415
DB
190static 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
205static DEFINE_PCI_DEVICE_TABLE(iosf_mbi_pci_ids) = {
04725ad5
OBL
206 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_BAYTRAIL) },
207 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_QUARK_X1000) },
46184415
DB
208 { 0, },
209};
210MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids);
211
212static 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
218static int __init iosf_mbi_init(void)
219{
220 return pci_register_driver(&iosf_mbi_pci_driver);
221}
222
223static 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
232module_init(iosf_mbi_init);
233module_exit(iosf_mbi_exit);
234
235MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
236MODULE_DESCRIPTION("IOSF Mailbox Interface accessor");
237MODULE_LICENSE("GPL v2");
This page took 0.065677 seconds and 5 git commands to generate.