1/* 2 * Driver for the CS5535/CS5536 Multi-Function General Purpose Timers (MFGPT) 3 * 4 * Copyright (C) 2006, Advanced Micro Devices, Inc. 5 * Copyright (C) 2007 Andres Salomon <dilinger@debian.org> 6 * Copyright (C) 2009 Andres Salomon <dilinger@collabora.co.uk> 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of version 2 of the GNU General Public License 10 * as published by the Free Software Foundation. 11 * 12 * The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book. 13 */ 14 15#include <linux/kernel.h> 16#include <linux/spinlock.h> 17#include <linux/interrupt.h> 18#include <linux/module.h> 19#include <linux/pci.h> 20#include <linux/cs5535.h> 21#include <linux/slab.h> 22 23#define DRV_NAME "cs5535-mfgpt" 24#define MFGPT_BAR 2 25 26static int mfgpt_reset_timers; 27module_param_named(mfgptfix, mfgpt_reset_timers, int, 0644); 28MODULE_PARM_DESC(mfgptfix, "Reset the MFGPT timers during init; " 29 "required by some broken BIOSes (ie, TinyBIOS < 0.99)."); 30 31struct cs5535_mfgpt_timer { 32 struct cs5535_mfgpt_chip *chip; 33 int nr; 34}; 35 36static struct cs5535_mfgpt_chip { 37 DECLARE_BITMAP(avail, MFGPT_MAX_TIMERS); 38 resource_size_t base; 39 40 struct pci_dev *pdev; 41 spinlock_t lock; 42 int initialized; 43} cs5535_mfgpt_chip; 44 45int cs5535_mfgpt_toggle_event(struct cs5535_mfgpt_timer *timer, int cmp, 46 int event, int enable) 47{ 48 uint32_t msr, mask, value, dummy; 49 int shift = (cmp == MFGPT_CMP1) ? 0 : 8; 50 51 if (!timer) { 52 WARN_ON(1); 53 return -EIO; 54 } 55 56 /* 57 * The register maps for these are described in sections 6.17.1.x of 58 * the AMD Geode CS5536 Companion Device Data Book. 59 */ 60 switch (event) { 61 case MFGPT_EVENT_RESET: 62 msr = MSR_MFGPT_NR; 63 mask = 1 << (timer->nr + 24); 64 break; 65 66 case MFGPT_EVENT_NMI: 67 msr = MSR_MFGPT_NR; 68 mask = 1 << (timer->nr + shift); 69 break; 70 71 case MFGPT_EVENT_IRQ: 72 msr = MSR_MFGPT_IRQ; 73 mask = 1 << (timer->nr + shift); 74 break; 75 76 default: 77 return -EIO; 78 } 79 80 rdmsr(msr, value, dummy); 81 82 if (enable) 83 value |= mask; 84 else 85 value &= ~mask; 86 87 wrmsr(msr, value, dummy); 88 return 0; 89} 90EXPORT_SYMBOL_GPL(cs5535_mfgpt_toggle_event); 91 92int cs5535_mfgpt_set_irq(struct cs5535_mfgpt_timer *timer, int cmp, int *irq, 93 int enable) 94{ 95 uint32_t zsel, lpc, dummy; 96 int shift; 97 98 if (!timer) { 99 WARN_ON(1); 100 return -EIO; 101 } 102 103 rdmsr(MSR_PIC_ZSEL_LOW, zsel, dummy); 104 shift = ((cmp == MFGPT_CMP1 ? 0 : 4) + timer->nr % 4) * 4; 105 if (((zsel >> shift) & 0xF) == 2) 106 return -EIO; 107 108 /* Choose IRQ: if none supplied, keep IRQ already set or use default */ 109 if (!*irq) 110 *irq = (zsel >> shift) & 0xF; 111 if (!*irq) 112 *irq = CONFIG_CS5535_MFGPT_DEFAULT_IRQ; 113 114 /* Can't use IRQ if it's 0 (=disabled), 2, or routed to LPC */ 115 if (*irq < 1 || *irq == 2 || *irq > 15) 116 return -EIO; 117 rdmsr(MSR_PIC_IRQM_LPC, lpc, dummy); 118 if (lpc & (1 << *irq)) 119 return -EIO; 120 121 /* All chosen and checked - go for it */ 122 if (cs5535_mfgpt_toggle_event(timer, cmp, MFGPT_EVENT_IRQ, enable)) 123 return -EIO; 124 if (enable) { 125 zsel = (zsel & ~(0xF << shift)) | (*irq << shift); 126 wrmsr(MSR_PIC_ZSEL_LOW, zsel, dummy); 127 } 128 129 return 0; 130} 131EXPORT_SYMBOL_GPL(cs5535_mfgpt_set_irq); 132 133struct cs5535_mfgpt_timer *cs5535_mfgpt_alloc_timer(int timer_nr, int domain) 134{ 135 struct cs5535_mfgpt_chip *mfgpt = &cs5535_mfgpt_chip; 136 struct cs5535_mfgpt_timer *timer = NULL; 137 unsigned long flags; 138 int max; 139 140 if (!mfgpt->initialized) 141 goto done; 142 143 /* only allocate timers from the working domain if requested */ 144 if (domain == MFGPT_DOMAIN_WORKING) 145 max = 6; 146 else 147 max = MFGPT_MAX_TIMERS; 148 149 if (timer_nr >= max) { 150 /* programmer error. silly programmers! */ 151 WARN_ON(1); 152 goto done; 153 } 154 155 spin_lock_irqsave(&mfgpt->lock, flags); 156 if (timer_nr < 0) { 157 unsigned long t; 158 159 /* try to find any available timer */ 160 t = find_first_bit(mfgpt->avail, max); 161 /* set timer_nr to -1 if no timers available */ 162 timer_nr = t < max ? (int) t : -1; 163 } else { 164 /* check if the requested timer's available */ 165 if (test_bit(timer_nr, mfgpt->avail)) 166 timer_nr = -1; 167 } 168 169 if (timer_nr >= 0) 170 /* if timer_nr is not -1, it's an available timer */ 171 __clear_bit(timer_nr, mfgpt->avail); 172 spin_unlock_irqrestore(&mfgpt->lock, flags); 173 174 if (timer_nr < 0) 175 goto done; 176 177 timer = kmalloc(sizeof(*timer), GFP_KERNEL); 178 if (!timer) { 179 /* aw hell */ 180 spin_lock_irqsave(&mfgpt->lock, flags); 181 __set_bit(timer_nr, mfgpt->avail); 182 spin_unlock_irqrestore(&mfgpt->lock, flags); 183 goto done; 184 } 185 timer->chip = mfgpt; 186 timer->nr = timer_nr; 187 dev_info(&mfgpt->pdev->dev, "registered timer %d\n", timer_nr); 188 189done: 190 return timer; 191} 192EXPORT_SYMBOL_GPL(cs5535_mfgpt_alloc_timer); 193 194void cs5535_mfgpt_free_timer(struct cs5535_mfgpt_timer *timer) 195{ 196 unsigned long flags; 197 uint16_t val; 198 199 /* timer can be made available again only if never set up */ 200 val = cs5535_mfgpt_read(timer, MFGPT_REG_SETUP); 201 if (!(val & MFGPT_SETUP_SETUP)) { 202 spin_lock_irqsave(&timer->chip->lock, flags); 203 __set_bit(timer->nr, timer->chip->avail); 204 spin_unlock_irqrestore(&timer->chip->lock, flags); 205 } 206 207 kfree(timer); 208} 209EXPORT_SYMBOL_GPL(cs5535_mfgpt_free_timer); 210 211uint16_t cs5535_mfgpt_read(struct cs5535_mfgpt_timer *timer, uint16_t reg) 212{ 213 return inw(timer->chip->base + reg + (timer->nr * 8)); 214} 215EXPORT_SYMBOL_GPL(cs5535_mfgpt_read); 216 217void cs5535_mfgpt_write(struct cs5535_mfgpt_timer *timer, uint16_t reg, 218 uint16_t value) 219{ 220 outw(value, timer->chip->base + reg + (timer->nr * 8)); 221} 222EXPORT_SYMBOL_GPL(cs5535_mfgpt_write); 223 224/* 225 * This is a sledgehammer that resets all MFGPT timers. This is required by 226 * some broken BIOSes which leave the system in an unstable state 227 * (TinyBIOS 0.98, for example; fixed in 0.99). It's uncertain as to 228 * whether or not this secret MSR can be used to release individual timers. 229 * Jordan tells me that he and Mitch once played w/ it, but it's unclear 230 * what the results of that were (and they experienced some instability). 231 */ 232static void __init reset_all_timers(void) 233{ 234 uint32_t val, dummy; 235 236 /* The following undocumented bit resets the MFGPT timers */ 237 val = 0xFF; dummy = 0; 238 wrmsr(MSR_MFGPT_SETUP, val, dummy); 239} 240 241/* 242 * Check whether any MFGPTs are available for the kernel to use. In most 243 * cases, firmware that uses AMD's VSA code will claim all timers during 244 * bootup; we certainly don't want to take them if they're already in use. 245 * In other cases (such as with VSAless OpenFirmware), the system firmware 246 * leaves timers available for us to use. 247 */ 248static int __init scan_timers(struct cs5535_mfgpt_chip *mfgpt) 249{ 250 struct cs5535_mfgpt_timer timer = { .chip = mfgpt }; 251 unsigned long flags; 252 int timers = 0; 253 uint16_t val; 254 int i; 255 256 if (mfgpt_reset_timers) 257 reset_all_timers(); 258 259 /* just to be safe, protect this section w/ lock */ 260 spin_lock_irqsave(&mfgpt->lock, flags); 261 for (i = 0; i < MFGPT_MAX_TIMERS; i++) { 262 timer.nr = i; 263 val = cs5535_mfgpt_read(&timer, MFGPT_REG_SETUP); 264 if (!(val & MFGPT_SETUP_SETUP)) { 265 __set_bit(i, mfgpt->avail); 266 timers++; 267 } 268 } 269 spin_unlock_irqrestore(&mfgpt->lock, flags); 270 271 return timers; 272} 273 274static int __init cs5535_mfgpt_probe(struct pci_dev *pdev, 275 const struct pci_device_id *pci_id) 276{ 277 int err, t; 278 279 /* There are two ways to get the MFGPT base address; one is by 280 * fetching it from MSR_LBAR_MFGPT, the other is by reading the 281 * PCI BAR info. The latter method is easier (especially across 282 * different architectures), so we'll stick with that for now. If 283 * it turns out to be unreliable in the face of crappy BIOSes, we 284 * can always go back to using MSRs.. */ 285 286 err = pci_enable_device_io(pdev); 287 if (err) { 288 dev_err(&pdev->dev, "can't enable device IO\n"); 289 goto done; 290 } 291 292 err = pci_request_region(pdev, MFGPT_BAR, DRV_NAME); 293 if (err) { 294 dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", MFGPT_BAR); 295 goto done; 296 } 297 298 /* set up the driver-specific struct */ 299 cs5535_mfgpt_chip.base = pci_resource_start(pdev, MFGPT_BAR); 300 cs5535_mfgpt_chip.pdev = pdev; 301 spin_lock_init(&cs5535_mfgpt_chip.lock); 302 303 dev_info(&pdev->dev, "allocated PCI BAR #%d: base 0x%llx\n", MFGPT_BAR, 304 (unsigned long long) cs5535_mfgpt_chip.base); 305 306 /* detect the available timers */ 307 t = scan_timers(&cs5535_mfgpt_chip); 308 dev_info(&pdev->dev, DRV_NAME ": %d MFGPT timers available\n", t); 309 cs5535_mfgpt_chip.initialized = 1; 310 return 0; 311 312done: 313 return err; 314} 315 316static struct pci_device_id cs5535_mfgpt_pci_tbl[] = { 317 { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, 318 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) }, 319 { 0, }, 320}; 321MODULE_DEVICE_TABLE(pci, cs5535_mfgpt_pci_tbl); 322 323/* 324 * Just like with the cs5535-gpio driver, we can't use the standard PCI driver 325 * registration stuff. It only allows only one driver to bind to each PCI 326 * device, and we want the GPIO and MFGPT drivers to be able to share a PCI 327 * device. Instead, we manually scan for the PCI device, request a single 328 * region, and keep track of the devices that we're using. 329 */ 330 331static int __init cs5535_mfgpt_scan_pci(void) 332{ 333 struct pci_dev *pdev; 334 int err = -ENODEV; 335 int i; 336 337 for (i = 0; i < ARRAY_SIZE(cs5535_mfgpt_pci_tbl); i++) { 338 pdev = pci_get_device(cs5535_mfgpt_pci_tbl[i].vendor, 339 cs5535_mfgpt_pci_tbl[i].device, NULL); 340 if (pdev) { 341 err = cs5535_mfgpt_probe(pdev, 342 &cs5535_mfgpt_pci_tbl[i]); 343 if (err) 344 pci_dev_put(pdev); 345 346 /* we only support a single CS5535/6 southbridge */ 347 break; 348 } 349 } 350 351 return err; 352} 353 354static int __init cs5535_mfgpt_init(void) 355{ 356 return cs5535_mfgpt_scan_pci(); 357} 358 359module_init(cs5535_mfgpt_init); 360 361MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>"); 362MODULE_DESCRIPTION("CS5535/CS5536 MFGPT timer driver"); 363MODULE_LICENSE("GPL"); 364