| Andres Salomon | f62e518 | 2007-07-21 17:11:38 +0200 | [diff] [blame] | 1 | /* | 
|  | 2 | * AMD Geode southbridge support code | 
|  | 3 | * Copyright (C) 2006, Advanced Micro Devices, Inc. | 
| Andres Salomon | ade7614 | 2008-01-30 13:33:35 +0100 | [diff] [blame] | 4 | * Copyright (C) 2007, Andres Salomon <dilinger@debian.org> | 
| Andres Salomon | f62e518 | 2007-07-21 17:11:38 +0200 | [diff] [blame] | 5 | * | 
|  | 6 | * This program is free software; you can redistribute it and/or | 
|  | 7 | * modify it under the terms of version 2 of the GNU General Public License | 
|  | 8 | * as published by the Free Software Foundation. | 
|  | 9 | */ | 
|  | 10 |  | 
|  | 11 | #include <linux/kernel.h> | 
|  | 12 | #include <linux/module.h> | 
|  | 13 | #include <linux/ioport.h> | 
|  | 14 | #include <linux/io.h> | 
|  | 15 | #include <asm/msr.h> | 
|  | 16 | #include <asm/geode.h> | 
|  | 17 |  | 
|  | 18 | static struct { | 
|  | 19 | char *name; | 
|  | 20 | u32 msr; | 
|  | 21 | int size; | 
|  | 22 | u32 base; | 
|  | 23 | } lbars[] = { | 
|  | 24 | { "geode-pms",   MSR_LBAR_PMS, LBAR_PMS_SIZE, 0 }, | 
|  | 25 | { "geode-acpi",  MSR_LBAR_ACPI, LBAR_ACPI_SIZE, 0 }, | 
|  | 26 | { "geode-gpio",  MSR_LBAR_GPIO, LBAR_GPIO_SIZE, 0 }, | 
|  | 27 | { "geode-mfgpt", MSR_LBAR_MFGPT, LBAR_MFGPT_SIZE, 0 } | 
|  | 28 | }; | 
|  | 29 |  | 
|  | 30 | static void __init init_lbars(void) | 
|  | 31 | { | 
|  | 32 | u32 lo, hi; | 
|  | 33 | int i; | 
|  | 34 |  | 
|  | 35 | for (i = 0; i < ARRAY_SIZE(lbars); i++) { | 
|  | 36 | rdmsr(lbars[i].msr, lo, hi); | 
|  | 37 | if (hi & 0x01) | 
|  | 38 | lbars[i].base = lo & 0x0000ffff; | 
|  | 39 |  | 
|  | 40 | if (lbars[i].base == 0) | 
|  | 41 | printk(KERN_ERR "geode:  Couldn't initialize '%s'\n", | 
|  | 42 | lbars[i].name); | 
|  | 43 | } | 
|  | 44 | } | 
|  | 45 |  | 
|  | 46 | int geode_get_dev_base(unsigned int dev) | 
|  | 47 | { | 
|  | 48 | BUG_ON(dev >= ARRAY_SIZE(lbars)); | 
|  | 49 | return lbars[dev].base; | 
|  | 50 | } | 
|  | 51 | EXPORT_SYMBOL_GPL(geode_get_dev_base); | 
|  | 52 |  | 
|  | 53 | /* === GPIO API === */ | 
|  | 54 |  | 
| Andres Salomon | ade7614 | 2008-01-30 13:33:35 +0100 | [diff] [blame] | 55 | void geode_gpio_set(u32 gpio, unsigned int reg) | 
| Andres Salomon | f62e518 | 2007-07-21 17:11:38 +0200 | [diff] [blame] | 56 | { | 
|  | 57 | u32 base = geode_get_dev_base(GEODE_DEV_GPIO); | 
|  | 58 |  | 
|  | 59 | if (!base) | 
|  | 60 | return; | 
|  | 61 |  | 
| Andres Salomon | ade7614 | 2008-01-30 13:33:35 +0100 | [diff] [blame] | 62 | /* low bank register */ | 
|  | 63 | if (gpio & 0xFFFF) | 
|  | 64 | outl(gpio & 0xFFFF, base + reg); | 
|  | 65 | /* high bank register */ | 
|  | 66 | gpio >>= 16; | 
|  | 67 | if (gpio) | 
|  | 68 | outl(gpio, base + 0x80 + reg); | 
| Andres Salomon | f62e518 | 2007-07-21 17:11:38 +0200 | [diff] [blame] | 69 | } | 
|  | 70 | EXPORT_SYMBOL_GPL(geode_gpio_set); | 
|  | 71 |  | 
| Andres Salomon | ade7614 | 2008-01-30 13:33:35 +0100 | [diff] [blame] | 72 | void geode_gpio_clear(u32 gpio, unsigned int reg) | 
| Andres Salomon | f62e518 | 2007-07-21 17:11:38 +0200 | [diff] [blame] | 73 | { | 
|  | 74 | u32 base = geode_get_dev_base(GEODE_DEV_GPIO); | 
|  | 75 |  | 
|  | 76 | if (!base) | 
|  | 77 | return; | 
|  | 78 |  | 
| Andres Salomon | ade7614 | 2008-01-30 13:33:35 +0100 | [diff] [blame] | 79 | /* low bank register */ | 
|  | 80 | if (gpio & 0xFFFF) | 
|  | 81 | outl((gpio & 0xFFFF) << 16, base + reg); | 
|  | 82 | /* high bank register */ | 
|  | 83 | gpio &= (0xFFFF << 16); | 
|  | 84 | if (gpio) | 
|  | 85 | outl(gpio, base + 0x80 + reg); | 
| Andres Salomon | f62e518 | 2007-07-21 17:11:38 +0200 | [diff] [blame] | 86 | } | 
|  | 87 | EXPORT_SYMBOL_GPL(geode_gpio_clear); | 
|  | 88 |  | 
| Andres Salomon | ade7614 | 2008-01-30 13:33:35 +0100 | [diff] [blame] | 89 | int geode_gpio_isset(u32 gpio, unsigned int reg) | 
| Andres Salomon | f62e518 | 2007-07-21 17:11:38 +0200 | [diff] [blame] | 90 | { | 
|  | 91 | u32 base = geode_get_dev_base(GEODE_DEV_GPIO); | 
| Andres Salomon | ade7614 | 2008-01-30 13:33:35 +0100 | [diff] [blame] | 92 | u32 val; | 
| Andres Salomon | f62e518 | 2007-07-21 17:11:38 +0200 | [diff] [blame] | 93 |  | 
|  | 94 | if (!base) | 
|  | 95 | return 0; | 
|  | 96 |  | 
| Andres Salomon | ade7614 | 2008-01-30 13:33:35 +0100 | [diff] [blame] | 97 | /* low bank register */ | 
|  | 98 | if (gpio & 0xFFFF) { | 
|  | 99 | val = inl(base + reg) & (gpio & 0xFFFF); | 
|  | 100 | if ((gpio & 0xFFFF) == val) | 
|  | 101 | return 1; | 
|  | 102 | } | 
|  | 103 | /* high bank register */ | 
|  | 104 | gpio >>= 16; | 
|  | 105 | if (gpio) { | 
|  | 106 | val = inl(base + 0x80 + reg) & gpio; | 
|  | 107 | if (gpio == val) | 
|  | 108 | return 1; | 
|  | 109 | } | 
|  | 110 | return 0; | 
| Andres Salomon | f62e518 | 2007-07-21 17:11:38 +0200 | [diff] [blame] | 111 | } | 
|  | 112 | EXPORT_SYMBOL_GPL(geode_gpio_isset); | 
|  | 113 |  | 
|  | 114 | void geode_gpio_set_irq(unsigned int group, unsigned int irq) | 
|  | 115 | { | 
|  | 116 | u32 lo, hi; | 
|  | 117 |  | 
|  | 118 | if (group > 7 || irq > 15) | 
|  | 119 | return; | 
|  | 120 |  | 
|  | 121 | rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi); | 
|  | 122 |  | 
|  | 123 | lo &= ~(0xF << (group * 4)); | 
|  | 124 | lo |= (irq & 0xF) << (group * 4); | 
|  | 125 |  | 
|  | 126 | wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi); | 
|  | 127 | } | 
|  | 128 | EXPORT_SYMBOL_GPL(geode_gpio_set_irq); | 
|  | 129 |  | 
|  | 130 | void geode_gpio_setup_event(unsigned int gpio, int pair, int pme) | 
|  | 131 | { | 
|  | 132 | u32 base = geode_get_dev_base(GEODE_DEV_GPIO); | 
|  | 133 | u32 offset, shift, val; | 
|  | 134 |  | 
|  | 135 | if (gpio >= 24) | 
|  | 136 | offset = GPIO_MAP_W; | 
|  | 137 | else if (gpio >= 16) | 
|  | 138 | offset = GPIO_MAP_Z; | 
|  | 139 | else if (gpio >= 8) | 
|  | 140 | offset = GPIO_MAP_Y; | 
|  | 141 | else | 
|  | 142 | offset = GPIO_MAP_X; | 
|  | 143 |  | 
|  | 144 | shift = (gpio % 8) * 4; | 
|  | 145 |  | 
|  | 146 | val = inl(base + offset); | 
|  | 147 |  | 
|  | 148 | /* Clear whatever was there before */ | 
|  | 149 | val &= ~(0xF << shift); | 
|  | 150 |  | 
|  | 151 | /* And set the new value */ | 
|  | 152 |  | 
|  | 153 | val |= ((pair & 7) << shift); | 
|  | 154 |  | 
|  | 155 | /* Set the PME bit if this is a PME event */ | 
|  | 156 |  | 
|  | 157 | if (pme) | 
|  | 158 | val |= (1 << (shift + 3)); | 
|  | 159 |  | 
|  | 160 | outl(val, base + offset); | 
|  | 161 | } | 
|  | 162 | EXPORT_SYMBOL_GPL(geode_gpio_setup_event); | 
|  | 163 |  | 
| Andres Salomon | 547acec | 2008-05-07 13:07:37 -0700 | [diff] [blame] | 164 | int geode_has_vsa2(void) | 
|  | 165 | { | 
|  | 166 | static int has_vsa2 = -1; | 
|  | 167 |  | 
|  | 168 | if (has_vsa2 == -1) { | 
| Jordan Crouse | ffe6e1d | 2008-06-18 11:34:38 -0600 | [diff] [blame] | 169 | u16 val; | 
|  | 170 |  | 
| Andres Salomon | 547acec | 2008-05-07 13:07:37 -0700 | [diff] [blame] | 171 | /* | 
|  | 172 | * The VSA has virtual registers that we can query for a | 
|  | 173 | * signature. | 
|  | 174 | */ | 
|  | 175 | outw(VSA_VR_UNLOCK, VSA_VRC_INDEX); | 
|  | 176 | outw(VSA_VR_SIGNATURE, VSA_VRC_INDEX); | 
|  | 177 |  | 
| Jordan Crouse | ffe6e1d | 2008-06-18 11:34:38 -0600 | [diff] [blame] | 178 | val = inw(VSA_VRC_DATA); | 
|  | 179 | has_vsa2 = (val == AMD_VSA_SIG || val == GSW_VSA_SIG); | 
| Andres Salomon | 547acec | 2008-05-07 13:07:37 -0700 | [diff] [blame] | 180 | } | 
|  | 181 |  | 
|  | 182 | return has_vsa2; | 
|  | 183 | } | 
|  | 184 | EXPORT_SYMBOL_GPL(geode_has_vsa2); | 
|  | 185 |  | 
| Andres Salomon | f62e518 | 2007-07-21 17:11:38 +0200 | [diff] [blame] | 186 | static int __init geode_southbridge_init(void) | 
|  | 187 | { | 
|  | 188 | if (!is_geode()) | 
|  | 189 | return -ENODEV; | 
|  | 190 |  | 
|  | 191 | init_lbars(); | 
| Jordan Crouse | f087515 | 2008-02-09 23:24:08 +0100 | [diff] [blame] | 192 | (void) mfgpt_timer_setup(); | 
| Andres Salomon | f62e518 | 2007-07-21 17:11:38 +0200 | [diff] [blame] | 193 | return 0; | 
|  | 194 | } | 
|  | 195 |  | 
|  | 196 | postcore_initcall(geode_southbridge_init); |