Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1/*
  2 * Support for indirect PCI bridges.
  3 *
  4 * Copyright (C) 1998 Gabriel Paubert.
  5 *
  6 * This program is free software; you can redistribute it and/or
  7 * modify it under the terms of the GNU General Public License
  8 * as published by the Free Software Foundation; either version
  9 * 2 of the License, or (at your option) any later version.
 10 */
 11
 12#include <linux/kernel.h>
 13#include <linux/pci.h>
 14#include <linux/delay.h>
 15#include <linux/string.h>
 16#include <linux/init.h>
 17
 18#include <linux/io.h>
 19#include <asm/prom.h>
 20#include <asm/pci-bridge.h>
 21
 22static int
 23indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
 24		     int len, u32 *val)
 25{
 26	struct pci_controller *hose = pci_bus_to_host(bus);
 27	volatile void __iomem *cfg_data;
 28	u8 cfg_type = 0;
 29	u32 bus_no, reg;
 30
 31	if (hose->indirect_type & INDIRECT_TYPE_NO_PCIE_LINK) {
 32		if (bus->number != hose->first_busno)
 33			return PCIBIOS_DEVICE_NOT_FOUND;
 34		if (devfn != 0)
 35			return PCIBIOS_DEVICE_NOT_FOUND;
 36	}
 37
 38	if (hose->indirect_type & INDIRECT_TYPE_SET_CFG_TYPE)
 39		if (bus->number != hose->first_busno)
 40			cfg_type = 1;
 41
 42	bus_no = (bus->number == hose->first_busno) ?
 43			hose->self_busno : bus->number;
 44
 45	if (hose->indirect_type & INDIRECT_TYPE_EXT_REG)
 46		reg = ((offset & 0xf00) << 16) | (offset & 0xfc);
 47	else
 48		reg = offset & 0xfc; /* Only 3 bits for function */
 49
 50	if (hose->indirect_type & INDIRECT_TYPE_BIG_ENDIAN)
 51		out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
 52			 (devfn << 8) | reg | cfg_type));
 53	else
 54		out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
 55			 (devfn << 8) | reg | cfg_type));
 56
 57	/*
 58	 * Note: the caller has already checked that offset is
 59	 * suitably aligned and that len is 1, 2 or 4.
 60	 */
 61	cfg_data = hose->cfg_data + (offset & 3); /* Only 3 bits for function */
 62	switch (len) {
 63	case 1:
 64		*val = in_8(cfg_data);
 65		break;
 66	case 2:
 67		*val = in_le16(cfg_data);
 68		break;
 69	default:
 70		*val = in_le32(cfg_data);
 71		break;
 72	}
 73	return PCIBIOS_SUCCESSFUL;
 74}
 75
 76static int
 77indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset,
 78		      int len, u32 val)
 79{
 80	struct pci_controller *hose = pci_bus_to_host(bus);
 81	volatile void __iomem *cfg_data;
 82	u8 cfg_type = 0;
 83	u32 bus_no, reg;
 84
 85	if (hose->indirect_type & INDIRECT_TYPE_NO_PCIE_LINK) {
 86		if (bus->number != hose->first_busno)
 87			return PCIBIOS_DEVICE_NOT_FOUND;
 88		if (devfn != 0)
 89			return PCIBIOS_DEVICE_NOT_FOUND;
 90	}
 91
 92	if (hose->indirect_type & INDIRECT_TYPE_SET_CFG_TYPE)
 93		if (bus->number != hose->first_busno)
 94			cfg_type = 1;
 95
 96	bus_no = (bus->number == hose->first_busno) ?
 97			hose->self_busno : bus->number;
 98
 99	if (hose->indirect_type & INDIRECT_TYPE_EXT_REG)
100		reg = ((offset & 0xf00) << 16) | (offset & 0xfc);
101	else
102		reg = offset & 0xfc;
103
104	if (hose->indirect_type & INDIRECT_TYPE_BIG_ENDIAN)
105		out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
106			 (devfn << 8) | reg | cfg_type));
107	else
108		out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
109			 (devfn << 8) | reg | cfg_type));
110
111	/* suppress setting of PCI_PRIMARY_BUS */
112	if (hose->indirect_type & INDIRECT_TYPE_SURPRESS_PRIMARY_BUS)
113		if ((offset == PCI_PRIMARY_BUS) &&
114			(bus->number == hose->first_busno))
115			val &= 0xffffff00;
116
117	/* Workaround for PCI_28 Errata in 440EPx/GRx */
118	if ((hose->indirect_type & INDIRECT_TYPE_BROKEN_MRM) &&
119			offset == PCI_CACHE_LINE_SIZE) {
120		val = 0;
121	}
122
123	/*
124	 * Note: the caller has already checked that offset is
125	 * suitably aligned and that len is 1, 2 or 4.
126	 */
127	cfg_data = hose->cfg_data + (offset & 3);
128	switch (len) {
129	case 1:
130		out_8(cfg_data, val);
131		break;
132	case 2:
133		out_le16(cfg_data, val);
134		break;
135	default:
136		out_le32(cfg_data, val);
137		break;
138	}
139
140	return PCIBIOS_SUCCESSFUL;
141}
142
143static struct pci_ops indirect_pci_ops = {
144	.read = indirect_read_config,
145	.write = indirect_write_config,
146};
147
148void __init
149setup_indirect_pci(struct pci_controller *hose,
150		   resource_size_t cfg_addr,
151		   resource_size_t cfg_data, u32 flags)
152{
153	resource_size_t base = cfg_addr & PAGE_MASK;
154	void __iomem *mbase;
155
156	mbase = ioremap(base, PAGE_SIZE);
157	hose->cfg_addr = mbase + (cfg_addr & ~PAGE_MASK);
158	if ((cfg_data & PAGE_MASK) != base)
159		mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE);
160	hose->cfg_data = mbase + (cfg_data & ~PAGE_MASK);
161	hose->ops = &indirect_pci_ops;
162	hose->indirect_type = flags;
163}