amiints.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:13k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  * BK Id: SCCS/s.amiints.c 1.8 05/21/01 00:48:24 cort
  3.  */
  4. /*
  5.  * linux/arch/m68k/amiga/amiints.c -- Amiga Linux interrupt handling code
  6.  *
  7.  * This file is subject to the terms and conditions of the GNU General Public
  8.  * License.  See the file COPYING in the main directory of this archive
  9.  * for more details.
  10.  *
  11.  * 11/07/96: rewritten interrupt handling, irq lists are exists now only for
  12.  *           this sources where it makes sense (VERTB/PORTS/EXTER) and you must
  13.  *           be careful that dev_id for this sources is unique since this the
  14.  *           only possibility to distinguish between different handlers for
  15.  *           free_irq. irq lists also have different irq flags:
  16.  *           - IRQ_FLG_FAST: handler is inserted at top of list (after other
  17.  *                           fast handlers)
  18.  *           - IRQ_FLG_SLOW: handler is inserted at bottom of list and before
  19.  *                           they're executed irq level is set to the previous
  20.  *                           one, but handlers don't need to be reentrant, if
  21.  *                           reentrance occurred, slow handlers will be just
  22.  *                           called again.
  23.  *           The whole interrupt handling for CIAs is moved to cia.c
  24.  *           /Roman Zippel
  25.  *
  26.  * 07/08/99: rewamp of the interrupt handling - we now have two types of
  27.  *           interrupts, normal and fast handlers, fast handlers being
  28.  *           marked with SA_INTERRUPT and runs with all other interrupts
  29.  *           disabled. Normal interrupts disable their own source but
  30.  *           run with all other interrupt sources enabled.
  31.  *           PORTS and EXTER interrupts are always shared even if the
  32.  *           drivers do not explicitly mark this when calling
  33.  *           request_irq which they really should do.
  34.  *           This is similar to the way interrupts are handled on all
  35.  *           other architectures and makes a ton of sense besides
  36.  *           having the advantage of making it easier to share
  37.  *           drivers.
  38.  *           /Jes
  39.  */
  40. #include <linux/config.h>
  41. #include <linux/types.h>
  42. #include <linux/kernel.h>
  43. #include <linux/sched.h>
  44. #include <linux/kernel_stat.h>
  45. #include <linux/init.h>
  46. #include <asm/system.h>
  47. #include <asm/irq.h>
  48. #include <asm/traps.h>
  49. #include <asm/amigahw.h>
  50. #include <asm/amigaints.h>
  51. #include <asm/amipcmcia.h>
  52. #ifdef CONFIG_APUS
  53. #include <asm/amigappc.h>
  54. #endif
  55. extern int cia_request_irq(int irq,
  56.                            void (*handler)(int, void *, struct pt_regs *),
  57.                            unsigned long flags, const char *devname, void *dev_id);
  58. extern void cia_free_irq(unsigned int irq, void *dev_id);
  59. extern void cia_init_IRQ(struct ciabase *base);
  60. extern int cia_get_irq_list(struct ciabase *base, char *buf);
  61. /* irq node variables for amiga interrupt sources */
  62. static irq_node_t *ami_irq_list[AMI_STD_IRQS];
  63. unsigned short ami_intena_vals[AMI_STD_IRQS] = {
  64. IF_VERTB, IF_COPER, IF_AUD0, IF_AUD1, IF_AUD2, IF_AUD3, IF_BLIT,
  65. IF_DSKSYN, IF_DSKBLK, IF_RBF, IF_TBE, IF_SOFT, IF_PORTS, IF_EXTER
  66. };
  67. static const unsigned char ami_servers[AMI_STD_IRQS] = {
  68. 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1
  69. };
  70. static short ami_ablecount[AMI_IRQS];
  71. static void ami_badint(int irq, void *dev_id, struct pt_regs *fp)
  72. {
  73. num_spurious += 1;
  74. }
  75. /*
  76.  * void amiga_init_IRQ(void)
  77.  *
  78.  * Parameters: None
  79.  *
  80.  * Returns: Nothing
  81.  *
  82.  * This function should be called during kernel startup to initialize
  83.  * the amiga IRQ handling routines.
  84.  */
  85. __init
  86. void amiga_init_IRQ(void)
  87. {
  88. int i;
  89. /* initialize handlers */
  90. for (i = 0; i < AMI_STD_IRQS; i++) {
  91. if (ami_servers[i]) {
  92. ami_irq_list[i] = NULL;
  93. } else {
  94. ami_irq_list[i] = new_irq_node();
  95. ami_irq_list[i]->handler = ami_badint;
  96. ami_irq_list[i]->flags   = 0;
  97. ami_irq_list[i]->dev_id  = NULL;
  98. ami_irq_list[i]->devname = NULL;
  99. ami_irq_list[i]->next    = NULL;
  100. }
  101. }
  102. for (i = 0; i < AMI_IRQS; i++)
  103. ami_ablecount[i] = 0;
  104. /* turn off PCMCIA interrupts */
  105. if (AMIGAHW_PRESENT(PCMCIA))
  106. pcmcia_disable_irq();
  107. /* turn off all interrupts... */
  108. custom.intena = 0x7fff;
  109. custom.intreq = 0x7fff;
  110. #ifdef CONFIG_APUS
  111. /* Clear any inter-CPU interrupt requests. Circumvents bug in
  112.            Blizzard IPL emulation HW (or so it appears). */
  113. APUS_WRITE(APUS_INT_LVL, INTLVL_SETRESET | INTLVL_MASK);
  114. /* Init IPL emulation. */
  115. APUS_WRITE(APUS_REG_INT, REGINT_INTMASTER | REGINT_ENABLEIPL);
  116. APUS_WRITE(APUS_IPL_EMU, IPLEMU_DISABLEINT);
  117. APUS_WRITE(APUS_IPL_EMU, IPLEMU_SETRESET | IPLEMU_IPLMASK);
  118. #endif
  119. /* ... and enable the master interrupt bit */
  120. custom.intena = IF_SETCLR | IF_INTEN;
  121. cia_init_IRQ(&ciaa_base);
  122. cia_init_IRQ(&ciab_base);
  123. }
  124. static inline int amiga_insert_irq(irq_node_t **list, irq_node_t *node)
  125. {
  126. unsigned long flags;
  127. irq_node_t *cur;
  128. if (!node->dev_id)
  129. printk("%s: Warning: dev_id of %s is zeron",
  130.        __FUNCTION__, node->devname);
  131. save_flags(flags);
  132. cli();
  133. cur = *list;
  134. if (node->flags & SA_INTERRUPT) {
  135. if (node->flags & SA_SHIRQ)
  136. return -EBUSY;
  137. /*
  138.  * There should never be more than one
  139.  */
  140. while (cur && cur->flags & SA_INTERRUPT) {
  141. list = &cur->next;
  142. cur = cur->next;
  143. }
  144. } else {
  145. while (cur) {
  146. list = &cur->next;
  147. cur = cur->next;
  148. }
  149. }
  150. node->next = cur;
  151. *list = node;
  152. restore_flags(flags);
  153. return 0;
  154. }
  155. static inline void amiga_delete_irq(irq_node_t **list, void *dev_id)
  156. {
  157. unsigned long flags;
  158. irq_node_t *node;
  159. save_flags(flags);
  160. cli();
  161. for (node = *list; node; list = &node->next, node = *list) {
  162. if (node->dev_id == dev_id) {
  163. *list = node->next;
  164. /* Mark it as free. */
  165. node->handler = NULL;
  166. restore_flags(flags);
  167. return;
  168. }
  169. }
  170. restore_flags(flags);
  171. printk ("%s: tried to remove invalid irqn", __FUNCTION__);
  172. }
  173. /*
  174.  * amiga_request_irq : add an interrupt service routine for a particular
  175.  *                     machine specific interrupt source.
  176.  *                     If the addition was successful, it returns 0.
  177.  */
  178. int amiga_request_irq(unsigned int irq,
  179.       void (*handler)(int, void *, struct pt_regs *),
  180.                       unsigned long flags, const char *devname, void *dev_id)
  181. {
  182. irq_node_t *node;
  183. int error = 0;
  184. if (irq >= AMI_IRQS) {
  185. printk ("%s: Unknown IRQ %d from %sn", __FUNCTION__,
  186. irq, devname);
  187. return -ENXIO;
  188. }
  189. if (irq >= IRQ_AMIGA_AUTO)
  190. return sys_request_irq(irq - IRQ_AMIGA_AUTO, handler,
  191.                        flags, devname, dev_id);
  192. if (irq >= IRQ_AMIGA_CIAA)
  193. return cia_request_irq(irq, handler, flags, devname, dev_id);
  194. /*
  195.  * IRQ_AMIGA_PORTS & IRQ_AMIGA_EXTER defaults to shared,
  196.  * we could add a check here for the SA_SHIRQ flag but all drivers
  197.  * should be aware of sharing anyway.
  198.  */
  199. if (ami_servers[irq]) {
  200. if (!(node = new_irq_node()))
  201. return -ENOMEM;
  202. node->handler = handler;
  203. node->flags   = flags;
  204. node->dev_id  = dev_id;
  205. node->devname = devname;
  206. node->next    = NULL;
  207. error = amiga_insert_irq(&ami_irq_list[irq], node);
  208. } else {
  209. ami_irq_list[irq]->handler = handler;
  210. ami_irq_list[irq]->flags   = flags;
  211. ami_irq_list[irq]->dev_id  = dev_id;
  212. ami_irq_list[irq]->devname = devname;
  213. }
  214. /* enable the interrupt */
  215. if (irq < IRQ_AMIGA_PORTS && !ami_ablecount[irq])
  216. custom.intena = IF_SETCLR | ami_intena_vals[irq];
  217. return error;
  218. }
  219. void amiga_free_irq(unsigned int irq, void *dev_id)
  220. {
  221. if (irq >= AMI_IRQS) {
  222. printk ("%s: Unknown IRQ %dn", __FUNCTION__, irq);
  223. return;
  224. }
  225. if (irq >= IRQ_AMIGA_AUTO) {
  226. sys_free_irq(irq - IRQ_AMIGA_AUTO, dev_id);
  227. return;
  228. }
  229. if (irq >= IRQ_AMIGA_CIAA) {
  230. cia_free_irq(irq, dev_id);
  231. return;
  232. }
  233. if (ami_servers[irq]) {
  234. amiga_delete_irq(&ami_irq_list[irq], dev_id);
  235. /* if server list empty, disable the interrupt */
  236. if (!ami_irq_list[irq] && irq < IRQ_AMIGA_PORTS)
  237. custom.intena = ami_intena_vals[irq];
  238. } else {
  239. if (ami_irq_list[irq]->dev_id != dev_id)
  240. printk("%s: removing probably wrong IRQ %d from %sn",
  241.        __FUNCTION__, irq, ami_irq_list[irq]->devname);
  242. ami_irq_list[irq]->handler = ami_badint;
  243. ami_irq_list[irq]->flags   = 0;
  244. ami_irq_list[irq]->dev_id  = NULL;
  245. ami_irq_list[irq]->devname = NULL;
  246. custom.intena = ami_intena_vals[irq];
  247. }
  248. }
  249. /*
  250.  * Enable/disable a particular machine specific interrupt source.
  251.  * Note that this may affect other interrupts in case of a shared interrupt.
  252.  * This function should only be called for a _very_ short time to change some
  253.  * internal data, that may not be changed by the interrupt at the same time.
  254.  * ami_(enable|disable)_irq calls may also be nested.
  255.  */
  256. void amiga_enable_irq(unsigned int irq)
  257. {
  258. if (irq >= AMI_IRQS) {
  259. printk("%s: Unknown IRQ %dn", __FUNCTION__, irq);
  260. return;
  261. }
  262. ami_ablecount[irq]--;
  263. if (ami_ablecount[irq]<0)
  264. ami_ablecount[irq]=0;
  265. else if (ami_ablecount[irq])
  266. return;
  267. /* No action for auto-vector interrupts */
  268. if (irq >= IRQ_AMIGA_AUTO){
  269. printk("%s: Trying to enable auto-vector IRQ %in",
  270.        __FUNCTION__, irq - IRQ_AMIGA_AUTO);
  271. return;
  272. }
  273. if (irq >= IRQ_AMIGA_CIAA) {
  274. cia_set_irq(irq, 0);
  275. cia_able_irq(irq, 1);
  276. return;
  277. }
  278. /* enable the interrupt */
  279. custom.intena = IF_SETCLR | ami_intena_vals[irq];
  280. }
  281. void amiga_disable_irq(unsigned int irq)
  282. {
  283. if (irq >= AMI_IRQS) {
  284. printk("%s: Unknown IRQ %dn", __FUNCTION__, irq);
  285. return;
  286. }
  287. if (ami_ablecount[irq]++)
  288. return;
  289. /* No action for auto-vector interrupts */
  290. if (irq >= IRQ_AMIGA_AUTO) {
  291. printk("%s: Trying to disable auto-vector IRQ %in",
  292.        __FUNCTION__, irq - IRQ_AMIGA_AUTO);
  293. return;
  294. }
  295. if (irq >= IRQ_AMIGA_CIAA) {
  296. cia_able_irq(irq, 0);
  297. return;
  298. }
  299. /* disable the interrupt */
  300. custom.intena = ami_intena_vals[irq];
  301. }
  302. inline void amiga_do_irq(int irq, struct pt_regs *fp)
  303. {
  304. kstat.irqs[0][SYS_IRQS + irq]++;
  305. ami_irq_list[irq]->handler(irq, ami_irq_list[irq]->dev_id, fp);
  306. }
  307. void amiga_do_irq_list(int irq, struct pt_regs *fp)
  308. {
  309. irq_node_t *node;
  310. kstat.irqs[0][SYS_IRQS + irq]++;
  311. custom.intreq = ami_intena_vals[irq];
  312. for (node = ami_irq_list[irq]; node; node = node->next)
  313. node->handler(irq, node->dev_id, fp);
  314. }
  315. /*
  316.  * The builtin Amiga hardware interrupt handlers.
  317.  */
  318. static void ami_int1(int irq, void *dev_id, struct pt_regs *fp)
  319. {
  320. unsigned short ints = custom.intreqr & custom.intenar;
  321. /* if serial transmit buffer empty, interrupt */
  322. if (ints & IF_TBE) {
  323. custom.intreq = IF_TBE;
  324. amiga_do_irq(IRQ_AMIGA_TBE, fp);
  325. }
  326. /* if floppy disk transfer complete, interrupt */
  327. if (ints & IF_DSKBLK) {
  328. custom.intreq = IF_DSKBLK;
  329. amiga_do_irq(IRQ_AMIGA_DSKBLK, fp);
  330. }
  331. /* if software interrupt set, interrupt */
  332. if (ints & IF_SOFT) {
  333. custom.intreq = IF_SOFT;
  334. amiga_do_irq(IRQ_AMIGA_SOFT, fp);
  335. }
  336. }
  337. static void ami_int3(int irq, void *dev_id, struct pt_regs *fp)
  338. {
  339. unsigned short ints = custom.intreqr & custom.intenar;
  340. /* if a blitter interrupt */
  341. if (ints & IF_BLIT) {
  342. custom.intreq = IF_BLIT;
  343. amiga_do_irq(IRQ_AMIGA_BLIT, fp);
  344. }
  345. /* if a copper interrupt */
  346. if (ints & IF_COPER) {
  347. custom.intreq = IF_COPER;
  348. amiga_do_irq(IRQ_AMIGA_COPPER, fp);
  349. }
  350. /* if a vertical blank interrupt */
  351. if (ints & IF_VERTB)
  352. amiga_do_irq_list(IRQ_AMIGA_VERTB, fp);
  353. }
  354. static void ami_int4(int irq, void *dev_id, struct pt_regs *fp)
  355. {
  356. unsigned short ints = custom.intreqr & custom.intenar;
  357. /* if audio 0 interrupt */
  358. if (ints & IF_AUD0) {
  359. custom.intreq = IF_AUD0;
  360. amiga_do_irq(IRQ_AMIGA_AUD0, fp);
  361. }
  362. /* if audio 1 interrupt */
  363. if (ints & IF_AUD1) {
  364. custom.intreq = IF_AUD1;
  365. amiga_do_irq(IRQ_AMIGA_AUD1, fp);
  366. }
  367. /* if audio 2 interrupt */
  368. if (ints & IF_AUD2) {
  369. custom.intreq = IF_AUD2;
  370. amiga_do_irq(IRQ_AMIGA_AUD2, fp);
  371. }
  372. /* if audio 3 interrupt */
  373. if (ints & IF_AUD3) {
  374. custom.intreq = IF_AUD3;
  375. amiga_do_irq(IRQ_AMIGA_AUD3, fp);
  376. }
  377. }
  378. static void ami_int5(int irq, void *dev_id, struct pt_regs *fp)
  379. {
  380. unsigned short ints = custom.intreqr & custom.intenar;
  381. /* if serial receive buffer full interrupt */
  382. if (ints & IF_RBF) {
  383. /* acknowledge of IF_RBF must be done by the serial interrupt */
  384. amiga_do_irq(IRQ_AMIGA_RBF, fp);
  385. }
  386. /* if a disk sync interrupt */
  387. if (ints & IF_DSKSYN) {
  388. custom.intreq = IF_DSKSYN;
  389. amiga_do_irq(IRQ_AMIGA_DSKSYN, fp);
  390. }
  391. }
  392. static void ami_int7(int irq, void *dev_id, struct pt_regs *fp)
  393. {
  394. panic ("level 7 interrupt receivedn");
  395. }
  396. #ifdef CONFIG_APUS
  397. /* The PPC irq handling links all handlers requested on the same vector
  398.    and executes them in a loop. Having ami_badint at the end of the chain
  399.    is a bad idea. */
  400. void (*amiga_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = {
  401. NULL, ami_int1, NULL, ami_int3,
  402. ami_int4, ami_int5, NULL, ami_int7
  403. };
  404. #else
  405. void (*amiga_default_handler[SYS_IRQS])(int, void *, struct pt_regs *) = {
  406. ami_badint, ami_int1, ami_badint, ami_int3,
  407. ami_int4, ami_int5, ami_badint, ami_int7
  408. };
  409. #endif
  410. int amiga_get_irq_list(char *buf)
  411. {
  412. int i, len = 0;
  413. irq_node_t *node;
  414. for (i = 0; i < AMI_STD_IRQS; i++) {
  415. if (!(node = ami_irq_list[i]))
  416. continue;
  417. len += sprintf(buf+len, "ami  %2d: %10u ", i,
  418.                kstat.irqs[0][SYS_IRQS + i]);
  419. do {
  420. if (node->flags & SA_INTERRUPT)
  421. len += sprintf(buf+len, "F ");
  422. else
  423. len += sprintf(buf+len, "  ");
  424. len += sprintf(buf+len, "%sn", node->devname);
  425. if ((node = node->next))
  426. len += sprintf(buf+len, "                    ");
  427. } while (node);
  428. }
  429. len += cia_get_irq_list(&ciaa_base, buf+len);
  430. len += cia_get_irq_list(&ciab_base, buf+len);
  431. return len;
  432. }