Linux Audio

Check our new training course

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