Commit | Line | Data |
---|---|---|
eecb3e4e AS |
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 | ||
ac9bbd08 TY |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | ||
eecb3e4e | 11 | #include <linux/acpi.h> |
bed4ab77 | 12 | #include <linux/delay.h> |
097cd83a AS |
13 | #include <linux/gpio.h> |
14 | #include <asm/olpc.h> | |
15 | ||
16 | /* TODO: this eventually belongs in linux/vx855.h */ | |
17 | #define NR_VX855_GPI 14 | |
18 | #define NR_VX855_GPO 13 | |
19 | #define NR_VX855_GPIO 15 | |
20 | ||
21 | #define VX855_GPI(n) (n) | |
22 | #define VX855_GPO(n) (NR_VX855_GPI + (n)) | |
23 | #define VX855_GPIO(n) (NR_VX855_GPI + NR_VX855_GPO + (n)) | |
24 | ||
25 | #include "olpc_dcon.h" | |
eecb3e4e AS |
26 | |
27 | /* Hardware setup on the XO 1.5: | |
a3712f49 SB |
28 | * DCONLOAD connects to VX855_GPIO1 (not SMBCK2) |
29 | * DCONBLANK connects to VX855_GPIO8 (not SSPICLK) unused in driver | |
eecb3e4e AS |
30 | * DCONSTAT0 connects to VX855_GPI10 (not SSPISDI) |
31 | * DCONSTAT1 connects to VX855_GPI11 (not nSSPISS) | |
8f2fb16a | 32 | * DCONIRQ connects to VX855_GPIO12 |
eecb3e4e AS |
33 | * DCONSMBDATA connects to VX855 graphics CRTSPD |
34 | * DCONSMBCLK connects to VX855 graphics CRTSPCLK | |
35 | */ | |
36 | ||
a3712f49 SB |
37 | #define VX855_GENL_PURPOSE_OUTPUT 0x44c /* PMIO_Rx4c-4f */ |
38 | #define VX855_GPI_STATUS_CHG 0x450 /* PMIO_Rx50 */ | |
39 | #define VX855_GPI_SCI_SMI 0x452 /* PMIO_Rx52 */ | |
eecb3e4e AS |
40 | #define BIT_GPIO12 0x40 |
41 | ||
42 | #define PREFIX "OLPC DCON:" | |
43 | ||
eecb3e4e AS |
44 | static void dcon_clear_irq(void) |
45 | { | |
8f2fb16a AS |
46 | /* irq status will appear in PMIO_Rx50[6] (RW1C) on gpio12 */ |
47 | outb(BIT_GPIO12, VX855_GPI_STATUS_CHG); | |
eecb3e4e AS |
48 | } |
49 | ||
50 | static int dcon_was_irq(void) | |
51 | { | |
52 | u_int8_t tmp; | |
53 | ||
8f2fb16a AS |
54 | /* irq status will appear in PMIO_Rx50[6] on gpio12 */ |
55 | tmp = inb(VX855_GPI_STATUS_CHG); | |
56 | return !!(tmp & BIT_GPIO12); | |
eecb3e4e AS |
57 | |
58 | return 0; | |
59 | } | |
60 | ||
bbe963f1 | 61 | static int dcon_init_xo_1_5(struct dcon_priv *dcon) |
eecb3e4e AS |
62 | { |
63 | unsigned int irq; | |
eecb3e4e | 64 | |
8f2fb16a | 65 | dcon_clear_irq(); |
eecb3e4e | 66 | |
8f2fb16a AS |
67 | /* set PMIO_Rx52[6] to enable SCI/SMI on gpio12 */ |
68 | outb(inb(VX855_GPI_SCI_SMI)|BIT_GPIO12, VX855_GPI_SCI_SMI); | |
eecb3e4e AS |
69 | |
70 | /* Determine the current state of DCONLOAD, likely set by firmware */ | |
8f2fb16a | 71 | /* GPIO1 */ |
bbe963f1 | 72 | dcon->curr_src = (inl(VX855_GENL_PURPOSE_OUTPUT) & 0x1000) ? |
eecb3e4e | 73 | DCON_SOURCE_CPU : DCON_SOURCE_DCON; |
bbe963f1 | 74 | dcon->pending_src = dcon->curr_src; |
eecb3e4e | 75 | |
eecb3e4e AS |
76 | /* we're sharing the IRQ with ACPI */ |
77 | irq = acpi_gbl_FADT.sci_interrupt; | |
bbe963f1 | 78 | if (request_irq(irq, &dcon_interrupt, IRQF_SHARED, "DCON", dcon)) { |
ac9bbd08 | 79 | pr_err("DCON (IRQ%d) allocation failed\n", irq); |
eecb3e4e AS |
80 | return 1; |
81 | } | |
82 | ||
eecb3e4e AS |
83 | return 0; |
84 | } | |
85 | ||
86 | static void set_i2c_line(int sda, int scl) | |
87 | { | |
88 | unsigned char tmp; | |
89 | unsigned int port = 0x26; | |
90 | ||
91 | /* FIXME: This directly accesses the CRT GPIO controller !!! */ | |
92 | outb(port, 0x3c4); | |
93 | tmp = inb(0x3c5); | |
94 | ||
95 | if (scl) | |
96 | tmp |= 0x20; | |
97 | else | |
98 | tmp &= ~0x20; | |
99 | ||
100 | if (sda) | |
101 | tmp |= 0x10; | |
102 | else | |
103 | tmp &= ~0x10; | |
104 | ||
105 | tmp |= 0x01; | |
106 | ||
107 | outb(port, 0x3c4); | |
108 | outb(tmp, 0x3c5); | |
109 | } | |
110 | ||
111 | ||
112 | static void dcon_wiggle_xo_1_5(void) | |
113 | { | |
114 | int x; | |
115 | ||
116 | /* | |
117 | * According to HiMax, when powering the DCON up we should hold | |
118 | * SMB_DATA high for 8 SMB_CLK cycles. This will force the DCON | |
119 | * state machine to reset to a (sane) initial state. Mitch Bradley | |
120 | * did some testing and discovered that holding for 16 SMB_CLK cycles | |
121 | * worked a lot more reliably, so that's what we do here. | |
a3712f49 | 122 | */ |
eecb3e4e AS |
123 | set_i2c_line(1, 1); |
124 | ||
125 | for (x = 0; x < 16; x++) { | |
126 | udelay(5); | |
127 | set_i2c_line(1, 0); | |
128 | udelay(5); | |
129 | set_i2c_line(1, 1); | |
130 | } | |
131 | udelay(5); | |
132 | ||
8f2fb16a AS |
133 | /* set PMIO_Rx52[6] to enable SCI/SMI on gpio12 */ |
134 | outb(inb(VX855_GPI_SCI_SMI)|BIT_GPIO12, VX855_GPI_SCI_SMI); | |
eecb3e4e AS |
135 | } |
136 | ||
137 | static void dcon_set_dconload_xo_1_5(int val) | |
138 | { | |
8f2fb16a | 139 | gpio_set_value(VX855_GPIO(1), val); |
eecb3e4e AS |
140 | } |
141 | ||
91762057 | 142 | static int dcon_read_status_xo_1_5(u8 *status) |
eecb3e4e | 143 | { |
eecb3e4e AS |
144 | if (!dcon_was_irq()) |
145 | return -1; | |
146 | ||
a3712f49 | 147 | /* i believe this is the same as "inb(0x44b) & 3" */ |
91762057 XW |
148 | *status = gpio_get_value(VX855_GPI(10)); |
149 | *status |= gpio_get_value(VX855_GPI(11)) << 1; | |
eecb3e4e AS |
150 | |
151 | dcon_clear_irq(); | |
152 | ||
91762057 | 153 | return 0; |
eecb3e4e AS |
154 | } |
155 | ||
097cd83a | 156 | struct dcon_platform_data dcon_pdata_xo_1_5 = { |
eecb3e4e AS |
157 | .init = dcon_init_xo_1_5, |
158 | .bus_stabilize_wiggle = dcon_wiggle_xo_1_5, | |
159 | .set_dconload = dcon_set_dconload_xo_1_5, | |
160 | .read_status = dcon_read_status_xo_1_5, | |
161 | }; |