diff options
Diffstat (limited to 'arch/sh/drivers/pci/ops-sh7786.c')
-rw-r--r-- | arch/sh/drivers/pci/ops-sh7786.c | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/arch/sh/drivers/pci/ops-sh7786.c b/arch/sh/drivers/pci/ops-sh7786.c new file mode 100644 index 000000000000..48f594b9582b --- /dev/null +++ b/arch/sh/drivers/pci/ops-sh7786.c | |||
@@ -0,0 +1,134 @@ | |||
1 | /* | ||
2 | * Generic SH7786 PCI-Express operations. | ||
3 | * | ||
4 | * Copyright (C) 2009 Paul Mundt | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License v2. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/pci.h> | ||
13 | #include <linux/io.h> | ||
14 | #include <linux/spinlock.h> | ||
15 | #include "pcie-sh7786.h" | ||
16 | |||
17 | enum { | ||
18 | PCI_ACCESS_READ, | ||
19 | PCI_ACCESS_WRITE, | ||
20 | }; | ||
21 | |||
22 | static DEFINE_SPINLOCK(sh7786_pcie_lock); | ||
23 | |||
24 | static int sh7786_pcie_config_access(unsigned char access_type, | ||
25 | struct pci_bus *bus, unsigned int devfn, int where, u32 *data) | ||
26 | { | ||
27 | struct pci_channel *chan = bus->sysdata; | ||
28 | int dev, func; | ||
29 | |||
30 | dev = PCI_SLOT(devfn); | ||
31 | func = PCI_FUNC(devfn); | ||
32 | |||
33 | if (bus->number > 255 || dev > 31 || func > 7) | ||
34 | return PCIBIOS_FUNC_NOT_SUPPORTED; | ||
35 | if (devfn) | ||
36 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
37 | |||
38 | /* Set the PIO address */ | ||
39 | pci_write_reg(chan, (bus->number << 24) | (dev << 19) | | ||
40 | (func << 16) | (where & ~3), SH4A_PCIEPAR); | ||
41 | |||
42 | /* Enable the configuration access */ | ||
43 | pci_write_reg(chan, (1 << 31), SH4A_PCIEPCTLR); | ||
44 | |||
45 | if (access_type == PCI_ACCESS_READ) | ||
46 | *data = pci_read_reg(chan, SH4A_PCIEPDR); | ||
47 | else | ||
48 | pci_write_reg(chan, *data, SH4A_PCIEPDR); | ||
49 | |||
50 | /* Check for master and target aborts */ | ||
51 | if (pci_read_reg(chan, SH4A_PCIEPCICONF1) & ((1 << 29) | (1 << 28))) | ||
52 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
53 | |||
54 | return PCIBIOS_SUCCESSFUL; | ||
55 | } | ||
56 | |||
57 | static int sh7786_pcie_read(struct pci_bus *bus, unsigned int devfn, | ||
58 | int where, int size, u32 *val) | ||
59 | { | ||
60 | unsigned long flags; | ||
61 | int ret; | ||
62 | u32 data; | ||
63 | |||
64 | if ((size == 2) && (where & 1)) | ||
65 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
66 | else if ((size == 4) && (where & 3)) | ||
67 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
68 | |||
69 | spin_lock_irqsave(&sh7786_pcie_lock, flags); | ||
70 | ret = sh7786_pcie_config_access(PCI_ACCESS_READ, bus, | ||
71 | devfn, where, &data); | ||
72 | if (ret != PCIBIOS_SUCCESSFUL) | ||
73 | goto out; | ||
74 | |||
75 | if (size == 1) | ||
76 | *val = (data >> ((where & 3) << 3)) & 0xff; | ||
77 | else if (size == 2) | ||
78 | *val = (data >> ((where & 2) << 3)) & 0xffff; | ||
79 | else | ||
80 | *val = data; | ||
81 | |||
82 | dev_dbg(&bus->dev, "pcie-config-read: bus=%3d devfn=0x%04x " | ||
83 | "where=0x%04x size=%d val=0x%08lx\n", bus->number, | ||
84 | devfn, where, size, (unsigned long)*val); | ||
85 | |||
86 | out: | ||
87 | spin_unlock_irqrestore(&sh7786_pcie_lock, flags); | ||
88 | return ret; | ||
89 | } | ||
90 | |||
91 | static int sh7786_pcie_write(struct pci_bus *bus, unsigned int devfn, | ||
92 | int where, int size, u32 val) | ||
93 | { | ||
94 | unsigned long flags; | ||
95 | int shift, ret; | ||
96 | u32 data; | ||
97 | |||
98 | if ((size == 2) && (where & 1)) | ||
99 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
100 | else if ((size == 4) && (where & 3)) | ||
101 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
102 | |||
103 | spin_lock_irqsave(&sh7786_pcie_lock, flags); | ||
104 | ret = sh7786_pcie_config_access(PCI_ACCESS_READ, bus, | ||
105 | devfn, where, &data); | ||
106 | if (ret != PCIBIOS_SUCCESSFUL) | ||
107 | goto out; | ||
108 | |||
109 | dev_dbg(&bus->dev, "pcie-config-write: bus=%3d devfn=0x%04x " | ||
110 | "where=0x%04x size=%d val=%08lx\n", bus->number, | ||
111 | devfn, where, size, (unsigned long)val); | ||
112 | |||
113 | if (size == 1) { | ||
114 | shift = (where & 3) << 3; | ||
115 | data &= ~(0xff << shift); | ||
116 | data |= ((val & 0xff) << shift); | ||
117 | } else if (size == 2) { | ||
118 | shift = (where & 2) << 3; | ||
119 | data &= ~(0xffff << shift); | ||
120 | data |= ((val & 0xffff) << shift); | ||
121 | } else | ||
122 | data = val; | ||
123 | |||
124 | ret = sh7786_pcie_config_access(PCI_ACCESS_WRITE, bus, | ||
125 | devfn, where, &data); | ||
126 | out: | ||
127 | spin_unlock_irqrestore(&sh7786_pcie_lock, flags); | ||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | struct pci_ops sh7786_pci_ops = { | ||
132 | .read = sh7786_pcie_read, | ||
133 | .write = sh7786_pcie_write, | ||
134 | }; | ||