Merge branch 'packaging' of git://git.kernel.org/pub/scm/linux/kernel/git/mmarek...
[cascardo/linux.git] / drivers / staging / olpc_dcon / olpc_dcon_xo_1.c
1 /*
2  * Mainly by David Woodhouse, somewhat modified by Jordan Crouse
3  *
4  * Copyright © 2006-2007  Red Hat, Inc.
5  * Copyright © 2006-2007  Advanced Micro Devices, Inc.
6  * Copyright © 2009       VIA Technology, Inc.
7  * Copyright (c) 2010  Andres Salomon <dilinger@queued.net>
8  *
9  * This program is free software.  You can redistribute it and/or
10  * modify it under the terms of version 2 of the GNU General Public
11  * License as published by the Free Software Foundation.
12  */
13
14 #include <asm/olpc.h>
15
16 #include "olpc_dcon.h"
17
18 /* Base address of the GPIO registers */
19 static unsigned long gpio_base;
20
21 /*
22  * List of GPIOs that we care about:
23  * (in)  GPIO12   -- DCONBLANK
24  * (in)  GPIO[56] -- DCONSTAT[01]
25  * (out) GPIO11   -- DCONLOAD
26  */
27
28 #define IN_GPIOS ((1<<5) | (1<<6) | (1<<7) | (1<<12))
29 #define OUT_GPIOS (1<<11)
30
31 static int dcon_init_xo_1(void)
32 {
33         unsigned long lo, hi;
34         unsigned char lob;
35
36         rdmsr(MSR_LBAR_GPIO, lo, hi);
37
38         /* Check the mask and whether GPIO is enabled (sanity check) */
39         if (hi != 0x0000f001) {
40                 printk(KERN_ERR "GPIO not enabled -- cannot use DCON\n");
41                 return -ENODEV;
42         }
43
44         /* Mask off the IO base address */
45         gpio_base = lo & 0x0000ff00;
46
47         /* Turn off the event enable for GPIO7 just to be safe */
48         outl(1 << (16+7), gpio_base + GPIOx_EVNT_EN);
49
50         /* Set the directions for the GPIO pins */
51         outl(OUT_GPIOS | (IN_GPIOS << 16), gpio_base + GPIOx_OUT_EN);
52         outl(IN_GPIOS | (OUT_GPIOS << 16), gpio_base + GPIOx_IN_EN);
53
54         /* Set up the interrupt mappings */
55
56         /* Set the IRQ to pair 2 */
57         geode_gpio_event_irq(OLPC_GPIO_DCON_IRQ, 2);
58
59         /* Enable group 2 to trigger the DCON interrupt */
60         geode_gpio_set_irq(2, DCON_IRQ);
61
62         /* Select edge level for interrupt (in PIC) */
63         lob = inb(0x4d0);
64         lob &= ~(1 << DCON_IRQ);
65         outb(lob, 0x4d0);
66
67         /* Register the interupt handler */
68         if (request_irq(DCON_IRQ, &dcon_interrupt, 0, "DCON", &dcon_driver))
69                 return -EIO;
70
71         /* Clear INV_EN for GPIO7 (DCONIRQ) */
72         outl((1<<(16+7)), gpio_base + GPIOx_INV_EN);
73
74         /* Enable filter for GPIO12 (DCONBLANK) */
75         outl(1<<(12), gpio_base + GPIOx_IN_FLTR_EN);
76
77         /* Disable filter for GPIO7 */
78         outl(1<<(16+7), gpio_base + GPIOx_IN_FLTR_EN);
79
80         /* Disable event counter for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */
81
82         outl(1<<(16+7), gpio_base + GPIOx_EVNTCNT_EN);
83         outl(1<<(16+12), gpio_base + GPIOx_EVNTCNT_EN);
84
85         /* Add GPIO12 to the Filter Event Pair #7 */
86         outb(12, gpio_base + GPIO_FE7_SEL);
87
88         /* Turn off negative Edge Enable for GPIO12 */
89         outl(1<<(16+12), gpio_base + GPIOx_NEGEDGE_EN);
90
91         /* Enable negative Edge Enable for GPIO7 */
92         outl(1<<7, gpio_base + GPIOx_NEGEDGE_EN);
93
94         /* Zero the filter amount for Filter Event Pair #7 */
95         outw(0, gpio_base + GPIO_FLT7_AMNT);
96
97         /* Clear the negative edge status for GPIO7 and GPIO12 */
98         outl((1<<7) | (1<<12), gpio_base+0x4c);
99
100         /* FIXME:  Clear the posiitive status as well, just to be sure */
101         outl((1<<7) | (1<<12), gpio_base+0x48);
102
103         /* Enable events for GPIO7 (DCONIRQ) and GPIO12 (DCONBLANK) */
104         outl((1<<(7))|(1<<12), gpio_base + GPIOx_EVNT_EN);
105
106         /* Determine the current state by reading the GPIO bit */
107         /* Earlier stages of the boot process have established the state */
108         dcon_source = inl(gpio_base + GPIOx_OUT_VAL) & (1<<11)
109                 ? DCON_SOURCE_CPU
110                 : DCON_SOURCE_DCON;
111         dcon_pending = dcon_source;
112
113         return 0;
114 }
115
116 static void dcon_wiggle_xo_1(void)
117 {
118         int x;
119
120         /*
121          * According to HiMax, when powering the DCON up we should hold
122          * SMB_DATA high for 8 SMB_CLK cycles.  This will force the DCON
123          * state machine to reset to a (sane) initial state.  Mitch Bradley
124          * did some testing and discovered that holding for 16 SMB_CLK cycles
125          * worked a lot more reliably, so that's what we do here.
126          *
127          * According to the cs5536 spec, to set GPIO14 to SMB_CLK we must
128          * simultaneously set AUX1 IN/OUT to GPIO14; ditto for SMB_DATA and
129          * GPIO15.
130          */
131         geode_gpio_set(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_VAL);
132         geode_gpio_set(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_ENABLE);
133         geode_gpio_clear(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1);
134         geode_gpio_clear(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX2);
135         geode_gpio_clear(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1);
136
137         for (x = 0; x < 16; x++) {
138                 udelay(5);
139                 geode_gpio_clear(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL);
140                 udelay(5);
141                 geode_gpio_set(OLPC_GPIO_SMB_CLK, GPIO_OUTPUT_VAL);
142         }
143         udelay(5);
144         geode_gpio_set(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_OUTPUT_AUX1);
145         geode_gpio_set(OLPC_GPIO_SMB_CLK|OLPC_GPIO_SMB_DATA, GPIO_INPUT_AUX1);
146 }
147
148 static void dcon_set_dconload_1(int val)
149 {
150         if (val)        
151                 outl(1<<11, gpio_base + GPIOx_OUT_VAL);
152         else
153                 outl(1<<(11 + 16), gpio_base + GPIOx_OUT_VAL);
154 }
155
156 static int dcon_read_status_xo_1(void)
157 {
158         int status = inl(gpio_base + GPIOx_READ_BACK) >> 5;
159         
160         /* Clear the negative edge status for GPIO7 */
161         outl(1 << 7, gpio_base + GPIOx_NEGEDGE_STS);
162
163         return status;
164 }
165
166 static struct dcon_platform_data dcon_pdata_xo_1 = {
167         .init = dcon_init_xo_1,
168         .bus_stabilize_wiggle = dcon_wiggle_xo_1,
169         .set_dconload = dcon_set_dconload_1,
170         .read_status = dcon_read_status_xo_1,
171 };