Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging-2.6
[pandora-kernel.git] / drivers / staging / olpc_dcon / olpc_dcon_xo_1_5.c
1 /*
2  * Copyright (c) 2009,2010       One Laptop per Child
3  *
4  * This program is free software.  You can redistribute it and/or
5  * modify it under the terms of version 2 of the GNU General Public
6  * License as published by the Free Software Foundation.
7  */
8
9 #include <linux/acpi.h>
10
11 /* Hardware setup on the XO 1.5:
12  *      DCONLOAD connects to
13  *              VX855_GPO12 (not nCR_PWOFF)   (rev A)
14  *              VX855_GPIO1 (not SMBCK2)      (rev B)
15  *      DCONBLANK connects to VX855_GPIO8 (not SSPICLK)  unused in driver
16  *      DCONSTAT0 connects to VX855_GPI10 (not SSPISDI)
17  *      DCONSTAT1 connects to VX855_GPI11 (not nSSPISS)
18  *      DCONIRQ connects to VX855_GPIO12 (on B3.  on B2, it goes to
19  *              SMBALRT, which doesn't work.)
20  *      DCONSMBDATA connects to VX855 graphics CRTSPD
21  *      DCONSMBCLK connects to VX855 graphics CRTSPCLK
22  */
23
24 #define TEST_B2 0   // define to test B3 paths on a modded B2 board
25
26 #define VX855_GENL_PURPOSE_OUTPUT 0x44c // PMIO_Rx4c-4f
27 #define VX855_GPI_STATUS_CHG 0x450  // PMIO_Rx50
28 #define VX855_GPI_SCI_SMI 0x452  // PMIO_Rx52
29 #define BIT_GPIO12 0x40
30
31 #define PREFIX "OLPC DCON:"
32
33 /*
34   there is no support here for DCONIRQ on 1.5 boards earlier than
35   B3.  the issue is that the DCONIRQ signal on earlier boards is
36   routed to SMBALRT, which turns out to to be a level sensitive
37   interrupt.  the DCONIRQ signal is far too short (11usec) to
38   be detected reliably in that case.  including support for
39   DCONIRQ functions no better than none at all.
40 */
41
42 static struct dcon_platform_data dcon_pdata_xo_1_5;
43
44 static void dcon_clear_irq(void)
45 {
46         if (TEST_B2 || olpc_board_at_least(olpc_board(BOARD_XO_1_5_B3))) {
47                 // irq status will appear in PMIO_Rx50[6] (RW1C) on gpio12
48                 outb(BIT_GPIO12, VX855_GPI_STATUS_CHG);
49         }
50 }
51
52 static int dcon_was_irq(void)
53 {
54         u_int8_t tmp;
55
56         if (TEST_B2 || olpc_board_at_least(olpc_board(BOARD_XO_1_5_B3))) {
57                 // irq status will appear in PMIO_Rx50[6] on gpio12
58                 tmp = inb(VX855_GPI_STATUS_CHG);
59                 return !!(tmp & BIT_GPIO12);
60         }
61
62         return 0;
63 }
64
65 static int dcon_init_xo_1_5(void)
66 {
67         unsigned int irq;
68         u_int8_t tmp;
69         struct pci_dev *pdev;
70         
71         
72         pdev = pci_get_device(PCI_VENDOR_ID_VIA,
73                               PCI_DEVICE_ID_VIA_VX855, NULL);
74         if (!pdev) {
75                 printk(KERN_ERR "cannot find VX855 PCI ID\n");
76                 return 1;
77         }
78
79         if (olpc_board_at_least(olpc_board(BOARD_XO_1_5_B1))) {
80                 pci_read_config_byte(pdev, 0x95, &tmp);
81                 pci_write_config_byte(pdev, 0x95, tmp|0x0c);
82         } else {
83                 /* Set GPO12 to GPO mode, not nCR_PWOFF */
84                 pci_read_config_byte(pdev, 0x9b, &tmp);
85                 pci_write_config_byte(pdev, 0x9b, tmp|0x01);
86         }
87
88         /* Set GPIO8 to GPIO mode, not SSPICLK */
89         pci_read_config_byte(pdev, 0xe3, &tmp);
90         pci_write_config_byte(pdev, 0xe3, tmp | 0x04);
91
92         /* Set GPI10/GPI11 to GPI mode, not SSPISDI/SSPISS */
93         pci_read_config_byte(pdev, 0xe4, &tmp);
94         pci_write_config_byte(pdev, 0xe4, tmp|0x08);
95
96         if (TEST_B2 || olpc_board_at_least(olpc_board(BOARD_XO_1_5_B3))) {
97                 // clear PMU_RxE1[6] to select SCI on GPIO12
98                 // clear PMU_RxE0[6] to choose falling edge
99                 pci_read_config_byte(pdev, 0xe1, &tmp);
100                 pci_write_config_byte(pdev, 0xe1, tmp & ~BIT_GPIO12);
101                 pci_read_config_byte(pdev, 0xe0, &tmp);
102                 pci_write_config_byte(pdev, 0xe0, tmp & ~BIT_GPIO12);
103
104                 dcon_clear_irq();
105
106                 // set   PMIO_Rx52[6] to enable SCI/SMI on gpio12
107                 outb(inb(VX855_GPI_SCI_SMI)|BIT_GPIO12, VX855_GPI_SCI_SMI);
108
109         }
110
111         /* Determine the current state of DCONLOAD, likely set by firmware */
112         if (olpc_board_at_least(olpc_board(BOARD_XO_1_5_B1))) {
113                 // GPIO1
114                 dcon_source = (inl(VX855_GENL_PURPOSE_OUTPUT) & 0x1000) ?
115                         DCON_SOURCE_CPU : DCON_SOURCE_DCON;
116         } else {
117                 // GPO12
118                 dcon_source = (inl(VX855_GENL_PURPOSE_OUTPUT) & 0x04000000) ?
119                         DCON_SOURCE_CPU : DCON_SOURCE_DCON;
120         }
121         dcon_pending = dcon_source;
122
123         pci_dev_put(pdev);
124
125         /* we're sharing the IRQ with ACPI */
126         irq = acpi_gbl_FADT.sci_interrupt;
127         if (request_irq(irq, &dcon_interrupt, IRQF_SHARED, "DCON", &dcon_driver)) {
128                 printk(KERN_ERR PREFIX "DCON (IRQ%d) allocation failed\n", irq);
129                 return 1;
130         }
131
132
133         return 0;
134 }
135
136 static void set_i2c_line(int sda, int scl)
137 {
138         unsigned char tmp;
139         unsigned int port = 0x26;
140
141         /* FIXME: This directly accesses the CRT GPIO controller !!! */
142         outb(port, 0x3c4);
143         tmp = inb(0x3c5);
144
145         if (scl)
146                 tmp |= 0x20;
147         else
148                 tmp &= ~0x20;
149
150         if (sda)
151                 tmp |= 0x10;
152         else
153                 tmp &= ~0x10;
154
155         tmp |= 0x01;
156
157         outb(port, 0x3c4);
158         outb(tmp, 0x3c5);
159 }
160
161
162 static void dcon_wiggle_xo_1_5(void)
163 {
164         int x;
165
166         /*
167          * According to HiMax, when powering the DCON up we should hold
168          * SMB_DATA high for 8 SMB_CLK cycles.  This will force the DCON
169          * state machine to reset to a (sane) initial state.  Mitch Bradley
170          * did some testing and discovered that holding for 16 SMB_CLK cycles
171          * worked a lot more reliably, so that's what we do here.
172          */
173         set_i2c_line(1, 1);
174
175         for (x = 0; x < 16; x++) {
176                 udelay(5);
177                 set_i2c_line(1, 0);
178                 udelay(5);
179                 set_i2c_line(1, 1);
180         }
181         udelay(5);
182
183         if (TEST_B2 || olpc_board_at_least(olpc_board(BOARD_XO_1_5_B3))) {
184                 // set   PMIO_Rx52[6] to enable SCI/SMI on gpio12
185                 outb(inb(VX855_GPI_SCI_SMI)|BIT_GPIO12, VX855_GPI_SCI_SMI);
186         }
187 }
188
189 static void dcon_set_dconload_xo_1_5(int val)
190 {
191         if (olpc_board_at_least(olpc_board(BOARD_XO_1_5_B1))) {
192                 gpio_set_value(VX855_GPIO(1), val);
193         } else {
194                 gpio_set_value(VX855_GPO(12), val);
195         }
196 }
197
198 static int dcon_read_status_xo_1_5(void) 
199 {
200         int status;
201         
202         if (!dcon_was_irq())
203                 return -1;
204
205         // i believe this is the same as "inb(0x44b) & 3"
206         status = gpio_get_value(VX855_GPI(10));
207         status |= gpio_get_value(VX855_GPI(11)) << 1; 
208
209         dcon_clear_irq();
210
211         return status;
212 }
213
214 static struct dcon_platform_data dcon_pdata_xo_1_5 = {
215         .init = dcon_init_xo_1_5,
216         .bus_stabilize_wiggle = dcon_wiggle_xo_1_5,
217         .set_dconload = dcon_set_dconload_xo_1_5,
218         .read_status = dcon_read_status_xo_1_5,
219 };