PageRenderTime 76ms CodeModel.GetById 46ms app.highlight 28ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/xen/xen-pciback/conf_space_header.c

https://bitbucket.org/cyanogenmod/android_kernel_asus_tf300t
C | 386 lines | 307 code | 60 blank | 19 comment | 53 complexity | f8317111bbbd94f7c58b4736df284f67 MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
  1/*
  2 * PCI Backend - Handles the virtual fields in the configuration space headers.
  3 *
  4 * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
  5 */
  6
  7#include <linux/kernel.h>
  8#include <linux/pci.h>
  9#include "pciback.h"
 10#include "conf_space.h"
 11
 12struct pci_bar_info {
 13	u32 val;
 14	u32 len_val;
 15	int which;
 16};
 17
 18#define DRV_NAME	"xen-pciback"
 19#define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO))
 20#define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER)
 21
 22static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data)
 23{
 24	int i;
 25	int ret;
 26
 27	ret = xen_pcibk_read_config_word(dev, offset, value, data);
 28	if (!atomic_read(&dev->enable_cnt))
 29		return ret;
 30
 31	for (i = 0; i < PCI_ROM_RESOURCE; i++) {
 32		if (dev->resource[i].flags & IORESOURCE_IO)
 33			*value |= PCI_COMMAND_IO;
 34		if (dev->resource[i].flags & IORESOURCE_MEM)
 35			*value |= PCI_COMMAND_MEMORY;
 36	}
 37
 38	return ret;
 39}
 40
 41static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
 42{
 43	struct xen_pcibk_dev_data *dev_data;
 44	int err;
 45
 46	dev_data = pci_get_drvdata(dev);
 47	if (!pci_is_enabled(dev) && is_enable_cmd(value)) {
 48		if (unlikely(verbose_request))
 49			printk(KERN_DEBUG DRV_NAME ": %s: enable\n",
 50			       pci_name(dev));
 51		err = pci_enable_device(dev);
 52		if (err)
 53			return err;
 54		if (dev_data)
 55			dev_data->enable_intx = 1;
 56	} else if (pci_is_enabled(dev) && !is_enable_cmd(value)) {
 57		if (unlikely(verbose_request))
 58			printk(KERN_DEBUG DRV_NAME ": %s: disable\n",
 59			       pci_name(dev));
 60		pci_disable_device(dev);
 61		if (dev_data)
 62			dev_data->enable_intx = 0;
 63	}
 64
 65	if (!dev->is_busmaster && is_master_cmd(value)) {
 66		if (unlikely(verbose_request))
 67			printk(KERN_DEBUG DRV_NAME ": %s: set bus master\n",
 68			       pci_name(dev));
 69		pci_set_master(dev);
 70	}
 71
 72	if (value & PCI_COMMAND_INVALIDATE) {
 73		if (unlikely(verbose_request))
 74			printk(KERN_DEBUG
 75			       DRV_NAME ": %s: enable memory-write-invalidate\n",
 76			       pci_name(dev));
 77		err = pci_set_mwi(dev);
 78		if (err) {
 79			printk(KERN_WARNING
 80			       DRV_NAME ": %s: cannot enable "
 81			       "memory-write-invalidate (%d)\n",
 82			       pci_name(dev), err);
 83			value &= ~PCI_COMMAND_INVALIDATE;
 84		}
 85	}
 86
 87	return pci_write_config_word(dev, offset, value);
 88}
 89
 90static int rom_write(struct pci_dev *dev, int offset, u32 value, void *data)
 91{
 92	struct pci_bar_info *bar = data;
 93
 94	if (unlikely(!bar)) {
 95		printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n",
 96		       pci_name(dev));
 97		return XEN_PCI_ERR_op_failed;
 98	}
 99
