| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 1 | /* | 
|  | 2 | *  This program is free software; you can redistribute it and/or modify it | 
|  | 3 | *  under the terms of the GNU General Public License version 2 as published | 
|  | 4 | *  by the Free Software Foundation. | 
|  | 5 | * | 
|  | 6 | *  Copyright (C) 2010 John Crispin <blogic@openwrt.org> | 
|  | 7 | */ | 
|  | 8 |  | 
|  | 9 | #include <linux/types.h> | 
|  | 10 | #include <linux/pci.h> | 
|  | 11 | #include <linux/kernel.h> | 
|  | 12 | #include <linux/init.h> | 
|  | 13 | #include <linux/delay.h> | 
|  | 14 | #include <linux/mm.h> | 
|  | 15 | #include <linux/vmalloc.h> | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 16 | #include <linux/module.h> | 
|  | 17 | #include <linux/clk.h> | 
|  | 18 | #include <linux/of_platform.h> | 
|  | 19 | #include <linux/of_gpio.h> | 
|  | 20 | #include <linux/of_irq.h> | 
|  | 21 | #include <linux/of_pci.h> | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 22 |  | 
|  | 23 | #include <asm/pci.h> | 
|  | 24 | #include <asm/gpio.h> | 
|  | 25 | #include <asm/addrspace.h> | 
|  | 26 |  | 
|  | 27 | #include <lantiq_soc.h> | 
|  | 28 | #include <lantiq_irq.h> | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 29 |  | 
|  | 30 | #include "pci-lantiq.h" | 
|  | 31 |  | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 32 | #define PCI_CR_FCI_ADDR_MAP0		0x00C0 | 
|  | 33 | #define PCI_CR_FCI_ADDR_MAP1		0x00C4 | 
|  | 34 | #define PCI_CR_FCI_ADDR_MAP2		0x00C8 | 
|  | 35 | #define PCI_CR_FCI_ADDR_MAP3		0x00CC | 
|  | 36 | #define PCI_CR_FCI_ADDR_MAP4		0x00D0 | 
|  | 37 | #define PCI_CR_FCI_ADDR_MAP5		0x00D4 | 
|  | 38 | #define PCI_CR_FCI_ADDR_MAP6		0x00D8 | 
|  | 39 | #define PCI_CR_FCI_ADDR_MAP7		0x00DC | 
|  | 40 | #define PCI_CR_CLK_CTRL			0x0000 | 
|  | 41 | #define PCI_CR_PCI_MOD			0x0030 | 
|  | 42 | #define PCI_CR_PC_ARB			0x0080 | 
|  | 43 | #define PCI_CR_FCI_ADDR_MAP11hg		0x00E4 | 
|  | 44 | #define PCI_CR_BAR11MASK		0x0044 | 
|  | 45 | #define PCI_CR_BAR12MASK		0x0048 | 
|  | 46 | #define PCI_CR_BAR13MASK		0x004C | 
|  | 47 | #define PCI_CS_BASE_ADDR1		0x0010 | 
|  | 48 | #define PCI_CR_PCI_ADDR_MAP11		0x0064 | 
|  | 49 | #define PCI_CR_FCI_BURST_LENGTH		0x00E8 | 
|  | 50 | #define PCI_CR_PCI_EOI			0x002C | 
|  | 51 | #define PCI_CS_STS_CMD			0x0004 | 
|  | 52 |  | 
|  | 53 | #define PCI_MASTER0_REQ_MASK_2BITS	8 | 
|  | 54 | #define PCI_MASTER1_REQ_MASK_2BITS	10 | 
|  | 55 | #define PCI_MASTER2_REQ_MASK_2BITS	12 | 
|  | 56 | #define INTERNAL_ARB_ENABLE_BIT		0 | 
|  | 57 |  | 
|  | 58 | #define LTQ_CGU_IFCCR		0x0018 | 
|  | 59 | #define LTQ_CGU_PCICR		0x0034 | 
|  | 60 |  | 
|  | 61 | #define ltq_pci_w32(x, y)	ltq_w32((x), ltq_pci_membase + (y)) | 
|  | 62 | #define ltq_pci_r32(x)		ltq_r32(ltq_pci_membase + (x)) | 
|  | 63 |  | 
|  | 64 | #define ltq_pci_cfg_w32(x, y)	ltq_w32((x), ltq_pci_mapped_cfg + (y)) | 
|  | 65 | #define ltq_pci_cfg_r32(x)	ltq_r32(ltq_pci_mapped_cfg + (x)) | 
|  | 66 |  | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 67 | __iomem void *ltq_pci_mapped_cfg; | 
|  | 68 | static __iomem void *ltq_pci_membase; | 
|  | 69 |  | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 70 | static int reset_gpio; | 
|  | 71 | static struct clk *clk_pci, *clk_external; | 
|  | 72 | static struct resource pci_io_resource; | 
|  | 73 | static struct resource pci_mem_resource; | 
|  | 74 | static struct pci_ops pci_ops = { | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 75 | .read	= ltq_pci_read_config_dword, | 
|  | 76 | .write	= ltq_pci_write_config_dword | 
|  | 77 | }; | 
|  | 78 |  | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 79 | static struct pci_controller pci_controller = { | 
|  | 80 | .pci_ops	= &pci_ops, | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 81 | .mem_resource	= &pci_mem_resource, | 
|  | 82 | .mem_offset	= 0x00000000UL, | 
|  | 83 | .io_resource	= &pci_io_resource, | 
|  | 84 | .io_offset	= 0x00000000UL, | 
|  | 85 | }; | 
|  | 86 |  | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 87 | static inline u32 ltq_calc_bar11mask(void) | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 88 | { | 
|  | 89 | u32 mem, bar11mask; | 
|  | 90 |  | 
|  | 91 | /* BAR11MASK value depends on available memory on system. */ | 
|  | 92 | mem = num_physpages * PAGE_SIZE; | 
|  | 93 | bar11mask = (0x0ffffff0 & ~((1 << (fls(mem) - 1)) - 1)) | 8; | 
|  | 94 |  | 
|  | 95 | return bar11mask; | 
|  | 96 | } | 
|  | 97 |  | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 98 | static int __devinit ltq_pci_startup(struct platform_device *pdev) | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 99 | { | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 100 | struct device_node *node = pdev->dev.of_node; | 
|  | 101 | const __be32 *req_mask, *bus_clk; | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 102 | u32 temp_buffer; | 
|  | 103 |  | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 104 | /* get our clocks */ | 
|  | 105 | clk_pci = clk_get(&pdev->dev, NULL); | 
|  | 106 | if (IS_ERR(clk_pci)) { | 
|  | 107 | dev_err(&pdev->dev, "failed to get pci clock\n"); | 
|  | 108 | return PTR_ERR(clk_pci); | 
| John Crispin | 0596954 | 2011-07-18 18:04:12 +0200 | [diff] [blame] | 109 | } | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 110 |  | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 111 | clk_external = clk_get(&pdev->dev, "external"); | 
|  | 112 | if (IS_ERR(clk_external)) { | 
|  | 113 | clk_put(clk_pci); | 
|  | 114 | dev_err(&pdev->dev, "failed to get external pci clock\n"); | 
|  | 115 | return PTR_ERR(clk_external); | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 116 | } | 
|  | 117 |  | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 118 | /* read the bus speed that we want */ | 
|  | 119 | bus_clk = of_get_property(node, "lantiq,bus-clock", NULL); | 
|  | 120 | if (bus_clk) | 
|  | 121 | clk_set_rate(clk_pci, *bus_clk); | 
|  | 122 |  | 
|  | 123 | /* and enable the clocks */ | 
|  | 124 | clk_enable(clk_pci); | 
|  | 125 | if (of_find_property(node, "lantiq,external-clock", NULL)) | 
|  | 126 | clk_enable(clk_external); | 
|  | 127 | else | 
|  | 128 | clk_disable(clk_external); | 
|  | 129 |  | 
|  | 130 | /* setup reset gpio used by pci */ | 
|  | 131 | reset_gpio = of_get_named_gpio(node, "gpio-reset", 0); | 
| Roland Stigge | 42b14cb | 2012-06-18 11:28:26 +0200 | [diff] [blame] | 132 | if (gpio_is_valid(reset_gpio)) | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 133 | devm_gpio_request(&pdev->dev, reset_gpio, "pci-reset"); | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 134 |  | 
|  | 135 | /* enable auto-switching between PCI and EBU */ | 
|  | 136 | ltq_pci_w32(0xa, PCI_CR_CLK_CTRL); | 
|  | 137 |  | 
|  | 138 | /* busy, i.e. configuration is not done, PCI access has to be retried */ | 
|  | 139 | ltq_pci_w32(ltq_pci_r32(PCI_CR_PCI_MOD) & ~(1 << 24), PCI_CR_PCI_MOD); | 
|  | 140 | wmb(); | 
|  | 141 | /* BUS Master/IO/MEM access */ | 
|  | 142 | ltq_pci_cfg_w32(ltq_pci_cfg_r32(PCI_CS_STS_CMD) | 7, PCI_CS_STS_CMD); | 
|  | 143 |  | 
|  | 144 | /* enable external 2 PCI masters */ | 
|  | 145 | temp_buffer = ltq_pci_r32(PCI_CR_PC_ARB); | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 146 | /* setup the request mask */ | 
|  | 147 | req_mask = of_get_property(node, "req-mask", NULL); | 
|  | 148 | if (req_mask) | 
|  | 149 | temp_buffer &= ~((*req_mask & 0xf) << 16); | 
|  | 150 | else | 
|  | 151 | temp_buffer &= ~0xf0000; | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 152 | /* enable internal arbiter */ | 
|  | 153 | temp_buffer |= (1 << INTERNAL_ARB_ENABLE_BIT); | 
|  | 154 | /* enable internal PCI master reqest */ | 
|  | 155 | temp_buffer &= (~(3 << PCI_MASTER0_REQ_MASK_2BITS)); | 
|  | 156 |  | 
|  | 157 | /* enable EBU request */ | 
|  | 158 | temp_buffer &= (~(3 << PCI_MASTER1_REQ_MASK_2BITS)); | 
|  | 159 |  | 
|  | 160 | /* enable all external masters request */ | 
|  | 161 | temp_buffer &= (~(3 << PCI_MASTER2_REQ_MASK_2BITS)); | 
|  | 162 | ltq_pci_w32(temp_buffer, PCI_CR_PC_ARB); | 
|  | 163 | wmb(); | 
|  | 164 |  | 
|  | 165 | /* setup BAR memory regions */ | 
|  | 166 | ltq_pci_w32(0x18000000, PCI_CR_FCI_ADDR_MAP0); | 
|  | 167 | ltq_pci_w32(0x18400000, PCI_CR_FCI_ADDR_MAP1); | 
|  | 168 | ltq_pci_w32(0x18800000, PCI_CR_FCI_ADDR_MAP2); | 
|  | 169 | ltq_pci_w32(0x18c00000, PCI_CR_FCI_ADDR_MAP3); | 
|  | 170 | ltq_pci_w32(0x19000000, PCI_CR_FCI_ADDR_MAP4); | 
|  | 171 | ltq_pci_w32(0x19400000, PCI_CR_FCI_ADDR_MAP5); | 
|  | 172 | ltq_pci_w32(0x19800000, PCI_CR_FCI_ADDR_MAP6); | 
|  | 173 | ltq_pci_w32(0x19c00000, PCI_CR_FCI_ADDR_MAP7); | 
|  | 174 | ltq_pci_w32(0x1ae00000, PCI_CR_FCI_ADDR_MAP11hg); | 
|  | 175 | ltq_pci_w32(ltq_calc_bar11mask(), PCI_CR_BAR11MASK); | 
|  | 176 | ltq_pci_w32(0, PCI_CR_PCI_ADDR_MAP11); | 
|  | 177 | ltq_pci_w32(0, PCI_CS_BASE_ADDR1); | 
|  | 178 | /* both TX and RX endian swap are enabled */ | 
|  | 179 | ltq_pci_w32(ltq_pci_r32(PCI_CR_PCI_EOI) | 3, PCI_CR_PCI_EOI); | 
|  | 180 | wmb(); | 
|  | 181 | ltq_pci_w32(ltq_pci_r32(PCI_CR_BAR12MASK) | 0x80000000, | 
|  | 182 | PCI_CR_BAR12MASK); | 
|  | 183 | ltq_pci_w32(ltq_pci_r32(PCI_CR_BAR13MASK) | 0x80000000, | 
|  | 184 | PCI_CR_BAR13MASK); | 
|  | 185 | /*use 8 dw burst length */ | 
|  | 186 | ltq_pci_w32(0x303, PCI_CR_FCI_BURST_LENGTH); | 
|  | 187 | ltq_pci_w32(ltq_pci_r32(PCI_CR_PCI_MOD) | (1 << 24), PCI_CR_PCI_MOD); | 
|  | 188 | wmb(); | 
|  | 189 |  | 
|  | 190 | /* setup irq line */ | 
|  | 191 | ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_CON) | 0xc, LTQ_EBU_PCC_CON); | 
|  | 192 | ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_IEN) | 0x10, LTQ_EBU_PCC_IEN); | 
|  | 193 |  | 
|  | 194 | /* toggle reset pin */ | 
| Roland Stigge | 42b14cb | 2012-06-18 11:28:26 +0200 | [diff] [blame] | 195 | if (gpio_is_valid(reset_gpio)) { | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 196 | __gpio_set_value(reset_gpio, 0); | 
|  | 197 | wmb(); | 
|  | 198 | mdelay(1); | 
|  | 199 | __gpio_set_value(reset_gpio, 1); | 
|  | 200 | } | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 201 | return 0; | 
|  | 202 | } | 
|  | 203 |  | 
|  | 204 | static int __devinit ltq_pci_probe(struct platform_device *pdev) | 
|  | 205 | { | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 206 | struct resource *res_cfg, *res_bridge; | 
| Bjorn Helgaas | 2909060 | 2012-02-23 20:18:57 -0700 | [diff] [blame] | 207 |  | 
|  | 208 | pci_clear_flags(PCI_PROBE_ONLY); | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 209 |  | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 210 | res_cfg = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | 211 | res_bridge = platform_get_resource(pdev, IORESOURCE_MEM, 1); | 
|  | 212 | if (!res_cfg || !res_bridge) { | 
|  | 213 | dev_err(&pdev->dev, "missing memory reources\n"); | 
|  | 214 | return -EINVAL; | 
|  | 215 | } | 
|  | 216 |  | 
|  | 217 | ltq_pci_membase = devm_request_and_ioremap(&pdev->dev, res_bridge); | 
|  | 218 | ltq_pci_mapped_cfg = devm_request_and_ioremap(&pdev->dev, res_cfg); | 
|  | 219 |  | 
|  | 220 | if (!ltq_pci_membase || !ltq_pci_mapped_cfg) { | 
|  | 221 | dev_err(&pdev->dev, "failed to remap resources\n"); | 
|  | 222 | return -ENOMEM; | 
|  | 223 | } | 
|  | 224 |  | 
|  | 225 | ltq_pci_startup(pdev); | 
|  | 226 |  | 
|  | 227 | pci_load_of_ranges(&pci_controller, pdev->dev.of_node); | 
|  | 228 | register_pci_controller(&pci_controller); | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 229 | return 0; | 
|  | 230 | } | 
|  | 231 |  | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 232 | static const struct of_device_id ltq_pci_match[] = { | 
|  | 233 | { .compatible = "lantiq,pci-xway" }, | 
|  | 234 | {}, | 
|  | 235 | }; | 
|  | 236 | MODULE_DEVICE_TABLE(of, ltq_pci_match); | 
|  | 237 |  | 
|  | 238 | static struct platform_driver ltq_pci_driver = { | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 239 | .probe = ltq_pci_probe, | 
|  | 240 | .driver = { | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 241 | .name = "pci-xway", | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 242 | .owner = THIS_MODULE, | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 243 | .of_match_table = ltq_pci_match, | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 244 | }, | 
|  | 245 | }; | 
|  | 246 |  | 
|  | 247 | int __init pcibios_init(void) | 
|  | 248 | { | 
|  | 249 | int ret = platform_driver_register(<q_pci_driver); | 
|  | 250 | if (ret) | 
| John Crispin | 57c8cb8 | 2012-05-11 18:47:30 +0200 | [diff] [blame] | 251 | pr_info("pci-xway: Error registering platform driver!"); | 
| John Crispin | e47d488 | 2011-03-30 09:27:49 +0200 | [diff] [blame] | 252 | return ret; | 
|  | 253 | } | 
|  | 254 |  | 
|  | 255 | arch_initcall(pcibios_init); |