sys_marvel.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. /*
  2. * linux/arch/alpha/kernel/sys_marvel.c
  3. *
  4. * Marvel / IO7 support
  5. */
  6. #include <linux/kernel.h>
  7. #include <linux/types.h>
  8. #include <linux/mm.h>
  9. #include <linux/sched.h>
  10. #include <linux/pci.h>
  11. #include <linux/init.h>
  12. #include <linux/bitops.h>
  13. #include <asm/ptrace.h>
  14. #include <asm/system.h>
  15. #include <asm/dma.h>
  16. #include <asm/irq.h>
  17. #include <asm/mmu_context.h>
  18. #include <asm/io.h>
  19. #include <asm/pgtable.h>
  20. #include <asm/core_marvel.h>
  21. #include <asm/hwrpb.h>
  22. #include <asm/tlbflush.h>
  23. #include <asm/vga.h>
  24. #include <asm/rtc.h>
  25. #include "proto.h"
  26. #include "err_impl.h"
  27. #include "irq_impl.h"
  28. #include "pci_impl.h"
  29. #include "machvec_impl.h"
  30. #if NR_IRQS < MARVEL_NR_IRQS
  31. # error NR_IRQS < MARVEL_NR_IRQS !!!
  32. #endif
  33. /*
  34. * Interrupt handling.
  35. */
  36. static void
  37. io7_device_interrupt(unsigned long vector)
  38. {
  39. unsigned int pid;
  40. unsigned int irq;
  41. /*
  42. * Vector is 0x800 + (interrupt)
  43. *
  44. * where (interrupt) is:
  45. *
  46. * ...16|15 14|13 4|3 0
  47. * -----+-----+--------+---
  48. * PE | 0 | irq | 0
  49. *
  50. * where (irq) is
  51. *
  52. * 0x0800 - 0x0ff0 - 0x0800 + (LSI id << 4)
  53. * 0x1000 - 0x2ff0 - 0x1000 + (MSI_DAT<8:0> << 4)
  54. */
  55. pid = vector >> 16;
  56. irq = ((vector & 0xffff) - 0x800) >> 4;
  57. irq += 16; /* offset for legacy */
  58. irq &= MARVEL_IRQ_VEC_IRQ_MASK; /* not too many bits */
  59. irq |= pid << MARVEL_IRQ_VEC_PE_SHIFT; /* merge the pid */
  60. handle_irq(irq);
  61. }
  62. static volatile unsigned long *
  63. io7_get_irq_ctl(unsigned int irq, struct io7 **pio7)
  64. {
  65. volatile unsigned long *ctl;
  66. unsigned int pid;
  67. struct io7 *io7;
  68. pid = irq >> MARVEL_IRQ_VEC_PE_SHIFT;
  69. if (!(io7 = marvel_find_io7(pid))) {
  70. printk(KERN_ERR
  71. "%s for nonexistent io7 -- vec %x, pid %d\n",
  72. __func__, irq, pid);
  73. return NULL;
  74. }
  75. irq &= MARVEL_IRQ_VEC_IRQ_MASK; /* isolate the vector */
  76. irq -= 16; /* subtract legacy bias */
  77. if (irq >= 0x180) {
  78. printk(KERN_ERR
  79. "%s for invalid irq -- pid %d adjusted irq %x\n",
  80. __func__, pid, irq);
  81. return NULL;
  82. }
  83. ctl = &io7->csrs->PO7_LSI_CTL[irq & 0xff].csr; /* assume LSI */
  84. if (irq >= 0x80) /* MSI */
  85. ctl = &io7->csrs->PO7_MSI_CTL[((irq - 0x80) >> 5) & 0x0f].csr;
  86. if (pio7) *pio7 = io7;
  87. return ctl;
  88. }
  89. static void
  90. io7_enable_irq(unsigned int irq)
  91. {
  92. volatile unsigned long *ctl;
  93. struct io7 *io7;
  94. ctl = io7_get_irq_ctl(irq, &io7);
  95. if (!ctl || !io7) {
  96. printk(KERN_ERR "%s: get_ctl failed for irq %x\n",
  97. __func__, irq);
  98. return;
  99. }
  100. spin_lock(&io7->irq_lock);
  101. *ctl |= 1UL << 24;
  102. mb();
  103. *ctl;
  104. spin_unlock(&io7->irq_lock);
  105. }
  106. static void
  107. io7_disable_irq(unsigned int irq)
  108. {
  109. volatile unsigned long *ctl;
  110. struct io7 *io7;
  111. ctl = io7_get_irq_ctl(irq, &io7);
  112. if (!ctl || !io7) {
  113. printk(KERN_ERR "%s: get_ctl failed for irq %x\n",
  114. __func__, irq);
  115. return;
  116. }
  117. spin_lock(&io7->irq_lock);
  118. *ctl &= ~(1UL << 24);
  119. mb();
  120. *ctl;
  121. spin_unlock(&io7->irq_lock);
  122. }
  123. static unsigned int
  124. io7_startup_irq(unsigned int irq)
  125. {
  126. io7_enable_irq(irq);
  127. return 0; /* never anything pending */
  128. }
  129. static void
  130. io7_end_irq(unsigned int irq)
  131. {
  132. if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
  133. io7_enable_irq(irq);
  134. }
  135. static void
  136. marvel_irq_noop(unsigned int irq)
  137. {
  138. return;
  139. }
  140. static unsigned int
  141. marvel_irq_noop_return(unsigned int irq)
  142. {
  143. return 0;
  144. }
  145. static struct hw_interrupt_type marvel_legacy_irq_type = {
  146. .typename = "LEGACY",
  147. .startup = marvel_irq_noop_return,
  148. .shutdown = marvel_irq_noop,
  149. .enable = marvel_irq_noop,
  150. .disable = marvel_irq_noop,
  151. .ack = marvel_irq_noop,
  152. .end = marvel_irq_noop,
  153. };
  154. static struct hw_interrupt_type io7_lsi_irq_type = {
  155. .typename = "LSI",
  156. .startup = io7_startup_irq,
  157. .shutdown = io7_disable_irq,
  158. .enable = io7_enable_irq,
  159. .disable = io7_disable_irq,
  160. .ack = io7_disable_irq,
  161. .end = io7_end_irq,
  162. };
  163. static struct hw_interrupt_type io7_msi_irq_type = {
  164. .typename = "MSI",
  165. .startup = io7_startup_irq,
  166. .shutdown = io7_disable_irq,
  167. .enable = io7_enable_irq,
  168. .disable = io7_disable_irq,
  169. .ack = marvel_irq_noop,
  170. .end = io7_end_irq,
  171. };
  172. static void
  173. io7_redirect_irq(struct io7 *io7,
  174. volatile unsigned long *csr,
  175. unsigned int where)
  176. {
  177. unsigned long val;
  178. val = *csr;
  179. val &= ~(0x1ffUL << 24); /* clear the target pid */
  180. val |= ((unsigned long)where << 24); /* set the new target pid */
  181. *csr = val;
  182. mb();
  183. *csr;
  184. }
  185. static void
  186. io7_redirect_one_lsi(struct io7 *io7, unsigned int which, unsigned int where)
  187. {
  188. unsigned long val;
  189. /*
  190. * LSI_CTL has target PID @ 14
  191. */
  192. val = io7->csrs->PO7_LSI_CTL[which].csr;
  193. val &= ~(0x1ffUL << 14); /* clear the target pid */
  194. val |= ((unsigned long)where << 14); /* set the new target pid */
  195. io7->csrs->PO7_LSI_CTL[which].csr = val;
  196. mb();
  197. io7->csrs->PO7_LSI_CTL[which].csr;
  198. }
  199. static void
  200. io7_redirect_one_msi(struct io7 *io7, unsigned int which, unsigned int where)
  201. {
  202. unsigned long val;
  203. /*
  204. * MSI_CTL has target PID @ 14
  205. */
  206. val = io7->csrs->PO7_MSI_CTL[which].csr;
  207. val &= ~(0x1ffUL << 14); /* clear the target pid */
  208. val |= ((unsigned long)where << 14); /* set the new target pid */
  209. io7->csrs->PO7_MSI_CTL[which].csr = val;
  210. mb();
  211. io7->csrs->PO7_MSI_CTL[which].csr;
  212. }
  213. static void __init
  214. init_one_io7_lsi(struct io7 *io7, unsigned int which, unsigned int where)
  215. {
  216. /*
  217. * LSI_CTL has target PID @ 14
  218. */
  219. io7->csrs->PO7_LSI_CTL[which].csr = ((unsigned long)where << 14);
  220. mb();
  221. io7->csrs->PO7_LSI_CTL[which].csr;
  222. }
  223. static void __init
  224. init_one_io7_msi(struct io7 *io7, unsigned int which, unsigned int where)
  225. {
  226. /*
  227. * MSI_CTL has target PID @ 14
  228. */
  229. io7->csrs->PO7_MSI_CTL[which].csr = ((unsigned long)where << 14);
  230. mb();
  231. io7->csrs->PO7_MSI_CTL[which].csr;
  232. }
  233. static void __init
  234. init_io7_irqs(struct io7 *io7,
  235. struct hw_interrupt_type *lsi_ops,
  236. struct hw_interrupt_type *msi_ops)
  237. {
  238. long base = (io7->pe << MARVEL_IRQ_VEC_PE_SHIFT) + 16;
  239. long i;
  240. printk("Initializing interrupts for IO7 at PE %u - base %lx\n",
  241. io7->pe, base);
  242. /*
  243. * Where should interrupts from this IO7 go?
  244. *
  245. * They really should be sent to the local CPU to avoid having to
  246. * traverse the mesh, but if it's not an SMP kernel, they have to
  247. * go to the boot CPU. Send them all to the boot CPU for now,
  248. * as each secondary starts, it can redirect it's local device
  249. * interrupts.
  250. */
  251. printk(" Interrupts reported to CPU at PE %u\n", boot_cpuid);
  252. spin_lock(&io7->irq_lock);
  253. /* set up the error irqs */
  254. io7_redirect_irq(io7, &io7->csrs->HLT_CTL.csr, boot_cpuid);
  255. io7_redirect_irq(io7, &io7->csrs->HPI_CTL.csr, boot_cpuid);
  256. io7_redirect_irq(io7, &io7->csrs->CRD_CTL.csr, boot_cpuid);
  257. io7_redirect_irq(io7, &io7->csrs->STV_CTL.csr, boot_cpuid);
  258. io7_redirect_irq(io7, &io7->csrs->HEI_CTL.csr, boot_cpuid);
  259. /* Set up the lsi irqs. */
  260. for (i = 0; i < 128; ++i) {
  261. irq_desc[base + i].status = IRQ_DISABLED | IRQ_LEVEL;
  262. irq_desc[base + i].chip = lsi_ops;
  263. }
  264. /* Disable the implemented irqs in hardware. */
  265. for (i = 0; i < 0x60; ++i)
  266. init_one_io7_lsi(io7, i, boot_cpuid);
  267. init_one_io7_lsi(io7, 0x74, boot_cpuid);
  268. init_one_io7_lsi(io7, 0x75, boot_cpuid);
  269. /* Set up the msi irqs. */
  270. for (i = 128; i < (128 + 512); ++i) {
  271. irq_desc[base + i].status = IRQ_DISABLED | IRQ_LEVEL;
  272. irq_desc[base + i].chip = msi_ops;
  273. }
  274. for (i = 0; i < 16; ++i)
  275. init_one_io7_msi(io7, i, boot_cpuid);
  276. spin_unlock(&io7->irq_lock);
  277. }
  278. static void __init
  279. marvel_init_irq(void)
  280. {
  281. int i;
  282. struct io7 *io7 = NULL;
  283. /* Reserve the legacy irqs. */
  284. for (i = 0; i < 16; ++i) {
  285. irq_desc[i].status = IRQ_DISABLED;
  286. irq_desc[i].chip = &marvel_legacy_irq_type;
  287. }
  288. /* Init the io7 irqs. */
  289. for (io7 = NULL; (io7 = marvel_next_io7(io7)) != NULL; )
  290. init_io7_irqs(io7, &io7_lsi_irq_type, &io7_msi_irq_type);
  291. }
  292. static int
  293. marvel_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
  294. {
  295. struct pci_controller *hose = dev->sysdata;
  296. struct io7_port *io7_port = hose->sysdata;
  297. struct io7 *io7 = io7_port->io7;
  298. int msi_loc, msi_data_off;
  299. u16 msg_ctl;
  300. u16 msg_dat;
  301. u8 intline;
  302. int irq;
  303. pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &intline);
  304. irq = intline;
  305. msi_loc = pci_find_capability(dev, PCI_CAP_ID_MSI);
  306. msg_ctl = 0;
  307. if (msi_loc)
  308. pci_read_config_word(dev, msi_loc + PCI_MSI_FLAGS, &msg_ctl);
  309. if (msg_ctl & PCI_MSI_FLAGS_ENABLE) {
  310. msi_data_off = PCI_MSI_DATA_32;
  311. if (msg_ctl & PCI_MSI_FLAGS_64BIT)
  312. msi_data_off = PCI_MSI_DATA_64;
  313. pci_read_config_word(dev, msi_loc + msi_data_off, &msg_dat);
  314. irq = msg_dat & 0x1ff; /* we use msg_data<8:0> */
  315. irq += 0x80; /* offset for lsi */
  316. #if 1
  317. printk("PCI:%d:%d:%d (hose %d) is using MSI\n",
  318. dev->bus->number,
  319. PCI_SLOT(dev->devfn),
  320. PCI_FUNC(dev->devfn),
  321. hose->index);
  322. printk(" %d message(s) from 0x%04x\n",
  323. 1 << ((msg_ctl & PCI_MSI_FLAGS_QSIZE) >> 4),
  324. msg_dat);
  325. printk(" reporting on %d IRQ(s) from %d (0x%x)\n",
  326. 1 << ((msg_ctl & PCI_MSI_FLAGS_QSIZE) >> 4),
  327. (irq + 16) | (io7->pe << MARVEL_IRQ_VEC_PE_SHIFT),
  328. (irq + 16) | (io7->pe << MARVEL_IRQ_VEC_PE_SHIFT));
  329. #endif
  330. #if 0
  331. pci_write_config_word(dev, msi_loc + PCI_MSI_FLAGS,
  332. msg_ctl & ~PCI_MSI_FLAGS_ENABLE);
  333. pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &intline);
  334. irq = intline;
  335. printk(" forcing LSI interrupt on irq %d [0x%x]\n", irq, irq);
  336. #endif
  337. }
  338. irq += 16; /* offset for legacy */
  339. irq |= io7->pe << MARVEL_IRQ_VEC_PE_SHIFT; /* merge the pid */
  340. return irq;
  341. }
  342. static void __init
  343. marvel_init_pci(void)
  344. {
  345. struct io7 *io7;
  346. marvel_register_error_handlers();
  347. pci_probe_only = 1;
  348. common_init_pci();
  349. locate_and_init_vga(NULL);
  350. /* Clear any io7 errors. */
  351. for (io7 = NULL; (io7 = marvel_next_io7(io7)) != NULL; )
  352. io7_clear_errors(io7);
  353. }
  354. static void __init
  355. marvel_init_rtc(void)
  356. {
  357. init_rtc_irq();
  358. }
  359. struct marvel_rtc_time {
  360. struct rtc_time *time;
  361. int retval;
  362. };
  363. #ifdef CONFIG_SMP
  364. static void
  365. smp_get_rtc_time(void *data)
  366. {
  367. struct marvel_rtc_time *mrt = data;
  368. mrt->retval = __get_rtc_time(mrt->time);
  369. }
  370. static void
  371. smp_set_rtc_time(void *data)
  372. {
  373. struct marvel_rtc_time *mrt = data;
  374. mrt->retval = __set_rtc_time(mrt->time);
  375. }
  376. #endif
  377. static unsigned int
  378. marvel_get_rtc_time(struct rtc_time *time)
  379. {
  380. #ifdef CONFIG_SMP
  381. struct marvel_rtc_time mrt;
  382. if (smp_processor_id() != boot_cpuid) {
  383. mrt.time = time;
  384. smp_call_function_single(boot_cpuid, smp_get_rtc_time, &mrt, 1);
  385. return mrt.retval;
  386. }
  387. #endif
  388. return __get_rtc_time(time);
  389. }
  390. static int
  391. marvel_set_rtc_time(struct rtc_time *time)
  392. {
  393. #ifdef CONFIG_SMP
  394. struct marvel_rtc_time mrt;
  395. if (smp_processor_id() != boot_cpuid) {
  396. mrt.time = time;
  397. smp_call_function_single(boot_cpuid, smp_set_rtc_time, &mrt, 1);
  398. return mrt.retval;
  399. }
  400. #endif
  401. return __set_rtc_time(time);
  402. }
  403. static void
  404. marvel_smp_callin(void)
  405. {
  406. int cpuid = hard_smp_processor_id();
  407. struct io7 *io7 = marvel_find_io7(cpuid);
  408. unsigned int i;
  409. if (!io7)
  410. return;
  411. /*
  412. * There is a local IO7 - redirect all of its interrupts here.
  413. */
  414. printk("Redirecting IO7 interrupts to local CPU at PE %u\n", cpuid);
  415. /* Redirect the error IRQS here. */
  416. io7_redirect_irq(io7, &io7->csrs->HLT_CTL.csr, cpuid);
  417. io7_redirect_irq(io7, &io7->csrs->HPI_CTL.csr, cpuid);
  418. io7_redirect_irq(io7, &io7->csrs->CRD_CTL.csr, cpuid);
  419. io7_redirect_irq(io7, &io7->csrs->STV_CTL.csr, cpuid);
  420. io7_redirect_irq(io7, &io7->csrs->HEI_CTL.csr, cpuid);
  421. /* Redirect the implemented LSIs here. */
  422. for (i = 0; i < 0x60; ++i)
  423. io7_redirect_one_lsi(io7, i, cpuid);
  424. io7_redirect_one_lsi(io7, 0x74, cpuid);
  425. io7_redirect_one_lsi(io7, 0x75, cpuid);
  426. /* Redirect the MSIs here. */
  427. for (i = 0; i < 16; ++i)
  428. io7_redirect_one_msi(io7, i, cpuid);
  429. }
  430. /*
  431. * System Vectors
  432. */
  433. struct alpha_machine_vector marvel_ev7_mv __initmv = {
  434. .vector_name = "MARVEL/EV7",
  435. DO_EV7_MMU,
  436. .rtc_port = 0x70,
  437. .rtc_get_time = marvel_get_rtc_time,
  438. .rtc_set_time = marvel_set_rtc_time,
  439. DO_MARVEL_IO,
  440. .machine_check = marvel_machine_check,
  441. .max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS,
  442. .min_io_address = DEFAULT_IO_BASE,
  443. .min_mem_address = DEFAULT_MEM_BASE,
  444. .pci_dac_offset = IO7_DAC_OFFSET,
  445. .nr_irqs = MARVEL_NR_IRQS,
  446. .device_interrupt = io7_device_interrupt,
  447. .agp_info = marvel_agp_info,
  448. .smp_callin = marvel_smp_callin,
  449. .init_arch = marvel_init_arch,
  450. .init_irq = marvel_init_irq,
  451. .init_rtc = marvel_init_rtc,
  452. .init_pci = marvel_init_pci,
  453. .kill_arch = marvel_kill_arch,
  454. .pci_map_irq = marvel_map_irq,
  455. .pci_swizzle = common_swizzle,
  456. .pa_to_nid = marvel_pa_to_nid,
  457. .cpuid_to_nid = marvel_cpuid_to_nid,
  458. .node_mem_start = marvel_node_mem_start,
  459. .node_mem_size = marvel_node_mem_size,
  460. };
  461. ALIAS_MV(marvel_ev7)