100	/* A write to obtain the length must happen as a 32-bit write.
101	 * This does not (yet) support writing individual bytes
102	 */
103	if (value == ~PCI_ROM_ADDRESS_ENABLE)
104		bar->which = 1;
105	else {
106		u32 tmpval;
107		pci_read_config_dword(dev, offset, &tmpval);
108		if (tmpval != bar->val && value == bar->val) {
109			/* Allow restoration of bar value. */
110			pci_write_config_dword(dev, offset, bar->val);
111		}
112		bar->which = 0;
113	}
114
115	/* Do we need to support enabling/disabling the rom address here? */
116
117	return 0;
118}
119
120/* For the BARs, only allow writes which write ~0 or
121 * the correct resource information
122 * (Needed for when the driver probes the resource usage)
123 */
124static int bar_write(struct pci_dev *dev, int offset, u32 value, void *data)
125{
126	struct pci_bar_info *bar = data;
127
128	if (unlikely(!bar)) {
129		printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n",
130		       pci_name(dev));
131		return XEN_PCI_ERR_op_failed;
132	}
133
134	/* A write to obtain the length must happen as a 32-bit write.
135	 * This does not (yet) support writing individual bytes
136	 */
137	if (value == ~0)
138		bar->which = 1;
139	else {
140		u32 tmpval;
141		pci_read_config_dword(dev, offset, &tmpval);
142		if (tmpval != bar->val && value == bar->val) {
143			/* Allow restoration of bar value. */
144			pci_write_config_dword(dev, offset, bar->val);
145		}
146		bar->which = 0;
147	}
148
149	return 0;
150}
151
152static int bar_read(struct pci_dev *dev, int offset, u32 * value, void *data)
153{
154	struct pci_bar_info *bar = data;
155
156	if (unlikely(!bar)) {
157		printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n",
158		       pci_name(dev));
159		return XEN_PCI_ERR_op_failed;
160	}
161
162	*value = bar->which ? bar->len_val : bar->val;
163
164	return 0;
165}
166
167static inline void read_dev_bar(struct pci_dev *dev,
168				struct pci_bar_info *bar_info, int offset,
169				u32 len_mask)
170{
171	int	pos;
172	struct resource	*res = dev->resource;
173
174	if (offset == PCI_ROM_ADDRESS || offset == PCI_ROM_ADDRESS1)
175		pos = PCI_ROM_RESOURCE;
176	else {
177		pos = (offset - PCI_BASE_ADDRESS_0) / 4;
178		if (pos && ((res[pos - 1].flags & (PCI_BASE_ADDRESS_SPACE |
179				PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
180			   (PCI_BASE_ADDRESS_SPACE_MEMORY |
181				PCI_BASE_ADDRESS_MEM_TYPE_64))) {
182			bar_info->val = res[pos - 1].start >> 32;
183			bar_info->len_val = res[pos - 1].end >> 32;
184			return;
185		}
186	}
187
188	bar_info->val = res[pos].start |
189			(res[pos].flags & PCI_REGION_FLAG_MASK);
190	bar_info->len_val = res[pos].end - res[pos].start + 1;
191}
192
193static void *bar_init(struct pci_dev *dev, int offset)
194{
195	struct pci_bar_info *bar = kmalloc(sizeof(*bar), GFP_KERNEL);
196
197	if (!bar)
198		return ERR_PTR(-ENOMEM);
199
200	read_dev_bar(dev, bar, offset, ~0);
201	bar->which = 0;
202
203	return bar;
204}
205
206static void *rom_init(struct pci_dev *dev, int offset)
207{
208	struct pci_bar_info *bar = kmalloc(sizeof(*bar), GFP_KERNEL);
209
210	if (!bar)
211		return ERR_PTR(-ENOMEM);
212
213	read_dev_bar(dev, bar, offset, ~PCI_ROM_ADDRESS_ENABLE);
214	bar->which = 0;
215
216	return bar;
217}
218
219static void bar_reset(struct pci_dev *dev, int offset, void *data)
220{
221	struct pci_bar_info *bar = data;
222
223	bar->which = 0;
224}
225
226static void bar_release(struct pci_dev *dev, int offset, void *data)
227{
228	kfree(data);
229}
230
231static int xen_pcibk_read_vendor(struct pci_dev *dev, int offset,
232			       u16 *value, void *data)
233{
234	*value = dev->vendor;
235
236	return 0;
237}
238
239static int xen_pcibk_read_device(struct pci_dev *dev, int offset,
240			       u16 *value, void *data)
241{
242	*value = dev->device;
243
244	return 0;
245}
246
247static int interrupt_read(struct pci_dev *dev, int offset, u8 * value,
248			  void *data)
249{
250	*value = (u8) dev->irq;
251
252	return 0;
253}
254
255static int bist_write(struct pci_dev *dev, int offset, u8 value, void *data)
256{
257	u8 cur_value;
258	int err;
259
260	err = pci_read_config_byte(dev, offset, &cur_value);
261	if (err)
262		goto out;
263
264	if ((cur_value & ~PCI_BIST_START) == (value & ~PCI_BIST_START)
265	    || value == PCI_BIST_START)
266		err = pci_write_config_byte(dev, offset, value);
267
268out:
269	return err;
270}
271
272static const struct config_field header_common[] = {
273	{
274	 .offset    = PCI_VENDOR_ID,
275	 .size      = 2,
276	 .u.w.read  = xen_pcibk_read_vendor,
277	},
278	{
279	 .offset    = PCI_DEVICE_ID,
280	 .size      = 2,
281	 .u.w.read  = xen_pcibk_read_device,
282	},
283	{
284	 .offset    = PCI_COMMAND,
285	 .size      = 2,
286	 .u.w.read  = command_read,
287	 .u.w.write = command_write,
288	},
289	{
290	 .offset    = PCI_INTERRUPT_LINE,
291	 .size      = 1,
292	 .u.b.read  = interrupt_read,
293	},
294	{
295	 .offset    = PCI_INTERRUPT_PIN,
296	 .size      = 1,
297	 .u.b.read  = xen_pcibk_read_config_byte,
298	},
299	{
300	 /* Any side effects of letting driver domain control cache line? */
301	 .offset    = PCI_CACHE_LINE_SIZE,
302	 .size      = 1,
303	 .u.b.read  = xen_pcibk_read_config_byte,
304	 .u.b.write = xen_pcibk_write_config_byte,
305	},
306	{
307	 .offset    = PCI_LATENCY_TIMER,
308	 .size      = 1,
309	 .u.b.read  = xen_pcibk_read_config_byte,
310	},
311	{
312	 .offset    = PCI_BIST,
313	 .size      = 1,
314	 .u.b.read  = xen_pcibk_read_config_byte,
315	 .u.b.write = bist_write,
316	},
317	{}
318};
319
320#define CFG_FIELD_BAR(reg_offset)			\
321	{						\
322	.offset     = reg_offset,			\
323	.size       = 4,				\
324	.init       = bar_init,				\
325	.reset      = bar_reset,			\
326	.release    = bar_release,			\
327	.u.dw.read  = bar_read,				\
328	.u.dw.write = bar_write,			\
329	}
330
331#define CFG_FIELD_ROM(reg_offset)			\
332	{						\
333	.offset     = reg_offset,			\
334	.size       = 4,				\
335	.init       = rom_init,				\
336	.reset      = bar_reset,			\
337	.release    = bar_release,			\
338	.u.dw.read  = bar_read,				\
339	.u.dw.write = rom_write,			\
340	}
341
342static const struct config_field header_0[] = {
343	CFG_FIELD_BAR(PCI_BASE_ADDRESS_0),
344	CFG_FIELD_BAR(PCI_BASE_ADDRESS_1),
345	CFG_FIELD_BAR(PCI_BASE_ADDRESS_2),
346	CFG_FIELD_BAR(PCI_BASE_ADDRESS_3),
347	CFG_FIELD_BAR(PCI_BASE_ADDRESS_4),
348	CFG_FIELD_BAR(PCI_BASE_ADDRESS_5),
349	CFG_FIELD_ROM(PCI_ROM_ADDRESS),
350	{}
351};
352
353static const struct config_field header_1[] = {
354	CFG_FIELD_BAR(PCI_BASE_ADDRESS_0),
355	CFG_FIELD_BAR(PCI_BASE_ADDRESS_1),
356	CFG_FIELD_ROM(PCI_ROM_ADDRESS1),
357	{}
358};
359
360int xen_pcibk_config_header_add_fields(struct pci_dev *dev)
361{
362	int err;
363
364	err = xen_pcibk_config_add_fields(dev, header_common);
365	if (err)
366		goto out;
367
368	switch (dev->hdr_type) {
369	case PCI_HEADER_TYPE_NORMAL:
370		err = xen_pcibk_config_add_fields(dev, header_0);
371		break;
372
373	case PCI_HEADER_TYPE_BRIDGE:
374		err = xen_pcibk_config_add_fields(dev, header_1);
375		break;
376
377	default:
378		err = -EINVAL;
379		printk(KERN_ERR DRV_NAME ": %s: Unsupported header type %d!\n",
380		       pci_name(dev), dev->hdr_type);
381		break;
382	}
383
384out:
385	return err;
386}