From 8c43d2b0ca10cea9eb62d8996fb93f330be88ada Mon Sep 17 00:00:00 2001 From: Joe Liccese Date: Mon, 25 Jun 2012 12:22:09 -0500 Subject: [PATCH 01/12] powerpc: Add T4 LAC device tree binding & defs The Interlaken is a narrow, high speed channelized chip-to-chip interface. To facilitate interoperability between a data path device and a look-aside co-processor, the Interlaken Look-Aside protocol is defined for short transaction-related transfers. Although based on the Interlaken protocol, Interlaken Look-Aside is not directly compatible with Interlaken and can be considered a different operation mode. The Interlaken LA controller connects internal platform to Interlaken serial interface. It accepts LA command through software portals, which are system memory mapped 4KB spaces. The LA commands are then translated into the Interlaken control words and data words, which are sent on TX side to TCAM through SerDes lanes. Signed-off-by: Joe Liccese Signed-off-by: Scott Wood --- .../bindings/powerpc/fsl/interlaken-lac.txt | 309 ++++++++++++++++++ .../boot/dts/fsl/interlaken-lac-portals.dtsi | 156 +++++++++ arch/powerpc/boot/dts/fsl/interlaken-lac.dtsi | 45 +++ 3 files changed, 510 insertions(+) create mode 100644 Documentation/devicetree/bindings/powerpc/fsl/interlaken-lac.txt create mode 100644 arch/powerpc/boot/dts/fsl/interlaken-lac-portals.dtsi create mode 100644 arch/powerpc/boot/dts/fsl/interlaken-lac.dtsi diff --git a/Documentation/devicetree/bindings/powerpc/fsl/interlaken-lac.txt b/Documentation/devicetree/bindings/powerpc/fsl/interlaken-lac.txt new file mode 100644 index 000000000000..641bc13983e1 --- /dev/null +++ b/Documentation/devicetree/bindings/powerpc/fsl/interlaken-lac.txt @@ -0,0 +1,309 @@ +=============================================================================== +Freescale Interlaken Look-Aside Controller Device Bindings +Copyright 2012 Freescale Semiconductor Inc. + +CONTENTS + - Interlaken Look-Aside Controller (LAC) Node + - Example LAC Node + - Interlaken Look-Aside Controller (LAC) Software Portal Node + - Interlaken Look-Aside Controller (LAC) Software Portal Child Nodes + - Example LAC SWP Node with Child Nodes + +============================================================================== +Interlaken Look-Aside Controller (LAC) Node + +DESCRIPTION + +The Interlaken is a narrow, high speed channelized chip-to-chip interface. To +facilitate interoperability between a data path device and a look-aside +co-processor, the Interlaken Look-Aside protocol is defined for short +transaction-related transfers. Although based on the Interlaken protocol, +Interlaken Look-Aside is not directly compatible with Interlaken and can be +considered a different operation mode. + +The Interlaken LA controller connects internal platform to Interlaken serial +interface. It accepts LA command through software portals, which are system +memory mapped 4KB spaces. The LA commands are then translated into the +Interlaken control words and data words, which are sent on TX side to TCAM +through SerDes lanes. + +There are two 4KiB spaces defined within the LAC global register memory map. +There is a full register set at 0x0000-0x0FFF (also known as the "hypervisor" +version), and a subset at 0x1000-0x1FFF. The former is a superset of the +latter, and includes certain registers that should not be accessible to +partitioned software. Separate nodes are used for each region, with a phandle +linking the hypervisor node to the normal operating node. + +PROPERTIES + + - compatible + Usage: required + Value type: + Definition: Must include "fsl,interlaken-lac". This represents only + those LAC CCSR registers not protected in partitioned + software. The version of the device is determined by the LAC + IP Block Revision Register (IPBRR0) at offset 0x0BF8. + + Table of correspondences between IPBRR0 values and example + chips: + Value Device + ----------- ------- + 0x02000100 T4240 + + The Hypervisor node has a different compatible. It must include + "fsl,interlaken-lac-hv". This node represents the protected + LAC register space and is required except inside a partition + where access to the hypervisor node is to be denied. + + - fsl,non-hv-node + Usage: required in "fsl,interlaken-lac-hv" + Value type: + Definition: Points to the non-protected LAC CCSR mapped register space + node. + + - reg + Usage: required + Value type: + Definition: A standard property. The first resource represents the + Interlaken LAC configuration registers. + + - interrupts: + Usage: required in non-hv node only + Value type: + Definition: Interrupt mapping for Interlaken LAC error IRQ. + +EXAMPLE + lac: lac@229000 { + compatible = "fsl,interlaken-lac" + reg = <0x229000 0x1000>; + interrupts = <16 2 1 18>; + }; + + lac-hv@228000 { + compatible = "fsl,interlaken-lac-hv" + reg = <0x228000 0x1000>; + fsl,non-hv-node = <&lac>; + }; + +=============================================================================== +Interlaken Look-Aside Controller (LAC) Software Portal Container Node + +DESCRIPTION +The Interlaken Look-Aside Controller (LAC) utilizes Software Portals to accept +Interlaken Look-Aside (ILA) commands. The Interlaken LAC software portal +memory map occupies 128KB of memory space. The software portal memory space is +intended to be cache-enabled. WIMG for each software space is required to be +0010 if stashing is enabled; otherwise, WIMG can be 0000 or 0010. + +PROPERTIES + + - #address-cells + Usage: required + Value type: + Definition: A standard property. Must have a value of 1. + + - #size-cells + Usage: required + Value type: + Definition: A standard property. Must have a value of 1. + + - compatible + Usage: required + Value type: + Definition: Must include "fsl,interlaken-lac-portals" + + - ranges + Usage: required + Value type: + Definition: A standard property. Specifies the address and length + of the LAC portal memory space. + +=============================================================================== +Interlaken Look-Aside Controller (LAC) Software Portals Child Nodes + +DESCRIPTION +There are up to 24 available software portals with each software portal +requiring 4KB of consecutive memory within the software portal memory mapped +space. + +PROPERTIES + + - compatible + Usage: required + Value type: + Definition: Must include "fsl,interlaken-lac-portal-vX.Y" where X is + the Major version (IP_MJ) found in the LAC IP Block Revision + Register (IPBRR0), at offset 0x0BF8, and Y is the Minor version + (IP_MN). + + Table of correspondences between version values and example chips: + Value Device + ------ ------- + 1.0 T4240 + + - reg + Usage: required + Value type: + Definition: A standard property. The first resource represents the + Interlaken LAC software portal registers. + + - fsl,liodn + Value type: + Definition: The logical I/O device number (LIODN) for this device. The + LIODN is a number expressed by this device and used to perform + look-ups in the IOMMU (PAMU) address table when performing + DMAs. This property is automatically added by u-boot. + +=============================================================================== +EXAMPLE + +lac-portals { + #address-cells = <0x1>; + #size-cells = <0x1>; + compatible = "fsl,interlaken-lac-portals"; + ranges = <0x0 0xf 0xf4400000 0x20000>; + + lportal0: lac-portal@0 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x204>; + reg = <0x0 0x1000>; + }; + + lportal1: lac-portal@1000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x205>; + reg = <0x1000 0x1000>; + }; + + lportal2: lac-portal@2000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x206>; + reg = <0x2000 0x1000>; + }; + + lportal3: lac-portal@3000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x207>; + reg = <0x3000 0x1000>; + }; + + lportal4: lac-portal@4000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x208>; + reg = <0x4000 0x1000>; + }; + + lportal5: lac-portal@5000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x209>; + reg = <0x5000 0x1000>; + }; + + lportal6: lac-portal@6000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x20A>; + reg = <0x6000 0x1000>; + }; + + lportal7: lac-portal@7000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x20B>; + reg = <0x7000 0x1000>; + }; + + lportal8: lac-portal@8000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x20C>; + reg = <0x8000 0x1000>; + }; + + lportal9: lac-portal@9000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x20D>; + reg = <0x9000 0x1000>; + }; + + lportal10: lac-portal@A000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x20E>; + reg = <0xA000 0x1000>; + }; + + lportal11: lac-portal@B000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x20F>; + reg = <0xB000 0x1000>; + }; + + lportal12: lac-portal@C000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x210>; + reg = <0xC000 0x1000>; + }; + + lportal13: lac-portal@D000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x211>; + reg = <0xD000 0x1000>; + }; + + lportal14: lac-portal@E000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x212>; + reg = <0xE000 0x1000>; + }; + + lportal15: lac-portal@F000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x213>; + reg = <0xF000 0x1000>; + }; + + lportal16: lac-portal@10000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x214>; + reg = <0x10000 0x1000>; + }; + + lportal17: lac-portal@11000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x215>; + reg = <0x11000 0x1000>; + }; + + lportal8: lac-portal@1200 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x216>; + reg = <0x12000 0x1000>; + }; + + lportal19: lac-portal@13000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x217>; + reg = <0x13000 0x1000>; + }; + + lportal20: lac-portal@14000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x218>; + reg = <0x14000 0x1000>; + }; + + lportal21: lac-portal@15000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x219>; + reg = <0x15000 0x1000>; + }; + + lportal22: lac-portal@16000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x21A>; + reg = <0x16000 0x1000>; + }; + + lportal23: lac-portal@17000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + fsl,liodn = <0x21B>; + reg = <0x17000 0x1000>; + }; +}; diff --git a/arch/powerpc/boot/dts/fsl/interlaken-lac-portals.dtsi b/arch/powerpc/boot/dts/fsl/interlaken-lac-portals.dtsi new file mode 100644 index 000000000000..9cffccf4e07e --- /dev/null +++ b/arch/powerpc/boot/dts/fsl/interlaken-lac-portals.dtsi @@ -0,0 +1,156 @@ +/* T4240 Interlaken LAC Portal device tree stub with 24 portals. + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#address-cells = <0x1>; +#size-cells = <0x1>; +compatible = "fsl,interlaken-lac-portals"; + +lportal0: lac-portal@0 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x0 0x1000>; +}; + +lportal1: lac-portal@1000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x1000 0x1000>; +}; + +lportal2: lac-portal@2000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x2000 0x1000>; +}; + +lportal3: lac-portal@3000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x3000 0x1000>; +}; + +lportal4: lac-portal@4000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x4000 0x1000>; +}; + +lportal5: lac-portal@5000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x5000 0x1000>; +}; + +lportal6: lac-portal@6000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x6000 0x1000>; +}; + +lportal7: lac-portal@7000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x7000 0x1000>; +}; + +lportal8: lac-portal@8000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x8000 0x1000>; +}; + +lportal9: lac-portal@9000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x9000 0x1000>; +}; + +lportal10: lac-portal@A000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0xA000 0x1000>; +}; + +lportal11: lac-portal@B000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0xB000 0x1000>; +}; + +lportal12: lac-portal@C000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0xC000 0x1000>; +}; + +lportal13: lac-portal@D000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0xD000 0x1000>; +}; + +lportal14: lac-portal@E000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0xE000 0x1000>; +}; + +lportal15: lac-portal@F000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0xF000 0x1000>; +}; + +lportal16: lac-portal@10000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x10000 0x1000>; +}; + +lportal17: lac-portal@11000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x11000 0x1000>; +}; + +lportal18: lac-portal@1200 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x12000 0x1000>; +}; + +lportal19: lac-portal@13000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x13000 0x1000>; +}; + +lportal20: lac-portal@14000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x14000 0x1000>; +}; + +lportal21: lac-portal@15000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x15000 0x1000>; +}; + +lportal22: lac-portal@16000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x16000 0x1000>; +}; + +lportal23: lac-portal@17000 { + compatible = "fsl,interlaken-lac-portal-v1.0"; + reg = <0x17000 0x1000>; +}; diff --git a/arch/powerpc/boot/dts/fsl/interlaken-lac.dtsi b/arch/powerpc/boot/dts/fsl/interlaken-lac.dtsi new file mode 100644 index 000000000000..e8208720ac0e --- /dev/null +++ b/arch/powerpc/boot/dts/fsl/interlaken-lac.dtsi @@ -0,0 +1,45 @@ +/* + * T4 Interlaken Look-aside Controller (LAC) device tree stub + * + * Copyright 2012 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +lac: lac@229000 { + compatible = "fsl,interlaken-lac"; + reg = <0x229000 0x1000>; + interrupts = <16 2 1 18>; +}; + +lac-hv@228000 { + compatible = "fsl,interlaken-lac-hv"; + reg = <0x228000 0x1000>; + fsl,non-hv-node = <&lac>; +}; From 98c7355fb373d7c29e5c45d0a423810ad2476b34 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 8 Oct 2012 20:39:52 +0800 Subject: [PATCH 02/12] powerpc/83xx: use module_i2c_driver to simplify the code Use the module_i2c_driver() macro to make the code smaller and a bit simpler. dpatch engine is used to auto generate this patch. (https://github.com/weiyj/dpatch) Signed-off-by: Wei Yongjun Signed-off-by: Scott Wood --- arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c index 624cb51d19c9..7bc315822935 100644 --- a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c +++ b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c @@ -231,17 +231,7 @@ static struct i2c_driver mcu_driver = { .id_table = mcu_ids, }; -static int __init mcu_init(void) -{ - return i2c_add_driver(&mcu_driver); -} -module_init(mcu_init); - -static void __exit mcu_exit(void) -{ - i2c_del_driver(&mcu_driver); -} -module_exit(mcu_exit); +module_i2c_driver(mcu_driver); MODULE_DESCRIPTION("Power Management and GPIO expander driver for " "MPC8349E-mITX-compatible MCU"); From a16d8aa4726a944ffc1616689ae34ff6a902faba Mon Sep 17 00:00:00 2001 From: Sachin Surendran Date: Mon, 26 Nov 2012 11:20:01 +1300 Subject: [PATCH 03/12] i2c-cpm: Fix to takeback i2c bus master-ship after a collision In case of collision on i2c bus the controller which lost bus mastership stays as a slave for all subsequent transfers. This results in the i2c controller never writing to the bus for future transactions, resulting in i2c transfer timeouts. This fix checks for a collision on last I2C transaction and sets the I2COM_MASTER bit for the new transaction. Signed-off-by: Sachin Surendran Signed-off-by: Scott Wood --- drivers/i2c/busses/i2c-cpm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index 3823623baa48..9e6002108720 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -338,6 +338,14 @@ static int cpm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) tptr = 0; rptr = 0; + /* + * If there was a collision in the last i2c transaction, + * Set I2COM_MASTER as it was cleared during collision. + */ + if (in_be16(&tbdf->cbd_sc) & BD_SC_CL) { + out_8(&cpm->i2c_reg->i2com, I2COM_MASTER); + } + while (tptr < num) { pmsg = &msgs[tptr]; dev_dbg(&adap->dev, "R: %d T: %d\n", rptr, tptr); From 3978bdb4ed653342b0be66c031bf61b72cc55d60 Mon Sep 17 00:00:00 2001 From: Tudor Laurentiu Date: Tue, 5 Mar 2013 17:52:49 +0200 Subject: [PATCH 04/12] powerpc/watchdog: Don't enable interrupt on PPC64 BookE Critical interrupts are not handled on PPC64 BookE machines, so when the first watchdog interrupt fires the machine will freeze without a warning until it's rebooted by the second watchdog trigger. Plus, the interrupt isn't used anyway since the driver expects a usermode app to ping the watchdog periodically. Signed-off-by: Laurentiu Tudor Signed-off-by: Scott Wood --- drivers/watchdog/booke_wdt.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c index a8dbceb32914..f1b8d555080e 100644 --- a/drivers/watchdog/booke_wdt.c +++ b/drivers/watchdog/booke_wdt.c @@ -138,6 +138,14 @@ static void __booke_wdt_enable(void *data) val &= ~WDTP_MASK; val |= (TCR_WIE|TCR_WRC(WRC_CHIP)|WDTP(booke_wdt_period)); +#ifdef CONFIG_PPC_BOOK3E_64 + /* + * Crit ints are currently broken on PPC64 Book-E, so + * just disable them for now. + */ + val &= ~TCR_WIE; +#endif + mtspr(SPRN_TCR, val); } From 86d379690c3b005418fafc9afdcdfc731a043862 Mon Sep 17 00:00:00 2001 From: Hongtao Jia Date: Wed, 10 Apr 2013 10:52:55 +0800 Subject: [PATCH 05/12] powerpc/mpic: Add get_version API both for internal and external use MPIC version is useful information for both mpic_alloc() and mpic_init(). The patch provide an API to get MPIC version for reusing the code. Also, some other IP block may need MPIC version for their own use. The API for external use is also provided. Signed-off-by: Jia Hongtao Signed-off-by: Li Yang Signed-off-by: Scott Wood --- arch/powerpc/include/asm/mpic.h | 3 +++ arch/powerpc/sysdev/mpic.c | 32 +++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h index c0f9ef90f0b8..ea6bf7220da9 100644 --- a/arch/powerpc/include/asm/mpic.h +++ b/arch/powerpc/include/asm/mpic.h @@ -393,6 +393,9 @@ struct mpic #define MPIC_REGSET_STANDARD MPIC_REGSET(0) /* Original MPIC */ #define MPIC_REGSET_TSI108 MPIC_REGSET(1) /* Tsi108/109 PIC */ +/* Get the version of primary MPIC */ +extern u32 fsl_mpic_primary_get_version(void); + /* Allocate the controller structure and setup the linux irq descs * for the range if interrupts passed in. No HW initialization is * actually performed. diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 3cc2f9159ab1..1a4e19c6a688 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -1173,10 +1173,33 @@ static struct irq_domain_ops mpic_host_ops = { .xlate = mpic_host_xlate, }; +static u32 fsl_mpic_get_version(struct mpic *mpic) +{ + u32 brr1; + + if (!(mpic->flags & MPIC_FSL)) + return 0; + + brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs, + MPIC_FSL_BRR1); + + return brr1 & MPIC_FSL_BRR1_VER; +} + /* * Exported functions */ +u32 fsl_mpic_primary_get_version(void) +{ + struct mpic *mpic = mpic_primary; + + if (mpic) + return fsl_mpic_get_version(mpic); + + return 0; +} + struct mpic * __init mpic_alloc(struct device_node *node, phys_addr_t phys_addr, unsigned int flags, @@ -1323,7 +1346,6 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000); if (mpic->flags & MPIC_FSL) { - u32 brr1; int ret; /* @@ -1334,9 +1356,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic_map(mpic, mpic->paddr, &mpic->thiscpuregs, MPIC_CPU_THISBASE, 0x1000); - brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs, - MPIC_FSL_BRR1); - fsl_version = brr1 & MPIC_FSL_BRR1_VER; + fsl_version = fsl_mpic_get_version(mpic); /* Error interrupt mask register (EIMR) is required for * handling individual device error interrupts. EIMR @@ -1526,9 +1546,7 @@ void __init mpic_init(struct mpic *mpic) mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf); if (mpic->flags & MPIC_FSL) { - u32 brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs, - MPIC_FSL_BRR1); - u32 version = brr1 & MPIC_FSL_BRR1_VER; + u32 version = fsl_mpic_get_version(mpic); /* * Timer group B is present at the latest in MPIC 3.1 (e.g. From 2dd1c132d5c22677843658f5736f0de1cdf57bc1 Mon Sep 17 00:00:00 2001 From: Chunhe Lan Date: Fri, 19 Apr 2013 16:13:18 +0800 Subject: [PATCH 06/12] powerpc/fsl: Enable CONFIG_E1000E in mpc85xx_smp_defconfig On the most boards of Freescale platform, they use the PCI-Express Intel(R) PRO/1000 gigabit ethernet card to work. So enable the corresponding driver for it. Signed-off-by: Chunhe Lan Signed-off-by: Scott Wood --- arch/powerpc/configs/mpc85xx_smp_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/configs/mpc85xx_smp_defconfig b/arch/powerpc/configs/mpc85xx_smp_defconfig index 165e6b32baef..152fa05b15e4 100644 --- a/arch/powerpc/configs/mpc85xx_smp_defconfig +++ b/arch/powerpc/configs/mpc85xx_smp_defconfig @@ -131,6 +131,7 @@ CONFIG_DUMMY=y CONFIG_FS_ENET=y CONFIG_UCC_GETH=y CONFIG_GIANFAR=y +CONFIG_E1000E=y CONFIG_MARVELL_PHY=y CONFIG_DAVICOM_PHY=y CONFIG_CICADA_PHY=y From 7601f59765a48bef329a429101eabcb0e2503634 Mon Sep 17 00:00:00 2001 From: LEROY Christophe Date: Thu, 18 Apr 2013 07:26:11 +0200 Subject: [PATCH 07/12] powerpc/8xx: Erroneous double irq_eoi() on CPM IRQ in MPC8xx irq_eoi() is already called by generic_handle_irq() so it shall not be called a again Signed-off-by: Christophe Leroy Signed-off-by: Scott Wood --- arch/powerpc/platforms/8xx/m8xx_setup.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/arch/powerpc/platforms/8xx/m8xx_setup.c b/arch/powerpc/platforms/8xx/m8xx_setup.c index 806cbbd86ec6..587a2828b06c 100644 --- a/arch/powerpc/platforms/8xx/m8xx_setup.c +++ b/arch/powerpc/platforms/8xx/m8xx_setup.c @@ -219,19 +219,12 @@ void mpc8xx_restart(char *cmd) static void cpm_cascade(unsigned int irq, struct irq_desc *desc) { - struct irq_chip *chip; - int cascade_irq; - - if ((cascade_irq = cpm_get_irq()) >= 0) { - struct irq_desc *cdesc = irq_to_desc(cascade_irq); + struct irq_chip *chip = irq_desc_get_chip(desc); + int cascade_irq = cpm_get_irq(); + if (cascade_irq >= 0) generic_handle_irq(cascade_irq); - chip = irq_desc_get_chip(cdesc); - chip->irq_eoi(&cdesc->irq_data); - } - - chip = irq_desc_get_chip(desc); chip->irq_eoi(&desc->irq_data); } From 9837b43c5f3514e5d28f65f1513f4dc6759d2810 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Thu, 11 Apr 2013 09:32:34 +0800 Subject: [PATCH 08/12] powerpc/85xx: enable coreint for all the 64bit boards With the patch 7230c564 (powerpc: Rework lazy-interrupt handling), it seems that the coreint works pretty well on the 85xx 64bit kernel. So use the coreint by default for these boards. Signed-off-by: Kevin Hao Signed-off-by: Scott Wood --- arch/powerpc/platforms/85xx/p5020_ds.c | 5 ----- arch/powerpc/platforms/85xx/p5040_ds.c | 5 ----- arch/powerpc/platforms/85xx/t4240_qds.c | 5 ----- 3 files changed, 15 deletions(-) diff --git a/arch/powerpc/platforms/85xx/p5020_ds.c b/arch/powerpc/platforms/85xx/p5020_ds.c index 753a42c29d4d..39cfa4044e6c 100644 --- a/arch/powerpc/platforms/85xx/p5020_ds.c +++ b/arch/powerpc/platforms/85xx/p5020_ds.c @@ -75,12 +75,7 @@ define_machine(p5020_ds) { #ifdef CONFIG_PCI .pcibios_fixup_bus = fsl_pcibios_fixup_bus, #endif -/* coreint doesn't play nice with lazy EE, use legacy mpic for now */ -#ifdef CONFIG_PPC64 - .get_irq = mpic_get_irq, -#else .get_irq = mpic_get_coreint_irq, -#endif .restart = fsl_rstcr_restart, .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, diff --git a/arch/powerpc/platforms/85xx/p5040_ds.c b/arch/powerpc/platforms/85xx/p5040_ds.c index 11381851828e..f70e74cddf97 100644 --- a/arch/powerpc/platforms/85xx/p5040_ds.c +++ b/arch/powerpc/platforms/85xx/p5040_ds.c @@ -66,12 +66,7 @@ define_machine(p5040_ds) { #ifdef CONFIG_PCI .pcibios_fixup_bus = fsl_pcibios_fixup_bus, #endif -/* coreint doesn't play nice with lazy EE, use legacy mpic for now */ -#ifdef CONFIG_PPC64 - .get_irq = mpic_get_irq, -#else .get_irq = mpic_get_coreint_irq, -#endif .restart = fsl_rstcr_restart, .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, diff --git a/arch/powerpc/platforms/85xx/t4240_qds.c b/arch/powerpc/platforms/85xx/t4240_qds.c index 5998e9f33304..91ead6b1b8af 100644 --- a/arch/powerpc/platforms/85xx/t4240_qds.c +++ b/arch/powerpc/platforms/85xx/t4240_qds.c @@ -75,12 +75,7 @@ define_machine(t4240_qds) { #ifdef CONFIG_PCI .pcibios_fixup_bus = fsl_pcibios_fixup_bus, #endif -/* coreint doesn't play nice with lazy EE, use legacy mpic for now */ -#ifdef CONFIG_PPC64 - .get_irq = mpic_get_irq, -#else .get_irq = mpic_get_coreint_irq, -#endif .restart = fsl_rstcr_restart, .calibrate_decr = generic_calibrate_decr, .progress = udbg_progress, From 5ff04b7287d87c1db74f47360365905ed9a97ff7 Mon Sep 17 00:00:00 2001 From: "Dongsheng.wang@freescale.com" Date: Tue, 9 Apr 2013 10:22:29 +0800 Subject: [PATCH 09/12] powerpc/mpic: add irq_set_wake support Add irq_set_wake support. Just add IRQF_NO_SUSPEND to desc->action->flag. So the wake up interrupt will not be disable in suspend_device_irqs. Signed-off-by: Wang Dongsheng Signed-off-by: Scott Wood --- arch/powerpc/sysdev/mpic.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 1a4e19c6a688..4635d11f2dc2 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -920,6 +920,22 @@ int mpic_set_irq_type(struct irq_data *d, unsigned int flow_type) return IRQ_SET_MASK_OK_NOCOPY; } +static int mpic_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct irq_desc *desc = container_of(d, struct irq_desc, irq_data); + struct mpic *mpic = mpic_from_irq_data(d); + + if (!(mpic->flags & MPIC_FSL)) + return -ENXIO; + + if (on) + desc->action->flags |= IRQF_NO_SUSPEND; + else + desc->action->flags &= ~IRQF_NO_SUSPEND; + + return 0; +} + void mpic_set_vector(unsigned int virq, unsigned int vector) { struct mpic *mpic = mpic_from_irq(virq); @@ -957,6 +973,7 @@ static struct irq_chip mpic_irq_chip = { .irq_unmask = mpic_unmask_irq, .irq_eoi = mpic_end_irq, .irq_set_type = mpic_set_irq_type, + .irq_set_wake = mpic_irq_set_wake, }; #ifdef CONFIG_SMP @@ -971,6 +988,7 @@ static struct irq_chip mpic_tm_chip = { .irq_mask = mpic_mask_tm, .irq_unmask = mpic_unmask_tm, .irq_eoi = mpic_end_irq, + .irq_set_wake = mpic_irq_set_wake, }; #ifdef CONFIG_MPIC_U3_HT_IRQS From 36ca09be6ff77a4e5b54b8b68ed7be7aa184250b Mon Sep 17 00:00:00 2001 From: "Dongsheng.wang@freescale.com" Date: Tue, 9 Apr 2013 10:22:30 +0800 Subject: [PATCH 10/12] powerpc/mpic: add global timer support The MPIC global timer is a hardware timer inside the Freescale PIC complying with OpenPIC standard. When the specified interval times out, the hardware timer generates an interrupt. The driver currently is only tested on fsl chip, but it can potentially support other global timers complying to OpenPIC standard. The two independent groups of global timer on fsl chip, group A and group B, are identical in their functionality, except that they appear at different locations within the PIC register map. The hardware timer can be cascaded to create timers larger than the default 31-bit global timers. Timer cascade fields allow configuration of up to two 63-bit timers. But These two groups of timers cannot be cascaded together. It can be used as a wakeup source for low power modes. It also could be used as periodical timer for protocols, drivers and etc. Signed-off-by: Wang Dongsheng Signed-off-by: Li Yang Signed-off-by: Scott Wood --- arch/powerpc/include/asm/mpic_timer.h | 46 ++ arch/powerpc/platforms/Kconfig | 12 + arch/powerpc/sysdev/Makefile | 1 + arch/powerpc/sysdev/mpic_timer.c | 593 ++++++++++++++++++++++++++ 4 files changed, 652 insertions(+) create mode 100644 arch/powerpc/include/asm/mpic_timer.h create mode 100644 arch/powerpc/sysdev/mpic_timer.c diff --git a/arch/powerpc/include/asm/mpic_timer.h b/arch/powerpc/include/asm/mpic_timer.h new file mode 100644 index 000000000000..0e23cd4ac8aa --- /dev/null +++ b/arch/powerpc/include/asm/mpic_timer.h @@ -0,0 +1,46 @@ +/* + * arch/powerpc/include/asm/mpic_timer.h + * + * Header file for Mpic Global Timer + * + * Copyright 2013 Freescale Semiconductor, Inc. + * + * Author: Wang Dongsheng + * Li Yang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __MPIC_TIMER__ +#define __MPIC_TIMER__ + +#include +#include + +struct mpic_timer { + void *dev; + struct cascade_priv *cascade_handle; + unsigned int num; + unsigned int irq; +}; + +#ifdef CONFIG_MPIC_TIMER +struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev, + const struct timeval *time); +void mpic_start_timer(struct mpic_timer *handle); +void mpic_stop_timer(struct mpic_timer *handle); +void mpic_get_remain_time(struct mpic_timer *handle, struct timeval *time); +void mpic_free_timer(struct mpic_timer *handle); +#else +struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev, + const struct timeval *time) { return NULL; } +void mpic_start_timer(struct mpic_timer *handle) { } +void mpic_stop_timer(struct mpic_timer *handle) { } +void mpic_get_remain_time(struct mpic_timer *handle, struct timeval *time) { } +void mpic_free_timer(struct mpic_timer *handle) { } +#endif + +#endif diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index bed8c607588c..7d07d9e8e1dd 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -86,6 +86,18 @@ config MPIC bool default n +config MPIC_TIMER + bool "MPIC Global Timer" + depends on MPIC && FSL_SOC + default n + help + The MPIC global timer is a hardware timer inside the + Freescale PIC complying with OpenPIC standard. When the + specified interval times out, the hardware timer generates + an interrupt. The driver currently is only tested on fsl + chip, but it can potentially support other global timers + complying with the OpenPIC standard. + config PPC_EPAPR_HV_PIC bool default n diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 99464a7bdb3b..2eb72c1a4291 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -4,6 +4,7 @@ ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC) mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) +obj-$(CONFIG_MPIC_TIMER) += mpic_timer.o mpic-msgr-obj-$(CONFIG_MPIC_MSGR) += mpic_msgr.o obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) $(mpic-msgr-obj-y) obj-$(CONFIG_PPC_EPAPR_HV_PIC) += ehv_pic.o diff --git a/arch/powerpc/sysdev/mpic_timer.c b/arch/powerpc/sysdev/mpic_timer.c new file mode 100644 index 000000000000..c06db92a4fb1 --- /dev/null +++ b/arch/powerpc/sysdev/mpic_timer.c @@ -0,0 +1,593 @@ +/* + * MPIC timer driver + * + * Copyright 2013 Freescale Semiconductor, Inc. + * Author: Dongsheng Wang + * Li Yang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define FSL_GLOBAL_TIMER 0x1 + +/* Clock Ratio + * Divide by 64 0x00000300 + * Divide by 32 0x00000200 + * Divide by 16 0x00000100 + * Divide by 8 0x00000000 (Hardware default div) + */ +#define MPIC_TIMER_TCR_CLKDIV 0x00000300 + +#define MPIC_TIMER_TCR_ROVR_OFFSET 24 + +#define TIMER_STOP 0x80000000 +#define TIMERS_PER_GROUP 4 +#define MAX_TICKS (~0U >> 1) +#define MAX_TICKS_CASCADE (~0U) +#define TIMER_OFFSET(num) (1 << (TIMERS_PER_GROUP - 1 - num)) + +/* tv_usec should be less than ONE_SECOND, otherwise use tv_sec */ +#define ONE_SECOND 1000000 + +struct timer_regs { + u32 gtccr; + u32 res0[3]; + u32 gtbcr; + u32 res1[3]; + u32 gtvpr; + u32 res2[3]; + u32 gtdr; + u32 res3[3]; +}; + +struct cascade_priv { + u32 tcr_value; /* TCR register: CASC & ROVR value */ + unsigned int cascade_map; /* cascade map */ + unsigned int timer_num; /* cascade control timer */ +}; + +struct timer_group_priv { + struct timer_regs __iomem *regs; + struct mpic_timer timer[TIMERS_PER_GROUP]; + struct list_head node; + unsigned int timerfreq; + unsigned int idle; + unsigned int flags; + spinlock_t lock; + void __iomem *group_tcr; +}; + +static struct cascade_priv cascade_timer[] = { + /* cascade timer 0 and 1 */ + {0x1, 0xc, 0x1}, + /* cascade timer 1 and 2 */ + {0x2, 0x6, 0x2}, + /* cascade timer 2 and 3 */ + {0x4, 0x3, 0x3} +}; + +static LIST_HEAD(timer_group_list); + +static void convert_ticks_to_time(struct timer_group_priv *priv, + const u64 ticks, struct timeval *time) +{ + u64 tmp_sec; + + time->tv_sec = (__kernel_time_t)div_u64(ticks, priv->timerfreq); + tmp_sec = (u64)time->tv_sec * (u64)priv->timerfreq; + + time->tv_usec = (__kernel_suseconds_t) + div_u64((ticks - tmp_sec) * 1000000, priv->timerfreq); + + return; +} + +/* the time set by the user is converted to "ticks" */ +static int convert_time_to_ticks(struct timer_group_priv *priv, + const struct timeval *time, u64 *ticks) +{ + u64 max_value; /* prevent u64 overflow */ + u64 tmp = 0; + + u64 tmp_sec; + u64 tmp_ms; + u64 tmp_us; + + max_value = div_u64(ULLONG_MAX, priv->timerfreq); + + if (time->tv_sec > max_value || + (time->tv_sec == max_value && time->tv_usec > 0)) + return -EINVAL; + + tmp_sec = (u64)time->tv_sec * (u64)priv->timerfreq; + tmp += tmp_sec; + + tmp_ms = time->tv_usec / 1000; + tmp_ms = div_u64((u64)tmp_ms * (u64)priv->timerfreq, 1000); + tmp += tmp_ms; + + tmp_us = time->tv_usec % 1000; + tmp_us = div_u64((u64)tmp_us * (u64)priv->timerfreq, 1000000); + tmp += tmp_us; + + *ticks = tmp; + + return 0; +} + +/* detect whether there is a cascade timer available */ +static struct mpic_timer *detect_idle_cascade_timer( + struct timer_group_priv *priv) +{ + struct cascade_priv *casc_priv; + unsigned int map; + unsigned int array_size = ARRAY_SIZE(cascade_timer); + unsigned int num; + unsigned int i; + unsigned long flags; + + casc_priv = cascade_timer; + for (i = 0; i < array_size; i++) { + spin_lock_irqsave(&priv->lock, flags); + map = casc_priv->cascade_map & priv->idle; + if (map == casc_priv->cascade_map) { + num = casc_priv->timer_num; + priv->timer[num].cascade_handle = casc_priv; + + /* set timer busy */ + priv->idle &= ~casc_priv->cascade_map; + spin_unlock_irqrestore(&priv->lock, flags); + return &priv->timer[num]; + } + spin_unlock_irqrestore(&priv->lock, flags); + casc_priv++; + } + + return NULL; +} + +static int set_cascade_timer(struct timer_group_priv *priv, u64 ticks, + unsigned int num) +{ + struct cascade_priv *casc_priv; + u32 tcr; + u32 tmp_ticks; + u32 rem_ticks; + + /* set group tcr reg for cascade */ + casc_priv = priv->timer[num].cascade_handle; + if (!casc_priv) + return -EINVAL; + + tcr = casc_priv->tcr_value | + (casc_priv->tcr_value << MPIC_TIMER_TCR_ROVR_OFFSET); + setbits32(priv->group_tcr, tcr); + + tmp_ticks = div_u64_rem(ticks, MAX_TICKS_CASCADE, &rem_ticks); + + out_be32(&priv->regs[num].gtccr, 0); + out_be32(&priv->regs[num].gtbcr, tmp_ticks | TIMER_STOP); + + out_be32(&priv->regs[num - 1].gtccr, 0); + out_be32(&priv->regs[num - 1].gtbcr, rem_ticks); + + return 0; +} + +static struct mpic_timer *get_cascade_timer(struct timer_group_priv *priv, + u64 ticks) +{ + struct mpic_timer *allocated_timer; + + /* Two cascade timers: Support the maximum time */ + const u64 max_ticks = (u64)MAX_TICKS * (u64)MAX_TICKS_CASCADE; + int ret; + + if (ticks > max_ticks) + return NULL; + + /* detect idle timer */ + allocated_timer = detect_idle_cascade_timer(priv); + if (!allocated_timer) + return NULL; + + /* set ticks to timer */ + ret = set_cascade_timer(priv, ticks, allocated_timer->num); + if (ret < 0) + return NULL; + + return allocated_timer; +} + +static struct mpic_timer *get_timer(const struct timeval *time) +{ + struct timer_group_priv *priv; + struct mpic_timer *timer; + + u64 ticks; + unsigned int num; + unsigned int i; + unsigned long flags; + int ret; + + list_for_each_entry(priv, &timer_group_list, node) { + ret = convert_time_to_ticks(priv, time, &ticks); + if (ret < 0) + return NULL; + + if (ticks > MAX_TICKS) { + if (!(priv->flags & FSL_GLOBAL_TIMER)) + return NULL; + + timer = get_cascade_timer(priv, ticks); + if (!timer) + continue; + + return timer; + } + + for (i = 0; i < TIMERS_PER_GROUP; i++) { + /* one timer: Reverse allocation */ + num = TIMERS_PER_GROUP - 1 - i; + spin_lock_irqsave(&priv->lock, flags); + if (priv->idle & (1 << i)) { + /* set timer busy */ + priv->idle &= ~(1 << i); + /* set ticks & stop timer */ + out_be32(&priv->regs[num].gtbcr, + ticks | TIMER_STOP); + out_be32(&priv->regs[num].gtccr, 0); + priv->timer[num].cascade_handle = NULL; + spin_unlock_irqrestore(&priv->lock, flags); + return &priv->timer[num]; + } + spin_unlock_irqrestore(&priv->lock, flags); + } + } + + return NULL; +} + +/** + * mpic_start_timer - start hardware timer + * @handle: the timer to be started. + * + * It will do ->fn(->dev) callback from the hardware interrupt at + * the ->timeval point in the future. + */ +void mpic_start_timer(struct mpic_timer *handle) +{ + struct timer_group_priv *priv = container_of(handle, + struct timer_group_priv, timer[handle->num]); + + clrbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP); +} +EXPORT_SYMBOL(mpic_start_timer); + +/** + * mpic_stop_timer - stop hardware timer + * @handle: the timer to be stoped + * + * The timer periodically generates an interrupt. Unless user stops the timer. + */ +void mpic_stop_timer(struct mpic_timer *handle) +{ + struct timer_group_priv *priv = container_of(handle, + struct timer_group_priv, timer[handle->num]); + struct cascade_priv *casc_priv; + + setbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP); + + casc_priv = priv->timer[handle->num].cascade_handle; + if (casc_priv) { + out_be32(&priv->regs[handle->num].gtccr, 0); + out_be32(&priv->regs[handle->num - 1].gtccr, 0); + } else { + out_be32(&priv->regs[handle->num].gtccr, 0); + } +} +EXPORT_SYMBOL(mpic_stop_timer); + +/** + * mpic_get_remain_time - get timer time + * @handle: the timer to be selected. + * @time: time for timer + * + * Query timer remaining time. + */ +void mpic_get_remain_time(struct mpic_timer *handle, struct timeval *time) +{ + struct timer_group_priv *priv = container_of(handle, + struct timer_group_priv, timer[handle->num]); + struct cascade_priv *casc_priv; + + u64 ticks; + u32 tmp_ticks; + + casc_priv = priv->timer[handle->num].cascade_handle; + if (casc_priv) { + tmp_ticks = in_be32(&priv->regs[handle->num].gtccr); + ticks = ((u64)tmp_ticks & UINT_MAX) * (u64)MAX_TICKS_CASCADE; + tmp_ticks = in_be32(&priv->regs[handle->num - 1].gtccr); + ticks += tmp_ticks; + } else { + ticks = in_be32(&priv->regs[handle->num].gtccr); + } + + convert_ticks_to_time(priv, ticks, time); +} +EXPORT_SYMBOL(mpic_get_remain_time); + +/** + * mpic_free_timer - free hardware timer + * @handle: the timer to be removed. + * + * Free the timer. + * + * Note: can not be used in interrupt context. + */ +void mpic_free_timer(struct mpic_timer *handle) +{ + struct timer_group_priv *priv = container_of(handle, + struct timer_group_priv, timer[handle->num]); + + struct cascade_priv *casc_priv; + unsigned long flags; + + mpic_stop_timer(handle); + + casc_priv = priv->timer[handle->num].cascade_handle; + + free_irq(priv->timer[handle->num].irq, priv->timer[handle->num].dev); + + spin_lock_irqsave(&priv->lock, flags); + if (casc_priv) { + u32 tcr; + tcr = casc_priv->tcr_value | (casc_priv->tcr_value << + MPIC_TIMER_TCR_ROVR_OFFSET); + clrbits32(priv->group_tcr, tcr); + priv->idle |= casc_priv->cascade_map; + priv->timer[handle->num].cascade_handle = NULL; + } else { + priv->idle |= TIMER_OFFSET(handle->num); + } + spin_unlock_irqrestore(&priv->lock, flags); +} +EXPORT_SYMBOL(mpic_free_timer); + +/** + * mpic_request_timer - get a hardware timer + * @fn: interrupt handler function + * @dev: callback function of the data + * @time: time for timer + * + * This executes the "request_irq", returning NULL + * else "handle" on success. + */ +struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev, + const struct timeval *time) +{ + struct mpic_timer *allocated_timer; + int ret; + + if (list_empty(&timer_group_list)) + return NULL; + + if (!(time->tv_sec + time->tv_usec) || + time->tv_sec < 0 || time->tv_usec < 0) + return NULL; + + if (time->tv_usec > ONE_SECOND) + return NULL; + + allocated_timer = get_timer(time); + if (!allocated_timer) + return NULL; + + ret = request_irq(allocated_timer->irq, fn, + IRQF_TRIGGER_LOW, "global-timer", dev); + if (ret) { + mpic_free_timer(allocated_timer); + return NULL; + } + + allocated_timer->dev = dev; + + return allocated_timer; +} +EXPORT_SYMBOL(mpic_request_timer); + +static int timer_group_get_freq(struct device_node *np, + struct timer_group_priv *priv) +{ + u32 div; + + if (priv->flags & FSL_GLOBAL_TIMER) { + struct device_node *dn; + + dn = of_find_compatible_node(NULL, NULL, "fsl,mpic"); + if (dn) { + of_property_read_u32(dn, "clock-frequency", + &priv->timerfreq); + of_node_put(dn); + } + } + + if (priv->timerfreq <= 0) + return -EINVAL; + + if (priv->flags & FSL_GLOBAL_TIMER) { + div = (1 << (MPIC_TIMER_TCR_CLKDIV >> 8)) * 8; + priv->timerfreq /= div; + } + + return 0; +} + +static int timer_group_get_irq(struct device_node *np, + struct timer_group_priv *priv) +{ + const u32 all_timer[] = { 0, TIMERS_PER_GROUP }; + const u32 *p; + u32 offset; + u32 count; + + unsigned int i; + unsigned int j; + unsigned int irq_index = 0; + unsigned int irq; + int len; + + p = of_get_property(np, "fsl,available-ranges", &len); + if (p && len % (2 * sizeof(u32)) != 0) { + pr_err("%s: malformed available-ranges property.\n", + np->full_name); + return -EINVAL; + } + + if (!p) { + p = all_timer; + len = sizeof(all_timer); + } + + len /= 2 * sizeof(u32); + + for (i = 0; i < len; i++) { + offset = p[i * 2]; + count = p[i * 2 + 1]; + for (j = 0; j < count; j++) { + irq = irq_of_parse_and_map(np, irq_index); + if (!irq) { + pr_err("%s: irq parse and map failed.\n", + np->full_name); + return -EINVAL; + } + + /* Set timer idle */ + priv->idle |= TIMER_OFFSET((offset + j)); + priv->timer[offset + j].irq = irq; + priv->timer[offset + j].num = offset + j; + irq_index++; + } + } + + return 0; +} + +static void timer_group_init(struct device_node *np) +{ + struct timer_group_priv *priv; + unsigned int i = 0; + int ret; + + priv = kzalloc(sizeof(struct timer_group_priv), GFP_KERNEL); + if (!priv) { + pr_err("%s: cannot allocate memory for group.\n", + np->full_name); + return; + } + + if (of_device_is_compatible(np, "fsl,mpic-global-timer")) + priv->flags |= FSL_GLOBAL_TIMER; + + priv->regs = of_iomap(np, i++); + if (!priv->regs) { + pr_err("%s: cannot ioremap timer register address.\n", + np->full_name); + goto out; + } + + if (priv->flags & FSL_GLOBAL_TIMER) { + priv->group_tcr = of_iomap(np, i++); + if (!priv->group_tcr) { + pr_err("%s: cannot ioremap tcr address.\n", + np->full_name); + goto out; + } + } + + ret = timer_group_get_freq(np, priv); + if (ret < 0) { + pr_err("%s: cannot get timer frequency.\n", np->full_name); + goto out; + } + + ret = timer_group_get_irq(np, priv); + if (ret < 0) { + pr_err("%s: cannot get timer irqs.\n", np->full_name); + goto out; + } + + spin_lock_init(&priv->lock); + + /* Init FSL timer hardware */ + if (priv->flags & FSL_GLOBAL_TIMER) + setbits32(priv->group_tcr, MPIC_TIMER_TCR_CLKDIV); + + list_add_tail(&priv->node, &timer_group_list); + + return; + +out: + if (priv->regs) + iounmap(priv->regs); + + if (priv->group_tcr) + iounmap(priv->group_tcr); + + kfree(priv); +} + +static void mpic_timer_resume(void) +{ + struct timer_group_priv *priv; + + list_for_each_entry(priv, &timer_group_list, node) { + /* Init FSL timer hardware */ + if (priv->flags & FSL_GLOBAL_TIMER) + setbits32(priv->group_tcr, MPIC_TIMER_TCR_CLKDIV); + } +} + +static const struct of_device_id mpic_timer_ids[] = { + { .compatible = "fsl,mpic-global-timer", }, + {}, +}; + +static struct syscore_ops mpic_timer_syscore_ops = { + .resume = mpic_timer_resume, +}; + +static int __init mpic_timer_init(void) +{ + struct device_node *np = NULL; + + for_each_matching_node(np, mpic_timer_ids) + timer_group_init(np); + + register_syscore_ops(&mpic_timer_syscore_ops); + + if (list_empty(&timer_group_list)) + return -ENODEV; + + return 0; +} +subsys_initcall(mpic_timer_init); From 9e6f31a9dbc58f6a5661f8dc8c919441b8d3ced4 Mon Sep 17 00:00:00 2001 From: "Dongsheng.wang@freescale.com" Date: Tue, 9 Apr 2013 10:22:31 +0800 Subject: [PATCH 11/12] powerpc/mpic: create mpic subsystem object Register a mpic subsystem at /sys/devices/system/ Signed-off-by: Wang Dongsheng Signed-off-by: Scott Wood --- arch/powerpc/include/asm/mpic.h | 2 ++ arch/powerpc/sysdev/mpic.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h index ea6bf7220da9..4a1ac9fbf186 100644 --- a/arch/powerpc/include/asm/mpic.h +++ b/arch/powerpc/include/asm/mpic.h @@ -339,6 +339,8 @@ struct mpic #endif }; +extern struct bus_type mpic_subsys; + /* * MPIC flags (passed to mpic_alloc) * diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 4635d11f2dc2..1be54faf60dd 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -48,6 +48,12 @@ #define DBG(fmt...) #endif +struct bus_type mpic_subsys = { + .name = "mpic", + .dev_name = "mpic", +}; +EXPORT_SYMBOL_GPL(mpic_subsys); + static struct mpic *mpics; static struct mpic *mpic_primary; static DEFINE_RAW_SPINLOCK(mpic_lock); @@ -2035,6 +2041,8 @@ static struct syscore_ops mpic_syscore_ops = { static int mpic_init_sys(void) { register_syscore_ops(&mpic_syscore_ops); + subsys_system_register(&mpic_subsys, NULL); + return 0; } From a63b3bc7db32b63bfe5f48fa8582f931db81c86e Mon Sep 17 00:00:00 2001 From: "Dongsheng.wang@freescale.com" Date: Tue, 9 Apr 2013 10:22:32 +0800 Subject: [PATCH 12/12] powerpc/fsl: add MPIC timer wakeup support The driver provides a way to wake up the system by the MPIC timer. For example, echo 5 > /sys/devices/system/mpic/timer_wakeup echo standby > /sys/power/state After 5 seconds the MPIC timer will generate an interrupt to wake up the system. Signed-off-by: Wang Dongsheng Signed-off-by: Zhao Chenhui Signed-off-by: Li Yang --- arch/powerpc/platforms/Kconfig | 9 ++ arch/powerpc/sysdev/Makefile | 1 + arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c | 161 ++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 7d07d9e8e1dd..0847ee6cd616 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -98,6 +98,15 @@ config MPIC_TIMER chip, but it can potentially support other global timers complying with the OpenPIC standard. +config FSL_MPIC_TIMER_WAKEUP + tristate "Freescale MPIC global timer wakeup driver" + depends on FSL_SOC && MPIC_TIMER && PM + default n + help + The driver provides a way to wake up the system by MPIC + timer. + e.g. "echo 5 > /sys/devices/system/mpic/timer_wakeup" + config PPC_EPAPR_HV_PIC bool default n diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 2eb72c1a4291..f67ac900d870 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -5,6 +5,7 @@ ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC) mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) obj-$(CONFIG_MPIC_TIMER) += mpic_timer.o +obj-$(CONFIG_FSL_MPIC_TIMER_WAKEUP) += fsl_mpic_timer_wakeup.o mpic-msgr-obj-$(CONFIG_MPIC_MSGR) += mpic_msgr.o obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) $(mpic-msgr-obj-y) obj-$(CONFIG_PPC_EPAPR_HV_PIC) += ehv_pic.o diff --git a/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c b/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c new file mode 100644 index 000000000000..1707bf04dec6 --- /dev/null +++ b/arch/powerpc/sysdev/fsl_mpic_timer_wakeup.c @@ -0,0 +1,161 @@ +/* + * MPIC timer wakeup driver + * + * Copyright 2013 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 as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct fsl_mpic_timer_wakeup { + struct mpic_timer *timer; + struct work_struct free_work; +}; + +static struct fsl_mpic_timer_wakeup *fsl_wakeup; +static DEFINE_MUTEX(sysfs_lock); + +static void fsl_free_resource(struct work_struct *ws) +{ + struct fsl_mpic_timer_wakeup *wakeup = + container_of(ws, struct fsl_mpic_timer_wakeup, free_work); + + mutex_lock(&sysfs_lock); + + if (wakeup->timer) { + disable_irq_wake(wakeup->timer->irq); + mpic_free_timer(wakeup->timer); + } + + wakeup->timer = NULL; + mutex_unlock(&sysfs_lock); +} + +static irqreturn_t fsl_mpic_timer_irq(int irq, void *dev_id) +{ + struct fsl_mpic_timer_wakeup *wakeup = dev_id; + + schedule_work(&wakeup->free_work); + + return wakeup->timer ? IRQ_HANDLED : IRQ_NONE; +} + +static ssize_t fsl_timer_wakeup_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct timeval interval; + int val = 0; + + mutex_lock(&sysfs_lock); + if (fsl_wakeup->timer) { + mpic_get_remain_time(fsl_wakeup->timer, &interval); + val = interval.tv_sec + 1; + } + mutex_unlock(&sysfs_lock); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t fsl_timer_wakeup_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct timeval interval; + int ret; + + interval.tv_usec = 0; + if (kstrtol(buf, 0, &interval.tv_sec)) + return -EINVAL; + + mutex_lock(&sysfs_lock); + + if (fsl_wakeup->timer) { + disable_irq_wake(fsl_wakeup->timer->irq); + mpic_free_timer(fsl_wakeup->timer); + fsl_wakeup->timer = NULL; + } + + if (!interval.tv_sec) { + mutex_unlock(&sysfs_lock); + return count; + } + + fsl_wakeup->timer = mpic_request_timer(fsl_mpic_timer_irq, + fsl_wakeup, &interval); + if (!fsl_wakeup->timer) { + mutex_unlock(&sysfs_lock); + return -EINVAL; + } + + ret = enable_irq_wake(fsl_wakeup->timer->irq); + if (ret) { + mpic_free_timer(fsl_wakeup->timer); + fsl_wakeup->timer = NULL; + mutex_unlock(&sysfs_lock); + + return ret; + } + + mpic_start_timer(fsl_wakeup->timer); + + mutex_unlock(&sysfs_lock); + + return count; +} + +static struct device_attribute mpic_attributes = __ATTR(timer_wakeup, 0644, + fsl_timer_wakeup_show, fsl_timer_wakeup_store); + +static int __init fsl_wakeup_sys_init(void) +{ + int ret; + + fsl_wakeup = kzalloc(sizeof(struct fsl_mpic_timer_wakeup), GFP_KERNEL); + if (!fsl_wakeup) + return -ENOMEM; + + INIT_WORK(&fsl_wakeup->free_work, fsl_free_resource); + + ret = device_create_file(mpic_subsys.dev_root, &mpic_attributes); + if (ret) + kfree(fsl_wakeup); + + return ret; +} + +static void __exit fsl_wakeup_sys_exit(void) +{ + device_remove_file(mpic_subsys.dev_root, &mpic_attributes); + + mutex_lock(&sysfs_lock); + + if (fsl_wakeup->timer) { + disable_irq_wake(fsl_wakeup->timer->irq); + mpic_free_timer(fsl_wakeup->timer); + } + + kfree(fsl_wakeup); + + mutex_unlock(&sysfs_lock); +} + +module_init(fsl_wakeup_sys_init); +module_exit(fsl_wakeup_sys_exit); + +MODULE_DESCRIPTION("Freescale MPIC global timer wakeup driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Wang Dongsheng ");