Andres Salomon | eecb3e4 | 2010-09-24 19:13:42 -0700 | [diff] [blame] | 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 | }; |