diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/pcmcia/sa1100_h3600.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/pcmcia/sa1100_h3600.c')
-rw-r--r-- | drivers/pcmcia/sa1100_h3600.c | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/drivers/pcmcia/sa1100_h3600.c b/drivers/pcmcia/sa1100_h3600.c new file mode 100644 index 000000000000..64fd5e37f2d2 --- /dev/null +++ b/drivers/pcmcia/sa1100_h3600.c | |||
@@ -0,0 +1,142 @@ | |||
1 | /* | ||
2 | * drivers/pcmcia/sa1100_h3600.c | ||
3 | * | ||
4 | * PCMCIA implementation routines for H3600 | ||
5 | * | ||
6 | */ | ||
7 | #include <linux/module.h> | ||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/sched.h> | ||
10 | #include <linux/device.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/delay.h> | ||
14 | |||
15 | #include <asm/hardware.h> | ||
16 | #include <asm/irq.h> | ||
17 | #include <asm/mach-types.h> | ||
18 | #include <asm/arch/h3600.h> | ||
19 | |||
20 | #include "sa1100_generic.h" | ||
21 | |||
22 | static struct pcmcia_irqs irqs[] = { | ||
23 | { 0, IRQ_GPIO_H3600_PCMCIA_CD0, "PCMCIA CD0" }, | ||
24 | { 1, IRQ_GPIO_H3600_PCMCIA_CD1, "PCMCIA CD1" } | ||
25 | }; | ||
26 | |||
27 | static int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt) | ||
28 | { | ||
29 | skt->irq = skt->nr ? IRQ_GPIO_H3600_PCMCIA_IRQ1 | ||
30 | : IRQ_GPIO_H3600_PCMCIA_IRQ0; | ||
31 | |||
32 | |||
33 | return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); | ||
34 | } | ||
35 | |||
36 | static void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) | ||
37 | { | ||
38 | soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); | ||
39 | |||
40 | /* Disable CF bus: */ | ||
41 | clr_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON); | ||
42 | clr_h3600_egpio(IPAQ_EGPIO_OPT_ON); | ||
43 | set_h3600_egpio(IPAQ_EGPIO_OPT_RESET); | ||
44 | } | ||
45 | |||
46 | static void | ||
47 | h3600_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) | ||
48 | { | ||
49 | unsigned long levels = GPLR; | ||
50 | |||
51 | switch (skt->nr) { | ||
52 | case 0: | ||
53 | state->detect = levels & GPIO_H3600_PCMCIA_CD0 ? 0 : 1; | ||
54 | state->ready = levels & GPIO_H3600_PCMCIA_IRQ0 ? 1 : 0; | ||
55 | state->bvd1 = 0; | ||
56 | state->bvd2 = 0; | ||
57 | state->wrprot = 0; /* Not available on H3600. */ | ||
58 | state->vs_3v = 0; | ||
59 | state->vs_Xv = 0; | ||
60 | break; | ||
61 | |||
62 | case 1: | ||
63 | state->detect = levels & GPIO_H3600_PCMCIA_CD1 ? 0 : 1; | ||
64 | state->ready = levels & GPIO_H3600_PCMCIA_IRQ1 ? 1 : 0; | ||
65 | state->bvd1 = 0; | ||
66 | state->bvd2 = 0; | ||
67 | state->wrprot = 0; /* Not available on H3600. */ | ||
68 | state->vs_3v = 0; | ||
69 | state->vs_Xv = 0; | ||
70 | break; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | static int | ||
75 | h3600_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) | ||
76 | { | ||
77 | if (state->Vcc != 0 && state->Vcc != 33 && state->Vcc != 50) { | ||
78 | printk(KERN_ERR "h3600_pcmcia: unrecognized Vcc %u.%uV\n", | ||
79 | state->Vcc / 10, state->Vcc % 10); | ||
80 | return -1; | ||
81 | } | ||
82 | |||
83 | if (state->flags & SS_RESET) | ||
84 | set_h3600_egpio(IPAQ_EGPIO_CARD_RESET); | ||
85 | else | ||
86 | clr_h3600_egpio(IPAQ_EGPIO_CARD_RESET); | ||
87 | |||
88 | /* Silently ignore Vpp, output enable, speaker enable. */ | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static void h3600_pcmcia_socket_init(struct soc_pcmcia_socket *skt) | ||
94 | { | ||
95 | /* Enable CF bus: */ | ||
96 | set_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON); | ||
97 | set_h3600_egpio(IPAQ_EGPIO_OPT_ON); | ||
98 | clr_h3600_egpio(IPAQ_EGPIO_OPT_RESET); | ||
99 | |||
100 | msleep(10); | ||
101 | |||
102 | soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs)); | ||
103 | } | ||
104 | |||
105 | static void h3600_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) | ||
106 | { | ||
107 | soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs)); | ||
108 | |||
109 | /* | ||
110 | * FIXME: This doesn't fit well. We don't have the mechanism in | ||
111 | * the generic PCMCIA layer to deal with the idea of two sockets | ||
112 | * on one bus. We rely on the cs.c behaviour shutting down | ||
113 | * socket 0 then socket 1. | ||
114 | */ | ||
115 | if (skt->nr == 1) { | ||
116 | clr_h3600_egpio(IPAQ_EGPIO_OPT_ON); | ||
117 | clr_h3600_egpio(IPAQ_EGPIO_OPT_NVRAM_ON); | ||
118 | /* hmm, does this suck power? */ | ||
119 | set_h3600_egpio(IPAQ_EGPIO_OPT_RESET); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | struct pcmcia_low_level h3600_pcmcia_ops = { | ||
124 | .owner = THIS_MODULE, | ||
125 | .hw_init = h3600_pcmcia_hw_init, | ||
126 | .hw_shutdown = h3600_pcmcia_hw_shutdown, | ||
127 | .socket_state = h3600_pcmcia_socket_state, | ||
128 | .configure_socket = h3600_pcmcia_configure_socket, | ||
129 | |||
130 | .socket_init = h3600_pcmcia_socket_init, | ||
131 | .socket_suspend = h3600_pcmcia_socket_suspend, | ||
132 | }; | ||
133 | |||
134 | int __init pcmcia_h3600_init(struct device *dev) | ||
135 | { | ||
136 | int ret = -ENODEV; | ||
137 | |||
138 | if (machine_is_h3600()) | ||
139 | ret = sa11xx_drv_pcmcia_probe(dev, &h3600_pcmcia_ops, 0, 2); | ||
140 | |||
141 | return ret; | ||
142 | } | ||