diff options
Diffstat (limited to 'arch/powerpc/platforms')
409 files changed, 108518 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/40x/Kconfig b/arch/powerpc/platforms/40x/Kconfig new file mode 100644 index 00000000000..a392d12dd21 --- /dev/null +++ b/arch/powerpc/platforms/40x/Kconfig @@ -0,0 +1,169 @@ +config ACADIA + bool "Acadia" + depends on 40x + default n + select PPC40x_SIMPLE + select 405EZ + help + This option enables support for the AMCC 405EZ Acadia evaluation board. + +config EP405 + bool "EP405/EP405PC" + depends on 40x + default n + select 405GP + select PCI + help + This option enables support for the EP405/EP405PC boards. + +config HOTFOOT + bool "Hotfoot" + depends on 40x + default n + select 405EP + select PPC40x_SIMPLE + select PCI + help + This option enables support for the ESTEEM 195E Hotfoot board. + +config KILAUEA + bool "Kilauea" + depends on 40x + default n + select 405EX + select PPC40x_SIMPLE + select PPC4xx_PCI_EXPRESS + select PCI_MSI + select PPC4xx_MSI + help + This option enables support for the AMCC PPC405EX evaluation board. + +config MAKALU + bool "Makalu" + depends on 40x + default n + select 405EX + select PCI + select PPC4xx_PCI_EXPRESS + select PPC40x_SIMPLE + help + This option enables support for the AMCC PPC405EX board. + +config WALNUT + bool "Walnut" + depends on 40x + default y + select 405GP + select PCI + select OF_RTC + help + This option enables support for the IBM PPC405GP evaluation board. + +config XILINX_VIRTEX_GENERIC_BOARD + bool "Generic Xilinx Virtex board" + depends on 40x + default n + select XILINX_VIRTEX_II_PRO + select XILINX_VIRTEX_4_FX + help + This option enables generic support for Xilinx Virtex based boards. + + The generic virtex board support matches any device tree which + specifies 'xilinx,virtex' in its compatible field. This includes + the Xilinx ML3xx and ML4xx reference designs using the powerpc + core. + + Most Virtex designs should use this unless it needs to do some + special configuration at board probe time. + +config OBS600 + bool "OpenBlockS 600" + depends on 40x + default n + select 405EX + select PPC40x_SIMPLE + help + This option enables support for PlatHome OpenBlockS 600 server + + +config PPC40x_SIMPLE + bool "Simple PowerPC 40x board support" + depends on 40x + default n + help + This option enables the simple PowerPC 40x platform support. + +# OAK doesn't exist but wanted to keep this around for any future 403GCX boards +config 403GCX + bool + #depends on OAK + select IBM405_ERR51 + +config 405GP + bool + select IBM405_ERR77 + select IBM405_ERR51 + select IBM_EMAC_ZMII + +config 405EP + bool + +config 405EX + bool + select IBM_EMAC_EMAC4 + select IBM_EMAC_RGMII + +config 405EZ + bool + select IBM_EMAC_NO_FLOW_CTRL + select IBM_EMAC_MAL_CLR_ICINTSTAT + select IBM_EMAC_MAL_COMMON_ERR + +config 405GPR + bool + +config XILINX_VIRTEX + bool + select DEFAULT_UIMAGE + +config XILINX_VIRTEX_II_PRO + bool + select XILINX_VIRTEX + select IBM405_ERR77 + select IBM405_ERR51 + +config XILINX_VIRTEX_4_FX + bool + select XILINX_VIRTEX + +config STB03xxx + bool + select IBM405_ERR77 + select IBM405_ERR51 + +config PPC4xx_GPIO + bool "PPC4xx GPIO support" + depends on 40x + select ARCH_REQUIRE_GPIOLIB + select GENERIC_GPIO + help + Enable gpiolib support for ppc40x based boards + +# 40x errata/workaround config symbols, selected by the CPU models above + +# All 405-based cores up until the 405GPR and 405EP have this errata. +config IBM405_ERR77 + bool + +# All 40x-based cores, up until the 405GPR and 405EP have this errata. +config IBM405_ERR51 + bool + +config APM8018X + bool "APM8018X" + depends on 40x + default n + select PPC40x_SIMPLE + help + This option enables support for the AppliedMicro APM8018X evaluation + board. diff --git a/arch/powerpc/platforms/40x/Makefile b/arch/powerpc/platforms/40x/Makefile new file mode 100644 index 00000000000..88c22de0c85 --- /dev/null +++ b/arch/powerpc/platforms/40x/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_WALNUT) += walnut.o +obj-$(CONFIG_XILINX_VIRTEX_GENERIC_BOARD) += virtex.o +obj-$(CONFIG_EP405) += ep405.o +obj-$(CONFIG_PPC40x_SIMPLE) += ppc40x_simple.o diff --git a/arch/powerpc/platforms/40x/ep405.c b/arch/powerpc/platforms/40x/ep405.c new file mode 100644 index 00000000000..b0389bbe4f9 --- /dev/null +++ b/arch/powerpc/platforms/40x/ep405.c @@ -0,0 +1,125 @@ +/* + * Architecture- / platform-specific boot-time initialization code for + * IBM PowerPC 4xx based boards. Adapted from original + * code by Gary Thomas, Cort Dougan <cort@fsmlabs.com>, and Dan Malek + * <dan@net4x.com>. + * + * Copyright(c) 1999-2000 Grant Erickson <grant@lcse.umn.edu> + * + * Rewritten and ported to the merged powerpc tree: + * Copyright 2007 IBM Corporation + * Josh Boyer <jwboyer@linux.vnet.ibm.com> + * + * Adapted to EP405 by Ben. Herrenschmidt <benh@kernel.crashing.org> + * + * TODO: Wire up the PCI IRQ mux and the southbridge interrupts + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include <linux/init.h> +#include <linux/of_platform.h> + +#include <asm/machdep.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/time.h> +#include <asm/uic.h> +#include <asm/pci-bridge.h> +#include <asm/ppc4xx.h> + +static struct device_node *bcsr_node; +static void __iomem *bcsr_regs; + +/* BCSR registers */ +#define BCSR_ID 0 +#define BCSR_PCI_CTRL 1 +#define BCSR_FLASH_NV_POR_CTRL 2 +#define BCSR_FENET_UART_CTRL 3 +#define BCSR_PCI_IRQ 4 +#define BCSR_XIRQ_SELECT 5 +#define BCSR_XIRQ_ROUTING 6 +#define BCSR_XIRQ_STATUS 7 +#define BCSR_XIRQ_STATUS2 8 +#define BCSR_SW_STAT_LED_CTRL 9 +#define BCSR_GPIO_IRQ_PAR_CTRL 10 +/* there's more, can't be bothered typing them tho */ + + +static __initdata struct of_device_id ep405_of_bus[] = { + { .compatible = "ibm,plb3", }, + { .compatible = "ibm,opb", }, + { .compatible = "ibm,ebc", }, + {}, +}; + +static int __init ep405_device_probe(void) +{ + of_platform_bus_probe(NULL, ep405_of_bus, NULL); + + return 0; +} +machine_device_initcall(ep405, ep405_device_probe); + +static void __init ep405_init_bcsr(void) +{ + const u8 *irq_routing; + int i; + + /* Find the bloody thing & map it */ + bcsr_node = of_find_compatible_node(NULL, NULL, "ep405-bcsr"); + if (bcsr_node == NULL) { + printk(KERN_ERR "EP405 BCSR not found !\n"); + return; + } + bcsr_regs = of_iomap(bcsr_node, 0); + if (bcsr_regs == NULL) { + printk(KERN_ERR "EP405 BCSR failed to map !\n"); + return; + } + + /* Get the irq-routing property and apply the routing to the CPLD */ + irq_routing = of_get_property(bcsr_node, "irq-routing", NULL); + if (irq_routing == NULL) + return; + for (i = 0; i < 16; i++) { + u8 irq = irq_routing[i]; + out_8(bcsr_regs + BCSR_XIRQ_SELECT, i); + out_8(bcsr_regs + BCSR_XIRQ_ROUTING, irq); + } + in_8(bcsr_regs + BCSR_XIRQ_SELECT); + mb(); + out_8(bcsr_regs + BCSR_GPIO_IRQ_PAR_CTRL, 0xfe); +} + +static void __init ep405_setup_arch(void) +{ + /* Find & init the BCSR CPLD */ + ep405_init_bcsr(); + + pci_set_flags(PCI_REASSIGN_ALL_RSRC); +} + +static int __init ep405_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (!of_flat_dt_is_compatible(root, "ep405")) + return 0; + + return 1; +} + +define_machine(ep405) { + .name = "EP405", + .probe = ep405_probe, + .setup_arch = ep405_setup_arch, + .progress = udbg_progress, + .init_IRQ = uic_init_tree, + .get_irq = uic_get_irq, + .restart = ppc4xx_reset_system, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/40x/ppc40x_simple.c b/arch/powerpc/platforms/40x/ppc40x_simple.c new file mode 100644 index 00000000000..97612068fae --- /dev/null +++ b/arch/powerpc/platforms/40x/ppc40x_simple.c @@ -0,0 +1,81 @@ +/* + * Generic PowerPC 40x platform support + * + * Copyright 2008 IBM Corporation + * + * 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; version 2 of the License. + * + * This implements simple platform support for PowerPC 44x chips. This is + * mostly used for eval boards or other simple and "generic" 44x boards. If + * your board has custom functions or hardware, then you will likely want to + * implement your own board.c file to accommodate it. + */ + +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <asm/ppc4xx.h> +#include <asm/prom.h> +#include <asm/time.h> +#include <asm/udbg.h> +#include <asm/uic.h> + +#include <linux/init.h> +#include <linux/of_platform.h> + +static __initdata struct of_device_id ppc40x_of_bus[] = { + { .compatible = "ibm,plb3", }, + { .compatible = "ibm,plb4", }, + { .compatible = "ibm,opb", }, + { .compatible = "ibm,ebc", }, + { .compatible = "simple-bus", }, + {}, +}; + +static int __init ppc40x_device_probe(void) +{ + of_platform_bus_probe(NULL, ppc40x_of_bus, NULL); + + return 0; +} +machine_device_initcall(ppc40x_simple, ppc40x_device_probe); + +/* This is the list of boards that can be supported by this simple + * platform code. This does _not_ mean the boards are compatible, + * as they most certainly are not from a device tree perspective. + * However, their differences are handled by the device tree and the + * drivers and therefore they don't need custom board support files. + * + * Again, if your board needs to do things differently then create a + * board.c file for it rather than adding it to this list. + */ +static const char *board[] __initdata = { + "amcc,acadia", + "amcc,haleakala", + "amcc,kilauea", + "amcc,makalu", + "apm,klondike", + "est,hotfoot", + "plathome,obs600" +}; + +static int __init ppc40x_probe(void) +{ + if (of_flat_dt_match(of_get_flat_dt_root(), board)) { + pci_set_flags(PCI_REASSIGN_ALL_RSRC); + return 1; + } + + return 0; +} + +define_machine(ppc40x_simple) { + .name = "PowerPC 40x Platform", + .probe = ppc40x_probe, + .progress = udbg_progress, + .init_IRQ = uic_init_tree, + .get_irq = uic_get_irq, + .restart = ppc4xx_reset_system, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/40x/virtex.c b/arch/powerpc/platforms/40x/virtex.c new file mode 100644 index 00000000000..d0fc6866b00 --- /dev/null +++ b/arch/powerpc/platforms/40x/virtex.c @@ -0,0 +1,56 @@ +/* + * Xilinx Virtex (IIpro & 4FX) based board support + * + * Copyright 2007 Secret Lab Technologies Ltd. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/init.h> +#include <linux/of_platform.h> +#include <asm/machdep.h> +#include <asm/prom.h> +#include <asm/time.h> +#include <asm/xilinx_intc.h> +#include <asm/xilinx_pci.h> +#include <asm/ppc4xx.h> + +static struct of_device_id xilinx_of_bus_ids[] __initdata = { + { .compatible = "xlnx,plb-v46-1.00.a", }, + { .compatible = "xlnx,plb-v34-1.01.a", }, + { .compatible = "xlnx,plb-v34-1.02.a", }, + { .compatible = "xlnx,opb-v20-1.10.c", }, + { .compatible = "xlnx,dcr-v29-1.00.a", }, + { .compatible = "xlnx,compound", }, + {} +}; + +static int __init virtex_device_probe(void) +{ + of_platform_bus_probe(NULL, xilinx_of_bus_ids, NULL); + + return 0; +} +machine_device_initcall(virtex, virtex_device_probe); + +static int __init virtex_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (!of_flat_dt_is_compatible(root, "xlnx,virtex")) + return 0; + + return 1; +} + +define_machine(virtex) { + .name = "Xilinx Virtex", + .probe = virtex_probe, + .setup_arch = xilinx_pci_init, + .init_IRQ = xilinx_intc_init_tree, + .get_irq = xilinx_intc_get_irq, + .restart = ppc4xx_reset_system, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/40x/walnut.c b/arch/powerpc/platforms/40x/walnut.c new file mode 100644 index 00000000000..8b691df72f7 --- /dev/null +++ b/arch/powerpc/platforms/40x/walnut.c @@ -0,0 +1,67 @@ +/* + * Architecture- / platform-specific boot-time initialization code for + * IBM PowerPC 4xx based boards. Adapted from original + * code by Gary Thomas, Cort Dougan <cort@fsmlabs.com>, and Dan Malek + * <dan@net4x.com>. + * + * Copyright(c) 1999-2000 Grant Erickson <grant@lcse.umn.edu> + * + * Rewritten and ported to the merged powerpc tree: + * Copyright 2007 IBM Corporation + * Josh Boyer <jwboyer@linux.vnet.ibm.com> + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include <linux/init.h> +#include <linux/of_platform.h> +#include <linux/rtc.h> + +#include <asm/machdep.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/time.h> +#include <asm/uic.h> +#include <asm/pci-bridge.h> +#include <asm/ppc4xx.h> + +static __initdata struct of_device_id walnut_of_bus[] = { + { .compatible = "ibm,plb3", }, + { .compatible = "ibm,opb", }, + { .compatible = "ibm,ebc", }, + {}, +}; + +static int __init walnut_device_probe(void) +{ + of_platform_bus_probe(NULL, walnut_of_bus, NULL); + of_instantiate_rtc(); + + return 0; +} +machine_device_initcall(walnut, walnut_device_probe); + +static int __init walnut_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (!of_flat_dt_is_compatible(root, "ibm,walnut")) + return 0; + + pci_set_flags(PCI_REASSIGN_ALL_RSRC); + + return 1; +} + +define_machine(walnut) { + .name = "Walnut", + .probe = walnut_probe, + .progress = udbg_progress, + .init_IRQ = uic_init_tree, + .get_irq = uic_get_irq, + .restart = ppc4xx_reset_system, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/44x/44x.h b/arch/powerpc/platforms/44x/44x.h new file mode 100644 index 00000000000..63f703ecd23 --- /dev/null +++ b/arch/powerpc/platforms/44x/44x.h @@ -0,0 +1,11 @@ +#ifndef __POWERPC_PLATFORMS_44X_44X_H +#define __POWERPC_PLATFORMS_44X_44X_H + +extern u8 as1_readb(volatile u8 __iomem *addr); +extern void as1_writeb(u8 data, volatile u8 __iomem *addr); + +#define GPIO0_OSRH 0xC +#define GPIO0_TSRH 0x14 +#define GPIO0_ISR1H 0x34 + +#endif /* __POWERPC_PLATFORMS_44X_44X_H */ diff --git a/arch/powerpc/platforms/44x/Kconfig b/arch/powerpc/platforms/44x/Kconfig new file mode 100644 index 00000000000..fcf6bf2ceee --- /dev/null +++ b/arch/powerpc/platforms/44x/Kconfig @@ -0,0 +1,328 @@ +config PPC_47x + bool "Support for 47x variant" + depends on 44x + default n + select MPIC + help + This option enables support for the 47x family of processors and is + not currently compatible with other 44x or 46x varients + +config BAMBOO + bool "Bamboo" + depends on 44x + default n + select PPC44x_SIMPLE + select 440EP + select PCI + help + This option enables support for the IBM PPC440EP evaluation board. + +config BLUESTONE + bool "Bluestone" + depends on 44x + default n + select PPC44x_SIMPLE + select APM821xx + select IBM_EMAC_RGMII + help + This option enables support for the APM APM821xx Evaluation board. + +config EBONY + bool "Ebony" + depends on 44x + default y + select 440GP + select PCI + select OF_RTC + help + This option enables support for the IBM PPC440GP evaluation board. + +config SAM440EP + bool "Sam440ep" + depends on 44x + default n + select 440EP + select PCI + help + This option enables support for the ACube Sam440ep board. + +config SEQUOIA + bool "Sequoia" + depends on 44x + default n + select PPC44x_SIMPLE + select 440EPX + help + This option enables support for the AMCC PPC440EPX evaluation board. + +config TAISHAN + bool "Taishan" + depends on 44x + default n + select PPC44x_SIMPLE + select 440GX + select PCI + help + This option enables support for the AMCC PPC440GX "Taishan" + evaluation board. + +config KATMAI + bool "Katmai" + depends on 44x + default n + select PPC44x_SIMPLE + select 440SPe + select PCI + select PPC4xx_PCI_EXPRESS + select PCI_MSI + select PPC4xx_MSI + help + This option enables support for the AMCC PPC440SPe evaluation board. + +config RAINIER + bool "Rainier" + depends on 44x + default n + select PPC44x_SIMPLE + select 440GRX + select PCI + help + This option enables support for the AMCC PPC440GRX evaluation board. + +config WARP + bool "PIKA Warp" + depends on 44x + default n + select 440EP + help + This option enables support for the PIKA Warp(tm) Appliance. The Warp + is a small computer replacement with up to 9 ports of FXO/FXS plus VOIP + stations and trunks. + + See http://www.pikatechnologies.com/ and follow the "PIKA for Computer + Telephony Developers" link for more information. + +config ARCHES + bool "Arches" + depends on 44x + default n + select PPC44x_SIMPLE + select 460EX # Odd since it uses 460GT but the effects are the same + select PCI + select PPC4xx_PCI_EXPRESS + help + This option enables support for the AMCC Dual PPC460GT evaluation board. + +config CANYONLANDS + bool "Canyonlands" + depends on 44x + default n + select 460EX + select PCI + select PPC4xx_PCI_EXPRESS + select PCI_MSI + select PPC4xx_MSI + select IBM_EMAC_RGMII + select IBM_EMAC_ZMII + help + This option enables support for the AMCC PPC460EX evaluation board. + +config GLACIER + bool "Glacier" + depends on 44x + default n + select PPC44x_SIMPLE + select 460EX # Odd since it uses 460GT but the effects are the same + select PCI + select PPC4xx_PCI_EXPRESS + select IBM_EMAC_RGMII + select IBM_EMAC_ZMII + help + This option enables support for the AMCC PPC460GT evaluation board. + +config REDWOOD + bool "Redwood" + depends on 44x + default n + select PPC44x_SIMPLE + select 460SX + select PCI + select PPC4xx_PCI_EXPRESS + select PCI_MSI + select PPC4xx_MSI + help + This option enables support for the AMCC PPC460SX Redwood board. + +config EIGER + bool "Eiger" + depends on 44x + default n + select PPC44x_SIMPLE + select 460SX + select PCI + select PPC4xx_PCI_EXPRESS + select IBM_EMAC_RGMII + help + This option enables support for the AMCC PPC460SX evaluation board. + +config YOSEMITE + bool "Yosemite" + depends on 44x + default n + select PPC44x_SIMPLE + select 440EP + select PCI + help + This option enables support for the AMCC PPC440EP evaluation board. + +config ISS4xx + bool "ISS 4xx Simulator" + depends on (44x || 40x) + default n + select 405GP if 40x + select 440GP if 44x && !PPC_47x + select PPC_FPU + select OF_RTC + help + This option enables support for the IBM ISS simulation environment + +config CURRITUCK + bool "IBM Currituck (476fpe) Support" + depends on PPC_47x + default n + select SWIOTLB + select 476FPE + select PPC4xx_PCI_EXPRESS + help + This option enables support for the IBM Currituck (476fpe) evaluation board + +config ICON + bool "Icon" + depends on 44x + default n + select PPC44x_SIMPLE + select 440SPe + select PCI + select PPC4xx_PCI_EXPRESS + help + This option enables support for the AMCC PPC440SPe evaluation board. + +config XILINX_VIRTEX440_GENERIC_BOARD + bool "Generic Xilinx Virtex 5 FXT board support" + depends on 44x + default n + select XILINX_VIRTEX_5_FXT + help + This option enables generic support for Xilinx Virtex based boards + that use a 440 based processor in the Virtex 5 FXT FPGA architecture. + + The generic virtex board support matches any device tree which + specifies 'xlnx,virtex440' in its compatible field. This includes + the Xilinx ML5xx reference designs using the powerpc core. + + Most Virtex 5 designs should use this unless it needs to do some + special configuration at board probe time. + +config XILINX_ML510 + bool "Xilinx ML510 extra support" + depends on XILINX_VIRTEX440_GENERIC_BOARD + select PPC_PCI_CHOICE + select XILINX_PCI if PCI + select PPC_INDIRECT_PCI if PCI + select PPC_I8259 if PCI + help + This option enables extra support for features on the Xilinx ML510 + board. The ML510 has a PCI bus with ALI south bridge. + +config PPC44x_SIMPLE + bool "Simple PowerPC 44x board support" + depends on 44x + default n + help + This option enables the simple PowerPC 44x platform support. + +config PPC4xx_GPIO + bool "PPC4xx GPIO support" + depends on 44x + select ARCH_REQUIRE_GPIOLIB + select GENERIC_GPIO + help + Enable gpiolib support for ppc440 based boards + +# 44x specific CPU modules, selected based on the board above. +config 440EP + bool + select PPC_FPU + select IBM440EP_ERR42 + select IBM_EMAC_ZMII + select USB_ARCH_HAS_OHCI + +config 440EPX + bool + select PPC_FPU + select IBM_EMAC_EMAC4 + select IBM_EMAC_RGMII + select IBM_EMAC_ZMII + +config 440GRX + bool + select IBM_EMAC_EMAC4 + select IBM_EMAC_RGMII + select IBM_EMAC_ZMII + +config 440GP + bool + select IBM_EMAC_ZMII + +config 440GX + bool + select IBM_EMAC_EMAC4 + select IBM_EMAC_RGMII + select IBM_EMAC_ZMII #test only + select IBM_EMAC_TAH #test only + +config 440SP + bool + +config 440SPe + bool + select IBM_EMAC_EMAC4 + +config 460EX + bool + select PPC_FPU + select IBM_EMAC_EMAC4 + select IBM_EMAC_TAH + +config 460SX + bool + select PPC_FPU + select IBM_EMAC_EMAC4 + select IBM_EMAC_RGMII + select IBM_EMAC_ZMII + select IBM_EMAC_TAH + +config 476FPE + bool + select PPC_FPU + +config APM821xx + bool + select PPC_FPU + select IBM_EMAC_EMAC4 + select IBM_EMAC_TAH + +# 44x errata/workaround config symbols, selected by the CPU models above +config IBM440EP_ERR42 + bool + +# Xilinx specific config options. +config XILINX_VIRTEX + bool + select DEFAULT_UIMAGE + +# Xilinx Virtex 5 FXT FPGA architecture, selected by a Xilinx board above +config XILINX_VIRTEX_5_FXT + bool + select XILINX_VIRTEX + diff --git a/arch/powerpc/platforms/44x/Makefile b/arch/powerpc/platforms/44x/Makefile new file mode 100644 index 00000000000..d03833abec0 --- /dev/null +++ b/arch/powerpc/platforms/44x/Makefile @@ -0,0 +1,13 @@ +obj-$(CONFIG_44x) += misc_44x.o +ifneq ($(CONFIG_PPC4xx_CPM),y) +obj-$(CONFIG_44x) += idle.o +endif +obj-$(CONFIG_PPC44x_SIMPLE) += ppc44x_simple.o +obj-$(CONFIG_EBONY) += ebony.o +obj-$(CONFIG_SAM440EP) += sam440ep.o +obj-$(CONFIG_WARP) += warp.o +obj-$(CONFIG_XILINX_VIRTEX_5_FXT) += virtex.o +obj-$(CONFIG_XILINX_ML510) += virtex_ml510.o +obj-$(CONFIG_ISS4xx) += iss4xx.o +obj-$(CONFIG_CANYONLANDS)+= canyonlands.o +obj-$(CONFIG_CURRITUCK) += currituck.o diff --git a/arch/powerpc/platforms/44x/canyonlands.c b/arch/powerpc/platforms/44x/canyonlands.c new file mode 100644 index 00000000000..e300dd4c89b --- /dev/null +++ b/arch/powerpc/platforms/44x/canyonlands.c @@ -0,0 +1,134 @@ +/* + * This contain platform specific code for APM PPC460EX based Canyonlands + * board. + * + * Copyright (c) 2010, Applied Micro Circuits Corporation + * Author: Rupjyoti Sarmah <rsarmah@apm.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <asm/pci-bridge.h> +#include <asm/ppc4xx.h> +#include <asm/udbg.h> +#include <asm/uic.h> +#include <linux/of_platform.h> +#include <linux/delay.h> +#include "44x.h" + +#define BCSR_USB_EN 0x11 + +static __initdata struct of_device_id ppc460ex_of_bus[] = { + { .compatible = "ibm,plb4", }, + { .compatible = "ibm,opb", }, + { .compatible = "ibm,ebc", }, + { .compatible = "simple-bus", }, + {}, +}; + +static int __init ppc460ex_device_probe(void) +{ + of_platform_bus_probe(NULL, ppc460ex_of_bus, NULL); + + return 0; +} +machine_device_initcall(canyonlands, ppc460ex_device_probe); + +/* Using this code only for the Canyonlands board. */ + +static int __init ppc460ex_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + if (of_flat_dt_is_compatible(root, "amcc,canyonlands")) { + pci_set_flags(PCI_REASSIGN_ALL_RSRC); + return 1; + } + return 0; +} + +/* USB PHY fixup code on Canyonlands kit. */ + +static int __init ppc460ex_canyonlands_fixup(void) +{ + u8 __iomem *bcsr ; + void __iomem *vaddr; + struct device_node *np; + int ret = 0; + + np = of_find_compatible_node(NULL, NULL, "amcc,ppc460ex-bcsr"); + if (!np) { + printk(KERN_ERR "failed did not find amcc, ppc460ex bcsr node\n"); + return -ENODEV; + } + + bcsr = of_iomap(np, 0); + of_node_put(np); + + if (!bcsr) { + printk(KERN_CRIT "Could not remap bcsr\n"); + ret = -ENODEV; + goto err_bcsr; + } + + np = of_find_compatible_node(NULL, NULL, "ibm,ppc4xx-gpio"); + if (!np) { + printk(KERN_ERR "failed did not find ibm,ppc4xx-gpio node\n"); + return -ENODEV; + } + + vaddr = of_iomap(np, 0); + of_node_put(np); + + if (!vaddr) { + printk(KERN_CRIT "Could not get gpio node address\n"); + ret = -ENODEV; + goto err_gpio; + } + /* Disable USB, through the BCSR7 bits */ + setbits8(&bcsr[7], BCSR_USB_EN); + + /* Wait for a while after reset */ + msleep(100); + + /* Enable USB here */ + clrbits8(&bcsr[7], BCSR_USB_EN); + + /* + * Configure multiplexed gpio16 and gpio19 as alternate1 output + * source after USB reset. In this configuration gpio16 will be + * USB2HStop and gpio19 will be USB2DStop. For more details refer to + * table 34-7 of PPC460EX user manual. + */ + setbits32((vaddr + GPIO0_OSRH), 0x42000000); + setbits32((vaddr + GPIO0_TSRH), 0x42000000); +err_gpio: + iounmap(vaddr); +err_bcsr: + iounmap(bcsr); + return ret; +} +machine_device_initcall(canyonlands, ppc460ex_canyonlands_fixup); +define_machine(canyonlands) { + .name = "Canyonlands", + .probe = ppc460ex_probe, + .progress = udbg_progress, + .init_IRQ = uic_init_tree, + .get_irq = uic_get_irq, + .restart = ppc4xx_reset_system, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/44x/currituck.c b/arch/powerpc/platforms/44x/currituck.c new file mode 100644 index 00000000000..3f6229b5dee --- /dev/null +++ b/arch/powerpc/platforms/44x/currituck.c @@ -0,0 +1,204 @@ +/* + * Currituck board specific routines + * + * Copyright © 2011 Tony Breeds IBM Corporation + * + * Based on earlier code: + * Matt Porter <mporter@kernel.crashing.org> + * Copyright 2002-2005 MontaVista Software Inc. + * + * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> + * Copyright (c) 2003-2005 Zultys Technologies + * + * Rewritten and ported to the merged powerpc tree: + * Copyright 2007 David Gibson <dwg@au1.ibm.com>, IBM Corporation. + * Copyright © 2011 David Kliekamp IBM Corporation + * + * 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 <linux/init.h> +#include <linux/memblock.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/rtc.h> + +#include <asm/machdep.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/time.h> +#include <asm/uic.h> +#include <asm/ppc4xx.h> +#include <asm/mpic.h> +#include <asm/mmu.h> + +#include <linux/pci.h> + +static __initdata struct of_device_id ppc47x_of_bus[] = { + { .compatible = "ibm,plb4", }, + { .compatible = "ibm,plb6", }, + { .compatible = "ibm,opb", }, + { .compatible = "ibm,ebc", }, + {}, +}; + +/* The EEPROM is missing and the default values are bogus. This forces USB in + * to EHCI mode */ +static void __devinit quirk_ppc_currituck_usb_fixup(struct pci_dev *dev) +{ + if (of_machine_is_compatible("ibm,currituck")) { + pci_write_config_dword(dev, 0xe0, 0x0114231f); + pci_write_config_dword(dev, 0xe4, 0x00006c40); + } +} +DECLARE_PCI_FIXUP_HEADER(0x1033, 0x0035, quirk_ppc_currituck_usb_fixup); + +static int __init ppc47x_device_probe(void) +{ + of_platform_bus_probe(NULL, ppc47x_of_bus, NULL); + + return 0; +} +machine_device_initcall(ppc47x, ppc47x_device_probe); + +/* We can have either UICs or MPICs */ +static void __init ppc47x_init_irq(void) +{ + struct device_node *np; + + /* Find top level interrupt controller */ + for_each_node_with_property(np, "interrupt-controller") { + if (of_get_property(np, "interrupts", NULL) == NULL) + break; + } + if (np == NULL) + panic("Can't find top level interrupt controller"); + + /* Check type and do appropriate initialization */ + if (of_device_is_compatible(np, "chrp,open-pic")) { + /* The MPIC driver will get everything it needs from the + * device-tree, just pass 0 to all arguments + */ + struct mpic *mpic = + mpic_alloc(np, 0, 0, 0, 0, " MPIC "); + BUG_ON(mpic == NULL); + mpic_init(mpic); + ppc_md.get_irq = mpic_get_irq; + } else + panic("Unrecognized top level interrupt controller"); +} + +#ifdef CONFIG_SMP +static void __cpuinit smp_ppc47x_setup_cpu(int cpu) +{ + mpic_setup_this_cpu(); +} + +static int __cpuinit smp_ppc47x_kick_cpu(int cpu) +{ + struct device_node *cpunode = of_get_cpu_node(cpu, NULL); + const u64 *spin_table_addr_prop; + u32 *spin_table; + extern void start_secondary_47x(void); + + BUG_ON(cpunode == NULL); + + /* Assume spin table. We could test for the enable-method in + * the device-tree but currently there's little point as it's + * our only supported method + */ + spin_table_addr_prop = + of_get_property(cpunode, "cpu-release-addr", NULL); + + if (spin_table_addr_prop == NULL) { + pr_err("CPU%d: Can't start, missing cpu-release-addr !\n", + cpu); + return 1; + } + + /* Assume it's mapped as part of the linear mapping. This is a bit + * fishy but will work fine for now + * + * XXX: Is there any reason to assume differently? + */ + spin_table = (u32 *)__va(*spin_table_addr_prop); + pr_debug("CPU%d: Spin table mapped at %p\n", cpu, spin_table); + + spin_table[3] = cpu; + smp_wmb(); + spin_table[1] = __pa(start_secondary_47x); + mb(); + + return 0; +} + +static struct smp_ops_t ppc47x_smp_ops = { + .probe = smp_mpic_probe, + .message_pass = smp_mpic_message_pass, + .setup_cpu = smp_ppc47x_setup_cpu, + .kick_cpu = smp_ppc47x_kick_cpu, + .give_timebase = smp_generic_give_timebase, + .take_timebase = smp_generic_take_timebase, +}; + +static void __init ppc47x_smp_init(void) +{ + if (mmu_has_feature(MMU_FTR_TYPE_47x)) + smp_ops = &ppc47x_smp_ops; +} + +#else /* CONFIG_SMP */ +static void __init ppc47x_smp_init(void) { } +#endif /* CONFIG_SMP */ + +static void __init ppc47x_setup_arch(void) +{ + + /* No need to check the DMA config as we /know/ our windows are all of + * RAM. Lets hope that doesn't change */ +#ifdef CONFIG_SWIOTLB + if (memblock_end_of_DRAM() > 0xffffffff) { + ppc_swiotlb_enable = 1; + set_pci_dma_ops(&swiotlb_dma_ops); + ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_swiotlb; + } +#endif + ppc47x_smp_init(); +} + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init ppc47x_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (!of_flat_dt_is_compatible(root, "ibm,currituck")) + return 0; + + return 1; +} + +/* Use USB controller should have been hardware swizzled but it wasn't :( */ +static void ppc47x_pci_irq_fixup(struct pci_dev *dev) +{ + if (dev->vendor == 0x1033 && (dev->device == 0x0035 || + dev->device == 0x00e0)) { + dev->irq = irq_create_mapping(NULL, 47); + pr_info("%s: Mapping irq 47 %d\n", __func__, dev->irq); + } +} + +define_machine(ppc47x) { + .name = "PowerPC 47x", + .probe = ppc47x_probe, + .progress = udbg_progress, + .init_IRQ = ppc47x_init_irq, + .setup_arch = ppc47x_setup_arch, + .pci_irq_fixup = ppc47x_pci_irq_fixup, + .restart = ppc4xx_reset_system, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/44x/ebony.c b/arch/powerpc/platforms/44x/ebony.c new file mode 100644 index 00000000000..6a4232bbdf8 --- /dev/null +++ b/arch/powerpc/platforms/44x/ebony.c @@ -0,0 +1,70 @@ +/* + * Ebony board specific routines + * + * Matt Porter <mporter@kernel.crashing.org> + * Copyright 2002-2005 MontaVista Software Inc. + * + * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> + * Copyright (c) 2003-2005 Zultys Technologies + * + * Rewritten and ported to the merged powerpc tree: + * Copyright 2007 David Gibson <dwg@au1.ibm.com>, IBM Corporation. + * + * 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 <linux/init.h> +#include <linux/of_platform.h> +#include <linux/rtc.h> + +#include <asm/machdep.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/time.h> +#include <asm/uic.h> +#include <asm/pci-bridge.h> +#include <asm/ppc4xx.h> + +static __initdata struct of_device_id ebony_of_bus[] = { + { .compatible = "ibm,plb4", }, + { .compatible = "ibm,opb", }, + { .compatible = "ibm,ebc", }, + {}, +}; + +static int __init ebony_device_probe(void) +{ + of_platform_bus_probe(NULL, ebony_of_bus, NULL); + of_instantiate_rtc(); + + return 0; +} +machine_device_initcall(ebony, ebony_device_probe); + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init ebony_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (!of_flat_dt_is_compatible(root, "ibm,ebony")) + return 0; + + pci_set_flags(PCI_REASSIGN_ALL_RSRC); + + return 1; +} + +define_machine(ebony) { + .name = "Ebony", + .probe = ebony_probe, + .progress = udbg_progress, + .init_IRQ = uic_init_tree, + .get_irq = uic_get_irq, + .restart = ppc4xx_reset_system, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/44x/idle.c b/arch/powerpc/platforms/44x/idle.c new file mode 100644 index 00000000000..7a81f921fef --- /dev/null +++ b/arch/powerpc/platforms/44x/idle.c @@ -0,0 +1,67 @@ +/* + * Copyright 2008 IBM Corp. + * + * Based on arch/powerpc/platforms/pasemi/idle.c: + * Copyright (C) 2006-2007 PA Semi, Inc + * + * Added by: Jerone Young <jyoung5@us.ibm.com> + * + * 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 + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/of.h> +#include <linux/kernel.h> +#include <asm/machdep.h> + +static int mode_spin; + +static void ppc44x_idle(void) +{ + unsigned long msr_save; + + msr_save = mfmsr(); + /* set wait state MSR */ + mtmsr(msr_save|MSR_WE|MSR_EE|MSR_CE|MSR_DE); + isync(); + /* return to initial state */ + mtmsr(msr_save); + isync(); +} + +int __init ppc44x_idle_init(void) +{ + if (!mode_spin) { + /* If we are not setting spin mode + then we set to wait mode */ + ppc_md.power_save = &ppc44x_idle; + } + + return 0; +} + +arch_initcall(ppc44x_idle_init); + +static int __init idle_param(char *p) +{ + + if (!strcmp("spin", p)) { + mode_spin = 1; + ppc_md.power_save = NULL; + } + + return 0; +} + +early_param("idle", idle_param); diff --git a/arch/powerpc/platforms/44x/iss4xx.c b/arch/powerpc/platforms/44x/iss4xx.c new file mode 100644 index 00000000000..5b8cdbb82f8 --- /dev/null +++ b/arch/powerpc/platforms/44x/iss4xx.c @@ -0,0 +1,169 @@ +/* + * PPC476 board specific routines + * + * Copyright 2010 Torez Smith, IBM Corporation. + * + * Based on earlier code: + * Matt Porter <mporter@kernel.crashing.org> + * Copyright 2002-2005 MontaVista Software Inc. + * + * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> + * Copyright (c) 2003-2005 Zultys Technologies + * + * Rewritten and ported to the merged powerpc tree: + * Copyright 2007 David Gibson <dwg@au1.ibm.com>, IBM Corporation. + * + * 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 <linux/init.h> +#include <linux/of_platform.h> +#include <linux/rtc.h> + +#include <asm/machdep.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/time.h> +#include <asm/uic.h> +#include <asm/ppc4xx.h> +#include <asm/mpic.h> +#include <asm/mmu.h> + +static __initdata struct of_device_id iss4xx_of_bus[] = { + { .compatible = "ibm,plb4", }, + { .compatible = "ibm,plb6", }, + { .compatible = "ibm,opb", }, + { .compatible = "ibm,ebc", }, + {}, +}; + +static int __init iss4xx_device_probe(void) +{ + of_platform_bus_probe(NULL, iss4xx_of_bus, NULL); + of_instantiate_rtc(); + + return 0; +} +machine_device_initcall(iss4xx, iss4xx_device_probe); + +/* We can have either UICs or MPICs */ +static void __init iss4xx_init_irq(void) +{ + struct device_node *np; + + /* Find top level interrupt controller */ + for_each_node_with_property(np, "interrupt-controller") { + if (of_get_property(np, "interrupts", NULL) == NULL) + break; + } + if (np == NULL) + panic("Can't find top level interrupt controller"); + + /* Check type and do appropriate initialization */ + if (of_device_is_compatible(np, "ibm,uic")) { + uic_init_tree(); + ppc_md.get_irq = uic_get_irq; +#ifdef CONFIG_MPIC + } else if (of_device_is_compatible(np, "chrp,open-pic")) { + /* The MPIC driver will get everything it needs from the + * device-tree, just pass 0 to all arguments + */ + struct mpic *mpic = mpic_alloc(np, 0, 0, 0, 0, + " MPIC "); + BUG_ON(mpic == NULL); + mpic_init(mpic); + ppc_md.get_irq = mpic_get_irq; +#endif + } else + panic("Unrecognized top level interrupt controller"); +} + +#ifdef CONFIG_SMP +static void __cpuinit smp_iss4xx_setup_cpu(int cpu) +{ + mpic_setup_this_cpu(); +} + +static int __cpuinit smp_iss4xx_kick_cpu(int cpu) +{ + struct device_node *cpunode = of_get_cpu_node(cpu, NULL); + const u64 *spin_table_addr_prop; + u32 *spin_table; + extern void start_secondary_47x(void); + + BUG_ON(cpunode == NULL); + + /* Assume spin table. We could test for the enable-method in + * the device-tree but currently there's little point as it's + * our only supported method + */ + spin_table_addr_prop = of_get_property(cpunode, "cpu-release-addr", + NULL); + if (spin_table_addr_prop == NULL) { + pr_err("CPU%d: Can't start, missing cpu-release-addr !\n", cpu); + return -ENOENT; + } + + /* Assume it's mapped as part of the linear mapping. This is a bit + * fishy but will work fine for now + */ + spin_table = (u32 *)__va(*spin_table_addr_prop); + pr_debug("CPU%d: Spin table mapped at %p\n", cpu, spin_table); + + spin_table[3] = cpu; + smp_wmb(); + spin_table[1] = __pa(start_secondary_47x); + mb(); + + return 0; +} + +static struct smp_ops_t iss_smp_ops = { + .probe = smp_mpic_probe, + .message_pass = smp_mpic_message_pass, + .setup_cpu = smp_iss4xx_setup_cpu, + .kick_cpu = smp_iss4xx_kick_cpu, + .give_timebase = smp_generic_give_timebase, + .take_timebase = smp_generic_take_timebase, +}; + +static void __init iss4xx_smp_init(void) +{ + if (mmu_has_feature(MMU_FTR_TYPE_47x)) + smp_ops = &iss_smp_ops; +} + +#else /* CONFIG_SMP */ +static void __init iss4xx_smp_init(void) { } +#endif /* CONFIG_SMP */ + +static void __init iss4xx_setup_arch(void) +{ + iss4xx_smp_init(); +} + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init iss4xx_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (!of_flat_dt_is_compatible(root, "ibm,iss-4xx")) + return 0; + + return 1; +} + +define_machine(iss4xx) { + .name = "ISS-4xx", + .probe = iss4xx_probe, + .progress = udbg_progress, + .init_IRQ = iss4xx_init_irq, + .setup_arch = iss4xx_setup_arch, + .restart = ppc4xx_reset_system, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/44x/misc_44x.S b/arch/powerpc/platforms/44x/misc_44x.S new file mode 100644 index 00000000000..dc12b8009e4 --- /dev/null +++ b/arch/powerpc/platforms/44x/misc_44x.S @@ -0,0 +1,46 @@ +/* + * This file contains miscellaneous low-level functions for PPC 44x. + * Copyright 2007 David Gibson <dwg@au1.ibm.com>, IBM Corporation. + * + * 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 <asm/reg.h> +#include <asm/ppc_asm.h> + + .text + +/* + * Do an IO access in AS1 + */ +_GLOBAL(as1_readb) + mfmsr r7 + ori r0,r7,MSR_DS + sync + mtmsr r0 + sync + isync + lbz r3,0(r3) + sync + mtmsr r7 + sync + isync + blr + +_GLOBAL(as1_writeb) + mfmsr r7 + ori r0,r7,MSR_DS + sync + mtmsr r0 + sync + isync + stb r3,0(r4) + sync + mtmsr r7 + sync + isync + blr diff --git a/arch/powerpc/platforms/44x/ppc44x_simple.c b/arch/powerpc/platforms/44x/ppc44x_simple.c new file mode 100644 index 00000000000..8d220276341 --- /dev/null +++ b/arch/powerpc/platforms/44x/ppc44x_simple.c @@ -0,0 +1,91 @@ +/* + * Generic PowerPC 44x platform support + * + * Copyright 2008 IBM Corporation + * + * 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; version 2 of the License. + * + * This implements simple platform support for PowerPC 44x chips. This is + * mostly used for eval boards or other simple and "generic" 44x boards. If + * your board has custom functions or hardware, then you will likely want to + * implement your own board.c file to accommodate it. + */ + +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <asm/ppc4xx.h> +#include <asm/prom.h> +#include <asm/time.h> +#include <asm/udbg.h> +#include <asm/uic.h> + +#include <linux/init.h> +#include <linux/of_platform.h> + +static __initdata struct of_device_id ppc44x_of_bus[] = { + { .compatible = "ibm,plb4", }, + { .compatible = "ibm,opb", }, + { .compatible = "ibm,ebc", }, + { .compatible = "simple-bus", }, + {}, +}; + +static int __init ppc44x_device_probe(void) +{ + of_platform_bus_probe(NULL, ppc44x_of_bus, NULL); + + return 0; +} +machine_device_initcall(ppc44x_simple, ppc44x_device_probe); + +/* This is the list of boards that can be supported by this simple + * platform code. This does _not_ mean the boards are compatible, + * as they most certainly are not from a device tree perspective. + * However, their differences are handled by the device tree and the + * drivers and therefore they don't need custom board support files. + * + * Again, if your board needs to do things differently then create a + * board.c file for it rather than adding it to this list. + */ +static char *board[] __initdata = { + "amcc,arches", + "amcc,bamboo", + "amcc,bluestone", + "amcc,glacier", + "ibm,ebony", + "amcc,eiger", + "amcc,katmai", + "amcc,rainier", + "amcc,redwood", + "amcc,sequoia", + "amcc,taishan", + "amcc,yosemite", + "mosaixtech,icon" +}; + +static int __init ppc44x_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + int i = 0; + + for (i = 0; i < ARRAY_SIZE(board); i++) { + if (of_flat_dt_is_compatible(root, board[i])) { + pci_set_flags(PCI_REASSIGN_ALL_RSRC); + return 1; + } + } + + return 0; +} + +define_machine(ppc44x_simple) { + .name = "PowerPC 44x Platform", + .probe = ppc44x_probe, + .progress = udbg_progress, + .init_IRQ = uic_init_tree, + .get_irq = uic_get_irq, + .restart = ppc4xx_reset_system, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/44x/sam440ep.c b/arch/powerpc/platforms/44x/sam440ep.c new file mode 100644 index 00000000000..9e09b835758 --- /dev/null +++ b/arch/powerpc/platforms/44x/sam440ep.c @@ -0,0 +1,79 @@ +/* + * Sam440ep board specific routines based off bamboo.c code + * original copyrights below + * + * Wade Farnsworth <wfarnsworth@mvista.com> + * Copyright 2004 MontaVista Software Inc. + * + * Rewritten and ported to the merged powerpc tree: + * Josh Boyer <jwboyer@linux.vnet.ibm.com> + * Copyright 2007 IBM Corporation + * + * Modified from bamboo.c for sam440ep: + * Copyright 2008 Giuseppe Coviello <gicoviello@gmail.com> + * + * 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 <linux/init.h> +#include <linux/of_platform.h> + +#include <asm/machdep.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/time.h> +#include <asm/uic.h> +#include <asm/pci-bridge.h> +#include <asm/ppc4xx.h> +#include <linux/i2c.h> + +static __initdata struct of_device_id sam440ep_of_bus[] = { + { .compatible = "ibm,plb4", }, + { .compatible = "ibm,opb", }, + { .compatible = "ibm,ebc", }, + {}, +}; + +static int __init sam440ep_device_probe(void) +{ + of_platform_bus_probe(NULL, sam440ep_of_bus, NULL); + + return 0; +} +machine_device_initcall(sam440ep, sam440ep_device_probe); + +static int __init sam440ep_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (!of_flat_dt_is_compatible(root, "acube,sam440ep")) + return 0; + + pci_set_flags(PCI_REASSIGN_ALL_RSRC); + + return 1; +} + +define_machine(sam440ep) { + .name = "Sam440ep", + .probe = sam440ep_probe, + .progress = udbg_progress, + .init_IRQ = uic_init_tree, + .get_irq = uic_get_irq, + .restart = ppc4xx_reset_system, + .calibrate_decr = generic_calibrate_decr, +}; + +static struct i2c_board_info sam440ep_rtc_info = { + .type = "m41st85", + .addr = 0x68, + .irq = -1, +}; + +static int sam440ep_setup_rtc(void) +{ + return i2c_register_board_info(0, &sam440ep_rtc_info, 1); +} +machine_device_initcall(sam440ep, sam440ep_setup_rtc); diff --git a/arch/powerpc/platforms/44x/virtex.c b/arch/powerpc/platforms/44x/virtex.c new file mode 100644 index 00000000000..cf96ccaa760 --- /dev/null +++ b/arch/powerpc/platforms/44x/virtex.c @@ -0,0 +1,62 @@ +/* + * Xilinx Virtex 5FXT based board support, derived from + * the Xilinx Virtex (IIpro & 4FX) based board support + * + * Copyright 2007 Secret Lab Technologies Ltd. + * Copyright 2008 Xilinx, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/init.h> +#include <linux/of_platform.h> +#include <asm/machdep.h> +#include <asm/prom.h> +#include <asm/time.h> +#include <asm/xilinx_intc.h> +#include <asm/xilinx_pci.h> +#include <asm/reg.h> +#include <asm/ppc4xx.h> +#include "44x.h" + +static struct of_device_id xilinx_of_bus_ids[] __initdata = { + { .compatible = "simple-bus", }, + { .compatible = "xlnx,plb-v46-1.00.a", }, + { .compatible = "xlnx,plb-v46-1.02.a", }, + { .compatible = "xlnx,plb-v34-1.01.a", }, + { .compatible = "xlnx,plb-v34-1.02.a", }, + { .compatible = "xlnx,opb-v20-1.10.c", }, + { .compatible = "xlnx,dcr-v29-1.00.a", }, + { .compatible = "xlnx,compound", }, + {} +}; + +static int __init virtex_device_probe(void) +{ + of_platform_bus_probe(NULL, xilinx_of_bus_ids, NULL); + + return 0; +} +machine_device_initcall(virtex, virtex_device_probe); + +static int __init virtex_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (!of_flat_dt_is_compatible(root, "xlnx,virtex440")) + return 0; + + return 1; +} + +define_machine(virtex) { + .name = "Xilinx Virtex440", + .probe = virtex_probe, + .setup_arch = xilinx_pci_init, + .init_IRQ = xilinx_intc_init_tree, + .get_irq = xilinx_intc_get_irq, + .calibrate_decr = generic_calibrate_decr, + .restart = ppc4xx_reset_system, +}; diff --git a/arch/powerpc/platforms/44x/virtex_ml510.c b/arch/powerpc/platforms/44x/virtex_ml510.c new file mode 100644 index 00000000000..ba4a6e388a4 --- /dev/null +++ b/arch/powerpc/platforms/44x/virtex_ml510.c @@ -0,0 +1,29 @@ +#include <asm/i8259.h> +#include <linux/pci.h> +#include "44x.h" + +/** + * ml510_ail_quirk + */ +static void __devinit ml510_ali_quirk(struct pci_dev *dev) +{ + /* Enable the IDE controller */ + pci_write_config_byte(dev, 0x58, 0x4c); + /* Assign irq 14 to the primary ide channel */ + pci_write_config_byte(dev, 0x44, 0x0d); + /* Assign irq 15 to the secondary ide channel */ + pci_write_config_byte(dev, 0x75, 0x0f); + /* Set the ide controller in native mode */ + pci_write_config_byte(dev, 0x09, 0xff); + + /* INTB = disabled, INTA = disabled */ + pci_write_config_byte(dev, 0x48, 0x00); + /* INTD = disabled, INTC = disabled */ + pci_write_config_byte(dev, 0x4a, 0x00); + /* Audio = INT7, Modem = disabled. */ + pci_write_config_byte(dev, 0x4b, 0x60); + /* USB = INT7 */ + pci_write_config_byte(dev, 0x74, 0x06); +} +DECLARE_PCI_FIXUP_EARLY(0x10b9, 0x1533, ml510_ali_quirk); + diff --git a/arch/powerpc/platforms/44x/warp.c b/arch/powerpc/platforms/44x/warp.c new file mode 100644 index 00000000000..4cfa49901c0 --- /dev/null +++ b/arch/powerpc/platforms/44x/warp.c @@ -0,0 +1,320 @@ +/* + * PIKA Warp(tm) board specific routines + * + * Copyright (c) 2008-2009 PIKA Technologies + * Sean MacLennan <smaclennan@pikatech.com> + * + * 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 <linux/init.h> +#include <linux/of_platform.h> +#include <linux/kthread.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/of_gpio.h> +#include <linux/of_i2c.h> +#include <linux/slab.h> +#include <linux/export.h> + +#include <asm/machdep.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/time.h> +#include <asm/uic.h> +#include <asm/ppc4xx.h> + + +static __initdata struct of_device_id warp_of_bus[] = { + { .compatible = "ibm,plb4", }, + { .compatible = "ibm,opb", }, + { .compatible = "ibm,ebc", }, + {}, +}; + +static int __init warp_device_probe(void) +{ + of_platform_bus_probe(NULL, warp_of_bus, NULL); + return 0; +} +machine_device_initcall(warp, warp_device_probe); + +static int __init warp_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (!of_flat_dt_is_compatible(root, "pika,warp")) + return 0; + + /* For __dma_alloc_coherent */ + ISA_DMA_THRESHOLD = ~0L; + + return 1; +} + +define_machine(warp) { + .name = "Warp", + .probe = warp_probe, + .progress = udbg_progress, + .init_IRQ = uic_init_tree, + .get_irq = uic_get_irq, + .restart = ppc4xx_reset_system, + .calibrate_decr = generic_calibrate_decr, +}; + + +static int __init warp_post_info(void) +{ + struct device_node *np; + void __iomem *fpga; + u32 post1, post2; + + /* Sighhhh... POST information is in the sd area. */ + np = of_find_compatible_node(NULL, NULL, "pika,fpga-sd"); + if (np == NULL) + return -ENOENT; + + fpga = of_iomap(np, 0); + of_node_put(np); + if (fpga == NULL) + return -ENOENT; + + post1 = in_be32(fpga + 0x40); + post2 = in_be32(fpga + 0x44); + + iounmap(fpga); + + if (post1 || post2) + printk(KERN_INFO "Warp POST %08x %08x\n", post1, post2); + else + printk(KERN_INFO "Warp POST OK\n"); + + return 0; +} + + +#ifdef CONFIG_SENSORS_AD7414 + +static LIST_HEAD(dtm_shutdown_list); +static void __iomem *dtm_fpga; +static unsigned green_led, red_led; + + +struct dtm_shutdown { + struct list_head list; + void (*func)(void *arg); + void *arg; +}; + + +int pika_dtm_register_shutdown(void (*func)(void *arg), void *arg) +{ + struct dtm_shutdown *shutdown; + + shutdown = kmalloc(sizeof(struct dtm_shutdown), GFP_KERNEL); + if (shutdown == NULL) + return -ENOMEM; + + shutdown->func = func; + shutdown->arg = arg; + + list_add(&shutdown->list, &dtm_shutdown_list); + + return 0; +} + +int pika_dtm_unregister_shutdown(void (*func)(void *arg), void *arg) +{ + struct dtm_shutdown *shutdown; + + list_for_each_entry(shutdown, &dtm_shutdown_list, list) + if (shutdown->func == func && shutdown->arg == arg) { + list_del(&shutdown->list); + kfree(shutdown); + return 0; + } + + return -EINVAL; +} + +static irqreturn_t temp_isr(int irq, void *context) +{ + struct dtm_shutdown *shutdown; + int value = 1; + + local_irq_disable(); + + gpio_set_value(green_led, 0); + + /* Run through the shutdown list. */ + list_for_each_entry(shutdown, &dtm_shutdown_list, list) + shutdown->func(shutdown->arg); + + printk(KERN_EMERG "\n\nCritical Temperature Shutdown\n\n"); + + while (1) { + if (dtm_fpga) { + unsigned reset = in_be32(dtm_fpga + 0x14); + out_be32(dtm_fpga + 0x14, reset); + } + + gpio_set_value(red_led, value); + value ^= 1; + mdelay(500); + } + + /* Not reached */ + return IRQ_HANDLED; +} + +static int pika_setup_leds(void) +{ + struct device_node *np, *child; + + np = of_find_compatible_node(NULL, NULL, "gpio-leds"); + if (!np) { + printk(KERN_ERR __FILE__ ": Unable to find leds\n"); + return -ENOENT; + } + + for_each_child_of_node(np, child) + if (strcmp(child->name, "green") == 0) + green_led = of_get_gpio(child, 0); + else if (strcmp(child->name, "red") == 0) + red_led = of_get_gpio(child, 0); + + of_node_put(np); + + return 0; +} + +static void pika_setup_critical_temp(struct device_node *np, + struct i2c_client *client) +{ + int irq, rc; + + /* Do this before enabling critical temp interrupt since we + * may immediately interrupt. + */ + pika_setup_leds(); + + /* These registers are in 1 degree increments. */ + i2c_smbus_write_byte_data(client, 2, 65); /* Thigh */ + i2c_smbus_write_byte_data(client, 3, 0); /* Tlow */ + + irq = irq_of_parse_and_map(np, 0); + if (irq == NO_IRQ) { + printk(KERN_ERR __FILE__ ": Unable to get ad7414 irq\n"); + return; + } + + rc = request_irq(irq, temp_isr, 0, "ad7414", NULL); + if (rc) { + printk(KERN_ERR __FILE__ + ": Unable to request ad7414 irq %d = %d\n", irq, rc); + return; + } +} + +static inline void pika_dtm_check_fan(void __iomem *fpga) +{ + static int fan_state; + u32 fan = in_be32(fpga + 0x34) & (1 << 14); + + if (fan_state != fan) { + fan_state = fan; + if (fan) + printk(KERN_WARNING "Fan rotation error detected." + " Please check hardware.\n"); + } +} + +static int pika_dtm_thread(void __iomem *fpga) +{ + struct device_node *np; + struct i2c_client *client; + + np = of_find_compatible_node(NULL, NULL, "adi,ad7414"); + if (np == NULL) + return -ENOENT; + + client = of_find_i2c_device_by_node(np); + if (client == NULL) { + of_node_put(np); + return -ENOENT; + } + + pika_setup_critical_temp(np, client); + + of_node_put(np); + + printk(KERN_INFO "Warp DTM thread running.\n"); + + while (!kthread_should_stop()) { + int val; + + val = i2c_smbus_read_word_data(client, 0); + if (val < 0) + dev_dbg(&client->dev, "DTM read temp failed.\n"); + else { + s16 temp = swab16(val); + out_be32(fpga + 0x20, temp); + } + + pika_dtm_check_fan(fpga); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + } + + return 0; +} + +static int __init pika_dtm_start(void) +{ + struct task_struct *dtm_thread; + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "pika,fpga"); + if (np == NULL) + return -ENOENT; + + dtm_fpga = of_iomap(np, 0); + of_node_put(np); + if (dtm_fpga == NULL) + return -ENOENT; + + /* Must get post info before thread starts. */ + warp_post_info(); + + dtm_thread = kthread_run(pika_dtm_thread, dtm_fpga, "pika-dtm"); + if (IS_ERR(dtm_thread)) { + iounmap(dtm_fpga); + return PTR_ERR(dtm_thread); + } + + return 0; +} +machine_late_initcall(warp, pika_dtm_start); + +#else /* !CONFIG_SENSORS_AD7414 */ + +int pika_dtm_register_shutdown(void (*func)(void *arg), void *arg) +{ + return 0; +} + +int pika_dtm_unregister_shutdown(void (*func)(void *arg), void *arg) +{ + return 0; +} + +machine_late_initcall(warp, warp_post_info); + +#endif + +EXPORT_SYMBOL(pika_dtm_register_shutdown); +EXPORT_SYMBOL(pika_dtm_unregister_shutdown); diff --git a/arch/powerpc/platforms/512x/Kconfig b/arch/powerpc/platforms/512x/Kconfig new file mode 100644 index 00000000000..c16999802ec --- /dev/null +++ b/arch/powerpc/platforms/512x/Kconfig @@ -0,0 +1,34 @@ +config PPC_MPC512x + bool "512x-based boards" + depends on 6xx + select FSL_SOC + select IPIC + select PPC_CLOCK + select PPC_PCI_CHOICE + select FSL_PCI if PCI + select ARCH_WANT_OPTIONAL_GPIOLIB + +config MPC5121_ADS + bool "Freescale MPC5121E ADS" + depends on PPC_MPC512x + select DEFAULT_UIMAGE + help + This option enables support for the MPC5121E ADS board. + +config MPC5121_GENERIC + bool "Generic support for simple MPC5121 based boards" + depends on PPC_MPC512x + select DEFAULT_UIMAGE + help + This option enables support for simple MPC5121 based boards + which do not need custom platform specific setup. + + Compatible boards include: Protonic LVT base boards (ZANMCU + and VICVT2). + +config PDM360NG + bool "ifm PDM360NG board" + depends on PPC_MPC512x + select DEFAULT_UIMAGE + help + This option enables support for the PDM360NG board. diff --git a/arch/powerpc/platforms/512x/Makefile b/arch/powerpc/platforms/512x/Makefile new file mode 100644 index 00000000000..4efc1c4b6fb --- /dev/null +++ b/arch/powerpc/platforms/512x/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Freescale PowerPC 512x linux kernel. +# +obj-y += clock.o mpc512x_shared.o +obj-$(CONFIG_MPC5121_ADS) += mpc5121_ads.o mpc5121_ads_cpld.o +obj-$(CONFIG_MPC5121_GENERIC) += mpc5121_generic.o +obj-$(CONFIG_PDM360NG) += pdm360ng.o diff --git a/arch/powerpc/platforms/512x/clock.c b/arch/powerpc/platforms/512x/clock.c new file mode 100644 index 00000000000..1d8700ff60b --- /dev/null +++ b/arch/powerpc/platforms/512x/clock.c @@ -0,0 +1,744 @@ +/* + * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: John Rigby <jrigby@freescale.com> + * + * Implements the clk api defined in include/linux/clk.h + * + * Original based on linux/arch/arm/mach-integrator/clock.c + * + * Copyright (C) 2004 ARM Limited. + * Written by Deep Blue Solutions Limited. + * + * 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 + * published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/clk.h> +#include <linux/mutex.h> +#include <linux/io.h> + +#include <linux/of_platform.h> +#include <asm/mpc5xxx.h> +#include <asm/clk_interface.h> + +#undef CLK_DEBUG + +static int clocks_initialized; + +#define CLK_HAS_RATE 0x1 /* has rate in MHz */ +#define CLK_HAS_CTRL 0x2 /* has control reg and bit */ + +struct clk { + struct list_head node; + char name[32]; + int flags; + struct device *dev; + unsigned long rate; + struct module *owner; + void (*calc) (struct clk *); + struct clk *parent; + int reg, bit; /* CLK_HAS_CTRL */ + int div_shift; /* only used by generic_div_clk_calc */ +}; + +static LIST_HEAD(clocks); +static DEFINE_MUTEX(clocks_mutex); + +static struct clk *mpc5121_clk_get(struct device *dev, const char *id) +{ + struct clk *p, *clk = ERR_PTR(-ENOENT); + int dev_match = 0; + int id_match = 0; + + if (dev == NULL || id == NULL) + return clk; + + mutex_lock(&clocks_mutex); + list_for_each_entry(p, &clocks, node) { + if (dev == p->dev) + dev_match++; + if (strcmp(id, p->name) == 0) + id_match++; + if ((dev_match || id_match) && try_module_get(p->owner)) { + clk = p; + break; + } + } + mutex_unlock(&clocks_mutex); + + return clk; +} + +#ifdef CLK_DEBUG +static void dump_clocks(void) +{ + struct clk *p; + + mutex_lock(&clocks_mutex); + printk(KERN_INFO "CLOCKS:\n"); + list_for_each_entry(p, &clocks, node) { + pr_info(" %s=%ld", p->name, p->rate); + if (p->parent) + pr_cont(" %s=%ld", p->parent->name, + p->parent->rate); + if (p->flags & CLK_HAS_CTRL) + pr_cont(" reg/bit=%d/%d", p->reg, p->bit); + pr_cont("\n"); + } + mutex_unlock(&clocks_mutex); +} +#define DEBUG_CLK_DUMP() dump_clocks() +#else +#define DEBUG_CLK_DUMP() +#endif + + +static void mpc5121_clk_put(struct clk *clk) +{ + module_put(clk->owner); +} + +#define NRPSC 12 + +struct mpc512x_clockctl { + u32 spmr; /* System PLL Mode Reg */ + u32 sccr[2]; /* System Clk Ctrl Reg 1 & 2 */ + u32 scfr1; /* System Clk Freq Reg 1 */ + u32 scfr2; /* System Clk Freq Reg 2 */ + u32 reserved; + u32 bcr; /* Bread Crumb Reg */ + u32 pccr[NRPSC]; /* PSC Clk Ctrl Reg 0-11 */ + u32 spccr; /* SPDIF Clk Ctrl Reg */ + u32 cccr; /* CFM Clk Ctrl Reg */ + u32 dccr; /* DIU Clk Cnfg Reg */ +}; + +struct mpc512x_clockctl __iomem *clockctl; + +static int mpc5121_clk_enable(struct clk *clk) +{ + unsigned int mask; + + if (clk->flags & CLK_HAS_CTRL) { + mask = in_be32(&clockctl->sccr[clk->reg]); + mask |= 1 << clk->bit; + out_be32(&clockctl->sccr[clk->reg], mask); + } + return 0; +} + +static void mpc5121_clk_disable(struct clk *clk) +{ + unsigned int mask; + + if (clk->flags & CLK_HAS_CTRL) { + mask = in_be32(&clockctl->sccr[clk->reg]); + mask &= ~(1 << clk->bit); + out_be32(&clockctl->sccr[clk->reg], mask); + } +} + +static unsigned long mpc5121_clk_get_rate(struct clk *clk) +{ + if (clk->flags & CLK_HAS_RATE) + return clk->rate; + else + return 0; +} + +static long mpc5121_clk_round_rate(struct clk *clk, unsigned long rate) +{ + return rate; +} + +static int mpc5121_clk_set_rate(struct clk *clk, unsigned long rate) +{ + return 0; +} + +static int clk_register(struct clk *clk) +{ + mutex_lock(&clocks_mutex); + list_add(&clk->node, &clocks); + mutex_unlock(&clocks_mutex); + return 0; +} + +static unsigned long spmf_mult(void) +{ + /* + * Convert spmf to multiplier + */ + static int spmf_to_mult[] = { + 68, 1, 12, 16, + 20, 24, 28, 32, + 36, 40, 44, 48, + 52, 56, 60, 64 + }; + int spmf = (clockctl->spmr >> 24) & 0xf; + return spmf_to_mult[spmf]; +} + +static unsigned long sysdiv_div_x_2(void) +{ + /* + * Convert sysdiv to divisor x 2 + * Some divisors have fractional parts so + * multiply by 2 then divide by this value + */ + static int sysdiv_to_div_x_2[] = { + 4, 5, 6, 7, + 8, 9, 10, 14, + 12, 16, 18, 22, + 20, 24, 26, 30, + 28, 32, 34, 38, + 36, 40, 42, 46, + 44, 48, 50, 54, + 52, 56, 58, 62, + 60, 64, 66, + }; + int sysdiv = (clockctl->scfr2 >> 26) & 0x3f; + return sysdiv_to_div_x_2[sysdiv]; +} + +static unsigned long ref_to_sys(unsigned long rate) +{ + rate *= spmf_mult(); + rate *= 2; + rate /= sysdiv_div_x_2(); + + return rate; +} + +static unsigned long sys_to_ref(unsigned long rate) +{ + rate *= sysdiv_div_x_2(); + rate /= 2; + rate /= spmf_mult(); + + return rate; +} + +static long ips_to_ref(unsigned long rate) +{ + int ips_div = (clockctl->scfr1 >> 23) & 0x7; + + rate *= ips_div; /* csb_clk = ips_clk * ips_div */ + rate *= 2; /* sys_clk = csb_clk * 2 */ + return sys_to_ref(rate); +} + +static unsigned long devtree_getfreq(char *clockname) +{ + struct device_node *np; + const unsigned int *prop; + unsigned int val = 0; + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-immr"); + if (np) { + prop = of_get_property(np, clockname, NULL); + if (prop) + val = *prop; + of_node_put(np); + } + return val; +} + +static void ref_clk_calc(struct clk *clk) +{ + unsigned long rate; + + rate = devtree_getfreq("bus-frequency"); + if (rate == 0) { + printk(KERN_ERR "No bus-frequency in dev tree\n"); + clk->rate = 0; + return; + } + clk->rate = ips_to_ref(rate); +} + +static struct clk ref_clk = { + .name = "ref_clk", + .calc = ref_clk_calc, +}; + + +static void sys_clk_calc(struct clk *clk) +{ + clk->rate = ref_to_sys(ref_clk.rate); +} + +static struct clk sys_clk = { + .name = "sys_clk", + .calc = sys_clk_calc, +}; + +static void diu_clk_calc(struct clk *clk) +{ + int diudiv_x_2 = clockctl->scfr1 & 0xff; + unsigned long rate; + + rate = sys_clk.rate; + + rate *= 2; + rate /= diudiv_x_2; + + clk->rate = rate; +} + +static void viu_clk_calc(struct clk *clk) +{ + unsigned long rate; + + rate = sys_clk.rate; + rate /= 2; + clk->rate = rate; +} + +static void half_clk_calc(struct clk *clk) +{ + clk->rate = clk->parent->rate / 2; +} + +static void generic_div_clk_calc(struct clk *clk) +{ + int div = (clockctl->scfr1 >> clk->div_shift) & 0x7; + + clk->rate = clk->parent->rate / div; +} + +static void unity_clk_calc(struct clk *clk) +{ + clk->rate = clk->parent->rate; +} + +static struct clk csb_clk = { + .name = "csb_clk", + .calc = half_clk_calc, + .parent = &sys_clk, +}; + +static void e300_clk_calc(struct clk *clk) +{ + int spmf = (clockctl->spmr >> 16) & 0xf; + int ratex2 = clk->parent->rate * spmf; + + clk->rate = ratex2 / 2; +} + +static struct clk e300_clk = { + .name = "e300_clk", + .calc = e300_clk_calc, + .parent = &csb_clk, +}; + +static struct clk ips_clk = { + .name = "ips_clk", + .calc = generic_div_clk_calc, + .parent = &csb_clk, + .div_shift = 23, +}; + +/* + * Clocks controlled by SCCR1 (.reg = 0) + */ +static struct clk lpc_clk = { + .name = "lpc_clk", + .flags = CLK_HAS_CTRL, + .reg = 0, + .bit = 30, + .calc = generic_div_clk_calc, + .parent = &ips_clk, + .div_shift = 11, +}; + +static struct clk nfc_clk = { + .name = "nfc_clk", + .flags = CLK_HAS_CTRL, + .reg = 0, + .bit = 29, + .calc = generic_div_clk_calc, + .parent = &ips_clk, + .div_shift = 8, +}; + +static struct clk pata_clk = { + .name = "pata_clk", + .flags = CLK_HAS_CTRL, + .reg = 0, + .bit = 28, + .calc = unity_clk_calc, + .parent = &ips_clk, +}; + +/* + * PSC clocks (bits 27 - 16) + * are setup elsewhere + */ + +static struct clk sata_clk = { + .name = "sata_clk", + .flags = CLK_HAS_CTRL, + .reg = 0, + .bit = 14, + .calc = unity_clk_calc, + .parent = &ips_clk, +}; + +static struct clk fec_clk = { + .name = "fec_clk", + .flags = CLK_HAS_CTRL, + .reg = 0, + .bit = 13, + .calc = unity_clk_calc, + .parent = &ips_clk, +}; + +static struct clk pci_clk = { + .name = "pci_clk", + .flags = CLK_HAS_CTRL, + .reg = 0, + .bit = 11, + .calc = generic_div_clk_calc, + .parent = &csb_clk, + .div_shift = 20, +}; + +/* + * Clocks controlled by SCCR2 (.reg = 1) + */ +static struct clk diu_clk = { + .name = "diu_clk", + .flags = CLK_HAS_CTRL, + .reg = 1, + .bit = 31, + .calc = diu_clk_calc, +}; + +static struct clk viu_clk = { + .name = "viu_clk", + .flags = CLK_HAS_CTRL, + .reg = 1, + .bit = 18, + .calc = viu_clk_calc, +}; + +static struct clk axe_clk = { + .name = "axe_clk", + .flags = CLK_HAS_CTRL, + .reg = 1, + .bit = 30, + .calc = unity_clk_calc, + .parent = &csb_clk, +}; + +static struct clk usb1_clk = { + .name = "usb1_clk", + .flags = CLK_HAS_CTRL, + .reg = 1, + .bit = 28, + .calc = unity_clk_calc, + .parent = &csb_clk, +}; + +static struct clk usb2_clk = { + .name = "usb2_clk", + .flags = CLK_HAS_CTRL, + .reg = 1, + .bit = 27, + .calc = unity_clk_calc, + .parent = &csb_clk, +}; + +static struct clk i2c_clk = { + .name = "i2c_clk", + .flags = CLK_HAS_CTRL, + .reg = 1, + .bit = 26, + .calc = unity_clk_calc, + .parent = &ips_clk, +}; + +static struct clk mscan_clk = { + .name = "mscan_clk", + .flags = CLK_HAS_CTRL, + .reg = 1, + .bit = 25, + .calc = unity_clk_calc, + .parent = &ips_clk, +}; + +static struct clk sdhc_clk = { + .name = "sdhc_clk", + .flags = CLK_HAS_CTRL, + .reg = 1, + .bit = 24, + .calc = unity_clk_calc, + .parent = &ips_clk, +}; + +static struct clk mbx_bus_clk = { + .name = "mbx_bus_clk", + .flags = CLK_HAS_CTRL, + .reg = 1, + .bit = 22, + .calc = half_clk_calc, + .parent = &csb_clk, +}; + +static struct clk mbx_clk = { + .name = "mbx_clk", + .flags = CLK_HAS_CTRL, + .reg = 1, + .bit = 21, + .calc = unity_clk_calc, + .parent = &csb_clk, +}; + +static struct clk mbx_3d_clk = { + .name = "mbx_3d_clk", + .flags = CLK_HAS_CTRL, + .reg = 1, + .bit = 20, + .calc = generic_div_clk_calc, + .parent = &mbx_bus_clk, + .div_shift = 14, +}; + +static void psc_mclk_in_calc(struct clk *clk) +{ + clk->rate = devtree_getfreq("psc_mclk_in"); + if (!clk->rate) + clk->rate = 25000000; +} + +static struct clk psc_mclk_in = { + .name = "psc_mclk_in", + .calc = psc_mclk_in_calc, +}; + +static struct clk spdif_txclk = { + .name = "spdif_txclk", + .flags = CLK_HAS_CTRL, + .reg = 1, + .bit = 23, +}; + +static struct clk spdif_rxclk = { + .name = "spdif_rxclk", + .flags = CLK_HAS_CTRL, + .reg = 1, + .bit = 23, +}; + +static void ac97_clk_calc(struct clk *clk) +{ + /* ac97 bit clock is always 24.567 MHz */ + clk->rate = 24567000; +} + +static struct clk ac97_clk = { + .name = "ac97_clk_in", + .calc = ac97_clk_calc, +}; + +struct clk *rate_clks[] = { + &ref_clk, + &sys_clk, + &diu_clk, + &viu_clk, + &csb_clk, + &e300_clk, + &ips_clk, + &fec_clk, + &sata_clk, + &pata_clk, + &nfc_clk, + &lpc_clk, + &mbx_bus_clk, + &mbx_clk, + &mbx_3d_clk, + &axe_clk, + &usb1_clk, + &usb2_clk, + &i2c_clk, + &mscan_clk, + &sdhc_clk, + &pci_clk, + &psc_mclk_in, + &spdif_txclk, + &spdif_rxclk, + &ac97_clk, + NULL +}; + +static void rate_clk_init(struct clk *clk) +{ + if (clk->calc) { + clk->calc(clk); + clk->flags |= CLK_HAS_RATE; + clk_register(clk); + } else { + printk(KERN_WARNING + "Could not initialize clk %s without a calc routine\n", + clk->name); + } +} + +static void rate_clks_init(void) +{ + struct clk **cpp, *clk; + + cpp = rate_clks; + while ((clk = *cpp++)) + rate_clk_init(clk); +} + +/* + * There are two clk enable registers with 32 enable bits each + * psc clocks and device clocks are all stored in dev_clks + */ +struct clk dev_clks[2][32]; + +/* + * Given a psc number return the dev_clk + * associated with it + */ +static struct clk *psc_dev_clk(int pscnum) +{ + int reg, bit; + struct clk *clk; + + reg = 0; + bit = 27 - pscnum; + + clk = &dev_clks[reg][bit]; + clk->reg = 0; + clk->bit = bit; + return clk; +} + +/* + * PSC clock rate calculation + */ +static void psc_calc_rate(struct clk *clk, int pscnum, struct device_node *np) +{ + unsigned long mclk_src = sys_clk.rate; + unsigned long mclk_div; + + /* + * Can only change value of mclk divider + * when the divider is disabled. + * + * Zero is not a valid divider so minimum + * divider is 1 + * + * disable/set divider/enable + */ + out_be32(&clockctl->pccr[pscnum], 0); + out_be32(&clockctl->pccr[pscnum], 0x00020000); + out_be32(&clockctl->pccr[pscnum], 0x00030000); + + if (clockctl->pccr[pscnum] & 0x80) { + clk->rate = spdif_rxclk.rate; + return; + } + + switch ((clockctl->pccr[pscnum] >> 14) & 0x3) { + case 0: + mclk_src = sys_clk.rate; + break; + case 1: + mclk_src = ref_clk.rate; + break; + case 2: + mclk_src = psc_mclk_in.rate; + break; + case 3: + mclk_src = spdif_txclk.rate; + break; + } + + mclk_div = ((clockctl->pccr[pscnum] >> 17) & 0x7fff) + 1; + clk->rate = mclk_src / mclk_div; +} + +/* + * Find all psc nodes in device tree and assign a clock + * with name "psc%d_mclk" and dev pointing at the device + * returned from of_find_device_by_node + */ +static void psc_clks_init(void) +{ + struct device_node *np; + const u32 *cell_index; + struct platform_device *ofdev; + + for_each_compatible_node(np, NULL, "fsl,mpc5121-psc") { + cell_index = of_get_property(np, "cell-index", NULL); + if (cell_index) { + int pscnum = *cell_index; + struct clk *clk = psc_dev_clk(pscnum); + + clk->flags = CLK_HAS_RATE | CLK_HAS_CTRL; + ofdev = of_find_device_by_node(np); + clk->dev = &ofdev->dev; + /* + * AC97 is special rate clock does + * not go through normal path + */ + if (strcmp("ac97", np->name) == 0) + clk->rate = ac97_clk.rate; + else + psc_calc_rate(clk, pscnum, np); + sprintf(clk->name, "psc%d_mclk", pscnum); + clk_register(clk); + clk_enable(clk); + } + } +} + +static struct clk_interface mpc5121_clk_functions = { + .clk_get = mpc5121_clk_get, + .clk_enable = mpc5121_clk_enable, + .clk_disable = mpc5121_clk_disable, + .clk_get_rate = mpc5121_clk_get_rate, + .clk_put = mpc5121_clk_put, + .clk_round_rate = mpc5121_clk_round_rate, + .clk_set_rate = mpc5121_clk_set_rate, + .clk_set_parent = NULL, + .clk_get_parent = NULL, +}; + +int __init mpc5121_clk_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-clock"); + if (np) { + clockctl = of_iomap(np, 0); + of_node_put(np); + } + + if (!clockctl) { + printk(KERN_ERR "Could not map clock control registers\n"); + return 0; + } + + rate_clks_init(); + psc_clks_init(); + + /* leave clockctl mapped forever */ + /*iounmap(clockctl); */ + DEBUG_CLK_DUMP(); + clocks_initialized++; + clk_functions = mpc5121_clk_functions; + return 0; +} diff --git a/arch/powerpc/platforms/512x/mpc5121_ads.c b/arch/powerpc/platforms/512x/mpc5121_ads.c new file mode 100644 index 00000000000..dcef6ade48e --- /dev/null +++ b/arch/powerpc/platforms/512x/mpc5121_ads.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2007, 2008 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: John Rigby, <jrigby@freescale.com>, Thur Mar 29 2007 + * + * Description: + * MPC5121 ADS board setup + * + * This 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 <linux/kernel.h> +#include <linux/io.h> +#include <linux/of_platform.h> + +#include <asm/machdep.h> +#include <asm/ipic.h> +#include <asm/prom.h> +#include <asm/time.h> + +#include <sysdev/fsl_pci.h> + +#include "mpc512x.h" +#include "mpc5121_ads.h" + +static void __init mpc5121_ads_setup_arch(void) +{ +#ifdef CONFIG_PCI + struct device_node *np; +#endif + printk(KERN_INFO "MPC5121 ADS board from Freescale Semiconductor\n"); + /* + * cpld regs are needed early + */ + mpc5121_ads_cpld_map(); + +#ifdef CONFIG_PCI + for_each_compatible_node(np, "pci", "fsl,mpc5121-pci") + mpc83xx_add_bridge(np); +#endif + mpc512x_setup_diu(); +} + +static void __init mpc5121_ads_init_IRQ(void) +{ + mpc512x_init_IRQ(); + mpc5121_ads_cpld_pic_init(); +} + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc5121_ads_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,mpc5121ads"); +} + +define_machine(mpc5121_ads) { + .name = "MPC5121 ADS", + .probe = mpc5121_ads_probe, + .setup_arch = mpc5121_ads_setup_arch, + .init = mpc512x_init, + .init_early = mpc512x_init_diu, + .init_IRQ = mpc5121_ads_init_IRQ, + .get_irq = ipic_get_irq, + .calibrate_decr = generic_calibrate_decr, + .restart = mpc512x_restart, +}; diff --git a/arch/powerpc/platforms/512x/mpc5121_ads.h b/arch/powerpc/platforms/512x/mpc5121_ads.h new file mode 100644 index 00000000000..662076cfee2 --- /dev/null +++ b/arch/powerpc/platforms/512x/mpc5121_ads.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2008 Freescale Semiconductor, Inc. All rights reserved. + * + * 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. + * + * Prototypes for ADS5121 specific code + */ + +#ifndef __MPC512ADS_H__ +#define __MPC512ADS_H__ +extern void __init mpc5121_ads_cpld_map(void); +extern void __init mpc5121_ads_cpld_pic_init(void); +#endif /* __MPC512ADS_H__ */ diff --git a/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c b/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c new file mode 100644 index 00000000000..9f09319352c --- /dev/null +++ b/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2008 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: John Rigby, <jrigby@freescale.com> + * + * Description: + * MPC5121ADS CPLD irq handling + * + * This 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. + */ + +#undef DEBUG + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <asm/prom.h> + +static struct device_node *cpld_pic_node; +static struct irq_host *cpld_pic_host; + +/* + * Bits to ignore in the misc_status register + * 0x10 touch screen pendown is hard routed to irq1 + * 0x02 pci status is read from pci status register + */ +#define MISC_IGNORE 0x12 + +/* + * Nothing to ignore in pci status register + */ +#define PCI_IGNORE 0x00 + +struct cpld_pic { + u8 pci_mask; + u8 pci_status; + u8 route; + u8 misc_mask; + u8 misc_status; + u8 misc_control; +}; + +static struct cpld_pic __iomem *cpld_regs; + +static void __iomem * +irq_to_pic_mask(unsigned int irq) +{ + return irq <= 7 ? &cpld_regs->pci_mask : &cpld_regs->misc_mask; +} + +static unsigned int +irq_to_pic_bit(unsigned int irq) +{ + return 1 << (irq & 0x7); +} + +static void +cpld_mask_irq(struct irq_data *d) +{ + unsigned int cpld_irq = (unsigned int)irqd_to_hwirq(d); + void __iomem *pic_mask = irq_to_pic_mask(cpld_irq); + + out_8(pic_mask, + in_8(pic_mask) | irq_to_pic_bit(cpld_irq)); +} + +static void +cpld_unmask_irq(struct irq_data *d) +{ + unsigned int cpld_irq = (unsigned int)irqd_to_hwirq(d); + void __iomem *pic_mask = irq_to_pic_mask(cpld_irq); + + out_8(pic_mask, + in_8(pic_mask) & ~irq_to_pic_bit(cpld_irq)); +} + +static struct irq_chip cpld_pic = { + .name = "CPLD PIC", + .irq_mask = cpld_mask_irq, + .irq_ack = cpld_mask_irq, + .irq_unmask = cpld_unmask_irq, +}; + +static int +cpld_pic_get_irq(int offset, u8 ignore, u8 __iomem *statusp, + u8 __iomem *maskp) +{ + int cpld_irq; + u8 status = in_8(statusp); + u8 mask = in_8(maskp); + + /* ignore don't cares and masked irqs */ + status |= (ignore | mask); + + if (status == 0xff) + return NO_IRQ; + + cpld_irq = ffz(status) + offset; + + return irq_linear_revmap(cpld_pic_host, cpld_irq); +} + +static void +cpld_pic_cascade(unsigned int irq, struct irq_desc *desc) +{ + irq = cpld_pic_get_irq(0, PCI_IGNORE, &cpld_regs->pci_status, + &cpld_regs->pci_mask); + if (irq != NO_IRQ) { + generic_handle_irq(irq); + return; + } + + irq = cpld_pic_get_irq(8, MISC_IGNORE, &cpld_regs->misc_status, + &cpld_regs->misc_mask); + if (irq != NO_IRQ) { + generic_handle_irq(irq); + return; + } +} + +static int +cpld_pic_host_match(struct irq_host *h, struct device_node *node) +{ + return cpld_pic_node == node; +} + +static int +cpld_pic_host_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_status_flags(virq, IRQ_LEVEL); + irq_set_chip_and_handler(virq, &cpld_pic, handle_level_irq); + return 0; +} + +static struct +irq_host_ops cpld_pic_host_ops = { + .match = cpld_pic_host_match, + .map = cpld_pic_host_map, +}; + +void __init +mpc5121_ads_cpld_map(void) +{ + struct device_node *np = NULL; + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld-pic"); + if (!np) { + printk(KERN_ERR "CPLD PIC init: can not find cpld-pic node\n"); + return; + } + + cpld_regs = of_iomap(np, 0); + of_node_put(np); +} + +void __init +mpc5121_ads_cpld_pic_init(void) +{ + unsigned int cascade_irq; + struct device_node *np = NULL; + + pr_debug("cpld_ic_init\n"); + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld-pic"); + if (!np) { + printk(KERN_ERR "CPLD PIC init: can not find cpld-pic node\n"); + return; + } + + if (!cpld_regs) + goto end; + + cascade_irq = irq_of_parse_and_map(np, 0); + if (cascade_irq == NO_IRQ) + goto end; + + /* + * statically route touch screen pendown through 1 + * and ignore it here + * route all others through our cascade irq + */ + out_8(&cpld_regs->route, 0xfd); + out_8(&cpld_regs->pci_mask, 0xff); + /* unmask pci ints in misc mask */ + out_8(&cpld_regs->misc_mask, ~(MISC_IGNORE)); + + cpld_pic_node = of_node_get(np); + + cpld_pic_host = + irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, 16, &cpld_pic_host_ops, 16); + if (!cpld_pic_host) { + printk(KERN_ERR "CPLD PIC: failed to allocate irq host!\n"); + goto end; + } + + irq_set_chained_handler(cascade_irq, cpld_pic_cascade); +end: + of_node_put(np); +} diff --git a/arch/powerpc/platforms/512x/mpc5121_generic.c b/arch/powerpc/platforms/512x/mpc5121_generic.c new file mode 100644 index 00000000000..926731f1ff0 --- /dev/null +++ b/arch/powerpc/platforms/512x/mpc5121_generic.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: John Rigby, <jrigby@freescale.com> + * + * Description: + * MPC5121 SoC setup + * + * This 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 <linux/kernel.h> +#include <linux/of_platform.h> + +#include <asm/machdep.h> +#include <asm/ipic.h> +#include <asm/prom.h> +#include <asm/time.h> + +#include "mpc512x.h" + +/* + * list of supported boards + */ +static const char *board[] __initdata = { + "prt,prtlvt", + NULL +}; + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc5121_generic_probe(void) +{ + return of_flat_dt_match(of_get_flat_dt_root(), board); +} + +define_machine(mpc5121_generic) { + .name = "MPC5121 generic", + .probe = mpc5121_generic_probe, + .init = mpc512x_init, + .init_early = mpc512x_init_diu, + .setup_arch = mpc512x_setup_diu, + .init_IRQ = mpc512x_init_IRQ, + .get_irq = ipic_get_irq, + .calibrate_decr = generic_calibrate_decr, + .restart = mpc512x_restart, +}; diff --git a/arch/powerpc/platforms/512x/mpc512x.h b/arch/powerpc/platforms/512x/mpc512x.h new file mode 100644 index 00000000000..1ab6d11d0b1 --- /dev/null +++ b/arch/powerpc/platforms/512x/mpc512x.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. + * + * 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. + * + * Prototypes for MPC512x shared code + */ + +#ifndef __MPC512X_H__ +#define __MPC512X_H__ +extern void __init mpc512x_init_IRQ(void); +extern void __init mpc512x_init(void); +extern int __init mpc5121_clk_init(void); +void __init mpc512x_declare_of_platform_devices(void); +extern void mpc512x_restart(char *cmd); +extern void mpc512x_init_diu(void); +extern void mpc512x_setup_diu(void); +#endif /* __MPC512X_H__ */ diff --git a/arch/powerpc/platforms/512x/mpc512x_shared.c b/arch/powerpc/platforms/512x/mpc512x_shared.c new file mode 100644 index 00000000000..cfe958e94e1 --- /dev/null +++ b/arch/powerpc/platforms/512x/mpc512x_shared.c @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: John Rigby <jrigby@freescale.com> + * + * Description: + * MPC512x Shared code + * + * This 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 <linux/kernel.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/of_platform.h> +#include <linux/fsl-diu-fb.h> +#include <linux/bootmem.h> +#include <sysdev/fsl_soc.h> + +#include <asm/cacheflush.h> +#include <asm/machdep.h> +#include <asm/ipic.h> +#include <asm/prom.h> +#include <asm/time.h> +#include <asm/mpc5121.h> +#include <asm/mpc52xx_psc.h> + +#include "mpc512x.h" + +static struct mpc512x_reset_module __iomem *reset_module_base; + +static void __init mpc512x_restart_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset"); + if (!np) + return; + + reset_module_base = of_iomap(np, 0); + of_node_put(np); +} + +void mpc512x_restart(char *cmd) +{ + if (reset_module_base) { + /* Enable software reset "RSTE" */ + out_be32(&reset_module_base->rpr, 0x52535445); + /* Set software hard reset */ + out_be32(&reset_module_base->rcr, 0x2); + } else { + pr_err("Restart module not mapped.\n"); + } + for (;;) + ; +} + +struct fsl_diu_shared_fb { + u8 gamma[0x300]; /* 32-bit aligned! */ + struct diu_ad ad0; /* 32-bit aligned! */ + phys_addr_t fb_phys; + size_t fb_len; + bool in_use; +}; + +u32 mpc512x_get_pixel_format(enum fsl_diu_monitor_port port, + unsigned int bits_per_pixel) +{ + switch (bits_per_pixel) { + case 32: + return 0x88883316; + case 24: + return 0x88082219; + case 16: + return 0x65053118; + } + return 0x00000400; +} + +void mpc512x_set_gamma_table(enum fsl_diu_monitor_port port, + char *gamma_table_base) +{ +} + +void mpc512x_set_monitor_port(enum fsl_diu_monitor_port port) +{ +} + +#define DIU_DIV_MASK 0x000000ff +void mpc512x_set_pixel_clock(unsigned int pixclock) +{ + unsigned long bestval, bestfreq, speed, busfreq; + unsigned long minpixclock, maxpixclock, pixval; + struct mpc512x_ccm __iomem *ccm; + struct device_node *np; + u32 temp; + long err; + int i; + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-clock"); + if (!np) { + pr_err("Can't find clock control module.\n"); + return; + } + + ccm = of_iomap(np, 0); + of_node_put(np); + if (!ccm) { + pr_err("Can't map clock control module reg.\n"); + return; + } + + np = of_find_node_by_type(NULL, "cpu"); + if (np) { + const unsigned int *prop = + of_get_property(np, "bus-frequency", NULL); + + of_node_put(np); + if (prop) { + busfreq = *prop; + } else { + pr_err("Can't get bus-frequency property\n"); + return; + } + } else { + pr_err("Can't find 'cpu' node.\n"); + return; + } + + /* Pixel Clock configuration */ + pr_debug("DIU: Bus Frequency = %lu\n", busfreq); + speed = busfreq * 4; /* DIU_DIV ratio is 4 * CSB_CLK / DIU_CLK */ + + /* Calculate the pixel clock with the smallest error */ + /* calculate the following in steps to avoid overflow */ + pr_debug("DIU pixclock in ps - %d\n", pixclock); + temp = (1000000000 / pixclock) * 1000; + pixclock = temp; + pr_debug("DIU pixclock freq - %u\n", pixclock); + + temp = temp / 20; /* pixclock * 0.05 */ + pr_debug("deviation = %d\n", temp); + minpixclock = pixclock - temp; + maxpixclock = pixclock + temp; + pr_debug("DIU minpixclock - %lu\n", minpixclock); + pr_debug("DIU maxpixclock - %lu\n", maxpixclock); + pixval = speed/pixclock; + pr_debug("DIU pixval = %lu\n", pixval); + + err = LONG_MAX; + bestval = pixval; + pr_debug("DIU bestval = %lu\n", bestval); + + bestfreq = 0; + for (i = -1; i <= 1; i++) { + temp = speed / (pixval+i); + pr_debug("DIU test pixval i=%d, pixval=%lu, temp freq. = %u\n", + i, pixval, temp); + if ((temp < minpixclock) || (temp > maxpixclock)) + pr_debug("DIU exceeds monitor range (%lu to %lu)\n", + minpixclock, maxpixclock); + else if (abs(temp - pixclock) < err) { + pr_debug("Entered the else if block %d\n", i); + err = abs(temp - pixclock); + bestval = pixval + i; + bestfreq = temp; + } + } + + pr_debug("DIU chose = %lx\n", bestval); + pr_debug("DIU error = %ld\n NomPixClk ", err); + pr_debug("DIU: Best Freq = %lx\n", bestfreq); + /* Modify DIU_DIV in CCM SCFR1 */ + temp = in_be32(&ccm->scfr1); + pr_debug("DIU: Current value of SCFR1: 0x%08x\n", temp); + temp &= ~DIU_DIV_MASK; + temp |= (bestval & DIU_DIV_MASK); + out_be32(&ccm->scfr1, temp); + pr_debug("DIU: Modified value of SCFR1: 0x%08x\n", temp); + iounmap(ccm); +} + +enum fsl_diu_monitor_port +mpc512x_valid_monitor_port(enum fsl_diu_monitor_port port) +{ + return FSL_DIU_PORT_DVI; +} + +static struct fsl_diu_shared_fb __attribute__ ((__aligned__(8))) diu_shared_fb; + +#if defined(CONFIG_FB_FSL_DIU) || \ + defined(CONFIG_FB_FSL_DIU_MODULE) +static inline void mpc512x_free_bootmem(struct page *page) +{ + __ClearPageReserved(page); + BUG_ON(PageTail(page)); + BUG_ON(atomic_read(&page->_count) > 1); + atomic_set(&page->_count, 1); + __free_page(page); + totalram_pages++; +} + +void mpc512x_release_bootmem(void) +{ + unsigned long addr = diu_shared_fb.fb_phys & PAGE_MASK; + unsigned long size = diu_shared_fb.fb_len; + unsigned long start, end; + + if (diu_shared_fb.in_use) { + start = PFN_UP(addr); + end = PFN_DOWN(addr + size); + + for (; start < end; start++) + mpc512x_free_bootmem(pfn_to_page(start)); + + diu_shared_fb.in_use = false; + } + diu_ops.release_bootmem = NULL; +} +#endif + +/* + * Check if DIU was pre-initialized. If so, perform steps + * needed to continue displaying through the whole boot process. + * Move area descriptor and gamma table elsewhere, they are + * destroyed by bootmem allocator otherwise. The frame buffer + * address range will be reserved in setup_arch() after bootmem + * allocator is up. + */ +void __init mpc512x_init_diu(void) +{ + struct device_node *np; + struct diu __iomem *diu_reg; + phys_addr_t desc; + void __iomem *vaddr; + unsigned long mode, pix_fmt, res, bpp; + unsigned long dst; + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu"); + if (!np) { + pr_err("No DIU node\n"); + return; + } + + diu_reg = of_iomap(np, 0); + of_node_put(np); + if (!diu_reg) { + pr_err("Can't map DIU\n"); + return; + } + + mode = in_be32(&diu_reg->diu_mode); + if (mode == MFB_MODE0) { + pr_info("%s: DIU OFF\n", __func__); + goto out; + } + + desc = in_be32(&diu_reg->desc[0]); + vaddr = ioremap(desc, sizeof(struct diu_ad)); + if (!vaddr) { + pr_err("Can't map DIU area desc.\n"); + goto out; + } + memcpy(&diu_shared_fb.ad0, vaddr, sizeof(struct diu_ad)); + /* flush fb area descriptor */ + dst = (unsigned long)&diu_shared_fb.ad0; + flush_dcache_range(dst, dst + sizeof(struct diu_ad) - 1); + + res = in_be32(&diu_reg->disp_size); + pix_fmt = in_le32(vaddr); + bpp = ((pix_fmt >> 16) & 0x3) + 1; + diu_shared_fb.fb_phys = in_le32(vaddr + 4); + diu_shared_fb.fb_len = ((res & 0xfff0000) >> 16) * (res & 0xfff) * bpp; + diu_shared_fb.in_use = true; + iounmap(vaddr); + + desc = in_be32(&diu_reg->gamma); + vaddr = ioremap(desc, sizeof(diu_shared_fb.gamma)); + if (!vaddr) { + pr_err("Can't map DIU area desc.\n"); + diu_shared_fb.in_use = false; + goto out; + } + memcpy(&diu_shared_fb.gamma, vaddr, sizeof(diu_shared_fb.gamma)); + /* flush gamma table */ + dst = (unsigned long)&diu_shared_fb.gamma; + flush_dcache_range(dst, dst + sizeof(diu_shared_fb.gamma) - 1); + + iounmap(vaddr); + out_be32(&diu_reg->gamma, virt_to_phys(&diu_shared_fb.gamma)); + out_be32(&diu_reg->desc[1], 0); + out_be32(&diu_reg->desc[2], 0); + out_be32(&diu_reg->desc[0], virt_to_phys(&diu_shared_fb.ad0)); + +out: + iounmap(diu_reg); +} + +void __init mpc512x_setup_diu(void) +{ + int ret; + + /* + * We do not allocate and configure new area for bitmap buffer + * because it would requere copying bitmap data (splash image) + * and so negatively affect boot time. Instead we reserve the + * already configured frame buffer area so that it won't be + * destroyed. The starting address of the area to reserve and + * also it's length is passed to reserve_bootmem(). It will be + * freed later on first open of fbdev, when splash image is not + * needed any more. + */ + if (diu_shared_fb.in_use) { + ret = reserve_bootmem(diu_shared_fb.fb_phys, + diu_shared_fb.fb_len, + BOOTMEM_EXCLUSIVE); + if (ret) { + pr_err("%s: reserve bootmem failed\n", __func__); + diu_shared_fb.in_use = false; + } + } + +#if defined(CONFIG_FB_FSL_DIU) || \ + defined(CONFIG_FB_FSL_DIU_MODULE) + diu_ops.get_pixel_format = mpc512x_get_pixel_format; + diu_ops.set_gamma_table = mpc512x_set_gamma_table; + diu_ops.set_monitor_port = mpc512x_set_monitor_port; + diu_ops.set_pixel_clock = mpc512x_set_pixel_clock; + diu_ops.valid_monitor_port = mpc512x_valid_monitor_port; + diu_ops.release_bootmem = mpc512x_release_bootmem; +#endif +} + +void __init mpc512x_init_IRQ(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-ipic"); + if (!np) + return; + + ipic_init(np, 0); + of_node_put(np); + + /* + * Initialize the default interrupt mapping priorities, + * in case the boot rom changed something on us. + */ + ipic_set_default_priority(); +} + +/* + * Nodes to do bus probe on, soc and localbus + */ +static struct of_device_id __initdata of_bus_ids[] = { + { .compatible = "fsl,mpc5121-immr", }, + { .compatible = "fsl,mpc5121-localbus", }, + {}, +}; + +void __init mpc512x_declare_of_platform_devices(void) +{ + struct device_node *np; + + if (of_platform_bus_probe(NULL, of_bus_ids, NULL)) + printk(KERN_ERR __FILE__ ": " + "Error while probing of_platform bus\n"); + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-nfc"); + if (np) { + of_platform_device_create(np, NULL, NULL); + of_node_put(np); + } +} + +#define DEFAULT_FIFO_SIZE 16 + +static unsigned int __init get_fifo_size(struct device_node *np, + char *prop_name) +{ + const unsigned int *fp; + + fp = of_get_property(np, prop_name, NULL); + if (fp) + return *fp; + + pr_warning("no %s property in %s node, defaulting to %d\n", + prop_name, np->full_name, DEFAULT_FIFO_SIZE); + + return DEFAULT_FIFO_SIZE; +} + +#define FIFOC(_base) ((struct mpc512x_psc_fifo __iomem *) \ + ((u32)(_base) + sizeof(struct mpc52xx_psc))) + +/* Init PSC FIFO space for TX and RX slices */ +void __init mpc512x_psc_fifo_init(void) +{ + struct device_node *np; + void __iomem *psc; + unsigned int tx_fifo_size; + unsigned int rx_fifo_size; + int fifobase = 0; /* current fifo address in 32 bit words */ + + for_each_compatible_node(np, NULL, "fsl,mpc5121-psc") { + tx_fifo_size = get_fifo_size(np, "fsl,tx-fifo-size"); + rx_fifo_size = get_fifo_size(np, "fsl,rx-fifo-size"); + + /* size in register is in 4 byte units */ + tx_fifo_size /= 4; + rx_fifo_size /= 4; + if (!tx_fifo_size) + tx_fifo_size = 1; + if (!rx_fifo_size) + rx_fifo_size = 1; + + psc = of_iomap(np, 0); + if (!psc) { + pr_err("%s: Can't map %s device\n", + __func__, np->full_name); + continue; + } + + /* FIFO space is 4KiB, check if requested size is available */ + if ((fifobase + tx_fifo_size + rx_fifo_size) > 0x1000) { + pr_err("%s: no fifo space available for %s\n", + __func__, np->full_name); + iounmap(psc); + /* + * chances are that another device requests less + * fifo space, so we continue. + */ + continue; + } + + /* set tx and rx fifo size registers */ + out_be32(&FIFOC(psc)->txsz, (fifobase << 16) | tx_fifo_size); + fifobase += tx_fifo_size; + out_be32(&FIFOC(psc)->rxsz, (fifobase << 16) | rx_fifo_size); + fifobase += rx_fifo_size; + + /* reset and enable the slices */ + out_be32(&FIFOC(psc)->txcmd, 0x80); + out_be32(&FIFOC(psc)->txcmd, 0x01); + out_be32(&FIFOC(psc)->rxcmd, 0x80); + out_be32(&FIFOC(psc)->rxcmd, 0x01); + + iounmap(psc); + } +} + +void __init mpc512x_init(void) +{ + mpc512x_declare_of_platform_devices(); + mpc5121_clk_init(); + mpc512x_restart_init(); + mpc512x_psc_fifo_init(); +} diff --git a/arch/powerpc/platforms/512x/pdm360ng.c b/arch/powerpc/platforms/512x/pdm360ng.c new file mode 100644 index 00000000000..0575e858291 --- /dev/null +++ b/arch/powerpc/platforms/512x/pdm360ng.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2010 DENX Software Engineering + * + * Anatolij Gustschin, <agust@denx.de> + * + * PDM360NG board setup + * + * This 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 <linux/kernel.h> +#include <linux/io.h> +#include <linux/of_platform.h> + +#include <asm/machdep.h> +#include <asm/ipic.h> + +#include "mpc512x.h" + +#if defined(CONFIG_TOUCHSCREEN_ADS7846) || \ + defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE) +#include <linux/interrupt.h> +#include <linux/spi/ads7846.h> +#include <linux/spi/spi.h> +#include <linux/notifier.h> + +static void *pdm360ng_gpio_base; + +static int pdm360ng_get_pendown_state(void) +{ + u32 reg; + + reg = in_be32(pdm360ng_gpio_base + 0xc); + if (reg & 0x40) + setbits32(pdm360ng_gpio_base + 0xc, 0x40); + + reg = in_be32(pdm360ng_gpio_base + 0x8); + + /* return 1 if pen is down */ + return (reg & 0x40) == 0; +} + +static struct ads7846_platform_data pdm360ng_ads7846_pdata = { + .model = 7845, + .get_pendown_state = pdm360ng_get_pendown_state, + .irq_flags = IRQF_TRIGGER_LOW, +}; + +static int __init pdm360ng_penirq_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-gpio"); + if (!np) { + pr_err("%s: Can't find 'mpc5121-gpio' node\n", __func__); + return -ENODEV; + } + + pdm360ng_gpio_base = of_iomap(np, 0); + of_node_put(np); + if (!pdm360ng_gpio_base) { + pr_err("%s: Can't map gpio regs.\n", __func__); + return -ENODEV; + } + out_be32(pdm360ng_gpio_base + 0xc, 0xffffffff); + setbits32(pdm360ng_gpio_base + 0x18, 0x2000); + setbits32(pdm360ng_gpio_base + 0x10, 0x40); + + return 0; +} + +static int pdm360ng_touchscreen_notifier_call(struct notifier_block *nb, + unsigned long event, void *__dev) +{ + struct device *dev = __dev; + + if ((event == BUS_NOTIFY_ADD_DEVICE) && + of_device_is_compatible(dev->of_node, "ti,ads7846")) { + dev->platform_data = &pdm360ng_ads7846_pdata; + return NOTIFY_OK; + } + return NOTIFY_DONE; +} + +static struct notifier_block pdm360ng_touchscreen_nb = { + .notifier_call = pdm360ng_touchscreen_notifier_call, +}; + +static void __init pdm360ng_touchscreen_init(void) +{ + if (pdm360ng_penirq_init()) + return; + + bus_register_notifier(&spi_bus_type, &pdm360ng_touchscreen_nb); +} +#else +static inline void __init pdm360ng_touchscreen_init(void) +{ +} +#endif /* CONFIG_TOUCHSCREEN_ADS7846 */ + +void __init pdm360ng_init(void) +{ + mpc512x_init(); + pdm360ng_touchscreen_init(); +} + +static int __init pdm360ng_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "ifm,pdm360ng"); +} + +define_machine(pdm360ng) { + .name = "PDM360NG", + .probe = pdm360ng_probe, + .setup_arch = mpc512x_setup_diu, + .init = pdm360ng_init, + .init_early = mpc512x_init_diu, + .init_IRQ = mpc512x_init_IRQ, + .get_irq = ipic_get_irq, + .calibrate_decr = generic_calibrate_decr, + .restart = mpc512x_restart, +}; diff --git a/arch/powerpc/platforms/52xx/Kconfig b/arch/powerpc/platforms/52xx/Kconfig new file mode 100644 index 00000000000..90f4496017e --- /dev/null +++ b/arch/powerpc/platforms/52xx/Kconfig @@ -0,0 +1,61 @@ +config PPC_MPC52xx + bool "52xx-based boards" + depends on 6xx + select PPC_CLOCK + select PPC_PCI_CHOICE + +config PPC_MPC5200_SIMPLE + bool "Generic support for simple MPC5200 based boards" + depends on PPC_MPC52xx + select DEFAULT_UIMAGE + help + This option enables support for a simple MPC52xx based boards which + do not need a custom platform specific setup. Such boards are + supported assuming the following: + + - GPIO pins are configured by the firmware, + - CDM configuration (clocking) is setup correctly by firmware, + - if the 'fsl,has-wdt' property is present in one of the + gpt nodes, then it is safe to use such gpt to reset the board, + - PCI is supported if enabled in the kernel configuration + and if there is a PCI bus node defined in the device tree. + + Boards that are compatible with this generic platform support + are: + intercontrol,digsy-mtc + phytec,pcm030 + phytec,pcm032 + promess,motionpro + schindler,cm5200 + tqc,tqm5200 + +config PPC_EFIKA + bool "bPlan Efika 5k2. MPC5200B based computer" + depends on PPC_MPC52xx + select PPC_RTAS + select RTAS_PROC + select PPC_NATIVE + +config PPC_LITE5200 + bool "Freescale Lite5200 Eval Board" + depends on PPC_MPC52xx + select DEFAULT_UIMAGE + +config PPC_MEDIA5200 + bool "Freescale Media5200 Eval Board" + depends on PPC_MPC52xx + select DEFAULT_UIMAGE + +config PPC_MPC5200_BUGFIX + bool "MPC5200 (L25R) bugfix support" + depends on PPC_MPC52xx + help + Enable workarounds for original MPC5200 errata. This is not required + for MPC5200B based boards. + + It is safe to say 'Y' here + +config PPC_MPC5200_LPBFIFO + tristate "MPC5200 LocalPlus bus FIFO driver" + depends on PPC_MPC52xx + select PPC_BESTCOMM_GEN_BD diff --git a/arch/powerpc/platforms/52xx/Makefile b/arch/powerpc/platforms/52xx/Makefile new file mode 100644 index 00000000000..4e62486791e --- /dev/null +++ b/arch/powerpc/platforms/52xx/Makefile @@ -0,0 +1,17 @@ +# +# Makefile for 52xx based boards +# +obj-y += mpc52xx_pic.o mpc52xx_common.o mpc52xx_gpt.o +obj-$(CONFIG_PCI) += mpc52xx_pci.o + +obj-$(CONFIG_PPC_MPC5200_SIMPLE) += mpc5200_simple.o +obj-$(CONFIG_PPC_EFIKA) += efika.o +obj-$(CONFIG_PPC_LITE5200) += lite5200.o +obj-$(CONFIG_PPC_MEDIA5200) += media5200.o + +obj-$(CONFIG_PM) += mpc52xx_sleep.o mpc52xx_pm.o +ifeq ($(CONFIG_PPC_LITE5200),y) + obj-$(CONFIG_PM) += lite5200_sleep.o lite5200_pm.o +endif + +obj-$(CONFIG_PPC_MPC5200_LPBFIFO) += mpc52xx_lpbfifo.o diff --git a/arch/powerpc/platforms/52xx/efika.c b/arch/powerpc/platforms/52xx/efika.c new file mode 100644 index 00000000000..18c10482019 --- /dev/null +++ b/arch/powerpc/platforms/52xx/efika.c @@ -0,0 +1,238 @@ +/* + * Efika 5K2 platform code + * Some code really inspired from the lite5200b platform. + * + * Copyright (C) 2006 bplan GmbH + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/init.h> +#include <generated/utsrelease.h> +#include <linux/pci.h> +#include <linux/of.h> +#include <asm/prom.h> +#include <asm/time.h> +#include <asm/machdep.h> +#include <asm/rtas.h> +#include <asm/mpc52xx.h> + +#define EFIKA_PLATFORM_NAME "Efika" + + +/* ------------------------------------------------------------------------ */ +/* PCI accesses thru RTAS */ +/* ------------------------------------------------------------------------ */ + +#ifdef CONFIG_PCI + +/* + * Access functions for PCI config space using RTAS calls. + */ +static int rtas_read_config(struct pci_bus *bus, unsigned int devfn, int offset, + int len, u32 * val) +{ + struct pci_controller *hose = pci_bus_to_host(bus); + unsigned long addr = (offset & 0xff) | ((devfn & 0xff) << 8) + | (((bus->number - hose->first_busno) & 0xff) << 16) + | (hose->global_number << 24); + int ret = -1; + int rval; + + rval = rtas_call(rtas_token("read-pci-config"), 2, 2, &ret, addr, len); + *val = ret; + return rval ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; +} + +static int rtas_write_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 val) +{ + struct pci_controller *hose = pci_bus_to_host(bus); + unsigned long addr = (offset & 0xff) | ((devfn & 0xff) << 8) + | (((bus->number - hose->first_busno) & 0xff) << 16) + | (hose->global_number << 24); + int rval; + + rval = rtas_call(rtas_token("write-pci-config"), 3, 1, NULL, + addr, len, val); + return rval ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops rtas_pci_ops = { + .read = rtas_read_config, + .write = rtas_write_config, +}; + + +static void __init efika_pcisetup(void) +{ + const int *bus_range; + int len; + struct pci_controller *hose; + struct device_node *root; + struct device_node *pcictrl; + + root = of_find_node_by_path("/"); + if (root == NULL) { + printk(KERN_WARNING EFIKA_PLATFORM_NAME + ": Unable to find the root node\n"); + return; + } + + for (pcictrl = NULL;;) { + pcictrl = of_get_next_child(root, pcictrl); + if ((pcictrl == NULL) || (strcmp(pcictrl->name, "pci") == 0)) + break; + } + + of_node_put(root); + + if (pcictrl == NULL) { + printk(KERN_WARNING EFIKA_PLATFORM_NAME + ": Unable to find the PCI bridge node\n"); + return; + } + + bus_range = of_get_property(pcictrl, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING EFIKA_PLATFORM_NAME + ": Can't get bus-range for %s\n", pcictrl->full_name); + goto out_put; + } + + if (bus_range[1] == bus_range[0]) + printk(KERN_INFO EFIKA_PLATFORM_NAME ": PCI bus %d", + bus_range[0]); + else + printk(KERN_INFO EFIKA_PLATFORM_NAME ": PCI buses %d..%d", + bus_range[0], bus_range[1]); + printk(" controlled by %s\n", pcictrl->full_name); + printk("\n"); + + hose = pcibios_alloc_controller(pcictrl); + if (!hose) { + printk(KERN_WARNING EFIKA_PLATFORM_NAME + ": Can't allocate PCI controller structure for %s\n", + pcictrl->full_name); + goto out_put; + } + + hose->first_busno = bus_range[0]; + hose->last_busno = bus_range[1]; + hose->ops = &rtas_pci_ops; + + pci_process_bridge_OF_ranges(hose, pcictrl, 0); + return; +out_put: + of_node_put(pcictrl); +} + +#else +static void __init efika_pcisetup(void) +{} +#endif + + + +/* ------------------------------------------------------------------------ */ +/* Platform setup */ +/* ------------------------------------------------------------------------ */ + +static void efika_show_cpuinfo(struct seq_file *m) +{ + struct device_node *root; + const char *revision; + const char *codegendescription; + const char *codegenvendor; + + root = of_find_node_by_path("/"); + if (!root) + return; + + revision = of_get_property(root, "revision", NULL); + codegendescription = of_get_property(root, "CODEGEN,description", NULL); + codegenvendor = of_get_property(root, "CODEGEN,vendor", NULL); + + if (codegendescription) + seq_printf(m, "machine\t\t: %s\n", codegendescription); + else + seq_printf(m, "machine\t\t: Efika\n"); + + if (revision) + seq_printf(m, "revision\t: %s\n", revision); + + if (codegenvendor) + seq_printf(m, "vendor\t\t: %s\n", codegenvendor); + + of_node_put(root); +} + +#ifdef CONFIG_PM +static void efika_suspend_prepare(void __iomem *mbar) +{ + u8 pin = 4; /* GPIO_WKUP_4 (GPIO_PSC6_0 - IRDA_RX) */ + u8 level = 1; /* wakeup on high level */ + /* IOW. to wake it up, short pins 1 and 3 on IRDA connector */ + mpc52xx_set_wakeup_gpio(pin, level); +} +#endif + +static void __init efika_setup_arch(void) +{ + rtas_initialize(); + + /* Map important registers from the internal memory map */ + mpc52xx_map_common_devices(); + + efika_pcisetup(); + +#ifdef CONFIG_PM + mpc52xx_suspend.board_suspend_prepare = efika_suspend_prepare; + mpc52xx_pm_init(); +#endif + + if (ppc_md.progress) + ppc_md.progress("Linux/PPC " UTS_RELEASE " running on Efika ;-)\n", 0x0); +} + +static int __init efika_probe(void) +{ + char *model = of_get_flat_dt_prop(of_get_flat_dt_root(), + "model", NULL); + + if (model == NULL) + return 0; + if (strcmp(model, "EFIKA5K2")) + return 0; + + ISA_DMA_THRESHOLD = ~0L; + DMA_MODE_READ = 0x44; + DMA_MODE_WRITE = 0x48; + + return 1; +} + +define_machine(efika) +{ + .name = EFIKA_PLATFORM_NAME, + .probe = efika_probe, + .setup_arch = efika_setup_arch, + .init = mpc52xx_declare_of_platform_devices, + .show_cpuinfo = efika_show_cpuinfo, + .init_IRQ = mpc52xx_init_irq, + .get_irq = mpc52xx_get_irq, + .restart = rtas_restart, + .power_off = rtas_power_off, + .halt = rtas_halt, + .set_rtc_time = rtas_set_rtc_time, + .get_rtc_time = rtas_get_rtc_time, + .progress = rtas_progress, + .get_boot_time = rtas_get_boot_time, + .calibrate_decr = generic_calibrate_decr, +#ifdef CONFIG_PCI + .phys_mem_access_prot = pci_phys_mem_access_prot, +#endif +}; + diff --git a/arch/powerpc/platforms/52xx/lite5200.c b/arch/powerpc/platforms/52xx/lite5200.c new file mode 100644 index 00000000000..01ffa64d2aa --- /dev/null +++ b/arch/powerpc/platforms/52xx/lite5200.c @@ -0,0 +1,198 @@ +/* + * Freescale Lite5200 board support + * + * Written by: Grant Likely <grant.likely@secretlab.ca> + * + * Copyright (C) Secret Lab Technologies Ltd. 2006. All rights reserved. + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved. + * + * Description: + * 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. + */ + +#undef DEBUG + +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/root_dev.h> +#include <linux/initrd.h> +#include <asm/time.h> +#include <asm/io.h> +#include <asm/machdep.h> +#include <asm/prom.h> +#include <asm/mpc52xx.h> + +/* ************************************************************************ + * + * Setup the architecture + * + */ + +/* mpc5200 device tree match tables */ +static struct of_device_id mpc5200_cdm_ids[] __initdata = { + { .compatible = "fsl,mpc5200-cdm", }, + { .compatible = "mpc5200-cdm", }, + {} +}; + +static struct of_device_id mpc5200_gpio_ids[] __initdata = { + { .compatible = "fsl,mpc5200-gpio", }, + { .compatible = "mpc5200-gpio", }, + {} +}; + +/* + * Fix clock configuration. + * + * Firmware is supposed to be responsible for this. If you are creating a + * new board port, do *NOT* duplicate this code. Fix your boot firmware + * to set it correctly in the first place + */ +static void __init +lite5200_fix_clock_config(void) +{ + struct device_node *np; + struct mpc52xx_cdm __iomem *cdm; + /* Map zones */ + np = of_find_matching_node(NULL, mpc5200_cdm_ids); + cdm = of_iomap(np, 0); + of_node_put(np); + if (!cdm) { + printk(KERN_ERR "%s() failed; expect abnormal behaviour\n", + __func__); + return; + } + + /* Use internal 48 Mhz */ + out_8(&cdm->ext_48mhz_en, 0x00); + out_8(&cdm->fd_enable, 0x01); + if (in_be32(&cdm->rstcfg) & 0x40) /* Assumes 33Mhz clock */ + out_be16(&cdm->fd_counters, 0x0001); + else + out_be16(&cdm->fd_counters, 0x5555); + + /* Unmap the regs */ + iounmap(cdm); +} + +/* + * Fix setting of port_config register. + * + * Firmware is supposed to be responsible for this. If you are creating a + * new board port, do *NOT* duplicate this code. Fix your boot firmware + * to set it correctly in the first place + */ +static void __init +lite5200_fix_port_config(void) +{ + struct device_node *np; + struct mpc52xx_gpio __iomem *gpio; + u32 port_config; + + np = of_find_matching_node(NULL, mpc5200_gpio_ids); + gpio = of_iomap(np, 0); + of_node_put(np); + if (!gpio) { + printk(KERN_ERR "%s() failed. expect abnormal behavior\n", + __func__); + return; + } + + /* Set port config */ + port_config = in_be32(&gpio->port_config); + + port_config &= ~0x00800000; /* 48Mhz internal, pin is GPIO */ + + port_config &= ~0x00007000; /* USB port : Differential mode */ + port_config |= 0x00001000; /* USB 1 only */ + + port_config &= ~0x03000000; /* ATA CS is on csb_4/5 */ + port_config |= 0x01000000; + + pr_debug("port_config: old:%x new:%x\n", + in_be32(&gpio->port_config), port_config); + out_be32(&gpio->port_config, port_config); + + /* Unmap zone */ + iounmap(gpio); +} + +#ifdef CONFIG_PM +static void lite5200_suspend_prepare(void __iomem *mbar) +{ + u8 pin = 1; /* GPIO_WKUP_1 (GPIO_PSC2_4) */ + u8 level = 0; /* wakeup on low level */ + mpc52xx_set_wakeup_gpio(pin, level); + + /* + * power down usb port + * this needs to be called before of-ohci suspend code + */ + + /* set ports to "power switched" and "powered at the same time" + * USB Rh descriptor A: NPS = 0, PSM = 0 */ + out_be32(mbar + 0x1048, in_be32(mbar + 0x1048) & ~0x300); + /* USB Rh status: LPS = 1 - turn off power */ + out_be32(mbar + 0x1050, 0x00000001); +} + +static void lite5200_resume_finish(void __iomem *mbar) +{ + /* USB Rh status: LPSC = 1 - turn on power */ + out_be32(mbar + 0x1050, 0x00010000); +} +#endif + +static void __init lite5200_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("lite5200_setup_arch()", 0); + + /* Map important registers from the internal memory map */ + mpc52xx_map_common_devices(); + + /* Some mpc5200 & mpc5200b related configuration */ + mpc5200_setup_xlb_arbiter(); + + /* Fix things that firmware should have done. */ + lite5200_fix_clock_config(); + lite5200_fix_port_config(); + +#ifdef CONFIG_PM + mpc52xx_suspend.board_suspend_prepare = lite5200_suspend_prepare; + mpc52xx_suspend.board_resume_finish = lite5200_resume_finish; + lite5200_pm_init(); +#endif + + mpc52xx_setup_pci(); +} + +static const char *board[] __initdata = { + "fsl,lite5200", + "fsl,lite5200b", + NULL, +}; + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init lite5200_probe(void) +{ + return of_flat_dt_match(of_get_flat_dt_root(), board); +} + +define_machine(lite5200) { + .name = "lite5200", + .probe = lite5200_probe, + .setup_arch = lite5200_setup_arch, + .init = mpc52xx_declare_of_platform_devices, + .init_IRQ = mpc52xx_init_irq, + .get_irq = mpc52xx_get_irq, + .restart = mpc52xx_restart, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/52xx/lite5200_pm.c b/arch/powerpc/platforms/52xx/lite5200_pm.c new file mode 100644 index 00000000000..eda0fc2a391 --- /dev/null +++ b/arch/powerpc/platforms/52xx/lite5200_pm.c @@ -0,0 +1,248 @@ +#include <linux/init.h> +#include <linux/suspend.h> +#include <asm/io.h> +#include <asm/time.h> +#include <asm/mpc52xx.h> + +/* defined in lite5200_sleep.S and only used here */ +extern void lite5200_low_power(void __iomem *sram, void __iomem *mbar); + +static struct mpc52xx_cdm __iomem *cdm; +static struct mpc52xx_intr __iomem *pic; +static struct mpc52xx_sdma __iomem *bes; +static struct mpc52xx_xlb __iomem *xlb; +static struct mpc52xx_gpio __iomem *gps; +static struct mpc52xx_gpio_wkup __iomem *gpw; +static void __iomem *pci; +static void __iomem *sram; +static const int sram_size = 0x4000; /* 16 kBytes */ +static void __iomem *mbar; + +static suspend_state_t lite5200_pm_target_state; + +static int lite5200_pm_valid(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + return 1; + default: + return 0; + } +} + +static int lite5200_pm_begin(suspend_state_t state) +{ + if (lite5200_pm_valid(state)) { + lite5200_pm_target_state = state; + return 0; + } + return -EINVAL; +} + +static int lite5200_pm_prepare(void) +{ + struct device_node *np; + const struct of_device_id immr_ids[] = { + { .compatible = "fsl,mpc5200-immr", }, + { .compatible = "fsl,mpc5200b-immr", }, + { .type = "soc", .compatible = "mpc5200", }, /* lite5200 */ + { .type = "builtin", .compatible = "mpc5200", }, /* efika */ + {} + }; + u64 regaddr64 = 0; + const u32 *regaddr_p; + + /* deep sleep? let mpc52xx code handle that */ + if (lite5200_pm_target_state == PM_SUSPEND_STANDBY) + return mpc52xx_pm_prepare(); + + if (lite5200_pm_target_state != PM_SUSPEND_MEM) + return -EINVAL; + + /* map registers */ + np = of_find_matching_node(NULL, immr_ids); + regaddr_p = of_get_address(np, 0, NULL, NULL); + if (regaddr_p) + regaddr64 = of_translate_address(np, regaddr_p); + of_node_put(np); + + mbar = ioremap((u32) regaddr64, 0xC000); + if (!mbar) { + printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__); + return -ENOSYS; + } + + cdm = mbar + 0x200; + pic = mbar + 0x500; + gps = mbar + 0xb00; + gpw = mbar + 0xc00; + pci = mbar + 0xd00; + bes = mbar + 0x1200; + xlb = mbar + 0x1f00; + sram = mbar + 0x8000; + + return 0; +} + +/* save and restore registers not bound to any real devices */ +static struct mpc52xx_cdm scdm; +static struct mpc52xx_intr spic; +static struct mpc52xx_sdma sbes; +static struct mpc52xx_xlb sxlb; +static struct mpc52xx_gpio sgps; +static struct mpc52xx_gpio_wkup sgpw; +static char spci[0x200]; + +static void lite5200_save_regs(void) +{ + _memcpy_fromio(&spic, pic, sizeof(*pic)); + _memcpy_fromio(&sbes, bes, sizeof(*bes)); + _memcpy_fromio(&scdm, cdm, sizeof(*cdm)); + _memcpy_fromio(&sxlb, xlb, sizeof(*xlb)); + _memcpy_fromio(&sgps, gps, sizeof(*gps)); + _memcpy_fromio(&sgpw, gpw, sizeof(*gpw)); + _memcpy_fromio(spci, pci, 0x200); + + _memcpy_fromio(saved_sram, sram, sram_size); +} + +static void lite5200_restore_regs(void) +{ + int i; + _memcpy_toio(sram, saved_sram, sram_size); + + /* PCI Configuration */ + _memcpy_toio(pci, spci, 0x200); + + /* + * GPIOs. Interrupt Master Enable has higher address then other + * registers, so just memcpy is ok. + */ + _memcpy_toio(gpw, &sgpw, sizeof(*gpw)); + _memcpy_toio(gps, &sgps, sizeof(*gps)); + + + /* XLB Arbitrer */ + out_be32(&xlb->snoop_window, sxlb.snoop_window); + out_be32(&xlb->master_priority, sxlb.master_priority); + out_be32(&xlb->master_pri_enable, sxlb.master_pri_enable); + + /* enable */ + out_be32(&xlb->int_enable, sxlb.int_enable); + out_be32(&xlb->config, sxlb.config); + + + /* CDM - Clock Distribution Module */ + out_8(&cdm->ipb_clk_sel, scdm.ipb_clk_sel); + out_8(&cdm->pci_clk_sel, scdm.pci_clk_sel); + + out_8(&cdm->ext_48mhz_en, scdm.ext_48mhz_en); + out_8(&cdm->fd_enable, scdm.fd_enable); + out_be16(&cdm->fd_counters, scdm.fd_counters); + + out_be32(&cdm->clk_enables, scdm.clk_enables); + + out_8(&cdm->osc_disable, scdm.osc_disable); + + out_be16(&cdm->mclken_div_psc1, scdm.mclken_div_psc1); + out_be16(&cdm->mclken_div_psc2, scdm.mclken_div_psc2); + out_be16(&cdm->mclken_div_psc3, scdm.mclken_div_psc3); + out_be16(&cdm->mclken_div_psc6, scdm.mclken_div_psc6); + + + /* BESTCOMM */ + out_be32(&bes->taskBar, sbes.taskBar); + out_be32(&bes->currentPointer, sbes.currentPointer); + out_be32(&bes->endPointer, sbes.endPointer); + out_be32(&bes->variablePointer, sbes.variablePointer); + + out_8(&bes->IntVect1, sbes.IntVect1); + out_8(&bes->IntVect2, sbes.IntVect2); + out_be16(&bes->PtdCntrl, sbes.PtdCntrl); + + for (i=0; i<32; i++) + out_8(&bes->ipr[i], sbes.ipr[i]); + + out_be32(&bes->cReqSelect, sbes.cReqSelect); + out_be32(&bes->task_size0, sbes.task_size0); + out_be32(&bes->task_size1, sbes.task_size1); + out_be32(&bes->MDEDebug, sbes.MDEDebug); + out_be32(&bes->ADSDebug, sbes.ADSDebug); + out_be32(&bes->Value1, sbes.Value1); + out_be32(&bes->Value2, sbes.Value2); + out_be32(&bes->Control, sbes.Control); + out_be32(&bes->Status, sbes.Status); + out_be32(&bes->PTDDebug, sbes.PTDDebug); + + /* restore tasks */ + for (i=0; i<16; i++) + out_be16(&bes->tcr[i], sbes.tcr[i]); + + /* enable interrupts */ + out_be32(&bes->IntPend, sbes.IntPend); + out_be32(&bes->IntMask, sbes.IntMask); + + + /* PIC */ + out_be32(&pic->per_pri1, spic.per_pri1); + out_be32(&pic->per_pri2, spic.per_pri2); + out_be32(&pic->per_pri3, spic.per_pri3); + + out_be32(&pic->main_pri1, spic.main_pri1); + out_be32(&pic->main_pri2, spic.main_pri2); + + out_be32(&pic->enc_status, spic.enc_status); + + /* unmask and enable interrupts */ + out_be32(&pic->per_mask, spic.per_mask); + out_be32(&pic->main_mask, spic.main_mask); + out_be32(&pic->ctrl, spic.ctrl); +} + +static int lite5200_pm_enter(suspend_state_t state) +{ + /* deep sleep? let mpc52xx code handle that */ + if (state == PM_SUSPEND_STANDBY) { + return mpc52xx_pm_enter(state); + } + + lite5200_save_regs(); + + /* effectively save FP regs */ + enable_kernel_fp(); + + lite5200_low_power(sram, mbar); + + lite5200_restore_regs(); + + iounmap(mbar); + return 0; +} + +static void lite5200_pm_finish(void) +{ + /* deep sleep? let mpc52xx code handle that */ + if (lite5200_pm_target_state == PM_SUSPEND_STANDBY) + mpc52xx_pm_finish(); +} + +static void lite5200_pm_end(void) +{ + lite5200_pm_target_state = PM_SUSPEND_ON; +} + +static const struct platform_suspend_ops lite5200_pm_ops = { + .valid = lite5200_pm_valid, + .begin = lite5200_pm_begin, + .prepare = lite5200_pm_prepare, + .enter = lite5200_pm_enter, + .finish = lite5200_pm_finish, + .end = lite5200_pm_end, +}; + +int __init lite5200_pm_init(void) +{ + suspend_set_ops(&lite5200_pm_ops); + return 0; +} diff --git a/arch/powerpc/platforms/52xx/lite5200_sleep.S b/arch/powerpc/platforms/52xx/lite5200_sleep.S new file mode 100644 index 00000000000..08ab6fefcf7 --- /dev/null +++ b/arch/powerpc/platforms/52xx/lite5200_sleep.S @@ -0,0 +1,412 @@ +#include <asm/reg.h> +#include <asm/ppc_asm.h> +#include <asm/processor.h> +#include <asm/cache.h> + + +#define SDRAM_CTRL 0x104 +#define SC_MODE_EN (1<<31) +#define SC_CKE (1<<30) +#define SC_REF_EN (1<<28) +#define SC_SOFT_PRE (1<<1) + +#define GPIOW_GPIOE 0xc00 +#define GPIOW_DDR 0xc08 +#define GPIOW_DVO 0xc0c + +#define CDM_CE 0x214 +#define CDM_SDRAM (1<<3) + + +/* helpers... beware: r10 and r4 are overwritten */ +#define SAVE_SPRN(reg, addr) \ + mfspr r10, SPRN_##reg; \ + stw r10, ((addr)*4)(r4); + +#define LOAD_SPRN(reg, addr) \ + lwz r10, ((addr)*4)(r4); \ + mtspr SPRN_##reg, r10; \ + sync; \ + isync; + + + .data +registers: + .space 0x5c*4 + .text + +/* ---------------------------------------------------------------------- */ +/* low-power mode with help of M68HLC908QT1 */ + + .globl lite5200_low_power +lite5200_low_power: + + mr r7, r3 /* save SRAM va */ + mr r8, r4 /* save MBAR va */ + + /* setup wakeup address for u-boot at physical location 0x0 */ + lis r3, CONFIG_KERNEL_START@h + lis r4, lite5200_wakeup@h + ori r4, r4, lite5200_wakeup@l + sub r4, r4, r3 + stw r4, 0(r3) + + + /* + * save stuff BDI overwrites + * 0xf0 (0xe0->0x100 gets overwritten when BDI connected; + * even when CONFIG_BDI* is disabled and MMU XLAT commented; heisenbug?)) + * WARNING: self-refresh doesn't seem to work when BDI2000 is connected, + * possibly because BDI sets SDRAM registers before wakeup code does + */ + lis r4, registers@h + ori r4, r4, registers@l + lwz r10, 0xf0(r3) + stw r10, (0x1d*4)(r4) + + /* save registers to r4 [destroys r10] */ + SAVE_SPRN(LR, 0x1c) + bl save_regs + + /* flush caches [destroys r3, r4] */ + bl flush_data_cache + + + /* copy code to sram */ + mr r4, r7 + li r3, (sram_code_end - sram_code)/4 + mtctr r3 + lis r3, sram_code@h + ori r3, r3, sram_code@l +1: + lwz r5, 0(r3) + stw r5, 0(r4) + addi r3, r3, 4 + addi r4, r4, 4 + bdnz 1b + + /* get tb_ticks_per_usec */ + lis r3, tb_ticks_per_usec@h + lwz r11, tb_ticks_per_usec@l(r3) + + /* disable I and D caches */ + mfspr r3, SPRN_HID0 + ori r3, r3, HID0_ICE | HID0_DCE + xori r3, r3, HID0_ICE | HID0_DCE + sync; isync; + mtspr SPRN_HID0, r3 + sync; isync; + + /* jump to sram */ + mtlr r7 + blrl + /* doesn't return */ + + +sram_code: + /* self refresh */ + lwz r4, SDRAM_CTRL(r8) + + /* send NOP (precharge) */ + oris r4, r4, SC_MODE_EN@h /* mode_en */ + stw r4, SDRAM_CTRL(r8) + sync + + ori r4, r4, SC_SOFT_PRE /* soft_pre */ + stw r4, SDRAM_CTRL(r8) + sync + xori r4, r4, SC_SOFT_PRE + + xoris r4, r4, SC_MODE_EN@h /* !mode_en */ + stw r4, SDRAM_CTRL(r8) + sync + + /* delay (for NOP to finish) */ + li r12, 1 + bl udelay + + /* + * mode_en must not be set when enabling self-refresh + * send AR with CKE low (self-refresh) + */ + oris r4, r4, (SC_REF_EN | SC_CKE)@h + xoris r4, r4, (SC_CKE)@h /* ref_en !cke */ + stw r4, SDRAM_CTRL(r8) + sync + + /* delay (after !CKE there should be two cycles) */ + li r12, 1 + bl udelay + + /* disable clock */ + lwz r4, CDM_CE(r8) + ori r4, r4, CDM_SDRAM + xori r4, r4, CDM_SDRAM + stw r4, CDM_CE(r8) + sync + + /* delay a bit */ + li r12, 1 + bl udelay + + + /* turn off with QT chip */ + li r4, 0x02 + stb r4, GPIOW_GPIOE(r8) /* enable gpio_wkup1 */ + sync + + stb r4, GPIOW_DVO(r8) /* "output" high */ + sync + stb r4, GPIOW_DDR(r8) /* output */ + sync + stb r4, GPIOW_DVO(r8) /* output high */ + sync + + /* 10uS delay */ + li r12, 10 + bl udelay + + /* turn off */ + li r4, 0 + stb r4, GPIOW_DVO(r8) /* output low */ + sync + + /* wait until we're offline */ + 1: + b 1b + + + /* local udelay in sram is needed */ + udelay: /* r11 - tb_ticks_per_usec, r12 - usecs, overwrites r13 */ + mullw r12, r12, r11 + mftb r13 /* start */ + addi r12, r13, r12 /* end */ + 1: + mftb r13 /* current */ + cmp cr0, r13, r12 + blt 1b + blr + +sram_code_end: + + + +/* uboot jumps here on resume */ +lite5200_wakeup: + bl restore_regs + + + /* HIDs, MSR */ + LOAD_SPRN(HID1, 0x19) + LOAD_SPRN(HID2, 0x1a) + + + /* address translation is tricky (see turn_on_mmu) */ + mfmsr r10 + ori r10, r10, MSR_DR | MSR_IR + + + mtspr SPRN_SRR1, r10 + lis r10, mmu_on@h + ori r10, r10, mmu_on@l + mtspr SPRN_SRR0, r10 + sync + rfi +mmu_on: + /* kernel offset (r4 is still set from restore_registers) */ + addis r4, r4, CONFIG_KERNEL_START@h + + + /* restore MSR */ + lwz r10, (4*0x1b)(r4) + mtmsr r10 + sync; isync; + + /* invalidate caches */ + mfspr r10, SPRN_HID0 + ori r5, r10, HID0_ICFI | HID0_DCI + mtspr SPRN_HID0, r5 /* invalidate caches */ + sync; isync; + mtspr SPRN_HID0, r10 + sync; isync; + + /* enable caches */ + lwz r10, (4*0x18)(r4) + mtspr SPRN_HID0, r10 /* restore (enable caches, DPM) */ + /* ^ this has to be after address translation set in MSR */ + sync + isync + + + /* restore 0xf0 (BDI2000) */ + lis r3, CONFIG_KERNEL_START@h + lwz r10, (0x1d*4)(r4) + stw r10, 0xf0(r3) + + LOAD_SPRN(LR, 0x1c) + + + blr + + +/* ---------------------------------------------------------------------- */ +/* boring code: helpers */ + +/* save registers */ +#define SAVE_BAT(n, addr) \ + SAVE_SPRN(DBAT##n##L, addr); \ + SAVE_SPRN(DBAT##n##U, addr+1); \ + SAVE_SPRN(IBAT##n##L, addr+2); \ + SAVE_SPRN(IBAT##n##U, addr+3); + +#define SAVE_SR(n, addr) \ + mfsr r10, n; \ + stw r10, ((addr)*4)(r4); + +#define SAVE_4SR(n, addr) \ + SAVE_SR(n, addr); \ + SAVE_SR(n+1, addr+1); \ + SAVE_SR(n+2, addr+2); \ + SAVE_SR(n+3, addr+3); + +save_regs: + stw r0, 0(r4) + stw r1, 0x4(r4) + stw r2, 0x8(r4) + stmw r11, 0xc(r4) /* 0xc -> 0x5f, (0x18*4-1) */ + + SAVE_SPRN(HID0, 0x18) + SAVE_SPRN(HID1, 0x19) + SAVE_SPRN(HID2, 0x1a) + mfmsr r10 + stw r10, (4*0x1b)(r4) + /*SAVE_SPRN(LR, 0x1c) have to save it before the call */ + /* 0x1d reserved by 0xf0 */ + SAVE_SPRN(RPA, 0x1e) + SAVE_SPRN(SDR1, 0x1f) + + /* save MMU regs */ + SAVE_BAT(0, 0x20) + SAVE_BAT(1, 0x24) + SAVE_BAT(2, 0x28) + SAVE_BAT(3, 0x2c) + SAVE_BAT(4, 0x30) + SAVE_BAT(5, 0x34) + SAVE_BAT(6, 0x38) + SAVE_BAT(7, 0x3c) + + SAVE_4SR(0, 0x40) + SAVE_4SR(4, 0x44) + SAVE_4SR(8, 0x48) + SAVE_4SR(12, 0x4c) + + SAVE_SPRN(SPRG0, 0x50) + SAVE_SPRN(SPRG1, 0x51) + SAVE_SPRN(SPRG2, 0x52) + SAVE_SPRN(SPRG3, 0x53) + SAVE_SPRN(SPRG4, 0x54) + SAVE_SPRN(SPRG5, 0x55) + SAVE_SPRN(SPRG6, 0x56) + SAVE_SPRN(SPRG7, 0x57) + + SAVE_SPRN(IABR, 0x58) + SAVE_SPRN(DABR, 0x59) + SAVE_SPRN(TBRL, 0x5a) + SAVE_SPRN(TBRU, 0x5b) + + blr + + +/* restore registers */ +#define LOAD_BAT(n, addr) \ + LOAD_SPRN(DBAT##n##L, addr); \ + LOAD_SPRN(DBAT##n##U, addr+1); \ + LOAD_SPRN(IBAT##n##L, addr+2); \ + LOAD_SPRN(IBAT##n##U, addr+3); + +#define LOAD_SR(n, addr) \ + lwz r10, ((addr)*4)(r4); \ + mtsr n, r10; + +#define LOAD_4SR(n, addr) \ + LOAD_SR(n, addr); \ + LOAD_SR(n+1, addr+1); \ + LOAD_SR(n+2, addr+2); \ + LOAD_SR(n+3, addr+3); + +restore_regs: + lis r4, registers@h + ori r4, r4, registers@l + + /* MMU is not up yet */ + subis r4, r4, CONFIG_KERNEL_START@h + + lwz r0, 0(r4) + lwz r1, 0x4(r4) + lwz r2, 0x8(r4) + lmw r11, 0xc(r4) + + /* + * these are a bit tricky + * + * 0x18 - HID0 + * 0x19 - HID1 + * 0x1a - HID2 + * 0x1b - MSR + * 0x1c - LR + * 0x1d - reserved by 0xf0 (BDI2000) + */ + LOAD_SPRN(RPA, 0x1e); + LOAD_SPRN(SDR1, 0x1f); + + /* restore MMU regs */ + LOAD_BAT(0, 0x20) + LOAD_BAT(1, 0x24) + LOAD_BAT(2, 0x28) + LOAD_BAT(3, 0x2c) + LOAD_BAT(4, 0x30) + LOAD_BAT(5, 0x34) + LOAD_BAT(6, 0x38) + LOAD_BAT(7, 0x3c) + + LOAD_4SR(0, 0x40) + LOAD_4SR(4, 0x44) + LOAD_4SR(8, 0x48) + LOAD_4SR(12, 0x4c) + + /* rest of regs */ + LOAD_SPRN(SPRG0, 0x50); + LOAD_SPRN(SPRG1, 0x51); + LOAD_SPRN(SPRG2, 0x52); + LOAD_SPRN(SPRG3, 0x53); + LOAD_SPRN(SPRG4, 0x54); + LOAD_SPRN(SPRG5, 0x55); + LOAD_SPRN(SPRG6, 0x56); + LOAD_SPRN(SPRG7, 0x57); + + LOAD_SPRN(IABR, 0x58); + LOAD_SPRN(DABR, 0x59); + LOAD_SPRN(TBWL, 0x5a); /* these two have separate R/W regs */ + LOAD_SPRN(TBWU, 0x5b); + + blr + + + +/* cache flushing code. copied from arch/ppc/boot/util.S */ +#define NUM_CACHE_LINES (128*8) + +/* + * Flush data cache + * Do this by just reading lots of stuff into the cache. + */ +flush_data_cache: + lis r3,CONFIG_KERNEL_START@h + ori r3,r3,CONFIG_KERNEL_START@l + li r4,NUM_CACHE_LINES + mtctr r4 +1: + lwz r4,0(r3) + addi r3,r3,L1_CACHE_BYTES /* Next line, please */ + bdnz 1b + blr diff --git a/arch/powerpc/platforms/52xx/media5200.c b/arch/powerpc/platforms/52xx/media5200.c new file mode 100644 index 00000000000..96f85e5e0cd --- /dev/null +++ b/arch/powerpc/platforms/52xx/media5200.c @@ -0,0 +1,260 @@ +/* + * Support for 'media5200-platform' compatible boards. + * + * Copyright (C) 2008 Secret Lab Technologies Ltd. + * + * 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. + * + * Description: + * This code implements support for the Freescape Media5200 platform + * (built around the MPC5200 SoC). + * + * Notable characteristic of the Media5200 is the presence of an FPGA + * that has all external IRQ lines routed through it. This file implements + * a cascaded interrupt controller driver which attaches itself to the + * Virtual IRQ subsystem after the primary mpc5200 interrupt controller + * is initialized. + * + */ + +#undef DEBUG + +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <asm/time.h> +#include <asm/prom.h> +#include <asm/machdep.h> +#include <asm/mpc52xx.h> + +static struct of_device_id mpc5200_gpio_ids[] __initdata = { + { .compatible = "fsl,mpc5200-gpio", }, + { .compatible = "mpc5200-gpio", }, + {} +}; + +/* FPGA register set */ +#define MEDIA5200_IRQ_ENABLE (0x40c) +#define MEDIA5200_IRQ_STATUS (0x410) +#define MEDIA5200_NUM_IRQS (6) +#define MEDIA5200_IRQ_SHIFT (32 - MEDIA5200_NUM_IRQS) + +struct media5200_irq { + void __iomem *regs; + spinlock_t lock; + struct irq_host *irqhost; +}; +struct media5200_irq media5200_irq; + +static void media5200_irq_unmask(struct irq_data *d) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&media5200_irq.lock, flags); + val = in_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE); + val |= 1 << (MEDIA5200_IRQ_SHIFT + irqd_to_hwirq(d)); + out_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE, val); + spin_unlock_irqrestore(&media5200_irq.lock, flags); +} + +static void media5200_irq_mask(struct irq_data *d) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&media5200_irq.lock, flags); + val = in_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE); + val &= ~(1 << (MEDIA5200_IRQ_SHIFT + irqd_to_hwirq(d))); + out_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE, val); + spin_unlock_irqrestore(&media5200_irq.lock, flags); +} + +static struct irq_chip media5200_irq_chip = { + .name = "Media5200 FPGA", + .irq_unmask = media5200_irq_unmask, + .irq_mask = media5200_irq_mask, + .irq_mask_ack = media5200_irq_mask, +}; + +void media5200_irq_cascade(unsigned int virq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + int sub_virq, val; + u32 status, enable; + + /* Mask off the cascaded IRQ */ + raw_spin_lock(&desc->lock); + chip->irq_mask(&desc->irq_data); + raw_spin_unlock(&desc->lock); + + /* Ask the FPGA for IRQ status. If 'val' is 0, then no irqs + * are pending. 'ffs()' is 1 based */ + status = in_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE); + enable = in_be32(media5200_irq.regs + MEDIA5200_IRQ_STATUS); + val = ffs((status & enable) >> MEDIA5200_IRQ_SHIFT); + if (val) { + sub_virq = irq_linear_revmap(media5200_irq.irqhost, val - 1); + /* pr_debug("%s: virq=%i s=%.8x e=%.8x hwirq=%i subvirq=%i\n", + * __func__, virq, status, enable, val - 1, sub_virq); + */ + generic_handle_irq(sub_virq); + } + + /* Processing done; can reenable the cascade now */ + raw_spin_lock(&desc->lock); + chip->irq_ack(&desc->irq_data); + if (!irqd_irq_disabled(&desc->irq_data)) + chip->irq_unmask(&desc->irq_data); + raw_spin_unlock(&desc->lock); +} + +static int media5200_irq_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw) +{ + pr_debug("%s: h=%p, virq=%i, hwirq=%i\n", __func__, h, virq, (int)hw); + irq_set_chip_data(virq, &media5200_irq); + irq_set_chip_and_handler(virq, &media5200_irq_chip, handle_level_irq); + irq_set_status_flags(virq, IRQ_LEVEL); + return 0; +} + +static int media5200_irq_xlate(struct irq_host *h, struct device_node *ct, + const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, + unsigned int *out_flags) +{ + if (intsize != 2) + return -1; + + pr_debug("%s: bank=%i, number=%i\n", __func__, intspec[0], intspec[1]); + *out_hwirq = intspec[1]; + *out_flags = IRQ_TYPE_NONE; + return 0; +} + +static struct irq_host_ops media5200_irq_ops = { + .map = media5200_irq_map, + .xlate = media5200_irq_xlate, +}; + +/* + * Setup Media5200 IRQ mapping + */ +static void __init media5200_init_irq(void) +{ + struct device_node *fpga_np; + int cascade_virq; + + /* First setup the regular MPC5200 interrupt controller */ + mpc52xx_init_irq(); + + /* Now find the FPGA IRQ */ + fpga_np = of_find_compatible_node(NULL, NULL, "fsl,media5200-fpga"); + if (!fpga_np) + goto out; + pr_debug("%s: found fpga node: %s\n", __func__, fpga_np->full_name); + + media5200_irq.regs = of_iomap(fpga_np, 0); + if (!media5200_irq.regs) + goto out; + pr_debug("%s: mapped to %p\n", __func__, media5200_irq.regs); + + cascade_virq = irq_of_parse_and_map(fpga_np, 0); + if (!cascade_virq) + goto out; + pr_debug("%s: cascaded on virq=%i\n", __func__, cascade_virq); + + /* Disable all FPGA IRQs */ + out_be32(media5200_irq.regs + MEDIA5200_IRQ_ENABLE, 0); + + spin_lock_init(&media5200_irq.lock); + + media5200_irq.irqhost = irq_alloc_host(fpga_np, IRQ_HOST_MAP_LINEAR, + MEDIA5200_NUM_IRQS, + &media5200_irq_ops, -1); + if (!media5200_irq.irqhost) + goto out; + pr_debug("%s: allocated irqhost\n", __func__); + + media5200_irq.irqhost->host_data = &media5200_irq; + + irq_set_handler_data(cascade_virq, &media5200_irq); + irq_set_chained_handler(cascade_virq, media5200_irq_cascade); + + return; + + out: + pr_err("Could not find Media5200 FPGA; PCI interrupts will not work\n"); +} + +/* + * Setup the architecture + */ +static void __init media5200_setup_arch(void) +{ + + struct device_node *np; + struct mpc52xx_gpio __iomem *gpio; + u32 port_config; + + if (ppc_md.progress) + ppc_md.progress("media5200_setup_arch()", 0); + + /* Map important registers from the internal memory map */ + mpc52xx_map_common_devices(); + + /* Some mpc5200 & mpc5200b related configuration */ + mpc5200_setup_xlb_arbiter(); + + mpc52xx_setup_pci(); + + np = of_find_matching_node(NULL, mpc5200_gpio_ids); + gpio = of_iomap(np, 0); + of_node_put(np); + if (!gpio) { + printk(KERN_ERR "%s() failed. expect abnormal behavior\n", + __func__); + return; + } + + /* Set port config */ + port_config = in_be32(&gpio->port_config); + + port_config &= ~0x03000000; /* ATA CS is on csb_4/5 */ + port_config |= 0x01000000; + + out_be32(&gpio->port_config, port_config); + + /* Unmap zone */ + iounmap(gpio); + +} + +/* list of the supported boards */ +static const char *board[] __initdata = { + "fsl,media5200", + NULL +}; + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init media5200_probe(void) +{ + return of_flat_dt_match(of_get_flat_dt_root(), board); +} + +define_machine(media5200_platform) { + .name = "media5200-platform", + .probe = media5200_probe, + .setup_arch = media5200_setup_arch, + .init = mpc52xx_declare_of_platform_devices, + .init_IRQ = media5200_init_irq, + .get_irq = mpc52xx_get_irq, + .restart = mpc52xx_restart, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/52xx/mpc5200_simple.c b/arch/powerpc/platforms/52xx/mpc5200_simple.c new file mode 100644 index 00000000000..846b789fb19 --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc5200_simple.c @@ -0,0 +1,82 @@ +/* + * Support for 'mpc5200-simple-platform' compatible boards. + * + * Written by Marian Balakowicz <m8@semihalf.com> + * Copyright (C) 2007 Semihalf + * + * 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. + * + * Description: + * This code implements support for a simple MPC52xx based boards which + * do not need a custom platform specific setup. Such boards are + * supported assuming the following: + * + * - GPIO pins are configured by the firmware, + * - CDM configuration (clocking) is setup correctly by firmware, + * - if the 'fsl,has-wdt' property is present in one of the + * gpt nodes, then it is safe to use such gpt to reset the board, + * - PCI is supported if enabled in the kernel configuration + * and if there is a PCI bus node defined in the device tree. + * + * Boards that are compatible with this generic platform support + * are listed in a 'board' table. + */ + +#undef DEBUG +#include <asm/time.h> +#include <asm/prom.h> +#include <asm/machdep.h> +#include <asm/mpc52xx.h> + +/* + * Setup the architecture + */ +static void __init mpc5200_simple_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("mpc5200_simple_setup_arch()", 0); + + /* Map important registers from the internal memory map */ + mpc52xx_map_common_devices(); + + /* Some mpc5200 & mpc5200b related configuration */ + mpc5200_setup_xlb_arbiter(); + + mpc52xx_setup_pci(); +} + +/* list of the supported boards */ +static const char *board[] __initdata = { + "anon,charon", + "intercontrol,digsy-mtc", + "manroland,mucmc52", + "manroland,uc101", + "phytec,pcm030", + "phytec,pcm032", + "promess,motionpro", + "schindler,cm5200", + "tqc,tqm5200", + NULL +}; + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc5200_simple_probe(void) +{ + return of_flat_dt_match(of_get_flat_dt_root(), board); +} + +define_machine(mpc5200_simple_platform) { + .name = "mpc5200-simple-platform", + .probe = mpc5200_simple_probe, + .setup_arch = mpc5200_simple_setup_arch, + .init = mpc52xx_declare_of_platform_devices, + .init_IRQ = mpc52xx_init_irq, + .get_irq = mpc52xx_get_irq, + .restart = mpc52xx_restart, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/52xx/mpc52xx_common.c b/arch/powerpc/platforms/52xx/mpc52xx_common.c new file mode 100644 index 00000000000..369fd5457a3 --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_common.c @@ -0,0 +1,346 @@ +/* + * + * Utility functions for the Freescale MPC52xx. + * + * Copyright (C) 2006 Sylvain Munaut <tnt@246tNt.com> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#undef DEBUG + +#include <linux/gpio.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/export.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/mpc52xx.h> + +/* MPC5200 device tree match tables */ +static struct of_device_id mpc52xx_xlb_ids[] __initdata = { + { .compatible = "fsl,mpc5200-xlb", }, + { .compatible = "mpc5200-xlb", }, + {} +}; +static struct of_device_id mpc52xx_bus_ids[] __initdata = { + { .compatible = "fsl,mpc5200-immr", }, + { .compatible = "fsl,mpc5200b-immr", }, + { .compatible = "simple-bus", }, + + /* depreciated matches; shouldn't be used in new device trees */ + { .compatible = "fsl,lpb", }, + { .type = "builtin", .compatible = "mpc5200", }, /* efika */ + { .type = "soc", .compatible = "mpc5200", }, /* lite5200 */ + {} +}; + +/* + * This variable is mapped in mpc52xx_map_wdt() and used in mpc52xx_restart(). + * Permanent mapping is required because mpc52xx_restart() can be called + * from interrupt context while node mapping (which calls ioremap()) + * cannot be used at such point. + */ +static DEFINE_SPINLOCK(mpc52xx_lock); +static struct mpc52xx_gpt __iomem *mpc52xx_wdt; +static struct mpc52xx_cdm __iomem *mpc52xx_cdm; + +/* + * Configure the XLB arbiter settings to match what Linux expects. + */ +void __init +mpc5200_setup_xlb_arbiter(void) +{ + struct device_node *np; + struct mpc52xx_xlb __iomem *xlb; + + np = of_find_matching_node(NULL, mpc52xx_xlb_ids); + xlb = of_iomap(np, 0); + of_node_put(np); + if (!xlb) { + printk(KERN_ERR __FILE__ ": " + "Error mapping XLB in mpc52xx_setup_cpu(). " + "Expect some abnormal behavior\n"); + return; + } + + /* Configure the XLB Arbiter priorities */ + out_be32(&xlb->master_pri_enable, 0xff); + out_be32(&xlb->master_priority, 0x11111111); + + /* + * Disable XLB pipelining + * (cfr errate 292. We could do this only just before ATA PIO + * transaction and re-enable it afterwards ...) + * Not needed on MPC5200B. + */ + if ((mfspr(SPRN_SVR) & MPC5200_SVR_MASK) == MPC5200_SVR) + out_be32(&xlb->config, in_be32(&xlb->config) | MPC52xx_XLB_CFG_PLDIS); + + iounmap(xlb); +} + +/* + * This variable is mapped in mpc52xx_map_common_devices and + * used in mpc5200_psc_ac97_gpio_reset(). + */ +static DEFINE_SPINLOCK(gpio_lock); +struct mpc52xx_gpio __iomem *simple_gpio; +struct mpc52xx_gpio_wkup __iomem *wkup_gpio; + +/** + * mpc52xx_declare_of_platform_devices: register internal devices and children + * of the localplus bus to the of_platform + * bus. + */ +void __init +mpc52xx_declare_of_platform_devices(void) +{ + /* Find every child of the SOC node and add it to of_platform */ + if (of_platform_bus_probe(NULL, mpc52xx_bus_ids, NULL)) + printk(KERN_ERR __FILE__ ": " + "Error while probing of_platform bus\n"); +} + +/* + * match tables used by mpc52xx_map_common_devices() + */ +static struct of_device_id mpc52xx_gpt_ids[] __initdata = { + { .compatible = "fsl,mpc5200-gpt", }, + { .compatible = "mpc5200-gpt", }, /* old */ + {} +}; +static struct of_device_id mpc52xx_cdm_ids[] __initdata = { + { .compatible = "fsl,mpc5200-cdm", }, + { .compatible = "mpc5200-cdm", }, /* old */ + {} +}; +static const struct of_device_id mpc52xx_gpio_simple[] = { + { .compatible = "fsl,mpc5200-gpio", }, + {} +}; +static const struct of_device_id mpc52xx_gpio_wkup[] = { + { .compatible = "fsl,mpc5200-gpio-wkup", }, + {} +}; + + +/** + * mpc52xx_map_common_devices: iomap devices required by common code + */ +void __init +mpc52xx_map_common_devices(void) +{ + struct device_node *np; + + /* mpc52xx_wdt is mapped here and used in mpc52xx_restart, + * possibly from a interrupt context. wdt is only implement + * on a gpt0, so check has-wdt property before mapping. + */ + for_each_matching_node(np, mpc52xx_gpt_ids) { + if (of_get_property(np, "fsl,has-wdt", NULL) || + of_get_property(np, "has-wdt", NULL)) { + mpc52xx_wdt = of_iomap(np, 0); + of_node_put(np); + break; + } + } + + /* Clock Distribution Module, used by PSC clock setting function */ + np = of_find_matching_node(NULL, mpc52xx_cdm_ids); + mpc52xx_cdm = of_iomap(np, 0); + of_node_put(np); + + /* simple_gpio registers */ + np = of_find_matching_node(NULL, mpc52xx_gpio_simple); + simple_gpio = of_iomap(np, 0); + of_node_put(np); + + /* wkup_gpio registers */ + np = of_find_matching_node(NULL, mpc52xx_gpio_wkup); + wkup_gpio = of_iomap(np, 0); + of_node_put(np); +} + +/** + * mpc52xx_set_psc_clkdiv: Set clock divider in the CDM for PSC ports + * + * @psc_id: id of psc port; must be 1,2,3 or 6 + * @clkdiv: clock divider value to put into CDM PSC register. + */ +int mpc52xx_set_psc_clkdiv(int psc_id, int clkdiv) +{ + unsigned long flags; + u16 __iomem *reg; + u32 val; + u32 mask; + u32 mclken_div; + + if (!mpc52xx_cdm) + return -ENODEV; + + mclken_div = 0x8000 | (clkdiv & 0x1FF); + switch (psc_id) { + case 1: reg = &mpc52xx_cdm->mclken_div_psc1; mask = 0x20; break; + case 2: reg = &mpc52xx_cdm->mclken_div_psc2; mask = 0x40; break; + case 3: reg = &mpc52xx_cdm->mclken_div_psc3; mask = 0x80; break; + case 6: reg = &mpc52xx_cdm->mclken_div_psc6; mask = 0x10; break; + default: + return -ENODEV; + } + + /* Set the rate and enable the clock */ + spin_lock_irqsave(&mpc52xx_lock, flags); + out_be16(reg, mclken_div); + val = in_be32(&mpc52xx_cdm->clk_enables); + out_be32(&mpc52xx_cdm->clk_enables, val | mask); + spin_unlock_irqrestore(&mpc52xx_lock, flags); + + return 0; +} +EXPORT_SYMBOL(mpc52xx_set_psc_clkdiv); + +/** + * mpc52xx_get_xtal_freq - Get SYS_XTAL_IN frequency for a device + * + * @node: device node + * + * Returns the frequency of the external oscillator clock connected + * to the SYS_XTAL_IN pin, or 0 if it cannot be determined. + */ +unsigned int mpc52xx_get_xtal_freq(struct device_node *node) +{ + u32 val; + unsigned int freq; + + if (!mpc52xx_cdm) + return 0; + + freq = mpc5xxx_get_bus_frequency(node); + if (!freq) + return 0; + + if (in_8(&mpc52xx_cdm->ipb_clk_sel) & 0x1) + freq *= 2; + + val = in_be32(&mpc52xx_cdm->rstcfg); + if (val & (1 << 5)) + freq *= 8; + else + freq *= 4; + if (val & (1 << 6)) + freq /= 12; + else + freq /= 16; + + return freq; +} +EXPORT_SYMBOL(mpc52xx_get_xtal_freq); + +/** + * mpc52xx_restart: ppc_md->restart hook for mpc5200 using the watchdog timer + */ +void +mpc52xx_restart(char *cmd) +{ + local_irq_disable(); + + /* Turn on the watchdog and wait for it to expire. + * It effectively does a reset. */ + if (mpc52xx_wdt) { + out_be32(&mpc52xx_wdt->mode, 0x00000000); + out_be32(&mpc52xx_wdt->count, 0x000000ff); + out_be32(&mpc52xx_wdt->mode, 0x00009004); + } else + printk(KERN_ERR __FILE__ ": " + "mpc52xx_restart: Can't access wdt. " + "Restart impossible, system halted.\n"); + + while (1); +} + +#define PSC1_RESET 0x1 +#define PSC1_SYNC 0x4 +#define PSC1_SDATA_OUT 0x1 +#define PSC2_RESET 0x2 +#define PSC2_SYNC (0x4<<4) +#define PSC2_SDATA_OUT (0x1<<4) +#define MPC52xx_GPIO_PSC1_MASK 0x7 +#define MPC52xx_GPIO_PSC2_MASK (0x7<<4) + +/** + * mpc5200_psc_ac97_gpio_reset: Use gpio pins to reset the ac97 bus + * + * @psc: psc number to reset (only psc 1 and 2 support ac97) + */ +int mpc5200_psc_ac97_gpio_reset(int psc_number) +{ + unsigned long flags; + u32 gpio; + u32 mux; + int out; + int reset; + int sync; + + if ((!simple_gpio) || (!wkup_gpio)) + return -ENODEV; + + switch (psc_number) { + case 0: + reset = PSC1_RESET; /* AC97_1_RES */ + sync = PSC1_SYNC; /* AC97_1_SYNC */ + out = PSC1_SDATA_OUT; /* AC97_1_SDATA_OUT */ + gpio = MPC52xx_GPIO_PSC1_MASK; + break; + case 1: + reset = PSC2_RESET; /* AC97_2_RES */ + sync = PSC2_SYNC; /* AC97_2_SYNC */ + out = PSC2_SDATA_OUT; /* AC97_2_SDATA_OUT */ + gpio = MPC52xx_GPIO_PSC2_MASK; + break; + default: + pr_err(__FILE__ ": Unable to determine PSC, no ac97 " + "cold-reset will be performed\n"); + return -ENODEV; + } + + spin_lock_irqsave(&gpio_lock, flags); + + /* Reconfiure pin-muxing to gpio */ + mux = in_be32(&simple_gpio->port_config); + out_be32(&simple_gpio->port_config, mux & (~gpio)); + + /* enable gpio pins for output */ + setbits8(&wkup_gpio->wkup_gpioe, reset); + setbits32(&simple_gpio->simple_gpioe, sync | out); + + setbits8(&wkup_gpio->wkup_ddr, reset); + setbits32(&simple_gpio->simple_ddr, sync | out); + + /* Assert cold reset */ + clrbits32(&simple_gpio->simple_dvo, sync | out); + clrbits8(&wkup_gpio->wkup_dvo, reset); + + /* wait for 1 us */ + udelay(1); + + /* Deassert reset */ + setbits8(&wkup_gpio->wkup_dvo, reset); + + /* wait at least 200ns */ + /* 7 ~= (200ns * timebase) / ns2sec */ + __delay(7); + + /* Restore pin-muxing */ + out_be32(&simple_gpio->port_config, mux); + + spin_unlock_irqrestore(&gpio_lock, flags); + + return 0; +} +EXPORT_SYMBOL(mpc5200_psc_ac97_gpio_reset); diff --git a/arch/powerpc/platforms/52xx/mpc52xx_gpt.c b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c new file mode 100644 index 00000000000..f94f06e5276 --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_gpt.c @@ -0,0 +1,801 @@ +/* + * MPC5200 General Purpose Timer device driver + * + * Copyright (c) 2009 Secret Lab Technologies Ltd. + * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * 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. + * + * This file is a driver for the the General Purpose Timer (gpt) devices + * found on the MPC5200 SoC. Each timer has an IO pin which can be used + * for GPIO or can be used to raise interrupts. The timer function can + * be used independently from the IO pin, or it can be used to control + * output signals or measure input signals. + * + * This driver supports the GPIO and IRQ controller functions of the GPT + * device. Timer functions are not yet supported. + * + * The timer gpt0 can be used as watchdog (wdt). If the wdt mode is used, + * this prevents the use of any gpt0 gpt function (i.e. they will fail with + * -EBUSY). Thus, the safety wdt function always has precedence over the gpt + * function. If the kernel has been compiled with CONFIG_WATCHDOG_NOWAYOUT, + * this means that gpt0 is locked in wdt mode until the next reboot - this + * may be a requirement in safety applications. + * + * To use the GPIO function, the following two properties must be added + * to the device tree node for the gpt device (typically in the .dts file + * for the board): + * gpio-controller; + * #gpio-cells = < 2 >; + * This driver will register the GPIO pin if it finds the gpio-controller + * property in the device tree. + * + * To use the IRQ controller function, the following two properties must + * be added to the device tree node for the gpt device: + * interrupt-controller; + * #interrupt-cells = < 1 >; + * The IRQ controller binding only uses one cell to specify the interrupt, + * and the IRQ flags are encoded in the cell. A cell is not used to encode + * the IRQ number because the GPT only has a single IRQ source. For flags, + * a value of '1' means rising edge sensitive and '2' means falling edge. + * + * The GPIO and the IRQ controller functions can be used at the same time, + * but in this use case the IO line will only work as an input. Trying to + * use it as a GPIO output will not work. + * + * When using the GPIO line as an output, it can either be driven as normal + * IO, or it can be an Open Collector (OC) output. At the moment it is the + * responsibility of either the bootloader or the platform setup code to set + * the output mode. This driver does not change the output mode setting. + */ + +#include <linux/device.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/watchdog.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/module.h> +#include <asm/div64.h> +#include <asm/mpc52xx.h> + +MODULE_DESCRIPTION("Freescale MPC52xx gpt driver"); +MODULE_AUTHOR("Sascha Hauer, Grant Likely, Albrecht Dreß"); +MODULE_LICENSE("GPL"); + +/** + * struct mpc52xx_gpt - Private data structure for MPC52xx GPT driver + * @dev: pointer to device structure + * @regs: virtual address of GPT registers + * @lock: spinlock to coordinate between different functions. + * @gc: gpio_chip instance structure; used when GPIO is enabled + * @irqhost: Pointer to irq_host instance; used when IRQ mode is supported + * @wdt_mode: only relevant for gpt0: bit 0 (MPC52xx_GPT_CAN_WDT) indicates + * if the gpt may be used as wdt, bit 1 (MPC52xx_GPT_IS_WDT) indicates + * if the timer is actively used as wdt which blocks gpt functions + */ +struct mpc52xx_gpt_priv { + struct list_head list; /* List of all GPT devices */ + struct device *dev; + struct mpc52xx_gpt __iomem *regs; + spinlock_t lock; + struct irq_host *irqhost; + u32 ipb_freq; + u8 wdt_mode; + +#if defined(CONFIG_GPIOLIB) + struct gpio_chip gc; +#endif +}; + +LIST_HEAD(mpc52xx_gpt_list); +DEFINE_MUTEX(mpc52xx_gpt_list_mutex); + +#define MPC52xx_GPT_MODE_MS_MASK (0x07) +#define MPC52xx_GPT_MODE_MS_IC (0x01) +#define MPC52xx_GPT_MODE_MS_OC (0x02) +#define MPC52xx_GPT_MODE_MS_PWM (0x03) +#define MPC52xx_GPT_MODE_MS_GPIO (0x04) + +#define MPC52xx_GPT_MODE_GPIO_MASK (0x30) +#define MPC52xx_GPT_MODE_GPIO_OUT_LOW (0x20) +#define MPC52xx_GPT_MODE_GPIO_OUT_HIGH (0x30) + +#define MPC52xx_GPT_MODE_COUNTER_ENABLE (0x1000) +#define MPC52xx_GPT_MODE_CONTINUOUS (0x0400) +#define MPC52xx_GPT_MODE_OPEN_DRAIN (0x0200) +#define MPC52xx_GPT_MODE_IRQ_EN (0x0100) +#define MPC52xx_GPT_MODE_WDT_EN (0x8000) + +#define MPC52xx_GPT_MODE_ICT_MASK (0x030000) +#define MPC52xx_GPT_MODE_ICT_RISING (0x010000) +#define MPC52xx_GPT_MODE_ICT_FALLING (0x020000) +#define MPC52xx_GPT_MODE_ICT_TOGGLE (0x030000) + +#define MPC52xx_GPT_MODE_WDT_PING (0xa5) + +#define MPC52xx_GPT_STATUS_IRQMASK (0x000f) + +#define MPC52xx_GPT_CAN_WDT (1 << 0) +#define MPC52xx_GPT_IS_WDT (1 << 1) + + +/* --------------------------------------------------------------------- + * Cascaded interrupt controller hooks + */ + +static void mpc52xx_gpt_irq_unmask(struct irq_data *d) +{ + struct mpc52xx_gpt_priv *gpt = irq_data_get_irq_chip_data(d); + unsigned long flags; + + spin_lock_irqsave(&gpt->lock, flags); + setbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_IRQ_EN); + spin_unlock_irqrestore(&gpt->lock, flags); +} + +static void mpc52xx_gpt_irq_mask(struct irq_data *d) +{ + struct mpc52xx_gpt_priv *gpt = irq_data_get_irq_chip_data(d); + unsigned long flags; + + spin_lock_irqsave(&gpt->lock, flags); + clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_IRQ_EN); + spin_unlock_irqrestore(&gpt->lock, flags); +} + +static void mpc52xx_gpt_irq_ack(struct irq_data *d) +{ + struct mpc52xx_gpt_priv *gpt = irq_data_get_irq_chip_data(d); + + out_be32(&gpt->regs->status, MPC52xx_GPT_STATUS_IRQMASK); +} + +static int mpc52xx_gpt_irq_set_type(struct irq_data *d, unsigned int flow_type) +{ + struct mpc52xx_gpt_priv *gpt = irq_data_get_irq_chip_data(d); + unsigned long flags; + u32 reg; + + dev_dbg(gpt->dev, "%s: virq=%i type=%x\n", __func__, d->irq, flow_type); + + spin_lock_irqsave(&gpt->lock, flags); + reg = in_be32(&gpt->regs->mode) & ~MPC52xx_GPT_MODE_ICT_MASK; + if (flow_type & IRQF_TRIGGER_RISING) + reg |= MPC52xx_GPT_MODE_ICT_RISING; + if (flow_type & IRQF_TRIGGER_FALLING) + reg |= MPC52xx_GPT_MODE_ICT_FALLING; + out_be32(&gpt->regs->mode, reg); + spin_unlock_irqrestore(&gpt->lock, flags); + + return 0; +} + +static struct irq_chip mpc52xx_gpt_irq_chip = { + .name = "MPC52xx GPT", + .irq_unmask = mpc52xx_gpt_irq_unmask, + .irq_mask = mpc52xx_gpt_irq_mask, + .irq_ack = mpc52xx_gpt_irq_ack, + .irq_set_type = mpc52xx_gpt_irq_set_type, +}; + +void mpc52xx_gpt_irq_cascade(unsigned int virq, struct irq_desc *desc) +{ + struct mpc52xx_gpt_priv *gpt = irq_get_handler_data(virq); + int sub_virq; + u32 status; + + status = in_be32(&gpt->regs->status) & MPC52xx_GPT_STATUS_IRQMASK; + if (status) { + sub_virq = irq_linear_revmap(gpt->irqhost, 0); + generic_handle_irq(sub_virq); + } +} + +static int mpc52xx_gpt_irq_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct mpc52xx_gpt_priv *gpt = h->host_data; + + dev_dbg(gpt->dev, "%s: h=%p, virq=%i\n", __func__, h, virq); + irq_set_chip_data(virq, gpt); + irq_set_chip_and_handler(virq, &mpc52xx_gpt_irq_chip, handle_edge_irq); + + return 0; +} + +static int mpc52xx_gpt_irq_xlate(struct irq_host *h, struct device_node *ct, + const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, + unsigned int *out_flags) +{ + struct mpc52xx_gpt_priv *gpt = h->host_data; + + dev_dbg(gpt->dev, "%s: flags=%i\n", __func__, intspec[0]); + + if ((intsize < 1) || (intspec[0] > 3)) { + dev_err(gpt->dev, "bad irq specifier in %s\n", ct->full_name); + return -EINVAL; + } + + *out_hwirq = 0; /* The GPT only has 1 IRQ line */ + *out_flags = intspec[0]; + + return 0; +} + +static struct irq_host_ops mpc52xx_gpt_irq_ops = { + .map = mpc52xx_gpt_irq_map, + .xlate = mpc52xx_gpt_irq_xlate, +}; + +static void +mpc52xx_gpt_irq_setup(struct mpc52xx_gpt_priv *gpt, struct device_node *node) +{ + int cascade_virq; + unsigned long flags; + u32 mode; + + cascade_virq = irq_of_parse_and_map(node, 0); + if (!cascade_virq) + return; + + gpt->irqhost = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR, 1, + &mpc52xx_gpt_irq_ops, -1); + if (!gpt->irqhost) { + dev_err(gpt->dev, "irq_alloc_host() failed\n"); + return; + } + + gpt->irqhost->host_data = gpt; + irq_set_handler_data(cascade_virq, gpt); + irq_set_chained_handler(cascade_virq, mpc52xx_gpt_irq_cascade); + + /* If the GPT is currently disabled, then change it to be in Input + * Capture mode. If the mode is non-zero, then the pin could be + * already in use for something. */ + spin_lock_irqsave(&gpt->lock, flags); + mode = in_be32(&gpt->regs->mode); + if ((mode & MPC52xx_GPT_MODE_MS_MASK) == 0) + out_be32(&gpt->regs->mode, mode | MPC52xx_GPT_MODE_MS_IC); + spin_unlock_irqrestore(&gpt->lock, flags); + + dev_dbg(gpt->dev, "%s() complete. virq=%i\n", __func__, cascade_virq); +} + + +/* --------------------------------------------------------------------- + * GPIOLIB hooks + */ +#if defined(CONFIG_GPIOLIB) +static inline struct mpc52xx_gpt_priv *gc_to_mpc52xx_gpt(struct gpio_chip *gc) +{ + return container_of(gc, struct mpc52xx_gpt_priv, gc); +} + +static int mpc52xx_gpt_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct mpc52xx_gpt_priv *gpt = gc_to_mpc52xx_gpt(gc); + + return (in_be32(&gpt->regs->status) >> 8) & 1; +} + +static void +mpc52xx_gpt_gpio_set(struct gpio_chip *gc, unsigned int gpio, int v) +{ + struct mpc52xx_gpt_priv *gpt = gc_to_mpc52xx_gpt(gc); + unsigned long flags; + u32 r; + + dev_dbg(gpt->dev, "%s: gpio:%d v:%d\n", __func__, gpio, v); + r = v ? MPC52xx_GPT_MODE_GPIO_OUT_HIGH : MPC52xx_GPT_MODE_GPIO_OUT_LOW; + + spin_lock_irqsave(&gpt->lock, flags); + clrsetbits_be32(&gpt->regs->mode, MPC52xx_GPT_MODE_GPIO_MASK, r); + spin_unlock_irqrestore(&gpt->lock, flags); +} + +static int mpc52xx_gpt_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct mpc52xx_gpt_priv *gpt = gc_to_mpc52xx_gpt(gc); + unsigned long flags; + + dev_dbg(gpt->dev, "%s: gpio:%d\n", __func__, gpio); + + spin_lock_irqsave(&gpt->lock, flags); + clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_GPIO_MASK); + spin_unlock_irqrestore(&gpt->lock, flags); + + return 0; +} + +static int +mpc52xx_gpt_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + mpc52xx_gpt_gpio_set(gc, gpio, val); + return 0; +} + +static void +mpc52xx_gpt_gpio_setup(struct mpc52xx_gpt_priv *gpt, struct device_node *node) +{ + int rc; + + /* Only setup GPIO if the device tree claims the GPT is + * a GPIO controller */ + if (!of_find_property(node, "gpio-controller", NULL)) + return; + + gpt->gc.label = kstrdup(node->full_name, GFP_KERNEL); + if (!gpt->gc.label) { + dev_err(gpt->dev, "out of memory\n"); + return; + } + + gpt->gc.ngpio = 1; + gpt->gc.direction_input = mpc52xx_gpt_gpio_dir_in; + gpt->gc.direction_output = mpc52xx_gpt_gpio_dir_out; + gpt->gc.get = mpc52xx_gpt_gpio_get; + gpt->gc.set = mpc52xx_gpt_gpio_set; + gpt->gc.base = -1; + gpt->gc.of_node = node; + + /* Setup external pin in GPIO mode */ + clrsetbits_be32(&gpt->regs->mode, MPC52xx_GPT_MODE_MS_MASK, + MPC52xx_GPT_MODE_MS_GPIO); + + rc = gpiochip_add(&gpt->gc); + if (rc) + dev_err(gpt->dev, "gpiochip_add() failed; rc=%i\n", rc); + + dev_dbg(gpt->dev, "%s() complete.\n", __func__); +} +#else /* defined(CONFIG_GPIOLIB) */ +static void +mpc52xx_gpt_gpio_setup(struct mpc52xx_gpt_priv *p, struct device_node *np) { } +#endif /* defined(CONFIG_GPIOLIB) */ + +/*********************************************************************** + * Timer API + */ + +/** + * mpc52xx_gpt_from_irq - Return the GPT device associated with an IRQ number + * @irq: irq of timer. + */ +struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq) +{ + struct mpc52xx_gpt_priv *gpt; + struct list_head *pos; + + /* Iterate over the list of timers looking for a matching device */ + mutex_lock(&mpc52xx_gpt_list_mutex); + list_for_each(pos, &mpc52xx_gpt_list) { + gpt = container_of(pos, struct mpc52xx_gpt_priv, list); + if (gpt->irqhost && irq == irq_linear_revmap(gpt->irqhost, 0)) { + mutex_unlock(&mpc52xx_gpt_list_mutex); + return gpt; + } + } + mutex_unlock(&mpc52xx_gpt_list_mutex); + + return NULL; +} +EXPORT_SYMBOL(mpc52xx_gpt_from_irq); + +static int mpc52xx_gpt_do_start(struct mpc52xx_gpt_priv *gpt, u64 period, + int continuous, int as_wdt) +{ + u32 clear, set; + u64 clocks; + u32 prescale; + unsigned long flags; + + clear = MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTINUOUS; + set = MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER_ENABLE; + if (as_wdt) { + clear |= MPC52xx_GPT_MODE_IRQ_EN; + set |= MPC52xx_GPT_MODE_WDT_EN; + } else if (continuous) + set |= MPC52xx_GPT_MODE_CONTINUOUS; + + /* Determine the number of clocks in the requested period. 64 bit + * arithmatic is done here to preserve the precision until the value + * is scaled back down into the u32 range. Period is in 'ns', bus + * frequency is in Hz. */ + clocks = period * (u64)gpt->ipb_freq; + do_div(clocks, 1000000000); /* Scale it down to ns range */ + + /* This device cannot handle a clock count greater than 32 bits */ + if (clocks > 0xffffffff) + return -EINVAL; + + /* Calculate the prescaler and count values from the clocks value. + * 'clocks' is the number of clock ticks in the period. The timer + * has 16 bit precision and a 16 bit prescaler. Prescaler is + * calculated by integer dividing the clocks by 0x10000 (shifting + * down 16 bits) to obtain the smallest possible divisor for clocks + * to get a 16 bit count value. + * + * Note: the prescale register is '1' based, not '0' based. ie. a + * value of '1' means divide the clock by one. 0xffff divides the + * clock by 0xffff. '0x0000' does not divide by zero, but wraps + * around and divides by 0x10000. That is why prescale must be + * a u32 variable, not a u16, for this calculation. */ + prescale = (clocks >> 16) + 1; + do_div(clocks, prescale); + if (clocks > 0xffff) { + pr_err("calculation error; prescale:%x clocks:%llx\n", + prescale, clocks); + return -EINVAL; + } + + /* Set and enable the timer, reject an attempt to use a wdt as gpt */ + spin_lock_irqsave(&gpt->lock, flags); + if (as_wdt) + gpt->wdt_mode |= MPC52xx_GPT_IS_WDT; + else if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT) != 0) { + spin_unlock_irqrestore(&gpt->lock, flags); + return -EBUSY; + } + out_be32(&gpt->regs->count, prescale << 16 | clocks); + clrsetbits_be32(&gpt->regs->mode, clear, set); + spin_unlock_irqrestore(&gpt->lock, flags); + + return 0; +} + +/** + * mpc52xx_gpt_start_timer - Set and enable the GPT timer + * @gpt: Pointer to gpt private data structure + * @period: period of timer in ns; max. ~130s @ 33MHz IPB clock + * @continuous: set to 1 to make timer continuous free running + * + * An interrupt will be generated every time the timer fires + */ +int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period, + int continuous) +{ + return mpc52xx_gpt_do_start(gpt, period, continuous, 0); +} +EXPORT_SYMBOL(mpc52xx_gpt_start_timer); + +/** + * mpc52xx_gpt_stop_timer - Stop a gpt + * @gpt: Pointer to gpt private data structure + * + * Returns an error if attempting to stop a wdt + */ +int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt) +{ + unsigned long flags; + + /* reject the operation if the timer is used as watchdog (gpt 0 only) */ + spin_lock_irqsave(&gpt->lock, flags); + if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT) != 0) { + spin_unlock_irqrestore(&gpt->lock, flags); + return -EBUSY; + } + + clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_COUNTER_ENABLE); + spin_unlock_irqrestore(&gpt->lock, flags); + return 0; +} +EXPORT_SYMBOL(mpc52xx_gpt_stop_timer); + +/** + * mpc52xx_gpt_timer_period - Read the timer period + * @gpt: Pointer to gpt private data structure + * + * Returns the timer period in ns + */ +u64 mpc52xx_gpt_timer_period(struct mpc52xx_gpt_priv *gpt) +{ + u64 period; + u64 prescale; + unsigned long flags; + + spin_lock_irqsave(&gpt->lock, flags); + period = in_be32(&gpt->regs->count); + spin_unlock_irqrestore(&gpt->lock, flags); + + prescale = period >> 16; + period &= 0xffff; + if (prescale == 0) + prescale = 0x10000; + period = period * prescale * 1000000000ULL; + do_div(period, (u64)gpt->ipb_freq); + return period; +} +EXPORT_SYMBOL(mpc52xx_gpt_timer_period); + +#if defined(CONFIG_MPC5200_WDT) +/*********************************************************************** + * Watchdog API for gpt0 + */ + +#define WDT_IDENTITY "mpc52xx watchdog on GPT0" + +/* wdt_is_active stores wether or not the /dev/watchdog device is opened */ +static unsigned long wdt_is_active; + +/* wdt-capable gpt */ +static struct mpc52xx_gpt_priv *mpc52xx_gpt_wdt; + +/* low-level wdt functions */ +static inline void mpc52xx_gpt_wdt_ping(struct mpc52xx_gpt_priv *gpt_wdt) +{ + unsigned long flags; + + spin_lock_irqsave(&gpt_wdt->lock, flags); + out_8((u8 *) &gpt_wdt->regs->mode, MPC52xx_GPT_MODE_WDT_PING); + spin_unlock_irqrestore(&gpt_wdt->lock, flags); +} + +/* wdt misc device api */ +static ssize_t mpc52xx_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + struct mpc52xx_gpt_priv *gpt_wdt = file->private_data; + mpc52xx_gpt_wdt_ping(gpt_wdt); + return 0; +} + +static const struct watchdog_info mpc5200_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, + .identity = WDT_IDENTITY, +}; + +static long mpc52xx_wdt_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct mpc52xx_gpt_priv *gpt_wdt = file->private_data; + int __user *data = (int __user *)arg; + int timeout; + u64 real_timeout; + int ret = 0; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user(data, &mpc5200_wdt_info, + sizeof(mpc5200_wdt_info)); + if (ret) + ret = -EFAULT; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + ret = put_user(0, data); + break; + + case WDIOC_KEEPALIVE: + mpc52xx_gpt_wdt_ping(gpt_wdt); + break; + + case WDIOC_SETTIMEOUT: + ret = get_user(timeout, data); + if (ret) + break; + real_timeout = (u64) timeout * 1000000000ULL; + ret = mpc52xx_gpt_do_start(gpt_wdt, real_timeout, 0, 1); + if (ret) + break; + /* fall through and return the timeout */ + + case WDIOC_GETTIMEOUT: + /* we need to round here as to avoid e.g. the following + * situation: + * - timeout requested is 1 second; + * - real timeout @33MHz is 999997090ns + * - the int divide by 10^9 will return 0. + */ + real_timeout = + mpc52xx_gpt_timer_period(gpt_wdt) + 500000000ULL; + do_div(real_timeout, 1000000000ULL); + timeout = (int) real_timeout; + ret = put_user(timeout, data); + break; + + default: + ret = -ENOTTY; + } + return ret; +} + +static int mpc52xx_wdt_open(struct inode *inode, struct file *file) +{ + int ret; + + /* sanity check */ + if (!mpc52xx_gpt_wdt) + return -ENODEV; + + /* /dev/watchdog can only be opened once */ + if (test_and_set_bit(0, &wdt_is_active)) + return -EBUSY; + + /* Set and activate the watchdog with 30 seconds timeout */ + ret = mpc52xx_gpt_do_start(mpc52xx_gpt_wdt, 30ULL * 1000000000ULL, + 0, 1); + if (ret) { + clear_bit(0, &wdt_is_active); + return ret; + } + + file->private_data = mpc52xx_gpt_wdt; + return nonseekable_open(inode, file); +} + +static int mpc52xx_wdt_release(struct inode *inode, struct file *file) +{ + /* note: releasing the wdt in NOWAYOUT-mode does not stop it */ +#if !defined(CONFIG_WATCHDOG_NOWAYOUT) + struct mpc52xx_gpt_priv *gpt_wdt = file->private_data; + unsigned long flags; + + spin_lock_irqsave(&gpt_wdt->lock, flags); + clrbits32(&gpt_wdt->regs->mode, + MPC52xx_GPT_MODE_COUNTER_ENABLE | MPC52xx_GPT_MODE_WDT_EN); + gpt_wdt->wdt_mode &= ~MPC52xx_GPT_IS_WDT; + spin_unlock_irqrestore(&gpt_wdt->lock, flags); +#endif + clear_bit(0, &wdt_is_active); + return 0; +} + + +static const struct file_operations mpc52xx_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = mpc52xx_wdt_write, + .unlocked_ioctl = mpc52xx_wdt_ioctl, + .open = mpc52xx_wdt_open, + .release = mpc52xx_wdt_release, +}; + +static struct miscdevice mpc52xx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &mpc52xx_wdt_fops, +}; + +static int __devinit mpc52xx_gpt_wdt_init(void) +{ + int err; + + /* try to register the watchdog misc device */ + err = misc_register(&mpc52xx_wdt_miscdev); + if (err) + pr_err("%s: cannot register watchdog device\n", WDT_IDENTITY); + else + pr_info("%s: watchdog device registered\n", WDT_IDENTITY); + return err; +} + +static int mpc52xx_gpt_wdt_setup(struct mpc52xx_gpt_priv *gpt, + const u32 *period) +{ + u64 real_timeout; + + /* remember the gpt for the wdt operation */ + mpc52xx_gpt_wdt = gpt; + + /* configure the wdt if the device tree contained a timeout */ + if (!period || *period == 0) + return 0; + + real_timeout = (u64) *period * 1000000000ULL; + if (mpc52xx_gpt_do_start(gpt, real_timeout, 0, 1)) + dev_warn(gpt->dev, "starting as wdt failed\n"); + else + dev_info(gpt->dev, "watchdog set to %us timeout\n", *period); + return 0; +} + +#else + +static int __devinit mpc52xx_gpt_wdt_init(void) +{ + return 0; +} + +static inline int mpc52xx_gpt_wdt_setup(struct mpc52xx_gpt_priv *gpt, + const u32 *period) +{ + return 0; +} + +#endif /* CONFIG_MPC5200_WDT */ + +/* --------------------------------------------------------------------- + * of_platform bus binding code + */ +static int __devinit mpc52xx_gpt_probe(struct platform_device *ofdev) +{ + struct mpc52xx_gpt_priv *gpt; + + gpt = kzalloc(sizeof *gpt, GFP_KERNEL); + if (!gpt) + return -ENOMEM; + + spin_lock_init(&gpt->lock); + gpt->dev = &ofdev->dev; + gpt->ipb_freq = mpc5xxx_get_bus_frequency(ofdev->dev.of_node); + gpt->regs = of_iomap(ofdev->dev.of_node, 0); + if (!gpt->regs) { + kfree(gpt); + return -ENOMEM; + } + + dev_set_drvdata(&ofdev->dev, gpt); + + mpc52xx_gpt_gpio_setup(gpt, ofdev->dev.of_node); + mpc52xx_gpt_irq_setup(gpt, ofdev->dev.of_node); + + mutex_lock(&mpc52xx_gpt_list_mutex); + list_add(&gpt->list, &mpc52xx_gpt_list); + mutex_unlock(&mpc52xx_gpt_list_mutex); + + /* check if this device could be a watchdog */ + if (of_get_property(ofdev->dev.of_node, "fsl,has-wdt", NULL) || + of_get_property(ofdev->dev.of_node, "has-wdt", NULL)) { + const u32 *on_boot_wdt; + + gpt->wdt_mode = MPC52xx_GPT_CAN_WDT; + on_boot_wdt = of_get_property(ofdev->dev.of_node, + "fsl,wdt-on-boot", NULL); + if (on_boot_wdt) { + dev_info(gpt->dev, "used as watchdog\n"); + gpt->wdt_mode |= MPC52xx_GPT_IS_WDT; + } else + dev_info(gpt->dev, "can function as watchdog\n"); + mpc52xx_gpt_wdt_setup(gpt, on_boot_wdt); + } + + return 0; +} + +static int mpc52xx_gpt_remove(struct platform_device *ofdev) +{ + return -EBUSY; +} + +static const struct of_device_id mpc52xx_gpt_match[] = { + { .compatible = "fsl,mpc5200-gpt", }, + + /* Depreciated compatible values; don't use for new dts files */ + { .compatible = "fsl,mpc5200-gpt-gpio", }, + { .compatible = "mpc5200-gpt", }, + {} +}; + +static struct platform_driver mpc52xx_gpt_driver = { + .driver = { + .name = "mpc52xx-gpt", + .owner = THIS_MODULE, + .of_match_table = mpc52xx_gpt_match, + }, + .probe = mpc52xx_gpt_probe, + .remove = mpc52xx_gpt_remove, +}; + +static int __init mpc52xx_gpt_init(void) +{ + return platform_driver_register(&mpc52xx_gpt_driver); +} + +/* Make sure GPIOs and IRQs get set up before anyone tries to use them */ +subsys_initcall(mpc52xx_gpt_init); +device_initcall(mpc52xx_gpt_wdt_init); diff --git a/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c new file mode 100644 index 00000000000..d61fb1c0c1a --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_lpbfifo.c @@ -0,0 +1,562 @@ +/* + * LocalPlus Bus FIFO driver for the Freescale MPC52xx. + * + * Copyright (C) 2009 Secret Lab Technologies Ltd. + * + * This file is released under the GPLv2 + * + * Todo: + * - Add support for multiple requests to be queued. + */ + +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/spinlock.h> +#include <linux/module.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/mpc52xx.h> +#include <asm/time.h> + +#include <sysdev/bestcomm/bestcomm.h> +#include <sysdev/bestcomm/bestcomm_priv.h> +#include <sysdev/bestcomm/gen_bd.h> + +MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); +MODULE_DESCRIPTION("MPC5200 LocalPlus FIFO device driver"); +MODULE_LICENSE("GPL"); + +#define LPBFIFO_REG_PACKET_SIZE (0x00) +#define LPBFIFO_REG_START_ADDRESS (0x04) +#define LPBFIFO_REG_CONTROL (0x08) +#define LPBFIFO_REG_ENABLE (0x0C) +#define LPBFIFO_REG_BYTES_DONE_STATUS (0x14) +#define LPBFIFO_REG_FIFO_DATA (0x40) +#define LPBFIFO_REG_FIFO_STATUS (0x44) +#define LPBFIFO_REG_FIFO_CONTROL (0x48) +#define LPBFIFO_REG_FIFO_ALARM (0x4C) + +struct mpc52xx_lpbfifo { + struct device *dev; + phys_addr_t regs_phys; + void __iomem *regs; + int irq; + spinlock_t lock; + + struct bcom_task *bcom_tx_task; + struct bcom_task *bcom_rx_task; + struct bcom_task *bcom_cur_task; + + /* Current state data */ + struct mpc52xx_lpbfifo_request *req; + int dma_irqs_enabled; +}; + +/* The MPC5200 has only one fifo, so only need one instance structure */ +static struct mpc52xx_lpbfifo lpbfifo; + +/** + * mpc52xx_lpbfifo_kick - Trigger the next block of data to be transferred + */ +static void mpc52xx_lpbfifo_kick(struct mpc52xx_lpbfifo_request *req) +{ + size_t transfer_size = req->size - req->pos; + struct bcom_bd *bd; + void __iomem *reg; + u32 *data; + int i; + int bit_fields; + int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA); + int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE; + int poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA; + + /* Set and clear the reset bits; is good practice in User Manual */ + out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); + + /* set master enable bit */ + out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000001); + if (!dma) { + /* While the FIFO can be setup for transfer sizes as large as + * 16M-1, the FIFO itself is only 512 bytes deep and it does + * not generate interrupts for FIFO full events (only transfer + * complete will raise an IRQ). Therefore when not using + * Bestcomm to drive the FIFO it needs to either be polled, or + * transfers need to constrained to the size of the fifo. + * + * This driver restricts the size of the transfer + */ + if (transfer_size > 512) + transfer_size = 512; + + /* Load the FIFO with data */ + if (write) { + reg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA; + data = req->data + req->pos; + for (i = 0; i < transfer_size; i += 4) + out_be32(reg, *data++); + } + + /* Unmask both error and completion irqs */ + out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x00000301); + } else { + /* Choose the correct direction + * + * Configure the watermarks so DMA will always complete correctly. + * It may be worth experimenting with the ALARM value to see if + * there is a performance impacit. However, if it is wrong there + * is a risk of DMA not transferring the last chunk of data + */ + if (write) { + out_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1e4); + out_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 7); + lpbfifo.bcom_cur_task = lpbfifo.bcom_tx_task; + } else { + out_be32(lpbfifo.regs + LPBFIFO_REG_FIFO_ALARM, 0x1ff); + out_8(lpbfifo.regs + LPBFIFO_REG_FIFO_CONTROL, 0); + lpbfifo.bcom_cur_task = lpbfifo.bcom_rx_task; + + if (poll_dma) { + if (lpbfifo.dma_irqs_enabled) { + disable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task)); + lpbfifo.dma_irqs_enabled = 0; + } + } else { + if (!lpbfifo.dma_irqs_enabled) { + enable_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task)); + lpbfifo.dma_irqs_enabled = 1; + } + } + } + + bd = bcom_prepare_next_buffer(lpbfifo.bcom_cur_task); + bd->status = transfer_size; + if (!write) { + /* + * In the DMA read case, the DMA doesn't complete, + * possibly due to incorrect watermarks in the ALARM + * and CONTROL regs. For now instead of trying to + * determine the right watermarks that will make this + * work, just increase the number of bytes the FIFO is + * expecting. + * + * When submitting another operation, the FIFO will get + * reset, so the condition of the FIFO waiting for a + * non-existent 4 bytes will get cleared. + */ + transfer_size += 4; /* BLECH! */ + } + bd->data[0] = req->data_phys + req->pos; + bcom_submit_next_buffer(lpbfifo.bcom_cur_task, NULL); + + /* error irq & master enabled bit */ + bit_fields = 0x00000201; + + /* Unmask irqs */ + if (write && (!poll_dma)) + bit_fields |= 0x00000100; /* completion irq too */ + out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, bit_fields); + } + + /* Set transfer size, width, chip select and READ mode */ + out_be32(lpbfifo.regs + LPBFIFO_REG_START_ADDRESS, + req->offset + req->pos); + out_be32(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, transfer_size); + + bit_fields = req->cs << 24 | 0x000008; + if (!write) + bit_fields |= 0x010000; /* read mode */ + out_be32(lpbfifo.regs + LPBFIFO_REG_CONTROL, bit_fields); + + /* Kick it off */ + out_8(lpbfifo.regs + LPBFIFO_REG_PACKET_SIZE, 0x01); + if (dma) + bcom_enable(lpbfifo.bcom_cur_task); +} + +/** + * mpc52xx_lpbfifo_irq - IRQ handler for LPB FIFO + * + * On transmit, the dma completion irq triggers before the fifo completion + * triggers. Handle the dma completion here instead of the LPB FIFO Bestcomm + * task completion irq because everything is not really done until the LPB FIFO + * completion irq triggers. + * + * In other words: + * For DMA, on receive, the "Fat Lady" is the bestcom completion irq. on + * transmit, the fifo completion irq is the "Fat Lady". The opera (or in this + * case the DMA/FIFO operation) is not finished until the "Fat Lady" sings. + * + * Reasons for entering this routine: + * 1) PIO mode rx and tx completion irq + * 2) DMA interrupt mode tx completion irq + * 3) DMA polled mode tx + * + * Exit conditions: + * 1) Transfer aborted + * 2) FIFO complete without DMA; more data to do + * 3) FIFO complete without DMA; all data transferred + * 4) FIFO complete using DMA + * + * Condition 1 can occur regardless of whether or not DMA is used. + * It requires executing the callback to report the error and exiting + * immediately. + * + * Condition 2 requires programming the FIFO with the next block of data + * + * Condition 3 requires executing the callback to report completion + * + * Condition 4 means the same as 3, except that we also retrieve the bcom + * buffer so DMA doesn't get clogged up. + * + * To make things trickier, the spinlock must be dropped before + * executing the callback, otherwise we could end up with a deadlock + * or nested spinlock condition. The out path is non-trivial, so + * extra fiddling is done to make sure all paths lead to the same + * outbound code. + */ +static irqreturn_t mpc52xx_lpbfifo_irq(int irq, void *dev_id) +{ + struct mpc52xx_lpbfifo_request *req; + u32 status = in_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS); + void __iomem *reg; + u32 *data; + int count, i; + int do_callback = 0; + u32 ts; + unsigned long flags; + int dma, write, poll_dma; + + spin_lock_irqsave(&lpbfifo.lock, flags); + ts = get_tbl(); + + req = lpbfifo.req; + if (!req) { + spin_unlock_irqrestore(&lpbfifo.lock, flags); + pr_err("bogus LPBFIFO IRQ\n"); + return IRQ_HANDLED; + } + + dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA); + write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE; + poll_dma = req->flags & MPC52XX_LPBFIFO_FLAG_POLL_DMA; + + if (dma && !write) { + spin_unlock_irqrestore(&lpbfifo.lock, flags); + pr_err("bogus LPBFIFO IRQ (dma and not writting)\n"); + return IRQ_HANDLED; + } + + if ((status & 0x01) == 0) { + goto out; + } + + /* check abort bit */ + if (status & 0x10) { + out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); + do_callback = 1; + goto out; + } + + /* Read result from hardware */ + count = in_be32(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS); + count &= 0x00ffffff; + + if (!dma && !write) { + /* copy the data out of the FIFO */ + reg = lpbfifo.regs + LPBFIFO_REG_FIFO_DATA; + data = req->data + req->pos; + for (i = 0; i < count; i += 4) + *data++ = in_be32(reg); + } + + /* Update transfer position and count */ + req->pos += count; + + /* Decide what to do next */ + if (req->size - req->pos) + mpc52xx_lpbfifo_kick(req); /* more work to do */ + else + do_callback = 1; + + out: + /* Clear the IRQ */ + out_8(lpbfifo.regs + LPBFIFO_REG_BYTES_DONE_STATUS, 0x01); + + if (dma && (status & 0x11)) { + /* + * Count the DMA as complete only when the FIFO completion + * status or abort bits are set. + * + * (status & 0x01) should always be the case except sometimes + * when using polled DMA. + * + * (status & 0x10) {transfer aborted}: This case needs more + * testing. + */ + bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL); + } + req->last_byte = ((u8 *)req->data)[req->size - 1]; + + /* When the do_callback flag is set; it means the transfer is finished + * so set the FIFO as idle */ + if (do_callback) + lpbfifo.req = NULL; + + if (irq != 0) /* don't increment on polled case */ + req->irq_count++; + + req->irq_ticks += get_tbl() - ts; + spin_unlock_irqrestore(&lpbfifo.lock, flags); + + /* Spinlock is released; it is now safe to call the callback */ + if (do_callback && req->callback) + req->callback(req); + + return IRQ_HANDLED; +} + +/** + * mpc52xx_lpbfifo_bcom_irq - IRQ handler for LPB FIFO Bestcomm task + * + * Only used when receiving data. + */ +static irqreturn_t mpc52xx_lpbfifo_bcom_irq(int irq, void *dev_id) +{ + struct mpc52xx_lpbfifo_request *req; + unsigned long flags; + u32 status; + u32 ts; + + spin_lock_irqsave(&lpbfifo.lock, flags); + ts = get_tbl(); + + req = lpbfifo.req; + if (!req || (req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA)) { + spin_unlock_irqrestore(&lpbfifo.lock, flags); + return IRQ_HANDLED; + } + + if (irq != 0) /* don't increment on polled case */ + req->irq_count++; + + if (!bcom_buffer_done(lpbfifo.bcom_cur_task)) { + spin_unlock_irqrestore(&lpbfifo.lock, flags); + + req->buffer_not_done_cnt++; + if ((req->buffer_not_done_cnt % 1000) == 0) + pr_err("transfer stalled\n"); + + return IRQ_HANDLED; + } + + bcom_retrieve_buffer(lpbfifo.bcom_cur_task, &status, NULL); + + req->last_byte = ((u8 *)req->data)[req->size - 1]; + + req->pos = status & 0x00ffffff; + + /* Mark the FIFO as idle */ + lpbfifo.req = NULL; + + /* Release the lock before calling out to the callback. */ + req->irq_ticks += get_tbl() - ts; + spin_unlock_irqrestore(&lpbfifo.lock, flags); + + if (req->callback) + req->callback(req); + + return IRQ_HANDLED; +} + +/** + * mpc52xx_lpbfifo_bcom_poll - Poll for DMA completion + */ +void mpc52xx_lpbfifo_poll(void) +{ + struct mpc52xx_lpbfifo_request *req = lpbfifo.req; + int dma = !(req->flags & MPC52XX_LPBFIFO_FLAG_NO_DMA); + int write = req->flags & MPC52XX_LPBFIFO_FLAG_WRITE; + + /* + * For more information, see comments on the "Fat Lady" + */ + if (dma && write) + mpc52xx_lpbfifo_irq(0, NULL); + else + mpc52xx_lpbfifo_bcom_irq(0, NULL); +} +EXPORT_SYMBOL(mpc52xx_lpbfifo_poll); + +/** + * mpc52xx_lpbfifo_submit - Submit an LPB FIFO transfer request. + * @req: Pointer to request structure + */ +int mpc52xx_lpbfifo_submit(struct mpc52xx_lpbfifo_request *req) +{ + unsigned long flags; + + if (!lpbfifo.regs) + return -ENODEV; + + spin_lock_irqsave(&lpbfifo.lock, flags); + + /* If the req pointer is already set, then a transfer is in progress */ + if (lpbfifo.req) { + spin_unlock_irqrestore(&lpbfifo.lock, flags); + return -EBUSY; + } + + /* Setup the transfer */ + lpbfifo.req = req; + req->irq_count = 0; + req->irq_ticks = 0; + req->buffer_not_done_cnt = 0; + req->pos = 0; + + mpc52xx_lpbfifo_kick(req); + spin_unlock_irqrestore(&lpbfifo.lock, flags); + return 0; +} +EXPORT_SYMBOL(mpc52xx_lpbfifo_submit); + +void mpc52xx_lpbfifo_abort(struct mpc52xx_lpbfifo_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&lpbfifo.lock, flags); + if (lpbfifo.req == req) { + /* Put it into reset and clear the state */ + bcom_gen_bd_rx_reset(lpbfifo.bcom_rx_task); + bcom_gen_bd_tx_reset(lpbfifo.bcom_tx_task); + out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); + lpbfifo.req = NULL; + } + spin_unlock_irqrestore(&lpbfifo.lock, flags); +} +EXPORT_SYMBOL(mpc52xx_lpbfifo_abort); + +static int __devinit mpc52xx_lpbfifo_probe(struct platform_device *op) +{ + struct resource res; + int rc = -ENOMEM; + + if (lpbfifo.dev != NULL) + return -ENOSPC; + + lpbfifo.irq = irq_of_parse_and_map(op->dev.of_node, 0); + if (!lpbfifo.irq) + return -ENODEV; + + if (of_address_to_resource(op->dev.of_node, 0, &res)) + return -ENODEV; + lpbfifo.regs_phys = res.start; + lpbfifo.regs = of_iomap(op->dev.of_node, 0); + if (!lpbfifo.regs) + return -ENOMEM; + + spin_lock_init(&lpbfifo.lock); + + /* Put FIFO into reset */ + out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); + + /* Register the interrupt handler */ + rc = request_irq(lpbfifo.irq, mpc52xx_lpbfifo_irq, 0, + "mpc52xx-lpbfifo", &lpbfifo); + if (rc) + goto err_irq; + + /* Request the Bestcomm receive (fifo --> memory) task and IRQ */ + lpbfifo.bcom_rx_task = + bcom_gen_bd_rx_init(2, res.start + LPBFIFO_REG_FIFO_DATA, + BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC, + 16*1024*1024); + if (!lpbfifo.bcom_rx_task) + goto err_bcom_rx; + + rc = request_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), + mpc52xx_lpbfifo_bcom_irq, 0, + "mpc52xx-lpbfifo-rx", &lpbfifo); + if (rc) + goto err_bcom_rx_irq; + + lpbfifo.dma_irqs_enabled = 1; + + /* Request the Bestcomm transmit (memory --> fifo) task and IRQ */ + lpbfifo.bcom_tx_task = + bcom_gen_bd_tx_init(2, res.start + LPBFIFO_REG_FIFO_DATA, + BCOM_INITIATOR_SCLPC, BCOM_IPR_SCLPC); + if (!lpbfifo.bcom_tx_task) + goto err_bcom_tx; + + lpbfifo.dev = &op->dev; + return 0; + + err_bcom_tx: + free_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo); + err_bcom_rx_irq: + bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task); + err_bcom_rx: + err_irq: + iounmap(lpbfifo.regs); + lpbfifo.regs = NULL; + + dev_err(&op->dev, "mpc52xx_lpbfifo_probe() failed\n"); + return -ENODEV; +} + + +static int __devexit mpc52xx_lpbfifo_remove(struct platform_device *op) +{ + if (lpbfifo.dev != &op->dev) + return 0; + + /* Put FIFO in reset */ + out_be32(lpbfifo.regs + LPBFIFO_REG_ENABLE, 0x01010000); + + /* Release the bestcomm transmit task */ + free_irq(bcom_get_task_irq(lpbfifo.bcom_tx_task), &lpbfifo); + bcom_gen_bd_tx_release(lpbfifo.bcom_tx_task); + + /* Release the bestcomm receive task */ + free_irq(bcom_get_task_irq(lpbfifo.bcom_rx_task), &lpbfifo); + bcom_gen_bd_rx_release(lpbfifo.bcom_rx_task); + + free_irq(lpbfifo.irq, &lpbfifo); + iounmap(lpbfifo.regs); + lpbfifo.regs = NULL; + lpbfifo.dev = NULL; + + return 0; +} + +static struct of_device_id mpc52xx_lpbfifo_match[] __devinitconst = { + { .compatible = "fsl,mpc5200-lpbfifo", }, + {}, +}; + +static struct platform_driver mpc52xx_lpbfifo_driver = { + .driver = { + .name = "mpc52xx-lpbfifo", + .owner = THIS_MODULE, + .of_match_table = mpc52xx_lpbfifo_match, + }, + .probe = mpc52xx_lpbfifo_probe, + .remove = __devexit_p(mpc52xx_lpbfifo_remove), +}; + +/*********************************************************************** + * Module init/exit + */ +static int __init mpc52xx_lpbfifo_init(void) +{ + return platform_driver_register(&mpc52xx_lpbfifo_driver); +} +module_init(mpc52xx_lpbfifo_init); + +static void __exit mpc52xx_lpbfifo_exit(void) +{ + platform_driver_unregister(&mpc52xx_lpbfifo_driver); +} +module_exit(mpc52xx_lpbfifo_exit); diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pci.c b/arch/powerpc/platforms/52xx/mpc52xx_pci.c new file mode 100644 index 00000000000..bfb11e01133 --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_pci.c @@ -0,0 +1,428 @@ +/* + * PCI code for the Freescale MPC52xx embedded CPU. + * + * Copyright (C) 2006 Secret Lab Technologies Ltd. + * Grant Likely <grant.likely@secretlab.ca> + * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#undef DEBUG + +#include <asm/pci.h> +#include <asm/mpc52xx.h> +#include <asm/delay.h> +#include <asm/machdep.h> +#include <linux/kernel.h> + + +/* ======================================================================== */ +/* Structures mapping & Defines for PCI Unit */ +/* ======================================================================== */ + +#define MPC52xx_PCI_GSCR_BM 0x40000000 +#define MPC52xx_PCI_GSCR_PE 0x20000000 +#define MPC52xx_PCI_GSCR_SE 0x10000000 +#define MPC52xx_PCI_GSCR_XLB2PCI_MASK 0x07000000 +#define MPC52xx_PCI_GSCR_XLB2PCI_SHIFT 24 +#define MPC52xx_PCI_GSCR_IPG2PCI_MASK 0x00070000 +#define MPC52xx_PCI_GSCR_IPG2PCI_SHIFT 16 +#define MPC52xx_PCI_GSCR_BME 0x00004000 +#define MPC52xx_PCI_GSCR_PEE 0x00002000 +#define MPC52xx_PCI_GSCR_SEE 0x00001000 +#define MPC52xx_PCI_GSCR_PR 0x00000001 + + +#define MPC52xx_PCI_IWBTAR_TRANSLATION(proc_ad,pci_ad,size) \ + ( ( (proc_ad) & 0xff000000 ) | \ + ( (((size) - 1) >> 8) & 0x00ff0000 ) | \ + ( ((pci_ad) >> 16) & 0x0000ff00 ) ) + +#define MPC52xx_PCI_IWCR_PACK(win0,win1,win2) (((win0) << 24) | \ + ((win1) << 16) | \ + ((win2) << 8)) + +#define MPC52xx_PCI_IWCR_DISABLE 0x0 +#define MPC52xx_PCI_IWCR_ENABLE 0x1 +#define MPC52xx_PCI_IWCR_READ 0x0 +#define MPC52xx_PCI_IWCR_READ_LINE 0x2 +#define MPC52xx_PCI_IWCR_READ_MULTI 0x4 +#define MPC52xx_PCI_IWCR_MEM 0x0 +#define MPC52xx_PCI_IWCR_IO 0x8 + +#define MPC52xx_PCI_TCR_P 0x01000000 +#define MPC52xx_PCI_TCR_LD 0x00010000 +#define MPC52xx_PCI_TCR_WCT8 0x00000008 + +#define MPC52xx_PCI_TBATR_DISABLE 0x0 +#define MPC52xx_PCI_TBATR_ENABLE 0x1 + +struct mpc52xx_pci { + u32 idr; /* PCI + 0x00 */ + u32 scr; /* PCI + 0x04 */ + u32 ccrir; /* PCI + 0x08 */ + u32 cr1; /* PCI + 0x0C */ + u32 bar0; /* PCI + 0x10 */ + u32 bar1; /* PCI + 0x14 */ + u8 reserved1[16]; /* PCI + 0x18 */ + u32 ccpr; /* PCI + 0x28 */ + u32 sid; /* PCI + 0x2C */ + u32 erbar; /* PCI + 0x30 */ + u32 cpr; /* PCI + 0x34 */ + u8 reserved2[4]; /* PCI + 0x38 */ + u32 cr2; /* PCI + 0x3C */ + u8 reserved3[32]; /* PCI + 0x40 */ + u32 gscr; /* PCI + 0x60 */ + u32 tbatr0; /* PCI + 0x64 */ + u32 tbatr1; /* PCI + 0x68 */ + u32 tcr; /* PCI + 0x6C */ + u32 iw0btar; /* PCI + 0x70 */ + u32 iw1btar; /* PCI + 0x74 */ + u32 iw2btar; /* PCI + 0x78 */ + u8 reserved4[4]; /* PCI + 0x7C */ + u32 iwcr; /* PCI + 0x80 */ + u32 icr; /* PCI + 0x84 */ + u32 isr; /* PCI + 0x88 */ + u32 arb; /* PCI + 0x8C */ + u8 reserved5[104]; /* PCI + 0x90 */ + u32 car; /* PCI + 0xF8 */ + u8 reserved6[4]; /* PCI + 0xFC */ +}; + +/* MPC5200 device tree match tables */ +const struct of_device_id mpc52xx_pci_ids[] __initdata = { + { .type = "pci", .compatible = "fsl,mpc5200-pci", }, + { .type = "pci", .compatible = "mpc5200-pci", }, + {} +}; + +/* ======================================================================== */ +/* PCI configuration access */ +/* ======================================================================== */ + +static int +mpc52xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 *val) +{ + struct pci_controller *hose = pci_bus_to_host(bus); + u32 value; + + if (ppc_md.pci_exclude_device) + if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + out_be32(hose->cfg_addr, + (1 << 31) | + (bus->number << 16) | + (devfn << 8) | + (offset & 0xfc)); + mb(); + +#if defined(CONFIG_PPC_MPC5200_BUGFIX) + if (bus->number) { + /* workaround for the bug 435 of the MPC5200 (L25R); + * Don't do 32 bits config access during type-1 cycles */ + switch (len) { + case 1: + value = in_8(((u8 __iomem *)hose->cfg_data) + + (offset & 3)); + break; + case 2: + value = in_le16(((u16 __iomem *)hose->cfg_data) + + ((offset>>1) & 1)); + break; + + default: + value = in_le16((u16 __iomem *)hose->cfg_data) | + (in_le16(((u16 __iomem *)hose->cfg_data) + 1) << 16); + break; + } + } + else +#endif + { + value = in_le32(hose->cfg_data); + + if (len != 4) { + value >>= ((offset & 0x3) << 3); + value &= 0xffffffff >> (32 - (len << 3)); + } + } + + *val = value; + + out_be32(hose->cfg_addr, 0); + mb(); + + return PCIBIOS_SUCCESSFUL; +} + +static int +mpc52xx_pci_write_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 val) +{ + struct pci_controller *hose = pci_bus_to_host(bus); + u32 value, mask; + + if (ppc_md.pci_exclude_device) + if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + out_be32(hose->cfg_addr, + (1 << 31) | + (bus->number << 16) | + (devfn << 8) | + (offset & 0xfc)); + mb(); + +#if defined(CONFIG_PPC_MPC5200_BUGFIX) + if (bus->number) { + /* workaround for the bug 435 of the MPC5200 (L25R); + * Don't do 32 bits config access during type-1 cycles */ + switch (len) { + case 1: + out_8(((u8 __iomem *)hose->cfg_data) + + (offset & 3), val); + break; + case 2: + out_le16(((u16 __iomem *)hose->cfg_data) + + ((offset>>1) & 1), val); + break; + + default: + out_le16((u16 __iomem *)hose->cfg_data, + (u16)val); + out_le16(((u16 __iomem *)hose->cfg_data) + 1, + (u16)(val>>16)); + break; + } + } + else +#endif + { + if (len != 4) { + value = in_le32(hose->cfg_data); + + offset = (offset & 0x3) << 3; + mask = (0xffffffff >> (32 - (len << 3))); + mask <<= offset; + + value &= ~mask; + val = value | ((val << offset) & mask); + } + + out_le32(hose->cfg_data, val); + } + mb(); + + out_be32(hose->cfg_addr, 0); + mb(); + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops mpc52xx_pci_ops = { + .read = mpc52xx_pci_read_config, + .write = mpc52xx_pci_write_config +}; + + +/* ======================================================================== */ +/* PCI setup */ +/* ======================================================================== */ + +static void __init +mpc52xx_pci_setup(struct pci_controller *hose, + struct mpc52xx_pci __iomem *pci_regs, phys_addr_t pci_phys) +{ + struct resource *res; + u32 tmp; + int iwcr0 = 0, iwcr1 = 0, iwcr2 = 0; + + pr_debug("mpc52xx_pci_setup(hose=%p, pci_regs=%p)\n", hose, pci_regs); + + /* pci_process_bridge_OF_ranges() found all our addresses for us; + * now store them in the right places */ + hose->cfg_addr = &pci_regs->car; + hose->cfg_data = hose->io_base_virt; + + /* Control regs */ + tmp = in_be32(&pci_regs->scr); + tmp |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + out_be32(&pci_regs->scr, tmp); + + /* Memory windows */ + res = &hose->mem_resources[0]; + if (res->flags) { + pr_debug("mem_resource[0] = " + "{.start=%llx, .end=%llx, .flags=%llx}\n", + (unsigned long long)res->start, + (unsigned long long)res->end, + (unsigned long long)res->flags); + out_be32(&pci_regs->iw0btar, + MPC52xx_PCI_IWBTAR_TRANSLATION(res->start, res->start, + resource_size(res))); + iwcr0 = MPC52xx_PCI_IWCR_ENABLE | MPC52xx_PCI_IWCR_MEM; + if (res->flags & IORESOURCE_PREFETCH) + iwcr0 |= MPC52xx_PCI_IWCR_READ_MULTI; + else + iwcr0 |= MPC52xx_PCI_IWCR_READ; + } + + res = &hose->mem_resources[1]; + if (res->flags) { + pr_debug("mem_resource[1] = {.start=%x, .end=%x, .flags=%lx}\n", + res->start, res->end, res->flags); + out_be32(&pci_regs->iw1btar, + MPC52xx_PCI_IWBTAR_TRANSLATION(res->start, res->start, + resource_size(res))); + iwcr1 = MPC52xx_PCI_IWCR_ENABLE | MPC52xx_PCI_IWCR_MEM; + if (res->flags & IORESOURCE_PREFETCH) + iwcr1 |= MPC52xx_PCI_IWCR_READ_MULTI; + else + iwcr1 |= MPC52xx_PCI_IWCR_READ; + } + + /* IO resources */ + res = &hose->io_resource; + if (!res) { + printk(KERN_ERR "%s: Didn't find IO resources\n", __FILE__); + return; + } + pr_debug(".io_resource={.start=%llx,.end=%llx,.flags=%llx} " + ".io_base_phys=0x%p\n", + (unsigned long long)res->start, + (unsigned long long)res->end, + (unsigned long long)res->flags, (void*)hose->io_base_phys); + out_be32(&pci_regs->iw2btar, + MPC52xx_PCI_IWBTAR_TRANSLATION(hose->io_base_phys, + res->start, + resource_size(res))); + iwcr2 = MPC52xx_PCI_IWCR_ENABLE | MPC52xx_PCI_IWCR_IO; + + /* Set all the IWCR fields at once; they're in the same reg */ + out_be32(&pci_regs->iwcr, MPC52xx_PCI_IWCR_PACK(iwcr0, iwcr1, iwcr2)); + + /* Map IMMR onto PCI bus */ + pci_phys &= 0xfffc0000; /* bar0 has only 14 significant bits */ + out_be32(&pci_regs->tbatr0, MPC52xx_PCI_TBATR_ENABLE | pci_phys); + out_be32(&pci_regs->bar0, PCI_BASE_ADDRESS_MEM_PREFETCH | pci_phys); + + /* Map memory onto PCI bus */ + out_be32(&pci_regs->tbatr1, MPC52xx_PCI_TBATR_ENABLE); + out_be32(&pci_regs->bar1, PCI_BASE_ADDRESS_MEM_PREFETCH); + + out_be32(&pci_regs->tcr, MPC52xx_PCI_TCR_LD | MPC52xx_PCI_TCR_WCT8); + + tmp = in_be32(&pci_regs->gscr); +#if 0 + /* Reset the exteral bus ( internal PCI controller is NOT resetted ) */ + /* Not necessary and can be a bad thing if for example the bootloader + is displaying a splash screen or ... Just left here for + documentation purpose if anyone need it */ + out_be32(&pci_regs->gscr, tmp | MPC52xx_PCI_GSCR_PR); + udelay(50); +#endif + + /* Make sure the PCI bridge is out of reset */ + out_be32(&pci_regs->gscr, tmp & ~MPC52xx_PCI_GSCR_PR); +} + +static void +mpc52xx_pci_fixup_resources(struct pci_dev *dev) +{ + int i; + + pr_debug("mpc52xx_pci_fixup_resources() %.4x:%.4x\n", + dev->vendor, dev->device); + + /* We don't rely on boot loader for PCI and resets all + devices */ + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + struct resource *res = &dev->resource[i]; + if (res->end > res->start) { /* Only valid resources */ + res->end -= res->start; + res->start = 0; + res->flags |= IORESOURCE_UNSET; + } + } + + /* The PCI Host bridge of MPC52xx has a prefetch memory resource + fixed to 1Gb. Doesn't fit in the resource system so we remove it */ + if ( (dev->vendor == PCI_VENDOR_ID_MOTOROLA) && + ( dev->device == PCI_DEVICE_ID_MOTOROLA_MPC5200 + || dev->device == PCI_DEVICE_ID_MOTOROLA_MPC5200B) ) { + struct resource *res = &dev->resource[1]; + res->start = res->end = res->flags = 0; + } +} + +int __init +mpc52xx_add_bridge(struct device_node *node) +{ + int len; + struct mpc52xx_pci __iomem *pci_regs; + struct pci_controller *hose; + const int *bus_range; + struct resource rsrc; + + pr_debug("Adding MPC52xx PCI host bridge %s\n", node->full_name); + + pci_add_flags(PCI_REASSIGN_ALL_BUS); + + if (of_address_to_resource(node, 0, &rsrc) != 0) { + printk(KERN_ERR "Can't get %s resources\n", node->full_name); + return -EINVAL; + } + + bus_range = of_get_property(node, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get %s bus-range, assume bus 0\n", + node->full_name); + bus_range = NULL; + } + + /* There are some PCI quirks on the 52xx, register the hook to + * fix them. */ + ppc_md.pcibios_fixup_resources = mpc52xx_pci_fixup_resources; + + /* Alloc and initialize the pci controller. Values in the device + * tree are needed to configure the 52xx PCI controller. Rather + * than parse the tree here, let pci_process_bridge_OF_ranges() + * do it for us and extract the values after the fact */ + hose = pcibios_alloc_controller(node); + if (!hose) + return -ENOMEM; + + hose->first_busno = bus_range ? bus_range[0] : 0; + hose->last_busno = bus_range ? bus_range[1] : 0xff; + + hose->ops = &mpc52xx_pci_ops; + + pci_regs = ioremap(rsrc.start, resource_size(&rsrc)); + if (!pci_regs) + return -ENOMEM; + + pci_process_bridge_OF_ranges(hose, node, 1); + + /* Finish setting up PCI using values obtained by + * pci_proces_bridge_OF_ranges */ + mpc52xx_pci_setup(hose, pci_regs, rsrc.start); + + return 0; +} + +void __init mpc52xx_setup_pci(void) +{ + struct device_node *pci; + + pci = of_find_matching_node(NULL, mpc52xx_pci_ids); + if (!pci) + return; + + mpc52xx_add_bridge(pci); + of_node_put(pci); +} diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pic.c b/arch/powerpc/platforms/52xx/mpc52xx_pic.c new file mode 100644 index 00000000000..1a9a4957057 --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_pic.c @@ -0,0 +1,517 @@ +/* + * + * Programmable Interrupt Controller functions for the Freescale MPC52xx. + * + * Copyright (C) 2008 Secret Lab Technologies Ltd. + * Copyright (C) 2006 bplan GmbH + * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com> + * Copyright (C) 2003 Montavista Software, Inc + * + * Based on the code from the 2.4 kernel by + * Dale Farnsworth <dfarnsworth@mvista.com> and Kent Borg. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +/* + * This is the device driver for the MPC5200 interrupt controller. + * + * hardware overview + * ----------------- + * The MPC5200 interrupt controller groups the all interrupt sources into + * three groups called 'critical', 'main', and 'peripheral'. The critical + * group has 3 irqs, External IRQ0, slice timer 0 irq, and wake from deep + * sleep. Main group include the other 3 external IRQs, slice timer 1, RTC, + * gpios, and the general purpose timers. Peripheral group contains the + * remaining irq sources from all of the on-chip peripherals (PSCs, Ethernet, + * USB, DMA, etc). + * + * virqs + * ----- + * The Linux IRQ subsystem requires that each irq source be assigned a + * system wide unique IRQ number starting at 1 (0 means no irq). Since + * systems can have multiple interrupt controllers, the virtual IRQ (virq) + * infrastructure lets each interrupt controller to define a local set + * of IRQ numbers and the virq infrastructure maps those numbers into + * a unique range of the global IRQ# space. + * + * To define a range of virq numbers for this controller, this driver first + * assigns a number to each of the irq groups (called the level 1 or L1 + * value). Within each group individual irq sources are also assigned a + * number, as defined by the MPC5200 user guide, and refers to it as the + * level 2 or L2 value. The virq number is determined by shifting up the + * L1 value by MPC52xx_IRQ_L1_OFFSET and ORing it with the L2 value. + * + * For example, the TMR0 interrupt is irq 9 in the main group. The + * virq for TMR0 is calculated by ((1 << MPC52xx_IRQ_L1_OFFSET) | 9). + * + * The observant reader will also notice that this driver defines a 4th + * interrupt group called 'bestcomm'. The bestcomm group isn't physically + * part of the MPC5200 interrupt controller, but it is used here to assign + * a separate virq number for each bestcomm task (since any of the 16 + * bestcomm tasks can cause the bestcomm interrupt to be raised). When a + * bestcomm interrupt occurs (peripheral group, irq 0) this driver determines + * which task needs servicing and returns the irq number for that task. This + * allows drivers which use bestcomm to define their own interrupt handlers. + * + * irq_chip structures + * ------------------- + * For actually manipulating IRQs (masking, enabling, clearing, etc) this + * driver defines four separate 'irq_chip' structures, one for the main + * group, one for the peripherals group, one for the bestcomm group and one + * for external interrupts. The irq_chip structures provide the hooks needed + * to manipulate each IRQ source, and since each group is has a separate set + * of registers for controlling the irq, it makes sense to divide up the + * hooks along those lines. + * + * You'll notice that there is not an irq_chip for the critical group and + * you'll also notice that there is an irq_chip defined for external + * interrupts even though there is no external interrupt group. The reason + * for this is that the four external interrupts are all managed with the same + * register even though one of the external IRQs is in the critical group and + * the other three are in the main group. For this reason it makes sense for + * the 4 external irqs to be managed using a separate set of hooks. The + * reason there is no crit irq_chip is that of the 3 irqs in the critical + * group, only external interrupt is actually support at this time by this + * driver and since external interrupt is the only one used, it can just + * be directed to make use of the external irq irq_chip. + * + * device tree bindings + * -------------------- + * The device tree bindings for this controller reflect the two level + * organization of irqs in the device. #interrupt-cells = <3> where the + * first cell is the group number [0..3], the second cell is the irq + * number in the group, and the third cell is the sense type (level/edge). + * For reference, the following is a list of the interrupt property values + * associated with external interrupt sources on the MPC5200 (just because + * it is non-obvious to determine what the interrupts property should be + * when reading the mpc5200 manual and it is a frequently asked question). + * + * External interrupts: + * <0 0 n> external irq0, n is sense (n=0: level high, + * <1 1 n> external irq1, n is sense n=1: edge rising, + * <1 2 n> external irq2, n is sense n=2: edge falling, + * <1 3 n> external irq3, n is sense n=3: level low) + */ +#undef DEBUG + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/of.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/mpc52xx.h> + +/* HW IRQ mapping */ +#define MPC52xx_IRQ_L1_CRIT (0) +#define MPC52xx_IRQ_L1_MAIN (1) +#define MPC52xx_IRQ_L1_PERP (2) +#define MPC52xx_IRQ_L1_SDMA (3) + +#define MPC52xx_IRQ_L1_OFFSET (6) +#define MPC52xx_IRQ_L1_MASK (0x00c0) +#define MPC52xx_IRQ_L2_MASK (0x003f) + +#define MPC52xx_IRQ_HIGHTESTHWIRQ (0xd0) + + +/* MPC5200 device tree match tables */ +static struct of_device_id mpc52xx_pic_ids[] __initdata = { + { .compatible = "fsl,mpc5200-pic", }, + { .compatible = "mpc5200-pic", }, + {} +}; +static struct of_device_id mpc52xx_sdma_ids[] __initdata = { + { .compatible = "fsl,mpc5200-bestcomm", }, + { .compatible = "mpc5200-bestcomm", }, + {} +}; + +static struct mpc52xx_intr __iomem *intr; +static struct mpc52xx_sdma __iomem *sdma; +static struct irq_host *mpc52xx_irqhost = NULL; + +static unsigned char mpc52xx_map_senses[4] = { + IRQ_TYPE_LEVEL_HIGH, + IRQ_TYPE_EDGE_RISING, + IRQ_TYPE_EDGE_FALLING, + IRQ_TYPE_LEVEL_LOW, +}; + +/* Utility functions */ +static inline void io_be_setbit(u32 __iomem *addr, int bitno) +{ + out_be32(addr, in_be32(addr) | (1 << bitno)); +} + +static inline void io_be_clrbit(u32 __iomem *addr, int bitno) +{ + out_be32(addr, in_be32(addr) & ~(1 << bitno)); +} + +/* + * IRQ[0-3] interrupt irq_chip + */ +static void mpc52xx_extirq_mask(struct irq_data *d) +{ + int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK; + io_be_clrbit(&intr->ctrl, 11 - l2irq); +} + +static void mpc52xx_extirq_unmask(struct irq_data *d) +{ + int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK; + io_be_setbit(&intr->ctrl, 11 - l2irq); +} + +static void mpc52xx_extirq_ack(struct irq_data *d) +{ + int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK; + io_be_setbit(&intr->ctrl, 27-l2irq); +} + +static int mpc52xx_extirq_set_type(struct irq_data *d, unsigned int flow_type) +{ + u32 ctrl_reg, type; + int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK; + void *handler = handle_level_irq; + + pr_debug("%s: irq=%x. l2=%d flow_type=%d\n", __func__, + (int) irqd_to_hwirq(d), l2irq, flow_type); + + switch (flow_type) { + case IRQF_TRIGGER_HIGH: type = 0; break; + case IRQF_TRIGGER_RISING: type = 1; handler = handle_edge_irq; break; + case IRQF_TRIGGER_FALLING: type = 2; handler = handle_edge_irq; break; + case IRQF_TRIGGER_LOW: type = 3; break; + default: + type = 0; + } + + ctrl_reg = in_be32(&intr->ctrl); + ctrl_reg &= ~(0x3 << (22 - (l2irq * 2))); + ctrl_reg |= (type << (22 - (l2irq * 2))); + out_be32(&intr->ctrl, ctrl_reg); + + __irq_set_handler_locked(d->irq, handler); + + return 0; +} + +static struct irq_chip mpc52xx_extirq_irqchip = { + .name = "MPC52xx External", + .irq_mask = mpc52xx_extirq_mask, + .irq_unmask = mpc52xx_extirq_unmask, + .irq_ack = mpc52xx_extirq_ack, + .irq_set_type = mpc52xx_extirq_set_type, +}; + +/* + * Main interrupt irq_chip + */ +static int mpc52xx_null_set_type(struct irq_data *d, unsigned int flow_type) +{ + return 0; /* Do nothing so that the sense mask will get updated */ +} + +static void mpc52xx_main_mask(struct irq_data *d) +{ + int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK; + io_be_setbit(&intr->main_mask, 16 - l2irq); +} + +static void mpc52xx_main_unmask(struct irq_data *d) +{ + int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK; + io_be_clrbit(&intr->main_mask, 16 - l2irq); +} + +static struct irq_chip mpc52xx_main_irqchip = { + .name = "MPC52xx Main", + .irq_mask = mpc52xx_main_mask, + .irq_mask_ack = mpc52xx_main_mask, + .irq_unmask = mpc52xx_main_unmask, + .irq_set_type = mpc52xx_null_set_type, +}; + +/* + * Peripherals interrupt irq_chip + */ +static void mpc52xx_periph_mask(struct irq_data *d) +{ + int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK; + io_be_setbit(&intr->per_mask, 31 - l2irq); +} + +static void mpc52xx_periph_unmask(struct irq_data *d) +{ + int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK; + io_be_clrbit(&intr->per_mask, 31 - l2irq); +} + +static struct irq_chip mpc52xx_periph_irqchip = { + .name = "MPC52xx Peripherals", + .irq_mask = mpc52xx_periph_mask, + .irq_mask_ack = mpc52xx_periph_mask, + .irq_unmask = mpc52xx_periph_unmask, + .irq_set_type = mpc52xx_null_set_type, +}; + +/* + * SDMA interrupt irq_chip + */ +static void mpc52xx_sdma_mask(struct irq_data *d) +{ + int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK; + io_be_setbit(&sdma->IntMask, l2irq); +} + +static void mpc52xx_sdma_unmask(struct irq_data *d) +{ + int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK; + io_be_clrbit(&sdma->IntMask, l2irq); +} + +static void mpc52xx_sdma_ack(struct irq_data *d) +{ + int l2irq = irqd_to_hwirq(d) & MPC52xx_IRQ_L2_MASK; + out_be32(&sdma->IntPend, 1 << l2irq); +} + +static struct irq_chip mpc52xx_sdma_irqchip = { + .name = "MPC52xx SDMA", + .irq_mask = mpc52xx_sdma_mask, + .irq_unmask = mpc52xx_sdma_unmask, + .irq_ack = mpc52xx_sdma_ack, + .irq_set_type = mpc52xx_null_set_type, +}; + +/** + * mpc52xx_is_extirq - Returns true if hwirq number is for an external IRQ + */ +static int mpc52xx_is_extirq(int l1, int l2) +{ + return ((l1 == 0) && (l2 == 0)) || + ((l1 == 1) && (l2 >= 1) && (l2 <= 3)); +} + +/** + * mpc52xx_irqhost_xlate - translate virq# from device tree interrupts property + */ +static int mpc52xx_irqhost_xlate(struct irq_host *h, struct device_node *ct, + const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, + unsigned int *out_flags) +{ + int intrvect_l1; + int intrvect_l2; + int intrvect_type; + int intrvect_linux; + + if (intsize != 3) + return -1; + + intrvect_l1 = (int)intspec[0]; + intrvect_l2 = (int)intspec[1]; + intrvect_type = (int)intspec[2] & 0x3; + + intrvect_linux = (intrvect_l1 << MPC52xx_IRQ_L1_OFFSET) & + MPC52xx_IRQ_L1_MASK; + intrvect_linux |= intrvect_l2 & MPC52xx_IRQ_L2_MASK; + + *out_hwirq = intrvect_linux; + *out_flags = IRQ_TYPE_LEVEL_LOW; + if (mpc52xx_is_extirq(intrvect_l1, intrvect_l2)) + *out_flags = mpc52xx_map_senses[intrvect_type]; + + pr_debug("return %x, l1=%d, l2=%d\n", intrvect_linux, intrvect_l1, + intrvect_l2); + return 0; +} + +/** + * mpc52xx_irqhost_map - Hook to map from virq to an irq_chip structure + */ +static int mpc52xx_irqhost_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t irq) +{ + int l1irq; + int l2irq; + struct irq_chip *irqchip; + void *hndlr; + int type; + u32 reg; + + l1irq = (irq & MPC52xx_IRQ_L1_MASK) >> MPC52xx_IRQ_L1_OFFSET; + l2irq = irq & MPC52xx_IRQ_L2_MASK; + + /* + * External IRQs are handled differently by the hardware so they are + * handled by a dedicated irq_chip structure. + */ + if (mpc52xx_is_extirq(l1irq, l2irq)) { + reg = in_be32(&intr->ctrl); + type = mpc52xx_map_senses[(reg >> (22 - l2irq * 2)) & 0x3]; + if ((type == IRQ_TYPE_EDGE_FALLING) || + (type == IRQ_TYPE_EDGE_RISING)) + hndlr = handle_edge_irq; + else + hndlr = handle_level_irq; + + irq_set_chip_and_handler(virq, &mpc52xx_extirq_irqchip, hndlr); + pr_debug("%s: External IRQ%i virq=%x, hw=%x. type=%x\n", + __func__, l2irq, virq, (int)irq, type); + return 0; + } + + /* It is an internal SOC irq. Choose the correct irq_chip */ + switch (l1irq) { + case MPC52xx_IRQ_L1_MAIN: irqchip = &mpc52xx_main_irqchip; break; + case MPC52xx_IRQ_L1_PERP: irqchip = &mpc52xx_periph_irqchip; break; + case MPC52xx_IRQ_L1_SDMA: irqchip = &mpc52xx_sdma_irqchip; break; + default: + pr_err("%s: invalid irq: virq=%i, l1=%i, l2=%i\n", + __func__, virq, l1irq, l2irq); + return -EINVAL; + } + + irq_set_chip_and_handler(virq, irqchip, handle_level_irq); + pr_debug("%s: virq=%x, l1=%i, l2=%i\n", __func__, virq, l1irq, l2irq); + + return 0; +} + +static struct irq_host_ops mpc52xx_irqhost_ops = { + .xlate = mpc52xx_irqhost_xlate, + .map = mpc52xx_irqhost_map, +}; + +/** + * mpc52xx_init_irq - Initialize and register with the virq subsystem + * + * Hook for setting up IRQs on an mpc5200 system. A pointer to this function + * is to be put into the machine definition structure. + * + * This function searches the device tree for an MPC5200 interrupt controller, + * initializes it, and registers it with the virq subsystem. + */ +void __init mpc52xx_init_irq(void) +{ + u32 intr_ctrl; + struct device_node *picnode; + struct device_node *np; + + /* Remap the necessary zones */ + picnode = of_find_matching_node(NULL, mpc52xx_pic_ids); + intr = of_iomap(picnode, 0); + if (!intr) + panic(__FILE__ ": find_and_map failed on 'mpc5200-pic'. " + "Check node !"); + + np = of_find_matching_node(NULL, mpc52xx_sdma_ids); + sdma = of_iomap(np, 0); + of_node_put(np); + if (!sdma) + panic(__FILE__ ": find_and_map failed on 'mpc5200-bestcomm'. " + "Check node !"); + + pr_debug("MPC5200 IRQ controller mapped to 0x%p\n", intr); + + /* Disable all interrupt sources. */ + out_be32(&sdma->IntPend, 0xffffffff); /* 1 means clear pending */ + out_be32(&sdma->IntMask, 0xffffffff); /* 1 means disabled */ + out_be32(&intr->per_mask, 0x7ffffc00); /* 1 means disabled */ + out_be32(&intr->main_mask, 0x00010fff); /* 1 means disabled */ + intr_ctrl = in_be32(&intr->ctrl); + intr_ctrl &= 0x00ff0000; /* Keeps IRQ[0-3] config */ + intr_ctrl |= 0x0f000000 | /* clear IRQ 0-3 */ + 0x00001000 | /* MEE master external enable */ + 0x00000000 | /* 0 means disable IRQ 0-3 */ + 0x00000001; /* CEb route critical normally */ + out_be32(&intr->ctrl, intr_ctrl); + + /* Zero a bunch of the priority settings. */ + out_be32(&intr->per_pri1, 0); + out_be32(&intr->per_pri2, 0); + out_be32(&intr->per_pri3, 0); + out_be32(&intr->main_pri1, 0); + out_be32(&intr->main_pri2, 0); + + /* + * As last step, add an irq host to translate the real + * hw irq information provided by the ofw to linux virq + */ + mpc52xx_irqhost = irq_alloc_host(picnode, IRQ_HOST_MAP_LINEAR, + MPC52xx_IRQ_HIGHTESTHWIRQ, + &mpc52xx_irqhost_ops, -1); + + if (!mpc52xx_irqhost) + panic(__FILE__ ": Cannot allocate the IRQ host\n"); + + irq_set_default_host(mpc52xx_irqhost); + + pr_info("MPC52xx PIC is up and running!\n"); +} + +/** + * mpc52xx_get_irq - Get pending interrupt number hook function + * + * Called by the interrupt handler to determine what IRQ handler needs to be + * executed. + * + * Status of pending interrupts is determined by reading the encoded status + * register. The encoded status register has three fields; one for each of the + * types of interrupts defined by the controller - 'critical', 'main' and + * 'peripheral'. This function reads the status register and returns the IRQ + * number associated with the highest priority pending interrupt. 'Critical' + * interrupts have the highest priority, followed by 'main' interrupts, and + * then 'peripheral'. + * + * The mpc5200 interrupt controller can be configured to boost the priority + * of individual 'peripheral' interrupts. If this is the case then a special + * value will appear in either the crit or main fields indicating a high + * or medium priority peripheral irq has occurred. + * + * This function checks each of the 3 irq request fields and returns the + * first pending interrupt that it finds. + * + * This function also identifies a 4th type of interrupt; 'bestcomm'. Each + * bestcomm DMA task can raise the bestcomm peripheral interrupt. When this + * occurs at task-specific IRQ# is decoded so that each task can have its + * own IRQ handler. + */ +unsigned int mpc52xx_get_irq(void) +{ + u32 status; + int irq; + + status = in_be32(&intr->enc_status); + if (status & 0x00000400) { /* critical */ + irq = (status >> 8) & 0x3; + if (irq == 2) /* high priority peripheral */ + goto peripheral; + irq |= (MPC52xx_IRQ_L1_CRIT << MPC52xx_IRQ_L1_OFFSET); + } else if (status & 0x00200000) { /* main */ + irq = (status >> 16) & 0x1f; + if (irq == 4) /* low priority peripheral */ + goto peripheral; + irq |= (MPC52xx_IRQ_L1_MAIN << MPC52xx_IRQ_L1_OFFSET); + } else if (status & 0x20000000) { /* peripheral */ + peripheral: + irq = (status >> 24) & 0x1f; + if (irq == 0) { /* bestcomm */ + status = in_be32(&sdma->IntPend); + irq = ffs(status) - 1; + irq |= (MPC52xx_IRQ_L1_SDMA << MPC52xx_IRQ_L1_OFFSET); + } else { + irq |= (MPC52xx_IRQ_L1_PERP << MPC52xx_IRQ_L1_OFFSET); + } + } else { + return NO_IRQ; + } + + return irq_linear_revmap(mpc52xx_irqhost, irq); +} diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pm.c b/arch/powerpc/platforms/52xx/mpc52xx_pm.c new file mode 100644 index 00000000000..8310e8b5b57 --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_pm.c @@ -0,0 +1,200 @@ +#include <linux/init.h> +#include <linux/suspend.h> +#include <linux/io.h> +#include <asm/time.h> +#include <asm/cacheflush.h> +#include <asm/mpc52xx.h> + +/* these are defined in mpc52xx_sleep.S, and only used here */ +extern void mpc52xx_deep_sleep(void __iomem *sram, void __iomem *sdram_regs, + struct mpc52xx_cdm __iomem *, struct mpc52xx_intr __iomem*); +extern void mpc52xx_ds_sram(void); +extern const long mpc52xx_ds_sram_size; +extern void mpc52xx_ds_cached(void); +extern const long mpc52xx_ds_cached_size; + +static void __iomem *mbar; +static void __iomem *sdram; +static struct mpc52xx_cdm __iomem *cdm; +static struct mpc52xx_intr __iomem *intr; +static struct mpc52xx_gpio_wkup __iomem *gpiow; +static void __iomem *sram; +static int sram_size; + +struct mpc52xx_suspend mpc52xx_suspend; + +static int mpc52xx_pm_valid(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + return 1; + default: + return 0; + } +} + +int mpc52xx_set_wakeup_gpio(u8 pin, u8 level) +{ + u16 tmp; + + /* enable gpio */ + out_8(&gpiow->wkup_gpioe, in_8(&gpiow->wkup_gpioe) | (1 << pin)); + /* set as input */ + out_8(&gpiow->wkup_ddr, in_8(&gpiow->wkup_ddr) & ~(1 << pin)); + /* enable deep sleep interrupt */ + out_8(&gpiow->wkup_inten, in_8(&gpiow->wkup_inten) | (1 << pin)); + /* low/high level creates wakeup interrupt */ + tmp = in_be16(&gpiow->wkup_itype); + tmp &= ~(0x3 << (pin * 2)); + tmp |= (!level + 1) << (pin * 2); + out_be16(&gpiow->wkup_itype, tmp); + /* master enable */ + out_8(&gpiow->wkup_maste, 1); + + return 0; +} + +int mpc52xx_pm_prepare(void) +{ + struct device_node *np; + const struct of_device_id immr_ids[] = { + { .compatible = "fsl,mpc5200-immr", }, + { .compatible = "fsl,mpc5200b-immr", }, + { .type = "soc", .compatible = "mpc5200", }, /* lite5200 */ + { .type = "builtin", .compatible = "mpc5200", }, /* efika */ + {} + }; + struct resource res; + + /* map the whole register space */ + np = of_find_matching_node(NULL, immr_ids); + + if (of_address_to_resource(np, 0, &res)) { + pr_err("mpc52xx_pm_prepare(): could not get IMMR address\n"); + of_node_put(np); + return -ENOSYS; + } + + mbar = ioremap(res.start, 0xc000); /* we should map whole region including SRAM */ + + of_node_put(np); + if (!mbar) { + pr_err("mpc52xx_pm_prepare(): could not map registers\n"); + return -ENOSYS; + } + /* these offsets are from mpc5200 users manual */ + sdram = mbar + 0x100; + cdm = mbar + 0x200; + intr = mbar + 0x500; + gpiow = mbar + 0xc00; + sram = mbar + 0x8000; /* Those will be handled by the */ + sram_size = 0x4000; /* bestcomm driver soon */ + + /* call board suspend code, if applicable */ + if (mpc52xx_suspend.board_suspend_prepare) + mpc52xx_suspend.board_suspend_prepare(mbar); + else { + printk(KERN_ALERT "%s: %i don't know how to wake up the board\n", + __func__, __LINE__); + goto out_unmap; + } + + return 0; + + out_unmap: + iounmap(mbar); + return -ENOSYS; +} + + +char saved_sram[0x4000]; + +int mpc52xx_pm_enter(suspend_state_t state) +{ + u32 clk_enables; + u32 msr, hid0; + u32 intr_main_mask; + void __iomem * irq_0x500 = (void __iomem *)CONFIG_KERNEL_START + 0x500; + unsigned long irq_0x500_stop = (unsigned long)irq_0x500 + mpc52xx_ds_cached_size; + char saved_0x500[mpc52xx_ds_cached_size]; + + /* disable all interrupts in PIC */ + intr_main_mask = in_be32(&intr->main_mask); + out_be32(&intr->main_mask, intr_main_mask | 0x1ffff); + + /* don't let DEC expire any time soon */ + mtspr(SPRN_DEC, 0x7fffffff); + + /* save SRAM */ + memcpy(saved_sram, sram, sram_size); + + /* copy low level suspend code to sram */ + memcpy(sram, mpc52xx_ds_sram, mpc52xx_ds_sram_size); + + out_8(&cdm->ccs_sleep_enable, 1); + out_8(&cdm->osc_sleep_enable, 1); + out_8(&cdm->ccs_qreq_test, 1); + + /* disable all but SDRAM and bestcomm (SRAM) clocks */ + clk_enables = in_be32(&cdm->clk_enables); + out_be32(&cdm->clk_enables, clk_enables & 0x00088000); + + /* disable power management */ + msr = mfmsr(); + mtmsr(msr & ~MSR_POW); + + /* enable sleep mode, disable others */ + hid0 = mfspr(SPRN_HID0); + mtspr(SPRN_HID0, (hid0 & ~(HID0_DOZE | HID0_NAP | HID0_DPM)) | HID0_SLEEP); + + /* save original, copy our irq handler, flush from dcache and invalidate icache */ + memcpy(saved_0x500, irq_0x500, mpc52xx_ds_cached_size); + memcpy(irq_0x500, mpc52xx_ds_cached, mpc52xx_ds_cached_size); + flush_icache_range((unsigned long)irq_0x500, irq_0x500_stop); + + /* call low-level sleep code */ + mpc52xx_deep_sleep(sram, sdram, cdm, intr); + + /* restore original irq handler */ + memcpy(irq_0x500, saved_0x500, mpc52xx_ds_cached_size); + flush_icache_range((unsigned long)irq_0x500, irq_0x500_stop); + + /* restore old power mode */ + mtmsr(msr & ~MSR_POW); + mtspr(SPRN_HID0, hid0); + mtmsr(msr); + + out_be32(&cdm->clk_enables, clk_enables); + out_8(&cdm->ccs_sleep_enable, 0); + out_8(&cdm->osc_sleep_enable, 0); + + /* restore SRAM */ + memcpy(sram, saved_sram, sram_size); + + /* reenable interrupts in PIC */ + out_be32(&intr->main_mask, intr_main_mask); + + return 0; +} + +void mpc52xx_pm_finish(void) +{ + /* call board resume code */ + if (mpc52xx_suspend.board_resume_finish) + mpc52xx_suspend.board_resume_finish(mbar); + + iounmap(mbar); +} + +static const struct platform_suspend_ops mpc52xx_pm_ops = { + .valid = mpc52xx_pm_valid, + .prepare = mpc52xx_pm_prepare, + .enter = mpc52xx_pm_enter, + .finish = mpc52xx_pm_finish, +}; + +int __init mpc52xx_pm_init(void) +{ + suspend_set_ops(&mpc52xx_pm_ops); + return 0; +} diff --git a/arch/powerpc/platforms/52xx/mpc52xx_sleep.S b/arch/powerpc/platforms/52xx/mpc52xx_sleep.S new file mode 100644 index 00000000000..4dc170b0ae1 --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_sleep.S @@ -0,0 +1,154 @@ +#include <asm/reg.h> +#include <asm/ppc_asm.h> +#include <asm/processor.h> + + +.text + +_GLOBAL(mpc52xx_deep_sleep) +mpc52xx_deep_sleep: /* args r3-r6: SRAM, SDRAM regs, CDM regs, INTR regs */ + + /* enable interrupts */ + mfmsr r7 + ori r7, r7, 0x8000 /* EE */ + mtmsr r7 + sync; isync; + + li r10, 0 /* flag that irq handler sets */ + + /* enable tmr7 (or any other) interrupt */ + lwz r8, 0x14(r6) /* intr->main_mask */ + ori r8, r8, 0x1 + xori r8, r8, 0x1 + stw r8, 0x14(r6) + sync + + /* emulate tmr7 interrupt */ + li r8, 0x1 + stw r8, 0x40(r6) /* intr->main_emulate */ + sync + + /* wait for it to happen */ +1: + cmpi cr0, r10, 1 + bne cr0, 1b + + /* lock icache */ + mfspr r10, SPRN_HID0 + ori r10, r10, 0x2000 + sync; isync; + mtspr SPRN_HID0, r10 + sync; isync; + + + mflr r9 /* save LR */ + + /* jump to sram */ + mtlr r3 + blrl + + mtlr r9 /* restore LR */ + + /* unlock icache */ + mfspr r10, SPRN_HID0 + ori r10, r10, 0x2000 + xori r10, r10, 0x2000 + sync; isync; + mtspr SPRN_HID0, r10 + sync; isync; + + + /* return to C code */ + blr + + +_GLOBAL(mpc52xx_ds_sram) +mpc52xx_ds_sram: + /* put SDRAM into self-refresh */ + lwz r8, 0x4(r4) /* sdram->ctrl */ + + oris r8, r8, 0x8000 /* mode_en */ + stw r8, 0x4(r4) + sync + + ori r8, r8, 0x0002 /* soft_pre */ + stw r8, 0x4(r4) + sync + xori r8, r8, 0x0002 + + xoris r8, r8, 0x8000 /* !mode_en */ + stw r8, 0x4(r4) + sync + + oris r8, r8, 0x5000 + xoris r8, r8, 0x4000 /* ref_en !cke */ + stw r8, 0x4(r4) + sync + + /* disable SDRAM clock */ + lwz r8, 0x14(r5) /* cdm->clkenable */ + ori r8, r8, 0x0008 + xori r8, r8, 0x0008 + stw r8, 0x14(r5) + sync + + + /* put mpc5200 to sleep */ + mfmsr r10 + oris r10, r10, 0x0004 /* POW = 1 */ + sync; isync; + mtmsr r10 + sync; isync; + + + /* enable clock */ + lwz r8, 0x14(r5) + ori r8, r8, 0x0008 + stw r8, 0x14(r5) + sync + + /* get ram out of self-refresh */ + lwz r8, 0x4(r4) + oris r8, r8, 0x5000 /* cke ref_en */ + stw r8, 0x4(r4) + sync + + blr +_GLOBAL(mpc52xx_ds_sram_size) +mpc52xx_ds_sram_size: + .long $-mpc52xx_ds_sram + + +/* ### interrupt handler for wakeup from deep-sleep ### */ +_GLOBAL(mpc52xx_ds_cached) +mpc52xx_ds_cached: + mtspr SPRN_SPRG0, r7 + mtspr SPRN_SPRG1, r8 + + /* disable emulated interrupt */ + mfspr r7, 311 /* MBAR */ + addi r7, r7, 0x540 /* intr->main_emul */ + li r8, 0 + stw r8, 0(r7) + sync + dcbf 0, r7 + + /* acknowledge wakeup, so CCS releases power pown */ + mfspr r7, 311 /* MBAR */ + addi r7, r7, 0x524 /* intr->enc_status */ + lwz r8, 0(r7) + ori r8, r8, 0x0400 + stw r8, 0(r7) + sync + dcbf 0, r7 + + /* flag - we handled the interrupt */ + li r10, 1 + + mfspr r8, SPRN_SPRG1 + mfspr r7, SPRN_SPRG0 + + rfi +_GLOBAL(mpc52xx_ds_cached_size) +mpc52xx_ds_cached_size: + .long $-mpc52xx_ds_cached diff --git a/arch/powerpc/platforms/82xx/Kconfig b/arch/powerpc/platforms/82xx/Kconfig new file mode 100644 index 00000000000..7c7df400382 --- /dev/null +++ b/arch/powerpc/platforms/82xx/Kconfig @@ -0,0 +1,72 @@ +menuconfig PPC_82xx + bool "82xx-based boards (PQ II)" + depends on 6xx + +if PPC_82xx + +config MPC8272_ADS + bool "Freescale MPC8272 ADS" + select DEFAULT_UIMAGE + select PQ2ADS + select 8272 + select 8260 + select FSL_SOC + select PQ2_ADS_PCI_PIC if PCI + help + This option enables support for the MPC8272 ADS board + +config PQ2FADS + bool "Freescale PQ2FADS" + select DEFAULT_UIMAGE + select PQ2ADS + select 8260 + select FSL_SOC + select PQ2_ADS_PCI_PIC if PCI + help + This option enables support for the PQ2FADS board + +config EP8248E + bool "Embedded Planet EP8248E (a.k.a. CWH-PPC-8248N-VE)" + select 8272 + select 8260 + select FSL_SOC + select PHYLIB + select MDIO_BITBANG + help + This enables support for the Embedded Planet EP8248E board. + + This board is also resold by Freescale as the QUICCStart + MPC8248 Evaluation System and/or the CWH-PPC-8248N-VE. + +config MGCOGE + bool "Keymile MGCOGE" + select 8272 + select 8260 + select FSL_SOC + help + This enables support for the Keymile MGCOGE board. + +endif + +config PQ2ADS + bool + default n + +config 8260 + bool + depends on 6xx + select CPM2 + help + The MPC8260 is a typical embedded CPU made by Freescale. Selecting + this option means that you wish to build a kernel for a machine with + an 8260 class CPU. + +config 8272 + bool + select 8260 + help + The MPC8272 CPM has a different internal dpram setup than other CPM2 + devices + +config PQ2_ADS_PCI_PIC + bool diff --git a/arch/powerpc/platforms/82xx/Makefile b/arch/powerpc/platforms/82xx/Makefile new file mode 100644 index 00000000000..455fe21e37c --- /dev/null +++ b/arch/powerpc/platforms/82xx/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the PowerPC 82xx linux kernel. +# +obj-$(CONFIG_MPC8272_ADS) += mpc8272_ads.o +obj-$(CONFIG_CPM2) += pq2.o +obj-$(CONFIG_PQ2_ADS_PCI_PIC) += pq2ads-pci-pic.o +obj-$(CONFIG_PQ2FADS) += pq2fads.o +obj-$(CONFIG_EP8248E) += ep8248e.o +obj-$(CONFIG_MGCOGE) += km82xx.o diff --git a/arch/powerpc/platforms/82xx/ep8248e.c b/arch/powerpc/platforms/82xx/ep8248e.c new file mode 100644 index 00000000000..10ff526cd04 --- /dev/null +++ b/arch/powerpc/platforms/82xx/ep8248e.c @@ -0,0 +1,335 @@ +/* + * Embedded Planet EP8248E support + * + * Copyright 2007 Freescale Semiconductor, Inc. + * Author: Scott Wood <scottwood@freescale.com> + * + * 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 <linux/init.h> +#include <linux/interrupt.h> +#include <linux/fsl_devices.h> +#include <linux/mdio-bitbang.h> +#include <linux/of_mdio.h> +#include <linux/slab.h> +#include <linux/of_platform.h> + +#include <asm/io.h> +#include <asm/cpm2.h> +#include <asm/udbg.h> +#include <asm/machdep.h> +#include <asm/time.h> +#include <asm/mpc8260.h> +#include <asm/prom.h> + +#include <sysdev/fsl_soc.h> +#include <sysdev/cpm2_pic.h> + +#include "pq2.h" + +static u8 __iomem *ep8248e_bcsr; +static struct device_node *ep8248e_bcsr_node; + +#define BCSR7_SCC2_ENABLE 0x10 + +#define BCSR8_PHY1_ENABLE 0x80 +#define BCSR8_PHY1_POWER 0x40 +#define BCSR8_PHY2_ENABLE 0x20 +#define BCSR8_PHY2_POWER 0x10 +#define BCSR8_MDIO_READ 0x04 +#define BCSR8_MDIO_CLOCK 0x02 +#define BCSR8_MDIO_DATA 0x01 + +#define BCSR9_USB_ENABLE 0x80 +#define BCSR9_USB_POWER 0x40 +#define BCSR9_USB_HOST 0x20 +#define BCSR9_USB_FULL_SPEED_TARGET 0x10 + +static void __init ep8248e_pic_init(void) +{ + struct device_node *np = of_find_compatible_node(NULL, NULL, "fsl,pq2-pic"); + if (!np) { + printk(KERN_ERR "PIC init: can not find cpm-pic node\n"); + return; + } + + cpm2_pic_init(np); + of_node_put(np); +} + +static void ep8248e_set_mdc(struct mdiobb_ctrl *ctrl, int level) +{ + if (level) + setbits8(&ep8248e_bcsr[8], BCSR8_MDIO_CLOCK); + else + clrbits8(&ep8248e_bcsr[8], BCSR8_MDIO_CLOCK); + + /* Read back to flush the write. */ + in_8(&ep8248e_bcsr[8]); +} + +static void ep8248e_set_mdio_dir(struct mdiobb_ctrl *ctrl, int output) +{ + if (output) + clrbits8(&ep8248e_bcsr[8], BCSR8_MDIO_READ); + else + setbits8(&ep8248e_bcsr[8], BCSR8_MDIO_READ); + + /* Read back to flush the write. */ + in_8(&ep8248e_bcsr[8]); +} + +static void ep8248e_set_mdio_data(struct mdiobb_ctrl *ctrl, int data) +{ + if (data) + setbits8(&ep8248e_bcsr[8], BCSR8_MDIO_DATA); + else + clrbits8(&ep8248e_bcsr[8], BCSR8_MDIO_DATA); + + /* Read back to flush the write. */ + in_8(&ep8248e_bcsr[8]); +} + +static int ep8248e_get_mdio_data(struct mdiobb_ctrl *ctrl) +{ + return in_8(&ep8248e_bcsr[8]) & BCSR8_MDIO_DATA; +} + +static const struct mdiobb_ops ep8248e_mdio_ops = { + .set_mdc = ep8248e_set_mdc, + .set_mdio_dir = ep8248e_set_mdio_dir, + .set_mdio_data = ep8248e_set_mdio_data, + .get_mdio_data = ep8248e_get_mdio_data, + .owner = THIS_MODULE, +}; + +static struct mdiobb_ctrl ep8248e_mdio_ctrl = { + .ops = &ep8248e_mdio_ops, +}; + +static int __devinit ep8248e_mdio_probe(struct platform_device *ofdev) +{ + struct mii_bus *bus; + struct resource res; + struct device_node *node; + int ret; + + node = of_get_parent(ofdev->dev.of_node); + of_node_put(node); + if (node != ep8248e_bcsr_node) + return -ENODEV; + + ret = of_address_to_resource(ofdev->dev.of_node, 0, &res); + if (ret) + return ret; + + bus = alloc_mdio_bitbang(&ep8248e_mdio_ctrl); + if (!bus) + return -ENOMEM; + + bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); + if (bus->irq == NULL) { + ret = -ENOMEM; + goto err_free_bus; + } + + bus->name = "ep8248e-mdio-bitbang"; + bus->parent = &ofdev->dev; + snprintf(bus->id, MII_BUS_ID_SIZE, "%x", res.start); + + ret = of_mdiobus_register(bus, ofdev->dev.of_node); + if (ret) + goto err_free_irq; + + return 0; +err_free_irq: + kfree(bus->irq); +err_free_bus: + free_mdio_bitbang(bus); + return ret; +} + +static int ep8248e_mdio_remove(struct platform_device *ofdev) +{ + BUG(); + return 0; +} + +static const struct of_device_id ep8248e_mdio_match[] = { + { + .compatible = "fsl,ep8248e-mdio-bitbang", + }, + {}, +}; + +static struct platform_driver ep8248e_mdio_driver = { + .driver = { + .name = "ep8248e-mdio-bitbang", + .owner = THIS_MODULE, + .of_match_table = ep8248e_mdio_match, + }, + .probe = ep8248e_mdio_probe, + .remove = ep8248e_mdio_remove, +}; + +struct cpm_pin { + int port, pin, flags; +}; + +static __initdata struct cpm_pin ep8248e_pins[] = { + /* SMC1 */ + {2, 4, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 5, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + + /* SCC1 */ + {2, 14, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 15, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {3, 29, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {3, 30, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {3, 31, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* FCC1 */ + {0, 14, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 15, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 16, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 17, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 18, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 19, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 20, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 21, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 26, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {0, 27, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {0, 28, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {0, 29, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {0, 30, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {0, 31, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {2, 21, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 22, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* FCC2 */ + {1, 18, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 19, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 20, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 21, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 22, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 23, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 24, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 25, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 26, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 27, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 28, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 29, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {1, 30, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 31, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {2, 18, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 19, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* I2C */ + {4, 14, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {4, 15, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + + /* USB */ + {2, 10, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 11, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 20, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {2, 24, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {3, 23, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {3, 24, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {3, 25, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, +}; + +static void __init init_ioports(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ep8248e_pins); i++) { + const struct cpm_pin *pin = &ep8248e_pins[i]; + cpm2_set_pin(pin->port, pin->pin, pin->flags); + } + + cpm2_smc_clk_setup(CPM_CLK_SMC1, CPM_BRG7); + cpm2_clk_setup(CPM_CLK_SCC1, CPM_BRG1, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_SCC1, CPM_BRG1, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_SCC3, CPM_CLK8, CPM_CLK_TX); /* USB */ + cpm2_clk_setup(CPM_CLK_FCC1, CPM_CLK11, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_FCC1, CPM_CLK10, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_FCC2, CPM_CLK13, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_FCC2, CPM_CLK14, CPM_CLK_TX); +} + +static void __init ep8248e_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("ep8248e_setup_arch()", 0); + + cpm2_reset(); + + /* When this is set, snooping CPM DMA from RAM causes + * machine checks. See erratum SIU18. + */ + clrbits32(&cpm2_immr->im_siu_conf.siu_82xx.sc_bcr, MPC82XX_BCR_PLDP); + + ep8248e_bcsr_node = + of_find_compatible_node(NULL, NULL, "fsl,ep8248e-bcsr"); + if (!ep8248e_bcsr_node) { + printk(KERN_ERR "No bcsr in device tree\n"); + return; + } + + ep8248e_bcsr = of_iomap(ep8248e_bcsr_node, 0); + if (!ep8248e_bcsr) { + printk(KERN_ERR "Cannot map BCSR registers\n"); + of_node_put(ep8248e_bcsr_node); + ep8248e_bcsr_node = NULL; + return; + } + + setbits8(&ep8248e_bcsr[7], BCSR7_SCC2_ENABLE); + setbits8(&ep8248e_bcsr[8], BCSR8_PHY1_ENABLE | BCSR8_PHY1_POWER | + BCSR8_PHY2_ENABLE | BCSR8_PHY2_POWER); + + init_ioports(); + + if (ppc_md.progress) + ppc_md.progress("ep8248e_setup_arch(), finish", 0); +} + +static __initdata struct of_device_id of_bus_ids[] = { + { .compatible = "simple-bus", }, + { .compatible = "fsl,ep8248e-bcsr", }, + {}, +}; + +static int __init declare_of_platform_devices(void) +{ + of_platform_bus_probe(NULL, of_bus_ids, NULL); + platform_driver_register(&ep8248e_mdio_driver); + + return 0; +} +machine_device_initcall(ep8248e, declare_of_platform_devices); + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init ep8248e_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + return of_flat_dt_is_compatible(root, "fsl,ep8248e"); +} + +define_machine(ep8248e) +{ + .name = "Embedded Planet EP8248E", + .probe = ep8248e_probe, + .setup_arch = ep8248e_setup_arch, + .init_IRQ = ep8248e_pic_init, + .get_irq = cpm2_get_irq, + .calibrate_decr = generic_calibrate_decr, + .restart = pq2_restart, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/82xx/km82xx.c b/arch/powerpc/platforms/82xx/km82xx.c new file mode 100644 index 00000000000..3661bcdc326 --- /dev/null +++ b/arch/powerpc/platforms/82xx/km82xx.c @@ -0,0 +1,210 @@ +/* + * Keymile km82xx support + * Copyright 2008-2011 DENX Software Engineering GmbH + * Author: Heiko Schocher <hs@denx.de> + * + * based on code from: + * Copyright 2007 Freescale Semiconductor, Inc. + * Author: Scott Wood <scottwood@freescale.com> + * + * 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 <linux/init.h> +#include <linux/interrupt.h> +#include <linux/fsl_devices.h> +#include <linux/of_platform.h> + +#include <asm/io.h> +#include <asm/cpm2.h> +#include <asm/udbg.h> +#include <asm/machdep.h> +#include <asm/time.h> +#include <asm/mpc8260.h> +#include <asm/prom.h> + +#include <sysdev/fsl_soc.h> +#include <sysdev/cpm2_pic.h> + +#include "pq2.h" + +static void __init km82xx_pic_init(void) +{ + struct device_node *np = of_find_compatible_node(NULL, NULL, + "fsl,pq2-pic"); + if (!np) { + printk(KERN_ERR "PIC init: can not find cpm-pic node\n"); + return; + } + + cpm2_pic_init(np); + of_node_put(np); +} + +struct cpm_pin { + int port, pin, flags; +}; + +static __initdata struct cpm_pin km82xx_pins[] = { + /* SMC1 */ + {2, 4, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 5, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + + /* SMC2 */ + {0, 8, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 9, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + + /* SCC1 */ + {2, 21, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 15, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {3, 31, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {3, 30, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + + /* SCC4 */ + {2, 25, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 24, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 9, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 8, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {3, 22, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {3, 21, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + + /* FCC1 */ + {0, 14, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 15, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 16, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 17, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 18, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 19, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 20, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 21, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 26, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {0, 27, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {0, 28, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {0, 29, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {0, 30, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {0, 31, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + + {2, 22, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 23, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* FCC2 */ + {1, 18, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 19, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 20, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 21, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 22, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 23, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 24, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 25, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 26, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 27, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 28, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 29, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {1, 30, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 31, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + + {2, 18, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 19, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* MDC */ + {0, 13, CPM_PIN_OUTPUT | CPM_PIN_GPIO}, + +#if defined(CONFIG_I2C_CPM) + /* I2C */ + {3, 14, CPM_PIN_INPUT | CPM_PIN_SECONDARY | CPM_PIN_OPENDRAIN}, + {3, 15, CPM_PIN_INPUT | CPM_PIN_SECONDARY | CPM_PIN_OPENDRAIN}, +#endif + + /* USB */ + {0, 10, CPM_PIN_OUTPUT | CPM_PIN_GPIO}, /* FULL_SPEED */ + {0, 11, CPM_PIN_OUTPUT | CPM_PIN_GPIO}, /*/SLAVE */ + {2, 10, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* RXN */ + {2, 11, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* RXP */ + {2, 20, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, /* /OE */ + {2, 27, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* RXCLK */ + {3, 23, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, /* TXP */ + {3, 24, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, /* TXN */ + {3, 25, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* RXD */ +}; + +static void __init init_ioports(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(km82xx_pins); i++) { + const struct cpm_pin *pin = &km82xx_pins[i]; + cpm2_set_pin(pin->port, pin->pin, pin->flags); + } + + cpm2_smc_clk_setup(CPM_CLK_SMC2, CPM_BRG8); + cpm2_smc_clk_setup(CPM_CLK_SMC1, CPM_BRG7); + cpm2_clk_setup(CPM_CLK_SCC1, CPM_CLK11, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_SCC1, CPM_CLK11, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_SCC3, CPM_CLK5, CPM_CLK_RTX); + cpm2_clk_setup(CPM_CLK_SCC4, CPM_CLK7, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_SCC4, CPM_CLK8, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_FCC1, CPM_CLK10, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_FCC1, CPM_CLK9, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_FCC2, CPM_CLK13, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_FCC2, CPM_CLK14, CPM_CLK_TX); + + /* Force USB FULL SPEED bit to '1' */ + setbits32(&cpm2_immr->im_ioport.iop_pdata, 1 << (31 - 10)); + /* clear USB_SLAVE */ + clrbits32(&cpm2_immr->im_ioport.iop_pdata, 1 << (31 - 11)); +} + +static void __init km82xx_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("km82xx_setup_arch()", 0); + + cpm2_reset(); + + /* When this is set, snooping CPM DMA from RAM causes + * machine checks. See erratum SIU18. + */ + clrbits32(&cpm2_immr->im_siu_conf.siu_82xx.sc_bcr, MPC82XX_BCR_PLDP); + + init_ioports(); + + if (ppc_md.progress) + ppc_md.progress("km82xx_setup_arch(), finish", 0); +} + +static __initdata struct of_device_id of_bus_ids[] = { + { .compatible = "simple-bus", }, + {}, +}; + +static int __init declare_of_platform_devices(void) +{ + of_platform_bus_probe(NULL, of_bus_ids, NULL); + + return 0; +} +machine_device_initcall(km82xx, declare_of_platform_devices); + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init km82xx_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + return of_flat_dt_is_compatible(root, "keymile,km82xx"); +} + +define_machine(km82xx) +{ + .name = "Keymile km82xx", + .probe = km82xx_probe, + .setup_arch = km82xx_setup_arch, + .init_IRQ = km82xx_pic_init, + .get_irq = cpm2_get_irq, + .calibrate_decr = generic_calibrate_decr, + .restart = pq2_restart, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/82xx/m82xx_pci.h b/arch/powerpc/platforms/82xx/m82xx_pci.h new file mode 100644 index 00000000000..65e38a7ff48 --- /dev/null +++ b/arch/powerpc/platforms/82xx/m82xx_pci.h @@ -0,0 +1,17 @@ +#ifndef _PPC_KERNEL_M82XX_PCI_H +#define _PPC_KERNEL_M82XX_PCI_H + +/* + * 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. + */ + +#define SIU_INT_IRQ1 ((uint)0x13 + CPM_IRQ_OFFSET) + +#ifndef _IO_BASE +#define _IO_BASE isa_io_base +#endif + +#endif /* _PPC_KERNEL_M8260_PCI_H */ diff --git a/arch/powerpc/platforms/82xx/mpc8272_ads.c b/arch/powerpc/platforms/82xx/mpc8272_ads.c new file mode 100644 index 00000000000..30394b409b3 --- /dev/null +++ b/arch/powerpc/platforms/82xx/mpc8272_ads.c @@ -0,0 +1,216 @@ +/* + * MPC8272 ADS board support + * + * Copyright 2007 Freescale Semiconductor, Inc. + * Author: Scott Wood <scottwood@freescale.com> + * + * Based on code by Vitaly Bordug <vbordug@ru.mvista.com> + * Copyright (c) 2006 MontaVista Software, 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 <linux/init.h> +#include <linux/interrupt.h> +#include <linux/fsl_devices.h> +#include <linux/of_platform.h> +#include <linux/io.h> + +#include <asm/cpm2.h> +#include <asm/udbg.h> +#include <asm/machdep.h> +#include <asm/time.h> + +#include <platforms/82xx/pq2.h> + +#include <sysdev/fsl_soc.h> +#include <sysdev/cpm2_pic.h> + +#include "pq2.h" + +static void __init mpc8272_ads_pic_init(void) +{ + struct device_node *np = of_find_compatible_node(NULL, NULL, + "fsl,cpm2-pic"); + if (!np) { + printk(KERN_ERR "PIC init: can not find fsl,cpm2-pic node\n"); + return; + } + + cpm2_pic_init(np); + of_node_put(np); + + /* Initialize stuff for the 82xx CPLD IC and install demux */ + pq2ads_pci_init_irq(); +} + +struct cpm_pin { + int port, pin, flags; +}; + +static struct cpm_pin mpc8272_ads_pins[] = { + /* SCC1 */ + {3, 30, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {3, 31, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* SCC4 */ + {3, 21, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {3, 22, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* FCC1 */ + {0, 14, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 15, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 16, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 17, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 18, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 19, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 20, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 21, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 26, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {0, 27, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {0, 28, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {0, 29, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {0, 30, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {0, 31, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {2, 21, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 22, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* FCC2 */ + {1, 18, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 19, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 20, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 21, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 22, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 23, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 24, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 25, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 26, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 27, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 28, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 29, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {1, 30, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 31, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {2, 16, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 17, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* I2C */ + {3, 14, CPM_PIN_INPUT | CPM_PIN_SECONDARY | CPM_PIN_OPENDRAIN}, + {3, 15, CPM_PIN_INPUT | CPM_PIN_SECONDARY | CPM_PIN_OPENDRAIN}, + + /* USB */ + {2, 10, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 11, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 20, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {2, 24, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {3, 23, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {3, 24, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {3, 25, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, +}; + +static void __init init_ioports(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mpc8272_ads_pins); i++) { + struct cpm_pin *pin = &mpc8272_ads_pins[i]; + cpm2_set_pin(pin->port, pin->pin, pin->flags); + } + + cpm2_clk_setup(CPM_CLK_SCC1, CPM_BRG1, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_SCC1, CPM_BRG1, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_SCC3, CPM_CLK8, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_SCC3, CPM_CLK8, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_SCC4, CPM_BRG4, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_SCC4, CPM_BRG4, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_FCC1, CPM_CLK11, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_FCC1, CPM_CLK10, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_FCC2, CPM_CLK15, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_FCC2, CPM_CLK16, CPM_CLK_TX); +} + +static void __init mpc8272_ads_setup_arch(void) +{ + struct device_node *np; + __be32 __iomem *bcsr; + + if (ppc_md.progress) + ppc_md.progress("mpc8272_ads_setup_arch()", 0); + + cpm2_reset(); + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc8272ads-bcsr"); + if (!np) { + printk(KERN_ERR "No bcsr in device tree\n"); + return; + } + + bcsr = of_iomap(np, 0); + of_node_put(np); + if (!bcsr) { + printk(KERN_ERR "Cannot map BCSR registers\n"); + return; + } + +#define BCSR1_FETHIEN 0x08000000 +#define BCSR1_FETH_RST 0x04000000 +#define BCSR1_RS232_EN1 0x02000000 +#define BCSR1_RS232_EN2 0x01000000 +#define BCSR3_USB_nEN 0x80000000 +#define BCSR3_FETHIEN2 0x10000000 +#define BCSR3_FETH2_RST 0x08000000 + + clrbits32(&bcsr[1], BCSR1_RS232_EN1 | BCSR1_RS232_EN2 | BCSR1_FETHIEN); + setbits32(&bcsr[1], BCSR1_FETH_RST); + + clrbits32(&bcsr[3], BCSR3_FETHIEN2); + setbits32(&bcsr[3], BCSR3_FETH2_RST); + + clrbits32(&bcsr[3], BCSR3_USB_nEN); + + iounmap(bcsr); + + init_ioports(); + pq2_init_pci(); + + if (ppc_md.progress) + ppc_md.progress("mpc8272_ads_setup_arch(), finish", 0); +} + +static struct of_device_id __initdata of_bus_ids[] = { + { .name = "soc", }, + { .name = "cpm", }, + { .name = "localbus", }, + {}, +}; + +static int __init declare_of_platform_devices(void) +{ + /* Publish the QE devices */ + of_platform_bus_probe(NULL, of_bus_ids, NULL); + return 0; +} +machine_device_initcall(mpc8272_ads, declare_of_platform_devices); + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init mpc8272_ads_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + return of_flat_dt_is_compatible(root, "fsl,mpc8272ads"); +} + +define_machine(mpc8272_ads) +{ + .name = "Freescale MPC8272 ADS", + .probe = mpc8272_ads_probe, + .setup_arch = mpc8272_ads_setup_arch, + .init_IRQ = mpc8272_ads_pic_init, + .get_irq = cpm2_get_irq, + .calibrate_decr = generic_calibrate_decr, + .restart = pq2_restart, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/82xx/pq2.c b/arch/powerpc/platforms/82xx/pq2.c new file mode 100644 index 00000000000..d111b024eaf --- /dev/null +++ b/arch/powerpc/platforms/82xx/pq2.c @@ -0,0 +1,82 @@ +/* + * Common PowerQUICC II code. + * + * Author: Scott Wood <scottwood@freescale.com> + * Copyright (c) 2007 Freescale Semiconductor + * + * Based on code by Vitaly Bordug <vbordug@ru.mvista.com> + * pq2_restart fix by Wade Farnsworth <wfarnsworth@mvista.com> + * Copyright (c) 2006 MontaVista Software, 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 <asm/cpm2.h> +#include <asm/io.h> +#include <asm/pci-bridge.h> +#include <asm/system.h> + +#include <platforms/82xx/pq2.h> + +#define RMR_CSRE 0x00000001 + +void pq2_restart(char *cmd) +{ + local_irq_disable(); + setbits32(&cpm2_immr->im_clkrst.car_rmr, RMR_CSRE); + + /* Clear the ME,EE,IR & DR bits in MSR to cause checkstop */ + mtmsr(mfmsr() & ~(MSR_ME | MSR_EE | MSR_IR | MSR_DR)); + in_8(&cpm2_immr->im_clkrst.res[0]); + + panic("Restart failed\n"); +} + +#ifdef CONFIG_PCI +static int pq2_pci_exclude_device(struct pci_controller *hose, + u_char bus, u8 devfn) +{ + if (bus == 0 && PCI_SLOT(devfn) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + else + return PCIBIOS_SUCCESSFUL; +} + +static void __init pq2_pci_add_bridge(struct device_node *np) +{ + struct pci_controller *hose; + struct resource r; + + if (of_address_to_resource(np, 0, &r) || r.end - r.start < 0x10b) + goto err; + + pci_add_flags(PCI_REASSIGN_ALL_BUS); + + hose = pcibios_alloc_controller(np); + if (!hose) + return; + + hose->dn = np; + + setup_indirect_pci(hose, r.start + 0x100, r.start + 0x104, 0); + pci_process_bridge_OF_ranges(hose, np, 1); + + return; + +err: + printk(KERN_ERR "No valid PCI reg property in device tree\n"); +} + +void __init pq2_init_pci(void) +{ + struct device_node *np = NULL; + + ppc_md.pci_exclude_device = pq2_pci_exclude_device; + + while ((np = of_find_compatible_node(np, NULL, "fsl,pq2-pci"))) + pq2_pci_add_bridge(np); +} +#endif diff --git a/arch/powerpc/platforms/82xx/pq2.h b/arch/powerpc/platforms/82xx/pq2.h new file mode 100644 index 00000000000..a41f84ae232 --- /dev/null +++ b/arch/powerpc/platforms/82xx/pq2.h @@ -0,0 +1,20 @@ +#ifndef _PQ2_H +#define _PQ2_H + +void pq2_restart(char *cmd); + +#ifdef CONFIG_PCI +int pq2ads_pci_init_irq(void); +void pq2_init_pci(void); +#else +static inline int pq2ads_pci_init_irq(void) +{ + return 0; +} + +static inline void pq2_init_pci(void) +{ +} +#endif + +#endif diff --git a/arch/powerpc/platforms/82xx/pq2ads-pci-pic.c b/arch/powerpc/platforms/82xx/pq2ads-pci-pic.c new file mode 100644 index 00000000000..8ccf9ed62fe --- /dev/null +++ b/arch/powerpc/platforms/82xx/pq2ads-pci-pic.c @@ -0,0 +1,186 @@ +/* + * PQ2 ADS-style PCI interrupt controller + * + * Copyright 2007 Freescale Semiconductor, Inc. + * Author: Scott Wood <scottwood@freescale.com> + * + * Loosely based on mpc82xx ADS support by Vitaly Bordug <vbordug@ru.mvista.com> + * Copyright (c) 2006 MontaVista Software, 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 published + * by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/irq.h> +#include <linux/types.h> +#include <linux/bootmem.h> +#include <linux/slab.h> + +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/cpm2.h> + +#include "pq2.h" + +static DEFINE_RAW_SPINLOCK(pci_pic_lock); + +struct pq2ads_pci_pic { + struct device_node *node; + struct irq_host *host; + + struct { + u32 stat; + u32 mask; + } __iomem *regs; +}; + +#define NUM_IRQS 32 + +static void pq2ads_pci_mask_irq(struct irq_data *d) +{ + struct pq2ads_pci_pic *priv = irq_data_get_irq_chip_data(d); + int irq = NUM_IRQS - irqd_to_hwirq(d) - 1; + + if (irq != -1) { + unsigned long flags; + raw_spin_lock_irqsave(&pci_pic_lock, flags); + + setbits32(&priv->regs->mask, 1 << irq); + mb(); + + raw_spin_unlock_irqrestore(&pci_pic_lock, flags); + } +} + +static void pq2ads_pci_unmask_irq(struct irq_data *d) +{ + struct pq2ads_pci_pic *priv = irq_data_get_irq_chip_data(d); + int irq = NUM_IRQS - irqd_to_hwirq(d) - 1; + + if (irq != -1) { + unsigned long flags; + + raw_spin_lock_irqsave(&pci_pic_lock, flags); + clrbits32(&priv->regs->mask, 1 << irq); + raw_spin_unlock_irqrestore(&pci_pic_lock, flags); + } +} + +static struct irq_chip pq2ads_pci_ic = { + .name = "PQ2 ADS PCI", + .irq_mask = pq2ads_pci_mask_irq, + .irq_mask_ack = pq2ads_pci_mask_irq, + .irq_ack = pq2ads_pci_mask_irq, + .irq_unmask = pq2ads_pci_unmask_irq, + .irq_enable = pq2ads_pci_unmask_irq, + .irq_disable = pq2ads_pci_mask_irq +}; + +static void pq2ads_pci_irq_demux(unsigned int irq, struct irq_desc *desc) +{ + struct pq2ads_pci_pic *priv = irq_desc_get_handler_data(desc); + u32 stat, mask, pend; + int bit; + + for (;;) { + stat = in_be32(&priv->regs->stat); + mask = in_be32(&priv->regs->mask); + + pend = stat & ~mask; + + if (!pend) + break; + + for (bit = 0; pend != 0; ++bit, pend <<= 1) { + if (pend & 0x80000000) { + int virq = irq_linear_revmap(priv->host, bit); + generic_handle_irq(virq); + } + } + } +} + +static int pci_pic_host_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_status_flags(virq, IRQ_LEVEL); + irq_set_chip_data(virq, h->host_data); + irq_set_chip_and_handler(virq, &pq2ads_pci_ic, handle_level_irq); + return 0; +} + +static struct irq_host_ops pci_pic_host_ops = { + .map = pci_pic_host_map, +}; + +int __init pq2ads_pci_init_irq(void) +{ + struct pq2ads_pci_pic *priv; + struct irq_host *host; + struct device_node *np; + int ret = -ENODEV; + int irq; + + np = of_find_compatible_node(NULL, NULL, "fsl,pq2ads-pci-pic"); + if (!np) { + printk(KERN_ERR "No pci pic node in device tree.\n"); + of_node_put(np); + goto out; + } + + irq = irq_of_parse_and_map(np, 0); + if (irq == NO_IRQ) { + printk(KERN_ERR "No interrupt in pci pic node.\n"); + of_node_put(np); + goto out; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + of_node_put(np); + ret = -ENOMEM; + goto out_unmap_irq; + } + + /* PCI interrupt controller registers: status and mask */ + priv->regs = of_iomap(np, 0); + if (!priv->regs) { + printk(KERN_ERR "Cannot map PCI PIC registers.\n"); + goto out_free_bootmem; + } + + /* mask all PCI interrupts */ + out_be32(&priv->regs->mask, ~0); + mb(); + + host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, NUM_IRQS, + &pci_pic_host_ops, NUM_IRQS); + if (!host) { + ret = -ENOMEM; + goto out_unmap_regs; + } + + host->host_data = priv; + + priv->host = host; + host->host_data = priv; + irq_set_handler_data(irq, priv); + irq_set_chained_handler(irq, pq2ads_pci_irq_demux); + + of_node_put(np); + return 0; + +out_unmap_regs: + iounmap(priv->regs); +out_free_bootmem: + free_bootmem((unsigned long)priv, + sizeof(struct pq2ads_pci_pic)); + of_node_put(np); +out_unmap_irq: + irq_dispose_mapping(irq); +out: + return ret; +} diff --git a/arch/powerpc/platforms/82xx/pq2ads.h b/arch/powerpc/platforms/82xx/pq2ads.h new file mode 100644 index 00000000000..6cf0f97486e --- /dev/null +++ b/arch/powerpc/platforms/82xx/pq2ads.h @@ -0,0 +1,44 @@ +/* + * PQ2/mpc8260 board-specific stuff + * + * A collection of structures, addresses, and values associated with + * the Freescale MPC8260ADS/MPC8266ADS-PCI boards. + * Copied from the RPX-Classic and SBS8260 stuff. + * + * Author: Vitaly Bordug <vbordug@ru.mvista.com> + * + * Originally written by Dan Malek for Motorola MPC8260 family + * + * Copyright (c) 2001 Dan Malek <dan@embeddedalley.com> + * Copyright (c) 2006 MontaVista Software, 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. + */ + +#ifdef __KERNEL__ +#ifndef __MACH_ADS8260_DEFS +#define __MACH_ADS8260_DEFS + +#include <linux/seq_file.h> + +/* The ADS8260 has 16, 32-bit wide control/status registers, accessed + * only on word boundaries. + * Not all are used (yet), or are interesting to us (yet). + */ + +/* Things of interest in the CSR. + */ +#define BCSR0_LED0 ((uint)0x02000000) /* 0 == on */ +#define BCSR0_LED1 ((uint)0x01000000) /* 0 == on */ +#define BCSR1_FETHIEN ((uint)0x08000000) /* 0 == enable*/ +#define BCSR1_FETH_RST ((uint)0x04000000) /* 0 == reset */ +#define BCSR1_RS232_EN1 ((uint)0x02000000) /* 0 ==enable */ +#define BCSR1_RS232_EN2 ((uint)0x01000000) /* 0 ==enable */ +#define BCSR3_FETHIEN2 ((uint)0x10000000) /* 0 == enable*/ +#define BCSR3_FETH2_RST ((uint)0x80000000) /* 0 == reset */ + +#endif /* __MACH_ADS8260_DEFS */ +#endif /* __KERNEL__ */ diff --git a/arch/powerpc/platforms/82xx/pq2fads.c b/arch/powerpc/platforms/82xx/pq2fads.c new file mode 100644 index 00000000000..e1dceeec499 --- /dev/null +++ b/arch/powerpc/platforms/82xx/pq2fads.c @@ -0,0 +1,194 @@ +/* + * PQ2FADS board support + * + * Copyright 2007 Freescale Semiconductor, Inc. + * Author: Scott Wood <scottwood@freescale.com> + * + * Loosely based on mp82xx ADS support by Vitaly Bordug <vbordug@ru.mvista.com> + * Copyright (c) 2006 MontaVista Software, 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 published + * by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/fsl_devices.h> +#include <linux/of_platform.h> + +#include <asm/io.h> +#include <asm/cpm2.h> +#include <asm/udbg.h> +#include <asm/machdep.h> +#include <asm/time.h> + +#include <sysdev/fsl_soc.h> +#include <sysdev/cpm2_pic.h> + +#include "pq2ads.h" +#include "pq2.h" + +static void __init pq2fads_pic_init(void) +{ + struct device_node *np = of_find_compatible_node(NULL, NULL, "fsl,cpm2-pic"); + if (!np) { + printk(KERN_ERR "PIC init: can not find fsl,cpm2-pic node\n"); + return; + } + + cpm2_pic_init(np); + of_node_put(np); + + /* Initialize stuff for the 82xx CPLD IC and install demux */ + pq2ads_pci_init_irq(); +} + +struct cpm_pin { + int port, pin, flags; +}; + +static struct cpm_pin pq2fads_pins[] = { + /* SCC1 */ + {3, 30, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {3, 31, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* SCC2 */ + {3, 27, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {3, 28, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* FCC2 */ + {1, 18, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 19, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 20, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 21, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 22, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 23, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 24, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 25, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 26, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 27, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 28, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 29, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {1, 30, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 31, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {2, 18, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 19, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* FCC3 */ + {1, 4, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 5, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 6, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 7, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 8, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 9, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 10, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 11, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 12, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 13, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 14, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 15, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 16, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 17, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 16, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 17, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, +}; + +static void __init init_ioports(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pq2fads_pins); i++) { + struct cpm_pin *pin = &pq2fads_pins[i]; + cpm2_set_pin(pin->port, pin->pin, pin->flags); + } + + cpm2_clk_setup(CPM_CLK_SCC1, CPM_BRG1, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_SCC1, CPM_BRG1, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_SCC2, CPM_BRG2, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_SCC2, CPM_BRG2, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_FCC2, CPM_CLK13, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_FCC2, CPM_CLK14, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_FCC3, CPM_CLK15, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_FCC3, CPM_CLK16, CPM_CLK_TX); +} + +static void __init pq2fads_setup_arch(void) +{ + struct device_node *np; + __be32 __iomem *bcsr; + + if (ppc_md.progress) + ppc_md.progress("pq2fads_setup_arch()", 0); + + cpm2_reset(); + + np = of_find_compatible_node(NULL, NULL, "fsl,pq2fads-bcsr"); + if (!np) { + printk(KERN_ERR "No fsl,pq2fads-bcsr in device tree\n"); + return; + } + + bcsr = of_iomap(np, 0); + of_node_put(np); + if (!bcsr) { + printk(KERN_ERR "Cannot map BCSR registers\n"); + return; + } + + /* Enable the serial and ethernet ports */ + + clrbits32(&bcsr[1], BCSR1_RS232_EN1 | BCSR1_RS232_EN2 | BCSR1_FETHIEN); + setbits32(&bcsr[1], BCSR1_FETH_RST); + + clrbits32(&bcsr[3], BCSR3_FETHIEN2); + setbits32(&bcsr[3], BCSR3_FETH2_RST); + + iounmap(bcsr); + + init_ioports(); + + /* Enable external IRQs */ + clrbits32(&cpm2_immr->im_siu_conf.siu_82xx.sc_siumcr, 0x0c000000); + + pq2_init_pci(); + + if (ppc_md.progress) + ppc_md.progress("pq2fads_setup_arch(), finish", 0); +} + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init pq2fads_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + return of_flat_dt_is_compatible(root, "fsl,pq2fads"); +} + +static struct of_device_id __initdata of_bus_ids[] = { + { .name = "soc", }, + { .name = "cpm", }, + { .name = "localbus", }, + {}, +}; + +static int __init declare_of_platform_devices(void) +{ + /* Publish the QE devices */ + of_platform_bus_probe(NULL, of_bus_ids, NULL); + return 0; +} +machine_device_initcall(pq2fads, declare_of_platform_devices); + +define_machine(pq2fads) +{ + .name = "Freescale PQ2FADS", + .probe = pq2fads_probe, + .setup_arch = pq2fads_setup_arch, + .init_IRQ = pq2fads_pic_init, + .get_irq = cpm2_get_irq, + .calibrate_decr = generic_calibrate_decr, + .restart = pq2_restart, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig new file mode 100644 index 00000000000..670a033264c --- /dev/null +++ b/arch/powerpc/platforms/83xx/Kconfig @@ -0,0 +1,134 @@ +menuconfig PPC_83xx + bool "83xx-based boards" + depends on 6xx + select PPC_UDBG_16550 + select PPC_PCI_CHOICE + select FSL_PCI if PCI + select FSL_SOC + select IPIC + +if PPC_83xx + +config MPC830x_RDB + bool "Freescale MPC830x RDB and derivatives" + select DEFAULT_UIMAGE + select PPC_MPC831x + select FSL_GTM + help + This option enables support for the MPC8308 RDB and MPC8308 P1M boards. + +config MPC831x_RDB + bool "Freescale MPC831x RDB" + select DEFAULT_UIMAGE + select PPC_MPC831x + help + This option enables support for the MPC8313 RDB and MPC8315 RDB boards. + +config MPC832x_MDS + bool "Freescale MPC832x MDS" + select DEFAULT_UIMAGE + select PPC_MPC832x + help + This option enables support for the MPC832x MDS evaluation board. + +config MPC832x_RDB + bool "Freescale MPC832x RDB" + select DEFAULT_UIMAGE + select PPC_MPC832x + help + This option enables support for the MPC8323 RDB board. + +config MPC834x_MDS + bool "Freescale MPC834x MDS" + select DEFAULT_UIMAGE + select PPC_MPC834x + help + This option enables support for the MPC 834x MDS evaluation board. + + Be aware that PCI buses can only function when MDS board is plugged + into the PIB (Platform IO Board) board from Freescale which provide + 3 PCI slots. The PIBs PCI initialization is the bootloader's + responsibility. + +config MPC834x_ITX + bool "Freescale MPC834x ITX" + select DEFAULT_UIMAGE + select PPC_MPC834x + help + This option enables support for the MPC 834x ITX evaluation board. + + Be aware that PCI initialization is the bootloader's + responsibility. + +config MPC836x_MDS + bool "Freescale MPC836x MDS" + select DEFAULT_UIMAGE + help + This option enables support for the MPC836x MDS Processor Board. + +config MPC836x_RDK + bool "Freescale/Logic MPC836x RDK" + select DEFAULT_UIMAGE + select FSL_GTM + select FSL_LBC + help + This option enables support for the MPC836x RDK Processor Board, + also known as ZOOM PowerQUICC Kit. + +config MPC837x_MDS + bool "Freescale MPC837x MDS" + select DEFAULT_UIMAGE + select PPC_MPC837x + help + This option enables support for the MPC837x MDS Processor Board. + +config MPC837x_RDB + bool "Freescale MPC837x RDB/WLAN" + select DEFAULT_UIMAGE + select PPC_MPC837x + help + This option enables support for the MPC837x RDB and WLAN Boards. + +config SBC834x + bool "Wind River SBC834x" + select DEFAULT_UIMAGE + select PPC_MPC834x + help + This option enables support for the Wind River SBC834x board. + +config ASP834x + bool "Analogue & Micro ASP 834x" + select PPC_MPC834x + select REDBOOT + help + This enables support for the Analogue & Micro ASP 83xx + board. + +config KMETER1 + bool "Keymile KMETER1" + select DEFAULT_UIMAGE + select QUICC_ENGINE + help + This enables support for the Keymile KMETER1 board. + + +endif + +# used for usb & gpio +config PPC_MPC831x + bool + select ARCH_WANT_OPTIONAL_GPIOLIB + +# used for math-emu +config PPC_MPC832x + bool + +# used for usb & gpio +config PPC_MPC834x + bool + select ARCH_WANT_OPTIONAL_GPIOLIB + +# used for usb & gpio +config PPC_MPC837x + bool + select ARCH_WANT_OPTIONAL_GPIOLIB diff --git a/arch/powerpc/platforms/83xx/Makefile b/arch/powerpc/platforms/83xx/Makefile new file mode 100644 index 00000000000..ed95bfcbcbf --- /dev/null +++ b/arch/powerpc/platforms/83xx/Makefile @@ -0,0 +1,19 @@ +# +# Makefile for the PowerPC 83xx linux kernel. +# +obj-y := misc.o usb.o +obj-$(CONFIG_SUSPEND) += suspend.o suspend-asm.o +obj-$(CONFIG_MCU_MPC8349EMITX) += mcu_mpc8349emitx.o +obj-$(CONFIG_MPC830x_RDB) += mpc830x_rdb.o +obj-$(CONFIG_MPC831x_RDB) += mpc831x_rdb.o +obj-$(CONFIG_MPC832x_RDB) += mpc832x_rdb.o +obj-$(CONFIG_MPC834x_MDS) += mpc834x_mds.o +obj-$(CONFIG_MPC834x_ITX) += mpc834x_itx.o +obj-$(CONFIG_MPC836x_MDS) += mpc836x_mds.o +obj-$(CONFIG_MPC836x_RDK) += mpc836x_rdk.o +obj-$(CONFIG_MPC832x_MDS) += mpc832x_mds.o +obj-$(CONFIG_MPC837x_MDS) += mpc837x_mds.o +obj-$(CONFIG_SBC834x) += sbc834x.o +obj-$(CONFIG_MPC837x_RDB) += mpc837x_rdb.o +obj-$(CONFIG_ASP834x) += asp834x.o +obj-$(CONFIG_KMETER1) += km83xx.o diff --git a/arch/powerpc/platforms/83xx/asp834x.c b/arch/powerpc/platforms/83xx/asp834x.c new file mode 100644 index 00000000000..464ea8e0292 --- /dev/null +++ b/arch/powerpc/platforms/83xx/asp834x.c @@ -0,0 +1,60 @@ +/* + * arch/powerpc/platforms/83xx/asp834x.c + * + * Analogue & Micro ASP8347 board specific routines + * clone of mpc834x_itx + * + * Copyright 2008 Codehermit + * + * Maintainer: Bryan O'Donoghue <bodonoghue@codhermit.ie> + * + * 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 <linux/pci.h> +#include <linux/of_platform.h> + +#include <asm/time.h> +#include <asm/ipic.h> +#include <asm/udbg.h> + +#include "mpc83xx.h" + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init asp834x_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("asp834x_setup_arch()", 0); + + mpc834x_usb_cfg(); +} + +machine_device_initcall(asp834x, mpc83xx_declare_of_platform_devices); + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init asp834x_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + return of_flat_dt_is_compatible(root, "analogue-and-micro,asp8347e"); +} + +define_machine(asp834x) { + .name = "ASP8347E", + .probe = asp834x_probe, + .setup_arch = asp834x_setup_arch, + .init_IRQ = mpc83xx_ipic_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/km83xx.c b/arch/powerpc/platforms/83xx/km83xx.c new file mode 100644 index 00000000000..65eb792a0d0 --- /dev/null +++ b/arch/powerpc/platforms/83xx/km83xx.c @@ -0,0 +1,159 @@ +/* + * Copyright 2008-2011 DENX Software Engineering GmbH + * Author: Heiko Schocher <hs@denx.de> + * + * Description: + * Keymile KMETER1 board specific routines. + * + * 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/reboot.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/major.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/seq_file.h> +#include <linux/root_dev.h> +#include <linux/initrd.h> +#include <linux/of_platform.h> +#include <linux/of_device.h> + +#include <asm/system.h> +#include <linux/atomic.h> +#include <asm/time.h> +#include <asm/io.h> +#include <asm/machdep.h> +#include <asm/ipic.h> +#include <asm/irq.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> +#include <asm/qe.h> +#include <asm/qe_ic.h> + +#include "mpc83xx.h" + +#define SVR_REV(svr) (((svr) >> 0) & 0xFFFF) /* Revision field */ +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init mpc83xx_km_setup_arch(void) +{ +#ifdef CONFIG_QUICC_ENGINE + struct device_node *np; +#endif + + if (ppc_md.progress) + ppc_md.progress("kmpbec83xx_setup_arch()", 0); + + mpc83xx_setup_pci(); + +#ifdef CONFIG_QUICC_ENGINE + qe_reset(); + + np = of_find_node_by_name(NULL, "par_io"); + if (np != NULL) { + par_io_init(np); + of_node_put(np); + + for_each_node_by_name(np, "spi") + par_io_of_config(np); + + for (np = NULL; (np = of_find_node_by_name(np, "ucc")) != NULL;) + par_io_of_config(np); + } + + np = of_find_compatible_node(NULL, "network", "ucc_geth"); + if (np != NULL) { + uint svid; + + /* handle mpc8360ea rev.2.1 erratum 2: RGMII Timing */ + svid = mfspr(SPRN_SVR); + if (SVR_REV(svid) == 0x0021) { + struct device_node *np_par; + struct resource res; + void __iomem *base; + int ret; + + np_par = of_find_node_by_name(NULL, "par_io"); + if (np_par == NULL) { + printk(KERN_WARNING "%s couldn;t find par_io node\n", + __func__); + return; + } + /* Map Parallel I/O ports registers */ + ret = of_address_to_resource(np_par, 0, &res); + if (ret) { + printk(KERN_WARNING "%s couldn;t map par_io registers\n", + __func__); + return; + } + base = ioremap(res.start, resource_size(&res)); + + /* + * IMMR + 0x14A8[4:5] = 11 (clk delay for UCC 2) + * IMMR + 0x14A8[18:19] = 11 (clk delay for UCC 1) + */ + setbits32((base + 0xa8), 0x0c003000); + + /* + * IMMR + 0x14AC[20:27] = 10101010 + * (data delay for both UCC's) + */ + clrsetbits_be32((base + 0xac), 0xff0, 0xaa0); + iounmap(base); + of_node_put(np_par); + } + of_node_put(np); + } +#endif /* CONFIG_QUICC_ENGINE */ +} + +machine_device_initcall(mpc83xx_km, mpc83xx_declare_of_platform_devices); + +/* list of the supported boards */ +static char *board[] __initdata = { + "Keymile,KMETER1", + "Keymile,kmpbec8321", + NULL +}; + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc83xx_km_probe(void) +{ + unsigned long node = of_get_flat_dt_root(); + int i = 0; + + while (board[i]) { + if (of_flat_dt_is_compatible(node, board[i])) + break; + i++; + } + return (board[i] != NULL); +} + +define_machine(mpc83xx_km) { + .name = "mpc83xx-km-platform", + .probe = mpc83xx_km_probe, + .setup_arch = mpc83xx_km_setup_arch, + .init_IRQ = mpc83xx_ipic_and_qe_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c new file mode 100644 index 00000000000..ef6537b8ed3 --- /dev/null +++ b/arch/powerpc/platforms/83xx/mcu_mpc8349emitx.c @@ -0,0 +1,250 @@ +/* + * Power Management and GPIO expander driver for MPC8349E-mITX-compatible MCU + * + * Copyright (c) 2008 MontaVista Software, Inc. + * + * Author: Anton Vorontsov <avorontsov@ru.mvista.com> + * + * 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 <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/mutex.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/slab.h> +#include <linux/kthread.h> +#include <linux/reboot.h> +#include <asm/prom.h> +#include <asm/machdep.h> + +/* + * I don't have specifications for the MCU firmware, I found this register + * and bits positions by the trial&error method. + */ +#define MCU_REG_CTRL 0x20 +#define MCU_CTRL_POFF 0x40 +#define MCU_CTRL_BTN 0x80 + +#define MCU_NUM_GPIO 2 + +struct mcu { + struct mutex lock; + struct i2c_client *client; + struct gpio_chip gc; + u8 reg_ctrl; +}; + +static struct mcu *glob_mcu; + +struct task_struct *shutdown_thread; +static int shutdown_thread_fn(void *data) +{ + int ret; + struct mcu *mcu = glob_mcu; + + while (!kthread_should_stop()) { + ret = i2c_smbus_read_byte_data(mcu->client, MCU_REG_CTRL); + if (ret < 0) + pr_err("MCU status reg read failed.\n"); + mcu->reg_ctrl = ret; + + + if (mcu->reg_ctrl & MCU_CTRL_BTN) { + i2c_smbus_write_byte_data(mcu->client, MCU_REG_CTRL, + mcu->reg_ctrl & ~MCU_CTRL_BTN); + + ctrl_alt_del(); + } + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + } + + return 0; +} + +static ssize_t show_status(struct device *d, + struct device_attribute *attr, char *buf) +{ + int ret; + struct mcu *mcu = glob_mcu; + + ret = i2c_smbus_read_byte_data(mcu->client, MCU_REG_CTRL); + if (ret < 0) + return -ENODEV; + mcu->reg_ctrl = ret; + + return sprintf(buf, "%02x\n", ret); +} +static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); + +static void mcu_power_off(void) +{ + struct mcu *mcu = glob_mcu; + + pr_info("Sending power-off request to the MCU...\n"); + mutex_lock(&mcu->lock); + i2c_smbus_write_byte_data(mcu->client, MCU_REG_CTRL, + mcu->reg_ctrl | MCU_CTRL_POFF); + mutex_unlock(&mcu->lock); +} + +static void mcu_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct mcu *mcu = container_of(gc, struct mcu, gc); + u8 bit = 1 << (4 + gpio); + + mutex_lock(&mcu->lock); + if (val) + mcu->reg_ctrl &= ~bit; + else + mcu->reg_ctrl |= bit; + + i2c_smbus_write_byte_data(mcu->client, MCU_REG_CTRL, mcu->reg_ctrl); + mutex_unlock(&mcu->lock); +} + +static int mcu_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + mcu_gpio_set(gc, gpio, val); + return 0; +} + +static int mcu_gpiochip_add(struct mcu *mcu) +{ + struct device_node *np; + struct gpio_chip *gc = &mcu->gc; + + np = of_find_compatible_node(NULL, NULL, "fsl,mcu-mpc8349emitx"); + if (!np) + return -ENODEV; + + gc->owner = THIS_MODULE; + gc->label = np->full_name; + gc->can_sleep = 1; + gc->ngpio = MCU_NUM_GPIO; + gc->base = -1; + gc->set = mcu_gpio_set; + gc->direction_output = mcu_gpio_dir_out; + gc->of_node = np; + + return gpiochip_add(gc); +} + +static int mcu_gpiochip_remove(struct mcu *mcu) +{ + return gpiochip_remove(&mcu->gc); +} + +static int __devinit mcu_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mcu *mcu; + int ret; + + mcu = kzalloc(sizeof(*mcu), GFP_KERNEL); + if (!mcu) + return -ENOMEM; + + mutex_init(&mcu->lock); + mcu->client = client; + i2c_set_clientdata(client, mcu); + + ret = i2c_smbus_read_byte_data(mcu->client, MCU_REG_CTRL); + if (ret < 0) + goto err; + mcu->reg_ctrl = ret; + + ret = mcu_gpiochip_add(mcu); + if (ret) + goto err; + + /* XXX: this is potentially racy, but there is no lock for ppc_md */ + if (!ppc_md.power_off) { + glob_mcu = mcu; + ppc_md.power_off = mcu_power_off; + dev_info(&client->dev, "will provide power-off service\n"); + } + + if (device_create_file(&client->dev, &dev_attr_status)) + dev_err(&client->dev, + "couldn't create device file for status\n"); + + shutdown_thread = kthread_run(shutdown_thread_fn, NULL, + "mcu-i2c-shdn"); + + return 0; +err: + kfree(mcu); + return ret; +} + +static int __devexit mcu_remove(struct i2c_client *client) +{ + struct mcu *mcu = i2c_get_clientdata(client); + int ret; + + kthread_stop(shutdown_thread); + + device_remove_file(&client->dev, &dev_attr_status); + + if (glob_mcu == mcu) { + ppc_md.power_off = NULL; + glob_mcu = NULL; + } + + ret = mcu_gpiochip_remove(mcu); + if (ret) + return ret; + i2c_set_clientdata(client, NULL); + kfree(mcu); + return 0; +} + +static const struct i2c_device_id mcu_ids[] = { + { "mcu-mpc8349emitx", }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, mcu_ids); + +static struct of_device_id mcu_of_match_table[] __devinitdata = { + { .compatible = "fsl,mcu-mpc8349emitx", }, + { }, +}; + +static struct i2c_driver mcu_driver = { + .driver = { + .name = "mcu-mpc8349emitx", + .owner = THIS_MODULE, + .of_match_table = mcu_of_match_table, + }, + .probe = mcu_probe, + .remove = __devexit_p(mcu_remove), + .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_DESCRIPTION("Power Management and GPIO expander driver for " + "MPC8349E-mITX-compatible MCU"); +MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); +MODULE_LICENSE("GPL"); diff --git a/arch/powerpc/platforms/83xx/misc.c b/arch/powerpc/platforms/83xx/misc.c new file mode 100644 index 00000000000..125336f750c --- /dev/null +++ b/arch/powerpc/platforms/83xx/misc.c @@ -0,0 +1,144 @@ +/* + * misc setup functions for MPC83xx + * + * Maintainer: Kumar Gala <galak@kernel.crashing.org> + * + * 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/of_platform.h> +#include <linux/pci.h> + +#include <asm/io.h> +#include <asm/hw_irq.h> +#include <asm/ipic.h> +#include <asm/qe_ic.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#include "mpc83xx.h" + +static __be32 __iomem *restart_reg_base; + +static int __init mpc83xx_restart_init(void) +{ + /* map reset restart_reg_baseister space */ + restart_reg_base = ioremap(get_immrbase() + 0x900, 0xff); + + return 0; +} + +arch_initcall(mpc83xx_restart_init); + +void mpc83xx_restart(char *cmd) +{ +#define RST_OFFSET 0x00000900 +#define RST_PROT_REG 0x00000018 +#define RST_CTRL_REG 0x0000001c + + local_irq_disable(); + + if (restart_reg_base) { + /* enable software reset "RSTE" */ + out_be32(restart_reg_base + (RST_PROT_REG >> 2), 0x52535445); + + /* set software hard reset */ + out_be32(restart_reg_base + (RST_CTRL_REG >> 2), 0x2); + } else { + printk (KERN_EMERG "Error: Restart registers not mapped, spinning!\n"); + } + + for (;;) ; +} + +long __init mpc83xx_time_init(void) +{ +#define SPCR_OFFSET 0x00000110 +#define SPCR_TBEN 0x00400000 + __be32 __iomem *spcr = ioremap(get_immrbase() + SPCR_OFFSET, 4); + __be32 tmp; + + tmp = in_be32(spcr); + out_be32(spcr, tmp | SPCR_TBEN); + + iounmap(spcr); + + return 0; +} + +void __init mpc83xx_ipic_init_IRQ(void) +{ + struct device_node *np; + + /* looking for fsl,pq2pro-pic which is asl compatible with fsl,ipic */ + np = of_find_compatible_node(NULL, NULL, "fsl,ipic"); + if (!np) + np = of_find_node_by_type(NULL, "ipic"); + if (!np) + return; + + ipic_init(np, 0); + + of_node_put(np); + + /* Initialize the default interrupt mapping priorities, + * in case the boot rom changed something on us. + */ + ipic_set_default_priority(); +} + +#ifdef CONFIG_QUICC_ENGINE +void __init mpc83xx_qe_init_IRQ(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic"); + if (!np) { + np = of_find_node_by_type(NULL, "qeic"); + if (!np) + return; + } + qe_ic_init(np, 0, qe_ic_cascade_low_ipic, qe_ic_cascade_high_ipic); + of_node_put(np); +} + +void __init mpc83xx_ipic_and_qe_init_IRQ(void) +{ + mpc83xx_ipic_init_IRQ(); + mpc83xx_qe_init_IRQ(); +} +#endif /* CONFIG_QUICC_ENGINE */ + +static struct of_device_id __initdata of_bus_ids[] = { + { .type = "soc", }, + { .compatible = "soc", }, + { .compatible = "simple-bus" }, + { .compatible = "gianfar" }, + { .compatible = "gpio-leds", }, + { .type = "qe", }, + { .compatible = "fsl,qe", }, + {}, +}; + +int __init mpc83xx_declare_of_platform_devices(void) +{ + of_platform_bus_probe(NULL, of_bus_ids, NULL); + return 0; +} + +#ifdef CONFIG_PCI +void __init mpc83xx_setup_pci(void) +{ + struct device_node *np; + + for_each_compatible_node(np, "pci", "fsl,mpc8349-pci") + mpc83xx_add_bridge(np); + for_each_compatible_node(np, "pci", "fsl,mpc8314-pcie") + mpc83xx_add_bridge(np); +} +#endif diff --git a/arch/powerpc/platforms/83xx/mpc830x_rdb.c b/arch/powerpc/platforms/83xx/mpc830x_rdb.c new file mode 100644 index 00000000000..4f2d9fea77b --- /dev/null +++ b/arch/powerpc/platforms/83xx/mpc830x_rdb.c @@ -0,0 +1,64 @@ +/* + * arch/powerpc/platforms/83xx/mpc830x_rdb.c + * + * Description: MPC830x RDB board specific routines. + * This file is based on mpc831x_rdb.c + * + * Copyright (C) Freescale Semiconductor, Inc. 2009. All rights reserved. + * Copyright (C) 2010. Ilya Yanok, Emcraft Systems, yanok@emcraft.com + * + * 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 <linux/pci.h> +#include <linux/of_platform.h> +#include <asm/time.h> +#include <asm/ipic.h> +#include <asm/udbg.h> +#include <sysdev/fsl_pci.h> +#include <sysdev/fsl_soc.h> +#include "mpc83xx.h" + +/* + * Setup the architecture + */ +static void __init mpc830x_rdb_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("mpc830x_rdb_setup_arch()", 0); + + mpc83xx_setup_pci(); + mpc831x_usb_cfg(); +} + +static const char *board[] __initdata = { + "MPC8308RDB", + "fsl,mpc8308rdb", + "denx,mpc8308_p1m", + NULL +}; + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc830x_rdb_probe(void) +{ + return of_flat_dt_match(of_get_flat_dt_root(), board); +} + +machine_device_initcall(mpc830x_rdb, mpc83xx_declare_of_platform_devices); + +define_machine(mpc830x_rdb) { + .name = "MPC830x RDB", + .probe = mpc830x_rdb_probe, + .setup_arch = mpc830x_rdb_setup_arch, + .init_IRQ = mpc83xx_ipic_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/mpc831x_rdb.c b/arch/powerpc/platforms/83xx/mpc831x_rdb.c new file mode 100644 index 00000000000..fa25977c52d --- /dev/null +++ b/arch/powerpc/platforms/83xx/mpc831x_rdb.c @@ -0,0 +1,64 @@ +/* + * arch/powerpc/platforms/83xx/mpc831x_rdb.c + * + * Description: MPC831x RDB board specific routines. + * This file is based on mpc834x_sys.c + * Author: Lo Wlison <r43300@freescale.com> + * + * Copyright (C) Freescale Semiconductor, Inc. 2006. All rights reserved. + * + * 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 <linux/pci.h> +#include <linux/of_platform.h> + +#include <asm/time.h> +#include <asm/ipic.h> +#include <asm/udbg.h> +#include <sysdev/fsl_pci.h> + +#include "mpc83xx.h" + +/* + * Setup the architecture + */ +static void __init mpc831x_rdb_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("mpc831x_rdb_setup_arch()", 0); + + mpc83xx_setup_pci(); + mpc831x_usb_cfg(); +} + +static const char *board[] __initdata = { + "MPC8313ERDB", + "fsl,mpc8315erdb", + NULL +}; + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc831x_rdb_probe(void) +{ + return of_flat_dt_match(of_get_flat_dt_root(), board); +} + +machine_device_initcall(mpc831x_rdb, mpc83xx_declare_of_platform_devices); + +define_machine(mpc831x_rdb) { + .name = "MPC831x RDB", + .probe = mpc831x_rdb_probe, + .setup_arch = mpc831x_rdb_setup_arch, + .init_IRQ = mpc83xx_ipic_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c new file mode 100644 index 00000000000..e36bc611dd6 --- /dev/null +++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved. + * + * Description: + * MPC832xE MDS board specific routines. + * + * 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/reboot.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/major.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/seq_file.h> +#include <linux/root_dev.h> +#include <linux/initrd.h> +#include <linux/of_platform.h> +#include <linux/of_device.h> + +#include <asm/system.h> +#include <linux/atomic.h> +#include <asm/time.h> +#include <asm/io.h> +#include <asm/machdep.h> +#include <asm/ipic.h> +#include <asm/irq.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> +#include <asm/qe.h> +#include <asm/qe_ic.h> + +#include "mpc83xx.h" + +#undef DEBUG +#ifdef DEBUG +#define DBG(fmt...) udbg_printf(fmt) +#else +#define DBG(fmt...) +#endif + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init mpc832x_sys_setup_arch(void) +{ + struct device_node *np; + u8 __iomem *bcsr_regs = NULL; + + if (ppc_md.progress) + ppc_md.progress("mpc832x_sys_setup_arch()", 0); + + /* Map BCSR area */ + np = of_find_node_by_name(NULL, "bcsr"); + if (np) { + struct resource res; + + of_address_to_resource(np, 0, &res); + bcsr_regs = ioremap(res.start, resource_size(&res)); + of_node_put(np); + } + + mpc83xx_setup_pci(); + +#ifdef CONFIG_QUICC_ENGINE + qe_reset(); + + if ((np = of_find_node_by_name(NULL, "par_io")) != NULL) { + par_io_init(np); + of_node_put(np); + + for (np = NULL; (np = of_find_node_by_name(np, "ucc")) != NULL;) + par_io_of_config(np); + } + + if ((np = of_find_compatible_node(NULL, "network", "ucc_geth")) + != NULL){ + /* Reset the Ethernet PHYs */ +#define BCSR8_FETH_RST 0x50 + clrbits8(&bcsr_regs[8], BCSR8_FETH_RST); + udelay(1000); + setbits8(&bcsr_regs[8], BCSR8_FETH_RST); + iounmap(bcsr_regs); + of_node_put(np); + } +#endif /* CONFIG_QUICC_ENGINE */ +} + +machine_device_initcall(mpc832x_mds, mpc83xx_declare_of_platform_devices); + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc832x_sys_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "MPC832xMDS"); +} + +define_machine(mpc832x_mds) { + .name = "MPC832x MDS", + .probe = mpc832x_sys_probe, + .setup_arch = mpc832x_sys_setup_arch, + .init_IRQ = mpc83xx_ipic_and_qe_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c new file mode 100644 index 00000000000..eff5baabc3f --- /dev/null +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -0,0 +1,240 @@ +/* + * arch/powerpc/platforms/83xx/mpc832x_rdb.c + * + * Copyright (C) Freescale Semiconductor, Inc. 2007. All rights reserved. + * + * Description: + * MPC832x RDB board specific routines. + * This file is based on mpc832x_mds.c and mpc8313_rdb.c + * Author: Michael Barkowski <michael.barkowski@freescale.com> + * + * 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 <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/spi/spi.h> +#include <linux/spi/mmc_spi.h> +#include <linux/mmc/host.h> +#include <linux/of_platform.h> +#include <linux/fsl_devices.h> + +#include <asm/time.h> +#include <asm/ipic.h> +#include <asm/udbg.h> +#include <asm/qe.h> +#include <asm/qe_ic.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#include "mpc83xx.h" + +#undef DEBUG +#ifdef DEBUG +#define DBG(fmt...) udbg_printf(fmt) +#else +#define DBG(fmt...) +#endif + +#ifdef CONFIG_QUICC_ENGINE +static int __init of_fsl_spi_probe(char *type, char *compatible, u32 sysclk, + struct spi_board_info *board_infos, + unsigned int num_board_infos, + void (*cs_control)(struct spi_device *dev, + bool on)) +{ + struct device_node *np; + unsigned int i = 0; + + for_each_compatible_node(np, type, compatible) { + int ret; + unsigned int j; + const void *prop; + struct resource res[2]; + struct platform_device *pdev; + struct fsl_spi_platform_data pdata = { + .cs_control = cs_control, + }; + + memset(res, 0, sizeof(res)); + + pdata.sysclk = sysclk; + + prop = of_get_property(np, "reg", NULL); + if (!prop) + goto err; + pdata.bus_num = *(u32 *)prop; + + prop = of_get_property(np, "cell-index", NULL); + if (prop) + i = *(u32 *)prop; + + prop = of_get_property(np, "mode", NULL); + if (prop && !strcmp(prop, "cpu-qe")) + pdata.flags = SPI_QE_CPU_MODE; + + for (j = 0; j < num_board_infos; j++) { + if (board_infos[j].bus_num == pdata.bus_num) + pdata.max_chipselect++; + } + + if (!pdata.max_chipselect) + continue; + + ret = of_address_to_resource(np, 0, &res[0]); + if (ret) + goto err; + + ret = of_irq_to_resource(np, 0, &res[1]); + if (ret == NO_IRQ) + goto err; + + pdev = platform_device_alloc("mpc83xx_spi", i); + if (!pdev) + goto err; + + ret = platform_device_add_data(pdev, &pdata, sizeof(pdata)); + if (ret) + goto unreg; + + ret = platform_device_add_resources(pdev, res, + ARRAY_SIZE(res)); + if (ret) + goto unreg; + + ret = platform_device_add(pdev); + if (ret) + goto unreg; + + goto next; +unreg: + platform_device_del(pdev); +err: + pr_err("%s: registration failed\n", np->full_name); +next: + i++; + } + + return i; +} + +static int __init fsl_spi_init(struct spi_board_info *board_infos, + unsigned int num_board_infos, + void (*cs_control)(struct spi_device *spi, + bool on)) +{ + u32 sysclk = -1; + int ret; + + /* SPI controller is either clocked from QE or SoC clock */ + sysclk = get_brgfreq(); + if (sysclk == -1) { + sysclk = fsl_get_sys_freq(); + if (sysclk == -1) + return -ENODEV; + } + + ret = of_fsl_spi_probe(NULL, "fsl,spi", sysclk, board_infos, + num_board_infos, cs_control); + if (!ret) + of_fsl_spi_probe("spi", "fsl_spi", sysclk, board_infos, + num_board_infos, cs_control); + + return spi_register_board_info(board_infos, num_board_infos); +} + +static void mpc83xx_spi_cs_control(struct spi_device *spi, bool on) +{ + pr_debug("%s %d %d\n", __func__, spi->chip_select, on); + par_io_data_set(3, 13, on); +} + +static struct mmc_spi_platform_data mpc832x_mmc_pdata = { + .ocr_mask = MMC_VDD_33_34, +}; + +static struct spi_board_info mpc832x_spi_boardinfo = { + .bus_num = 0x4c0, + .chip_select = 0, + .max_speed_hz = 50000000, + .modalias = "mmc_spi", + .platform_data = &mpc832x_mmc_pdata, +}; + +static int __init mpc832x_spi_init(void) +{ + par_io_config_pin(3, 0, 3, 0, 1, 0); /* SPI1 MOSI, I/O */ + par_io_config_pin(3, 1, 3, 0, 1, 0); /* SPI1 MISO, I/O */ + par_io_config_pin(3, 2, 3, 0, 1, 0); /* SPI1 CLK, I/O */ + par_io_config_pin(3, 3, 2, 0, 1, 0); /* SPI1 SEL, I */ + + par_io_config_pin(3, 13, 1, 0, 0, 0); /* !SD_CS, O */ + par_io_config_pin(3, 14, 2, 0, 0, 0); /* SD_INSERT, I */ + par_io_config_pin(3, 15, 2, 0, 0, 0); /* SD_PROTECT,I */ + + /* + * Don't bother with legacy stuff when device tree contains + * mmc-spi-slot node. + */ + if (of_find_compatible_node(NULL, NULL, "mmc-spi-slot")) + return 0; + return fsl_spi_init(&mpc832x_spi_boardinfo, 1, mpc83xx_spi_cs_control); +} +machine_device_initcall(mpc832x_rdb, mpc832x_spi_init); +#endif /* CONFIG_QUICC_ENGINE */ + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init mpc832x_rdb_setup_arch(void) +{ +#if defined(CONFIG_QUICC_ENGINE) + struct device_node *np; +#endif + + if (ppc_md.progress) + ppc_md.progress("mpc832x_rdb_setup_arch()", 0); + + mpc83xx_setup_pci(); + +#ifdef CONFIG_QUICC_ENGINE + qe_reset(); + + if ((np = of_find_node_by_name(NULL, "par_io")) != NULL) { + par_io_init(np); + of_node_put(np); + + for (np = NULL; (np = of_find_node_by_name(np, "ucc")) != NULL;) + par_io_of_config(np); + } +#endif /* CONFIG_QUICC_ENGINE */ +} + +machine_device_initcall(mpc832x_rdb, mpc83xx_declare_of_platform_devices); + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc832x_rdb_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "MPC832xRDB"); +} + +define_machine(mpc832x_rdb) { + .name = "MPC832x RDB", + .probe = mpc832x_rdb_probe, + .setup_arch = mpc832x_rdb_setup_arch, + .init_IRQ = mpc83xx_ipic_and_qe_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/mpc834x_itx.c b/arch/powerpc/platforms/83xx/mpc834x_itx.c new file mode 100644 index 00000000000..39849dd1b5b --- /dev/null +++ b/arch/powerpc/platforms/83xx/mpc834x_itx.c @@ -0,0 +1,89 @@ +/* + * arch/powerpc/platforms/83xx/mpc834x_itx.c + * + * MPC834x ITX board specific routines + * + * Maintainer: Kumar Gala <galak@kernel.crashing.org> + * + * 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/reboot.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/major.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/seq_file.h> +#include <linux/root_dev.h> +#include <linux/of_platform.h> + +#include <asm/system.h> +#include <linux/atomic.h> +#include <asm/time.h> +#include <asm/io.h> +#include <asm/machdep.h> +#include <asm/ipic.h> +#include <asm/irq.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#include "mpc83xx.h" + +static struct of_device_id __initdata mpc834x_itx_ids[] = { + { .compatible = "fsl,pq2pro-localbus", }, + {}, +}; + +static int __init mpc834x_itx_declare_of_platform_devices(void) +{ + mpc83xx_declare_of_platform_devices(); + return of_platform_bus_probe(NULL, mpc834x_itx_ids, NULL); +} +machine_device_initcall(mpc834x_itx, mpc834x_itx_declare_of_platform_devices); + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init mpc834x_itx_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("mpc834x_itx_setup_arch()", 0); + + mpc83xx_setup_pci(); + + mpc834x_usb_cfg(); +} + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc834x_itx_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "MPC834xMITX"); +} + +define_machine(mpc834x_itx) { + .name = "MPC834x ITX", + .probe = mpc834x_itx_probe, + .setup_arch = mpc834x_itx_setup_arch, + .init_IRQ = mpc83xx_ipic_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/mpc834x_mds.c b/arch/powerpc/platforms/83xx/mpc834x_mds.c new file mode 100644 index 00000000000..5828d8e97c3 --- /dev/null +++ b/arch/powerpc/platforms/83xx/mpc834x_mds.c @@ -0,0 +1,110 @@ +/* + * arch/powerpc/platforms/83xx/mpc834x_mds.c + * + * MPC834x MDS board specific routines + * + * Maintainer: Kumar Gala <galak@kernel.crashing.org> + * + * 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/reboot.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/major.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/seq_file.h> +#include <linux/root_dev.h> +#include <linux/of_platform.h> + +#include <asm/system.h> +#include <linux/atomic.h> +#include <asm/time.h> +#include <asm/io.h> +#include <asm/machdep.h> +#include <asm/ipic.h> +#include <asm/irq.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#include "mpc83xx.h" + +#define BCSR5_INT_USB 0x02 +static int mpc834xemds_usb_cfg(void) +{ + struct device_node *np; + void __iomem *bcsr_regs = NULL; + u8 bcsr5; + + mpc834x_usb_cfg(); + /* Map BCSR area */ + np = of_find_node_by_name(NULL, "bcsr"); + if (np) { + struct resource res; + + of_address_to_resource(np, 0, &res); + bcsr_regs = ioremap(res.start, resource_size(&res)); + of_node_put(np); + } + if (!bcsr_regs) + return -1; + + /* + * if Processor Board is plugged into PIB board, + * force to use the PHY on Processor Board + */ + bcsr5 = in_8(bcsr_regs + 5); + if (!(bcsr5 & BCSR5_INT_USB)) + out_8(bcsr_regs + 5, (bcsr5 | BCSR5_INT_USB)); + iounmap(bcsr_regs); + return 0; +} + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init mpc834x_mds_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("mpc834x_mds_setup_arch()", 0); + + mpc83xx_setup_pci(); + + mpc834xemds_usb_cfg(); +} + +machine_device_initcall(mpc834x_mds, mpc83xx_declare_of_platform_devices); + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc834x_mds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "MPC834xMDS"); +} + +define_machine(mpc834x_mds) { + .name = "MPC834x MDS", + .probe = mpc834x_mds_probe, + .setup_arch = mpc834x_mds_setup_arch, + .init_IRQ = mpc83xx_ipic_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c new file mode 100644 index 00000000000..ad8e4bcd7d5 --- /dev/null +++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved. + * + * Author: Li Yang <LeoLi@freescale.com> + * Yin Olivia <Hong-hua.Yin@freescale.com> + * + * Description: + * MPC8360E MDS board specific routines. + * + * Changelog: + * Jun 21, 2006 Initial version + * + * 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/compiler.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/reboot.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/major.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/seq_file.h> +#include <linux/root_dev.h> +#include <linux/initrd.h> +#include <linux/of_platform.h> +#include <linux/of_device.h> + +#include <asm/system.h> +#include <linux/atomic.h> +#include <asm/time.h> +#include <asm/io.h> +#include <asm/machdep.h> +#include <asm/ipic.h> +#include <asm/irq.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> +#include <sysdev/simple_gpio.h> +#include <asm/qe.h> +#include <asm/qe_ic.h> + +#include "mpc83xx.h" + +#undef DEBUG +#ifdef DEBUG +#define DBG(fmt...) udbg_printf(fmt) +#else +#define DBG(fmt...) +#endif + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init mpc836x_mds_setup_arch(void) +{ + struct device_node *np; + u8 __iomem *bcsr_regs = NULL; + + if (ppc_md.progress) + ppc_md.progress("mpc836x_mds_setup_arch()", 0); + + /* Map BCSR area */ + np = of_find_node_by_name(NULL, "bcsr"); + if (np) { + struct resource res; + + of_address_to_resource(np, 0, &res); + bcsr_regs = ioremap(res.start, resource_size(&res)); + of_node_put(np); + } + + mpc83xx_setup_pci(); + +#ifdef CONFIG_QUICC_ENGINE + qe_reset(); + + if ((np = of_find_node_by_name(NULL, "par_io")) != NULL) { + par_io_init(np); + of_node_put(np); + + for (np = NULL; (np = of_find_node_by_name(np, "ucc")) != NULL;) + par_io_of_config(np); +#ifdef CONFIG_QE_USB + /* Must fixup Par IO before QE GPIO chips are registered. */ + par_io_config_pin(1, 2, 1, 0, 3, 0); /* USBOE */ + par_io_config_pin(1, 3, 1, 0, 3, 0); /* USBTP */ + par_io_config_pin(1, 8, 1, 0, 1, 0); /* USBTN */ + par_io_config_pin(1, 10, 2, 0, 3, 0); /* USBRXD */ + par_io_config_pin(1, 9, 2, 1, 3, 0); /* USBRP */ + par_io_config_pin(1, 11, 2, 1, 3, 0); /* USBRN */ + par_io_config_pin(2, 20, 2, 0, 1, 0); /* CLK21 */ +#endif /* CONFIG_QE_USB */ + } + + if ((np = of_find_compatible_node(NULL, "network", "ucc_geth")) + != NULL){ + uint svid; + + /* Reset the Ethernet PHY */ +#define BCSR9_GETHRST 0x20 + clrbits8(&bcsr_regs[9], BCSR9_GETHRST); + udelay(1000); + setbits8(&bcsr_regs[9], BCSR9_GETHRST); + + /* handle mpc8360ea rev.2.1 erratum 2: RGMII Timing */ + svid = mfspr(SPRN_SVR); + if (svid == 0x80480021) { + void __iomem *immap; + + immap = ioremap(get_immrbase() + 0x14a8, 8); + + /* + * IMMR + 0x14A8[4:5] = 11 (clk delay for UCC 2) + * IMMR + 0x14A8[18:19] = 11 (clk delay for UCC 1) + */ + setbits32(immap, 0x0c003000); + + /* + * IMMR + 0x14AC[20:27] = 10101010 + * (data delay for both UCC's) + */ + clrsetbits_be32(immap + 4, 0xff0, 0xaa0); + + iounmap(immap); + } + + iounmap(bcsr_regs); + of_node_put(np); + } +#endif /* CONFIG_QUICC_ENGINE */ +} + +machine_device_initcall(mpc836x_mds, mpc83xx_declare_of_platform_devices); + +#ifdef CONFIG_QE_USB +static int __init mpc836x_usb_cfg(void) +{ + u8 __iomem *bcsr; + struct device_node *np; + const char *mode; + int ret = 0; + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc8360mds-bcsr"); + if (!np) + return -ENODEV; + + bcsr = of_iomap(np, 0); + of_node_put(np); + if (!bcsr) + return -ENOMEM; + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc8323-qe-usb"); + if (!np) { + ret = -ENODEV; + goto err; + } + +#define BCSR8_TSEC1M_MASK (0x3 << 6) +#define BCSR8_TSEC1M_RGMII (0x0 << 6) +#define BCSR8_TSEC2M_MASK (0x3 << 4) +#define BCSR8_TSEC2M_RGMII (0x0 << 4) + /* + * Default is GMII (2), but we should set it to RGMII (0) if we use + * USB (Eth PHY is in RGMII mode anyway). + */ + clrsetbits_8(&bcsr[8], BCSR8_TSEC1M_MASK | BCSR8_TSEC2M_MASK, + BCSR8_TSEC1M_RGMII | BCSR8_TSEC2M_RGMII); + +#define BCSR13_USBMASK 0x0f +#define BCSR13_nUSBEN 0x08 /* 1 - Disable, 0 - Enable */ +#define BCSR13_USBSPEED 0x04 /* 1 - Full, 0 - Low */ +#define BCSR13_USBMODE 0x02 /* 1 - Host, 0 - Function */ +#define BCSR13_nUSBVCC 0x01 /* 1 - gets VBUS, 0 - supplies VBUS */ + + clrsetbits_8(&bcsr[13], BCSR13_USBMASK, BCSR13_USBSPEED); + + mode = of_get_property(np, "mode", NULL); + if (mode && !strcmp(mode, "peripheral")) { + setbits8(&bcsr[13], BCSR13_nUSBVCC); + qe_usb_clock_set(QE_CLK21, 48000000); + } else { + setbits8(&bcsr[13], BCSR13_USBMODE); + /* + * The BCSR GPIOs are used to control power and + * speed of the USB transceiver. This is needed for + * the USB Host only. + */ + simple_gpiochip_init("fsl,mpc8360mds-bcsr-gpio"); + } + + of_node_put(np); +err: + iounmap(bcsr); + return ret; +} +machine_arch_initcall(mpc836x_mds, mpc836x_usb_cfg); +#endif /* CONFIG_QE_USB */ + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc836x_mds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "MPC836xMDS"); +} + +define_machine(mpc836x_mds) { + .name = "MPC836x MDS", + .probe = mpc836x_mds_probe, + .setup_arch = mpc836x_mds_setup_arch, + .init_IRQ = mpc83xx_ipic_and_qe_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/mpc836x_rdk.c b/arch/powerpc/platforms/83xx/mpc836x_rdk.c new file mode 100644 index 00000000000..f8769d713d6 --- /dev/null +++ b/arch/powerpc/platforms/83xx/mpc836x_rdk.c @@ -0,0 +1,63 @@ +/* + * MPC8360E-RDK board file. + * + * Copyright (c) 2006 Freescale Semicondutor, Inc. + * Copyright (c) 2007-2008 MontaVista Software, Inc. + * + * Author: Anton Vorontsov <avorontsov@ru.mvista.com> + * + * 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 <linux/kernel.h> +#include <linux/pci.h> +#include <linux/of_platform.h> +#include <linux/io.h> +#include <asm/prom.h> +#include <asm/time.h> +#include <asm/ipic.h> +#include <asm/udbg.h> +#include <asm/qe.h> +#include <asm/qe_ic.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#include "mpc83xx.h" + +machine_device_initcall(mpc836x_rdk, mpc83xx_declare_of_platform_devices); + +static void __init mpc836x_rdk_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("mpc836x_rdk_setup_arch()", 0); + + mpc83xx_setup_pci(); +#ifdef CONFIG_QUICC_ENGINE + qe_reset(); +#endif +} + +/* + * Called very early, MMU is off, device-tree isn't unflattened. + */ +static int __init mpc836x_rdk_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,mpc8360rdk"); +} + +define_machine(mpc836x_rdk) { + .name = "MPC836x RDK", + .probe = mpc836x_rdk_probe, + .setup_arch = mpc836x_rdk_setup_arch, + .init_IRQ = mpc83xx_ipic_and_qe_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/mpc837x_mds.c b/arch/powerpc/platforms/83xx/mpc837x_mds.c new file mode 100644 index 00000000000..e53a60b6c86 --- /dev/null +++ b/arch/powerpc/platforms/83xx/mpc837x_mds.c @@ -0,0 +1,111 @@ +/* + * arch/powerpc/platforms/83xx/mpc837x_mds.c + * + * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. + * + * MPC837x MDS board specific routines + * + * 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 <linux/pci.h> +#include <linux/of.h> +#include <linux/of_platform.h> + +#include <asm/time.h> +#include <asm/ipic.h> +#include <asm/udbg.h> +#include <asm/prom.h> +#include <sysdev/fsl_pci.h> + +#include "mpc83xx.h" + +#define BCSR12_USB_SER_MASK 0x8a +#define BCSR12_USB_SER_PIN 0x80 +#define BCSR12_USB_SER_DEVICE 0x02 + +static int mpc837xmds_usb_cfg(void) +{ + struct device_node *np; + const void *phy_type, *mode; + void __iomem *bcsr_regs = NULL; + u8 bcsr12; + int ret; + + ret = mpc837x_usb_cfg(); + if (ret) + return ret; + /* Map BCSR area */ + np = of_find_compatible_node(NULL, NULL, "fsl,mpc837xmds-bcsr"); + if (np) { + bcsr_regs = of_iomap(np, 0); + of_node_put(np); + } + if (!bcsr_regs) + return -1; + + np = of_find_node_by_name(NULL, "usb"); + if (!np) { + ret = -ENODEV; + goto out; + } + phy_type = of_get_property(np, "phy_type", NULL); + if (phy_type && !strcmp(phy_type, "ulpi")) { + clrbits8(bcsr_regs + 12, BCSR12_USB_SER_PIN); + } else if (phy_type && !strcmp(phy_type, "serial")) { + mode = of_get_property(np, "dr_mode", NULL); + bcsr12 = in_8(bcsr_regs + 12) & ~BCSR12_USB_SER_MASK; + bcsr12 |= BCSR12_USB_SER_PIN; + if (mode && !strcmp(mode, "peripheral")) + bcsr12 |= BCSR12_USB_SER_DEVICE; + out_8(bcsr_regs + 12, bcsr12); + } else { + printk(KERN_ERR "USB DR: unsupported PHY\n"); + } + + of_node_put(np); +out: + iounmap(bcsr_regs); + return ret; +} + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init mpc837x_mds_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("mpc837x_mds_setup_arch()", 0); + + mpc83xx_setup_pci(); + mpc837xmds_usb_cfg(); +} + +machine_device_initcall(mpc837x_mds, mpc83xx_declare_of_platform_devices); + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc837x_mds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,mpc837xmds"); +} + +define_machine(mpc837x_mds) { + .name = "MPC837x MDS", + .probe = mpc837x_mds_probe, + .setup_arch = mpc837x_mds_setup_arch, + .init_IRQ = mpc83xx_ipic_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/mpc837x_rdb.c b/arch/powerpc/platforms/83xx/mpc837x_rdb.c new file mode 100644 index 00000000000..16c9c9cbbb7 --- /dev/null +++ b/arch/powerpc/platforms/83xx/mpc837x_rdb.c @@ -0,0 +1,89 @@ +/* + * arch/powerpc/platforms/83xx/mpc837x_rdb.c + * + * Copyright (C) 2007 Freescale Semicondutor, Inc. All rights reserved. + * + * MPC837x RDB board specific routines + * + * 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 <linux/pci.h> +#include <linux/of_platform.h> + +#include <asm/time.h> +#include <asm/ipic.h> +#include <asm/udbg.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#include "mpc83xx.h" + +static void mpc837x_rdb_sd_cfg(void) +{ + void __iomem *im; + + im = ioremap(get_immrbase(), 0x1000); + if (!im) { + WARN_ON(1); + return; + } + + /* + * On RDB boards (in contrast to MDS) USBB pins are used for SD only, + * so we can safely mux them away from the USB block. + */ + clrsetbits_be32(im + MPC83XX_SICRL_OFFS, MPC837X_SICRL_USBB_MASK, + MPC837X_SICRL_SD); + clrsetbits_be32(im + MPC83XX_SICRH_OFFS, MPC837X_SICRH_SPI_MASK, + MPC837X_SICRH_SD); + iounmap(im); +} + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init mpc837x_rdb_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("mpc837x_rdb_setup_arch()", 0); + + mpc83xx_setup_pci(); + mpc837x_usb_cfg(); + mpc837x_rdb_sd_cfg(); +} + +machine_device_initcall(mpc837x_rdb, mpc83xx_declare_of_platform_devices); + +static const char *board[] __initdata = { + "fsl,mpc8377rdb", + "fsl,mpc8378rdb", + "fsl,mpc8379rdb", + "fsl,mpc8377wlan", + NULL +}; + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc837x_rdb_probe(void) +{ + return of_flat_dt_match(of_get_flat_dt_root(), board); +} + +define_machine(mpc837x_rdb) { + .name = "MPC837x RDB/WLAN", + .probe = mpc837x_rdb_probe, + .setup_arch = mpc837x_rdb_setup_arch, + .init_IRQ = mpc83xx_ipic_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/mpc83xx.h b/arch/powerpc/platforms/83xx/mpc83xx.h new file mode 100644 index 00000000000..0cf74d7ea1c --- /dev/null +++ b/arch/powerpc/platforms/83xx/mpc83xx.h @@ -0,0 +1,90 @@ +#ifndef __MPC83XX_H__ +#define __MPC83XX_H__ + +#include <linux/init.h> +#include <linux/device.h> +#include <asm/pci-bridge.h> + +/* System Clock Control Register */ +#define MPC83XX_SCCR_OFFS 0xA08 +#define MPC83XX_SCCR_USB_MASK 0x00f00000 +#define MPC83XX_SCCR_USB_MPHCM_11 0x00c00000 +#define MPC83XX_SCCR_USB_MPHCM_01 0x00400000 +#define MPC83XX_SCCR_USB_MPHCM_10 0x00800000 +#define MPC83XX_SCCR_USB_DRCM_11 0x00300000 +#define MPC83XX_SCCR_USB_DRCM_01 0x00100000 +#define MPC83XX_SCCR_USB_DRCM_10 0x00200000 +#define MPC8315_SCCR_USB_MASK 0x00c00000 +#define MPC8315_SCCR_USB_DRCM_11 0x00c00000 +#define MPC8315_SCCR_USB_DRCM_01 0x00400000 +#define MPC837X_SCCR_USB_DRCM_11 0x00c00000 + +/* system i/o configuration register low */ +#define MPC83XX_SICRL_OFFS 0x114 +#define MPC834X_SICRL_USB_MASK 0x60000000 +#define MPC834X_SICRL_USB0 0x20000000 +#define MPC834X_SICRL_USB1 0x40000000 +#define MPC831X_SICRL_USB_MASK 0x00000c00 +#define MPC831X_SICRL_USB_ULPI 0x00000800 +#define MPC8315_SICRL_USB_MASK 0x000000fc +#define MPC8315_SICRL_USB_ULPI 0x00000054 +#define MPC837X_SICRL_USB_MASK 0xf0000000 +#define MPC837X_SICRL_USB_ULPI 0x50000000 +#define MPC837X_SICRL_USBB_MASK 0x30000000 +#define MPC837X_SICRL_SD 0x20000000 + +/* system i/o configuration register high */ +#define MPC83XX_SICRH_OFFS 0x118 +#define MPC8308_SICRH_USB_MASK 0x000c0000 +#define MPC8308_SICRH_USB_ULPI 0x00040000 +#define MPC834X_SICRH_USB_UTMI 0x00020000 +#define MPC831X_SICRH_USB_MASK 0x000000e0 +#define MPC831X_SICRH_USB_ULPI 0x000000a0 +#define MPC8315_SICRH_USB_MASK 0x0000ff00 +#define MPC8315_SICRH_USB_ULPI 0x00000000 +#define MPC837X_SICRH_SPI_MASK 0x00000003 +#define MPC837X_SICRH_SD 0x00000001 + +/* USB Control Register */ +#define FSL_USB2_CONTROL_OFFS 0x500 +#define CONTROL_UTMI_PHY_EN 0x00000200 +#define CONTROL_REFSEL_24MHZ 0x00000040 +#define CONTROL_REFSEL_48MHZ 0x00000080 +#define CONTROL_PHY_CLK_SEL_ULPI 0x00000400 +#define CONTROL_OTG_PORT 0x00000020 + +/* USB PORTSC Registers */ +#define FSL_USB2_PORTSC1_OFFS 0x184 +#define FSL_USB2_PORTSC2_OFFS 0x188 +#define PORTSCX_PTW_16BIT 0x10000000 +#define PORTSCX_PTS_UTMI 0x00000000 +#define PORTSCX_PTS_ULPI 0x80000000 + +/* + * Declaration for the various functions exported by the + * mpc83xx_* files. Mostly for use by mpc83xx_setup + */ + +extern void mpc83xx_restart(char *cmd); +extern long mpc83xx_time_init(void); +extern int mpc837x_usb_cfg(void); +extern int mpc834x_usb_cfg(void); +extern int mpc831x_usb_cfg(void); +extern void mpc83xx_ipic_init_IRQ(void); +#ifdef CONFIG_QUICC_ENGINE +extern void mpc83xx_qe_init_IRQ(void); +extern void mpc83xx_ipic_and_qe_init_IRQ(void); +#else +static inline void __init mpc83xx_qe_init_IRQ(void) {} +#define mpc83xx_ipic_and_qe_init_IRQ mpc83xx_ipic_init_IRQ +#endif /* CONFIG_QUICC_ENGINE */ + +#ifdef CONFIG_PCI +extern void mpc83xx_setup_pci(void); +#else +#define mpc83xx_setup_pci() do {} while (0) +#endif + +extern int mpc83xx_declare_of_platform_devices(void); + +#endif /* __MPC83XX_H__ */ diff --git a/arch/powerpc/platforms/83xx/sbc834x.c b/arch/powerpc/platforms/83xx/sbc834x.c new file mode 100644 index 00000000000..8a81d7640b1 --- /dev/null +++ b/arch/powerpc/platforms/83xx/sbc834x.c @@ -0,0 +1,79 @@ +/* + * arch/powerpc/platforms/83xx/sbc834x.c + * + * Wind River SBC834x board specific routines + * + * By Paul Gortmaker (see MAINTAINERS for contact information) + * + * Based largely on the mpc834x_mds.c support by Kumar Gala. + * + * 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/reboot.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/major.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/seq_file.h> +#include <linux/root_dev.h> +#include <linux/of_platform.h> + +#include <asm/system.h> +#include <linux/atomic.h> +#include <asm/time.h> +#include <asm/io.h> +#include <asm/machdep.h> +#include <asm/ipic.h> +#include <asm/irq.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#include "mpc83xx.h" + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init sbc834x_setup_arch(void) +{ + if (ppc_md.progress) + ppc_md.progress("sbc834x_setup_arch()", 0); + + mpc83xx_setup_pci(); +} + +machine_device_initcall(sbc834x, mpc83xx_declare_of_platform_devices); + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init sbc834x_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "SBC834xE"); +} + +define_machine(sbc834x) { + .name = "SBC834xE", + .probe = sbc834x_probe, + .setup_arch = sbc834x_setup_arch, + .init_IRQ = mpc83xx_ipic_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/83xx/suspend-asm.S b/arch/powerpc/platforms/83xx/suspend-asm.S new file mode 100644 index 00000000000..3d1ecd21177 --- /dev/null +++ b/arch/powerpc/platforms/83xx/suspend-asm.S @@ -0,0 +1,533 @@ +/* + * Enter and leave deep sleep state on MPC83xx + * + * Copyright (c) 2006-2008 Freescale Semiconductor, Inc. + * Author: Scott Wood <scottwood@freescale.com> + * + * 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 published + * by the Free Software Foundation. + */ + +#include <asm/page.h> +#include <asm/ppc_asm.h> +#include <asm/reg.h> +#include <asm/asm-offsets.h> + +#define SS_MEMSAVE 0x00 /* First 8 bytes of RAM */ +#define SS_HID 0x08 /* 3 HIDs */ +#define SS_IABR 0x14 /* 2 IABRs */ +#define SS_IBCR 0x1c +#define SS_DABR 0x20 /* 2 DABRs */ +#define SS_DBCR 0x28 +#define SS_SP 0x2c +#define SS_SR 0x30 /* 16 segment registers */ +#define SS_R2 0x70 +#define SS_MSR 0x74 +#define SS_SDR1 0x78 +#define SS_LR 0x7c +#define SS_SPRG 0x80 /* 4 SPRGs */ +#define SS_DBAT 0x90 /* 8 DBATs */ +#define SS_IBAT 0xd0 /* 8 IBATs */ +#define SS_TB 0x110 +#define SS_CR 0x118 +#define SS_GPREG 0x11c /* r12-r31 */ +#define STATE_SAVE_SIZE 0x16c + + .section .data + .align 5 + +mpc83xx_sleep_save_area: + .space STATE_SAVE_SIZE +immrbase: + .long 0 + + .section .text + .align 5 + + /* r3 = physical address of IMMR */ +_GLOBAL(mpc83xx_enter_deep_sleep) + lis r4, immrbase@ha + stw r3, immrbase@l(r4) + + /* The first 2 words of memory are used to communicate with the + * bootloader, to tell it how to resume. + * + * The first word is the magic number 0xf5153ae5, and the second + * is the pointer to mpc83xx_deep_resume. + * + * The original content of these two words is saved in SS_MEMSAVE. + */ + + lis r3, mpc83xx_sleep_save_area@h + ori r3, r3, mpc83xx_sleep_save_area@l + + lis r4, KERNELBASE@h + lwz r5, 0(r4) + lwz r6, 4(r4) + + stw r5, SS_MEMSAVE+0(r3) + stw r6, SS_MEMSAVE+4(r3) + + mfspr r5, SPRN_HID0 + mfspr r6, SPRN_HID1 + mfspr r7, SPRN_HID2 + + stw r5, SS_HID+0(r3) + stw r6, SS_HID+4(r3) + stw r7, SS_HID+8(r3) + + mfspr r4, SPRN_IABR + mfspr r5, SPRN_IABR2 + mfspr r6, SPRN_IBCR + mfspr r7, SPRN_DABR + mfspr r8, SPRN_DABR2 + mfspr r9, SPRN_DBCR + + stw r4, SS_IABR+0(r3) + stw r5, SS_IABR+4(r3) + stw r6, SS_IBCR(r3) + stw r7, SS_DABR+0(r3) + stw r8, SS_DABR+4(r3) + stw r9, SS_DBCR(r3) + + mfspr r4, SPRN_SPRG0 + mfspr r5, SPRN_SPRG1 + mfspr r6, SPRN_SPRG2 + mfspr r7, SPRN_SPRG3 + mfsdr1 r8 + + stw r4, SS_SPRG+0(r3) + stw r5, SS_SPRG+4(r3) + stw r6, SS_SPRG+8(r3) + stw r7, SS_SPRG+12(r3) + stw r8, SS_SDR1(r3) + + mfspr r4, SPRN_DBAT0U + mfspr r5, SPRN_DBAT0L + mfspr r6, SPRN_DBAT1U + mfspr r7, SPRN_DBAT1L + + stw r4, SS_DBAT+0x00(r3) + stw r5, SS_DBAT+0x04(r3) + stw r6, SS_DBAT+0x08(r3) + stw r7, SS_DBAT+0x0c(r3) + + mfspr r4, SPRN_DBAT2U + mfspr r5, SPRN_DBAT2L + mfspr r6, SPRN_DBAT3U + mfspr r7, SPRN_DBAT3L + + stw r4, SS_DBAT+0x10(r3) + stw r5, SS_DBAT+0x14(r3) + stw r6, SS_DBAT+0x18(r3) + stw r7, SS_DBAT+0x1c(r3) + + mfspr r4, SPRN_DBAT4U + mfspr r5, SPRN_DBAT4L + mfspr r6, SPRN_DBAT5U + mfspr r7, SPRN_DBAT5L + + stw r4, SS_DBAT+0x20(r3) + stw r5, SS_DBAT+0x24(r3) + stw r6, SS_DBAT+0x28(r3) + stw r7, SS_DBAT+0x2c(r3) + + mfspr r4, SPRN_DBAT6U + mfspr r5, SPRN_DBAT6L + mfspr r6, SPRN_DBAT7U + mfspr r7, SPRN_DBAT7L + + stw r4, SS_DBAT+0x30(r3) + stw r5, SS_DBAT+0x34(r3) + stw r6, SS_DBAT+0x38(r3) + stw r7, SS_DBAT+0x3c(r3) + + mfspr r4, SPRN_IBAT0U + mfspr r5, SPRN_IBAT0L + mfspr r6, SPRN_IBAT1U + mfspr r7, SPRN_IBAT1L + + stw r4, SS_IBAT+0x00(r3) + stw r5, SS_IBAT+0x04(r3) + stw r6, SS_IBAT+0x08(r3) + stw r7, SS_IBAT+0x0c(r3) + + mfspr r4, SPRN_IBAT2U + mfspr r5, SPRN_IBAT2L + mfspr r6, SPRN_IBAT3U + mfspr r7, SPRN_IBAT3L + + stw r4, SS_IBAT+0x10(r3) + stw r5, SS_IBAT+0x14(r3) + stw r6, SS_IBAT+0x18(r3) + stw r7, SS_IBAT+0x1c(r3) + + mfspr r4, SPRN_IBAT4U + mfspr r5, SPRN_IBAT4L + mfspr r6, SPRN_IBAT5U + mfspr r7, SPRN_IBAT5L + + stw r4, SS_IBAT+0x20(r3) + stw r5, SS_IBAT+0x24(r3) + stw r6, SS_IBAT+0x28(r3) + stw r7, SS_IBAT+0x2c(r3) + + mfspr r4, SPRN_IBAT6U + mfspr r5, SPRN_IBAT6L + mfspr r6, SPRN_IBAT7U + mfspr r7, SPRN_IBAT7L + + stw r4, SS_IBAT+0x30(r3) + stw r5, SS_IBAT+0x34(r3) + stw r6, SS_IBAT+0x38(r3) + stw r7, SS_IBAT+0x3c(r3) + + mfmsr r4 + mflr r5 + mfcr r6 + + stw r4, SS_MSR(r3) + stw r5, SS_LR(r3) + stw r6, SS_CR(r3) + stw r1, SS_SP(r3) + stw r2, SS_R2(r3) + +1: mftbu r4 + mftb r5 + mftbu r6 + cmpw r4, r6 + bne 1b + + stw r4, SS_TB+0(r3) + stw r5, SS_TB+4(r3) + + stmw r12, SS_GPREG(r3) + + li r4, 0 + addi r6, r3, SS_SR-4 +1: mfsrin r5, r4 + stwu r5, 4(r6) + addis r4, r4, 0x1000 + cmpwi r4, 0 + bne 1b + + /* Disable machine checks and critical exceptions */ + mfmsr r4 + rlwinm r4, r4, 0, ~MSR_CE + rlwinm r4, r4, 0, ~MSR_ME + mtmsr r4 + isync + +#define TMP_VIRT_IMMR 0xf0000000 +#define DEFAULT_IMMR_VALUE 0xff400000 +#define IMMRBAR_BASE 0x0000 + + lis r4, immrbase@ha + lwz r4, immrbase@l(r4) + + /* Use DBAT0 to address the current IMMR space */ + + ori r4, r4, 0x002a + mtspr SPRN_DBAT0L, r4 + lis r8, TMP_VIRT_IMMR@h + ori r4, r8, 0x001e /* 1 MByte accessible from Kernel Space only */ + mtspr SPRN_DBAT0U, r4 + isync + + /* Use DBAT1 to address the original IMMR space */ + + lis r4, DEFAULT_IMMR_VALUE@h + ori r4, r4, 0x002a + mtspr SPRN_DBAT1L, r4 + lis r9, (TMP_VIRT_IMMR + 0x01000000)@h + ori r4, r9, 0x001e /* 1 MByte accessible from Kernel Space only */ + mtspr SPRN_DBAT1U, r4 + isync + + /* Use DBAT2 to address the beginning of RAM. This isn't done + * using the normal virtual mapping, because with page debugging + * enabled it will be read-only. + */ + + li r4, 0x0002 + mtspr SPRN_DBAT2L, r4 + lis r4, KERNELBASE@h + ori r4, r4, 0x001e /* 1 MByte accessible from Kernel Space only */ + mtspr SPRN_DBAT2U, r4 + isync + + /* Flush the cache with our BAT, as there will be TLB misses + * otherwise if page debugging is enabled, and these misses + * will disturb the PLRU algorithm. + */ + + bl __flush_disable_L1 + + /* Keep the i-cache enabled, so the hack below for low-boot + * flash will work. + */ + mfspr r3, SPRN_HID0 + ori r3, r3, HID0_ICE + mtspr SPRN_HID0, r3 + isync + + lis r6, 0xf515 + ori r6, r6, 0x3ae5 + + lis r7, mpc83xx_deep_resume@h + ori r7, r7, mpc83xx_deep_resume@l + tophys(r7, r7) + + lis r5, KERNELBASE@h + stw r6, 0(r5) + stw r7, 4(r5) + + /* Reset BARs */ + + li r4, 0 + stw r4, 0x0024(r8) + stw r4, 0x002c(r8) + stw r4, 0x0034(r8) + stw r4, 0x003c(r8) + stw r4, 0x0064(r8) + stw r4, 0x006c(r8) + + /* Rev 1 of the 8313 has problems with wakeup events that are + * pending during the transition to deep sleep state (such as if + * the PCI host sets the state to D3 and then D0 in rapid + * succession). This check shrinks the race window somewhat. + * + * See erratum PCI23, though the problem is not limited + * to PCI. + */ + + lwz r3, 0x0b04(r8) + andi. r3, r3, 1 + bne- mpc83xx_deep_resume + + /* Move IMMR back to the default location, following the + * procedure specified in the MPC8313 manual. + */ + lwz r4, IMMRBAR_BASE(r8) + isync + lis r4, DEFAULT_IMMR_VALUE@h + stw r4, IMMRBAR_BASE(r8) + lis r4, KERNELBASE@h + lwz r4, 0(r4) + isync + lwz r4, IMMRBAR_BASE(r9) + mr r8, r9 + isync + + /* Check the Reset Configuration Word to see whether flash needs + * to be mapped at a low address or a high address. + */ + + lwz r4, 0x0904(r8) + andis. r4, r4, 0x0400 + li r4, 0 + beq boot_low + lis r4, 0xff80 +boot_low: + stw r4, 0x0020(r8) + lis r7, 0x8000 + ori r7, r7, 0x0016 + + mfspr r5, SPRN_HID0 + rlwinm r5, r5, 0, ~(HID0_DOZE | HID0_NAP) + oris r5, r5, HID0_SLEEP@h + mtspr SPRN_HID0, r5 + isync + + mfmsr r5 + oris r5, r5, MSR_POW@h + + /* Enable the flash mapping at the appropriate address. This + * mapping will override the RAM mapping if booting low, so there's + * no need to disable the latter. This must be done inside the same + * cache line as setting MSR_POW, so that no instruction fetches + * from RAM happen after the flash mapping is turned on. + */ + + .align 5 + stw r7, 0x0024(r8) + sync + isync + mtmsr r5 + isync +1: b 1b + +mpc83xx_deep_resume: + lis r4, 1f@h + ori r4, r4, 1f@l + tophys(r4, r4) + mtsrr0 r4 + + mfmsr r4 + rlwinm r4, r4, 0, ~(MSR_IR | MSR_DR) + mtsrr1 r4 + + rfi + +1: tlbia + bl __inval_enable_L1 + + lis r3, mpc83xx_sleep_save_area@h + ori r3, r3, mpc83xx_sleep_save_area@l + tophys(r3, r3) + + lwz r5, SS_MEMSAVE+0(r3) + lwz r6, SS_MEMSAVE+4(r3) + + stw r5, 0(0) + stw r6, 4(0) + + lwz r5, SS_HID+0(r3) + lwz r6, SS_HID+4(r3) + lwz r7, SS_HID+8(r3) + + mtspr SPRN_HID0, r5 + mtspr SPRN_HID1, r6 + mtspr SPRN_HID2, r7 + + lwz r4, SS_IABR+0(r3) + lwz r5, SS_IABR+4(r3) + lwz r6, SS_IBCR(r3) + lwz r7, SS_DABR+0(r3) + lwz r8, SS_DABR+4(r3) + lwz r9, SS_DBCR(r3) + + mtspr SPRN_IABR, r4 + mtspr SPRN_IABR2, r5 + mtspr SPRN_IBCR, r6 + mtspr SPRN_DABR, r7 + mtspr SPRN_DABR2, r8 + mtspr SPRN_DBCR, r9 + + li r4, 0 + addi r6, r3, SS_SR-4 +1: lwzu r5, 4(r6) + mtsrin r5, r4 + addis r4, r4, 0x1000 + cmpwi r4, 0 + bne 1b + + lwz r4, SS_DBAT+0x00(r3) + lwz r5, SS_DBAT+0x04(r3) + lwz r6, SS_DBAT+0x08(r3) + lwz r7, SS_DBAT+0x0c(r3) + + mtspr SPRN_DBAT0U, r4 + mtspr SPRN_DBAT0L, r5 + mtspr SPRN_DBAT1U, r6 + mtspr SPRN_DBAT1L, r7 + + lwz r4, SS_DBAT+0x10(r3) + lwz r5, SS_DBAT+0x14(r3) + lwz r6, SS_DBAT+0x18(r3) + lwz r7, SS_DBAT+0x1c(r3) + + mtspr SPRN_DBAT2U, r4 + mtspr SPRN_DBAT2L, r5 + mtspr SPRN_DBAT3U, r6 + mtspr SPRN_DBAT3L, r7 + + lwz r4, SS_DBAT+0x20(r3) + lwz r5, SS_DBAT+0x24(r3) + lwz r6, SS_DBAT+0x28(r3) + lwz r7, SS_DBAT+0x2c(r3) + + mtspr SPRN_DBAT4U, r4 + mtspr SPRN_DBAT4L, r5 + mtspr SPRN_DBAT5U, r6 + mtspr SPRN_DBAT5L, r7 + + lwz r4, SS_DBAT+0x30(r3) + lwz r5, SS_DBAT+0x34(r3) + lwz r6, SS_DBAT+0x38(r3) + lwz r7, SS_DBAT+0x3c(r3) + + mtspr SPRN_DBAT6U, r4 + mtspr SPRN_DBAT6L, r5 + mtspr SPRN_DBAT7U, r6 + mtspr SPRN_DBAT7L, r7 + + lwz r4, SS_IBAT+0x00(r3) + lwz r5, SS_IBAT+0x04(r3) + lwz r6, SS_IBAT+0x08(r3) + lwz r7, SS_IBAT+0x0c(r3) + + mtspr SPRN_IBAT0U, r4 + mtspr SPRN_IBAT0L, r5 + mtspr SPRN_IBAT1U, r6 + mtspr SPRN_IBAT1L, r7 + + lwz r4, SS_IBAT+0x10(r3) + lwz r5, SS_IBAT+0x14(r3) + lwz r6, SS_IBAT+0x18(r3) + lwz r7, SS_IBAT+0x1c(r3) + + mtspr SPRN_IBAT2U, r4 + mtspr SPRN_IBAT2L, r5 + mtspr SPRN_IBAT3U, r6 + mtspr SPRN_IBAT3L, r7 + + lwz r4, SS_IBAT+0x20(r3) + lwz r5, SS_IBAT+0x24(r3) + lwz r6, SS_IBAT+0x28(r3) + lwz r7, SS_IBAT+0x2c(r3) + + mtspr SPRN_IBAT4U, r4 + mtspr SPRN_IBAT4L, r5 + mtspr SPRN_IBAT5U, r6 + mtspr SPRN_IBAT5L, r7 + + lwz r4, SS_IBAT+0x30(r3) + lwz r5, SS_IBAT+0x34(r3) + lwz r6, SS_IBAT+0x38(r3) + lwz r7, SS_IBAT+0x3c(r3) + + mtspr SPRN_IBAT6U, r4 + mtspr SPRN_IBAT6L, r5 + mtspr SPRN_IBAT7U, r6 + mtspr SPRN_IBAT7L, r7 + + lwz r4, SS_SPRG+0(r3) + lwz r5, SS_SPRG+4(r3) + lwz r6, SS_SPRG+8(r3) + lwz r7, SS_SPRG+12(r3) + lwz r8, SS_SDR1(r3) + + mtspr SPRN_SPRG0, r4 + mtspr SPRN_SPRG1, r5 + mtspr SPRN_SPRG2, r6 + mtspr SPRN_SPRG3, r7 + mtsdr1 r8 + + lwz r4, SS_MSR(r3) + lwz r5, SS_LR(r3) + lwz r6, SS_CR(r3) + lwz r1, SS_SP(r3) + lwz r2, SS_R2(r3) + + mtsrr1 r4 + mtsrr0 r5 + mtcr r6 + + li r4, 0 + mtspr SPRN_TBWL, r4 + + lwz r4, SS_TB+0(r3) + lwz r5, SS_TB+4(r3) + + mtspr SPRN_TBWU, r4 + mtspr SPRN_TBWL, r5 + + lmw r12, SS_GPREG(r3) + + /* Kick decrementer */ + li r0, 1 + mtdec r0 + + rfi diff --git a/arch/powerpc/platforms/83xx/suspend.c b/arch/powerpc/platforms/83xx/suspend.c new file mode 100644 index 00000000000..edf66870d97 --- /dev/null +++ b/arch/powerpc/platforms/83xx/suspend.c @@ -0,0 +1,448 @@ +/* + * MPC83xx suspend support + * + * Author: Scott Wood <scottwood@freescale.com> + * + * Copyright (c) 2006-2007 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 published + * by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/types.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/suspend.h> +#include <linux/fsl_devices.h> +#include <linux/of_platform.h> +#include <linux/export.h> + +#include <asm/reg.h> +#include <asm/io.h> +#include <asm/time.h> +#include <asm/mpc6xx.h> + +#include <sysdev/fsl_soc.h> + +#define PMCCR1_NEXT_STATE 0x0C /* Next state for power management */ +#define PMCCR1_NEXT_STATE_SHIFT 2 +#define PMCCR1_CURR_STATE 0x03 /* Current state for power management*/ +#define IMMR_SYSCR_OFFSET 0x100 +#define IMMR_RCW_OFFSET 0x900 +#define RCW_PCI_HOST 0x80000000 + +void mpc83xx_enter_deep_sleep(phys_addr_t immrbase); + +struct mpc83xx_pmc { + u32 config; +#define PMCCR_DLPEN 2 /* DDR SDRAM low power enable */ +#define PMCCR_SLPEN 1 /* System low power enable */ + + u32 event; + u32 mask; +/* All but PMCI are deep-sleep only */ +#define PMCER_GPIO 0x100 +#define PMCER_PCI 0x080 +#define PMCER_USB 0x040 +#define PMCER_ETSEC1 0x020 +#define PMCER_ETSEC2 0x010 +#define PMCER_TIMER 0x008 +#define PMCER_INT1 0x004 +#define PMCER_INT2 0x002 +#define PMCER_PMCI 0x001 +#define PMCER_ALL 0x1FF + + /* deep-sleep only */ + u32 config1; +#define PMCCR1_USE_STATE 0x80000000 +#define PMCCR1_PME_EN 0x00000080 +#define PMCCR1_ASSERT_PME 0x00000040 +#define PMCCR1_POWER_OFF 0x00000020 + + /* deep-sleep only */ + u32 config2; +}; + +struct mpc83xx_rcw { + u32 rcwlr; + u32 rcwhr; +}; + +struct mpc83xx_clock { + u32 spmr; + u32 occr; + u32 sccr; +}; + +struct mpc83xx_syscr { + __be32 sgprl; + __be32 sgprh; + __be32 spridr; + __be32 :32; + __be32 spcr; + __be32 sicrl; + __be32 sicrh; +}; + +struct mpc83xx_saved { + u32 sicrl; + u32 sicrh; + u32 sccr; +}; + +struct pmc_type { + int has_deep_sleep; +}; + +static struct platform_device *pmc_dev; +static int has_deep_sleep, deep_sleeping; +static int pmc_irq; +static struct mpc83xx_pmc __iomem *pmc_regs; +static struct mpc83xx_clock __iomem *clock_regs; +static struct mpc83xx_syscr __iomem *syscr_regs; +static struct mpc83xx_saved saved_regs; +static int is_pci_agent, wake_from_pci; +static phys_addr_t immrbase; +static int pci_pm_state; +static DECLARE_WAIT_QUEUE_HEAD(agent_wq); + +int fsl_deep_sleep(void) +{ + return deep_sleeping; +} +EXPORT_SYMBOL(fsl_deep_sleep); + +static int mpc83xx_change_state(void) +{ + u32 curr_state; + u32 reg_cfg1 = in_be32(&pmc_regs->config1); + + if (is_pci_agent) { + pci_pm_state = (reg_cfg1 & PMCCR1_NEXT_STATE) >> + PMCCR1_NEXT_STATE_SHIFT; + curr_state = reg_cfg1 & PMCCR1_CURR_STATE; + + if (curr_state != pci_pm_state) { + reg_cfg1 &= ~PMCCR1_CURR_STATE; + reg_cfg1 |= pci_pm_state; + out_be32(&pmc_regs->config1, reg_cfg1); + + wake_up(&agent_wq); + return 1; + } + } + + return 0; +} + +static irqreturn_t pmc_irq_handler(int irq, void *dev_id) +{ + u32 event = in_be32(&pmc_regs->event); + int ret = IRQ_NONE; + + if (mpc83xx_change_state()) + ret = IRQ_HANDLED; + + if (event) { + out_be32(&pmc_regs->event, event); + ret = IRQ_HANDLED; + } + + return ret; +} + +static void mpc83xx_suspend_restore_regs(void) +{ + out_be32(&syscr_regs->sicrl, saved_regs.sicrl); + out_be32(&syscr_regs->sicrh, saved_regs.sicrh); + out_be32(&clock_regs->sccr, saved_regs.sccr); +} + +static void mpc83xx_suspend_save_regs(void) +{ + saved_regs.sicrl = in_be32(&syscr_regs->sicrl); + saved_regs.sicrh = in_be32(&syscr_regs->sicrh); + saved_regs.sccr = in_be32(&clock_regs->sccr); +} + +static int mpc83xx_suspend_enter(suspend_state_t state) +{ + int ret = -EAGAIN; + + /* Don't go to sleep if there's a race where pci_pm_state changes + * between the agent thread checking it and the PM code disabling + * interrupts. + */ + if (wake_from_pci) { + if (pci_pm_state != (deep_sleeping ? 3 : 2)) + goto out; + + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) | PMCCR1_PME_EN); + } + + /* Put the system into low-power mode and the RAM + * into self-refresh mode once the core goes to + * sleep. + */ + + out_be32(&pmc_regs->config, PMCCR_SLPEN | PMCCR_DLPEN); + + /* If it has deep sleep (i.e. it's an 831x or compatible), + * disable power to the core upon entering sleep mode. This will + * require going through the boot firmware upon a wakeup event. + */ + + if (deep_sleeping) { + mpc83xx_suspend_save_regs(); + + out_be32(&pmc_regs->mask, PMCER_ALL); + + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) | PMCCR1_POWER_OFF); + + enable_kernel_fp(); + + mpc83xx_enter_deep_sleep(immrbase); + + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) & ~PMCCR1_POWER_OFF); + + out_be32(&pmc_regs->mask, PMCER_PMCI); + + mpc83xx_suspend_restore_regs(); + } else { + out_be32(&pmc_regs->mask, PMCER_PMCI); + + mpc6xx_enter_standby(); + } + + ret = 0; + +out: + out_be32(&pmc_regs->config1, + in_be32(&pmc_regs->config1) & ~PMCCR1_PME_EN); + + return ret; +} + +static void mpc83xx_suspend_end(void) +{ + deep_sleeping = 0; +} + +static int mpc83xx_suspend_valid(suspend_state_t state) +{ + return state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM; +} + +static int mpc83xx_suspend_begin(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + deep_sleeping = 0; + return 0; + + case PM_SUSPEND_MEM: + if (has_deep_sleep) + deep_sleeping = 1; + + return 0; + + default: + return -EINVAL; + } +} + +static int agent_thread_fn(void *data) +{ + while (1) { + wait_event_interruptible(agent_wq, pci_pm_state >= 2); + try_to_freeze(); + + if (signal_pending(current) || pci_pm_state < 2) + continue; + + /* With a preemptible kernel (or SMP), this could race with + * a userspace-driven suspend request. It's probably best + * to avoid mixing the two with such a configuration (or + * else fix it by adding a mutex to state_store that we can + * synchronize with). + */ + + wake_from_pci = 1; + + pm_suspend(pci_pm_state == 3 ? PM_SUSPEND_MEM : + PM_SUSPEND_STANDBY); + + wake_from_pci = 0; + } + + return 0; +} + +static void mpc83xx_set_agent(void) +{ + out_be32(&pmc_regs->config1, PMCCR1_USE_STATE); + out_be32(&pmc_regs->mask, PMCER_PMCI); + + kthread_run(agent_thread_fn, NULL, "PCI power mgt"); +} + +static int mpc83xx_is_pci_agent(void) +{ + struct mpc83xx_rcw __iomem *rcw_regs; + int ret; + + rcw_regs = ioremap(get_immrbase() + IMMR_RCW_OFFSET, + sizeof(struct mpc83xx_rcw)); + + if (!rcw_regs) + return -ENOMEM; + + ret = !(in_be32(&rcw_regs->rcwhr) & RCW_PCI_HOST); + + iounmap(rcw_regs); + return ret; +} + +static const struct platform_suspend_ops mpc83xx_suspend_ops = { + .valid = mpc83xx_suspend_valid, + .begin = mpc83xx_suspend_begin, + .enter = mpc83xx_suspend_enter, + .end = mpc83xx_suspend_end, +}; + +static struct of_device_id pmc_match[]; +static int pmc_probe(struct platform_device *ofdev) +{ + const struct of_device_id *match; + struct device_node *np = ofdev->dev.of_node; + struct resource res; + struct pmc_type *type; + int ret = 0; + + match = of_match_device(pmc_match, &ofdev->dev); + if (!match) + return -EINVAL; + + type = match->data; + + if (!of_device_is_available(np)) + return -ENODEV; + + has_deep_sleep = type->has_deep_sleep; + immrbase = get_immrbase(); + pmc_dev = ofdev; + + is_pci_agent = mpc83xx_is_pci_agent(); + if (is_pci_agent < 0) + return is_pci_agent; + + ret = of_address_to_resource(np, 0, &res); + if (ret) + return -ENODEV; + + pmc_irq = irq_of_parse_and_map(np, 0); + if (pmc_irq != NO_IRQ) { + ret = request_irq(pmc_irq, pmc_irq_handler, IRQF_SHARED, + "pmc", ofdev); + + if (ret) + return -EBUSY; + } + + pmc_regs = ioremap(res.start, sizeof(struct mpc83xx_pmc)); + + if (!pmc_regs) { + ret = -ENOMEM; + goto out; + } + + ret = of_address_to_resource(np, 1, &res); + if (ret) { + ret = -ENODEV; + goto out_pmc; + } + + clock_regs = ioremap(res.start, sizeof(struct mpc83xx_pmc)); + + if (!clock_regs) { + ret = -ENOMEM; + goto out_pmc; + } + + if (has_deep_sleep) { + syscr_regs = ioremap(immrbase + IMMR_SYSCR_OFFSET, + sizeof(*syscr_regs)); + if (!syscr_regs) { + ret = -ENOMEM; + goto out_syscr; + } + } + + if (is_pci_agent) + mpc83xx_set_agent(); + + suspend_set_ops(&mpc83xx_suspend_ops); + return 0; + +out_syscr: + iounmap(clock_regs); +out_pmc: + iounmap(pmc_regs); +out: + if (pmc_irq != NO_IRQ) + free_irq(pmc_irq, ofdev); + + return ret; +} + +static int pmc_remove(struct platform_device *ofdev) +{ + return -EPERM; +}; + +static struct pmc_type pmc_types[] = { + { + .has_deep_sleep = 1, + }, + { + .has_deep_sleep = 0, + } +}; + +static struct of_device_id pmc_match[] = { + { + .compatible = "fsl,mpc8313-pmc", + .data = &pmc_types[0], + }, + { + .compatible = "fsl,mpc8349-pmc", + .data = &pmc_types[1], + }, + {} +}; + +static struct platform_driver pmc_driver = { + .driver = { + .name = "mpc83xx-pmc", + .owner = THIS_MODULE, + .of_match_table = pmc_match, + }, + .probe = pmc_probe, + .remove = pmc_remove +}; + +static int pmc_init(void) +{ + return platform_driver_register(&pmc_driver); +} + +module_init(pmc_init); diff --git a/arch/powerpc/platforms/83xx/usb.c b/arch/powerpc/platforms/83xx/usb.c new file mode 100644 index 00000000000..1ad748bb39b --- /dev/null +++ b/arch/powerpc/platforms/83xx/usb.c @@ -0,0 +1,254 @@ +/* + * Freescale 83xx USB SOC setup code + * + * Copyright (C) 2007 Freescale Semiconductor, Inc. + * Author: 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/of.h> + +#include <asm/io.h> +#include <asm/prom.h> +#include <sysdev/fsl_soc.h> + +#include "mpc83xx.h" + + +#ifdef CONFIG_PPC_MPC834x +int mpc834x_usb_cfg(void) +{ + unsigned long sccr, sicrl, sicrh; + void __iomem *immap; + struct device_node *np = NULL; + int port0_is_dr = 0, port1_is_dr = 0; + const void *prop, *dr_mode; + + immap = ioremap(get_immrbase(), 0x1000); + if (!immap) + return -ENOMEM; + + /* Read registers */ + /* Note: DR and MPH must use the same clock setting in SCCR */ + sccr = in_be32(immap + MPC83XX_SCCR_OFFS) & ~MPC83XX_SCCR_USB_MASK; + sicrl = in_be32(immap + MPC83XX_SICRL_OFFS) & ~MPC834X_SICRL_USB_MASK; + sicrh = in_be32(immap + MPC83XX_SICRH_OFFS) & ~MPC834X_SICRH_USB_UTMI; + + np = of_find_compatible_node(NULL, NULL, "fsl-usb2-dr"); + if (np) { + sccr |= MPC83XX_SCCR_USB_DRCM_11; /* 1:3 */ + + prop = of_get_property(np, "phy_type", NULL); + port1_is_dr = 1; + if (prop && (!strcmp(prop, "utmi") || + !strcmp(prop, "utmi_wide"))) { + sicrl |= MPC834X_SICRL_USB0 | MPC834X_SICRL_USB1; + sicrh |= MPC834X_SICRH_USB_UTMI; + port0_is_dr = 1; + } else if (prop && !strcmp(prop, "serial")) { + dr_mode = of_get_property(np, "dr_mode", NULL); + if (dr_mode && !strcmp(dr_mode, "otg")) { + sicrl |= MPC834X_SICRL_USB0 | MPC834X_SICRL_USB1; + port0_is_dr = 1; + } else { + sicrl |= MPC834X_SICRL_USB1; + } + } else if (prop && !strcmp(prop, "ulpi")) { + sicrl |= MPC834X_SICRL_USB1; + } else { + printk(KERN_WARNING "834x USB PHY type not supported\n"); + } + of_node_put(np); + } + np = of_find_compatible_node(NULL, NULL, "fsl-usb2-mph"); + if (np) { + sccr |= MPC83XX_SCCR_USB_MPHCM_11; /* 1:3 */ + + prop = of_get_property(np, "port0", NULL); + if (prop) { + if (port0_is_dr) + printk(KERN_WARNING + "834x USB port0 can't be used by both DR and MPH!\n"); + sicrl &= ~MPC834X_SICRL_USB0; + } + prop = of_get_property(np, "port1", NULL); + if (prop) { + if (port1_is_dr) + printk(KERN_WARNING + "834x USB port1 can't be used by both DR and MPH!\n"); + sicrl &= ~MPC834X_SICRL_USB1; + } + of_node_put(np); + } + + /* Write back */ + out_be32(immap + MPC83XX_SCCR_OFFS, sccr); + out_be32(immap + MPC83XX_SICRL_OFFS, sicrl); + out_be32(immap + MPC83XX_SICRH_OFFS, sicrh); + + iounmap(immap); + return 0; +} +#endif /* CONFIG_PPC_MPC834x */ + +#ifdef CONFIG_PPC_MPC831x +int mpc831x_usb_cfg(void) +{ + u32 temp; + void __iomem *immap, *usb_regs; + struct device_node *np = NULL; + struct device_node *immr_node = NULL; + const void *prop; + struct resource res; + int ret = 0; +#ifdef CONFIG_USB_OTG + const void *dr_mode; +#endif + + np = of_find_compatible_node(NULL, NULL, "fsl-usb2-dr"); + if (!np) + return -ENODEV; + prop = of_get_property(np, "phy_type", NULL); + + /* Map IMMR space for pin and clock settings */ + immap = ioremap(get_immrbase(), 0x1000); + if (!immap) { + of_node_put(np); + return -ENOMEM; + } + + /* Configure clock */ + immr_node = of_get_parent(np); + if (immr_node && (of_device_is_compatible(immr_node, "fsl,mpc8315-immr") || + of_device_is_compatible(immr_node, "fsl,mpc8308-immr"))) + clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, + MPC8315_SCCR_USB_MASK, + MPC8315_SCCR_USB_DRCM_01); + else + clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, + MPC83XX_SCCR_USB_MASK, + MPC83XX_SCCR_USB_DRCM_11); + + /* Configure pin mux for ULPI. There is no pin mux for UTMI */ + if (prop && !strcmp(prop, "ulpi")) { + if (of_device_is_compatible(immr_node, "fsl,mpc8308-immr")) { + clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, + MPC8308_SICRH_USB_MASK, + MPC8308_SICRH_USB_ULPI); + } else if (of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) { + clrsetbits_be32(immap + MPC83XX_SICRL_OFFS, + MPC8315_SICRL_USB_MASK, + MPC8315_SICRL_USB_ULPI); + clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, + MPC8315_SICRH_USB_MASK, + MPC8315_SICRH_USB_ULPI); + } else { + clrsetbits_be32(immap + MPC83XX_SICRL_OFFS, + MPC831X_SICRL_USB_MASK, + MPC831X_SICRL_USB_ULPI); + clrsetbits_be32(immap + MPC83XX_SICRH_OFFS, + MPC831X_SICRH_USB_MASK, + MPC831X_SICRH_USB_ULPI); + } + } + + iounmap(immap); + + if (immr_node) + of_node_put(immr_node); + + /* Map USB SOC space */ + ret = of_address_to_resource(np, 0, &res); + if (ret) { + of_node_put(np); + return ret; + } + usb_regs = ioremap(res.start, resource_size(&res)); + + /* Using on-chip PHY */ + if (prop && (!strcmp(prop, "utmi_wide") || + !strcmp(prop, "utmi"))) { + u32 refsel; + + if (of_device_is_compatible(immr_node, "fsl,mpc8308-immr")) + goto out; + + if (of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) + refsel = CONTROL_REFSEL_24MHZ; + else + refsel = CONTROL_REFSEL_48MHZ; + /* Set UTMI_PHY_EN and REFSEL */ + out_be32(usb_regs + FSL_USB2_CONTROL_OFFS, + CONTROL_UTMI_PHY_EN | refsel); + /* Using external UPLI PHY */ + } else if (prop && !strcmp(prop, "ulpi")) { + /* Set PHY_CLK_SEL to ULPI */ + temp = CONTROL_PHY_CLK_SEL_ULPI; +#ifdef CONFIG_USB_OTG + /* Set OTG_PORT */ + if (!of_device_is_compatible(immr_node, "fsl,mpc8308-immr")) { + dr_mode = of_get_property(np, "dr_mode", NULL); + if (dr_mode && !strcmp(dr_mode, "otg")) + temp |= CONTROL_OTG_PORT; + } +#endif /* CONFIG_USB_OTG */ + out_be32(usb_regs + FSL_USB2_CONTROL_OFFS, temp); + } else { + printk(KERN_WARNING "831x USB PHY type not supported\n"); + ret = -EINVAL; + } + +out: + iounmap(usb_regs); + of_node_put(np); + return ret; +} +#endif /* CONFIG_PPC_MPC831x */ + +#ifdef CONFIG_PPC_MPC837x +int mpc837x_usb_cfg(void) +{ + void __iomem *immap; + struct device_node *np = NULL; + const void *prop; + int ret = 0; + + np = of_find_compatible_node(NULL, NULL, "fsl-usb2-dr"); + if (!np || !of_device_is_available(np)) + return -ENODEV; + prop = of_get_property(np, "phy_type", NULL); + + if (!prop || (strcmp(prop, "ulpi") && strcmp(prop, "serial"))) { + printk(KERN_WARNING "837x USB PHY type not supported\n"); + of_node_put(np); + return -EINVAL; + } + + /* Map IMMR space for pin and clock settings */ + immap = ioremap(get_immrbase(), 0x1000); + if (!immap) { + of_node_put(np); + return -ENOMEM; + } + + /* Configure clock */ + clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, MPC837X_SCCR_USB_DRCM_11, + MPC837X_SCCR_USB_DRCM_11); + + /* Configure pin mux for ULPI/serial */ + clrsetbits_be32(immap + MPC83XX_SICRL_OFFS, MPC837X_SICRL_USB_MASK, + MPC837X_SICRL_USB_ULPI); + + iounmap(immap); + of_node_put(np); + return ret; +} +#endif /* CONFIG_PPC_MPC837x */ diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig new file mode 100644 index 00000000000..d7946be298b --- /dev/null +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -0,0 +1,244 @@ +menuconfig FSL_SOC_BOOKE + bool "Freescale Book-E Machine Type" + depends on PPC_85xx || PPC_BOOK3E + select FSL_SOC + select PPC_UDBG_16550 + select MPIC + select PPC_PCI_CHOICE + select FSL_PCI if PCI + select SERIAL_8250_SHARE_IRQ if SERIAL_8250 + default y + +if FSL_SOC_BOOKE + +if PPC32 + +config MPC8540_ADS + bool "Freescale MPC8540 ADS" + select DEFAULT_UIMAGE + help + This option enables support for the MPC 8540 ADS board + +config MPC8560_ADS + bool "Freescale MPC8560 ADS" + select DEFAULT_UIMAGE + select CPM2 + help + This option enables support for the MPC 8560 ADS board + +config MPC85xx_CDS + bool "Freescale MPC85xx CDS" + select DEFAULT_UIMAGE + select PPC_I8259 + help + This option enables support for the MPC85xx CDS board + +config MPC85xx_MDS + bool "Freescale MPC85xx MDS" + select DEFAULT_UIMAGE + select PHYLIB + select HAS_RAPIDIO + select SWIOTLB + help + This option enables support for the MPC85xx MDS board + +config MPC8536_DS + bool "Freescale MPC8536 DS" + select DEFAULT_UIMAGE + select SWIOTLB + help + This option enables support for the MPC8536 DS board + +config MPC85xx_DS + bool "Freescale MPC85xx DS" + select PPC_I8259 + select DEFAULT_UIMAGE + select FSL_ULI1575 if PCI + select SWIOTLB + help + This option enables support for the MPC85xx DS (MPC8544 DS) board + +config MPC85xx_RDB + bool "Freescale MPC85xx RDB" + select PPC_I8259 + select DEFAULT_UIMAGE + select FSL_ULI1575 if PCI + select SWIOTLB + help + This option enables support for the MPC85xx RDB (P2020 RDB) board + +config P1010_RDB + bool "Freescale P1010RDB" + select DEFAULT_UIMAGE + help + This option enables support for the MPC85xx RDB (P1010 RDB) board + + P1010RDB contains P1010Si, which provides CPU performance up to 800 + MHz and 1600 DMIPS, additional functionality and faster interfaces + (DDR3/3L, SATA II, and PCI Express). + +config P1022_DS + bool "Freescale P1022 DS" + select DEFAULT_UIMAGE + select PHYS_64BIT # The DTS has 36-bit addresses + select SWIOTLB + help + This option enables support for the Freescale P1022DS reference board. + +config P1023_RDS + bool "Freescale P1023 RDS" + select DEFAULT_UIMAGE + help + This option enables support for the P1023 RDS board + +config SOCRATES + bool "Socrates" + select DEFAULT_UIMAGE + help + This option enables support for the Socrates board. + +config KSI8560 + bool "Emerson KSI8560" + select DEFAULT_UIMAGE + help + This option enables support for the Emerson KSI8560 board + +config XES_MPC85xx + bool "X-ES single-board computer" + select DEFAULT_UIMAGE + help + This option enables support for the various single-board + computers from Extreme Engineering Solutions (X-ES) based on + Freescale MPC85xx processors. + Manufacturer: Extreme Engineering Solutions, Inc. + URL: <http://www.xes-inc.com/> + +config STX_GP3 + bool "Silicon Turnkey Express GP3" + help + This option enables support for the Silicon Turnkey Express GP3 + board. + select CPM2 + select DEFAULT_UIMAGE + +config TQM8540 + bool "TQ Components TQM8540" + help + This option enables support for the TQ Components TQM8540 board. + select DEFAULT_UIMAGE + select TQM85xx + +config TQM8541 + bool "TQ Components TQM8541" + help + This option enables support for the TQ Components TQM8541 board. + select DEFAULT_UIMAGE + select TQM85xx + select CPM2 + +config TQM8548 + bool "TQ Components TQM8548" + help + This option enables support for the TQ Components TQM8548 board. + select DEFAULT_UIMAGE + select TQM85xx + +config TQM8555 + bool "TQ Components TQM8555" + help + This option enables support for the TQ Components TQM8555 board. + select DEFAULT_UIMAGE + select TQM85xx + select CPM2 + +config TQM8560 + bool "TQ Components TQM8560" + help + This option enables support for the TQ Components TQM8560 board. + select DEFAULT_UIMAGE + select TQM85xx + select CPM2 + +config SBC8548 + bool "Wind River SBC8548" + select DEFAULT_UIMAGE + help + This option enables support for the Wind River SBC8548 board + +config SBC8560 + bool "Wind River SBC8560" + select DEFAULT_UIMAGE + help + This option enables support for the Wind River SBC8560 board + +config P2041_RDB + bool "Freescale P2041 RDB" + select DEFAULT_UIMAGE + select PPC_E500MC + select PHYS_64BIT + select SWIOTLB + select ARCH_REQUIRE_GPIOLIB + select GPIO_MPC8XXX + select HAS_RAPIDIO + select PPC_EPAPR_HV_PIC + help + This option enables support for the P2041 RDB board + +config P3041_DS + bool "Freescale P3041 DS" + select DEFAULT_UIMAGE + select PPC_E500MC + select PHYS_64BIT + select SWIOTLB + select ARCH_REQUIRE_GPIOLIB + select GPIO_MPC8XXX + select HAS_RAPIDIO + select PPC_EPAPR_HV_PIC + help + This option enables support for the P3041 DS board + +config P3060_QDS + bool "Freescale P3060 QDS" + select DEFAULT_UIMAGE + select PPC_E500MC + select PHYS_64BIT + select SWIOTLB + select GPIO_MPC8XXX + select HAS_RAPIDIO + select PPC_EPAPR_HV_PIC + help + This option enables support for the P3060 QDS board + +config P4080_DS + bool "Freescale P4080 DS" + select DEFAULT_UIMAGE + select PPC_E500MC + select PHYS_64BIT + select SWIOTLB + select ARCH_REQUIRE_GPIOLIB + select GPIO_MPC8XXX + select HAS_RAPIDIO + select PPC_EPAPR_HV_PIC + help + This option enables support for the P4080 DS board + +endif # PPC32 + +config P5020_DS + bool "Freescale P5020 DS" + select DEFAULT_UIMAGE + select E500 + select PPC_E500MC + select PHYS_64BIT + select SWIOTLB + select ARCH_REQUIRE_GPIOLIB + select GPIO_MPC8XXX + select HAS_RAPIDIO + select PPC_EPAPR_HV_PIC + help + This option enables support for the P5020 DS board + +endif # FSL_SOC_BOOKE + +config TQM85xx + bool diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile new file mode 100644 index 00000000000..9cb2d4320dc --- /dev/null +++ b/arch/powerpc/platforms/85xx/Makefile @@ -0,0 +1,29 @@ +# +# Makefile for the PowerPC 85xx linux kernel. +# +obj-$(CONFIG_SMP) += smp.o + +obj-y += common.o + +obj-$(CONFIG_MPC8540_ADS) += mpc85xx_ads.o +obj-$(CONFIG_MPC8560_ADS) += mpc85xx_ads.o +obj-$(CONFIG_MPC85xx_CDS) += mpc85xx_cds.o +obj-$(CONFIG_MPC8536_DS) += mpc8536_ds.o +obj-$(CONFIG_MPC85xx_DS) += mpc85xx_ds.o +obj-$(CONFIG_MPC85xx_MDS) += mpc85xx_mds.o +obj-$(CONFIG_MPC85xx_RDB) += mpc85xx_rdb.o +obj-$(CONFIG_P1010_RDB) += p1010rdb.o +obj-$(CONFIG_P1022_DS) += p1022_ds.o +obj-$(CONFIG_P1023_RDS) += p1023_rds.o +obj-$(CONFIG_P2041_RDB) += p2041_rdb.o corenet_ds.o +obj-$(CONFIG_P3041_DS) += p3041_ds.o corenet_ds.o +obj-$(CONFIG_P3060_QDS) += p3060_qds.o corenet_ds.o +obj-$(CONFIG_P4080_DS) += p4080_ds.o corenet_ds.o +obj-$(CONFIG_P5020_DS) += p5020_ds.o corenet_ds.o +obj-$(CONFIG_STX_GP3) += stx_gp3.o +obj-$(CONFIG_TQM85xx) += tqm85xx.o +obj-$(CONFIG_SBC8560) += sbc8560.o +obj-$(CONFIG_SBC8548) += sbc8548.o +obj-$(CONFIG_SOCRATES) += socrates.o socrates_fpga_pic.o +obj-$(CONFIG_KSI8560) += ksi8560.o +obj-$(CONFIG_XES_MPC85xx) += xes_mpc85xx.o diff --git a/arch/powerpc/platforms/85xx/common.c b/arch/powerpc/platforms/85xx/common.c new file mode 100644 index 00000000000..9fef5302adc --- /dev/null +++ b/arch/powerpc/platforms/85xx/common.c @@ -0,0 +1,66 @@ +/* + * Routines common to most mpc85xx-based boards. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/of_platform.h> + +#include <sysdev/cpm2_pic.h> + +#include "mpc85xx.h" + +static struct of_device_id __initdata mpc85xx_common_ids[] = { + { .type = "soc", }, + { .compatible = "soc", }, + { .compatible = "simple-bus", }, + { .name = "cpm", }, + { .name = "localbus", }, + { .compatible = "gianfar", }, + { .compatible = "fsl,qe", }, + { .compatible = "fsl,cpm2", }, + { .compatible = "fsl,srio", }, + {}, +}; + +int __init mpc85xx_common_publish_devices(void) +{ + return of_platform_bus_probe(NULL, mpc85xx_common_ids, NULL); +} +#ifdef CONFIG_CPM2 +static void cpm2_cascade(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + int cascade_irq; + + while ((cascade_irq = cpm2_get_irq()) >= 0) + generic_handle_irq(cascade_irq); + + chip->irq_eoi(&desc->irq_data); +} + + +void __init mpc85xx_cpm2_pic_init(void) +{ + struct device_node *np; + int irq; + + /* Setup CPM2 PIC */ + np = of_find_compatible_node(NULL, NULL, "fsl,cpm2-pic"); + if (np == NULL) { + printk(KERN_ERR "PIC init: can not find fsl,cpm2-pic node\n"); + return; + } + irq = irq_of_parse_and_map(np, 0); + if (irq == NO_IRQ) { + of_node_put(np); + printk(KERN_ERR "PIC init: got no IRQ for cpm cascade\n"); + return; + } + + cpm2_pic_init(np); + of_node_put(np); + irq_set_chained_handler(irq, cpm2_cascade); +} +#endif diff --git a/arch/powerpc/platforms/85xx/corenet_ds.c b/arch/powerpc/platforms/85xx/corenet_ds.c new file mode 100644 index 00000000000..07e3e6c4737 --- /dev/null +++ b/arch/powerpc/platforms/85xx/corenet_ds.c @@ -0,0 +1,116 @@ +/* + * Corenet based SoC DS Setup + * + * Maintained by Kumar Gala (see MAINTAINERS for contact information) + * + * Copyright 2009-2011 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 <linux/kernel.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/memblock.h> + +#include <asm/system.h> +#include <asm/time.h> +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <asm/ppc-pci.h> +#include <mm/mmu_decl.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/mpic.h> + +#include <linux/of_platform.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> +#include "smp.h" + +void __init corenet_ds_pic_init(void) +{ + struct mpic *mpic; + unsigned int flags = MPIC_BIG_ENDIAN | + MPIC_BROKEN_FRR_NIRQS | MPIC_SINGLE_DEST_CPU; + + if (ppc_md.get_irq == mpic_get_coreint_irq) + flags |= MPIC_ENABLE_COREINT; + + mpic = mpic_alloc(NULL, 0, flags, 0, 256, " OpenPIC "); + BUG_ON(mpic == NULL); + + mpic_init(mpic); +} + +/* + * Setup the architecture + */ +void __init corenet_ds_setup_arch(void) +{ +#ifdef CONFIG_PCI + struct device_node *np; + struct pci_controller *hose; +#endif + dma_addr_t max = 0xffffffff; + + mpc85xx_smp_init(); + +#ifdef CONFIG_PCI + for_each_node_by_type(np, "pci") { + if (of_device_is_compatible(np, "fsl,p4080-pcie") || + of_device_is_compatible(np, "fsl,qoriq-pcie-v2.2")) { + fsl_add_bridge(np, 0); + hose = pci_find_hose_for_OF_device(np); + max = min(max, hose->dma_window_base_cur + + hose->dma_window_size); + } + } + +#ifdef CONFIG_PPC64 + pci_devs_phb_init(); +#endif +#endif + +#ifdef CONFIG_SWIOTLB + if (memblock_end_of_DRAM() > max) { + ppc_swiotlb_enable = 1; + set_pci_dma_ops(&swiotlb_dma_ops); + ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_swiotlb; + } +#endif + pr_info("%s board from Freescale Semiconductor\n", ppc_md.name); +} + +static const struct of_device_id of_device_ids[] __devinitconst = { + { + .compatible = "simple-bus" + }, + { + .compatible = "fsl,srio", + }, + { + .compatible = "fsl,p4080-pcie", + }, + { + .compatible = "fsl,qoriq-pcie-v2.2", + }, + /* The following two are for the Freescale hypervisor */ + { + .name = "hypervisor", + }, + { + .name = "handles", + }, + {} +}; + +int __init corenet_ds_publish_devices(void) +{ + return of_platform_bus_probe(NULL, of_device_ids, NULL); +} diff --git a/arch/powerpc/platforms/85xx/corenet_ds.h b/arch/powerpc/platforms/85xx/corenet_ds.h new file mode 100644 index 00000000000..ddd700b2303 --- /dev/null +++ b/arch/powerpc/platforms/85xx/corenet_ds.h @@ -0,0 +1,19 @@ +/* + * Corenet based SoC DS Setup + * + * Copyright 2009 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 + * published by the Free Software Foundation. + * + */ + +#ifndef CORENET_DS_H +#define CORENET_DS_H + +extern void __init corenet_ds_pic_init(void); +extern void __init corenet_ds_setup_arch(void); +extern int __init corenet_ds_publish_devices(void); + +#endif diff --git a/arch/powerpc/platforms/85xx/ksi8560.c b/arch/powerpc/platforms/85xx/ksi8560.c new file mode 100644 index 00000000000..20f75d7819c --- /dev/null +++ b/arch/powerpc/platforms/85xx/ksi8560.c @@ -0,0 +1,195 @@ +/* + * Board setup routines for the Emerson KSI8560 + * + * Author: Alexandr Smirnov <asmirnov@ru.mvista.com> + * + * Based on mpc85xx_ads.c maintained by Kumar Gala + * + * 2008 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + * + */ + +#include <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/delay.h> +#include <linux/seq_file.h> +#include <linux/of_platform.h> + +#include <asm/system.h> +#include <asm/time.h> +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <asm/mpic.h> +#include <mm/mmu_decl.h> +#include <asm/udbg.h> +#include <asm/prom.h> + +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#include <asm/cpm2.h> +#include <sysdev/cpm2_pic.h> + +#include "mpc85xx.h" + +#define KSI8560_CPLD_HVR 0x04 /* Hardware Version Register */ +#define KSI8560_CPLD_PVR 0x08 /* PLD Version Register */ +#define KSI8560_CPLD_RCR1 0x30 /* Reset Command Register 1 */ + +#define KSI8560_CPLD_RCR1_CPUHR 0x80 /* CPU Hard Reset */ + +static void __iomem *cpld_base = NULL; + +static void machine_restart(char *cmd) +{ + if (cpld_base) + out_8(cpld_base + KSI8560_CPLD_RCR1, KSI8560_CPLD_RCR1_CPUHR); + else + printk(KERN_ERR "Can't find CPLD base, hang forever\n"); + + for (;;); +} + +static void __init ksi8560_pic_init(void) +{ + struct mpic *mpic = mpic_alloc(NULL, 0, + MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, + 0, 256, " OpenPIC "); + BUG_ON(mpic == NULL); + mpic_init(mpic); + + mpc85xx_cpm2_pic_init(); +} + +#ifdef CONFIG_CPM2 +/* + * Setup I/O ports + */ +struct cpm_pin { + int port, pin, flags; +}; + +static struct cpm_pin __initdata ksi8560_pins[] = { + /* SCC1 */ + {3, 29, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {3, 30, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {3, 31, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* SCC2 */ + {3, 26, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {3, 27, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {3, 28, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* FCC1 */ + {0, 14, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 15, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 16, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 17, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {0, 18, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 19, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 20, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 21, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {0, 26, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {0, 27, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {0, 28, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {0, 29, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {0, 30, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {0, 31, CPM_PIN_INPUT | CPM_PIN_SECONDARY}, + {2, 23, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* CLK9 */ + {2, 22, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* CLK10 */ + +}; + +static void __init init_ioports(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ksi8560_pins); i++) { + struct cpm_pin *pin = &ksi8560_pins[i]; + cpm2_set_pin(pin->port, pin->pin, pin->flags); + } + + cpm2_clk_setup(CPM_CLK_SCC1, CPM_BRG1, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_SCC1, CPM_BRG1, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_SCC2, CPM_BRG2, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_SCC2, CPM_BRG2, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_FCC1, CPM_CLK9, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_FCC1, CPM_CLK10, CPM_CLK_TX); +} +#endif + +/* + * Setup the architecture + */ +static void __init ksi8560_setup_arch(void) +{ + struct device_node *cpld; + + cpld = of_find_compatible_node(NULL, NULL, "emerson,KSI8560-cpld"); + if (cpld) + cpld_base = of_iomap(cpld, 0); + else + printk(KERN_ERR "Can't find CPLD in device tree\n"); + + if (ppc_md.progress) + ppc_md.progress("ksi8560_setup_arch()", 0); + +#ifdef CONFIG_CPM2 + cpm2_reset(); + init_ioports(); +#endif +} + +static void ksi8560_show_cpuinfo(struct seq_file *m) +{ + uint pvid, svid, phid1; + + pvid = mfspr(SPRN_PVR); + svid = mfspr(SPRN_SVR); + + seq_printf(m, "Vendor\t\t: Emerson Network Power\n"); + seq_printf(m, "Board\t\t: KSI8560\n"); + + if (cpld_base) { + seq_printf(m, "Hardware rev\t: %d\n", + in_8(cpld_base + KSI8560_CPLD_HVR)); + seq_printf(m, "CPLD rev\t: %d\n", + in_8(cpld_base + KSI8560_CPLD_PVR)); + } else + seq_printf(m, "Unknown Hardware and CPLD revs\n"); + + seq_printf(m, "PVR\t\t: 0x%x\n", pvid); + seq_printf(m, "SVR\t\t: 0x%x\n", svid); + + /* Display cpu Pll setting */ + phid1 = mfspr(SPRN_HID1); + seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f)); +} + +machine_device_initcall(ksi8560, mpc85xx_common_publish_devices); + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init ksi8560_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "emerson,KSI8560"); +} + +define_machine(ksi8560) { + .name = "KSI8560", + .probe = ksi8560_probe, + .setup_arch = ksi8560_setup_arch, + .init_IRQ = ksi8560_pic_init, + .show_cpuinfo = ksi8560_show_cpuinfo, + .get_irq = mpic_get_irq, + .restart = machine_restart, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/85xx/mpc8536_ds.c b/arch/powerpc/platforms/85xx/mpc8536_ds.c new file mode 100644 index 00000000000..cf266826682 --- /dev/null +++ b/arch/powerpc/platforms/85xx/mpc8536_ds.c @@ -0,0 +1,117 @@ +/* + * MPC8536 DS Board Setup + * + * Copyright 2008 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/delay.h> +#include <linux/seq_file.h> +#include <linux/interrupt.h> +#include <linux/of_platform.h> +#include <linux/memblock.h> + +#include <asm/system.h> +#include <asm/time.h> +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <mm/mmu_decl.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/mpic.h> +#include <asm/swiotlb.h> + +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#include "mpc85xx.h" + +void __init mpc8536_ds_pic_init(void) +{ + struct mpic *mpic = mpic_alloc(NULL, 0, + MPIC_WANTS_RESET | + MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS, + 0, 256, " OpenPIC "); + BUG_ON(mpic == NULL); + mpic_init(mpic); +} + +/* + * Setup the architecture + */ +static void __init mpc8536_ds_setup_arch(void) +{ +#ifdef CONFIG_PCI + struct device_node *np; + struct pci_controller *hose; +#endif + dma_addr_t max = 0xffffffff; + + if (ppc_md.progress) + ppc_md.progress("mpc8536_ds_setup_arch()", 0); + +#ifdef CONFIG_PCI + for_each_node_by_type(np, "pci") { + if (of_device_is_compatible(np, "fsl,mpc8540-pci") || + of_device_is_compatible(np, "fsl,mpc8548-pcie")) { + struct resource rsrc; + of_address_to_resource(np, 0, &rsrc); + if ((rsrc.start & 0xfffff) == 0x8000) + fsl_add_bridge(np, 1); + else + fsl_add_bridge(np, 0); + + hose = pci_find_hose_for_OF_device(np); + max = min(max, hose->dma_window_base_cur + + hose->dma_window_size); + } + } + +#endif + +#ifdef CONFIG_SWIOTLB + if (memblock_end_of_DRAM() > max) { + ppc_swiotlb_enable = 1; + set_pci_dma_ops(&swiotlb_dma_ops); + ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_swiotlb; + } +#endif + + printk("MPC8536 DS board from Freescale Semiconductor\n"); +} + +machine_device_initcall(mpc8536_ds, mpc85xx_common_publish_devices); + +machine_arch_initcall(mpc8536_ds, swiotlb_setup_bus_notifier); + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init mpc8536_ds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,mpc8536ds"); +} + +define_machine(mpc8536_ds) { + .name = "MPC8536 DS", + .probe = mpc8536_ds_probe, + .setup_arch = mpc8536_ds_setup_arch, + .init_IRQ = mpc8536_ds_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/85xx/mpc85xx.h b/arch/powerpc/platforms/85xx/mpc85xx.h new file mode 100644 index 00000000000..2aa7c5dc2c7 --- /dev/null +++ b/arch/powerpc/platforms/85xx/mpc85xx.h @@ -0,0 +1,11 @@ +#ifndef MPC85xx_H +#define MPC85xx_H +extern int mpc85xx_common_publish_devices(void); + +#ifdef CONFIG_CPM2 +extern void mpc85xx_cpm2_pic_init(void); +#else +static inline void __init mpc85xx_cpm2_pic_init(void) {} +#endif /* CONFIG_CPM2 */ + +#endif diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ads.c b/arch/powerpc/platforms/85xx/mpc85xx_ads.c new file mode 100644 index 00000000000..3bebb5173bf --- /dev/null +++ b/arch/powerpc/platforms/85xx/mpc85xx_ads.c @@ -0,0 +1,200 @@ +/* + * MPC85xx setup and early boot code plus other random bits. + * + * Maintained by Kumar Gala (see MAINTAINERS for contact information) + * + * Copyright 2005 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/delay.h> +#include <linux/seq_file.h> +#include <linux/of_platform.h> + +#include <asm/system.h> +#include <asm/time.h> +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <asm/mpic.h> +#include <mm/mmu_decl.h> +#include <asm/udbg.h> + +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#ifdef CONFIG_CPM2 +#include <asm/cpm2.h> +#include <sysdev/cpm2_pic.h> +#endif + +#include "mpc85xx.h" + +#ifdef CONFIG_PCI +static int mpc85xx_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn) +{ + if (bus == 0 && PCI_SLOT(devfn) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + else + return PCIBIOS_SUCCESSFUL; +} +#endif /* CONFIG_PCI */ + +static void __init mpc85xx_ads_pic_init(void) +{ + struct mpic *mpic = mpic_alloc(NULL, 0, + MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, + 0, 256, " OpenPIC "); + BUG_ON(mpic == NULL); + mpic_init(mpic); + + mpc85xx_cpm2_pic_init(); +} + +/* + * Setup the architecture + */ +#ifdef CONFIG_CPM2 +struct cpm_pin { + int port, pin, flags; +}; + +static const struct cpm_pin mpc8560_ads_pins[] = { + /* SCC1 */ + {3, 29, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {3, 30, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {3, 31, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* SCC2 */ + {2, 12, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 13, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {3, 26, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {3, 27, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {3, 28, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + + /* FCC2 */ + {1, 18, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 19, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 20, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 21, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 22, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 23, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 24, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 25, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 26, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 27, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 28, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 29, CPM_PIN_OUTPUT | CPM_PIN_SECONDARY}, + {1, 30, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 31, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {2, 18, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* CLK14 */ + {2, 19, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* CLK13 */ + + /* FCC3 */ + {1, 4, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 5, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 6, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 8, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 9, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 10, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 11, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 12, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 13, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 14, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 15, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, + {1, 16, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {1, 17, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, + {2, 16, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* CLK16 */ + {2, 17, CPM_PIN_INPUT | CPM_PIN_PRIMARY}, /* CLK15 */ + {2, 27, CPM_PIN_OUTPUT | CPM_PIN_PRIMARY}, +}; + +static void __init init_ioports(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mpc8560_ads_pins); i++) { + const struct cpm_pin *pin = &mpc8560_ads_pins[i]; + cpm2_set_pin(pin->port, pin->pin, pin->flags); + } + + cpm2_clk_setup(CPM_CLK_SCC1, CPM_BRG1, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_SCC1, CPM_BRG1, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_SCC2, CPM_BRG2, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_SCC2, CPM_BRG2, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_FCC2, CPM_CLK13, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_FCC2, CPM_CLK14, CPM_CLK_TX); + cpm2_clk_setup(CPM_CLK_FCC3, CPM_CLK15, CPM_CLK_RX); + cpm2_clk_setup(CPM_CLK_FCC3, CPM_CLK16, CPM_CLK_TX); +} +#endif + +static void __init mpc85xx_ads_setup_arch(void) +{ +#ifdef CONFIG_PCI + struct device_node *np; +#endif + + if (ppc_md.progress) + ppc_md.progress("mpc85xx_ads_setup_arch()", 0); + +#ifdef CONFIG_CPM2 + cpm2_reset(); + init_ioports(); +#endif + +#ifdef CONFIG_PCI + for_each_compatible_node(np, "pci", "fsl,mpc8540-pci") + fsl_add_bridge(np, 1); + + ppc_md.pci_exclude_device = mpc85xx_exclude_device; +#endif +} + +static void mpc85xx_ads_show_cpuinfo(struct seq_file *m) +{ + uint pvid, svid, phid1; + + pvid = mfspr(SPRN_PVR); + svid = mfspr(SPRN_SVR); + + seq_printf(m, "Vendor\t\t: Freescale Semiconductor\n"); + seq_printf(m, "PVR\t\t: 0x%x\n", pvid); + seq_printf(m, "SVR\t\t: 0x%x\n", svid); + + /* Display cpu Pll setting */ + phid1 = mfspr(SPRN_HID1); + seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f)); +} + +machine_device_initcall(mpc85xx_ads, mpc85xx_common_publish_devices); + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init mpc85xx_ads_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "MPC85xxADS"); +} + +define_machine(mpc85xx_ads) { + .name = "MPC85xx ADS", + .probe = mpc85xx_ads_probe, + .setup_arch = mpc85xx_ads_setup_arch, + .init_IRQ = mpc85xx_ads_pic_init, + .show_cpuinfo = mpc85xx_ads_show_cpuinfo, + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/85xx/mpc85xx_cds.c b/arch/powerpc/platforms/85xx/mpc85xx_cds.c new file mode 100644 index 00000000000..40f03da616a --- /dev/null +++ b/arch/powerpc/platforms/85xx/mpc85xx_cds.c @@ -0,0 +1,332 @@ +/* + * MPC85xx setup and early boot code plus other random bits. + * + * Maintained by Kumar Gala (see MAINTAINERS for contact information) + * + * Copyright 2005 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/reboot.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/major.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/seq_file.h> +#include <linux/initrd.h> +#include <linux/interrupt.h> +#include <linux/fsl_devices.h> +#include <linux/of_platform.h> + +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/page.h> +#include <linux/atomic.h> +#include <asm/time.h> +#include <asm/io.h> +#include <asm/machdep.h> +#include <asm/ipic.h> +#include <asm/pci-bridge.h> +#include <asm/irq.h> +#include <mm/mmu_decl.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/mpic.h> +#include <asm/i8259.h> + +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#include "mpc85xx.h" + +/* CADMUS info */ +/* xxx - galak, move into device tree */ +#define CADMUS_BASE (0xf8004000) +#define CADMUS_SIZE (256) +#define CM_VER (0) +#define CM_CSR (1) +#define CM_RST (2) + + +static int cds_pci_slot = 2; +static volatile u8 *cadmus; + +#ifdef CONFIG_PCI + +#define ARCADIA_HOST_BRIDGE_IDSEL 17 +#define ARCADIA_2ND_BRIDGE_IDSEL 3 + +static int mpc85xx_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn) +{ + /* We explicitly do not go past the Tundra 320 Bridge */ + if ((bus == 1) && (PCI_SLOT(devfn) == ARCADIA_2ND_BRIDGE_IDSEL)) + return PCIBIOS_DEVICE_NOT_FOUND; + if ((bus == 0) && (PCI_SLOT(devfn) == ARCADIA_2ND_BRIDGE_IDSEL)) + return PCIBIOS_DEVICE_NOT_FOUND; + else + return PCIBIOS_SUCCESSFUL; +} + +static void mpc85xx_cds_restart(char *cmd) +{ + struct pci_dev *dev; + u_char tmp; + + if ((dev = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, + NULL))) { + + /* Use the VIA Super Southbridge to force a PCI reset */ + pci_read_config_byte(dev, 0x47, &tmp); + pci_write_config_byte(dev, 0x47, tmp | 1); + + /* Flush the outbound PCI write queues */ + pci_read_config_byte(dev, 0x47, &tmp); + + /* + * At this point, the harware reset should have triggered. + * However, if it doesn't work for some mysterious reason, + * just fall through to the default reset below. + */ + + pci_dev_put(dev); + } + + /* + * If we can't find the VIA chip (maybe the P2P bridge is disabled) + * or the VIA chip reset didn't work, just use the default reset. + */ + fsl_rstcr_restart(NULL); +} + +static void __init mpc85xx_cds_pci_irq_fixup(struct pci_dev *dev) +{ + u_char c; + if (dev->vendor == PCI_VENDOR_ID_VIA) { + switch (dev->device) { + case PCI_DEVICE_ID_VIA_82C586_1: + /* + * U-Boot does not set the enable bits + * for the IDE device. Force them on here. + */ + pci_read_config_byte(dev, 0x40, &c); + c |= 0x03; /* IDE: Chip Enable Bits */ + pci_write_config_byte(dev, 0x40, c); + + /* + * Since only primary interface works, force the + * IDE function to standard primary IDE interrupt + * w/ 8259 offset + */ + dev->irq = 14; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + break; + /* + * Force legacy USB interrupt routing + */ + case PCI_DEVICE_ID_VIA_82C586_2: + /* There are two USB controllers. + * Identify them by functon number + */ + if (PCI_FUNC(dev->devfn) == 3) + dev->irq = 11; + else + dev->irq = 10; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + default: + break; + } + } +} + +static void __devinit skip_fake_bridge(struct pci_dev *dev) +{ + /* Make it an error to skip the fake bridge + * in pci_setup_device() in probe.c */ + dev->hdr_type = 0x7f; +} +DECLARE_PCI_FIXUP_EARLY(0x1957, 0x3fff, skip_fake_bridge); +DECLARE_PCI_FIXUP_EARLY(0x3fff, 0x1957, skip_fake_bridge); +DECLARE_PCI_FIXUP_EARLY(0xff3f, 0x5719, skip_fake_bridge); + +#ifdef CONFIG_PPC_I8259 +static void mpc85xx_8259_cascade_handler(unsigned int irq, + struct irq_desc *desc) +{ + unsigned int cascade_irq = i8259_irq(); + + if (cascade_irq != NO_IRQ) + /* handle an interrupt from the 8259 */ + generic_handle_irq(cascade_irq); + + /* check for any interrupts from the shared IRQ line */ + handle_fasteoi_irq(irq, desc); +} + +static irqreturn_t mpc85xx_8259_cascade_action(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + +static struct irqaction mpc85xxcds_8259_irqaction = { + .handler = mpc85xx_8259_cascade_action, + .flags = IRQF_SHARED | IRQF_NO_THREAD, + .name = "8259 cascade", +}; +#endif /* PPC_I8259 */ +#endif /* CONFIG_PCI */ + +static void __init mpc85xx_cds_pic_init(void) +{ + struct mpic *mpic; + mpic = mpic_alloc(NULL, 0, + MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, + 0, 256, " OpenPIC "); + BUG_ON(mpic == NULL); + mpic_init(mpic); +} + +#if defined(CONFIG_PPC_I8259) && defined(CONFIG_PCI) +static int mpc85xx_cds_8259_attach(void) +{ + int ret; + struct device_node *np = NULL; + struct device_node *cascade_node = NULL; + int cascade_irq; + + /* Initialize the i8259 controller */ + for_each_node_by_type(np, "interrupt-controller") + if (of_device_is_compatible(np, "chrp,iic")) { + cascade_node = np; + break; + } + + if (cascade_node == NULL) { + printk(KERN_DEBUG "Could not find i8259 PIC\n"); + return -ENODEV; + } + + cascade_irq = irq_of_parse_and_map(cascade_node, 0); + if (cascade_irq == NO_IRQ) { + printk(KERN_ERR "Failed to map cascade interrupt\n"); + return -ENXIO; + } + + i8259_init(cascade_node, 0); + of_node_put(cascade_node); + + /* + * Hook the interrupt to make sure desc->action is never NULL. + * This is required to ensure that the interrupt does not get + * disabled when the last user of the shared IRQ line frees their + * interrupt. + */ + if ((ret = setup_irq(cascade_irq, &mpc85xxcds_8259_irqaction))) { + printk(KERN_ERR "Failed to setup cascade interrupt\n"); + return ret; + } + + /* Success. Connect our low-level cascade handler. */ + irq_set_handler(cascade_irq, mpc85xx_8259_cascade_handler); + + return 0; +} +machine_device_initcall(mpc85xx_cds, mpc85xx_cds_8259_attach); + +#endif /* CONFIG_PPC_I8259 */ + +/* + * Setup the architecture + */ +static void __init mpc85xx_cds_setup_arch(void) +{ +#ifdef CONFIG_PCI + struct device_node *np; +#endif + + if (ppc_md.progress) + ppc_md.progress("mpc85xx_cds_setup_arch()", 0); + + cadmus = ioremap(CADMUS_BASE, CADMUS_SIZE); + cds_pci_slot = ((cadmus[CM_CSR] >> 6) & 0x3) + 1; + + if (ppc_md.progress) { + char buf[40]; + snprintf(buf, 40, "CDS Version = 0x%x in slot %d\n", + cadmus[CM_VER], cds_pci_slot); + ppc_md.progress(buf, 0); + } + +#ifdef CONFIG_PCI + for_each_node_by_type(np, "pci") { + if (of_device_is_compatible(np, "fsl,mpc8540-pci") || + of_device_is_compatible(np, "fsl,mpc8548-pcie")) { + struct resource rsrc; + of_address_to_resource(np, 0, &rsrc); + if ((rsrc.start & 0xfffff) == 0x8000) + fsl_add_bridge(np, 1); + else + fsl_add_bridge(np, 0); + } + } + + ppc_md.pci_irq_fixup = mpc85xx_cds_pci_irq_fixup; + ppc_md.pci_exclude_device = mpc85xx_exclude_device; +#endif +} + +static void mpc85xx_cds_show_cpuinfo(struct seq_file *m) +{ + uint pvid, svid, phid1; + + pvid = mfspr(SPRN_PVR); + svid = mfspr(SPRN_SVR); + + seq_printf(m, "Vendor\t\t: Freescale Semiconductor\n"); + seq_printf(m, "Machine\t\t: MPC85xx CDS (0x%x)\n", cadmus[CM_VER]); + seq_printf(m, "PVR\t\t: 0x%x\n", pvid); + seq_printf(m, "SVR\t\t: 0x%x\n", svid); + + /* Display cpu Pll setting */ + phid1 = mfspr(SPRN_HID1); + seq_printf(m, "PLL setting\t: 0x%x\n", ((phid1 >> 24) & 0x3f)); +} + + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init mpc85xx_cds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "MPC85xxCDS"); +} + +machine_device_initcall(mpc85xx_cds, mpc85xx_common_publish_devices); + +define_machine(mpc85xx_cds) { + .name = "MPC85xx CDS", + .probe = mpc85xx_cds_probe, + .setup_arch = mpc85xx_cds_setup_arch, + .init_IRQ = mpc85xx_cds_pic_init, + .show_cpuinfo = mpc85xx_cds_show_cpuinfo, + .get_irq = mpic_get_irq, +#ifdef CONFIG_PCI + .restart = mpc85xx_cds_restart, + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#else + .restart = fsl_rstcr_restart, +#endif + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ds.c b/arch/powerpc/platforms/85xx/mpc85xx_ds.c new file mode 100644 index 00000000000..eefbb91e1d6 --- /dev/null +++ b/arch/powerpc/platforms/85xx/mpc85xx_ds.c @@ -0,0 +1,286 @@ +/* + * MPC85xx DS Board Setup + * + * Author Xianghua Xiao (x.xiao@freescale.com) + * Roy Zang <tie-fei.zang@freescale.com> + * - Add PCI/PCI Exprees support + * Copyright 2007 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/delay.h> +#include <linux/seq_file.h> +#include <linux/interrupt.h> +#include <linux/of_platform.h> +#include <linux/memblock.h> + +#include <asm/system.h> +#include <asm/time.h> +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <mm/mmu_decl.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/mpic.h> +#include <asm/i8259.h> +#include <asm/swiotlb.h> + +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> +#include "smp.h" + +#include "mpc85xx.h" + +#undef DEBUG + +#ifdef DEBUG +#define DBG(fmt, args...) printk(KERN_ERR "%s: " fmt, __func__, ## args) +#else +#define DBG(fmt, args...) +#endif + +#ifdef CONFIG_PPC_I8259 +static void mpc85xx_8259_cascade(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned int cascade_irq = i8259_irq(); + + if (cascade_irq != NO_IRQ) { + generic_handle_irq(cascade_irq); + } + chip->irq_eoi(&desc->irq_data); +} +#endif /* CONFIG_PPC_I8259 */ + +void __init mpc85xx_ds_pic_init(void) +{ + struct mpic *mpic; +#ifdef CONFIG_PPC_I8259 + struct device_node *np; + struct device_node *cascade_node = NULL; + int cascade_irq; +#endif + unsigned long root = of_get_flat_dt_root(); + + if (of_flat_dt_is_compatible(root, "fsl,MPC8572DS-CAMP")) { + mpic = mpic_alloc(NULL, 0, + MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS | + MPIC_SINGLE_DEST_CPU, + 0, 256, " OpenPIC "); + } else { + mpic = mpic_alloc(NULL, 0, + MPIC_WANTS_RESET | + MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS | + MPIC_SINGLE_DEST_CPU, + 0, 256, " OpenPIC "); + } + + BUG_ON(mpic == NULL); + mpic_init(mpic); + +#ifdef CONFIG_PPC_I8259 + /* Initialize the i8259 controller */ + for_each_node_by_type(np, "interrupt-controller") + if (of_device_is_compatible(np, "chrp,iic")) { + cascade_node = np; + break; + } + + if (cascade_node == NULL) { + printk(KERN_DEBUG "Could not find i8259 PIC\n"); + return; + } + + cascade_irq = irq_of_parse_and_map(cascade_node, 0); + if (cascade_irq == NO_IRQ) { + printk(KERN_ERR "Failed to map cascade interrupt\n"); + return; + } + + DBG("mpc85xxds: cascade mapped to irq %d\n", cascade_irq); + + i8259_init(cascade_node, 0); + of_node_put(cascade_node); + + irq_set_chained_handler(cascade_irq, mpc85xx_8259_cascade); +#endif /* CONFIG_PPC_I8259 */ +} + +#ifdef CONFIG_PCI +static int primary_phb_addr; +extern int uli_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn); + +static int mpc85xx_exclude_device(struct pci_controller *hose, + u_char bus, u_char devfn) +{ + struct device_node* node; + struct resource rsrc; + + node = hose->dn; + of_address_to_resource(node, 0, &rsrc); + + if ((rsrc.start & 0xfffff) == primary_phb_addr) { + return uli_exclude_device(hose, bus, devfn); + } + + return PCIBIOS_SUCCESSFUL; +} +#endif /* CONFIG_PCI */ + +/* + * Setup the architecture + */ +static void __init mpc85xx_ds_setup_arch(void) +{ +#ifdef CONFIG_PCI + struct device_node *np; + struct pci_controller *hose; +#endif + dma_addr_t max = 0xffffffff; + + if (ppc_md.progress) + ppc_md.progress("mpc85xx_ds_setup_arch()", 0); + +#ifdef CONFIG_PCI + for_each_node_by_type(np, "pci") { + if (of_device_is_compatible(np, "fsl,mpc8540-pci") || + of_device_is_compatible(np, "fsl,mpc8548-pcie") || + of_device_is_compatible(np, "fsl,p2020-pcie")) { + struct resource rsrc; + of_address_to_resource(np, 0, &rsrc); + if ((rsrc.start & 0xfffff) == primary_phb_addr) + fsl_add_bridge(np, 1); + else + fsl_add_bridge(np, 0); + + hose = pci_find_hose_for_OF_device(np); + max = min(max, hose->dma_window_base_cur + + hose->dma_window_size); + } + } + + ppc_md.pci_exclude_device = mpc85xx_exclude_device; +#endif + + mpc85xx_smp_init(); + +#ifdef CONFIG_SWIOTLB + if (memblock_end_of_DRAM() > max) { + ppc_swiotlb_enable = 1; + set_pci_dma_ops(&swiotlb_dma_ops); + ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_swiotlb; + } +#endif + + printk("MPC85xx DS board from Freescale Semiconductor\n"); +} + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init mpc8544_ds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (of_flat_dt_is_compatible(root, "MPC8544DS")) { +#ifdef CONFIG_PCI + primary_phb_addr = 0xb000; +#endif + return 1; + } + + return 0; +} + +machine_device_initcall(mpc8544_ds, mpc85xx_common_publish_devices); +machine_device_initcall(mpc8572_ds, mpc85xx_common_publish_devices); +machine_device_initcall(p2020_ds, mpc85xx_common_publish_devices); + +machine_arch_initcall(mpc8544_ds, swiotlb_setup_bus_notifier); +machine_arch_initcall(mpc8572_ds, swiotlb_setup_bus_notifier); +machine_arch_initcall(p2020_ds, swiotlb_setup_bus_notifier); + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init mpc8572_ds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (of_flat_dt_is_compatible(root, "fsl,MPC8572DS")) { +#ifdef CONFIG_PCI + primary_phb_addr = 0x8000; +#endif + return 1; + } + + return 0; +} + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init p2020_ds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (of_flat_dt_is_compatible(root, "fsl,P2020DS")) { +#ifdef CONFIG_PCI + primary_phb_addr = 0x9000; +#endif + return 1; + } + + return 0; +} + +define_machine(mpc8544_ds) { + .name = "MPC8544 DS", + .probe = mpc8544_ds_probe, + .setup_arch = mpc85xx_ds_setup_arch, + .init_IRQ = mpc85xx_ds_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; + +define_machine(mpc8572_ds) { + .name = "MPC8572 DS", + .probe = mpc8572_ds_probe, + .setup_arch = mpc85xx_ds_setup_arch, + .init_IRQ = mpc85xx_ds_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; + +define_machine(p2020_ds) { + .name = "P2020 DS", + .probe = p2020_ds_probe, + .setup_arch = mpc85xx_ds_setup_arch, + .init_IRQ = mpc85xx_ds_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c new file mode 100644 index 00000000000..1d15a0cd2c8 --- /dev/null +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -0,0 +1,510 @@ +/* + * Copyright (C) Freescale Semicondutor, Inc. 2006-2010. All rights reserved. + * + * Author: Andy Fleming <afleming@freescale.com> + * + * Based on 83xx/mpc8360e_pb.c by: + * Li Yang <LeoLi@freescale.com> + * Yin Olivia <Hong-hua.Yin@freescale.com> + * + * Description: + * MPC85xx MDS board specific routines. + * + * 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/reboot.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/major.h> +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/seq_file.h> +#include <linux/initrd.h> +#include <linux/fsl_devices.h> +#include <linux/of_platform.h> +#include <linux/of_device.h> +#include <linux/phy.h> +#include <linux/memblock.h> + +#include <asm/system.h> +#include <linux/atomic.h> +#include <asm/time.h> +#include <asm/io.h> +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <asm/irq.h> +#include <mm/mmu_decl.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> +#include <sysdev/simple_gpio.h> +#include <asm/qe.h> +#include <asm/qe_ic.h> +#include <asm/mpic.h> +#include <asm/swiotlb.h> +#include "smp.h" + +#include "mpc85xx.h" + +#undef DEBUG +#ifdef DEBUG +#define DBG(fmt...) udbg_printf(fmt) +#else +#define DBG(fmt...) +#endif + +#define MV88E1111_SCR 0x10 +#define MV88E1111_SCR_125CLK 0x0010 +static int mpc8568_fixup_125_clock(struct phy_device *phydev) +{ + int scr; + int err; + + /* Workaround for the 125 CLK Toggle */ + scr = phy_read(phydev, MV88E1111_SCR); + + if (scr < 0) + return scr; + + err = phy_write(phydev, MV88E1111_SCR, scr & ~(MV88E1111_SCR_125CLK)); + + if (err) + return err; + + err = phy_write(phydev, MII_BMCR, BMCR_RESET); + + if (err) + return err; + + scr = phy_read(phydev, MV88E1111_SCR); + + if (scr < 0) + return scr; + + err = phy_write(phydev, MV88E1111_SCR, scr | 0x0008); + + return err; +} + +static int mpc8568_mds_phy_fixups(struct phy_device *phydev) +{ + int temp; + int err; + + /* Errata */ + err = phy_write(phydev,29, 0x0006); + + if (err) + return err; + + temp = phy_read(phydev, 30); + + if (temp < 0) + return temp; + + temp = (temp & (~0x8000)) | 0x4000; + err = phy_write(phydev,30, temp); + + if (err) + return err; + + err = phy_write(phydev,29, 0x000a); + + if (err) + return err; + + temp = phy_read(phydev, 30); + + if (temp < 0) + return temp; + + temp = phy_read(phydev, 30); + + if (temp < 0) + return temp; + + temp &= ~0x0020; + + err = phy_write(phydev,30,temp); + + if (err) + return err; + + /* Disable automatic MDI/MDIX selection */ + temp = phy_read(phydev, 16); + + if (temp < 0) + return temp; + + temp &= ~0x0060; + err = phy_write(phydev,16,temp); + + return err; +} + +/* ************************************************************************ + * + * Setup the architecture + * + */ +#ifdef CONFIG_QUICC_ENGINE +static void __init mpc85xx_mds_reset_ucc_phys(void) +{ + struct device_node *np; + static u8 __iomem *bcsr_regs; + + /* Map BCSR area */ + np = of_find_node_by_name(NULL, "bcsr"); + if (!np) + return; + + bcsr_regs = of_iomap(np, 0); + of_node_put(np); + if (!bcsr_regs) + return; + + if (machine_is(mpc8568_mds)) { +#define BCSR_UCC1_GETH_EN (0x1 << 7) +#define BCSR_UCC2_GETH_EN (0x1 << 7) +#define BCSR_UCC1_MODE_MSK (0x3 << 4) +#define BCSR_UCC2_MODE_MSK (0x3 << 0) + + /* Turn off UCC1 & UCC2 */ + clrbits8(&bcsr_regs[8], BCSR_UCC1_GETH_EN); + clrbits8(&bcsr_regs[9], BCSR_UCC2_GETH_EN); + + /* Mode is RGMII, all bits clear */ + clrbits8(&bcsr_regs[11], BCSR_UCC1_MODE_MSK | + BCSR_UCC2_MODE_MSK); + + /* Turn UCC1 & UCC2 on */ + setbits8(&bcsr_regs[8], BCSR_UCC1_GETH_EN); + setbits8(&bcsr_regs[9], BCSR_UCC2_GETH_EN); + } else if (machine_is(mpc8569_mds)) { +#define BCSR7_UCC12_GETHnRST (0x1 << 2) +#define BCSR8_UEM_MARVELL_RST (0x1 << 1) +#define BCSR_UCC_RGMII (0x1 << 6) +#define BCSR_UCC_RTBI (0x1 << 5) + /* + * U-Boot mangles interrupt polarity for Marvell PHYs, + * so reset built-in and UEM Marvell PHYs, this puts + * the PHYs into their normal state. + */ + clrbits8(&bcsr_regs[7], BCSR7_UCC12_GETHnRST); + setbits8(&bcsr_regs[8], BCSR8_UEM_MARVELL_RST); + + setbits8(&bcsr_regs[7], BCSR7_UCC12_GETHnRST); + clrbits8(&bcsr_regs[8], BCSR8_UEM_MARVELL_RST); + + for (np = NULL; (np = of_find_compatible_node(np, + "network", + "ucc_geth")) != NULL;) { + const unsigned int *prop; + int ucc_num; + + prop = of_get_property(np, "cell-index", NULL); + if (prop == NULL) + continue; + + ucc_num = *prop - 1; + + prop = of_get_property(np, "phy-connection-type", NULL); + if (prop == NULL) + continue; + + if (strcmp("rtbi", (const char *)prop) == 0) + clrsetbits_8(&bcsr_regs[7 + ucc_num], + BCSR_UCC_RGMII, BCSR_UCC_RTBI); + } + } else if (machine_is(p1021_mds)) { +#define BCSR11_ENET_MICRST (0x1 << 5) + /* Reset Micrel PHY */ + clrbits8(&bcsr_regs[11], BCSR11_ENET_MICRST); + setbits8(&bcsr_regs[11], BCSR11_ENET_MICRST); + } + + iounmap(bcsr_regs); +} + +static void __init mpc85xx_mds_qe_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!np) { + np = of_find_node_by_name(NULL, "qe"); + if (!np) + return; + } + + if (!of_device_is_available(np)) { + of_node_put(np); + return; + } + + qe_reset(); + of_node_put(np); + + np = of_find_node_by_name(NULL, "par_io"); + if (np) { + struct device_node *ucc; + + par_io_init(np); + of_node_put(np); + + for_each_node_by_name(ucc, "ucc") + par_io_of_config(ucc); + } + + mpc85xx_mds_reset_ucc_phys(); + + if (machine_is(p1021_mds)) { +#define MPC85xx_PMUXCR_OFFSET 0x60 +#define MPC85xx_PMUXCR_QE0 0x00008000 +#define MPC85xx_PMUXCR_QE3 0x00001000 +#define MPC85xx_PMUXCR_QE9 0x00000040 +#define MPC85xx_PMUXCR_QE12 0x00000008 + static __be32 __iomem *pmuxcr; + + np = of_find_node_by_name(NULL, "global-utilities"); + + if (np) { + pmuxcr = of_iomap(np, 0) + MPC85xx_PMUXCR_OFFSET; + + if (!pmuxcr) + printk(KERN_EMERG "Error: Alternate function" + " signal multiplex control register not" + " mapped!\n"); + else + /* P1021 has pins muxed for QE and other functions. To + * enable QE UEC mode, we need to set bit QE0 for UCC1 + * in Eth mode, QE0 and QE3 for UCC5 in Eth mode, QE9 + * and QE12 for QE MII management signals in PMUXCR + * register. + */ + setbits32(pmuxcr, MPC85xx_PMUXCR_QE0 | + MPC85xx_PMUXCR_QE3 | + MPC85xx_PMUXCR_QE9 | + MPC85xx_PMUXCR_QE12); + + of_node_put(np); + } + + } +} + +static void __init mpc85xx_mds_qeic_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,qe"); + if (!of_device_is_available(np)) { + of_node_put(np); + return; + } + + np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic"); + if (!np) { + np = of_find_node_by_type(NULL, "qeic"); + if (!np) + return; + } + + if (machine_is(p1021_mds)) + qe_ic_init(np, 0, qe_ic_cascade_low_mpic, + qe_ic_cascade_high_mpic); + else + qe_ic_init(np, 0, qe_ic_cascade_muxed_mpic, NULL); + of_node_put(np); +} +#else +static void __init mpc85xx_mds_qe_init(void) { } +static void __init mpc85xx_mds_qeic_init(void) { } +#endif /* CONFIG_QUICC_ENGINE */ + +static void __init mpc85xx_mds_setup_arch(void) +{ +#ifdef CONFIG_PCI + struct pci_controller *hose; + struct device_node *np; +#endif + dma_addr_t max = 0xffffffff; + + if (ppc_md.progress) + ppc_md.progress("mpc85xx_mds_setup_arch()", 0); + +#ifdef CONFIG_PCI + for_each_node_by_type(np, "pci") { + if (of_device_is_compatible(np, "fsl,mpc8540-pci") || + of_device_is_compatible(np, "fsl,mpc8548-pcie")) { + struct resource rsrc; + of_address_to_resource(np, 0, &rsrc); + if ((rsrc.start & 0xfffff) == 0x8000) + fsl_add_bridge(np, 1); + else + fsl_add_bridge(np, 0); + + hose = pci_find_hose_for_OF_device(np); + max = min(max, hose->dma_window_base_cur + + hose->dma_window_size); + } + } +#endif + + mpc85xx_smp_init(); + + mpc85xx_mds_qe_init(); + +#ifdef CONFIG_SWIOTLB + if (memblock_end_of_DRAM() > max) { + ppc_swiotlb_enable = 1; + set_pci_dma_ops(&swiotlb_dma_ops); + ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_swiotlb; + } +#endif +} + + +static int __init board_fixups(void) +{ + char phy_id[20]; + char *compstrs[2] = {"fsl,gianfar-mdio", "fsl,ucc-mdio"}; + struct device_node *mdio; + struct resource res; + int i; + + for (i = 0; i < ARRAY_SIZE(compstrs); i++) { + mdio = of_find_compatible_node(NULL, NULL, compstrs[i]); + + of_address_to_resource(mdio, 0, &res); + snprintf(phy_id, sizeof(phy_id), "%llx:%02x", + (unsigned long long)res.start, 1); + + phy_register_fixup_for_id(phy_id, mpc8568_fixup_125_clock); + phy_register_fixup_for_id(phy_id, mpc8568_mds_phy_fixups); + + /* Register a workaround for errata */ + snprintf(phy_id, sizeof(phy_id), "%llx:%02x", + (unsigned long long)res.start, 7); + phy_register_fixup_for_id(phy_id, mpc8568_mds_phy_fixups); + + of_node_put(mdio); + } + + return 0; +} +machine_arch_initcall(mpc8568_mds, board_fixups); +machine_arch_initcall(mpc8569_mds, board_fixups); + +static struct of_device_id mpc85xx_ids[] = { + { .compatible = "fsl,mpc8548-guts", }, + { .compatible = "gpio-leds", }, + {}, +}; + +static int __init mpc85xx_publish_devices(void) +{ + if (machine_is(mpc8568_mds)) + simple_gpiochip_init("fsl,mpc8568mds-bcsr-gpio"); + if (machine_is(mpc8569_mds)) + simple_gpiochip_init("fsl,mpc8569mds-bcsr-gpio"); + + mpc85xx_common_publish_devices(); + of_platform_bus_probe(NULL, mpc85xx_ids, NULL); + + return 0; +} + +machine_device_initcall(mpc8568_mds, mpc85xx_publish_devices); +machine_device_initcall(mpc8569_mds, mpc85xx_publish_devices); +machine_device_initcall(p1021_mds, mpc85xx_common_publish_devices); + +machine_arch_initcall(mpc8568_mds, swiotlb_setup_bus_notifier); +machine_arch_initcall(mpc8569_mds, swiotlb_setup_bus_notifier); +machine_arch_initcall(p1021_mds, swiotlb_setup_bus_notifier); + +static void __init mpc85xx_mds_pic_init(void) +{ + struct mpic *mpic = mpic_alloc(NULL, 0, + MPIC_WANTS_RESET | MPIC_BIG_ENDIAN | + MPIC_BROKEN_FRR_NIRQS | MPIC_SINGLE_DEST_CPU, + 0, 256, " OpenPIC "); + BUG_ON(mpic == NULL); + + mpic_init(mpic); + mpc85xx_mds_qeic_init(); +} + +static int __init mpc85xx_mds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "MPC85xxMDS"); +} + +define_machine(mpc8568_mds) { + .name = "MPC8568 MDS", + .probe = mpc85xx_mds_probe, + .setup_arch = mpc85xx_mds_setup_arch, + .init_IRQ = mpc85xx_mds_pic_init, + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif +}; + +static int __init mpc8569_mds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,MPC8569EMDS"); +} + +define_machine(mpc8569_mds) { + .name = "MPC8569 MDS", + .probe = mpc8569_mds_probe, + .setup_arch = mpc85xx_mds_setup_arch, + .init_IRQ = mpc85xx_mds_pic_init, + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif +}; + +static int __init p1021_mds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,P1021MDS"); + +} + +define_machine(p1021_mds) { + .name = "P1021 MDS", + .probe = p1021_mds_probe, + .setup_arch = mpc85xx_mds_setup_arch, + .init_IRQ = mpc85xx_mds_pic_init, + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif +}; + diff --git a/arch/powerpc/platforms/85xx/mpc85xx_rdb.c b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c new file mode 100644 index 00000000000..ccf520e890b --- /dev/null +++ b/arch/powerpc/platforms/85xx/mpc85xx_rdb.c @@ -0,0 +1,141 @@ +/* + * MPC85xx RDB Board Setup + * + * Copyright 2009 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/delay.h> +#include <linux/seq_file.h> +#include <linux/interrupt.h> +#include <linux/of_platform.h> + +#include <asm/system.h> +#include <asm/time.h> +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <mm/mmu_decl.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/mpic.h> + +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> +#include "smp.h" + +#include "mpc85xx.h" + +#undef DEBUG + +#ifdef DEBUG +#define DBG(fmt, args...) printk(KERN_ERR "%s: " fmt, __func__, ## args) +#else +#define DBG(fmt, args...) +#endif + + +void __init mpc85xx_rdb_pic_init(void) +{ + struct mpic *mpic; + unsigned long root = of_get_flat_dt_root(); + + if (of_flat_dt_is_compatible(root, "fsl,MPC85XXRDB-CAMP")) { + mpic = mpic_alloc(NULL, 0, + MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS | + MPIC_SINGLE_DEST_CPU, + 0, 256, " OpenPIC "); + } else { + mpic = mpic_alloc(NULL, 0, + MPIC_WANTS_RESET | + MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS | + MPIC_SINGLE_DEST_CPU, + 0, 256, " OpenPIC "); + } + + BUG_ON(mpic == NULL); + mpic_init(mpic); +} + +/* + * Setup the architecture + */ +static void __init mpc85xx_rdb_setup_arch(void) +{ +#ifdef CONFIG_PCI + struct device_node *np; +#endif + + if (ppc_md.progress) + ppc_md.progress("mpc85xx_rdb_setup_arch()", 0); + +#ifdef CONFIG_PCI + for_each_node_by_type(np, "pci") { + if (of_device_is_compatible(np, "fsl,mpc8548-pcie")) + fsl_add_bridge(np, 0); + } + +#endif + + mpc85xx_smp_init(); + printk(KERN_INFO "MPC85xx RDB board from Freescale Semiconductor\n"); +} + +machine_device_initcall(p2020_rdb, mpc85xx_common_publish_devices); +machine_device_initcall(p1020_rdb, mpc85xx_common_publish_devices); + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init p2020_rdb_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (of_flat_dt_is_compatible(root, "fsl,P2020RDB")) + return 1; + return 0; +} + +static int __init p1020_rdb_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (of_flat_dt_is_compatible(root, "fsl,P1020RDB")) + return 1; + return 0; +} + +define_machine(p2020_rdb) { + .name = "P2020 RDB", + .probe = p2020_rdb_probe, + .setup_arch = mpc85xx_rdb_setup_arch, + .init_IRQ = mpc85xx_rdb_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; + +define_machine(p1020_rdb) { + .name = "P1020 RDB", + .probe = p1020_rdb_probe, + .setup_arch = mpc85xx_rdb_setup_arch, + .init_IRQ = mpc85xx_rdb_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/85xx/p1010rdb.c b/arch/powerpc/platforms/85xx/p1010rdb.c new file mode 100644 index 00000000000..538bc3f57e9 --- /dev/null +++ b/arch/powerpc/platforms/85xx/p1010rdb.c @@ -0,0 +1,96 @@ +/* + * P1010RDB Board Setup + * + * Copyright 2011 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 <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/of_platform.h> + +#include <asm/system.h> +#include <asm/time.h> +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <mm/mmu_decl.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/mpic.h> + +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#include "mpc85xx.h" + +void __init p1010_rdb_pic_init(void) +{ + struct mpic *mpic = mpic_alloc(NULL, 0, + MPIC_WANTS_RESET | MPIC_BIG_ENDIAN | + MPIC_BROKEN_FRR_NIRQS | MPIC_SINGLE_DEST_CPU, + 0, 256, " OpenPIC "); + + BUG_ON(mpic == NULL); + + mpic_init(mpic); +} + + +/* + * Setup the architecture + */ +static void __init p1010_rdb_setup_arch(void) +{ +#ifdef CONFIG_PCI + struct device_node *np; +#endif + + if (ppc_md.progress) + ppc_md.progress("p1010_rdb_setup_arch()", 0); + +#ifdef CONFIG_PCI + for_each_node_by_type(np, "pci") { + if (of_device_is_compatible(np, "fsl,p1010-pcie")) + fsl_add_bridge(np, 0); + } + +#endif + + printk(KERN_INFO "P1010 RDB board from Freescale Semiconductor\n"); +} + +machine_device_initcall(p1010_rdb, mpc85xx_common_publish_devices); +machine_arch_initcall(p1010_rdb, swiotlb_setup_bus_notifier); + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init p1010_rdb_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (of_flat_dt_is_compatible(root, "fsl,P1010RDB")) + return 1; + return 0; +} + +define_machine(p1010_rdb) { + .name = "P1010 RDB", + .probe = p1010_rdb_probe, + .setup_arch = p1010_rdb_setup_arch, + .init_IRQ = p1010_rdb_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/85xx/p1022_ds.c b/arch/powerpc/platforms/85xx/p1022_ds.c new file mode 100644 index 00000000000..b0984ada3f8 --- /dev/null +++ b/arch/powerpc/platforms/85xx/p1022_ds.c @@ -0,0 +1,343 @@ +/* + * P1022DS board specific routines + * + * Authors: Travis Wheatley <travis.wheatley@freescale.com> + * Dave Liu <daveliu@freescale.com> + * Timur Tabi <timur@freescale.com> + * + * Copyright 2010 Freescale Semiconductor, Inc. + * + * This file is taken from the Freescale P1022DS BSP, with modifications: + * 2) No AMP support + * 3) No PCI endpoint support + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/pci.h> +#include <linux/of_platform.h> +#include <linux/memblock.h> +#include <asm/div64.h> +#include <asm/mpic.h> +#include <asm/swiotlb.h> + +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> +#include <asm/udbg.h> +#include <asm/fsl_guts.h> +#include "smp.h" + +#include "mpc85xx.h" + +#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) + +/* + * Board-specific initialization of the DIU. This code should probably be + * executed when the DIU is opened, rather than in arch code, but the DIU + * driver does not have a mechanism for this (yet). + * + * This is especially problematic on the P1022DS because the local bus (eLBC) + * and the DIU video signals share the same pins, which means that enabling the + * DIU will disable access to NOR flash. + */ + +/* DIU Pixel Clock bits of the CLKDVDR Global Utilities register */ +#define CLKDVDR_PXCKEN 0x80000000 +#define CLKDVDR_PXCKINV 0x10000000 +#define CLKDVDR_PXCKDLY 0x06000000 +#define CLKDVDR_PXCLK_MASK 0x00FF0000 + +/* Some ngPIXIS register definitions */ +#define PX_BRDCFG1_DVIEN 0x80 +#define PX_BRDCFG1_DFPEN 0x40 +#define PX_BRDCFG1_BACKLIGHT 0x20 +#define PX_BRDCFG1_DDCEN 0x10 + +/* + * DIU Area Descriptor + * + * Note that we need to byte-swap the value before it's written to the AD + * register. So even though the registers don't look like they're in the same + * bit positions as they are on the MPC8610, the same value is written to the + * AD register on the MPC8610 and on the P1022. + */ +#define AD_BYTE_F 0x10000000 +#define AD_ALPHA_C_MASK 0x0E000000 +#define AD_ALPHA_C_SHIFT 25 +#define AD_BLUE_C_MASK 0x01800000 +#define AD_BLUE_C_SHIFT 23 +#define AD_GREEN_C_MASK 0x00600000 +#define AD_GREEN_C_SHIFT 21 +#define AD_RED_C_MASK 0x00180000 +#define AD_RED_C_SHIFT 19 +#define AD_PALETTE 0x00040000 +#define AD_PIXEL_S_MASK 0x00030000 +#define AD_PIXEL_S_SHIFT 16 +#define AD_COMP_3_MASK 0x0000F000 +#define AD_COMP_3_SHIFT 12 +#define AD_COMP_2_MASK 0x00000F00 +#define AD_COMP_2_SHIFT 8 +#define AD_COMP_1_MASK 0x000000F0 +#define AD_COMP_1_SHIFT 4 +#define AD_COMP_0_MASK 0x0000000F +#define AD_COMP_0_SHIFT 0 + +#define MAKE_AD(alpha, red, blue, green, size, c0, c1, c2, c3) \ + cpu_to_le32(AD_BYTE_F | (alpha << AD_ALPHA_C_SHIFT) | \ + (blue << AD_BLUE_C_SHIFT) | (green << AD_GREEN_C_SHIFT) | \ + (red << AD_RED_C_SHIFT) | (c3 << AD_COMP_3_SHIFT) | \ + (c2 << AD_COMP_2_SHIFT) | (c1 << AD_COMP_1_SHIFT) | \ + (c0 << AD_COMP_0_SHIFT) | (size << AD_PIXEL_S_SHIFT)) + +/** + * p1022ds_get_pixel_format: return the Area Descriptor for a given pixel depth + * + * The Area Descriptor is a 32-bit value that determine which bits in each + * pixel are to be used for each color. + */ +static u32 p1022ds_get_pixel_format(enum fsl_diu_monitor_port port, + unsigned int bits_per_pixel) +{ + switch (bits_per_pixel) { + case 32: + /* 0x88883316 */ + return MAKE_AD(3, 2, 0, 1, 3, 8, 8, 8, 8); + case 24: + /* 0x88082219 */ + return MAKE_AD(4, 0, 1, 2, 2, 0, 8, 8, 8); + case 16: + /* 0x65053118 */ + return MAKE_AD(4, 2, 1, 0, 1, 5, 6, 5, 0); + default: + pr_err("fsl-diu: unsupported pixel depth %u\n", bits_per_pixel); + return 0; + } +} + +/** + * p1022ds_set_gamma_table: update the gamma table, if necessary + * + * On some boards, the gamma table for some ports may need to be modified. + * This is not the case on the P1022DS, so we do nothing. +*/ +static void p1022ds_set_gamma_table(enum fsl_diu_monitor_port port, + char *gamma_table_base) +{ +} + +/** + * p1022ds_set_monitor_port: switch the output to a different monitor port + * + */ +static void p1022ds_set_monitor_port(enum fsl_diu_monitor_port port) +{ + struct device_node *np; + void __iomem *pixis; + u8 __iomem *brdcfg1; + + np = of_find_compatible_node(NULL, NULL, "fsl,p1022ds-fpga"); + if (!np) + /* older device trees used "fsl,p1022ds-pixis" */ + np = of_find_compatible_node(NULL, NULL, "fsl,p1022ds-pixis"); + if (!np) { + pr_err("p1022ds: missing ngPIXIS node\n"); + return; + } + + pixis = of_iomap(np, 0); + if (!pixis) { + pr_err("p1022ds: could not map ngPIXIS registers\n"); + return; + } + brdcfg1 = pixis + 9; /* BRDCFG1 is at offset 9 in the ngPIXIS */ + + switch (port) { + case FSL_DIU_PORT_DVI: + printk(KERN_INFO "%s:%u\n", __func__, __LINE__); + /* Enable the DVI port, disable the DFP and the backlight */ + clrsetbits_8(brdcfg1, PX_BRDCFG1_DFPEN | PX_BRDCFG1_BACKLIGHT, + PX_BRDCFG1_DVIEN); + break; + case FSL_DIU_PORT_LVDS: + printk(KERN_INFO "%s:%u\n", __func__, __LINE__); + /* Enable the DFP port, disable the DVI and the backlight */ + clrsetbits_8(brdcfg1, PX_BRDCFG1_DVIEN | PX_BRDCFG1_BACKLIGHT, + PX_BRDCFG1_DFPEN); + break; + default: + pr_err("p1022ds: unsupported monitor port %i\n", port); + } + + iounmap(pixis); +} + +/** + * p1022ds_set_pixel_clock: program the DIU's clock + * + * @pixclock: the wavelength, in picoseconds, of the clock + */ +void p1022ds_set_pixel_clock(unsigned int pixclock) +{ + struct device_node *guts_np = NULL; + struct ccsr_guts_85xx __iomem *guts; + unsigned long freq; + u64 temp; + u32 pxclk; + + /* Map the global utilities registers. */ + guts_np = of_find_compatible_node(NULL, NULL, "fsl,p1022-guts"); + if (!guts_np) { + pr_err("p1022ds: missing global utilties device node\n"); + return; + } + + guts = of_iomap(guts_np, 0); + of_node_put(guts_np); + if (!guts) { + pr_err("p1022ds: could not map global utilties device\n"); + return; + } + + /* Convert pixclock from a wavelength to a frequency */ + temp = 1000000000000ULL; + do_div(temp, pixclock); + freq = temp; + + /* + * 'pxclk' is the ratio of the platform clock to the pixel clock. + * This number is programmed into the CLKDVDR register, and the valid + * range of values is 2-255. + */ + pxclk = DIV_ROUND_CLOSEST(fsl_get_sys_freq(), freq); + pxclk = clamp_t(u32, pxclk, 2, 255); + + /* Disable the pixel clock, and set it to non-inverted and no delay */ + clrbits32(&guts->clkdvdr, + CLKDVDR_PXCKEN | CLKDVDR_PXCKDLY | CLKDVDR_PXCLK_MASK); + + /* Enable the clock and set the pxclk */ + setbits32(&guts->clkdvdr, CLKDVDR_PXCKEN | (pxclk << 16)); + + iounmap(guts); +} + +/** + * p1022ds_valid_monitor_port: set the monitor port for sysfs + */ +enum fsl_diu_monitor_port +p1022ds_valid_monitor_port(enum fsl_diu_monitor_port port) +{ + switch (port) { + case FSL_DIU_PORT_DVI: + case FSL_DIU_PORT_LVDS: + return port; + default: + return FSL_DIU_PORT_DVI; /* Dual-link LVDS is not supported */ + } +} + +#endif + +void __init p1022_ds_pic_init(void) +{ + struct mpic *mpic = mpic_alloc(NULL, 0, + MPIC_WANTS_RESET | + MPIC_BIG_ENDIAN | MPIC_BROKEN_FRR_NIRQS | + MPIC_SINGLE_DEST_CPU, + 0, 256, " OpenPIC "); + BUG_ON(mpic == NULL); + mpic_init(mpic); +} + +/* + * Setup the architecture + */ +static void __init p1022_ds_setup_arch(void) +{ +#ifdef CONFIG_PCI + struct device_node *np; +#endif + dma_addr_t max = 0xffffffff; + + if (ppc_md.progress) + ppc_md.progress("p1022_ds_setup_arch()", 0); + +#ifdef CONFIG_PCI + for_each_compatible_node(np, "pci", "fsl,p1022-pcie") { + struct resource rsrc; + struct pci_controller *hose; + + of_address_to_resource(np, 0, &rsrc); + + if ((rsrc.start & 0xfffff) == 0x8000) + fsl_add_bridge(np, 1); + else + fsl_add_bridge(np, 0); + + hose = pci_find_hose_for_OF_device(np); + max = min(max, hose->dma_window_base_cur + + hose->dma_window_size); + } +#endif + +#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE) + diu_ops.get_pixel_format = p1022ds_get_pixel_format; + diu_ops.set_gamma_table = p1022ds_set_gamma_table; + diu_ops.set_monitor_port = p1022ds_set_monitor_port; + diu_ops.set_pixel_clock = p1022ds_set_pixel_clock; + diu_ops.valid_monitor_port = p1022ds_valid_monitor_port; +#endif + + mpc85xx_smp_init(); + +#ifdef CONFIG_SWIOTLB + if (memblock_end_of_DRAM() > max) { + ppc_swiotlb_enable = 1; + set_pci_dma_ops(&swiotlb_dma_ops); + ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_swiotlb; + } +#endif + + pr_info("Freescale P1022 DS reference board\n"); +} + +static struct of_device_id __initdata p1022_ds_ids[] = { + /* So that the DMA channel nodes can be probed individually: */ + { .compatible = "fsl,eloplus-dma", }, + {}, +}; + +static int __init p1022_ds_publish_devices(void) +{ + mpc85xx_common_publish_devices(); + return of_platform_bus_probe(NULL, p1022_ds_ids, NULL); +} +machine_device_initcall(p1022_ds, p1022_ds_publish_devices); + +machine_arch_initcall(p1022_ds, swiotlb_setup_bus_notifier); + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init p1022_ds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,p1022ds"); +} + +define_machine(p1022_ds) { + .name = "P1022 DS", + .probe = p1022_ds_probe, + .setup_arch = p1022_ds_setup_arch, + .init_IRQ = p1022_ds_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/85xx/p1023_rds.c b/arch/powerpc/platforms/85xx/p1023_rds.c new file mode 100644 index 00000000000..d951e7027bb --- /dev/null +++ b/arch/powerpc/platforms/85xx/p1023_rds.c @@ -0,0 +1,127 @@ +/* + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * + * Author: Roy Zang <tie-fei.zang@freescale.com> + * + * Description: + * P1023 RDS Board Setup + * + * 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 <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/fsl_devices.h> +#include <linux/of_platform.h> +#include <linux/of_device.h> + +#include <asm/system.h> +#include <asm/time.h> +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <mm/mmu_decl.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/mpic.h> +#include "smp.h" + +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> + +#include "mpc85xx.h" + +/* ************************************************************************ + * + * Setup the architecture + * + */ +static void __init mpc85xx_rds_setup_arch(void) +{ + struct device_node *np; + + if (ppc_md.progress) + ppc_md.progress("p1023_rds_setup_arch()", 0); + + /* Map BCSR area */ + np = of_find_node_by_name(NULL, "bcsr"); + if (np != NULL) { + static u8 __iomem *bcsr_regs; + + bcsr_regs = of_iomap(np, 0); + of_node_put(np); + + if (!bcsr_regs) { + printk(KERN_ERR + "BCSR: Failed to map bcsr register space\n"); + return; + } else { +#define BCSR15_I2C_BUS0_SEG_CLR 0x07 +#define BCSR15_I2C_BUS0_SEG2 0x02 +/* + * Note: Accessing exclusively i2c devices. + * + * The i2c controller selects initially ID EEPROM in the u-boot; + * but if menu configuration selects RTC support in the kernel, + * the i2c controller switches to select RTC chip in the kernel. + */ +#ifdef CONFIG_RTC_CLASS + /* Enable RTC chip on the segment #2 of i2c */ + clrbits8(&bcsr_regs[15], BCSR15_I2C_BUS0_SEG_CLR); + setbits8(&bcsr_regs[15], BCSR15_I2C_BUS0_SEG2); +#endif + + iounmap(bcsr_regs); + } + } + +#ifdef CONFIG_PCI + for_each_compatible_node(np, "pci", "fsl,p1023-pcie") + fsl_add_bridge(np, 0); +#endif + + mpc85xx_smp_init(); +} + +machine_device_initcall(p1023_rds, mpc85xx_common_publish_devices); + +static void __init mpc85xx_rds_pic_init(void) +{ + struct mpic *mpic = mpic_alloc(NULL, 0, + MPIC_WANTS_RESET | MPIC_BIG_ENDIAN | + MPIC_BROKEN_FRR_NIRQS | MPIC_SINGLE_DEST_CPU, + 0, 256, " OpenPIC "); + + BUG_ON(mpic == NULL); + + mpic_init(mpic); +} + +static int __init p1023_rds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,P1023RDS"); + +} + +define_machine(p1023_rds) { + .name = "P1023 RDS", + .probe = p1023_rds_probe, + .setup_arch = mpc85xx_rds_setup_arch, + .init_IRQ = mpc85xx_rds_pic_init, + .get_irq = mpic_get_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif +}; + diff --git a/arch/powerpc/platforms/85xx/p2041_rdb.c b/arch/powerpc/platforms/85xx/p2041_rdb.c new file mode 100644 index 00000000000..eda6ed5683e --- /dev/null +++ b/arch/powerpc/platforms/85xx/p2041_rdb.c @@ -0,0 +1,88 @@ +/* + * P2041 RDB Setup + * + * Copyright 2011 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 <linux/kernel.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/phy.h> + +#include <asm/system.h> +#include <asm/time.h> +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <mm/mmu_decl.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/mpic.h> + +#include <linux/of_platform.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> +#include <asm/ehv_pic.h> + +#include "corenet_ds.h" + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init p2041_rdb_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); +#ifdef CONFIG_SMP + extern struct smp_ops_t smp_85xx_ops; +#endif + + if (of_flat_dt_is_compatible(root, "fsl,P2041RDB")) + return 1; + + /* Check if we're running under the Freescale hypervisor */ + if (of_flat_dt_is_compatible(root, "fsl,P2041RDB-hv")) { + ppc_md.init_IRQ = ehv_pic_init; + ppc_md.get_irq = ehv_pic_get_irq; + ppc_md.restart = fsl_hv_restart; + ppc_md.power_off = fsl_hv_halt; + ppc_md.halt = fsl_hv_halt; +#ifdef CONFIG_SMP + /* + * Disable the timebase sync operations because we can't write + * to the timebase registers under the hypervisor. + */ + smp_85xx_ops.give_timebase = NULL; + smp_85xx_ops.take_timebase = NULL; +#endif + return 1; + } + + return 0; +} + +define_machine(p2041_rdb) { + .name = "P2041 RDB", + .probe = p2041_rdb_probe, + .setup_arch = corenet_ds_setup_arch, + .init_IRQ = corenet_ds_pic_init, +#ifdef CONFIG_PCI + .pcibios_fixup_bus = fsl_pcibios_fixup_bus, +#endif + .get_irq = mpic_get_coreint_irq, + .restart = fsl_rstcr_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, + .power_save = e500_idle, +}; + +machine_device_initcall(p2041_rdb, corenet_ds_publish_devices); + +#ifdef CONFIG_SWIOTLB +machine_arch_initcall(p2041_rdb, swiotlb_setup_bus_notifier); +#endif diff --git a/arch/powerpc/platforms/85xx/p3041_ds.c b/arch/powerpc/platforms/85xx/p3041_ds.c new file mode 100644 index 00000000000..96d99a374dc --- /dev/null +++ b/arch/powerpc/platforms/85xx/p3041_ds.c @@ -0,0 +1,90 @@ +/* + * P3041 DS Setup + * + * Maintained by Kumar Gala (see MAINTAINERS for contact information) + * + * Copyright 2009-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 as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/phy.h> + +#include <asm/system.h> +#include <asm/time.h> +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <mm/mmu_decl.h> +#include <asm/prom.h> +#include <asm/udbg.h> +#include <asm/mpic.h> + +#include <linux/of_platform.h> +#include <sysdev/fsl_soc.h> +#include <sysdev/fsl_pci.h> +#include <asm/ehv_pic.h> + +#include "corenet_ds.h" + +/* + * Called very early, device-tree isn't unflattened + */ +static int __init p3041_ds_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); +#ifdef CONFIG_SMP + extern struct smp_ops_t smp_85xx_ops; +#endif + + if (of_flat_dt_is_compatible(root, "fsl,P3041DS")) + return 1; + + /* Check if we're running under the Freescale hypervisor */ + if (of_flat_dt_is_compatible(root, "fsl,P3041DS-hv")) { + ppc_md.init_IRQ = ehv_pic_init; + ppc_md.get_irq = ehv_pic_get_irq; + ppc_md.restart = fsl_hv_restart; + ppc_md.power_off = fsl_hv_halt; + ppc_md.halt = fsl_hv_halt; +#ifdef CONFIG_SMP + /* + * Disable the timebase sync operations because we can't write + * to the timebase registers under the hypervisor. + */ + smp_85xx_ops.give_timebase = NULL; + smp_85xx_ops.take_timebase = NULL; +#endif + return 1; + } + + return 0; +} + +define_machine(p3041_ds) { + .name = "P3041 DS", + .probe |