diff options
author | Aneesh V <aneesh@ti.com> | 2012-04-27 08:24:07 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-02 13:56:30 -0400 |
commit | 68b4aee35d1fb8399068aa6e6c908584690d0b06 (patch) | |
tree | 8ab447cf4a83d0fa6020663c96d440e3dbfd331e /drivers/memory | |
parent | a93de288aad3b046935d626065d4bcbb7d93b093 (diff) |
memory: emif: add interrupt and temperature handling
Add an ISR for EMIF that:
1. reports details of access errors
2. takes action on thermal events
Also clear all interrupts on shut-down. Pending IRQs
may casue problems during warm-reset.
Temperature handling:
EMIF can be configured to poll the temperature level
of an LPDDR2 device from the MR4 mode register in the
device. EMIF generates an interrupt whenever it identifies
a temperature level change between two consecutive pollings.
Some of the timing parameters need to be de-rated at high
temperatures. The interrupt handler takes care of doing
this and also takes care of going back to nominal settings
when temperature falls back to nominal levels.
Signed-off-by: Aneesh V <aneesh@ti.com>
Reviewed-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Reviewed-by: Benoit Cousson <b-cousson@ti.com>
[santosh.shilimkar@ti.com: Moved to drivers/memory from drivers/misc]
Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/memory')
-rw-r--r-- | drivers/memory/emif.c | 211 |
1 files changed, 209 insertions, 2 deletions
diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c index bd116eb8c738..a8dcf3515573 100644 --- a/drivers/memory/emif.c +++ b/drivers/memory/emif.c | |||
@@ -545,6 +545,42 @@ static u32 get_pwr_mgmt_ctrl(u32 freq, struct emif_data *emif, u32 ip_rev) | |||
545 | } | 545 | } |
546 | 546 | ||
547 | /* | 547 | /* |
548 | * Get the temperature level of the EMIF instance: | ||
549 | * Reads the MR4 register of attached SDRAM parts to find out the temperature | ||
550 | * level. If there are two parts attached(one on each CS), then the temperature | ||
551 | * level for the EMIF instance is the higher of the two temperatures. | ||
552 | */ | ||
553 | static void get_temperature_level(struct emif_data *emif) | ||
554 | { | ||
555 | u32 temp, temperature_level; | ||
556 | void __iomem *base; | ||
557 | |||
558 | base = emif->base; | ||
559 | |||
560 | /* Read mode register 4 */ | ||
561 | writel(DDR_MR4, base + EMIF_LPDDR2_MODE_REG_CONFIG); | ||
562 | temperature_level = readl(base + EMIF_LPDDR2_MODE_REG_DATA); | ||
563 | temperature_level = (temperature_level & MR4_SDRAM_REF_RATE_MASK) >> | ||
564 | MR4_SDRAM_REF_RATE_SHIFT; | ||
565 | |||
566 | if (emif->plat_data->device_info->cs1_used) { | ||
567 | writel(DDR_MR4 | CS_MASK, base + EMIF_LPDDR2_MODE_REG_CONFIG); | ||
568 | temp = readl(base + EMIF_LPDDR2_MODE_REG_DATA); | ||
569 | temp = (temp & MR4_SDRAM_REF_RATE_MASK) | ||
570 | >> MR4_SDRAM_REF_RATE_SHIFT; | ||
571 | temperature_level = max(temp, temperature_level); | ||
572 | } | ||
573 | |||
574 | /* treat everything less than nominal(3) in MR4 as nominal */ | ||
575 | if (unlikely(temperature_level < SDRAM_TEMP_NOMINAL)) | ||
576 | temperature_level = SDRAM_TEMP_NOMINAL; | ||
577 | |||
578 | /* if we get reserved value in MR4 persist with the existing value */ | ||
579 | if (likely(temperature_level != SDRAM_TEMP_RESERVED_4)) | ||
580 | emif->temperature_level = temperature_level; | ||
581 | } | ||
582 | |||
583 | /* | ||
548 | * Program EMIF shadow registers that are not dependent on temperature | 584 | * Program EMIF shadow registers that are not dependent on temperature |
549 | * or voltage | 585 | * or voltage |
550 | */ | 586 | */ |
@@ -627,6 +663,158 @@ out: | |||
627 | writel(ref_ctrl, base + EMIF_SDRAM_REFRESH_CTRL_SHDW); | 663 | writel(ref_ctrl, base + EMIF_SDRAM_REFRESH_CTRL_SHDW); |
628 | } | 664 | } |
629 | 665 | ||
666 | static irqreturn_t handle_temp_alert(void __iomem *base, struct emif_data *emif) | ||
667 | { | ||
668 | u32 old_temp_level; | ||
669 | irqreturn_t ret = IRQ_HANDLED; | ||
670 | |||
671 | spin_lock_irqsave(&emif_lock, irq_state); | ||
672 | old_temp_level = emif->temperature_level; | ||
673 | get_temperature_level(emif); | ||
674 | |||
675 | if (unlikely(emif->temperature_level == old_temp_level)) { | ||
676 | goto out; | ||
677 | } else if (!emif->curr_regs) { | ||
678 | dev_err(emif->dev, "temperature alert before registers are calculated, not de-rating timings\n"); | ||
679 | goto out; | ||
680 | } | ||
681 | |||
682 | if (emif->temperature_level < old_temp_level || | ||
683 | emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) { | ||
684 | /* | ||
685 | * Temperature coming down - defer handling to thread OR | ||
686 | * Temperature far too high - do kernel_power_off() from | ||
687 | * thread context | ||
688 | */ | ||
689 | ret = IRQ_WAKE_THREAD; | ||
690 | } else { | ||
691 | /* Temperature is going up - handle immediately */ | ||
692 | setup_temperature_sensitive_regs(emif, emif->curr_regs); | ||
693 | do_freq_update(); | ||
694 | } | ||
695 | |||
696 | out: | ||
697 | spin_unlock_irqrestore(&emif_lock, irq_state); | ||
698 | return ret; | ||
699 | } | ||
700 | |||
701 | static irqreturn_t emif_interrupt_handler(int irq, void *dev_id) | ||
702 | { | ||
703 | u32 interrupts; | ||
704 | struct emif_data *emif = dev_id; | ||
705 | void __iomem *base = emif->base; | ||
706 | struct device *dev = emif->dev; | ||
707 | irqreturn_t ret = IRQ_HANDLED; | ||
708 | |||
709 | /* Save the status and clear it */ | ||
710 | interrupts = readl(base + EMIF_SYSTEM_OCP_INTERRUPT_STATUS); | ||
711 | writel(interrupts, base + EMIF_SYSTEM_OCP_INTERRUPT_STATUS); | ||
712 | |||
713 | /* | ||
714 | * Handle temperature alert | ||
715 | * Temperature alert should be same for all ports | ||
716 | * So, it's enough to process it only for one of the ports | ||
717 | */ | ||
718 | if (interrupts & TA_SYS_MASK) | ||
719 | ret = handle_temp_alert(base, emif); | ||
720 | |||
721 | if (interrupts & ERR_SYS_MASK) | ||
722 | dev_err(dev, "Access error from SYS port - %x\n", interrupts); | ||
723 | |||
724 | if (emif->plat_data->hw_caps & EMIF_HW_CAPS_LL_INTERFACE) { | ||
725 | /* Save the status and clear it */ | ||
726 | interrupts = readl(base + EMIF_LL_OCP_INTERRUPT_STATUS); | ||
727 | writel(interrupts, base + EMIF_LL_OCP_INTERRUPT_STATUS); | ||
728 | |||
729 | if (interrupts & ERR_LL_MASK) | ||
730 | dev_err(dev, "Access error from LL port - %x\n", | ||
731 | interrupts); | ||
732 | } | ||
733 | |||
734 | return ret; | ||
735 | } | ||
736 | |||
737 | static irqreturn_t emif_threaded_isr(int irq, void *dev_id) | ||
738 | { | ||
739 | struct emif_data *emif = dev_id; | ||
740 | |||
741 | if (emif->temperature_level == SDRAM_TEMP_VERY_HIGH_SHUTDOWN) { | ||
742 | dev_emerg(emif->dev, "SDRAM temperature exceeds operating limit.. Needs shut down!!!\n"); | ||
743 | kernel_power_off(); | ||
744 | return IRQ_HANDLED; | ||
745 | } | ||
746 | |||
747 | spin_lock_irqsave(&emif_lock, irq_state); | ||
748 | |||
749 | if (emif->curr_regs) { | ||
750 | setup_temperature_sensitive_regs(emif, emif->curr_regs); | ||
751 | do_freq_update(); | ||
752 | } else { | ||
753 | dev_err(emif->dev, "temperature alert before registers are calculated, not de-rating timings\n"); | ||
754 | } | ||
755 | |||
756 | spin_unlock_irqrestore(&emif_lock, irq_state); | ||
757 | |||
758 | return IRQ_HANDLED; | ||
759 | } | ||
760 | |||
761 | static void clear_all_interrupts(struct emif_data *emif) | ||
762 | { | ||
763 | void __iomem *base = emif->base; | ||
764 | |||
765 | writel(readl(base + EMIF_SYSTEM_OCP_INTERRUPT_STATUS), | ||
766 | base + EMIF_SYSTEM_OCP_INTERRUPT_STATUS); | ||
767 | if (emif->plat_data->hw_caps & EMIF_HW_CAPS_LL_INTERFACE) | ||
768 | writel(readl(base + EMIF_LL_OCP_INTERRUPT_STATUS), | ||
769 | base + EMIF_LL_OCP_INTERRUPT_STATUS); | ||
770 | } | ||
771 | |||
772 | static void disable_and_clear_all_interrupts(struct emif_data *emif) | ||
773 | { | ||
774 | void __iomem *base = emif->base; | ||
775 | |||
776 | /* Disable all interrupts */ | ||
777 | writel(readl(base + EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_SET), | ||
778 | base + EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_CLEAR); | ||
779 | if (emif->plat_data->hw_caps & EMIF_HW_CAPS_LL_INTERFACE) | ||
780 | writel(readl(base + EMIF_LL_OCP_INTERRUPT_ENABLE_SET), | ||
781 | base + EMIF_LL_OCP_INTERRUPT_ENABLE_CLEAR); | ||
782 | |||
783 | /* Clear all interrupts */ | ||
784 | clear_all_interrupts(emif); | ||
785 | } | ||
786 | |||
787 | static int __init_or_module setup_interrupts(struct emif_data *emif, u32 irq) | ||
788 | { | ||
789 | u32 interrupts, type; | ||
790 | void __iomem *base = emif->base; | ||
791 | |||
792 | type = emif->plat_data->device_info->type; | ||
793 | |||
794 | clear_all_interrupts(emif); | ||
795 | |||
796 | /* Enable interrupts for SYS interface */ | ||
797 | interrupts = EN_ERR_SYS_MASK; | ||
798 | if (type == DDR_TYPE_LPDDR2_S2 || type == DDR_TYPE_LPDDR2_S4) | ||
799 | interrupts |= EN_TA_SYS_MASK; | ||
800 | writel(interrupts, base + EMIF_SYSTEM_OCP_INTERRUPT_ENABLE_SET); | ||
801 | |||
802 | /* Enable interrupts for LL interface */ | ||
803 | if (emif->plat_data->hw_caps & EMIF_HW_CAPS_LL_INTERFACE) { | ||
804 | /* TA need not be enabled for LL */ | ||
805 | interrupts = EN_ERR_LL_MASK; | ||
806 | writel(interrupts, base + EMIF_LL_OCP_INTERRUPT_ENABLE_SET); | ||
807 | } | ||
808 | |||
809 | /* setup IRQ handlers */ | ||
810 | return devm_request_threaded_irq(emif->dev, irq, | ||
811 | emif_interrupt_handler, | ||
812 | emif_threaded_isr, | ||
813 | 0, dev_name(emif->dev), | ||
814 | emif); | ||
815 | |||
816 | } | ||
817 | |||
630 | static void get_default_timings(struct emif_data *emif) | 818 | static void get_default_timings(struct emif_data *emif) |
631 | { | 819 | { |
632 | struct emif_platform_data *pd = emif->plat_data; | 820 | struct emif_platform_data *pd = emif->plat_data; |
@@ -803,6 +991,7 @@ static int __init_or_module emif_probe(struct platform_device *pdev) | |||
803 | { | 991 | { |
804 | struct emif_data *emif; | 992 | struct emif_data *emif; |
805 | struct resource *res; | 993 | struct resource *res; |
994 | int irq; | ||
806 | 995 | ||
807 | emif = get_device_details(pdev); | 996 | emif = get_device_details(pdev); |
808 | if (!emif) { | 997 | if (!emif) { |
@@ -831,6 +1020,16 @@ static int __init_or_module emif_probe(struct platform_device *pdev) | |||
831 | goto error; | 1020 | goto error; |
832 | } | 1021 | } |
833 | 1022 | ||
1023 | irq = platform_get_irq(pdev, 0); | ||
1024 | if (irq < 0) { | ||
1025 | dev_err(emif->dev, "%s: error getting IRQ resource - %d\n", | ||
1026 | __func__, irq); | ||
1027 | goto error; | ||
1028 | } | ||
1029 | |||
1030 | disable_and_clear_all_interrupts(emif); | ||
1031 | setup_interrupts(emif, irq); | ||
1032 | |||
834 | /* One-time actions taken on probing the first device */ | 1033 | /* One-time actions taken on probing the first device */ |
835 | if (!emif1) { | 1034 | if (!emif1) { |
836 | emif1 = emif; | 1035 | emif1 = emif; |
@@ -843,14 +1042,21 @@ static int __init_or_module emif_probe(struct platform_device *pdev) | |||
843 | */ | 1042 | */ |
844 | } | 1043 | } |
845 | 1044 | ||
846 | dev_info(&pdev->dev, "%s: device configured with addr = %p\n", | 1045 | dev_info(&pdev->dev, "%s: device configured with addr = %p and IRQ%d\n", |
847 | __func__, emif->base); | 1046 | __func__, emif->base, irq); |
848 | 1047 | ||
849 | return 0; | 1048 | return 0; |
850 | error: | 1049 | error: |
851 | return -ENODEV; | 1050 | return -ENODEV; |
852 | } | 1051 | } |
853 | 1052 | ||
1053 | static void emif_shutdown(struct platform_device *pdev) | ||
1054 | { | ||
1055 | struct emif_data *emif = platform_get_drvdata(pdev); | ||
1056 | |||
1057 | disable_and_clear_all_interrupts(emif); | ||
1058 | } | ||
1059 | |||
854 | static int get_emif_reg_values(struct emif_data *emif, u32 freq, | 1060 | static int get_emif_reg_values(struct emif_data *emif, u32 freq, |
855 | struct emif_regs *regs) | 1061 | struct emif_regs *regs) |
856 | { | 1062 | { |
@@ -1154,6 +1360,7 @@ static void __attribute__((unused)) freq_post_notify_handling(void) | |||
1154 | } | 1360 | } |
1155 | 1361 | ||
1156 | static struct platform_driver emif_driver = { | 1362 | static struct platform_driver emif_driver = { |
1363 | .shutdown = emif_shutdown, | ||
1157 | .driver = { | 1364 | .driver = { |
1158 | .name = "emif", | 1365 | .name = "emif", |
1159 | }, | 1366 | }, |