diff options
Diffstat (limited to 'arch/mips/pci/pci-ath724x.c')
-rw-r--r-- | arch/mips/pci/pci-ath724x.c | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/arch/mips/pci/pci-ath724x.c b/arch/mips/pci/pci-ath724x.c new file mode 100644 index 000000000000..a4dd24a4130b --- /dev/null +++ b/arch/mips/pci/pci-ath724x.c | |||
@@ -0,0 +1,174 @@ | |||
1 | /* | ||
2 | * Atheros 724x PCI support | ||
3 | * | ||
4 | * Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License version 2 as published | ||
8 | * by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/pci.h> | ||
12 | #include <asm/mach-ath79/pci-ath724x.h> | ||
13 | |||
14 | #define reg_read(_phys) (*(unsigned int *) KSEG1ADDR(_phys)) | ||
15 | #define reg_write(_phys, _val) ((*(unsigned int *) KSEG1ADDR(_phys)) = (_val)) | ||
16 | |||
17 | #define ATH724X_PCI_DEV_BASE 0x14000000 | ||
18 | #define ATH724X_PCI_MEM_BASE 0x10000000 | ||
19 | #define ATH724X_PCI_MEM_SIZE 0x08000000 | ||
20 | |||
21 | static DEFINE_SPINLOCK(ath724x_pci_lock); | ||
22 | static struct ath724x_pci_data *pci_data; | ||
23 | static int pci_data_size; | ||
24 | |||
25 | static int ath724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where, | ||
26 | int size, uint32_t *value) | ||
27 | { | ||
28 | unsigned long flags, addr, tval, mask; | ||
29 | |||
30 | if (devfn) | ||
31 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
32 | |||
33 | if (where & (size - 1)) | ||
34 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
35 | |||
36 | spin_lock_irqsave(&ath724x_pci_lock, flags); | ||
37 | |||
38 | switch (size) { | ||
39 | case 1: | ||
40 | addr = where & ~3; | ||
41 | mask = 0xff000000 >> ((where % 4) * 8); | ||
42 | tval = reg_read(ATH724X_PCI_DEV_BASE + addr); | ||
43 | tval = tval & ~mask; | ||
44 | *value = (tval >> ((4 - (where % 4))*8)); | ||
45 | break; | ||
46 | case 2: | ||
47 | addr = where & ~3; | ||
48 | mask = 0xffff0000 >> ((where % 4)*8); | ||
49 | tval = reg_read(ATH724X_PCI_DEV_BASE + addr); | ||
50 | tval = tval & ~mask; | ||
51 | *value = (tval >> ((4 - (where % 4))*8)); | ||
52 | break; | ||
53 | case 4: | ||
54 | *value = reg_read(ATH724X_PCI_DEV_BASE + where); | ||
55 | break; | ||
56 | default: | ||
57 | spin_unlock_irqrestore(&ath724x_pci_lock, flags); | ||
58 | |||
59 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
60 | } | ||
61 | |||
62 | spin_unlock_irqrestore(&ath724x_pci_lock, flags); | ||
63 | |||
64 | return PCIBIOS_SUCCESSFUL; | ||
65 | } | ||
66 | |||
67 | static int ath724x_pci_write(struct pci_bus *bus, unsigned int devfn, int where, | ||
68 | int size, uint32_t value) | ||
69 | { | ||
70 | unsigned long flags, tval, addr, mask; | ||
71 | |||
72 | if (devfn) | ||
73 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
74 | |||
75 | if (where & (size - 1)) | ||
76 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
77 | |||
78 | spin_lock_irqsave(&ath724x_pci_lock, flags); | ||
79 | |||
80 | switch (size) { | ||
81 | case 1: | ||
82 | addr = (ATH724X_PCI_DEV_BASE + where) & ~3; | ||
83 | mask = 0xff000000 >> ((where % 4)*8); | ||
84 | tval = reg_read(addr); | ||
85 | tval = tval & ~mask; | ||
86 | tval |= (value << ((4 - (where % 4))*8)) & mask; | ||
87 | reg_write(addr, tval); | ||
88 | break; | ||
89 | case 2: | ||
90 | addr = (ATH724X_PCI_DEV_BASE + where) & ~3; | ||
91 | mask = 0xffff0000 >> ((where % 4)*8); | ||
92 | tval = reg_read(addr); | ||
93 | tval = tval & ~mask; | ||
94 | tval |= (value << ((4 - (where % 4))*8)) & mask; | ||
95 | reg_write(addr, tval); | ||
96 | break; | ||
97 | case 4: | ||
98 | reg_write((ATH724X_PCI_DEV_BASE + where), value); | ||
99 | break; | ||
100 | default: | ||
101 | spin_unlock_irqrestore(&ath724x_pci_lock, flags); | ||
102 | |||
103 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
104 | } | ||
105 | |||
106 | spin_unlock_irqrestore(&ath724x_pci_lock, flags); | ||
107 | |||
108 | return PCIBIOS_SUCCESSFUL; | ||
109 | } | ||
110 | |||
111 | static struct pci_ops ath724x_pci_ops = { | ||
112 | .read = ath724x_pci_read, | ||
113 | .write = ath724x_pci_write, | ||
114 | }; | ||
115 | |||
116 | static struct resource ath724x_io_resource = { | ||
117 | .name = "PCI IO space", | ||
118 | .start = 0, | ||
119 | .end = 0, | ||
120 | .flags = IORESOURCE_IO, | ||
121 | }; | ||
122 | |||
123 | static struct resource ath724x_mem_resource = { | ||
124 | .name = "PCI memory space", | ||
125 | .start = ATH724X_PCI_MEM_BASE, | ||
126 | .end = ATH724X_PCI_MEM_BASE + ATH724X_PCI_MEM_SIZE - 1, | ||
127 | .flags = IORESOURCE_MEM, | ||
128 | }; | ||
129 | |||
130 | static struct pci_controller ath724x_pci_controller = { | ||
131 | .pci_ops = &ath724x_pci_ops, | ||
132 | .io_resource = &ath724x_io_resource, | ||
133 | .mem_resource = &ath724x_mem_resource, | ||
134 | }; | ||
135 | |||
136 | void ath724x_pci_add_data(struct ath724x_pci_data *data, int size) | ||
137 | { | ||
138 | pci_data = data; | ||
139 | pci_data_size = size; | ||
140 | } | ||
141 | |||
142 | int __init pcibios_map_irq(const struct pci_dev *dev, uint8_t slot, uint8_t pin) | ||
143 | { | ||
144 | unsigned int devfn = dev->devfn; | ||
145 | int irq = -1; | ||
146 | |||
147 | if (devfn > pci_data_size - 1) | ||
148 | return irq; | ||
149 | |||
150 | irq = pci_data[devfn].irq; | ||
151 | |||
152 | return irq; | ||
153 | } | ||
154 | |||
155 | int pcibios_plat_dev_init(struct pci_dev *dev) | ||
156 | { | ||
157 | unsigned int devfn = dev->devfn; | ||
158 | |||
159 | if (devfn > pci_data_size - 1) | ||
160 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
161 | |||
162 | dev->dev.platform_data = pci_data[devfn].pdata; | ||
163 | |||
164 | return PCIBIOS_SUCCESSFUL; | ||
165 | } | ||
166 | |||
167 | static int __init ath724x_pcibios_init(void) | ||
168 | { | ||
169 | register_pci_controller(&ath724x_pci_controller); | ||
170 | |||
171 | return PCIBIOS_SUCCESSFUL; | ||
172 | } | ||
173 | |||
174 | arch_initcall(ath724x_pcibios_init); | ||