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