diff options
author | Robert Lee <robert.lee@freescale.com> | 2010-01-18 10:05:02 +0800 |
---|---|---|
committer | Alejandro Gonzalez <alex.gonzalez@digi.com> | 2010-05-24 11:50:03 +0200 |
commit | d03c4f893ae7ca7408ac1576a849393fcf68620a (patch) | |
tree | 56474ff190a107429bd1391f4e7ecd8ac52d99e2 /drivers/power | |
parent | c6467c4000e3be01b6159adf392ad32746e2981f (diff) |
ENGR00116049-1 [imx23] Addition of FIQ system for chip errata/bo's
[imx23] Addition of FIQ system for chip errata and bo's
Signed-off-by: Robert Lee <robert.lee@freescale.com>
Signed-off-by: Alejandro Gonzalez <alex.gonzalez@digi.com>
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/stmp37xx/Makefile | 4 | ||||
-rw-r--r-- | drivers/power/stmp37xx/fiq.S | 108 | ||||
-rw-r--r-- | drivers/power/stmp37xx/linux.c | 159 |
3 files changed, 242 insertions, 29 deletions
diff --git a/drivers/power/stmp37xx/Makefile b/drivers/power/stmp37xx/Makefile index 25b340e5b014..65f670efdc93 100644 --- a/drivers/power/stmp37xx/Makefile +++ b/drivers/power/stmp37xx/Makefile @@ -5,4 +5,6 @@ obj-$(CONFIG_BATTERY_STMP3XXX) += stmp3xxx-battery.o stmp3xxx-battery-objs := ddi_bc_api.o ddi_bc_hw.o ddi_bc_init.o \ - ddi_bc_ramp.o ddi_bc_sm.o ddi_power_battery.o linux.o + ddi_bc_ramp.o ddi_bc_sm.o ddi_power_battery.o linux.o \ + fiq.o + diff --git a/drivers/power/stmp37xx/fiq.S b/drivers/power/stmp37xx/fiq.S new file mode 100644 index 000000000000..09c185dd1536 --- /dev/null +++ b/drivers/power/stmp37xx/fiq.S @@ -0,0 +1,108 @@ +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <mach/platform.h> +#include <mach/hardware.h> +#include <asm/pgtable-hwdef.h> +#include <mach/regs-power.h> +#include <mach/regs-clkctrl.h> +#include <mach/regs-timrot.h> + + .align 5 + .globl power_fiq_start + .globl power_fiq_end + .globl power_fiq_count + .globl lock_vector_tlb + +power_fiq_start: + + ldr r8,power_reg + ldr r9,[r8,#HW_POWER_CTRL ] + ldr r10,power_off + + @ when VDDIO_BO_IRQ, + @ disabled, handled in IRQ for now + @tst r9, #BM_POWER_CTRL_VDDIO_BO_IRQ + + + @ when BATT_BO_IRQ, VDDD_BO_IRQ, VDDA_BO_IRQ, power off chip + ldr r11,power_bo + tst r9, r11 + strne r10,[r8,#HW_POWER_RESET] + + @VDD5V_DROOP_IRQ + tst r9, #BM_POWER_CTRL_VDD5V_DROOP_IRQ + beq check_dcdc4p2 + + @ handle errata + ldr r10, [r8, #HW_POWER_DCDC4P2] + orr r10,r10,#(BM_POWER_DCDC4P2_TRG) + orr r10,r10,#(BF_POWER_DCDC4P2_CMPTRIP(31)) + str r10,[r8, #(HW_POWER_DCDC4P2)] + + @ if battery is below brownout level, shutdown asap + ldr r10, [r8, #HW_POWER_STS] + tst r10, #BM_POWER_STS_BATT_BO + ldr r10, power_off + strne r10, [r8, #HW_POWER_RESET] + + @ disable viddio irq + mov r11, #BM_POWER_CTRL_ENIRQ_VDDIO_BO + str r11, [r8, #HW_POWER_CTRL_CLR] + + @ enable battery BO irq + mov r11, #BM_POWER_CTRL_BATT_BO_IRQ + str r11, [r8, #HW_POWER_CTRL_CLR] + mov r11, #BM_POWER_CTRL_ENIRQBATT_BO + str r11, [r8, #HW_POWER_CTRL_SET] + + @ disable dcdc4p2 interrupt + mov r11, #BM_POWER_CTRL_ENIRQ_DCDC4P2_BO + str r11, [r8, #HW_POWER_CTRL_CLR] + + @ disable vdd5v_droop interrupt + mov r11, #BM_POWER_CTRL_ENIRQ_VDD5V_DROOP + str r11, [r8, #HW_POWER_CTRL_CLR] + +check_dcdc4p2: + @ when DCDC4P2_BO_IRQ, + tst r9, #BM_POWER_CTRL_DCDC4P2_BO_IRQ + + mov r11, #BM_POWER_CTRL_BATT_BO_IRQ + strne r11, [r8, #HW_POWER_CTRL_CLR] + + mov r11, #BM_POWER_CTRL_ENIRQBATT_BO + strne r11, [r8, #HW_POWER_CTRL_SET] + + mov r11, #BM_POWER_CTRL_ENIRQ_DCDC4P2_BO + strne r11, [r8, #HW_POWER_CTRL_CLR] + + + + @return from fiq + subs pc,lr, #4 + +power_reg: + .long REGS_POWER_BASE +power_off: + .long 0x3e770001 +power_bo: + .long BM_POWER_CTRL_BATT_BO_IRQ | \ + BM_POWER_CTRL_VDDA_BO_IRQ | BM_POWER_CTRL_VDDD_BO_IRQ +power_fiq_count: + .long 0 +power_fiq_end: + +lock_vector_tlb: + + mov r1, r0 @ set r1 to the value of the address to be locked down + mcr p15,0,r1,c8,c7,1 @ invalidate TLB single entry to ensure that + @ LockAddr is not already in the TLB + mrc p15,0,r0,c10,c0,0 @ read the lockdown register + orr r0,r0,#1 @ set the preserve bit + mcr p15,0,r0,c10,c0,0 @ write to the lockdown register + ldr r1,[r1] @ TLB will miss, and entry will be loaded + mrc p15,0,r0,c10,c0,0 @ read the lockdown register (victim will have + @ incremented) + bic r0,r0,#1 @ clear preserve bit + mcr p15,0,r0,c10,c0,0 @ write to the lockdown registerADR r1,LockAddr + mov pc,lr @ diff --git a/drivers/power/stmp37xx/linux.c b/drivers/power/stmp37xx/linux.c index e4e62f9cb066..8f9703753664 100644 --- a/drivers/power/stmp37xx/linux.c +++ b/drivers/power/stmp37xx/linux.c @@ -4,7 +4,7 @@ * Author: Steve Longerbeam <stevel@embeddedalley.com> * * Copyright (C) 2008 EmbeddedAlley Solutions Inc. - * Copyright 2008-2009 Freescale Semiconductor, Inc. + * Copyright 2008-2010 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as @@ -25,9 +25,12 @@ #include <mach/regs-power.h> #include <mach/regs-usbphy.h> #include <mach/platform.h> +#include <mach/irqs.h> +#include <mach/regs-icoll.h> #include <linux/delay.h> - +#include <linux/proc_fs.h> #include <linux/interrupt.h> +#include <asm/fiq.h> enum application_5v_status{ _5v_connected_verified, @@ -61,6 +64,9 @@ struct stmp3xxx_info { uint32_t sm_new_5v_disconnection_jiffies; enum application_5v_status sm_5v_connection_status; + + + #define USB_ONLINE 0x01 #define USB_REG_SET 0x02 #define USB_SM_RESTART 0x04 @@ -91,6 +97,8 @@ struct stmp3xxx_info { #define OS_SHUTDOWN_BATTERY_VOLTAGE_THRESHOLD_MV 3350 #endif +#define POWER_FIQ + /* #define DEBUG_IRQS */ /* There is no direct way to detect wall power presence, so assume the AC @@ -602,6 +610,7 @@ out: return ret; } +#ifndef POWER_FIQ static irqreturn_t stmp3xxx_irq_dcdc4p2_bo(int irq, void *cookie) { @@ -647,25 +656,29 @@ static irqreturn_t stmp3xxx_irq_vdda_brnout(int irq, void *cookie) #endif return IRQ_HANDLED; } -static irqreturn_t stmp3xxx_irq_vddio_brnout(int irq, void *cookie) + +static irqreturn_t stmp3xxx_irq_vdd5v_droop(int irq, void *cookie) { #ifdef DEBUG_IRQS struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie; - dev_info(info->dev, "vddio brownout interrupt occurred\n"); - ddi_power_disable_power_interrupts(); -#else - ddi_power_handle_vddio_brnout(); + dev_info(info->dev, "vdd5v droop interrupt occurred\n"); #endif + ddi_power_handle_vdd5v_droop(); + return IRQ_HANDLED; } -static irqreturn_t stmp3xxx_irq_vdd5v_droop(int irq, void *cookie) + +#endif /* if POWER_FIQ */ + +static irqreturn_t stmp3xxx_irq_vddio_brnout(int irq, void *cookie) { #ifdef DEBUG_IRQS struct stmp3xxx_info *info = (struct stmp3xxx_info *)cookie; - dev_info(info->dev, "vdd5v droop interrupt occurred\n"); + dev_info(info->dev, "vddio brownout interrupt occurred\n"); + ddi_power_disable_power_interrupts(); +#else + ddi_power_handle_vddio_brnout(); #endif - ddi_power_handle_vdd5v_droop(); - return IRQ_HANDLED; } @@ -738,6 +751,14 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev) goto free_info; } + info->irq_vddio_brnout = platform_get_resource( + pdev, IORESOURCE_IRQ, 5); + if (info->irq_vddio_brnout == NULL) { + printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); + goto free_info; + } + +#ifndef POWER_FIQ info->irq_dcdc4p2_bo = platform_get_resource(pdev, IORESOURCE_IRQ, 1); if (info->irq_dcdc4p2_bo == NULL) { printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); @@ -762,19 +783,13 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev) goto free_info; } - info->irq_vddio_brnout = platform_get_resource( - pdev, IORESOURCE_IRQ, 5); - if (info->irq_vddio_brnout == NULL) { - printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); - goto free_info; - } info->irq_vdd5v_droop = platform_get_resource(pdev, IORESOURCE_IRQ, 6); if (info->irq_vdd5v_droop == NULL) { printk(KERN_ERR "%s: failed to get irq resouce\n", __func__); goto free_info; } - +#endif platform_set_drvdata(pdev, info); @@ -828,6 +843,15 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev) goto stop_sm; } + ret = request_irq(info->irq_vddio_brnout->start, + stmp3xxx_irq_vddio_brnout, IRQF_DISABLED, + pdev->name, info); + if (ret) { + dev_err(info->dev, "failed to request irq\n"); + goto stop_sm; + } + +#ifndef POWER_FIQ ret = request_irq(info->irq_dcdc4p2_bo->start, stmp3xxx_irq_dcdc4p2_bo, IRQF_DISABLED, pdev->name, info); @@ -860,13 +884,6 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev) goto stop_sm; } - ret = request_irq(info->irq_vddio_brnout->start, - stmp3xxx_irq_vddio_brnout, IRQF_DISABLED, - pdev->name, info); - if (ret) { - dev_err(info->dev, "failed to request irq\n"); - goto stop_sm; - } ret = request_irq(info->irq_vdd5v_droop->start, stmp3xxx_irq_vdd5v_droop, IRQF_DISABLED, @@ -875,7 +892,7 @@ static int stmp3xxx_bat_probe(struct platform_device *pdev) dev_err(info->dev, "failed to request irq\n"); goto stop_sm; } - +#endif ret = power_supply_register(&pdev->dev, &info->bat); if (ret) { @@ -912,12 +929,15 @@ unregister_bat: power_supply_unregister(&info->bat); free_irq: free_irq(info->irq_vdd5v->start, pdev); + free_irq(info->irq_vddio_brnout->start, pdev); +#ifndef POWER_FIQ free_irq(info->irq_dcdc4p2_bo->start, pdev); free_irq(info->irq_batt_brnout->start, pdev); free_irq(info->irq_vddd_brnout->start, pdev); free_irq(info->irq_vdda_brnout->start, pdev); - free_irq(info->irq_vddio_brnout->start, pdev); free_irq(info->irq_vdd5v_droop->start, pdev); +#endif + stop_sm: ddi_bc_ShutDown(); free_info: @@ -932,12 +952,14 @@ static int stmp3xxx_bat_remove(struct platform_device *pdev) if (info->regulator) regulator_put(info->regulator); free_irq(info->irq_vdd5v->start, pdev); + free_irq(info->irq_vddio_brnout->start, pdev); +#ifndef POWER_FIQ free_irq(info->irq_dcdc4p2_bo->start, pdev); free_irq(info->irq_batt_brnout->start, pdev); free_irq(info->irq_vddd_brnout->start, pdev); free_irq(info->irq_vdda_brnout->start, pdev); - free_irq(info->irq_vddio_brnout->start, pdev); free_irq(info->irq_vdd5v_droop->start, pdev); +#endif ddi_bc_ShutDown(); power_supply_unregister(&info->usb); power_supply_unregister(&info->ac); @@ -1030,8 +1052,89 @@ static struct platform_driver stmp3xxx_batdrv = { }, }; +static int power_relinquish(void *data, int relinquish) +{ + return -1; +} + +static struct fiq_handler power_fiq = { + .name = "stmp3xxx-battery", + .fiq_op = power_relinquish +}; + +static struct pt_regs fiq_regs; +extern char power_fiq_start[], power_fiq_end[]; +extern void lock_vector_tlb(void *); +extern long power_fiq_count; +static struct proc_dir_entry *power_fiq_proc; + static int __init stmp3xxx_bat_init(void) { +#ifdef POWER_FIQ + int ret; + ret = claim_fiq(&power_fiq); + if (ret) { + pr_err("Can't claim fiq"); + } else { + get_fiq_regs(&fiq_regs); + set_fiq_handler(power_fiq_start, power_fiq_end-power_fiq_start); + lock_vector_tlb((void *)0xffff0000); + lock_vector_tlb(REGS_POWER_BASE); + + /* disable interrupts to be configured as FIQs */ + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_DCDC4P2_BO)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_BATT_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_VDDD_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_VDD18_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_CLR_ADDR(IRQ_VDD5V_DROOP)); + + /* Enable these interrupts as FIQs */ + __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_DCDC4P2_BO)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_BATT_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDDD_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDD18_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENFIQ, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDD5V_DROOP)); + + /* enable FIQ functionality */ + __raw_writel(BM_ICOLL_CTRL_FIQ_FINAL_ENABLE, + HW_ICOLL_CTRL_SET_ADDR); + + /* enable these interrupts */ + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_DCDC4P2_BO)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_BATT_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDDD_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDD18_BRNOUT)); + + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + HW_ICOLL_INTERRUPTn_SET_ADDR(IRQ_VDD5V_DROOP)); + + } +#endif return platform_driver_register(&stmp3xxx_batdrv); } |