diff options
| -rw-r--r-- | arch/x86/kernel/hpet.c | 198 |
1 files changed, 124 insertions, 74 deletions
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 175d24be0833..88b4da373081 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c | |||
| @@ -226,22 +226,7 @@ static void hpet_reserve_platform_timers(unsigned int id) { } | |||
| 226 | */ | 226 | */ |
| 227 | static unsigned long hpet_freq; | 227 | static unsigned long hpet_freq; |
| 228 | 228 | ||
| 229 | static void hpet_legacy_set_mode(enum clock_event_mode mode, | 229 | static struct clock_event_device hpet_clockevent; |
| 230 | struct clock_event_device *evt); | ||
| 231 | static int hpet_legacy_next_event(unsigned long delta, | ||
| 232 | struct clock_event_device *evt); | ||
| 233 | |||
| 234 | /* | ||
| 235 | * The hpet clock event device | ||
| 236 | */ | ||
| 237 | static struct clock_event_device hpet_clockevent = { | ||
| 238 | .name = "hpet", | ||
| 239 | .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, | ||
| 240 | .set_mode = hpet_legacy_set_mode, | ||
| 241 | .set_next_event = hpet_legacy_next_event, | ||
| 242 | .irq = 0, | ||
| 243 | .rating = 50, | ||
| 244 | }; | ||
| 245 | 230 | ||
| 246 | static void hpet_stop_counter(void) | 231 | static void hpet_stop_counter(void) |
| 247 | { | 232 | { |
| @@ -306,64 +291,74 @@ static void hpet_legacy_clockevent_register(void) | |||
| 306 | printk(KERN_DEBUG "hpet clockevent registered\n"); | 291 | printk(KERN_DEBUG "hpet clockevent registered\n"); |
| 307 | } | 292 | } |
| 308 | 293 | ||
| 309 | static void hpet_set_mode(enum clock_event_mode mode, | 294 | static int hpet_set_periodic(struct clock_event_device *evt, int timer) |
| 310 | struct clock_event_device *evt, int timer) | ||
| 311 | { | 295 | { |
| 312 | unsigned int cfg, cmp, now; | 296 | unsigned int cfg, cmp, now; |
| 313 | uint64_t delta; | 297 | uint64_t delta; |
| 314 | 298 | ||
| 315 | switch (mode) { | 299 | hpet_stop_counter(); |
| 316 | case CLOCK_EVT_MODE_PERIODIC: | 300 | delta = ((uint64_t)(NSEC_PER_SEC / HZ)) * evt->mult; |
| 317 | hpet_stop_counter(); | 301 | delta >>= evt->shift; |
| 318 | delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * evt->mult; | 302 | now = hpet_readl(HPET_COUNTER); |
| 319 | delta >>= evt->shift; | 303 | cmp = now + (unsigned int)delta; |
| 320 | now = hpet_readl(HPET_COUNTER); | 304 | cfg = hpet_readl(HPET_Tn_CFG(timer)); |
| 321 | cmp = now + (unsigned int) delta; | 305 | cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL | |
| 322 | cfg = hpet_readl(HPET_Tn_CFG(timer)); | 306 | HPET_TN_32BIT; |
| 323 | cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | | 307 | hpet_writel(cfg, HPET_Tn_CFG(timer)); |
| 324 | HPET_TN_SETVAL | HPET_TN_32BIT; | 308 | hpet_writel(cmp, HPET_Tn_CMP(timer)); |
| 325 | hpet_writel(cfg, HPET_Tn_CFG(timer)); | 309 | udelay(1); |
| 326 | hpet_writel(cmp, HPET_Tn_CMP(timer)); | 310 | /* |
| 327 | udelay(1); | 311 | * HPET on AMD 81xx needs a second write (with HPET_TN_SETVAL |
| 328 | /* | 312 | * cleared) to T0_CMP to set the period. The HPET_TN_SETVAL |
| 329 | * HPET on AMD 81xx needs a second write (with HPET_TN_SETVAL | 313 | * bit is automatically cleared after the first write. |
| 330 | * cleared) to T0_CMP to set the period. The HPET_TN_SETVAL | 314 | * (See AMD-8111 HyperTransport I/O Hub Data Sheet, |
| 331 | * bit is automatically cleared after the first write. | 315 | * Publication # 24674) |
| 332 | * (See AMD-8111 HyperTransport I/O Hub Data Sheet, | 316 | */ |
| 333 | * Publication # 24674) | 317 | hpet_writel((unsigned int)delta, HPET_Tn_CMP(timer)); |
| 334 | */ | 318 | hpet_start_counter(); |
| 335 | hpet_writel((unsigned int) delta, HPET_Tn_CMP(timer)); | 319 | hpet_print_config(); |
| 336 | hpet_start_counter(); | ||
| 337 | hpet_print_config(); | ||
| 338 | break; | ||
| 339 | 320 | ||
| 340 | case CLOCK_EVT_MODE_ONESHOT: | 321 | return 0; |
| 341 | cfg = hpet_readl(HPET_Tn_CFG(timer)); | 322 | } |
| 342 | cfg &= ~HPET_TN_PERIODIC; | ||
| 343 | cfg |= HPET_TN_ENABLE | HPET_TN_32BIT; | ||
| 344 | hpet_writel(cfg, HPET_Tn_CFG(timer)); | ||
| 345 | break; | ||
| 346 | 323 | ||
| 347 | case CLOCK_EVT_MODE_UNUSED: | 324 | static int hpet_set_oneshot(struct clock_event_device *evt, int timer) |
| 348 | case CLOCK_EVT_MODE_SHUTDOWN: | 325 | { |
| 349 | cfg = hpet_readl(HPET_Tn_CFG(timer)); | 326 | unsigned int cfg; |
| 350 | cfg &= ~HPET_TN_ENABLE; | ||
| 351 | hpet_writel(cfg, HPET_Tn_CFG(timer)); | ||
| 352 | break; | ||
| 353 | 327 | ||
| 354 | case CLOCK_EVT_MODE_RESUME: | 328 | cfg = hpet_readl(HPET_Tn_CFG(timer)); |
| 355 | if (timer == 0) { | 329 | cfg &= ~HPET_TN_PERIODIC; |
| 356 | hpet_enable_legacy_int(); | 330 | cfg |= HPET_TN_ENABLE | HPET_TN_32BIT; |
| 357 | } else { | 331 | hpet_writel(cfg, HPET_Tn_CFG(timer)); |
| 358 | struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); | 332 | |
| 359 | irq_domain_activate_irq(irq_get_irq_data(hdev->irq)); | 333 | return 0; |
| 360 | disable_irq(hdev->irq); | 334 | } |
| 361 | irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu)); | 335 | |
| 362 | enable_irq(hdev->irq); | 336 | static int hpet_shutdown(struct clock_event_device *evt, int timer) |
| 363 | } | 337 | { |
| 364 | hpet_print_config(); | 338 | unsigned int cfg; |
| 365 | break; | 339 | |
| 340 | cfg = hpet_readl(HPET_Tn_CFG(timer)); | ||
| 341 | cfg &= ~HPET_TN_ENABLE; | ||
| 342 | hpet_writel(cfg, HPET_Tn_CFG(timer)); | ||
| 343 | |||
| 344 | return 0; | ||
| 345 | } | ||
| 346 | |||
| 347 | static int hpet_resume(struct clock_event_device *evt, int timer) | ||
| 348 | { | ||
| 349 | if (!timer) { | ||
| 350 | hpet_enable_legacy_int(); | ||
| 351 | } else { | ||
| 352 | struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); | ||
| 353 | |||
| 354 | irq_domain_activate_irq(irq_get_irq_data(hdev->irq)); | ||
| 355 | disable_irq(hdev->irq); | ||
| 356 | irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu)); | ||
| 357 | enable_irq(hdev->irq); | ||
| 366 | } | 358 | } |
| 359 | hpet_print_config(); | ||
| 360 | |||
| 361 | return 0; | ||
| 367 | } | 362 | } |
| 368 | 363 | ||
| 369 | static int hpet_next_event(unsigned long delta, | 364 | static int hpet_next_event(unsigned long delta, |
| @@ -403,10 +398,24 @@ static int hpet_next_event(unsigned long delta, | |||
| 403 | return res < HPET_MIN_CYCLES ? -ETIME : 0; | 398 | return res < HPET_MIN_CYCLES ? -ETIME : 0; |
| 404 | } | 399 | } |
| 405 | 400 | ||
| 406 | static void hpet_legacy_set_mode(enum clock_event_mode mode, | 401 | static int hpet_legacy_shutdown(struct clock_event_device *evt) |
| 407 | struct clock_event_device *evt) | 402 | { |
| 403 | return hpet_shutdown(evt, 0); | ||
| 404 | } | ||
| 405 | |||
| 406 | static int hpet_legacy_set_oneshot(struct clock_event_device *evt) | ||
| 407 | { | ||
| 408 | return hpet_set_oneshot(evt, 0); | ||
| 409 | } | ||
| 410 | |||
| 411 | static int hpet_legacy_set_periodic(struct clock_event_device *evt) | ||
| 408 | { | 412 | { |
| 409 | hpet_set_mode(mode, evt, 0); | 413 | return hpet_set_periodic(evt, 0); |
| 414 | } | ||
| 415 | |||
| 416 | static int hpet_legacy_resume(struct clock_event_device *evt) | ||
| 417 | { | ||
| 418 | return hpet_resume(evt, 0); | ||
| 410 | } | 419 | } |
| 411 | 420 | ||
| 412 | static int hpet_legacy_next_event(unsigned long delta, | 421 | static int hpet_legacy_next_event(unsigned long delta, |
| @@ -416,6 +425,22 @@ static int hpet_legacy_next_event(unsigned long delta, | |||
| 416 | } | 425 | } |
| 417 | 426 | ||
| 418 | /* | 427 | /* |
| 428 | * The hpet clock event device | ||
| 429 | */ | ||
| 430 | static struct clock_event_device hpet_clockevent = { | ||
| 431 | .name = "hpet", | ||
| 432 | .features = CLOCK_EVT_FEAT_PERIODIC | | ||
| 433 | CLOCK_EVT_FEAT_ONESHOT, | ||
| 434 | .set_state_periodic = hpet_legacy_set_periodic, | ||
| 435 | .set_state_oneshot = hpet_legacy_set_oneshot, | ||
| 436 | .set_state_shutdown = hpet_legacy_shutdown, | ||
| 437 | .tick_resume = hpet_legacy_resume, | ||
| 438 | .set_next_event = hpet_legacy_next_event, | ||
| 439 | .irq = 0, | ||
| 440 | .rating = 50, | ||
| 441 | }; | ||
| 442 | |||
| 443 | /* | ||
| 419 | * HPET MSI Support | 444 | * HPET MSI Support |
| 420 | */ | 445 | */ |
| 421 | #ifdef CONFIG_PCI_MSI | 446 | #ifdef CONFIG_PCI_MSI |
| @@ -459,11 +484,32 @@ void hpet_msi_read(struct hpet_dev *hdev, struct msi_msg *msg) | |||
| 459 | msg->address_hi = 0; | 484 | msg->address_hi = 0; |
| 460 | } | 485 | } |
| 461 | 486 | ||
| 462 | static void hpet_msi_set_mode(enum clock_event_mode mode, | 487 | static int hpet_msi_shutdown(struct clock_event_device *evt) |
| 463 | struct clock_event_device *evt) | 488 | { |
| 489 | struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); | ||
| 490 | |||
| 491 | return hpet_shutdown(evt, hdev->num); | ||
| 492 | } | ||
| 493 | |||
| 494 | static int hpet_msi_set_oneshot(struct clock_event_device *evt) | ||
| 495 | { | ||
| 496 | struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); | ||
| 497 | |||
| 498 | return hpet_set_oneshot(evt, hdev->num); | ||
| 499 | } | ||
| 500 | |||
| 501 | static int hpet_msi_set_periodic(struct clock_event_device *evt) | ||
| 464 | { | 502 | { |
| 465 | struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); | 503 | struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); |
| 466 | hpet_set_mode(mode, evt, hdev->num); | 504 | |
| 505 | return hpet_set_periodic(evt, hdev->num); | ||
| 506 | } | ||
| 507 | |||
| 508 | static int hpet_msi_resume(struct clock_event_device *evt) | ||
| 509 | { | ||
| 510 | struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); | ||
| 511 | |||
| 512 | return hpet_resume(evt, hdev->num); | ||
| 467 | } | 513 | } |
| 468 | 514 | ||
| 469 | static int hpet_msi_next_event(unsigned long delta, | 515 | static int hpet_msi_next_event(unsigned long delta, |
| @@ -523,10 +569,14 @@ static void init_one_hpet_msi_clockevent(struct hpet_dev *hdev, int cpu) | |||
| 523 | 569 | ||
| 524 | evt->rating = 110; | 570 | evt->rating = 110; |
| 525 | evt->features = CLOCK_EVT_FEAT_ONESHOT; | 571 | evt->features = CLOCK_EVT_FEAT_ONESHOT; |
| 526 | if (hdev->flags & HPET_DEV_PERI_CAP) | 572 | if (hdev->flags & HPET_DEV_PERI_CAP) { |
| 527 | evt->features |= CLOCK_EVT_FEAT_PERIODIC; | 573 | evt->features |= CLOCK_EVT_FEAT_PERIODIC; |
| 574 | evt->set_state_periodic = hpet_msi_set_periodic; | ||
| 575 | } | ||
| 528 | 576 | ||
| 529 | evt->set_mode = hpet_msi_set_mode; | 577 | evt->set_state_shutdown = hpet_msi_shutdown; |
| 578 | evt->set_state_oneshot = hpet_msi_set_oneshot; | ||
| 579 | evt->tick_resume = hpet_msi_resume; | ||
| 530 | evt->set_next_event = hpet_msi_next_event; | 580 | evt->set_next_event = hpet_msi_next_event; |
| 531 | evt->cpumask = cpumask_of(hdev->cpu); | 581 | evt->cpumask = cpumask_of(hdev->cpu); |
| 532 | 582 | ||
