LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] ppc32: Add P3P440 (440GP) board support
From: Stefan Roese @ 2005-11-22 17:34 UTC (permalink / raw)
  To: linuxppc-embedded
In-Reply-To: <200509161306.17103.sr@denx.de>

[-- Attachment #1: Type: text/plain, Size: 381 bytes --]

This patch adds support for the Prodrive P3P440 (440GP) PMC module.

Please note, that in arch/ppc/syslib/Makefile "todc_time" was generated
on all 4xx platforms. This is a problem with the P3P440, since it uses
a different RTC. I removed this 4xx - todc_time dependency and added it
to those boards really using it.

Signed-off-by: Stefan Roese <sr@denx.de>

Best regards,
Stefan

[-- Attachment #2: add-p3p440-board-support.patch --]
[-- Type: text/x-diff, Size: 42611 bytes --]

[PATCH] ppc32: Add P3P440 (440GP) board support

This patch adds support for the Prodrive P3P440 (440GP) PMC module.

Please note, that in arch/ppc/syslib/Makefile "todc_time" was generated
on all 4xx platforms. This is a problem with the P3P440, since it uses
a different RTC. I removed this 4xx - todc_time dependency and added it
to those boards really using it.

Signed-off-by: Stefan Roese <sr@denx.de>

---
commit 2537b8507f952c4635c0108bed06f3aade110a43
tree cd84ab91112624e13e800acafea9bca9b1ecaa4d
parent 989e4d6cbc69191c41ddf4b1c492457410376b43
author Stefan Roese <sr@denx.de> Tue, 22 Nov 2005 18:29:05 +0100
committer Stefan Roese <sr@denx.de> Tue, 22 Nov 2005 18:29:05 +0100

 arch/ppc/configs/p3p440_defconfig | 1155 +++++++++++++++++++++++++++++++++++++
 arch/ppc/platforms/4xx/Kconfig    |   10 
 arch/ppc/platforms/4xx/Makefile   |    1 
 arch/ppc/platforms/4xx/ibm440gp.h |    5 
 arch/ppc/platforms/4xx/p3p440.c   |  362 ++++++++++++
 arch/ppc/platforms/4xx/p3p440.h   |   72 ++
 arch/ppc/syslib/Makefile          |    7 
 include/asm-ppc/ibm4xx.h          |    4 
 8 files changed, 1613 insertions(+), 3 deletions(-)

diff --git a/arch/ppc/configs/p3p440_defconfig b/arch/ppc/configs/p3p440_defconfig
new file mode 100644
index 0000000..4477fff
--- /dev/null
+++ b/arch/ppc/configs/p3p440_defconfig
@@ -0,0 +1,1155 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.14
+# Tue Nov 22 10:21:06 2005
+#
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_PPC=y
+CONFIG_PPC32=y
+CONFIG_GENERIC_NVRAM=y
+CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
+CONFIG_ARCH_MAY_HAVE_PC_FDC=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_CLEAN_COMPILE=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+
+#
+# General setup
+#
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+# CONFIG_AUDIT is not set
+# CONFIG_HOTPLUG is not set
+CONFIG_KOBJECT_UEVENT=y
+# CONFIG_IKCONFIG is not set
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_EMBEDDED=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SHMEM=y
+CONFIG_CC_ALIGN_FUNCTIONS=0
+CONFIG_CC_ALIGN_LABELS=0
+CONFIG_CC_ALIGN_LOOPS=0
+CONFIG_CC_ALIGN_JUMPS=0
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODULE_UNLOAD is not set
+CONFIG_OBSOLETE_MODPARM=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+
+#
+# Block layer
+#
+CONFIG_LBD=y
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+
+#
+# Processor
+#
+# CONFIG_6xx is not set
+# CONFIG_40x is not set
+CONFIG_44x=y
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8xx is not set
+# CONFIG_E200 is not set
+# CONFIG_E500 is not set
+CONFIG_BOOKE=y
+CONFIG_PTE_64BIT=y
+CONFIG_PHYS_64BIT=y
+# CONFIG_MATH_EMULATION is not set
+# CONFIG_KEXEC is not set
+# CONFIG_CPU_FREQ is not set
+CONFIG_4xx=y
+CONFIG_WANT_EARLY_SERIAL=y
+
+#
+# IBM 4xx options
+#
+# CONFIG_BAMBOO is not set
+# CONFIG_EBONY is not set
+# CONFIG_LUAN is not set
+# CONFIG_YUCCA is not set
+# CONFIG_OCOTEA is not set
+CONFIG_P3P440=y
+# CONFIG_YELLOWSTONE is not set
+# CONFIG_YOSEMITE is not set
+CONFIG_440GP=y
+CONFIG_440=y
+CONFIG_IBM_OCP=y
+# CONFIG_PPC4xx_DMA is not set
+CONFIG_PPC_GEN550=y
+CONFIG_NOT_COHERENT_CACHE=y
+
+#
+# Platform options
+#
+# CONFIG_PC_KEYBOARD is not set
+# CONFIG_HIGHMEM is not set
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="ip=on"
+CONFIG_SECCOMP=y
+CONFIG_ISA_DMA_API=y
+
+#
+# Bus options
+#
+# CONFIG_PPC_I8259 is not set
+CONFIG_PPC_INDIRECT_PCI=y
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+# CONFIG_PCI_LEGACY_PROC is not set
+# CONFIG_PCI_DEBUG is not set
+
+#
+# PCCARD (PCMCIA/CardBus) support
+#
+# CONFIG_PCCARD is not set
+
+#
+# Advanced setup
+#
+# CONFIG_ADVANCED_OPTIONS is not set
+
+#
+# Default settings for advanced configuration options are used
+#
+CONFIG_HIGHMEM_START=0xfe000000
+CONFIG_LOWMEM_SIZE=0x30000000
+CONFIG_KERNEL_START=0xc0000000
+CONFIG_TASK_SIZE=0x80000000
+CONFIG_CONSISTENT_START=0xff100000
+CONFIG_CONSISTENT_SIZE=0x00200000
+CONFIG_BOOT_LOAD=0x01000000
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_DHCP is not set
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_BIC=y
+
+#
+# IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+# CONFIG_IPV6 is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+# CONFIG_NETFILTER_NETLINK is not set
+
+#
+# IP: Netfilter Configuration
+#
+# CONFIG_IP_NF_CONNTRACK is not set
+# CONFIG_IP_NF_QUEUE is not set
+# CONFIG_IP_NF_IPTABLES is not set
+# CONFIG_IP_NF_ARPTABLES is not set
+
+#
+# DCCP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_DCCP is not set
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_NET_DIVERT is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+# CONFIG_NET_CLS_ROUTE is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_IEEE80211 is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+# CONFIG_STANDALONE is not set
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_FW_LOADER is not set
+# CONFIG_DEBUG_DRIVER is not set
+
+#
+# Connector - unified userspace <-> kernelspace linker
+#
+# CONFIG_CONNECTOR is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_P3P440=y
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLKMTD is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+
+#
+# NAND Flash Device Drivers
+#
+# CONFIG_MTD_NAND is not set
+
+#
+# OneNAND Flash Device Drivers
+#
+# CONFIG_MTD_ONENAND is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play support
+#
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_SX8 is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI Transport Attributes
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_ATTRS is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_LEGACY is not set
+# CONFIG_MEGARAID_SAS is not set
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+CONFIG_SCSI_QLA2XXX=y
+# CONFIG_SCSI_QLA21XX is not set
+# CONFIG_SCSI_QLA22XX is not set
+# CONFIG_SCSI_QLA2300 is not set
+# CONFIG_SCSI_QLA2322 is not set
+# CONFIG_SCSI_QLA6312 is not set
+# CONFIG_SCSI_QLA24XX is not set
+# CONFIG_SCSI_LPFC is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+
+#
+# Fusion MPT device support
+#
+# CONFIG_FUSION is not set
+# CONFIG_FUSION_SPI is not set
+# CONFIG_FUSION_FC is not set
+# CONFIG_FUSION_SAS is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+
+#
+# Macintosh device drivers
+#
+# CONFIG_WINDFARM is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# PHY device support
+#
+# CONFIG_PHYLIB is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_CASSINI is not set
+# CONFIG_NET_VENDOR_3COM is not set
+
+#
+# Tulip family network device support
+#
+# CONFIG_NET_TULIP is not set
+# CONFIG_HP100 is not set
+CONFIG_IBM_EMAC=y
+CONFIG_IBM_EMAC_RXB=64
+CONFIG_IBM_EMAC_TXB=8
+CONFIG_IBM_EMAC_POLL_WEIGHT=32
+CONFIG_IBM_EMAC_RX_COPY_THRESHOLD=256
+CONFIG_IBM_EMAC_RX_SKB_HEADROOM=0
+# CONFIG_IBM_EMAC_DEBUG is not set
+CONFIG_IBM_EMAC_ZMII=y
+CONFIG_NET_PCI=y
+# CONFIG_PCNET32 is not set
+# CONFIG_AMD8111_ETH is not set
+# CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_B44 is not set
+# CONFIG_FORCEDETH is not set
+# CONFIG_DGRS is not set
+# CONFIG_EEPRO100 is not set
+# CONFIG_E100 is not set
+# CONFIG_FEALNX is not set
+# CONFIG_NATSEMI is not set
+# CONFIG_NE2K_PCI is not set
+# CONFIG_8139CP is not set
+CONFIG_8139TOO=y
+CONFIG_8139TOO_PIO=y
+# CONFIG_8139TOO_TUNE_TWISTER is not set
+# CONFIG_8139TOO_8129 is not set
+# CONFIG_8139_OLD_RX_RESET is not set
+# CONFIG_SIS900 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_SUNDANCE is not set
+# CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+# CONFIG_E1000 is not set
+# CONFIG_NS83820 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
+# CONFIG_SIS190 is not set
+# CONFIG_SKGE is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_VIA_VELOCITY is not set
+# CONFIG_TIGON3 is not set
+# CONFIG_BNX2 is not set
+
+#
+# Ethernet (10000 Mbit)
+#
+# CONFIG_CHELSIO_T1 is not set
+# CONFIG_IXGB is not set
+# CONFIG_S2IO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NET_FC is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+# CONFIG_INPUT is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_EXTENDED=y
+# CONFIG_SERIAL_8250_MANY_PORTS is not set
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+# CONFIG_SERIAL_8250_DETECT_IRQ is not set
+# CONFIG_SERIAL_8250_RSA is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_JSM is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+
+#
+# IPMI
+#
+# CONFIG_IPMI_HANDLER is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+CONFIG_GEN_RTC=y
+# CONFIG_GEN_RTC_X is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+# CONFIG_RAW_DRIVER is not set
+
+#
+# TPM devices
+#
+# CONFIG_TCG_TPM is not set
+# CONFIG_TELCLOCK is not set
+
+#
+# I2C support
+#
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+
+#
+# I2C Algorithms
+#
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALGOPCA is not set
+
+#
+# I2C Hardware Bus support
+#
+# CONFIG_I2C_ALI1535 is not set
+# CONFIG_I2C_ALI1563 is not set
+# CONFIG_I2C_ALI15X3 is not set
+# CONFIG_I2C_AMD756 is not set
+# CONFIG_I2C_AMD8111 is not set
+# CONFIG_I2C_I801 is not set
+# CONFIG_I2C_I810 is not set
+# CONFIG_I2C_PIIX4 is not set
+CONFIG_I2C_IBM_IIC=y
+# CONFIG_I2C_MPC is not set
+# CONFIG_I2C_MPC8260 is not set
+# CONFIG_I2C_NFORCE2 is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_PROSAVAGE is not set
+# CONFIG_I2C_SAVAGE4 is not set
+# CONFIG_SCx200_ACB is not set
+# CONFIG_I2C_SIS5595 is not set
+# CONFIG_I2C_SIS630 is not set
+# CONFIG_I2C_SIS96X is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+# CONFIG_I2C_VOODOO3 is not set
+# CONFIG_I2C_PCA_ISA is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_SENSORS_DS1337 is not set
+# CONFIG_SENSORS_DS1374 is not set
+# CONFIG_SENSORS_EEPROM is not set
+CONFIG_SENSORS_MAX6900=y
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCF8563 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_RTC8564 is not set
+# CONFIG_SENSORS_M41T00 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_RTC_X1205_I2C is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+
+#
+# Dallas's 1-wire bus
+#
+# CONFIG_W1 is not set
+
+#
+# Hardware Monitoring support
+#
+CONFIG_HWMON=y
+# CONFIG_HWMON_VID is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1031 is not set
+# CONFIG_SENSORS_ADM9240 is not set
+# CONFIG_SENSORS_ASB100 is not set
+# CONFIG_SENSORS_ATXP1 is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_FSCHER is not set
+# CONFIG_SENSORS_FSCPOS is not set
+# CONFIG_SENSORS_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 is not set
+# CONFIG_SENSORS_LM78 is not set
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
+# CONFIG_SENSORS_LM85 is not set
+# CONFIG_SENSORS_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_LM92 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_SIS5595 is not set
+# CONFIG_SENSORS_SMSC47M1 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_VIA686A is not set
+# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83792D is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_HWMON_DEBUG_CHIP is not set
+
+#
+# Misc devices
+#
+
+#
+# Multimedia Capabilities Port drivers
+#
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+# CONFIG_DVB is not set
+
+#
+# Graphics support
+#
+# CONFIG_FB is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# USB support
+#
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_BANDWIDTH is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+
+#
+# USB Host Controller Drivers
+#
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_SPLIT_ISO is not set
+# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
+# CONFIG_USB_ISP116X_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_BIG_ENDIAN is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+CONFIG_USB_UHCI_HCD=y
+# CONFIG_USB_SL811_HCD is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+
+#
+# USB Input Devices
+#
+# CONFIG_USB_HID is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+
+#
+# USB Multimedia devices
+#
+# CONFIG_USB_DABUSB is not set
+
+#
+# Video4Linux support is needed for USB Multimedia device support
+#
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+
+#
+# USB Serial Converter support
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_AUERSWALD is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_PHIDGETKIT is not set
+# CONFIG_USB_PHIDGETSERVO is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TEST is not set
+
+#
+# USB DSL modem support
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+
+#
+# InfiniBand support
+#
+# CONFIG_INFINIBAND is not set
+
+#
+# SN Devices
+#
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_INOTIFY=y
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_SYSFS=y
+# CONFIG_TMPFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_RAMFS=y
+# CONFIG_RELAYFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+CONFIG_CRAMFS=y
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V3 is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_DIRECTIO is not set
+# CONFIG_NFSD is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+# CONFIG_9P_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+
+#
+# Library routines
+#
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+CONFIG_CRC32=y
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+# CONFIG_PROFILING is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_MAGIC_SYSRQ is not set
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_KGDB is not set
+# CONFIG_XMON is not set
+CONFIG_BDI_SWITCH=y
+# CONFIG_SERIAL_TEXT_DEBUG is not set
+CONFIG_PPC_OCP=y
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Hardware crypto devices
+#
diff --git a/arch/ppc/platforms/4xx/Kconfig b/arch/ppc/platforms/4xx/Kconfig
index d883791..d1864ec 100644
--- a/arch/ppc/platforms/4xx/Kconfig
+++ b/arch/ppc/platforms/4xx/Kconfig
@@ -94,6 +94,12 @@ config OCOTEA
 	help
 	  This option enables support for the IBM PPC440GX evaluation board.
 
+config P3P440
+	bool "P3P440"
+	select WANT_EARLY_SERIAL
+	help
+	  This option enables support for the Prodrive P3P440 board.
+
 endchoice
 
 config EP405PC
@@ -117,7 +123,7 @@ config 440EP
 
 config 440GP
 	bool
-	depends on EBONY
+	depends on EBONY || P3P440
 	default y
 
 config 440GX
@@ -169,7 +175,7 @@ config BOOKE
 
 config IBM_OCP
 	bool
-	depends on ASH || BAMBOO || BUBINGA || CPCI405 || EBONY || EP405 || LUAN || YUCCA || OCOTEA || REDWOOD_5 || REDWOOD_6 || SYCAMORE || WALNUT
+	depends on ASH || BAMBOO || BUBINGA || CPCI405 || EBONY || EP405 || LUAN || YUCCA || OCOTEA || P3P440 || REDWOOD_5 || REDWOOD_6 || SYCAMORE || WALNUT
 	default y
 
 config XILINX_OCP
diff --git a/arch/ppc/platforms/4xx/Makefile b/arch/ppc/platforms/4xx/Makefile
index c9bb611..4f2e484 100644
--- a/arch/ppc/platforms/4xx/Makefile
+++ b/arch/ppc/platforms/4xx/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_BUBINGA)		+= bubinga.o
 obj-$(CONFIG_LUAN)		+= luan.o
 obj-$(CONFIG_YUCCA)		+= yucca.o
 obj-$(CONFIG_OCOTEA)		+= ocotea.o
+obj-$(CONFIG_P3P440)		+= p3p440.o
 obj-$(CONFIG_REDWOOD_5)		+= redwood5.o
 obj-$(CONFIG_REDWOOD_6)		+= redwood6.o
 obj-$(CONFIG_SYCAMORE)		+= sycamore.o
diff --git a/arch/ppc/platforms/4xx/ibm440gp.h b/arch/ppc/platforms/4xx/ibm440gp.h
index ae1efc0..59fe9e6 100644
--- a/arch/ppc/platforms/4xx/ibm440gp.h
+++ b/arch/ppc/platforms/4xx/ibm440gp.h
@@ -28,6 +28,11 @@
 #define UART0_INT		0
 #define UART1_INT		1
 
+/* GPIO macro register defines */
+#define GPIO_BASE		0x0000000140000700ULL
+#define GPIO0_OR		(GPIO_BASE+0x0)
+#define GPIO0_IR		(GPIO_BASE+0x1C)
+
 /* Clock and Power Management */
 #define IBM_CPM_IIC0		0x80000000	/* IIC interface */
 #define IBM_CPM_IIC1		0x40000000	/* IIC interface */
diff --git a/arch/ppc/platforms/4xx/p3p440.c b/arch/ppc/platforms/4xx/p3p440.c
new file mode 100644
index 0000000..656dfc1
--- /dev/null
+++ b/arch/ppc/platforms/4xx/p3p440.c
@@ -0,0 +1,362 @@
+/*
+ * arch/ppc/platforms/4xx/p3p440.c
+ *
+ * P3P440 board specific routines
+ *
+ * Copyright (c) 2005 DENX Software Engineering
+ * Stefan Roese <sr@denx.de>
+ *
+ * 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/config.h>
+#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/types.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+#include <linux/initrd.h>
+#include <linux/seq_file.h>
+#include <linux/root_dev.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/machdep.h>
+#include <asm/ocp.h>
+#include <asm/pci-bridge.h>
+#include <asm/time.h>
+#include <asm/bootinfo.h>
+#include <asm/ppc4xx_pic.h>
+#include <asm/ppcboot.h>
+#include <asm/tlbflush.h>
+
+#include <syslib/gen550.h>
+#include <syslib/ibm440gp_common.h>
+
+#include <platforms/4xx/ibm440gp.h>
+
+extern bd_t __res;
+
+static struct ibm44x_clocks clocks __initdata;
+
+/*
+ * P3P440 external IRQ triggering/polarity settings
+ */
+unsigned char ppc4xx_uic_ext_irq_cfg[] __initdata = {
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* IRQ0: PCI_INTA */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* IRQ1: PCI_INTB */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* IRQ2: PCI_INTC */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* IRQ3: PCI_INTD */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* IRQ4: ETH INT */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* IRQ5: USB */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/*  */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/*  */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/*  */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/*  */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/*  */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/*  */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/*  */
+};
+
+static void __init
+p3p440_calibrate_decr(void)
+{
+	unsigned int freq;
+
+	freq = clocks.cpu;
+
+	ibm44x_calibrate_decr(freq);
+}
+
+static int
+p3p440_show_cpuinfo(struct seq_file *m)
+{
+	seq_printf(m, "vendor\t\t: Prodrive\n");
+	seq_printf(m, "machine\t\t: P3P440\n");
+
+	return 0;
+}
+
+#if defined(CONFIG_I2C_IBM_IIC) && defined(CONFIG_SENSORS_MAX6900)
+extern ulong max6900_get_rtc_time(void);
+extern int max6900_set_rtc_time(unsigned long nowtime);
+
+static int __init
+p3p440_rtc_hookup(void)
+{
+	struct timespec	tv;
+
+        ppc_md.set_rtc_time = max6900_set_rtc_time;
+        ppc_md.get_rtc_time = max6900_get_rtc_time;
+
+	tv.tv_nsec = 0;
+	tv.tv_sec = (ppc_md.get_rtc_time)();
+	do_settimeofday(&tv);
+
+	return 0;
+}
+late_initcall(p3p440_rtc_hookup);
+#endif
+
+static inline int
+p3p440_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin)
+{
+	static char pci_irq_table[][4] =
+		/*
+		 *	PCI IDSEL/INTPIN->INTLINE
+		 * 	   A   B   C   D
+		 */
+		{
+			/*
+			 * Note: PMC Module A has device id 0! This could
+			 * be a problem, since the PPC440 pci bridge is located
+			 * on this device. -sr
+			 */
+			{PCI_INTB, PCI_INTC, PCI_INTD, PCI_INTA}, /* device 2 */
+			{PCI_INTC, PCI_INTD, PCI_INTA, PCI_INTB}, /* device 3 */
+			{PCI_INTD, PCI_INTA, PCI_INTB, PCI_INTC}, /* device 4 */
+		};
+
+	const long min_idsel = 2, max_idsel = 4, irqs_per_slot = 4;
+	return PCI_IRQ_TABLE_LOOKUP;
+}
+
+#define PCIX_WRITEL(value, offset) \
+	(writel(value, pcix_reg_base + offset))
+
+/*
+ * FIXME: This is only here to "make it work".  This will move
+ * to a ibm_pcix.c which will contain a generic IBM PCIX bridge
+ * configuration library. -Matt
+ */
+static void __init
+p3p440_setup_pcix(void)
+{
+	void __iomem *pcix_reg_base;
+
+	pcix_reg_base = ioremap64(PCIX0_REG_BASE, PCIX_REG_SIZE);
+
+	/* Disable all windows */
+	PCIX_WRITEL(0, PCIX0_POM0SA);
+	PCIX_WRITEL(0, PCIX0_POM1SA);
+	PCIX_WRITEL(0, PCIX0_POM2SA);
+	PCIX_WRITEL(0, PCIX0_PIM0SA);
+	PCIX_WRITEL(0, PCIX0_PIM1SA);
+	PCIX_WRITEL(0, PCIX0_PIM2SA);
+
+	/* Setup 2GB PLB->PCI outbound mem window (3_8000_0000->0_8000_0000) */
+	PCIX_WRITEL(0x00000003, PCIX0_POM0LAH);
+	PCIX_WRITEL(0x80000000, PCIX0_POM0LAL);
+	PCIX_WRITEL(0x00000000, PCIX0_POM0PCIAH);
+	PCIX_WRITEL(0x80000000, PCIX0_POM0PCIAL);
+	PCIX_WRITEL(0x80000001, PCIX0_POM0SA);
+
+	/* Setup 2GB PCI->PLB inbound memory window at 0, enable MSIs */
+	PCIX_WRITEL(0x00000000, PCIX0_PIM0LAH);
+	PCIX_WRITEL(0x00000000, PCIX0_PIM0LAL);
+	PCIX_WRITEL(0x80000007, PCIX0_PIM0SA);
+
+	eieio();
+}
+
+static int is_monarch(void)
+{
+	void __iomem *gpio_base;
+	ulong val;
+
+	gpio_base = ioremap64(GPIO_BASE, 0x1000);
+	val = in_be32((void *)(gpio_base+0x1c));
+	iounmap((void *)gpio_base);
+
+	if (val  & CFG_MONARCH_IO)
+		return 0;
+	else
+		return 1;
+}
+
+static void __init
+p3p440_setup_hose(void)
+{
+	struct pci_controller *hose;
+
+	if (!is_monarch()) {
+		/*
+		 * REMARK: This Non-Monarch mode need perhaps some changes.
+		 * It's not tested at all, because of lack of hardware. --sr
+		 */
+		printk("P3P440-PCI: Non-Monarch detected, skipping PCI init!\n");
+		return;
+	}
+
+	/* Configure windows on the PCI-X host bridge */
+	p3p440_setup_pcix();
+
+	hose = pcibios_alloc_controller();
+
+	if (!hose)
+		return;
+
+	hose->first_busno = 0;
+	hose->last_busno = 0xff;
+
+	hose->pci_mem_offset = P3P440_PCI_MEM_OFFSET;
+
+	pci_init_resource(&hose->io_resource,
+			P3P440_PCI_LOWER_IO,
+			P3P440_PCI_UPPER_IO,
+			IORESOURCE_IO,
+			"PCI host bridge");
+
+	pci_init_resource(&hose->mem_resources[0],
+			P3P440_PCI_LOWER_MEM,
+			P3P440_PCI_UPPER_MEM,
+			IORESOURCE_MEM,
+			"PCI host bridge");
+
+	hose->io_space.start = P3P440_PCI_LOWER_IO;
+	hose->io_space.end = P3P440_PCI_UPPER_IO;
+	hose->mem_space.start = P3P440_PCI_LOWER_MEM;
+	hose->mem_space.end = P3P440_PCI_UPPER_MEM;
+	hose->io_base_virt = ioremap64(P3P440_PCI_IO_BASE, P3P440_PCI_IO_SIZE);
+	isa_io_base = (unsigned long)hose->io_base_virt;
+
+	setup_indirect_pci(hose,
+			P3P440_PCI_CFGA_PLB32,
+			P3P440_PCI_CFGD_PLB32);
+	hose->set_cfg_type = 1;
+
+	hose->last_busno = pciauto_bus_scan(hose, hose->first_busno);
+
+	ppc_md.pci_swizzle = common_swizzle;
+	ppc_md.pci_map_irq = p3p440_map_irq;
+}
+
+static void __init
+p3p440_early_serial_map(void)
+{
+	struct uart_port port;
+
+	/* Setup ioremapped serial port access */
+	memset(&port, 0, sizeof(port));
+	port.membase = ioremap64(PPC440GP_UART0_ADDR, 8);
+	port.irq = 0;
+	port.uartclk = clocks.uart0;
+	port.regshift = 0;
+	port.iotype = SERIAL_IO_MEM;
+	port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST;
+	port.line = 0;
+
+	if (early_serial_setup(&port) != 0) {
+		printk("Early serial init of port 0 failed\n");
+	}
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+	/* Configure debug serial access */
+	gen550_init(0, &port);
+
+	/* Purge TLB entry added in head_44x.S for early serial access */
+	_tlbie(UART0_IO_BASE);
+#endif
+
+	port.membase = ioremap64(PPC440GP_UART1_ADDR, 8);
+	port.irq = 1;
+	port.uartclk = clocks.uart1;
+	port.line = 1;
+
+	if (early_serial_setup(&port) != 0) {
+		printk("Early serial init of port 1 failed\n");
+	}
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+	/* Configure debug serial access */
+	gen550_init(1, &port);
+#endif
+}
+
+static void __init
+p3p440_setup_arch(void)
+{
+	struct ocp_def *def;
+	struct ocp_func_emac_data *emacdata;
+
+	/* Set mac_addr for each EMAC */
+	def = ocp_get_one_device(OCP_VENDOR_IBM, OCP_FUNC_EMAC, 0);
+	emacdata = def->additions;
+	emacdata->phy_map = 0x00000001;	/* Skip 0x00 */
+	emacdata->phy_mode = PHY_MODE_RMII;
+	memcpy(emacdata->mac_addr, __res.bi_enetaddr, 6);
+
+	def = ocp_get_one_device(OCP_VENDOR_IBM, OCP_FUNC_EMAC, 1);
+	emacdata = def->additions;
+	emacdata->phy_map = 0x00000001;	/* Skip 0x00 */
+	emacdata->phy_mode = PHY_MODE_RMII;
+	memcpy(emacdata->mac_addr, __res.bi_enet1addr, 6);
+
+	/*
+	 * Determine various clocks.
+	 * To be completely correct we should get SysClk
+	 * from FPGA, because it can be changed by on-board switches
+	 * --ebs
+	 */
+	ibm440gp_get_clocks(&clocks, 33333333, 0);
+	ocp_sys_info.opb_bus_freq = clocks.opb;
+
+	/* init to some ~sane value until calibrate_delay() runs */
+        loops_per_jiffy = 50000000/HZ;
+
+	/* Setup PCI host bridge */
+	p3p440_setup_hose();
+
+#ifdef CONFIG_BLK_DEV_INITRD
+	if (initrd_start)
+		ROOT_DEV = Root_RAM0;
+	else
+#endif
+#ifdef CONFIG_ROOT_NFS
+		ROOT_DEV = Root_NFS;
+#else
+		ROOT_DEV = Root_HDA1;
+#endif
+
+	p3p440_early_serial_map();
+
+	/* Identify the system */
+	printk("Prodrive P3P440 port (DENX Software Engineering (sr@denx.de))\n");
+}
+
+void __init platform_init(unsigned long r3, unsigned long r4,
+		unsigned long r5, unsigned long r6, unsigned long r7)
+{
+	ibm44x_platform_init(r3, r4, r5, r6, r7);
+
+	ppc_md.setup_arch = p3p440_setup_arch;
+	ppc_md.show_cpuinfo = p3p440_show_cpuinfo;
+	ppc_md.get_irq = NULL;		/* Set in ppc4xx_pic_init() */
+
+	ppc_md.calibrate_decr = p3p440_calibrate_decr;
+	ppc_md.time_init = NULL;
+	ppc_md.set_rtc_time = NULL;
+	ppc_md.get_rtc_time = NULL;
+
+#ifdef CONFIG_KGDB
+	ppc_md.early_serial_map = p3p440_early_serial_map;
+#endif
+}
+
diff --git a/arch/ppc/platforms/4xx/p3p440.h b/arch/ppc/platforms/4xx/p3p440.h
new file mode 100644
index 0000000..bd1b68a
--- /dev/null
+++ b/arch/ppc/platforms/4xx/p3p440.h
@@ -0,0 +1,72 @@
+/*
+ * arch/ppc/platforms/p3p440.h
+ *
+ * P3P440 board definitions
+ *
+ * Copyright (c) 2005 DENX Software Engineering
+ * Stefan Roese <sr@denx.de>
+ *
+ * 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 __ASM_P3P440_H__
+#define __ASM_P3P440_H__
+
+#include <linux/config.h>
+#include <platforms/4xx/ibm440gp.h>
+
+/*
+ * Serial port defines
+ */
+
+/* head_44x.S created UART mapping, used before early_serial_setup.
+ * We cannot use default OpenBIOS UART mappings because they
+ * don't work for configurations with more than 512M RAM.    --ebs
+ */
+#define UART0_IO_BASE	0xF0000200
+#define UART1_IO_BASE	0xF0000300
+
+/* no external clock used, will be filled dynamically */
+#define BASE_BAUD	691200
+
+#define STD_UART_OP(num)					\
+	{ 0, BASE_BAUD, 0, UART##num##_INT,			\
+		(ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST),	\
+		iomem_base: (void*)UART##num##_IO_BASE,		\
+		io_type: SERIAL_IO_MEM},
+
+#define SERIAL_PORT_DFNS	\
+	STD_UART_OP(0)		\
+	STD_UART_OP(1)
+
+/* PCI support */
+#define P3P440_PCI_LOWER_IO	0x00000000
+#define P3P440_PCI_UPPER_IO	0x0000ffff
+#define P3P440_PCI_LOWER_MEM	0x80002000
+#define P3P440_PCI_UPPER_MEM	0xffffefff
+
+#define P3P440_PCI_CFGREGS_BASE	0x000000020ec00000
+#define P3P440_PCI_CFGA_PLB32	0x0ec00000
+#define P3P440_PCI_CFGD_PLB32	0x0ec00004
+
+#define P3P440_PCI_IO_BASE	0x0000000208000000ULL
+#define P3P440_PCI_IO_SIZE	0x00010000
+#define P3P440_PCI_MEM_OFFSET	0x00000000
+
+#define PCI_INTA		23
+#define PCI_INTB		24
+#define PCI_INTC		25
+#define PCI_INTD		26
+
+#define CFG_GPIO_RDY		(0x80000000 >> 11)
+#define CFG_MONARCH_IO		(0x80000000 >> 18)
+#define CFG_EREADY_IO		(0x80000000 >> 20)
+#define CFG_LED_GREEN		(0x80000000 >> 21)
+#define CFG_LED_RED		(0x80000000 >> 22)
+
+#endif				/* __ASM_P3P440_H__ */
+#endif				/* __KERNEL__ */
diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile
index 5b7f2b8..82a0924 100644
--- a/arch/ppc/syslib/Makefile
+++ b/arch/ppc/syslib/Makefile
@@ -28,7 +28,6 @@ endif
 endif
 obj-$(CONFIG_44x)		+= ppc4xx_pic.o
 obj-$(CONFIG_40x)		+= ppc4xx_setup.o
-obj-$(CONFIG_GEN_RTC)		+= todc_time.o
 obj-$(CONFIG_PPC4xx_DMA)	+= ppc4xx_dma.o
 obj-$(CONFIG_PPC4xx_EDMA)	+= ppc4xx_sgdma.o
 ifeq ($(CONFIG_40x),y)
@@ -44,8 +43,11 @@ obj-$(CONFIG_POWER4)		+= open_pic2.o
 obj-$(CONFIG_PPC_CHRP)		+= open_pic.o
 obj-$(CONFIG_PPC_PREP)		+= open_pic.o todc_time.o
 obj-$(CONFIG_BAMBOO)		+= pci_auto.o todc_time.o
+obj-$(CONFIG_BUBINGA)		+= todc_time.o
+obj-$(CONFIG_CPCI405)		+= todc_time.o
 obj-$(CONFIG_CPCI690)		+= todc_time.o pci_auto.o
 obj-$(CONFIG_EBONY)		+= pci_auto.o todc_time.o
+obj-$(CONFIG_EP405)		+= todc_time.o
 obj-$(CONFIG_EV64260)		+= todc_time.o pci_auto.o
 obj-$(CONFIG_EV64360)		+= todc_time.o
 obj-$(CONFIG_CHESTNUT)		+= mv64360_pic.o pci_auto.o
@@ -61,6 +63,7 @@ obj-$(CONFIG_MV64X60)		+= mv64x60.o mv64
 obj-$(CONFIG_MVME5100)		+= open_pic.o todc_time.o \
 					pci_auto.o hawk_common.o
 obj-$(CONFIG_OCOTEA)		+= pci_auto.o todc_time.o
+obj-$(CONFIG_P3P440)		+= pci_auto.o
 obj-$(CONFIG_PAL4)		+= cpc700_pic.o
 obj-$(CONFIG_POWERPMC250)	+= pci_auto.o
 obj-$(CONFIG_PPLUS)		+= hawk_common.o open_pic.o \
@@ -74,6 +77,8 @@ obj-$(CONFIG_SANDPOINT)		+= pci_auto.o t
 obj-$(CONFIG_SBC82xx)		+= todc_time.o
 obj-$(CONFIG_SPRUCE)		+= cpc700_pic.o pci_auto.o \
 				   todc_time.o
+obj-$(CONFIG_SYCAMORE)		+= todc_time.o
+obj-$(CONFIG_WALNUT)		+= todc_time.o
 obj-$(CONFIG_8260)		+= m8260_setup.o pq2_devices.o pq2_sys.o \
 				   ppc_sys.o
 obj-$(CONFIG_PCI_8260)		+= m82xx_pci.o pci_auto.o
diff --git a/include/asm-ppc/ibm4xx.h b/include/asm-ppc/ibm4xx.h
index 6c28ae7..510d98a 100644
--- a/include/asm-ppc/ibm4xx.h
+++ b/include/asm-ppc/ibm4xx.h
@@ -105,6 +105,10 @@ void ppc4xx_init(unsigned long r3, unsig
 #include <platforms/4xx/ocotea.h>
 #endif
 
+#if defined(CONFIG_P3P440)
+#include <platforms/4xx/p3p440.h>
+#endif
+
 #ifndef __ASSEMBLY__
 #ifdef CONFIG_40x
 /*


\f
!-------------------------------------------------------------flip-



^ permalink raw reply related

* Re: ELDK 4.0 Release?
From: Wolfgang Denk @ 2005-11-22 16:35 UTC (permalink / raw)
  To: Jeff Stevens; +Cc: linuxppc-embedded
In-Reply-To: <20051122160507.45342.qmail@web33406.mail.mud.yahoo.com>

Dear Jeff,

in message <20051122160507.45342.qmail@web33406.mail.mud.yahoo.com> you wrote:
> Has anyone heard when ELDK 4.0 is going to be
> released?  I read on denx that it was going to be
> released around the middle of October.  I'm not

The plan was to have it ready for final testing by end of October and
ready for release for the SPS/Drivers trade fair. But there are still
problems we want to fix before the final relase.

> complaining, I understand it's free software.  I'm
> just wondering if I should wait for 4.0, or try to
> build my own 2.6 linux filesystem.

I guess we need two more weeks from now - assuming  no  new  problems
will be found. If you like you can try a beta version...

Best regards,

Wolfgang Denk

-- 
Software Engineering:  Embedded and Realtime Systems,  Embedded Linux
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
Faith may be defined briefly as an illogical belief in the  occurence
of the improbable.                                    - H. L. Mencken

^ permalink raw reply

* ELDK 4.0 Release?
From: Jeff Stevens @ 2005-11-22 16:05 UTC (permalink / raw)
  To: linuxppc-embedded
In-Reply-To: <259581790511220341l2761f315ua7c10a4db9c51afa@mail.gmail.com>

Has anyone heard when ELDK 4.0 is going to be
released?  I read on denx that it was going to be
released around the middle of October.  I'm not
complaining, I understand it's free software.  I'm
just wondering if I should wait for 4.0, or try to
build my own 2.6 linux filesystem.

Thanks,
  Jeff Stevens



		
__________________________________ 
Yahoo! FareChase: Search multiple travel sites in one click.
http://farechase.yahoo.com

^ permalink raw reply

* Re: powerpc  boot sequence rework
From: Simon Richter @ 2005-11-22 15:26 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: linuxppc-dev list, linuxppc64-dev
In-Reply-To: <1132645967.26560.221.camel@gaston>

[-- Attachment #1: Type: text/plain, Size: 673 bytes --]

Hi,

Benjamin Herrenschmidt wrote:

> Here's a quick review of what happens now before my intended changes and
> some hilights of the ppc32/ppc64 differences. I intentionally ignored
> APUS as I don't even pretend to understand what it does, it will have to
> adapt to our changes anyway :)

Heh. I was anticipating this since the Oldenburg meeting.

>  - Very early trampoline code from the firmware (prom_init/bootx_init)
> that could eventually be moved to a boot wrapper but is currently kept
> in the kernel for convenience.

That answers my question whether I should have the code that builds the 
flattened device tree in the kernel or in the bootloader.

    Simon

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 307 bytes --]

^ permalink raw reply

* Re: PowerBook5,8 - TrackPad update
From: Johannes Berg @ 2005-11-22 12:51 UTC (permalink / raw)
  To: Parag Warudkar; +Cc: linuxppc-dev, debian-powerpc, linux-kernel, Parag Warudkar
In-Reply-To: <70210ED5-37CA-40BC-8293-FF1DAA3E8BD5@comcast.net>

Parag Warudkar wrote:
> Ok, the format interpretation of the trackpad data is taking up more
> of my time and brain than I was happy with! :)

Thanks for sending the data to me, I'll take a look later.

I have doubts, however, about using the appletouch driver, if the format
changed significantly then the code will be largely different.

johannes

^ permalink raw reply

* Trying to understand initsenses and pci_irq_table
From: Richard Danter @ 2005-11-22 10:20 UTC (permalink / raw)
  To: linuxppc-embedded

Hi all,

I am trying to port a 2.6.14.2 kernel to a 74xx/107 board and am having 
some problems which I think may be interrupt related. I have based my 
board support on the Sandpoint but realise that I do not really 
understand the initsenses or pci_irq_table structures and was hoping 
someone could either explain it or point me in the direction of some docs.

So far the kernel does boot and mount a NFS root filesystem, but crashes 
soon after. Whilst it boots the serial is very slow. It is at 9600 baud 
as expected, but it seems the characters are being spaced out in time 
which makes me think the serial is running in a polled mode rather than 
interrupt driven.

The board is very simple. There are several versions and the one I am 
working on now has a 7400 CPU, a 107 mem controller, a couple of PCI 
slots, serial port and some LED's.

Interrupts A, B, C and D from the PCI slots are connected to IRQ 0..3 on 
the 107. The serial port chip (16550 compatible) is wired directly to 
IRQ 4. There are no other PIC's or any other logic to confuse things.

U-Boot is running just fine and shows the following when I use the "pci" 
command:

Scanning PCI devices on bus 0
BusDevFun  VendorId   DeviceId   Device Class       Sub-Class
_____________________________________________________________
00.00.00   0x1057     0x0004     Bridge device           0x00
00.10.00   0x8086     0x1030     Network controller      0x00
00.12.00   0x1011     0x0026     Bridge device           0x04


How should these be mapped with the structures in the kernel?

The fact that NFS seems to be working and that the network card 
(EEPRO100) says it is using IRQ 16 is just pure luck I think!

The kernel crashes after starting init. I see some random characters on 
the serial and then nothing. Looking in the log buffer via JTAG I can 
see that the init process exited with signal 11. I assume this is 
unrelated to the above, but any suggestions where I should look?

Many thanks,
Rich

^ permalink raw reply

* Anyone using Montavista mobilinux?
From: Paulinha @ 2005-11-22 11:41 UTC (permalink / raw)
  To: linuxppc-embedded

[-- Attachment #1: Type: text/plain, Size: 390 bytes --]

Hi all,

I am starting a new design for a mobile device, with a virtex 4, and based
on the ppc405 hard core it has inside.

Because of power issues, we are thinking about a OS which implies low
disipation, and we have heared about mobilinux (Montavista distribution, I
think).

I would like to hear some oppinions or different possibilities.

Thanks and have a nice day!
Paula

[-- Attachment #2: Type: text/html, Size: 429 bytes --]

^ permalink raw reply

* powerpc  boot sequence rework
From: Benjamin Herrenschmidt @ 2005-11-22  7:52 UTC (permalink / raw)
  To: linuxppc-dev list; +Cc: linuxppc64-dev

I'm about to start significantly reworking the boot sequence of ppc32 to
make it look more like ppc64, and to move both ppc32 and ppc64 into a
scheme where less "magic" is done by the architecture before
start_kernel and we rely more on setup_arch. This is all for
ARCH=powerpc only of course.

This is an explanation of what I intend to do and a request for comments
of course. However, at this point, I can only do ppc64 and CONFIG_6xx
ppc32, other processors will need equivalent changes done by their
various maintainers.

Here's a quick review of what happens now before my intended changes and
some hilights of the ppc32/ppc64 differences. I intentionally ignored
APUS as I don't even pretend to understand what it does, it will have to
adapt to our changes anyway :)

  - prom_init/bootx_init(). This is called right away, in whatever
context we were when entering the kernel, if the right "signature" is
found in registers on entry. Both of these are "trampolines" that just
call back into __start with a flattened device-tree. In fact, they could
almost be moved to be a completely separate binary, if it wasn't for the
only "hack" I kept around where they actually share a couple of globals
with the kernel: the initial btext setup for early debug to screen and
it's associated early display BAT.

  - early_init() (ppc32 only). Called in the same context as above. This
clears bss, does the CPU detection & features fixup and returns. This is
mostly a remain of arch/ppc early_init() which used to also call
prom_init/bootx_init. The CPU feature stuff diverges from ppc64 here. On
ppc64, both detection and fixup are done from assembly at very different
times: while detection is also done early in a similar way, fixup is
done after some early initialisation C code has been run, giving a
chance to the kernel to "override" some of the CPU features based on
properties in the device-trees among others. 

 - TLBs/BATs are cleanup up, some initial BATs are setup for mapping the
beginning of memory (and eventually some debug IOs) but MMU isn't turned
on yet. We do the CPU setup at this point. It's done on ppc64 in a
similar place, at the same time as we are identifying it in fact, while
it's two separate calls on ppc32.

 - The kernel is relocated to 0 and the MMU turned on. At this point, we
run with an initial mapping set by the BATs that maps part of the RAM
(enough for the kernel .text/.data/.bss and the flattened device tree
blob is what matters here). On non-6xx CPUs, we do some equivalent
mapping using pinned TLB entries of some sort or other equivalent
facilities. On ppc64, instead, we stick to real mode due to a nice
"feature" of ppc64 processors which is to ignore the top 2 bits of
addresses when running in real mode. Thus code/data linked at
0xc000000000000000 will be accessed just fine when this really is at
0 :) In both case, this "initial" MMU setup (or absence of it on ppc64)
is there so we can run some early C code that identifies the machine and
sets up the proper MMU configuration. Thus, even if practically
different (pinned mapping vs. real mode), this is functionally
equivalent.

 - we get to start_here() (ppc32) and it's equivalent on ppc64
(start_here_multiplatform(), iSeries is a bit different for now, but
that's irrelevant to this discussion). After some housekeeping, like
clearing BSS, setting the PACA and TOC registers on ppc64, some initial
stack pointer, etc... so we can call C code, we get to what is called
early_setup() on ppc64, and machine_init() followed by MMU_init() on
ppc32. While there is a significant difference in the implementations of
these beasts, their goal is fairly similar: Do some early
initialisations like figuring out the machine type, setting up ppc_md.
etc... and configure the MMU properly. This is where ppc64 gets a chance
to change the CPU features before the dynamic patching is done, while
ppc32 has already done it. While the MMU code has to be different, the
way the ppc_md is selected and the early parsing of the device-tree has
no good reason to remain different.

 - Here, there is a significant difference in code flow. After coming
back from MMU_init, ppc32 will enable the MMU and call start_kernel()
(the main entry point in the common code). Further initialisations will
be done from setup_arch() that is called almost right away by
start_kernel() itself. ppc64 first goes through setup_system() which
does a huge amount of things which are mostly equivalent of those same
initialisations that ppc32 does in setup_arch(), then comes back to
head_64.s which then finally calls start_kernel. ppc64 additionaly also
does some more initialisations in it's own setup_arch().
My recent changes already made the early bits of ppc32 setup_arch() look
very similar to what happens in ppc64 setup_system(). One notable
addition is the call to ppc_md.init_early() which gives a chance to the
platform to do some initialisations (typically setup some early debug
output) very early during boot. It existed on ppc64 but was never called
on ppc32.

Ok, now let's quickly explain what I have in mind:

So while the implementation differs a lot, the global idea is the same,
and can be defined by 4 major steps:

 - Very early trampoline code from the firmware (prom_init/bootx_init)
that could eventually be moved to a boot wrapper but is currently kept
in the kernel for convenience.

 - Relocation of the kernel to its final location with MMU disabled

 - Setup of an initial MMU environment that allows running of C code &
access to the kernel text/data/bss and the device-tree blob (but not
necessarily all of RAM), typically using pinned tlb entries, BATs or
real mode depending on the CPU/architecture, then call into that C code
that will, from within that environment, identify the machine and setup
the necessary bits & pieces so the MMU can be fully enabled

 - Fully enable the MMU, do some additional initialisation & start the
kernel.

Now, my first idea is that a lot of those magic "*_init_*" functions
that are called from asm could go. There is absolutely no need for them.
In fact, once we have setup the initial MMU environment (BATs, pinned
TLBs or real mode), we could directly go to start_kernel.

start_kernel() itself will then conveniently call setup_arch() which
should be able to do all that is necessary in order from _one_ spot in
the arch code instead of 3. Instead of returning to head_*.S for
enabling the MMU, for example, setup_arch() would simply call into an
MMU startup function (let's call mmu_enable()) that returns with the MMU
fully enabled.

The code in setup_arch() would be entirely common to ppc32 and ppc64
with just the right "hooks" to deal their differences in
implementations.

The only "issue" with that first idea is that start_kernel() will do a
couple of things before calling setup_arch() and we need to be sure
those won't cause the kernel to blow up because they rely on things
being more initialized than they already are:

 - lock_kernel: hopefully should not be a problem
 - page_address_init(): this is just a bunch of initialisation of
globals, and thus shouldn't be a problem
 - printk(): this is the biggest one, but at this point, no console
driver is registered yet, so it should just dump it's output into the
buffer without any problem

The only possible "issue" I've seen is that spinlocks must already be
operational, that is if your spinlock implementation relies on some CPU
feature patching, it hasn't been done yet, that sort of thing.

Here is a more detailed description of the code flow i have in mind from
setup_arch(), which would be entered directly from start_kernel() after
MMU has been turned on with the "initial" mapping (or left off on
ppc64). Note that the BSS init is supposed to have been moved to the asm
like it is on ppc64 and thus called before that point. This is also the
case of the initial CPU identification and setup, but not of the dynamic
patching, thus all matching what ppc64 does.

 - early_init_devtree(). Stores the pointer to the flattened blob,
initializes some critical kernel globals based on what is in the
flattened tree like the LMB array, the command line, etc...

 - probe machine type. This would be done in a way similar to what ppc64
does, by calling repeately into all present ppc_md's probe() callback
until one 'gets' it. I will probably kill the ppc_md. pointer array that
ppc64 has now though and have the ppc_md's all be stored in a separate
ELF section that can be iterated and discarded so avoid ifdef's in
setup.c. I'll also probably make ppc_md.'s statically initialized (at
least for most of the callbacks). The probe() function should _not_ rely
on any _machine number as this will _not_ have been set for you unlike
what happens now. It should use the device-tree and is the one to set
_machine (at least for now, until it gets deprecated). The functions for
iterating the flat device-tree are available for use at this point. That
means that things like pmac_init(), chrp_init() and prep_init() are
gone. Dead. Good riddance.  At this point, we have done what current
platform_init() does on ppc32 and we are half-way through what current
early_setup() does on ppc64.

 - mmu_initialize(). The MMU is fully initialized but not turned "On"
yet, that is, all necessary data structures for using the MMU in it's
"final" setup are initialized, hash table allocated, etc... but the MMU
is still running on it's initial setup. On ppc64, that means doing
htab_initialize() and slb_initialize/stab_initialize(). On ppc32, that
means doing pretty much what MMU_init currently does.
This _might_ contain a callback to ppc_md. to allow the platform to
intervene but I'd rather avoid it if I can. (That is the current
ppc_md.setup_io_mappings()). Note about ppc64: Setting the hash table
access function pointers will be already done at this point, and thus no
longer done in whatever platform init_early() callback is called later
on, thus the #ifdef's and platform type tests can be fone from
htab_initialize() among others. It's the platform probe() routine which
is reponsible for setting a global indicating the type of mmu callbacks
to use (probably a firmware feature, I've not completely decided yet,
firmware features should be initialized from probe() anyway). Whatever
is also done currently by mm_init_ppc64() goes here, that function is
totally obsolete.

 - we apply the CPU feature fixups now. The 3 step aboves had all a
chance to modify some of the CPU features, this is now over and the
dynamic patching of asm code is done now.

 - mmu_enable(). This is an asm routine (hopefully) called from C code
in the initial setup that should return to C code with the MMU fully
active on the "final" setup. On ppc64 or ppc32 with hash table, that
means SDR1 has been set to point to the hash table, kernel segments have
been configured (if relevant), and the BATs are loaded with their final
values, etc... Upon return from this function, it's expected that the
entire linear mapping is accessible, and that early ioremap can be done
(using the ioremap_bot technique for allocating early virtual space)

 - Now we get into the typical sequence done today by ppc32 setup_arch()
and ppc64 setup_system(), that is unflatten_device_tree(),
check_for_initrd(),initialize_cache_info() (ppc64 only),
rtas_initialize(), ppc_md.init_early(), find_legacy_serial_ports()
(might be worth having a config option for that one ?),
finish_device_tree(), xmon_init(), and register_early_udbg_console(). I
don't want to get into too much details here, suffice to say that we
basically start with unflattening the device-tree (we should set some
global somewhere to make the early flat tree walking functions fail from
that point btw) and give a chance to the architecture to do some very
early initializations. That's where powermac will initialize it's
"feature" stuff (detecting northbridge & IO chip, initializing bits &
pieces of them), and setup some udbg stuff to get early debug output. If
your platform doesn't use the legacy serial ports, it might want to do
similar things here.

 - Now we have reached the end of ppc64 current setup_system() and are
half way through ppc32 current setup_arch(). The rest of setup_arch()
can be merged trivially, with a few ifdef's here or there, it's mostly
random data structure/globals initialisations that can be made common or
moved elsewhere (init_mm init should definitely be elsewhere :), calling
do_init_bootmem(), etc... No need to get into details here, I'll deal
with these bits once I'm actually writing the code. It all ends with
calling the platform's own setup_arch() if any, and paging_init().

I also intend to kill ppc_init(), platforms can do their own
arch_initcall() if they need it, CPU regisitration for sysfs should go
to sysfs.c which can be made common) etc...

It will take me a few days to go through the rework and I'll need help
testing & fixing things. In the meantime, comments on the above are
welcome.

Ben.

^ permalink raw reply

* RE: [PATCH] MTD: Add support for the PM82x Boards.
From: Heiko Schocher @ 2005-11-22  6:18 UTC (permalink / raw)
  To: David Woodhouse; +Cc: linuxppc-dev, linux-mtd
In-Reply-To: <1132604987.15117.63.camel@baythorne.infradead.org>

Hello,

On Monday, November 21, 2005 9:30 PM David Woodhouse wrote:
>> the following Patch (against 2.6 kernel.org tree, COMMIT_ID:
>> f093182d313edde9b1f86dbdaf40ba4da2dbd0e7) adds support for
>> the NOR flashes and for the DiskOnChip on the PM82X Boards
>> from Microsys.
> 
> Please use the new DiskOnChip driver in drivers/mtd/nand/ not the old
> one.

I use the new driver.
I made the changes in drivers/mtd/devices/docprobe.c,  because I
ported it from the 2.4 Kernel. Should I delete this and resubmit
the patch?

thanks
Heiko

^ permalink raw reply

* [PATCH] powerpc: Add back support for booting from BootX
From: Benjamin Herrenschmidt @ 2005-11-22  6:16 UTC (permalink / raw)
  To: linuxppc64-dev; +Cc: linuxppc-dev list

ARCH=powerpc couldn't boot from BootX as it uses a "different" way of
getting in the kernel. This patch adds the necessary trampolines,
creating a flattened device-tree from the tree passed from MacOS, and
initializing the btext engine early for really-early debugging.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

Index: linux-serialfix/arch/powerpc/platforms/powermac/bootx_init.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-serialfix/arch/powerpc/platforms/powermac/bootx_init.c	2005-11-22 16:16:05.000000000 +1100
@@ -0,0 +1,547 @@
+/*
+ *  Early boot support code for BootX bootloader
+ *
+ *  Copyright (C) 2005 Ben. Herrenschmidt (benh@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/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <asm/sections.h>
+#include <asm/prom.h>
+#include <asm/page.h>
+#include <asm/bootx.h>
+#include <asm/bootinfo.h>
+#include <asm/btext.h>
+#include <asm/io.h>
+
+#undef DEBUG
+#define SET_BOOT_BAT
+
+#ifdef DEBUG
+#define DBG(fmt...) do { bootx_printf(fmt); } while(0)
+#else
+#define DBG(fmt...) do { } while(0)
+#endif
+
+extern void __start(unsigned long r3, unsigned long r4, unsigned long r5);
+
+static unsigned long __initdata bootx_dt_strbase;
+static unsigned long __initdata bootx_dt_strend;
+static unsigned long __initdata bootx_node_chosen;
+static boot_infos_t * __initdata bootx_info;
+static char __initdata bootx_disp_path[256];
+
+/* Is boot-info compatible ? */
+#define BOOT_INFO_IS_COMPATIBLE(bi) \
+	((bi)->compatible_version <= BOOT_INFO_VERSION)
+#define BOOT_INFO_IS_V2_COMPATIBLE(bi)	((bi)->version >= 2)
+#define BOOT_INFO_IS_V4_COMPATIBLE(bi)	((bi)->version >= 4)
+
+#ifdef CONFIG_BOOTX_TEXT
+static void __init bootx_printf(const char *format, ...)
+{
+	const char *p, *q, *s;
+	va_list args;
+	unsigned long v;
+
+	va_start(args, format);
+	for (p = format; *p != 0; p = q) {
+		for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q)
+			;
+		if (q > p)
+			btext_drawtext(p, q - p);
+		if (*q == 0)
+			break;
+		if (*q == '\n') {
+			++q;
+			btext_flushline();
+			btext_drawstring("\r\n");
+			btext_flushline();
+			continue;
+		}
+		++q;
+		if (*q == 0)
+			break;
+		switch (*q) {
+		case 's':
+			++q;
+			s = va_arg(args, const char *);
+			if (s == NULL)
+				s = "<NULL>";
+			btext_drawstring(s);
+			break;
+		case 'x':
+			++q;
+			v = va_arg(args, unsigned long);
+			btext_drawhex(v);
+			break;
+		}
+	}
+}
+#else /* CONFIG_BOOTX_TEXT */
+static void __init bootx_printf(const char *format, ...) {}
+#endif /* CONFIG_BOOTX_TEXT */
+
+static void * __init bootx_early_getprop(unsigned long base,
+					 unsigned long node,
+					 char *prop)
+{
+	struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node);
+	u32 *ppp = &np->properties;
+
+	while(*ppp) {
+		struct bootx_dt_prop *pp =
+			(struct bootx_dt_prop *)(base + *ppp);
+
+		if (strcmp((char *)((unsigned long)pp->name + base),
+			   prop) == 0) {
+			return (void *)((unsigned long)pp->value + base);
+		}
+		ppp = &pp->next;
+	}
+	return NULL;
+}
+
+#define dt_push_token(token, mem) \
+	do { \
+		*(mem) = _ALIGN_UP(*(mem),4); \
+		*((u32 *)*(mem)) = token; \
+		*(mem) += 4; \
+	} while(0)
+
+static unsigned long __init bootx_dt_find_string(char *str)
+{
+	char *s, *os;
+
+	s = os = (char *)bootx_dt_strbase;
+	s += 4;
+	while (s <  (char *)bootx_dt_strend) {
+		if (strcmp(s, str) == 0)
+			return s - os;
+		s += strlen(s) + 1;
+	}
+	return 0;
+}
+
+static void __init bootx_dt_add_prop(char *name, void *data, int size,
+				  unsigned long *mem_end)
+{
+	unsigned long soff = bootx_dt_find_string(name);
+	if (data == NULL)
+		size = 0;
+	if (soff == 0) {
+		bootx_printf("WARNING: Can't find string index for <%s>\n",
+			     name);
+		return;
+	}
+	if (size > 0x20000) {
+		bootx_printf("WARNING: ignoring large property ");
+		bootx_printf("%s length 0x%x\n", name, size);
+		return;
+	}
+	dt_push_token(OF_DT_PROP, mem_end);
+	dt_push_token(size, mem_end);
+	dt_push_token(soff, mem_end);
+
+	/* push property content */
+	if (size && data) {
+		memcpy((void *)*mem_end, data, size);
+		*mem_end = _ALIGN_UP(*mem_end + size, 4);
+	}
+}
+
+static void __init bootx_add_chosen_props(unsigned long base,
+					  unsigned long *mem_end)
+{
+	u32 val = _MACH_Pmac;
+
+	bootx_dt_add_prop("linux,platform", &val, 4, mem_end);
+
+	if (bootx_info->kernelParamsOffset) {
+		char *args = (char *)((unsigned long)bootx_info) +
+			bootx_info->kernelParamsOffset;
+		bootx_dt_add_prop("bootargs", args, strlen(args) + 1, mem_end);
+	}
+	if (bootx_info->ramDisk) {
+		val = ((unsigned long)bootx_info) + bootx_info->ramDisk;
+		bootx_dt_add_prop("linux,initrd-start", &val, 4, mem_end);
+		val += bootx_info->ramDiskSize;
+		bootx_dt_add_prop("linux,initrd-end", &val, 4, mem_end);
+	}
+	if (strlen(bootx_disp_path))
+		bootx_dt_add_prop("linux,stdout-path", bootx_disp_path,
+				  strlen(bootx_disp_path) + 1, mem_end);
+}
+
+static void __init bootx_add_display_props(unsigned long base,
+					   unsigned long *mem_end)
+{
+	bootx_dt_add_prop("linux,boot-display", NULL, 0, mem_end);
+	bootx_dt_add_prop("linux,opened", NULL, 0, mem_end);
+}
+
+static void __init bootx_dt_add_string(char *s, unsigned long *mem_end)
+{
+	unsigned int l = strlen(s) + 1;
+	memcpy((void *)*mem_end, s, l);
+	bootx_dt_strend = *mem_end = *mem_end + l;
+}
+
+static void __init bootx_scan_dt_build_strings(unsigned long base,
+					       unsigned long node,
+					       unsigned long *mem_end)
+{
+	struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node);
+	u32 *cpp, *ppp = &np->properties;
+	unsigned long soff;
+	char *namep;
+
+	/* Keep refs to known nodes */
+	namep = np->full_name ? (char *)(base + np->full_name) : NULL;
+       	if (namep == NULL) {
+		bootx_printf("Node without a full name !\n");
+		namep = "";
+	}
+	DBG("* strings: %s\n", namep);
+
+	if (!strcmp(namep, "/chosen")) {
+		DBG(" detected /chosen ! adding properties names !\n");
+		bootx_dt_add_string("linux,platform", mem_end);
+		bootx_dt_add_string("linux,stdout-path", mem_end);
+		bootx_dt_add_string("linux,initrd-start", mem_end);
+		bootx_dt_add_string("linux,initrd-end", mem_end);
+		bootx_dt_add_string("bootargs", mem_end);
+		bootx_node_chosen = node;
+	}
+	if (node == bootx_info->dispDeviceRegEntryOffset) {
+		DBG(" detected display ! adding properties names !\n");
+		bootx_dt_add_string("linux,boot-display", mem_end);
+		bootx_dt_add_string("linux,opened", mem_end);
+		strncpy(bootx_disp_path, namep, 255);
+	}
+
+	/* get and store all property names */
+	while (*ppp) {
+		struct bootx_dt_prop *pp =
+			(struct bootx_dt_prop *)(base + *ppp);
+
+		namep = pp->name ? (char *)(base + pp->name) : NULL;
+ 		if (namep == NULL || strcmp(namep, "name") == 0)
+ 			goto next;
+		/* get/create string entry */
+		soff = bootx_dt_find_string(namep);
+		if (soff == 0)
+			bootx_dt_add_string(namep, mem_end);
+	next:
+		ppp = &pp->next;
+	}
+
+	/* do all our children */
+	cpp = &np->child;
+	while(*cpp) {
+		np = (struct bootx_dt_node *)(base + *cpp);
+		bootx_scan_dt_build_strings(base, *cpp, mem_end);
+		cpp = &np->sibling;
+	}
+}
+
+static void __init bootx_scan_dt_build_struct(unsigned long base,
+					      unsigned long node,
+					      unsigned long *mem_end)
+{
+	struct bootx_dt_node *np = (struct bootx_dt_node *)(base + node);
+	u32 *cpp, *ppp = &np->properties;
+	char *namep, *p, *ep, *lp;
+	int l;
+
+	dt_push_token(OF_DT_BEGIN_NODE, mem_end);
+
+	/* get the node's full name */
+	namep = np->full_name ? (char *)(base + np->full_name) : NULL;
+	if (namep == NULL)
+		namep = "";
+	l = strlen(namep);
+
+	DBG("* struct: %s\n", namep);
+
+	/* Fixup an Apple bug where they have bogus \0 chars in the
+	 * middle of the path in some properties, and extract
+	 * the unit name (everything after the last '/').
+	 */
+	memcpy((void *)*mem_end, namep, l + 1);
+	namep = (char *)*mem_end;
+	for (lp = p = namep, ep = namep + l; p < ep; p++) {
+		if (*p == '/')
+			lp = namep;
+		else if (*p != 0)
+			*lp++ = *p;
+	}
+	*lp = 0;
+	*mem_end = _ALIGN_UP((unsigned long)lp + 1, 4);
+
+	/* get and store all properties */
+	while (*ppp) {
+		struct bootx_dt_prop *pp =
+			(struct bootx_dt_prop *)(base + *ppp);
+
+		namep = pp->name ? (char *)(base + pp->name) : NULL;
+		/* Skip "name" */
+ 		if (namep == NULL || !strcmp(namep, "name"))
+ 			goto next;
+		/* Skip "bootargs" in /chosen too as we replace it */
+		if (node == bootx_node_chosen && !strcmp(namep, "bootargs"))
+			goto next;
+
+		/* push property head */
+		bootx_dt_add_prop(namep,
+				  pp->value ? (void *)(base + pp->value): NULL,
+				  pp->length, mem_end);
+	next:
+		ppp = &pp->next;
+	}
+
+	if (node == bootx_node_chosen)
+		bootx_add_chosen_props(base, mem_end);
+	if (node == bootx_info->dispDeviceRegEntryOffset)
+		bootx_add_display_props(base, mem_end);
+
+	/* do all our children */
+	cpp = &np->child;
+	while(*cpp) {
+		np = (struct bootx_dt_node *)(base + *cpp);
+		bootx_scan_dt_build_struct(base, *cpp, mem_end);
+		cpp = &np->sibling;
+	}
+
+	dt_push_token(OF_DT_END_NODE, mem_end);
+}
+
+static unsigned long __init bootx_flatten_dt(unsigned long start)
+{
+	boot_infos_t *bi = bootx_info;
+	unsigned long mem_start, mem_end;
+	struct boot_param_header *hdr;
+	unsigned long base;
+	u64 *rsvmap;
+
+	/* Start using memory after the big blob passed by BootX, get
+	 * some space for the header
+	 */
+	mem_start = mem_end = _ALIGN_UP(((unsigned long)bi) + start, 4);
+	DBG("Boot params header at: %x\n", mem_start);
+	hdr = (struct boot_param_header *)mem_start;
+	mem_end += sizeof(struct boot_param_header);
+	rsvmap = (u64 *)(_ALIGN_UP(mem_end, 8));
+	hdr->off_mem_rsvmap = ((unsigned long)rsvmap) - mem_start;
+	mem_end = ((unsigned long)rsvmap) + 8 * sizeof(u64);
+
+	/* Get base of tree */
+	base = ((unsigned long)bi) + bi->deviceTreeOffset;
+
+	/* Build string array */
+	DBG("Building string array at: %x\n", mem_end);
+	DBG("Device Tree Base=%x\n", base);
+	bootx_dt_strbase = mem_end;
+	mem_end += 4;
+	bootx_dt_strend = mem_end;
+	bootx_scan_dt_build_strings(base, 4, &mem_end);
+	hdr->off_dt_strings = bootx_dt_strbase - mem_start;
+	hdr->dt_strings_size = bootx_dt_strend - bootx_dt_strbase;
+
+	/* Build structure */
+	mem_end = _ALIGN(mem_end, 16);
+	DBG("Building device tree structure at: %x\n", mem_end);
+	hdr->off_dt_struct = mem_end - mem_start;
+	bootx_scan_dt_build_struct(base, 4, &mem_end);
+	dt_push_token(OF_DT_END, &mem_end);
+
+	/* Finish header */
+	hdr->boot_cpuid_phys = 0;
+	hdr->magic = OF_DT_HEADER;
+	hdr->totalsize = mem_end - mem_start;
+	hdr->version = OF_DT_VERSION;
+	/* Version 16 is not backward compatible */
+	hdr->last_comp_version = 0x10;
+
+	/* Reserve the whole thing and copy the reserve map in, we
+	 * also bump mem_reserve_cnt to cause further reservations to
+	 * fail since it's too late.
+	 */
+	mem_end = _ALIGN(mem_end, PAGE_SIZE);
+	DBG("End of boot params: %x\n", mem_end);
+	rsvmap[0] = mem_start;
+	rsvmap[1] = mem_end;
+	rsvmap[2] = 0;
+	rsvmap[3] = 0;
+
+	return (unsigned long)hdr;
+}
+
+
+#ifdef CONFIG_BOOTX_TEXT
+static void __init btext_welcome(boot_infos_t *bi)
+{
+	unsigned long flags;
+	unsigned long pvr;
+
+	bootx_printf("Welcome to Linux, kernel " UTS_RELEASE "\n");
+	bootx_printf("\nlinked at        : 0x%x", KERNELBASE);
+	bootx_printf("\nframe buffer at  : 0x%x", bi->dispDeviceBase);
+	bootx_printf(" (phys), 0x%x", bi->logicalDisplayBase);
+	bootx_printf(" (log)");
+	bootx_printf("\nklimit           : 0x%x",(unsigned long)klimit);
+	bootx_printf("\nboot_info at     : 0x%x", bi);
+	__asm__ __volatile__ ("mfmsr %0" : "=r" (flags));
+	bootx_printf("\nMSR              : 0x%x", flags);
+	__asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr));
+	bootx_printf("\nPVR              : 0x%x", pvr);
+	pvr >>= 16;
+	if (pvr > 1) {
+	    __asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags));
+	    bootx_printf("\nHID0             : 0x%x", flags);
+	}
+	if (pvr == 8 || pvr == 12 || pvr == 0x800c) {
+	    __asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags));
+	    bootx_printf("\nICTC             : 0x%x", flags);
+	}
+#ifdef DEBUG
+	bootx_printf("\n\n");
+	bootx_printf("bi->deviceTreeOffset   : 0x%x\n",
+		     bi->deviceTreeOffset);
+	bootx_printf("bi->deviceTreeSize     : 0x%x\n",
+		     bi->deviceTreeSize);
+#endif
+	bootx_printf("\n\n");
+}
+#endif /* CONFIG_BOOTX_TEXT */
+
+void __init bootx_init(unsigned long r3, unsigned long r4)
+{
+	boot_infos_t *bi = (boot_infos_t *) r4;
+	unsigned long hdr;
+	unsigned long space;
+	unsigned long ptr, x;
+	char *model;
+	unsigned long offset = reloc_offset();
+
+	reloc_got2(offset);
+
+	bootx_info = bi;
+
+	/* We haven't cleared any bss at this point, make sure
+	 * what we need is initialized
+	 */
+	bootx_dt_strbase = bootx_dt_strend = 0;
+	bootx_node_chosen = 0;
+	bootx_disp_path[0] = 0;
+
+	if (!BOOT_INFO_IS_V2_COMPATIBLE(bi))
+		bi->logicalDisplayBase = bi->dispDeviceBase;
+
+#ifdef CONFIG_BOOTX_TEXT
+	btext_setup_display(bi->dispDeviceRect[2] - bi->dispDeviceRect[0],
+			    bi->dispDeviceRect[3] - bi->dispDeviceRect[1],
+			    bi->dispDeviceDepth, bi->dispDeviceRowBytes,
+			    (unsigned long)bi->logicalDisplayBase);
+	btext_clearscreen();
+	btext_flushscreen();
+#endif /* CONFIG_BOOTX_TEXT */
+
+	/*
+	 * Test if boot-info is compatible.  Done only in config
+	 * CONFIG_BOOTX_TEXT since there is nothing much we can do
+	 * with an incompatible version, except display a message
+	 * and eventually hang the processor...
+	 *
+	 * I'll try to keep enough of boot-info compatible in the
+	 * future to always allow display of this message;
+	 */
+	if (!BOOT_INFO_IS_COMPATIBLE(bi)) {
+		bootx_printf(" !!! WARNING - Incompatible version"
+			     " of BootX !!!\n\n\n");
+		for (;;)
+			;
+	}
+	if (bi->architecture != BOOT_ARCH_PCI) {
+		bootx_printf(" !!! WARNING - Usupported machine"
+			     " architecture !\n");
+		for (;;)
+			;
+	}
+
+#ifdef CONFIG_BOOTX_TEXT
+	btext_welcome(bi);
+#endif
+	/* New BootX enters kernel with MMU off, i/os are not allowed
+	 * here. This hack will have been done by the boostrap anyway.
+	 */
+	if (bi->version < 4) {
+		/*
+		 * XXX If this is an iMac, turn off the USB controller.
+		 */
+		model = (char *) bootx_early_getprop(r4 + bi->deviceTreeOffset,
+						     4, "model");
+		if (model
+		    && (strcmp(model, "iMac,1") == 0
+			|| strcmp(model, "PowerMac1,1") == 0)) {
+			bootx_printf("iMac,1 detected, shutting down USB \n");
+			out_le32((unsigned *)0x80880008, 1);	/* XXX */
+		}
+	}
+
+	/* Get a pointer that points above the device tree, args, ramdisk,
+	 * etc... to use for generating the flattened tree
+	 */
+	if (bi->version < 5) {
+		space = bi->deviceTreeOffset + bi->deviceTreeSize;
+		if (bi->ramDisk)
+			space = bi->ramDisk + bi->ramDiskSize;
+	} else
+		space = bi->totalParamsSize;
+
+	bootx_printf("Total space used by parameters & ramdisk: %x \n", space);
+
+	/* New BootX will have flushed all TLBs and enters kernel with
+	 * MMU switched OFF, so this should not be useful anymore.
+	 */
+	if (bi->version < 4) {
+		bootx_printf("Touching pages...\n");
+
+		/*
+		 * Touch each page to make sure the PTEs for them
+		 * are in the hash table - the aim is to try to avoid
+		 * getting DSI exceptions while copying the kernel image.
+		 */
+		for (ptr = ((unsigned long) &_stext) & PAGE_MASK;
+		     ptr < (unsigned long)bi + space; ptr += PAGE_SIZE)
+			x = *(volatile unsigned long *)ptr;
+	}
+
+	/* Ok, now we need to generate a flattened device-tree to pass
+	 * to the kernel
+	 */
+	bootx_printf("Preparing boot params...\n");
+
+	hdr = bootx_flatten_dt(space);
+
+#ifdef CONFIG_BOOTX_TEXT
+#ifdef SET_BOOT_BAT
+	bootx_printf("Preparing BAT...\n");
+	btext_prepare_BAT();
+#else
+	btext_unmap();
+#endif
+#endif
+
+	reloc_got2(-offset);
+
+	__start(hdr, KERNELBASE + offset, 0);
+}
Index: linux-serialfix/arch/powerpc/kernel/head_32.S
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/head_32.S	2005-11-22 16:37:57.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/head_32.S	2005-11-22 17:09:32.000000000 +1100
@@ -125,6 +125,19 @@
 	bl	prom_init
 	trap
 
+/*
+ * Check for BootX signature when supporting PowerMac and branch to
+ * appropriate trampoline if it's present
+ */
+#ifdef CONFIG_PPC_PMAC
+1:	lis	r31,0x426f
+	ori	r31,r31,0x6f58
+	cmpw	0,r3,r31
+	bne	1f
+	bl	bootx_init
+	trap
+#endif /* CONFIG_PPC_PMAC */
+
 1:	mr	r31,r3			/* save parameters */
 	mr	r30,r4
 	li	r24,0			/* cpu # */
Index: linux-serialfix/arch/powerpc/platforms/powermac/Makefile
===================================================================
--- linux-serialfix.orig/arch/powerpc/platforms/powermac/Makefile	2005-11-22 16:33:30.000000000 +1100
+++ linux-serialfix/arch/powerpc/platforms/powermac/Makefile	2005-11-22 17:09:21.000000000 +1100
@@ -1,3 +1,5 @@
+CFLAGS_bootx_init.o  		+= -fPIC
+
 obj-y				+= pic.o setup.o time.o feature.o pci.o \
 				   sleep.o low_i2c.o cache.o
 obj-$(CONFIG_PMAC_BACKLIGHT)	+= backlight.o
@@ -6,5 +8,6 @@
 obj-$(CONFIG_NVRAM)		+= nvram.o
 # ppc64 pmac doesn't define CONFIG_NVRAM but needs nvram stuff
 obj-$(CONFIG_PPC64)		+= nvram.o
+obj-$(CONFIG_PPC32)		+= bootx_init.o
 obj-$(CONFIG_SMP)		+= smp.o
 obj-$(CONFIG_PPC_MERGE)		+= udbg_scc.o udbg_adb.o
Index: linux-serialfix/include/asm-powerpc/bootx.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-serialfix/include/asm-powerpc/bootx.h	2005-11-22 16:16:05.000000000 +1100
@@ -0,0 +1,166 @@
+/*
+ * This file describes the structure passed from the BootX application
+ * (for MacOS) when it is used to boot Linux.
+ *
+ * Written by Benjamin Herrenschmidt.
+ */
+
+
+#ifndef __ASM_BOOTX_H__
+#define __ASM_BOOTX_H__
+
+#ifdef macintosh
+#include <Types.h>
+#include "linux_type_defs.h"
+#endif
+
+#ifdef macintosh
+/* All this requires PowerPC alignment */
+#pragma options align=power
+#endif
+
+/* On kernel entry:
+ *
+ * r3 = 0x426f6f58    ('BooX')
+ * r4 = pointer to boot_infos
+ * r5 = NULL
+ *
+ * Data and instruction translation disabled, interrupts
+ * disabled, kernel loaded at physical 0x00000000 on PCI
+ * machines (will be different on NuBus).
+ */
+
+#define BOOT_INFO_VERSION               5
+#define BOOT_INFO_COMPATIBLE_VERSION    1
+
+/* Bit in the architecture flag mask. More to be defined in
+   future versions. Note that either BOOT_ARCH_PCI or
+   BOOT_ARCH_NUBUS is set. The other BOOT_ARCH_NUBUS_xxx are
+   set additionally when BOOT_ARCH_NUBUS is set.
+ */
+#define BOOT_ARCH_PCI                   0x00000001UL
+#define BOOT_ARCH_NUBUS                 0x00000002UL
+#define BOOT_ARCH_NUBUS_PDM             0x00000010UL
+#define BOOT_ARCH_NUBUS_PERFORMA        0x00000020UL
+#define BOOT_ARCH_NUBUS_POWERBOOK       0x00000040UL
+
+/*  Maximum number of ranges in phys memory map */
+#define MAX_MEM_MAP_SIZE				26
+
+/* This is the format of an element in the physical memory map. Note that
+   the map is optional and current BootX will only build it for pre-PCI
+   machines */
+typedef struct boot_info_map_entry
+{
+    __u32       physAddr;                /* Physical starting address */
+    __u32       size;                    /* Size in bytes */
+} boot_info_map_entry_t;
+
+
+/* Here are the boot informations that are passed to the bootstrap
+ * Note that the kernel arguments and the device tree are appended
+ * at the end of this structure. */
+typedef struct boot_infos
+{
+    /* Version of this structure */
+    __u32       version;
+    /* backward compatible down to version: */
+    __u32       compatible_version;
+
+    /* NEW (vers. 2) this holds the current _logical_ base addr of
+       the frame buffer (for use by early boot message) */
+    __u8*       logicalDisplayBase;
+
+    /* NEW (vers. 4) Apple's machine identification */
+    __u32       machineID;
+
+    /* NEW (vers. 4) Detected hw architecture */
+    __u32       architecture;
+
+    /* The device tree (internal addresses relative to the beginning of the tree,
+     * device tree offset relative to the beginning of this structure).
+     * On pre-PCI macintosh (BOOT_ARCH_PCI bit set to 0 in architecture), this
+     * field is 0.
+     */
+    __u32       deviceTreeOffset;        /* Device tree offset */
+    __u32       deviceTreeSize;          /* Size of the device tree */
+
+    /* Some infos about the current MacOS display */
+    __u32       dispDeviceRect[4];       /* left,top,right,bottom */
+    __u32       dispDeviceDepth;         /* (8, 16 or 32) */
+    __u8*       dispDeviceBase;          /* base address (physical) */
+    __u32       dispDeviceRowBytes;      /* rowbytes (in bytes) */
+    __u32       dispDeviceColorsOffset;  /* Colormap (8 bits only) or 0 (*) */
+    /* Optional offset in the registry to the current
+     * MacOS display. (Can be 0 when not detected) */
+     __u32      dispDeviceRegEntryOffset;
+
+    /* Optional pointer to boot ramdisk (offset from this structure) */
+    __u32       ramDisk;
+    __u32       ramDiskSize;             /* size of ramdisk image */
+
+    /* Kernel command line arguments (offset from this structure) */
+    __u32       kernelParamsOffset;
+
+    /* ALL BELOW NEW (vers. 4) */
+
+    /* This defines the physical memory. Valid with BOOT_ARCH_NUBUS flag
+       (non-PCI) only. On PCI, memory is contiguous and it's size is in the
+       device-tree. */
+    boot_info_map_entry_t
+    	        physMemoryMap[MAX_MEM_MAP_SIZE]; /* Where the phys memory is */
+    __u32       physMemoryMapSize;               /* How many entries in map */
+
+
+    /* The framebuffer size (optional, currently 0) */
+    __u32       frameBufferSize;         /* Represents a max size, can be 0. */
+
+    /* NEW (vers. 5) */
+
+    /* Total params size (args + colormap + device tree + ramdisk) */
+    __u32       totalParamsSize;
+
+} boot_infos_t;
+
+/* (*) The format of the colormap is 256 * 3 * 2 bytes. Each color index
+ * is represented by 3 short words containing a 16 bits (unsigned) color
+ * component. Later versions may contain the gamma table for direct-color
+ * devices here.
+ */
+#define BOOTX_COLORTABLE_SIZE    (256UL*3UL*2UL)
+
+/* BootX passes the device-tree using a format that comes from earlier
+ * ppc32 kernels. This used to match what is in prom.h, but not anymore
+ * so we now define it here
+ */
+struct bootx_dt_prop {
+	u32	name;
+	int	length;
+	u32	value;
+	u32	next;
+};
+
+struct bootx_dt_node {
+	u32	unused0;
+	u32	unused1;
+	u32	phandle;	/* not really available */
+	u32	unused2;
+	u32	unused3;
+	u32	unused4;
+	u32	unused5;
+	u32	full_name;
+	u32	properties;
+	u32	parent;
+	u32	child;
+	u32	sibling;
+	u32	next;
+	u32	allnext;
+};
+
+extern void bootx_init(unsigned long r4, unsigned long phys);
+
+#ifdef macintosh
+#pragma options align=reset
+#endif
+
+#endif

^ permalink raw reply

* [PATCH] powerpc: Unify udbg
From: Benjamin Herrenschmidt @ 2005-11-22  6:04 UTC (permalink / raw)
  To: linuxppc64-dev; +Cc: linuxppc-dev list

This patch unifies udbg for both ppc32 and ppc64 when building the
merged achitecture. xmon now has a single "back end". The powermac udbg
stuff gets enriched with some ADB capabilities and btext output. In
addition, the early_init callback is now called on ppc32 as well,
approx. in the same order as ppc64 regarding device-tree manipulations.
The init sequences of ppc32 and ppc64 are getting closer, I'll unify
them in a later patch.

For now, you can force udbg to the scc using "sccdbg" or to btext using
"btextdbg" on powermacs. I'll implement a cleaner way of forcing udbg
output to something else than the autodetected OF output device in a
later patch.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

Index: linux-serialfix/arch/powerpc/kernel/Makefile
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/Makefile	2005-11-22 16:33:30.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/Makefile	2005-11-22 16:33:30.000000000 +1100
@@ -18,7 +18,7 @@
 obj-$(CONFIG_PPC64)		+= setup_64.o binfmt_elf32.o sys_ppc32.o \
 				   signal_64.o ptrace32.o systbl.o \
 				   paca.o ioctl32.o cpu_setup_power4.o \
-				   firmware.o sysfs.o udbg.o idle_64.o
+				   firmware.o sysfs.o idle_64.o
 obj-$(CONFIG_PPC64)		+= vdso64/
 obj-$(CONFIG_ALTIVEC)		+= vecemu.o vector.o
 obj-$(CONFIG_POWER4)		+= idle_power4.o
@@ -45,7 +45,7 @@
 extra-y				+= vmlinux.lds
 
 obj-y				+= process.o init_task.o time.o \
-				   prom.o traps.o setup-common.o
+				   prom.o traps.o setup-common.o udbg.o
 obj-$(CONFIG_PPC32)		+= entry_32.o setup_32.o misc_32.o systbl.o
 obj-$(CONFIG_PPC64)		+= misc_64.o dma_64.o iommu.o
 obj-$(CONFIG_PPC_OF)		+= prom_init.o
@@ -55,8 +55,7 @@
 obj-$(CONFIG_SMP)		+= smp.o
 obj-$(CONFIG_KPROBES)		+= kprobes.o
 obj-$(CONFIG_PPC_MULTIPLATFORM) += legacy_serial.o
-obj64-$(CONFIG_PPC_MULTIPLATFORM) += udbg_16550.o
-obj64-$(CONFIG_PPC_PMAC)	+= udbg_scc.o
+obj-$(CONFIG_PPC_MULTIPLATFORM) += udbg_16550.o
 module-$(CONFIG_PPC64)		+= module_64.o
 obj-$(CONFIG_MODULES)		+= $(module-y)
 
Index: linux-serialfix/arch/powerpc/kernel/udbg.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/udbg.c	2005-11-14 10:41:58.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/udbg.c	2005-11-22 16:33:30.000000000 +1100
@@ -16,8 +16,8 @@
 #include <linux/console.h>
 #include <asm/processor.h>
 
-void (*udbg_putc)(unsigned char c);
-unsigned char (*udbg_getc)(void);
+void (*udbg_putc)(char c);
+char (*udbg_getc)(void);
 int (*udbg_getc_poll)(void);
 
 /* udbg library, used by xmon et al */
@@ -78,7 +78,7 @@
 #define UDBG_BUFSIZE 256
 void udbg_printf(const char *fmt, ...)
 {
-	unsigned char buf[UDBG_BUFSIZE];
+	char buf[UDBG_BUFSIZE];
 	va_list args;
 
 	va_start(args, fmt);
@@ -116,6 +116,8 @@
 /* called by setup_system */
 void register_early_udbg_console(void)
 {
+	if (early_console_initialized)
+		return;
 	early_console_initialized = 1;
 	register_console(&udbg_console);
 }
Index: linux-serialfix/arch/powerpc/kernel/udbg_scc.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/udbg_scc.c	2005-11-14 10:41:58.000000000 +1100
+++ /dev/null	1970-01-01 00:00:00.000000000 +0000
@@ -1,135 +0,0 @@
-/*
- * udbg for for zilog scc ports as found on Apple PowerMacs
- *
- * Copyright (C) 2001-2005 PPC 64 Team, IBM Corp
- *
- *      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/config.h>
-#include <linux/types.h>
-#include <asm/udbg.h>
-#include <asm/processor.h>
-#include <asm/io.h>
-#include <asm/prom.h>
-#include <asm/pmac_feature.h>
-
-extern u8 real_readb(volatile u8 __iomem  *addr);
-extern void real_writeb(u8 data, volatile u8 __iomem *addr);
-
-#define	SCC_TXRDY	4
-#define SCC_RXRDY	1
-
-static volatile u8 __iomem *sccc;
-static volatile u8 __iomem *sccd;
-
-static void udbg_scc_putc(unsigned char c)
-{
-	if (sccc) {
-		while ((in_8(sccc) & SCC_TXRDY) == 0)
-			;
-		out_8(sccd,  c);
-		if (c == '\n')
-			udbg_scc_putc('\r');
-	}
-}
-
-static int udbg_scc_getc_poll(void)
-{
-	if (sccc) {
-		if ((in_8(sccc) & SCC_RXRDY) != 0)
-			return in_8(sccd);
-		else
-			return -1;
-	}
-	return -1;
-}
-
-static unsigned char udbg_scc_getc(void)
-{
-	if (sccc) {
-		while ((in_8(sccc) & SCC_RXRDY) == 0)
-			;
-		return in_8(sccd);
-	}
-	return 0;
-}
-
-static unsigned char scc_inittab[] = {
-    13, 0,		/* set baud rate divisor */
-    12, 0,
-    14, 1,		/* baud rate gen enable, src=rtxc */
-    11, 0x50,		/* clocks = br gen */
-    5,  0xea,		/* tx 8 bits, assert DTR & RTS */
-    4,  0x46,		/* x16 clock, 1 stop */
-    3,  0xc1,		/* rx enable, 8 bits */
-};
-
-void udbg_init_scc(struct device_node *np)
-{
-	u32 *reg;
-	unsigned long addr;
-	int i, x;
-
-	if (np == NULL)
-		np = of_find_node_by_name(NULL, "escc");
-	if (np == NULL || np->parent == NULL)
-		return;
-
-	udbg_printf("found SCC...\n");
-	/* Get address within mac-io ASIC */
-	reg = (u32 *)get_property(np, "reg", NULL);
-	if (reg == NULL)
-		return;
-	addr = reg[0];
-	udbg_printf("local addr: %lx\n", addr);
-	/* Get address of mac-io PCI itself */
-	reg = (u32 *)get_property(np->parent, "assigned-addresses", NULL);
-	if (reg == NULL)
-		return;
-	addr += reg[2];
-	udbg_printf("final addr: %lx\n", addr);
-
-	/* Setup for 57600 8N1 */
-	addr += 0x20;
-	sccc = (volatile u8 * __iomem) ioremap(addr & PAGE_MASK, PAGE_SIZE) ;
-	sccc += addr & ~PAGE_MASK;
-	sccd = sccc + 0x10;
-
-	udbg_printf("ioremap result sccc: %p\n", sccc);
-	mb();
-
-	for (i = 20000; i != 0; --i)
-		x = in_8(sccc);
-	out_8(sccc, 0x09);		/* reset A or B side */
-	out_8(sccc, 0xc0);
-	for (i = 0; i < sizeof(scc_inittab); ++i)
-		out_8(sccc, scc_inittab[i]);
-
-	udbg_putc = udbg_scc_putc;
-	udbg_getc = udbg_scc_getc;
-	udbg_getc_poll = udbg_scc_getc_poll;
-
-	udbg_puts("Hello World !\n");
-}
-
-static void udbg_real_scc_putc(unsigned char c)
-{
-	while ((real_readb(sccc) & SCC_TXRDY) == 0)
-		;
-	real_writeb(c, sccd);
-	if (c == '\n')
-		udbg_real_scc_putc('\r');
-}
-
-void udbg_init_pmac_realmode(void)
-{
-	sccc = (volatile u8 __iomem *)0x80013020ul;
-	sccd = (volatile u8 __iomem *)0x80013030ul;
-
-	udbg_putc = udbg_real_scc_putc;
-	udbg_getc = NULL;
-	udbg_getc_poll = NULL;
-}
Index: linux-serialfix/arch/powerpc/platforms/powermac/udbg_adb.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-serialfix/arch/powerpc/platforms/powermac/udbg_adb.c	2005-11-22 16:38:49.000000000 +1100
@@ -0,0 +1,218 @@
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/bitops.h>
+#include <linux/ptrace.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <linux/cuda.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/xmon.h>
+#include <asm/prom.h>
+#include <asm/bootx.h>
+#include <asm/machdep.h>
+#include <asm/errno.h>
+#include <asm/pmac_feature.h>
+#include <asm/processor.h>
+#include <asm/delay.h>
+#include <asm/btext.h>
+#include <asm/time.h>
+#include <asm/udbg.h>
+
+/*
+ * This implementation is "special", it can "patch" the current
+ * udbg implementation and work on top of it. It must thus be
+ * initialized last
+ */
+
+static void (*udbg_adb_old_putc)(char c);
+static char (*udbg_adb_old_getc)(void);
+static int (*udbg_adb_old_getc_poll)(void);
+
+static enum {
+	input_adb_none,
+	input_adb_pmu,
+	input_adb_cuda,
+} input_type = input_adb_none;
+
+static int udbg_adb_use_btext;
+
+int xmon_wants_key, xmon_adb_keycode;
+
+static inline void udbg_adb_poll(void)
+{
+#ifdef CONFIG_ADB_PMU
+	if (input_type == input_adb_pmu)
+		pmu_poll_adb();
+#endif /* CONFIG_ADB_PMU */
+#ifdef CONFIG_ADB_CUDA
+	if (input_type == input_adb_cuda)
+		cuda_poll();
+#endif /* CONFIG_ADB_CUDA */
+}
+
+#ifdef CONFIG_BOOTX_TEXT
+static int xmon_adb_shiftstate;
+
+static unsigned char xmon_keytab[128] =
+	"asdfhgzxcv\000bqwer"				/* 0x00 - 0x0f */
+	"yt123465=97-80]o"				/* 0x10 - 0x1f */
+	"u[ip\rlj'k;\\,/nm."				/* 0x20 - 0x2f */
+	"\t `\177\0\033\0\0\0\0\0\0\0\0\0\0"		/* 0x30 - 0x3f */
+	"\0.\0*\0+\0\0\0\0\0/\r\0-\0"			/* 0x40 - 0x4f */
+	"\0\0000123456789\0\0\0";			/* 0x50 - 0x5f */
+
+static unsigned char xmon_shift_keytab[128] =
+	"ASDFHGZXCV\000BQWER"				/* 0x00 - 0x0f */
+	"YT!@#$^%+(&_*)}O"				/* 0x10 - 0x1f */
+	"U{IP\rLJ\"K:|<?NM>"				/* 0x20 - 0x2f */
+	"\t ~\177\0\033\0\0\0\0\0\0\0\0\0\0"		/* 0x30 - 0x3f */
+	"\0.\0*\0+\0\0\0\0\0/\r\0-\0"			/* 0x40 - 0x4f */
+	"\0\0000123456789\0\0\0";			/* 0x50 - 0x5f */
+
+static char udbg_adb_local_getc(void)
+{
+	int k, t, on;
+
+	xmon_wants_key = 1;
+	for (;;) {
+		xmon_adb_keycode = -1;
+		t = 0;
+		on = 0;
+		k = -1;
+		do {
+			if (--t < 0) {
+				on = 1 - on;
+				btext_drawchar(on? 0xdb: 0x20);
+				btext_drawchar('\b');
+				t = 200000;
+			}
+			udbg_adb_poll();
+			if (udbg_adb_old_getc_poll)
+				k = udbg_adb_old_getc_poll();
+		} while (k == -1 && xmon_adb_keycode == -1);
+		if (on)
+			btext_drawstring(" \b");
+		if (k != -1)
+			return k;
+		k = xmon_adb_keycode;
+
+		/* test for shift keys */
+		if ((k & 0x7f) == 0x38 || (k & 0x7f) == 0x7b) {
+			xmon_adb_shiftstate = (k & 0x80) == 0;
+			continue;
+		}
+		if (k >= 0x80)
+			continue;	/* ignore up transitions */
+		k = (xmon_adb_shiftstate? xmon_shift_keytab: xmon_keytab)[k];
+		if (k != 0)
+			break;
+	}
+	xmon_wants_key = 0;
+	return k;
+}
+#endif /* CONFIG_BOOTX_TEXT */
+
+static char udbg_adb_getc(void)
+{
+#ifdef CONFIG_BOOTX_TEXT
+	if (udbg_adb_use_btext && input_type != input_adb_none)
+		return udbg_adb_local_getc();
+#endif
+	if (udbg_adb_old_getc)
+		return udbg_adb_old_getc();
+	return -1;
+}
+
+/* getc_poll() is not really used, unless you have the xmon-over modem
+ * hack that doesn't quite concern us here, thus we just poll the low level
+ * ADB driver to prevent it from timing out and call back the original poll
+ * routine.
+ */
+static int udbg_adb_getc_poll(void)
+{
+	udbg_adb_poll();
+
+	if (udbg_adb_old_getc_poll)
+		return udbg_adb_old_getc_poll();
+	return -1;
+}
+
+static void udbg_adb_putc(char c)
+{
+#ifdef CONFIG_BOOTX_TEXT
+	if (udbg_adb_use_btext)
+		btext_drawchar(c);
+#endif
+	if (udbg_adb_old_putc)
+		return udbg_adb_old_putc(c);
+}
+
+void udbg_adb_init_early(void)
+{
+#ifdef CONFIG_BOOTX_TEXT
+	if (btext_find_display(1) == 0) {
+		udbg_adb_use_btext = 1;
+		udbg_putc = udbg_adb_putc;
+	}
+#endif
+}
+
+int udbg_adb_init(int force_btext)
+{
+	struct device_node *np;
+
+	/* Capture existing callbacks */
+	udbg_adb_old_putc = udbg_putc;
+	udbg_adb_old_getc = udbg_getc;
+	udbg_adb_old_getc_poll = udbg_getc_poll;
+
+	/* Check if our early init was already called */
+	if (udbg_adb_old_putc == udbg_adb_putc ||
+	    udbg_adb_old_putc == btext_drawchar)
+		udbg_adb_old_putc = NULL;
+
+	/* Set ours as output */
+	udbg_putc = udbg_adb_putc;
+	udbg_getc = udbg_adb_getc;
+	udbg_getc_poll = udbg_adb_getc_poll;
+
+#ifdef CONFIG_BOOTX_TEXT
+	/* Check if we should use btext output */
+	if (btext_find_display(force_btext) == 0)
+		udbg_adb_use_btext = 1;
+#endif
+
+	/* See if there is a keyboard in the device tree with a parent
+	 * of type "adb". If not, we return a failure, but we keep the
+	 * bext output set for now
+	 */
+	for (np = NULL; (np = of_find_node_by_name(np, "keyboard")) != NULL;) {
+		struct device_node *parent = of_get_parent(np);
+		int found = (parent && !strcmp(parent->type, "adb") == 0);
+		of_node_put(parent);
+		if (found)
+			break;
+	}
+	if (np == NULL)
+		return -ENODEV;
+	of_node_put(np);
+
+#ifdef CONFIG_ADB_PMU
+	if (find_via_pmu())
+		input_type = input_adb_pmu;
+#endif
+#ifdef CONFIG_ADB_CUDA
+	if (find_via_cuda())
+		input_type = input_adb_cuda;
+#endif
+
+	/* Same as above: nothing found, keep btext set for output */
+	if (input_type == input_adb_none)
+		return -ENODEV;
+
+	return 0;
+}
Index: linux-serialfix/arch/powerpc/platforms/powermac/udbg_scc.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-serialfix/arch/powerpc/platforms/powermac/udbg_scc.c	2005-11-22 16:33:30.000000000 +1100
@@ -0,0 +1,165 @@
+/*
+ * udbg for for zilog scc ports as found on Apple PowerMacs
+ *
+ * Copyright (C) 2001-2005 PPC 64 Team, IBM Corp
+ *
+ *      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/config.h>
+#include <linux/types.h>
+#include <asm/udbg.h>
+#include <asm/processor.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pmac_feature.h>
+
+extern u8 real_readb(volatile u8 __iomem  *addr);
+extern void real_writeb(u8 data, volatile u8 __iomem *addr);
+
+#define	SCC_TXRDY	4
+#define SCC_RXRDY	1
+
+static volatile u8 __iomem *sccc;
+static volatile u8 __iomem *sccd;
+
+static void udbg_scc_putc(char c)
+{
+	if (sccc) {
+		while ((in_8(sccc) & SCC_TXRDY) == 0)
+			;
+		out_8(sccd,  c);
+		if (c == '\n')
+			udbg_scc_putc('\r');
+	}
+}
+
+static int udbg_scc_getc_poll(void)
+{
+	if (sccc) {
+		if ((in_8(sccc) & SCC_RXRDY) != 0)
+			return in_8(sccd);
+		else
+			return -1;
+	}
+	return -1;
+}
+
+static char udbg_scc_getc(void)
+{
+	if (sccc) {
+		while ((in_8(sccc) & SCC_RXRDY) == 0)
+			;
+		return in_8(sccd);
+	}
+	return 0;
+}
+
+static unsigned char scc_inittab[] = {
+    13, 0,		/* set baud rate divisor */
+    12, 0,
+    14, 1,		/* baud rate gen enable, src=rtxc */
+    11, 0x50,		/* clocks = br gen */
+    5,  0xea,		/* tx 8 bits, assert DTR & RTS */
+    4,  0x46,		/* x16 clock, 1 stop */
+    3,  0xc1,		/* rx enable, 8 bits */
+};
+
+void udbg_scc_init(int force_scc)
+{
+	u32 *reg;
+	unsigned long addr;
+	struct device_node *stdout = NULL, *escc = NULL, *macio = NULL;
+	struct device_node *ch, *ch_def = NULL, *ch_a = NULL;
+	char *path;
+	int i, x;
+
+	escc = of_find_node_by_name(NULL, "escc");
+	if (escc == NULL)
+		goto bail;
+	macio = of_get_parent(escc);
+	if (macio == NULL)
+		goto bail;
+	path = (char *)get_property(of_chosen, "linux,stdout-path", NULL);
+	if (path != NULL)
+		stdout = of_find_node_by_path(path);
+	for (ch = NULL; (ch = of_get_next_child(escc, ch)) != NULL;) {
+		if (ch == stdout)
+			ch_def = of_node_get(ch);
+		if (strcmp(ch->name, "ch-a") == 0)
+			ch_a = of_node_get(ch);
+	}
+	if (ch_def == NULL && !force_scc)
+		goto bail;
+
+	ch = ch_def ? ch_def : ch_a;
+
+	/* Get address within mac-io ASIC */
+	reg = (u32 *)get_property(escc, "reg", NULL);
+	if (reg == NULL)
+		goto bail;
+	addr = reg[0];
+
+	/* Get address of mac-io PCI itself */
+	reg = (u32 *)get_property(macio, "assigned-addresses", NULL);
+	if (reg == NULL)
+		goto bail;
+	addr += reg[2];
+
+	/* Lock the serial port */
+	pmac_call_feature(PMAC_FTR_SCC_ENABLE, ch,
+			  PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1);
+
+
+	/* Setup for 57600 8N1 */
+	if (ch == ch_a)
+		addr += 0x20;
+	sccc = (volatile u8 * __iomem) ioremap(addr & PAGE_MASK, PAGE_SIZE) ;
+	sccc += addr & ~PAGE_MASK;
+	sccd = sccc + 0x10;
+
+	mb();
+
+	for (i = 20000; i != 0; --i)
+		x = in_8(sccc);
+	out_8(sccc, 0x09);		/* reset A or B side */
+	out_8(sccc, 0xc0);
+	for (i = 0; i < sizeof(scc_inittab); ++i)
+		out_8(sccc, scc_inittab[i]);
+
+	udbg_putc = udbg_scc_putc;
+	udbg_getc = udbg_scc_getc;
+	udbg_getc_poll = udbg_scc_getc_poll;
+
+	udbg_puts("Hello World !\n");
+
+ bail:
+	of_node_put(macio);
+	of_node_put(escc);
+	of_node_put(stdout);
+	of_node_put(ch_def);
+	of_node_put(ch_a);
+}
+
+#ifdef CONFIG_PPC64
+static void udbg_real_scc_putc(char c)
+{
+	while ((real_readb(sccc) & SCC_TXRDY) == 0)
+		;
+	real_writeb(c, sccd);
+	if (c == '\n')
+		udbg_real_scc_putc('\r');
+}
+
+void udbg_init_pmac_realmode(void)
+{
+	sccc = (volatile u8 __iomem *)0x80013020ul;
+	sccd = (volatile u8 __iomem *)0x80013030ul;
+
+	udbg_putc = udbg_real_scc_putc;
+	udbg_getc = NULL;
+	udbg_getc_poll = NULL;
+}
+#endif /* CONFIG_PPC64 */
Index: linux-serialfix/arch/powerpc/Kconfig
===================================================================
--- linux-serialfix.orig/arch/powerpc/Kconfig	2005-11-21 11:53:14.000000000 +1100
+++ linux-serialfix/arch/powerpc/Kconfig	2005-11-22 16:33:30.000000000 +1100
@@ -50,7 +50,7 @@
 
 config EARLY_PRINTK
 	bool
-	default y if PPC64
+	default y
 
 config COMPAT
 	bool
Index: linux-serialfix/arch/powerpc/kernel/btext.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/btext.c	2005-11-01 14:13:52.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/btext.c	2005-11-22 16:36:53.000000000 +1100
@@ -31,15 +31,18 @@
 static void draw_byte_16(unsigned char *bits, unsigned int *base, int rb);
 static void draw_byte_8(unsigned char *bits, unsigned int *base, int rb);
 
-static int g_loc_X;
-static int g_loc_Y;
-static int g_max_loc_X;
-static int g_max_loc_Y;
-
-static int dispDeviceRowBytes;
-static int dispDeviceDepth;
-static int dispDeviceRect[4];
-static unsigned char *dispDeviceBase, *logicalDisplayBase;
+#define __force_data __attribute__((__section__(".data")))
+
+static int g_loc_X __force_data;
+static int g_loc_Y __force_data;
+static int g_max_loc_X __force_data;
+static int g_max_loc_Y __force_data;
+
+static int dispDeviceRowBytes __force_data;
+static int dispDeviceDepth  __force_data;
+static int dispDeviceRect[4] __force_data;
+static unsigned char *dispDeviceBase __force_data;
+static unsigned char *logicalDisplayBase __force_data;
 
 unsigned long disp_BAT[2] __initdata = {0, 0};
 
@@ -47,7 +50,7 @@
 
 static unsigned char vga_font[cmapsz];
 
-int boot_text_mapped;
+int boot_text_mapped __force_data = 0;
 int force_printk_to_btext = 0;
 
 #ifdef CONFIG_PPC32
@@ -66,8 +69,7 @@
  * is really badly aligned, but I didn't encounter this case
  * yet.
  */
-void __init
-btext_prepare_BAT(void)
+void __init btext_prepare_BAT(void)
 {
 	unsigned long vaddr = KERNELBASE + 0x10000000;
 	unsigned long addr;
@@ -95,12 +97,13 @@
 }
 #endif
 
-/* This function will enable the early boot text when doing OF booting. This
- * way, xmon output should work too
+
+/* This function can be used to enable the early boot text when doing
+ * OF booting or within bootx init. It must be followed by a btext_unmap()
+ * call before the logical address becomes unuseable
  */
-void __init
-btext_setup_display(int width, int height, int depth, int pitch,
-		    unsigned long address)
+void __init btext_setup_display(int width, int height, int depth, int pitch,
+				unsigned long address)
 {
 	g_loc_X = 0;
 	g_loc_Y = 0;
@@ -116,6 +119,11 @@
 	boot_text_mapped = 1;
 }
 
+void __init btext_unmap(void)
+{
+	boot_text_mapped = 0;
+}
+
 /* Here's a small text engine to use during early boot
  * or for debugging purposes
  *
@@ -127,7 +135,7 @@
  *    changes.
  */
 
-void map_boot_text(void)
+static void map_boot_text(void)
 {
 	unsigned long base, offset, size;
 	unsigned char *vbase;
@@ -175,8 +183,9 @@
 	if (prop)
 		address = *prop;
 
-	/* FIXME: Add support for PCI reg properties */
-
+	/* FIXME: Add support for PCI reg properties. Right now, only
+	 * reliable on macs
+	 */
 	if (address == 0)
 		return -EINVAL;
 
@@ -184,7 +193,6 @@
 	g_loc_Y = 0;
 	g_max_loc_X = width / 8;
 	g_max_loc_Y = height / 16;
-	logicalDisplayBase = (unsigned char *)address;
 	dispDeviceBase = (unsigned char *)address;
 	dispDeviceRowBytes = pitch;
 	dispDeviceDepth = depth;
@@ -197,7 +205,7 @@
 	return 0;
 }
 
-void __init init_boot_display(void)
+int __init btext_find_display(int allow_nonstdout)
 {
 	char *name;
 	struct device_node *np = NULL; 
@@ -218,8 +226,8 @@
 	}
 	if (np)
 		rc = btext_initialize(np);
-	if (rc == 0)
-		return;
+	if (rc == 0 || !allow_nonstdout)
+		return rc;
 
 	for (np = NULL; (np = of_find_node_by_type(np, "display"));) {
 		if (get_property(np, "linux,opened", NULL)) {
@@ -228,8 +236,9 @@
 			printk("result: %d\n", rc);
 		}
 		if (rc == 0)
-			return;
+			break;
 	}
+	return rc;
 }
 
 /* Calc the base address of a given point (x,y) */
@@ -277,44 +286,83 @@
 
 void btext_clearscreen(void)
 {
-	unsigned long *base	= (unsigned long *)calc_base(0, 0);
+	unsigned int *base	= (unsigned int *)calc_base(0, 0);
 	unsigned long width 	= ((dispDeviceRect[2] - dispDeviceRect[0]) *
-					(dispDeviceDepth >> 3)) >> 3;
+					(dispDeviceDepth >> 3)) >> 2;
 	int i,j;
 
 	for (i=0; i<(dispDeviceRect[3] - dispDeviceRect[1]); i++)
 	{
-		unsigned long *ptr = base;
+		unsigned int *ptr = base;
 		for(j=width; j; --j)
 			*(ptr++) = 0;
-		base += (dispDeviceRowBytes >> 3);
+		base += (dispDeviceRowBytes >> 2);
+	}
+}
+
+void btext_flushscreen(void)
+{
+	unsigned int *base	= (unsigned int *)calc_base(0, 0);
+	unsigned long width 	= ((dispDeviceRect[2] - dispDeviceRect[0]) *
+					(dispDeviceDepth >> 3)) >> 2;
+	int i,j;
+
+	for (i=0; i < (dispDeviceRect[3] - dispDeviceRect[1]); i++)
+	{
+		unsigned int *ptr = base;
+		for(j = width; j > 0; j -= 8) {
+			__asm__ __volatile__ ("dcbst 0,%0" :: "r" (ptr));
+			ptr += 8;
+		}
+		base += (dispDeviceRowBytes >> 2);
 	}
+	__asm__ __volatile__ ("sync" ::: "memory");
 }
 
+void btext_flushline(void)
+{
+	unsigned int *base	= (unsigned int *)calc_base(0, g_loc_Y << 4);
+	unsigned long width 	= ((dispDeviceRect[2] - dispDeviceRect[0]) *
+					(dispDeviceDepth >> 3)) >> 2;
+	int i,j;
+
+	for (i=0; i < 16; i++)
+	{
+		unsigned int *ptr = base;
+		for(j = width; j > 0; j -= 8) {
+			__asm__ __volatile__ ("dcbst 0,%0" :: "r" (ptr));
+			ptr += 8;
+		}
+		base += (dispDeviceRowBytes >> 2);
+	}
+	__asm__ __volatile__ ("sync" ::: "memory");
+}
+
+
 #ifndef NO_SCROLL
 static void scrollscreen(void)
 {
-	unsigned long *src     	= (unsigned long *)calc_base(0,16);
-	unsigned long *dst     	= (unsigned long *)calc_base(0,0);
+	unsigned int *src     	= (unsigned int *)calc_base(0,16);
+	unsigned int *dst     	= (unsigned int *)calc_base(0,0);
 	unsigned long width    	= ((dispDeviceRect[2] - dispDeviceRect[0]) *
-				   (dispDeviceDepth >> 3)) >> 3;
+				   (dispDeviceDepth >> 3)) >> 2;
 	int i,j;
 
 	for (i=0; i<(dispDeviceRect[3] - dispDeviceRect[1] - 16); i++)
 	{
-		unsigned long *src_ptr = src;
-		unsigned long *dst_ptr = dst;
+		unsigned int *src_ptr = src;
+		unsigned int *dst_ptr = dst;
 		for(j=width; j; --j)
 			*(dst_ptr++) = *(src_ptr++);
-		src += (dispDeviceRowBytes >> 3);
-		dst += (dispDeviceRowBytes >> 3);
+		src += (dispDeviceRowBytes >> 2);
+		dst += (dispDeviceRowBytes >> 2);
 	}
 	for (i=0; i<16; i++)
 	{
-		unsigned long *dst_ptr = dst;
+		unsigned int *dst_ptr = dst;
 		for(j=width; j; --j)
 			*(dst_ptr++) = 0;
-		dst += (dispDeviceRowBytes >> 3);
+		dst += (dispDeviceRowBytes >> 2);
 	}
 }
 #endif /* ndef NO_SCROLL */
@@ -377,6 +425,14 @@
 		btext_drawchar(*c++);
 }
 
+void btext_drawtext(const char *c, unsigned int len)
+{
+	if (!boot_text_mapped)
+		return;
+	while (len--)
+		btext_drawchar(*c++);
+}
+
 void btext_drawhex(unsigned long v)
 {
 	char *hex_table = "0123456789abcdef";
Index: linux-serialfix/arch/powerpc/kernel/setup_32.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/setup_32.c	2005-11-22 16:33:30.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/setup_32.c	2005-11-22 16:33:30.000000000 +1100
@@ -40,6 +40,7 @@
 #include <asm/xmon.h>
 #include <asm/time.h>
 #include <asm/serial.h>
+#include <asm/udbg.h>
 
 #include "setup.h"
 
@@ -294,21 +295,11 @@
 
 	smp_setup_cpu_maps();
 
-#ifdef CONFIG_BOOTX_TEXT
-	init_boot_display();
-#endif
-
-#ifdef CONFIG_PPC_PMAC
-	/* This could be called "early setup arch", it must be done
-	 * now because xmon need it
-	 */
-	if (_machine == _MACH_Pmac)
-		pmac_feature_init();	/* New cool way */
-#endif
-
 #ifdef CONFIG_XMON_DEFAULT
 	xmon_init(1);
 #endif
+	/* Register early console */
+	register_early_udbg_console();
 
 #if defined(CONFIG_KGDB)
 	if (ppc_md.kgdb_map_scc)
Index: linux-serialfix/arch/powerpc/kernel/udbg_16550.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/udbg_16550.c	2005-11-22 16:33:30.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/udbg_16550.c	2005-11-22 16:33:30.000000000 +1100
@@ -47,7 +47,7 @@
 
 static volatile struct NS16550 __iomem *udbg_comport;
 
-static void udbg_550_putc(unsigned char c)
+static void udbg_550_putc(char c)
 {
 	if (udbg_comport) {
 		while ((in_8(&udbg_comport->lsr) & LSR_THRE) == 0)
@@ -69,7 +69,7 @@
 	return -1;
 }
 
-static unsigned char udbg_550_getc(void)
+static char udbg_550_getc(void)
 {
 	if (udbg_comport) {
 		while ((in_8(&udbg_comport->lsr) & LSR_DR) == 0)
Index: linux-serialfix/arch/powerpc/platforms/powermac/Makefile
===================================================================
--- linux-serialfix.orig/arch/powerpc/platforms/powermac/Makefile	2005-11-09 11:49:03.000000000 +1100
+++ linux-serialfix/arch/powerpc/platforms/powermac/Makefile	2005-11-22 16:33:30.000000000 +1100
@@ -7,3 +7,4 @@
 # ppc64 pmac doesn't define CONFIG_NVRAM but needs nvram stuff
 obj-$(CONFIG_PPC64)		+= nvram.o
 obj-$(CONFIG_SMP)		+= smp.o
+obj-$(CONFIG_PPC_MERGE)		+= udbg_scc.o udbg_adb.o
Index: linux-serialfix/arch/powerpc/platforms/powermac/setup.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/platforms/powermac/setup.c	2005-11-09 11:49:03.000000000 +1100
+++ linux-serialfix/arch/powerpc/platforms/powermac/setup.c	2005-11-22 16:33:30.000000000 +1100
@@ -76,6 +76,7 @@
 #include <asm/pmc.h>
 #include <asm/mpic.h>
 #include <asm/lmb.h>
+#include <asm/udbg.h>
 
 #include "pmac.h"
 
@@ -321,16 +322,6 @@
 	l2cr_init();
 #endif /* CONFIG_PPC32 */
 
-#ifdef CONFIG_PPC64
-	/* Probe motherboard chipset */
-	/* this is done earlier in setup_arch for 32-bit */
-	pmac_feature_init();
-
-	/* We can NAP */
-	powersave_nap = 1;
-	printk(KERN_INFO "Using native/NAP idle loop\n");
-#endif
-
 #ifdef CONFIG_KGDB
 	zs_kgdb_hook(0);
 #endif
@@ -621,13 +612,26 @@
 	 * and call ioremap
 	 */
 	hpte_init_native();
+#endif
 
-	/* Init SCC */
-	if (strstr(cmd_line, "sccdbg")) {
-		sccdbg = 1;
-		udbg_init_scc(NULL);
+	/* Enable early btext debug if requested */
+	if (strstr(cmd_line, "btextdbg")) {
+		udbg_adb_init_early();
+		register_early_udbg_console();
 	}
 
+	/* Probe motherboard chipset */
+	pmac_feature_init();
+
+	/* We can NAP */
+	powersave_nap = 1;
+	printk(KERN_INFO "Using native/NAP idle loop\n");
+
+	/* Initialize debug stuff */
+	udbg_scc_init(!!strstr(cmd_line, "sccdbg"));
+	udbg_adb_init(!!strstr(cmd_line, "btextdbg"));
+
+#ifdef CONFIG_PPC64
 	/* Setup interrupt mapping options */
 	ppc64_interrupt_controller = IC_OPEN_PIC;
 
@@ -637,19 +641,8 @@
 
 static void __init pmac_progress(char *s, unsigned short hex)
 {
-#ifdef CONFIG_PPC64
-	if (sccdbg) {
-		udbg_puts(s);
-		udbg_puts("\n");
-		return;
-	}
-#endif
-#ifdef CONFIG_BOOTX_TEXT
-	if (boot_text_mapped) {
-		btext_drawstring(s);
-		btext_drawchar('\n');
-	}
-#endif /* CONFIG_BOOTX_TEXT */
+	udbg_puts(s);
+	udbg_puts("\n");
 }
 
 /*
@@ -734,7 +727,8 @@
 }
 
 #ifdef CONFIG_PPC64
-static int pmac_probe_mode(struct pci_bus *bus)
+/* Move that to pci.c */
+static int pmac_pci_probe_mode(struct pci_bus *bus)
 {
 	struct device_node *node = bus->sysdata;
 
@@ -770,7 +764,7 @@
 	.check_legacy_ioport	= pmac_check_legacy_ioport,
 	.progress		= pmac_progress,
 #ifdef CONFIG_PPC64
-	.pci_probe_mode		= pmac_probe_mode,
+	.pci_probe_mode		= pmac_pci_probe_mode,
 	.idle_loop		= native_idle,
 	.enable_pmcs		= power4_enable_pmcs,
 #endif
Index: linux-serialfix/arch/powerpc/xmon/Makefile
===================================================================
--- linux-serialfix.orig/arch/powerpc/xmon/Makefile	2005-11-11 10:14:48.000000000 +1100
+++ linux-serialfix/arch/powerpc/xmon/Makefile	2005-11-22 16:33:30.000000000 +1100
@@ -3,9 +3,5 @@
 ifdef CONFIG_PPC64
 EXTRA_CFLAGS += -mno-minimal-toc
 endif
-
-obj-$(CONFIG_8xx)	+= start_8xx.o
-obj-$(CONFIG_6xx)	+= start_32.o
-obj-$(CONFIG_4xx)	+= start_32.o
-obj-$(CONFIG_PPC64)	+= start_64.o
-obj-y			+= xmon.o ppc-dis.o ppc-opc.o setjmp.o nonstdio.o
+obj-y			+= xmon.o ppc-dis.o ppc-opc.o setjmp.o start.o \
+			   nonstdio.o
Index: linux-serialfix/arch/powerpc/xmon/start.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-serialfix/arch/powerpc/xmon/start.c	2005-11-22 16:33:30.000000000 +1100
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ *      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/machdep.h>
+#include <asm/udbg.h>
+#include "nonstdio.h"
+
+void xmon_map_scc(void)
+{
+}
+
+int xmon_write(void *ptr, int nb)
+{
+	return udbg_write(ptr, nb);
+}
+
+int xmon_readchar(void)
+{
+	if (udbg_getc)
+		return udbg_getc();
+	return -1;
+}
+
+int xmon_read_poll(void)
+{
+	if (udbg_getc_poll)
+		return udbg_getc_poll();
+	return -1;
+}
Index: linux-serialfix/arch/powerpc/xmon/start_32.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/xmon/start_32.c	2005-11-11 10:14:48.000000000 +1100
+++ /dev/null	1970-01-01 00:00:00.000000000 +0000
@@ -1,441 +0,0 @@
-/*
- * Copyright (C) 1996 Paul Mackerras.
- */
-#include <linux/config.h>
-#include <linux/string.h>
-#include <asm/machdep.h>
-#include <asm/io.h>
-#include <asm/page.h>
-#include <linux/adb.h>
-#include <linux/pmu.h>
-#include <linux/cuda.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/bitops.h>
-#include <asm/xmon.h>
-#include <asm/prom.h>
-#include <asm/bootx.h>
-#include <asm/machdep.h>
-#include <asm/errno.h>
-#include <asm/pmac_feature.h>
-#include <asm/processor.h>
-#include <asm/delay.h>
-#include <asm/btext.h>
-#include <asm/time.h>
-#include "nonstdio.h"
-
-static volatile unsigned char __iomem *sccc, *sccd;
-unsigned int TXRDY, RXRDY, DLAB;
-
-static int use_serial;
-static int use_screen;
-static int via_modem;
-static int xmon_use_sccb;
-static struct device_node *channel_node;
-
-void buf_access(void)
-{
-	if (DLAB)
-		sccd[3] &= ~DLAB;	/* reset DLAB */
-}
-
-extern int adb_init(void);
-
-#ifdef CONFIG_PPC_CHRP
-/*
- * This looks in the "ranges" property for the primary PCI host bridge
- * to find the physical address of the start of PCI/ISA I/O space.
- * It is basically a cut-down version of pci_process_bridge_OF_ranges.
- */
-static unsigned long chrp_find_phys_io_base(void)
-{
-	struct device_node *node;
-	unsigned int *ranges;
-	unsigned long base = CHRP_ISA_IO_BASE;
-	int rlen = 0;
-	int np;
-
-	node = find_devices("isa");
-	if (node != NULL) {
-		node = node->parent;
-		if (node == NULL || node->type == NULL
-		    || strcmp(node->type, "pci") != 0)
-			node = NULL;
-	}
-	if (node == NULL)
-		node = find_devices("pci");
-	if (node == NULL)
-		return base;
-
-	ranges = (unsigned int *) get_property(node, "ranges", &rlen);
-	np = prom_n_addr_cells(node) + 5;
-	while ((rlen -= np * sizeof(unsigned int)) >= 0) {
-		if ((ranges[0] >> 24) == 1 && ranges[2] == 0) {
-			/* I/O space starting at 0, grab the phys base */
-			base = ranges[np - 3];
-			break;
-		}
-		ranges += np;
-	}
-	return base;
-}
-#endif /* CONFIG_PPC_CHRP */
-
-void xmon_map_scc(void)
-{
-#ifdef CONFIG_PPC_MULTIPLATFORM
-	volatile unsigned char __iomem *base;
-
-	if (_machine == _MACH_Pmac) {
-		struct device_node *np;
-		unsigned long addr;
-#ifdef CONFIG_BOOTX_TEXT
-		if (!use_screen && !use_serial
-		    && !machine_is_compatible("iMac")) {
-			/* see if there is a keyboard in the device tree
-			   with a parent of type "adb" */
-			for (np = find_devices("keyboard"); np; np = np->next)
-				if (np->parent && np->parent->type
-				    && strcmp(np->parent->type, "adb") == 0)
-					break;
-
-			/* needs to be hacked if xmon_printk is to be used
-			   from within find_via_pmu() */
-#ifdef CONFIG_ADB_PMU
-			if (np != NULL && boot_text_mapped && find_via_pmu())
-				use_screen = 1;
-#endif
-#ifdef CONFIG_ADB_CUDA
-			if (np != NULL && boot_text_mapped && find_via_cuda())
-				use_screen = 1;
-#endif
-		}
-		if (!use_screen && (np = find_devices("escc")) != NULL) {
-			/*
-			 * look for the device node for the serial port
-			 * we're using and see if it says it has a modem
-			 */
-			char *name = xmon_use_sccb? "ch-b": "ch-a";
-			char *slots;
-			int l;
-
-			np = np->child;
-			while (np != NULL && strcmp(np->name, name) != 0)
-				np = np->sibling;
-			if (np != NULL) {
-				/* XXX should parse this properly */
-				channel_node = np;
-				slots = get_property(np, "slot-names", &l);
-				if (slots != NULL && l >= 10
-				    && strcmp(slots+4, "Modem") == 0)
-					via_modem = 1;
-			}
-		}
-		btext_drawstring("xmon uses ");
-		if (use_screen)
-			btext_drawstring("screen and keyboard\n");
-		else {
-			if (via_modem)
-				btext_drawstring("modem on ");
-			btext_drawstring(xmon_use_sccb? "printer": "modem");
-			btext_drawstring(" port\n");
-		}
-
-#endif /* CONFIG_BOOTX_TEXT */
-
-#ifdef CHRP_ESCC
-		addr = 0xc1013020;
-#else
-		addr = 0xf3013020;
-#endif
-		TXRDY = 4;
-		RXRDY = 1;
-	
-		np = find_devices("mac-io");
-		if (np && np->n_addrs)
-			addr = np->addrs[0].address + 0x13020;
-		base = (volatile unsigned char *) ioremap(addr & PAGE_MASK, PAGE_SIZE);
-		sccc = base + (addr & ~PAGE_MASK);
-		sccd = sccc + 0x10;
-
-	} else {
-		base = (volatile unsigned char *) isa_io_base;
-
-#ifdef CONFIG_PPC_CHRP
-		if (_machine == _MACH_chrp)
-			base = (volatile unsigned char __iomem *)
-				ioremap(chrp_find_phys_io_base(), 0x1000);
-#endif
-
-		sccc = base + 0x3fd;
-		sccd = base + 0x3f8;
-		if (xmon_use_sccb) {
-			sccc -= 0x100;
-			sccd -= 0x100;
-		}
-		TXRDY = 0x20;
-		RXRDY = 1;
-		DLAB = 0x80;
-	}
-#elif defined(CONFIG_GEMINI)
-	/* should already be mapped by the kernel boot */
-	sccc = (volatile unsigned char __iomem *) 0xffeffb0d;
-	sccd = (volatile unsigned char __iomem *) 0xffeffb08;
-	TXRDY = 0x20;
-	RXRDY = 1;
-	DLAB = 0x80;
-#elif defined(CONFIG_405GP)
-	sccc = (volatile unsigned char __iomem *)0xef600305;
-	sccd = (volatile unsigned char __iomem *)0xef600300;
-	TXRDY = 0x20;
-	RXRDY = 1;
-	DLAB = 0x80;
-#endif /* platform */
-}
-
-static int scc_initialized = 0;
-
-void xmon_init_scc(void);
-extern void cuda_poll(void);
-
-static inline void do_poll_adb(void)
-{
-#ifdef CONFIG_ADB_PMU
-	if (sys_ctrler == SYS_CTRLER_PMU)
-		pmu_poll_adb();
-#endif /* CONFIG_ADB_PMU */
-#ifdef CONFIG_ADB_CUDA
-	if (sys_ctrler == SYS_CTRLER_CUDA)
-		cuda_poll();
-#endif /* CONFIG_ADB_CUDA */
-}
-
-int xmon_write(void *ptr, int nb)
-{
-	char *p = ptr;
-	int i, c, ct;
-
-#ifdef CONFIG_SMP
-	static unsigned long xmon_write_lock;
-	int lock_wait = 1000000;
-	int locked;
-
-	while ((locked = test_and_set_bit(0, &xmon_write_lock)) != 0)
-		if (--lock_wait == 0)
-			break;
-#endif
-
-#ifdef CONFIG_BOOTX_TEXT
-	if (use_screen) {
-		/* write it on the screen */
-		for (i = 0; i < nb; ++i)
-			btext_drawchar(*p++);
-		goto out;
-	}
-#endif
-	if (!scc_initialized)
-		xmon_init_scc();
-	ct = 0;
-	for (i = 0; i < nb; ++i) {
-		while ((*sccc & TXRDY) == 0)
-			do_poll_adb();
-		c = p[i];
-		if (c == '\n' && !ct) {
-			c = '\r';
-			ct = 1;
-			--i;
-		} else {
-			ct = 0;
-		}
-		buf_access();
-		*sccd = c;
-		eieio();
-	}
-
- out:
-#ifdef CONFIG_SMP
-	if (!locked)
-		clear_bit(0, &xmon_write_lock);
-#endif
-	return nb;
-}
-
-int xmon_wants_key;
-int xmon_adb_keycode;
-
-#ifdef CONFIG_BOOTX_TEXT
-static int xmon_adb_shiftstate;
-
-static unsigned char xmon_keytab[128] =
-	"asdfhgzxcv\000bqwer"				/* 0x00 - 0x0f */
-	"yt123465=97-80]o"				/* 0x10 - 0x1f */
-	"u[ip\rlj'k;\\,/nm."				/* 0x20 - 0x2f */
-	"\t `\177\0\033\0\0\0\0\0\0\0\0\0\0"		/* 0x30 - 0x3f */
-	"\0.\0*\0+\0\0\0\0\0/\r\0-\0"			/* 0x40 - 0x4f */
-	"\0\0000123456789\0\0\0";			/* 0x50 - 0x5f */
-
-static unsigned char xmon_shift_keytab[128] =
-	"ASDFHGZXCV\000BQWER"				/* 0x00 - 0x0f */
-	"YT!@#$^%+(&_*)}O"				/* 0x10 - 0x1f */
-	"U{IP\rLJ\"K:|<?NM>"				/* 0x20 - 0x2f */
-	"\t ~\177\0\033\0\0\0\0\0\0\0\0\0\0"		/* 0x30 - 0x3f */
-	"\0.\0*\0+\0\0\0\0\0/\r\0-\0"			/* 0x40 - 0x4f */
-	"\0\0000123456789\0\0\0";			/* 0x50 - 0x5f */
-
-static int xmon_get_adb_key(void)
-{
-	int k, t, on;
-
-	xmon_wants_key = 1;
-	for (;;) {
-		xmon_adb_keycode = -1;
-		t = 0;
-		on = 0;
-		do {
-			if (--t < 0) {
-				on = 1 - on;
-				btext_drawchar(on? 0xdb: 0x20);
-				btext_drawchar('\b');
-				t = 200000;
-			}
-			do_poll_adb();
-		} while (xmon_adb_keycode == -1);
-		k = xmon_adb_keycode;
-		if (on)
-			btext_drawstring(" \b");
-
-		/* test for shift keys */
-		if ((k & 0x7f) == 0x38 || (k & 0x7f) == 0x7b) {
-			xmon_adb_shiftstate = (k & 0x80) == 0;
-			continue;
-		}
-		if (k >= 0x80)
-			continue;	/* ignore up transitions */
-		k = (xmon_adb_shiftstate? xmon_shift_keytab: xmon_keytab)[k];
-		if (k != 0)
-			break;
-	}
-	xmon_wants_key = 0;
-	return k;
-}
-#endif /* CONFIG_BOOTX_TEXT */
-
-int xmon_readchar(void)
-{
-#ifdef CONFIG_BOOTX_TEXT
-	if (use_screen)
-		return xmon_get_adb_key();
-#endif
-	if (!scc_initialized)
-		xmon_init_scc();
-	while ((*sccc & RXRDY) == 0)
-		do_poll_adb();
-	buf_access();
-	return *sccd;
-}
-
-int xmon_read_poll(void)
-{
-	if ((*sccc & RXRDY) == 0) {
-		do_poll_adb();
-		return -1;
-	}
-	buf_access();
-	return *sccd;
-}
-
-static unsigned char scc_inittab[] = {
-    13, 0,		/* set baud rate divisor */
-    12, 1,
-    14, 1,		/* baud rate gen enable, src=rtxc */
-    11, 0x50,		/* clocks = br gen */
-    5,  0xea,		/* tx 8 bits, assert DTR & RTS */
-    4,  0x46,		/* x16 clock, 1 stop */
-    3,  0xc1,		/* rx enable, 8 bits */
-};
-
-void xmon_init_scc(void)
-{
-	if ( _machine == _MACH_chrp )
-	{
-		sccd[3] = 0x83; eieio();	/* LCR = 8N1 + DLAB */
-		sccd[0] = 12; eieio();		/* DLL = 9600 baud */
-		sccd[1] = 0; eieio();
-		sccd[2] = 0; eieio();		/* FCR = 0 */
-		sccd[3] = 3; eieio();		/* LCR = 8N1 */
-		sccd[1] = 0; eieio();		/* IER = 0 */
-	}
-	else if ( _machine == _MACH_Pmac )
-	{
-		int i, x;
-		unsigned long timeout;
-
-		if (channel_node != 0)
-			pmac_call_feature(
-				PMAC_FTR_SCC_ENABLE,
-				channel_node,
-				PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1);
-			printk(KERN_INFO "Serial port locked ON by debugger !\n");
-		if (via_modem && channel_node != 0) {
-			unsigned int t0;
-
-			pmac_call_feature(
-				PMAC_FTR_MODEM_ENABLE,
-				channel_node, 0, 1);
-			printk(KERN_INFO "Modem powered up by debugger !\n");
-			t0 = get_tbl();
-			timeout = 3 * tb_ticks_per_sec;
-			if (timeout == 0)
-				/* assume 25MHz if tb_ticks_per_sec not set */
-				timeout = 75000000;
-			while (get_tbl() - t0 < timeout)
-				eieio();
-		}
-		/* use the B channel if requested */
-		if (xmon_use_sccb) {
-			sccc = (volatile unsigned char *)
-				((unsigned long)sccc & ~0x20);
-			sccd = sccc + 0x10;
-		}
-		for (i = 20000; i != 0; --i) {
-			x = *sccc; eieio();
-		}
-		*sccc = 9; eieio();		/* reset A or B side */
-		*sccc = ((unsigned long)sccc & 0x20)? 0x80: 0x40; eieio();
-		for (i = 0; i < sizeof(scc_inittab); ++i) {
-			*sccc = scc_inittab[i];
-			eieio();
-		}
-	}
-	scc_initialized = 1;
-	if (via_modem) {
-		for (;;) {
-			xmon_write("ATE1V1\r", 7);
-			if (xmon_expect("OK", 5)) {
-				xmon_write("ATA\r", 4);
-				if (xmon_expect("CONNECT", 40))
-					break;
-			}
-			xmon_write("+++", 3);
-			xmon_expect("OK", 3);
-		}
-	}
-}
-
-void xmon_enter(void)
-{
-#ifdef CONFIG_ADB_PMU
-	if (_machine == _MACH_Pmac) {
-		pmu_suspend();
-	}
-#endif
-}
-
-void xmon_leave(void)
-{
-#ifdef CONFIG_ADB_PMU
-	if (_machine == _MACH_Pmac) {
-		pmu_resume();
-	}
-#endif
-}
Index: linux-serialfix/arch/powerpc/xmon/start_64.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/xmon/start_64.c	2005-11-11 10:14:48.000000000 +1100
+++ /dev/null	1970-01-01 00:00:00.000000000 +0000
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 1996 Paul Mackerras.
- *
- *      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/machdep.h>
-#include <asm/udbg.h>
-#include "nonstdio.h"
-
-void xmon_map_scc(void)
-{
-}
-
-int xmon_write(void *ptr, int nb)
-{
-	return udbg_write(ptr, nb);
-}
-
-int xmon_readchar(void)
-{
-	if (udbg_getc)
-		return udbg_getc();
-	return -1;
-}
-
-int xmon_read_poll(void)
-{
-	if (udbg_getc_poll)
-		return udbg_getc_poll();
-	return -1;
-}
Index: linux-serialfix/arch/powerpc/xmon/start_8xx.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/xmon/start_8xx.c	2005-11-11 10:14:48.000000000 +1100
+++ /dev/null	1970-01-01 00:00:00.000000000 +0000
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 1996 Paul Mackerras.
- * Copyright (C) 2000 Dan Malek.
- * Quick hack of Paul's code to make XMON work on 8xx processors.  Lots
- * of assumptions, like the SMC1 is used, it has been initialized by the
- * loader at some point, and we can just stuff and suck bytes.
- * We rely upon the 8xx uart driver to support us, as the interface
- * changes between boot up and operational phases of the kernel.
- */
-#include <linux/string.h>
-#include <asm/machdep.h>
-#include <asm/io.h>
-#include <asm/page.h>
-#include <linux/kernel.h>
-#include <asm/8xx_immap.h>
-#include <asm/mpc8xx.h>
-#include <asm/commproc.h>
-#include "nonstdio.h"
-
-extern int xmon_8xx_write(char *str, int nb);
-extern int xmon_8xx_read_poll(void);
-extern int xmon_8xx_read_char(void);
-
-void xmon_map_scc(void)
-{
-	cpmp = (cpm8xx_t *)&(((immap_t *)IMAP_ADDR)->im_cpm);
-}
-
-void xmon_init_scc(void);
-
-int xmon_write(void *ptr, int nb)
-{
-	return(xmon_8xx_write(ptr, nb));
-}
-
-int xmon_readchar(void)
-{
-	return xmon_8xx_read_char();
-}
-
-int xmon_read_poll(void)
-{
-	return(xmon_8xx_read_poll());
-}
Index: linux-serialfix/include/asm-powerpc/btext.h
===================================================================
--- linux-serialfix.orig/include/asm-powerpc/btext.h	2005-11-15 13:31:58.000000000 +1100
+++ linux-serialfix/include/asm-powerpc/btext.h	2005-11-22 16:35:41.000000000 +1100
@@ -7,21 +7,22 @@
 #define __PPC_BTEXT_H
 #ifdef __KERNEL__
 
-extern void btext_clearscreen(void);
-extern void btext_flushscreen(void);
-
-extern int boot_text_mapped;
-
-extern int btext_initialize(struct device_node *np);
-
-extern void map_boot_text(void);
-extern void init_boot_display(void);
+extern int btext_find_display(int allow_nonstdout);
 extern void btext_update_display(unsigned long phys, int width, int height,
 				 int depth, int pitch);
+extern void btext_setup_display(int width, int height, int depth, int pitch,
+				unsigned long address);
+extern void btext_prepare_BAT(void);
+extern void btext_unmap(void);
 
 extern void btext_drawchar(char c);
 extern void btext_drawstring(const char *str);
 extern void btext_drawhex(unsigned long v);
+extern void btext_drawtext(const char *c, unsigned int len);
+
+extern void btext_clearscreen(void);
+extern void btext_flushscreen(void);
+extern void btext_flushline(void);
 
 #endif /* __KERNEL__ */
 #endif /* __PPC_BTEXT_H */
Index: linux-serialfix/include/asm-powerpc/udbg.h
===================================================================
--- linux-serialfix.orig/include/asm-powerpc/udbg.h	2005-11-22 16:33:30.000000000 +1100
+++ linux-serialfix/include/asm-powerpc/udbg.h	2005-11-22 16:33:30.000000000 +1100
@@ -13,8 +13,8 @@
 #include <linux/compiler.h>
 #include <linux/init.h>
 
-extern void (*udbg_putc)(unsigned char c);
-extern unsigned char (*udbg_getc)(void);
+extern void (*udbg_putc)(char c);
+extern char (*udbg_getc)(void);
 extern int (*udbg_getc_poll)(void);
 
 extern void udbg_puts(const char *s);
@@ -30,5 +30,8 @@
 					  unsigned int clock);
 
 struct device_node;
-extern void udbg_init_scc(struct device_node *np);
+extern void udbg_scc_init(int force_scc);
+extern int udbg_adb_init(int force_btext);
+extern void udbg_adb_init_early(void);
+
 #endif /* _ASM_POWERPC_UDBG_H */
Index: linux-serialfix/arch/powerpc/kernel/setup_64.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/setup_64.c	2005-11-22 16:33:30.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/setup_64.c	2005-11-22 16:33:30.000000000 +1100
@@ -466,10 +466,6 @@
 	 */
 	finish_device_tree();
 
-#ifdef CONFIG_BOOTX_TEXT
-	init_boot_display();
-#endif
-
 	/*
 	 * Initialize xmon
 	 */
Index: linux-serialfix/arch/powerpc/platforms/powermac/feature.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/platforms/powermac/feature.c	2005-11-17 14:06:25.000000000 +1100
+++ linux-serialfix/arch/powerpc/platforms/powermac/feature.c	2005-11-22 16:33:30.000000000 +1100
@@ -2596,6 +2596,8 @@
  */
 static void __init probe_uninorth(void)
 {
+	u32 *addrp;
+	phys_addr_t address;
 	unsigned long actrl;
 
 	/* Locate core99 Uni-N */
@@ -2605,20 +2607,23 @@
 		uninorth_node = of_find_node_by_name(NULL, "u3");
 		uninorth_u3 = 1;
 	}
-	if (uninorth_node && uninorth_node->n_addrs > 0) {
-		unsigned long address = uninorth_node->addrs[0].address;
-		uninorth_base = ioremap(address, 0x40000);
-		uninorth_rev = in_be32(UN_REG(UNI_N_VERSION));
-		if (uninorth_u3)
-			u3_ht = ioremap(address + U3_HT_CONFIG_BASE, 0x1000);
-	} else
-		uninorth_node = NULL;
+	if (uninorth_node == NULL)
+		return;
 
-	if (!uninorth_node)
+	addrp = (u32 *)get_property(uninorth_node, "reg", NULL);
+	if (addrp == NULL)
+		return;
+	address = of_translate_address(uninorth_node, addrp);
+	if (address == 0)
 		return;
+	uninorth_base = ioremap(address, 0x40000);
+	uninorth_rev = in_be32(UN_REG(UNI_N_VERSION));
+	if (uninorth_u3)
+		u3_ht = ioremap(address + U3_HT_CONFIG_BASE, 0x1000);
 
-	printk(KERN_INFO "Found %s memory controller & host bridge, revision: %d\n",
-	       uninorth_u3 ? "U3" : "UniNorth", uninorth_rev);
+	printk(KERN_INFO "Found %s memory controller & host bridge,"
+	       " revision: %d\n", uninorth_u3 ? "U3" : "UniNorth",
+	       uninorth_rev);
 	printk(KERN_INFO "Mapped at 0x%08lx\n", (unsigned long)uninorth_base);
 
 	/* Set the arbitrer QAck delay according to what Apple does
@@ -2642,18 +2647,17 @@
 {
 	struct device_node*	node;
 	int			i;
-	volatile u32 __iomem *	base;
-	u32*			revp;
+	volatile u32 __iomem	*base;
+	u32			*addrp, *revp;
+	phys_addr_t		addr;
+	u64			size;
 
-	node = find_devices(name);
-	if (!node || !node->n_addrs)
-		return;
-	if (compat)
-		do {
-			if (device_is_compatible(node, compat))
-				break;
-			node = node->next;
-		} while (node);
+	for (node = NULL; (node = of_find_node_by_name(node, name)) != NULL;) {
+		if (!compat)
+			break;
+		if (device_is_compatible(node, compat))
+			break;
+	}
 	if (!node)
 		return;
 	for(i=0; i<MAX_MACIO_CHIPS; i++) {
@@ -2662,14 +2666,28 @@
 		if (macio_chips[i].of_node == node)
 			return;
 	}
+
 	if (i >= MAX_MACIO_CHIPS) {
 		printk(KERN_ERR "pmac_feature: Please increase MAX_MACIO_CHIPS !\n");
 		printk(KERN_ERR "pmac_feature: %s skipped\n", node->full_name);
 		return;
 	}
-	base = ioremap(node->addrs[0].address, node->addrs[0].size);
+	addrp = of_get_pci_addr(node, 0, &size);
+	if (addrp == NULL) {
+		printk(KERN_ERR "pmac_feature: %s: can't find base !\n",
+		       node->full_name);
+		return;
+	}
+	addr = of_translate_address(node, addrp);
+	if (addr == 0) {
+		printk(KERN_ERR "pmac_feature: %s, can't translate base !\n",
+		       node->full_name);
+		return;
+	}
+	base = ioremap(addr, (unsigned long)size);
 	if (!base) {
-		printk(KERN_ERR "pmac_feature: Can't map mac-io chip !\n");
+		printk(KERN_ERR "pmac_feature: %s, can't map mac-io chip !\n",
+		       node->full_name);
 		return;
 	}
 	if (type == macio_keylargo) {
Index: linux-serialfix/arch/powerpc/platforms/powermac/low_i2c.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/platforms/powermac/low_i2c.c	2005-11-01 14:13:53.000000000 +1100
+++ linux-serialfix/arch/powerpc/platforms/powermac/low_i2c.c	2005-11-22 16:33:30.000000000 +1100
@@ -36,7 +36,7 @@
 
 #ifdef DEBUG
 #define DBG(x...) do {\
-		printk(KERN_DEBUG "KW:" x);	\
+		printk(KERN_DEBUG "low_i2c:" x);	\
 	} while(0)
 #else
 #define DBG(x...)
@@ -342,7 +342,7 @@
 static void keywest_low_i2c_add(struct device_node *np)
 {
 	struct low_i2c_host	*host = find_low_i2c_host(NULL);
-	u32			*psteps, *prate, steps, aoffset = 0;
+	u32			*psteps, *prate, *addrp, steps;
 	struct device_node	*parent;
 
 	if (host == NULL) {
@@ -352,6 +352,16 @@
 	}
 	memset(host, 0, sizeof(*host));
 
+	/* Apple is kind enough to provide a valid AAPL,address property
+	 * on all i2c keywest nodes so far ... we would have to fallback
+	 * to macio parsing if that wasn't the case
+	 */
+	addrp = (u32 *)get_property(np, "AAPL,address", NULL);
+	if (addrp == NULL) {
+		printk(KERN_ERR "low_i2c: Can't find address for %s\n",
+		       np->full_name);
+		return;
+	}
 	init_MUTEX(&host->mutex);
 	host->np = of_node_get(np);	
 	psteps = (u32 *)get_property(np, "AAPL,address-step", NULL);
@@ -360,12 +370,10 @@
 		steps >>= 1;
 	parent = of_get_parent(np);
 	host->num_channels = 1;
-	if (parent && parent->name[0] == 'u') {
+	if (parent && parent->name[0] == 'u')
 		host->num_channels = 2;
-		aoffset = 3;
-	}
 	/* Select interface rate */
-	host->speed = KW_I2C_MODE_100KHZ;
+	host->speed = KW_I2C_MODE_25KHZ;
 	prate = (u32 *)get_property(np, "AAPL,i2c-rate", NULL);
 	if (prate) switch(*prate) {
 	case 100:
@@ -379,9 +387,12 @@
 		break;
 	}	
 
+	printk(KERN_INFO "low_i2c: Bus %s found at 0x%08x, %d channels,"
+	       " speed = %d KHz\n",
+	       np->full_name, *addrp, host->num_channels, prate ? *prate : 25);
+
 	host->mode = pmac_low_i2c_mode_std;
-	host->base = ioremap(np->addrs[0].address + aoffset,
-						np->addrs[0].size);
+	host->base = ioremap((*addrp), 0x1000);
 	host->func = keywest_low_i2c_func;
 }
 
Index: linux-serialfix/arch/powerpc/platforms/powermac/pic.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/platforms/powermac/pic.c	2005-11-11 10:14:48.000000000 +1100
+++ linux-serialfix/arch/powerpc/platforms/powermac/pic.c	2005-11-22 16:33:30.000000000 +1100
@@ -459,7 +459,7 @@
 			mpic_setup_cascade(irqctrler2->intrs[0].line,
 					   pmac_u3_cascade, mpic2);
 		}
-#if defined(CONFIG_XMON) && defined(CONFIG_PPC32)
+#ifdef CONFIG_XMON
 		{
 			struct device_node* pswitch;
 			int nmi_irq;
Index: linux-serialfix/arch/powerpc/platforms/pseries/lpar.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/platforms/pseries/lpar.c	2005-11-22 16:33:30.000000000 +1100
+++ linux-serialfix/arch/powerpc/platforms/pseries/lpar.c	2005-11-22 16:33:30.000000000 +1100
@@ -61,7 +61,7 @@
 int vtermno;	/* virtual terminal# for udbg  */
 
 #define __ALIGNED__ __attribute__((__aligned__(sizeof(long))))
-static void udbg_hvsi_putc(unsigned char c)
+static void udbg_hvsi_putc(char c)
 {
 	/* packet's seqno isn't used anyways */
 	uint8_t packet[] __ALIGNED__ = { 0xff, 5, 0, 0, c };
@@ -112,7 +112,7 @@
 	return ch;
 }
 
-static unsigned char udbg_hvsi_getc(void)
+static char udbg_hvsi_getc(void)
 {
 	int ch;
 	for (;;) {
@@ -128,7 +128,7 @@
 	}
 }
 
-static void udbg_putcLP(unsigned char c)
+static void udbg_putcLP(char c)
 {
 	char buf[16];
 	unsigned long rc;
@@ -173,7 +173,7 @@
 	return ch;
 }
 
-static unsigned char udbg_getcLP(void)
+static char udbg_getcLP(void)
 {
 	int ch;
 	for (;;) {
Index: linux-serialfix/arch/powerpc/kernel/head_32.S
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/head_32.S	2005-11-14 10:41:58.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/head_32.S	2005-11-22 16:37:57.000000000 +1100
@@ -153,6 +153,9 @@
 	bl	flush_tlbs
 
 	bl	initial_bats
+#if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT)
+	bl	setup_disp_bat
+#endif
 
 /*
  * Call setup_cpu for CPU 0 and initialize 6xx Idle
@@ -1306,6 +1309,32 @@
 	blr
 
 
+#if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT)
+setup_disp_bat:
+	/*
+	 * setup the display bat prepared for us in prom.c
+	 */
+	mflr	r8
+	bl	reloc_offset
+	mtlr	r8
+	addis	r8,r3,disp_BAT@ha
+	addi	r8,r8,disp_BAT@l
+	cmpwi	cr0,r8,0
+	beqlr
+	lwz	r11,0(r8)
+	lwz	r8,4(r8)
+	mfspr	r9,SPRN_PVR
+	rlwinm	r9,r9,16,16,31		/* r9 = 1 for 601, 4 for 604 */
+	cmpwi	0,r9,1
+	beq	1f
+	mtspr	SPRN_DBAT3L,r8
+	mtspr	SPRN_DBAT3U,r11
+	blr
+1:	mtspr	SPRN_IBAT3L,r8
+	mtspr	SPRN_IBAT3U,r11
+	blr
+#endif /* !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) */
+
 #ifdef CONFIG_8260
 /* Jump into the system reset for the rom.
  * We first disable the MMU, and then jump to the ROM reset address.
Index: linux-serialfix/arch/powerpc/mm/init_32.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/mm/init_32.c	2005-11-11 10:14:48.000000000 +1100
+++ linux-serialfix/arch/powerpc/mm/init_32.c	2005-11-22 16:37:32.000000000 +1100
@@ -188,6 +188,11 @@
 
 	if (ppc_md.progress)
 		ppc_md.progress("MMU:exit", 0x211);
+
+	/* From now on, btext is no longer BAT mapped if it was at all */
+#ifdef CONFIG_BOOTX_TEXT
+	btext_unmap();
+#endif
 }
 
 /* This is only called until mem_init is done. */
Index: linux-serialfix/drivers/i2c/busses/i2c-keywest.c
===================================================================
--- linux-serialfix.orig/drivers/i2c/busses/i2c-keywest.c	2005-11-01 14:13:54.000000000 +1100
+++ linux-serialfix/drivers/i2c/busses/i2c-keywest.c	2005-11-22 16:34:13.000000000 +1100
@@ -505,16 +505,23 @@
 create_iface(struct device_node *np, struct device *dev)
 {
 	unsigned long steps;
-	unsigned bsteps, tsize, i, nchan, addroffset;
+	unsigned bsteps, tsize, i, nchan;
 	struct keywest_iface* iface;
-	u32 *psteps, *prate;
+	u32 *psteps, *prate, *addrp;
 	int rc;
 
-	if (np->n_intrs < 1 || np->n_addrs < 1) {
-		printk(KERN_ERR "%s: Missing interrupt or address !\n",
+	if (np->n_intrs < 1) {
+		printk(KERN_ERR "%s: Missing interrupt !\n",
 		       np->full_name);
 		return -ENODEV;
 	}
+	addrp = (u32 *)get_property(np, "AAPL,address", NULL);
+	if (addrp == NULL) {
+		printk(KERN_ERR "%s: Can't find address !\n",
+		       np->full_name);
+		return -ENODEV;
+	}
+
 	if (pmac_low_i2c_lock(np))
 		return -ENODEV;
 
@@ -525,13 +532,10 @@
 	for (bsteps = 0; (steps & 0x01) == 0; bsteps++)
 		steps >>= 1;
 
-	if (np->parent->name[0] == 'u') {
+	if (np->parent->name[0] == 'u')
 		nchan = 2;
-		addroffset = 3;
-	} else {
-		addroffset = 0;
+	else
 		nchan = 1;
-	}
 
 	tsize = sizeof(struct keywest_iface) +
 		(sizeof(struct keywest_chan) + 4) * nchan;
@@ -550,8 +554,7 @@
 	iface->irq = np->intrs[0].line;
 	iface->channels = (struct keywest_chan *)
 		(((unsigned long)(iface + 1) + 3UL) & ~3UL);
-	iface->base = ioremap(np->addrs[0].address + addroffset,
-						np->addrs[0].size);
+	iface->base = ioremap(*addrp, 0x1000);
 	if (!iface->base) {
 		printk(KERN_ERR "i2c-keywest: can't map inteface !\n");
 		kfree(iface);
Index: linux-serialfix/drivers/macintosh/via-cuda.c
===================================================================
--- linux-serialfix.orig/drivers/macintosh/via-cuda.c	2005-11-01 14:13:54.000000000 +1100
+++ linux-serialfix/drivers/macintosh/via-cuda.c	2005-11-22 16:44:29.000000000 +1100
@@ -127,39 +127,34 @@
 #endif /* CONFIG_ADB */
 
 #ifdef CONFIG_PPC
-int __init
-find_via_cuda(void)
+int __init find_via_cuda(void)
 {
-    int err;
     struct adb_request req;
+    phys_addr_t taddr;
+    u32 *reg;
+    int err;
 
     if (vias != 0)
 	return 1;
-    vias = find_devices("via-cuda");
+    vias = of_find_node_by_name(NULL, "via-cuda");
     if (vias == 0)
 	return 0;
-    if (vias->next != 0)
-	printk(KERN_WARNING "Warning: only using 1st via-cuda\n");
-
-#if 0
-    { int i;
-
-    printk("find_via_cuda: node = %p, addrs =", vias->node);
-    for (i = 0; i < vias->n_addrs; ++i)
-	printk(" %x(%x)", vias->addrs[i].address, vias->addrs[i].size);
-    printk(", intrs =");
-    for (i = 0; i < vias->n_intrs; ++i)
-	printk(" %x", vias->intrs[i].line);
-    printk("\n"); }
-#endif
 
-    if (vias->n_addrs != 1 || vias->n_intrs != 1) {
-	printk(KERN_ERR "via-cuda: expecting 1 address (%d) and 1 interrupt (%d)\n",
-	       vias->n_addrs, vias->n_intrs);
-	if (vias->n_addrs < 1 || vias->n_intrs < 1)
-	    return 0;
+    reg = (u32 *)get_property(vias, "reg", NULL);
+    if (reg == NULL) {
+	    printk(KERN_ERR "via-cuda: No \"reg\" property !\n");
+	    goto fail;
+    }
+    taddr = of_translate_address(vias, reg);
+    if (taddr == 0) {
+	    printk(KERN_ERR "via-cuda: Can't translate address !\n");
+	    goto fail;
+    }
+    via = ioremap(taddr, 0x2000);
+    if (via == NULL) {
+	    printk(KERN_ERR "via-cuda: Can't map address !\n");
+	    goto fail;
     }
-    via = ioremap(vias->addrs->address, 0x2000);
 
     cuda_state = idle;
     sys_ctrler = SYS_CTRLER_CUDA;
@@ -185,6 +180,11 @@
 	cuda_poll();
 
     return 1;
+
+ fail:
+    of_node_put(vias);
+    vias = NULL;
+    return 0;
 }
 #endif /* CONFIG_PPC */
 
Index: linux-serialfix/drivers/macintosh/via-pmu.c
===================================================================
--- linux-serialfix.orig/drivers/macintosh/via-pmu.c	2005-11-09 11:49:03.000000000 +1100
+++ linux-serialfix/drivers/macintosh/via-pmu.c	2005-11-22 16:45:44.000000000 +1100
@@ -147,6 +147,7 @@
 static int pmu_kind = PMU_UNKNOWN;
 static int pmu_fully_inited = 0;
 static int pmu_has_adb;
+static struct device_node *gpio_node;
 static unsigned char __iomem *gpio_reg = NULL;
 static int gpio_irq = -1;
 static int gpio_irq_enabled = -1;
@@ -295,22 +296,26 @@
 };
 #endif /* CONFIG_PMAC_BACKLIGHT */
 
-int
-find_via_pmu(void)
+int __init find_via_pmu(void)
 {
+	phys_addr_t taddr;
+	u32 *reg;
+
 	if (via != 0)
 		return 1;
-	vias = find_devices("via-pmu");
-	if (vias == 0)
+	vias = of_find_node_by_name(NULL, "via-pmu");
+	if (vias == NULL)
 		return 0;
-	if (vias->next != 0)
-		printk(KERN_WARNING "Warning: only using 1st via-pmu\n");
 
-	if (vias->n_addrs < 1 || vias->n_intrs < 1) {
-		printk(KERN_ERR "via-pmu: %d addresses, %d interrupts!\n",
-		       vias->n_addrs, vias->n_intrs);
-		if (vias->n_addrs < 1 || vias->n_intrs < 1)
-			return 0;
+	reg = (u32 *)get_property(vias, "reg", NULL);
+	if (reg == NULL) {
+		printk(KERN_ERR "via-pmu: No \"reg\" property !\n");
+		goto fail;
+	}
+	taddr = of_translate_address(vias, reg);
+	if (taddr == 0) {
+		printk(KERN_ERR "via-pmu: Can't translate address !\n");
+		goto fail;
 	}
 
 	spin_lock_init(&pmu_lock);
@@ -331,7 +336,8 @@
 		pmu_kind = PMU_HEATHROW_BASED;
 	else if (device_is_compatible(vias->parent, "Keylargo")
 		 || device_is_compatible(vias->parent, "K2-Keylargo")) {
-		struct device_node *gpio, *gpiop;
+		struct device_node *gpiop;
+		phys_addr_t gaddr = 0;
 
 		pmu_kind = PMU_KEYLARGO_BASED;
 		pmu_has_adb = (find_type_devices("adb") != NULL);
@@ -341,19 +347,24 @@
 				PMU_INT_TICK |
 				PMU_INT_ENVIRONMENT;
 		
-		gpiop = find_devices("gpio");
-		if (gpiop && gpiop->n_addrs) {
-			gpio_reg = ioremap(gpiop->addrs->address, 0x10);
-			gpio = find_devices("extint-gpio1");
-			if (gpio == NULL)
-				gpio = find_devices("pmu-interrupt");
-			if (gpio && gpio->parent == gpiop && gpio->n_intrs)
-				gpio_irq = gpio->intrs[0].line;
+		gpiop = of_find_node_by_name(NULL, "gpio");
+		if (gpiop) {
+			reg = (u32 *)get_property(gpiop, "reg", NULL);
+			if (reg)
+				gaddr = of_translate_address(gpiop, reg);
+			if (gaddr != 0)
+				gpio_reg = ioremap(gaddr, 0x10);
 		}
+		if (gpio_reg == NULL)
+			printk(KERN_ERR "via-pmu: Can't find GPIO reg !\n");
 	} else
 		pmu_kind = PMU_UNKNOWN;
 
-	via = ioremap(vias->addrs->address, 0x2000);
+	via = ioremap(taddr, 0x2000);
+	if (via == NULL) {
+		printk(KERN_ERR "via-pmu: Can't map address !\n");
+		goto fail;
+	}
 	
 	out_8(&via[IER], IER_CLR | 0x7f);	/* disable all intrs */
 	out_8(&via[IFR], 0x7f);			/* clear IFR */
@@ -371,17 +382,19 @@
 	sys_ctrler = SYS_CTRLER_PMU;
 	
 	return 1;
+ fail:
+	of_node_put(vias);
+	vias = NULL;
+	return 0;
 }
 
 #ifdef CONFIG_ADB
-static int
-pmu_probe(void)
+static int pmu_probe(void)
 {
 	return vias == NULL? -ENODEV: 0;
 }
 
-static int __init
-pmu_init(void)
+static int __init pmu_init(void)
 {
 	if (vias == NULL)
 		return -ENODEV;
@@ -405,7 +418,7 @@
 	bright_req_2.complete = 1;
 	batt_req.complete = 1;
 
-#if defined(CONFIG_PPC32) && !defined(CONFIG_PPC_MERGE)
+#ifndef CONFIG_PPC_MERGE
 	if (pmu_kind == PMU_KEYLARGO_BASED)
 		openpic_set_irq_priority(vias->intrs[0].line,
 					 OPENPIC_PRIORITY_DEFAULT + 1);
@@ -418,10 +431,22 @@
 		return -EAGAIN;
 	}
 
-	if (pmu_kind == PMU_KEYLARGO_BASED && gpio_irq != -1) {
-		if (request_irq(gpio_irq, gpio1_interrupt, 0, "GPIO1 ADB", (void *)0))
-			printk(KERN_ERR "pmu: can't get irq %d (GPIO1)\n", gpio_irq);
-		gpio_irq_enabled = 1;
+	if (pmu_kind == PMU_KEYLARGO_BASED) {
+		gpio_node = of_find_node_by_name(NULL, "extint-gpio1");
+		if (gpio_node == NULL)
+			gpio_node = of_find_node_by_name(NULL,
+							 "pmu-interrupt");
+		if (gpio_node && gpio_node->n_intrs > 0)
+			gpio_irq = gpio_node->intrs[0].line;
+
+		if (gpio_irq != -1) {
+			if (request_irq(gpio_irq, gpio1_interrupt, 0,
+					"GPIO1 ADB", (void *)0))
+				printk(KERN_ERR "pmu: can't get irq %d"
+				       " (GPIO1)\n", gpio_irq);
+			else
+				gpio_irq_enabled = 1;
+		}
 	}
 
 	/* Enable interrupts */
@@ -1371,7 +1396,6 @@
 			}
 			pmu_done(req);
 		} else {
-#if defined(CONFIG_XMON) && !defined(CONFIG_PPC64)
 			if (len == 4 && data[1] == 0x2c) {
 				extern int xmon_wants_key, xmon_adb_keycode;
 				if (xmon_wants_key) {
@@ -1379,7 +1403,6 @@
 					return;
 				}
 			}
-#endif /* defined(CONFIG_XMON) && !defined(CONFIG_PPC64) */
 #ifdef CONFIG_ADB
 			/*
 			 * XXX On the [23]400 the PMU gives us an up
Index: linux-serialfix/arch/ppc/kernel/setup.c
===================================================================
--- linux-serialfix.orig/arch/ppc/kernel/setup.c	2005-11-17 14:06:25.000000000 +1100
+++ linux-serialfix/arch/ppc/kernel/setup.c	2005-11-22 16:52:56.000000000 +1100
@@ -744,6 +744,9 @@
 	/* so udelay does something sensible, assume <= 1000 bogomips */
 	loops_per_jiffy = 500000000 / HZ;
 
+	if (ppc_md.init_early)
+		ppc_md.init_early();
+
 #ifdef CONFIG_PPC_MULTIPLATFORM
 	/* This could be called "early setup arch", it must be done
 	 * now because xmon need it
Index: linux-serialfix/include/asm-ppc/btext.h
===================================================================
--- linux-serialfix.orig/include/asm-ppc/btext.h	2005-11-09 11:49:03.000000000 +1100
+++ linux-serialfix/include/asm-ppc/btext.h	2005-11-22 16:55:27.000000000 +1100
@@ -17,7 +17,7 @@
 extern boot_infos_t disp_bi;
 extern int boot_text_mapped;
 
-extern void init_boot_display(void);
+extern void btext_init(boot_infos_t *bi);
 extern void btext_welcome(void);
 extern void btext_prepare_BAT(void);
 extern void btext_setup_display(int width, int height, int depth, int pitch,
Index: linux-serialfix/include/asm-ppc/machdep.h
===================================================================
--- linux-serialfix.orig/include/asm-ppc/machdep.h	2005-11-01 14:13:56.000000000 +1100
+++ linux-serialfix/include/asm-ppc/machdep.h	2005-11-22 16:56:40.000000000 +1100
@@ -35,8 +35,10 @@
 	int		(*get_irq)(struct pt_regs *);
 	
 	/* A general init function, called by ppc_init in init/main.c.
-	   May be NULL. */
+	   May be NULL. DEPRECATED ! */
 	void		(*init)(void);
+	/* For compatibility with merged platforms */
+	void		(*init_early)(void);
 
 	void		(*restart)(char *cmd);
 	void		(*power_off)(void);

^ permalink raw reply

* [PATCH] powerpc: serial port discovery
From: Benjamin Herrenschmidt @ 2005-11-22  4:49 UTC (permalink / raw)
  To: linuxppc64-dev; +Cc: linuxppc-dev list

This moves the discovery of legacy serial ports to a separate file,
makes it common to ppc32 and ppc64, and reworks it to use the new OF
address translators to get to the ports early. This new version can also
detect some PCI serial cards using legacy chips and will probably match
those discovered port with the default console choice.

Only ppc64 gets udbg still yet, unifying udbg isn't finished yet.

It also adds some speed-probing code to udbg so that the default console
can come up at the same speed it was set to by the firmware.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

Same comments as for the previous one, this isn't 1.6.15 material.

Index: linux-serialfix/arch/powerpc/kernel/legacy_serial.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-serialfix/arch/powerpc/kernel/legacy_serial.c	2005-11-22 15:43:37.000000000 +1100
@@ -0,0 +1,478 @@
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/console.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/mmu.h>
+#include <asm/prom.h>
+#include <asm/serial.h>
+#include <asm/udbg.h>
+#include <asm/pci-bridge.h>
+#include <asm/ppc-pci.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt...) do { printk(fmt); } while(0)
+#else
+#define DBG(fmt...) do { } while(0)
+#endif
+
+#define MAX_LEGACY_SERIAL_PORTS	8
+
+static struct plat_serial8250_port
+legacy_serial_ports[MAX_LEGACY_SERIAL_PORTS+1];
+static struct legacy_serial_info {
+	struct device_node		*np;
+	unsigned int			speed;
+	unsigned int			clock;
+	phys_addr_t			taddr;
+} legacy_serial_infos[MAX_LEGACY_SERIAL_PORTS];
+static unsigned int legacy_serial_count;
+static int legacy_serial_console = -1;
+
+static int __init add_legacy_port(struct device_node *np, int want_index,
+				  int iotype, phys_addr_t base,
+				  phys_addr_t taddr, unsigned long irq)
+{
+	u32 *clk, *spd, clock;
+	int index;
+
+	/* get clock freq. if present */
+	clk = (u32 *)get_property(np, "clock-frequency", NULL);
+	clock = clk ? *clk : BASE_BAUD * 16;
+
+	/* get default speed if present */
+	spd = (u32 *)get_property(np, "current-speed", NULL);
+
+	/* If we have a location index, then try to use it */
+	if (want_index >= 0 && want_index < MAX_LEGACY_SERIAL_PORTS)
+		index = want_index;
+	else
+		index = legacy_serial_count;
+
+	/* if our index is still out of range, that mean that
+	 * array is full, we could scan for a free slot but that
+	 * make little sense to bother, just skip the port
+	 */
+	if (index >= MAX_LEGACY_SERIAL_PORTS)
+		return -1;
+	if (index >= legacy_serial_count)
+		legacy_serial_count = index + 1;
+
+	/* Check if there is a port who already claimed our slot */
+	if (legacy_serial_infos[index].np != 0) {
+		/* if we still have some room, move it, else override */
+		if (legacy_serial_count < MAX_LEGACY_SERIAL_PORTS) {
+			printk(KERN_INFO "Moved legacy port %d -> %d\n",
+			       index, legacy_serial_count);
+			legacy_serial_ports[legacy_serial_count] =
+				legacy_serial_ports[index];
+			legacy_serial_infos[legacy_serial_count] =
+				legacy_serial_infos[index];
+			legacy_serial_count++;
+		} else {
+			printk(KERN_INFO "Replacing legacy port %d\n", index);
+		}
+	}
+
+	/* Now fill the entry */
+	memset(&legacy_serial_ports[index], 0,
+	       sizeof(struct plat_serial8250_port));
+	if (iotype == UPIO_PORT)
+		legacy_serial_ports[index].iobase = base;
+	else
+		legacy_serial_ports[index].membase = (void __iomem *)base;
+	legacy_serial_ports[index].iotype = iotype;
+	legacy_serial_ports[index].uartclk = clock;
+	legacy_serial_ports[index].irq = irq;
+	legacy_serial_ports[index].flags = ASYNC_BOOT_AUTOCONF;
+	legacy_serial_infos[index].taddr = taddr;
+	legacy_serial_infos[index].np = of_node_get(np);
+	legacy_serial_infos[index].clock = clock;
+	legacy_serial_infos[index].speed = spd ? *spd : 0;
+
+	printk(KERN_INFO "Found legacy serial port %d for %s\n",
+	       index, np->full_name);
+	printk(KERN_INFO "  %s=%llx, taddr=%llx, irq=%lx, clk=%d, speed=%d\n",
+	       (iotype == UPIO_PORT) ? "port" : "mem",
+	       (unsigned long long)base, (unsigned long long)taddr, irq,
+	       legacy_serial_ports[index].uartclk,
+	       legacy_serial_infos[index].speed);
+
+	return index;
+}
+
+static int __init add_legacy_isa_port(struct device_node *np,
+				      struct device_node *isa_bridge)
+{
+	u32 *reg;
+	char *typep;
+	int index = -1;
+	phys_addr_t taddr;
+
+	/* Get the ISA port number */
+	reg = (u32 *)get_property(np, "reg", NULL);
+	if (reg == NULL)
+		return -1;
+
+	/* Verify it's an IO port, we don't support anything else */
+	if (!(reg[0] & 0x00000001))
+		return -1;
+
+	/* Now look for an "ibm,aix-loc" property that gives us ordering
+	 * if any...
+	 */
+	typep = (char *)get_property(np, "ibm,aix-loc", NULL);
+
+	/* If we have a location index, then use it */
+	if (typep && *typep == 'S')
+		index = simple_strtol(typep+1, NULL, 0) - 1;
+
+	/* Translate ISA address */
+	taddr = of_translate_address(np, reg);
+
+	/* Add port, irq will be dealt with later */
+	return add_legacy_port(np, index, UPIO_PORT, reg[1], taddr, NO_IRQ);
+
+}
+
+static int __init add_legacy_pci_port(struct device_node *np,
+				      struct device_node *pci_dev)
+{
+	phys_addr_t addr, base;
+	u32 *addrp;
+	int iotype, index = -1;
+
+#if 0
+	/* We only support ports that have a clock frequency properly
+	 * encoded in the device-tree (that is have an fcode). Anything
+	 * else can't be used that early and will be normally probed by
+	 * the generic 8250_pci driver later on.
+	 */
+	if (get_property(np, "clock-frequency", NULL) == NULL)
+		return -1;
+#endif
+
+	/* Get the PCI address. Assume BAR 0 */
+	addrp = of_get_pci_addr(pci_dev, 0, NULL);
+	if (addrp == NULL)
+		return -1;
+
+	/* We only support BAR 0 for now */
+	iotype = (addrp[0] & 0x02000000) ? UPIO_MEM : UPIO_PORT;
+	addr = of_translate_address(pci_dev, addrp);
+
+	/* Set the IO base to the same as the translated address for MMIO,
+	 * or to the domain local IO base for PIO (it will be fixed up later)
+	 */
+	if (iotype == UPIO_MEM)
+		base = addr;
+	else
+		base = addrp[2];
+
+	/* Try to guess an index... If we have subdevices of the pci dev,
+	 * we get to their "reg" property
+	 */
+	if (np != pci_dev) {
+		u32 *reg = (u32 *)get_property(np, "reg", NULL);
+		if (reg && (*reg < 4))
+			index = legacy_serial_count + *reg;
+	}
+
+	/* Add port, irq will be dealt with later. We passed a translated
+	 * IO port value. It will be fixed up later along with the irq
+	 */
+	return add_legacy_port(np, index, iotype, base, addr, NO_IRQ);
+}
+
+/*
+ * This is called very early, as part of setup_system() or eventually
+ * setup_arch(), basically before anything else in this file. This function
+ * will try to build a list of all the available 8250-compatible serial ports
+ * in the machine using the Open Firmware device-tree. It currently only deals
+ * with ISA and PCI busses but could be extended. It allows a very early boot
+ * console to be initialized, that list is also used later to provide 8250 with
+ * the machine non-PCI ports and to properly pick the default console port
+ */
+void __init find_legacy_serial_ports(void)
+{
+	struct device_node *np, *stdout;
+	char *path;
+	int index;
+
+	DBG(" -> find_legacy_serial_port()\n");
+
+	/* Now find out if one of these is out firmware console */
+	path = (char *)get_property(of_chosen, "linux,stdout-path", NULL);
+	if (path == NULL) {
+		DBG(" no linux,stdout-path !\n");
+		return;
+	}
+	stdout = of_find_node_by_path(path);
+	if (stdout) {
+		DBG("stdout is %s\n", stdout->full_name);
+	}
+
+	/* First fill our array with ISA ports */
+	for (np = NULL; (np = of_find_node_by_type(np, "serial"));) {
+		struct device_node *isa = of_get_parent(np);
+		if (isa && !strcmp(isa->name, "isa")) {
+			index = add_legacy_isa_port(np, isa);
+			if (index >= 0 && np == stdout)
+				legacy_serial_console = index;
+		}
+		of_node_put(isa);
+	}
+
+	/* Next, try to locate PCI ports */
+	for (np = NULL; (np = of_find_all_nodes(np));) {
+		struct device_node *pci, *parent = of_get_parent(np);
+		if (parent && !strcmp(parent->name, "isa")) {
+			of_node_put(parent);
+			continue;
+		}
+		if (strcmp(np->name, "serial") && strcmp(np->type, "serial")) {
+			of_node_put(parent);
+			continue;
+		}
+		/* Check for known pciclass, and also check wether we have
+		 * a device with child nodes for ports or not
+		 */
+		if (device_is_compatible(np, "pciclass,0700") ||
+		    device_is_compatible(np, "pciclass,070002"))
+			pci = np;
+		else if (device_is_compatible(parent, "pciclass,0700") ||
+			 device_is_compatible(parent, "pciclass,070002"))
+			pci = parent;
+		else {
+			of_node_put(parent);
+			continue;
+		}
+		index = add_legacy_pci_port(np, pci);
+		if (index >= 0 && np == stdout)
+			legacy_serial_console = index;
+		of_node_put(parent);
+	}
+
+	DBG("legacy_serial_console = %d\n", legacy_serial_console);
+
+	/* udbg is 64 bits only for now, that will change soon though ... */
+#ifdef CONFIG_PPC64
+	while (legacy_serial_console >= 0) {
+		struct legacy_serial_info *info =
+			&legacy_serial_infos[legacy_serial_console];
+		void __iomem *addr;
+
+		if (info->taddr == 0)
+			break;
+		addr = ioremap(info->taddr, 0x1000);
+		if (addr == NULL)
+			break;
+		if (info->speed == 0)
+			info->speed = udbg_probe_uart_speed(addr, info->clock);
+		DBG("default console speed = %d\n", info->speed);
+		udbg_init_uart(addr, info->speed, info->clock);
+		break;
+	}
+#endif /* CONFIG_PPC64 */
+
+	DBG(" <- find_legacy_serial_port()\n");
+}
+
+static struct platform_device serial_device = {
+	.name	= "serial8250",
+	.id	= PLAT8250_DEV_PLATFORM,
+	.dev	= {
+		.platform_data = legacy_serial_ports,
+	},
+};
+
+static void __init fixup_port_irq(int index,
+				  struct device_node *np,
+				  struct plat_serial8250_port *port)
+{
+	DBG("fixup_port_irq(%d)\n", index);
+
+	/* Check for interrupts in that node */
+	if (np->n_intrs > 0) {
+		port->irq = np->intrs[0].line;
+		DBG(" port %d (%s), irq=%d\n",
+		    index, np->full_name, port->irq);
+		return;
+	}
+
+	/* Check for interrupts in the parent */
+	np = of_get_parent(np);
+	if (np == NULL)
+		return;
+
+	if (np->n_intrs > 0) {
+		port->irq = np->intrs[0].line;
+		DBG(" port %d (%s), irq=%d\n",
+		    index, np->full_name, port->irq);
+	}
+	of_node_put(np);
+}
+
+static void __init fixup_port_pio(int index,
+				  struct device_node *np,
+				  struct plat_serial8250_port *port)
+{
+	struct pci_controller *hose;
+
+	DBG("fixup_port_pio(%d)\n", index);
+
+	hose = pci_find_hose_for_OF_device(np);
+	if (hose) {
+		unsigned long offset = (unsigned long)hose->io_base_virt -
+#ifdef CONFIG_PPC64
+			pci_io_base;
+#else
+			isa_io_base;
+#endif
+		DBG("port %d, IO %lx -> %lx\n",
+		    index, port->iobase, port->iobase + offset);
+		port->iobase += offset;
+	}
+}
+
+/*
+ * This is called as an arch initcall, hopefully before the PCI bus is
+ * probed and/or the 8250 driver loaded since we need to register our
+ * platform devices before 8250 PCI ones are detected as some of them
+ * must properly "override" the platform ones.
+ *
+ * This function fixes up the interrupt value for platform ports as it
+ * couldn't be done earlier before interrupt maps have been parsed. It
+ * also "corrects" the IO address for PIO ports for the same reason,
+ * since earlier, the PHBs virtual IO space wasn't assigned yet. It then
+ * registers all those platform ports for use by the 8250 driver when it
+ * finally loads.
+ */
+static int __init serial_dev_init(void)
+{
+	int i;
+
+	if (legacy_serial_count == 0)
+		return -ENODEV;
+
+	/*
+	 * Before we register the platfrom serial devices, we need
+	 * to fixup their interrutps and their IO ports.
+	 */
+	DBG("Fixing serial ports interrupts and IO ports ...\n");
+
+	for (i = 0; i < legacy_serial_count; i++) {
+		struct plat_serial8250_port *port = &legacy_serial_ports[i];
+		struct device_node *np = legacy_serial_infos[i].np;
+
+		if (port->irq == NO_IRQ)
+			fixup_port_irq(i, np, port);
+		if (port->iotype == UPIO_PORT)
+			fixup_port_pio(i, np, port);
+	}
+
+	DBG("Registering platform serial ports\n");
+
+	return platform_device_register(&serial_device);
+}
+arch_initcall(serial_dev_init);
+
+
+/*
+ * This is called very early, as part of console_init() (typically just after
+ * time_init()). This function is respondible for trying to find a good
+ * default console on serial ports. It tries to match the open firmware
+ * default output with one of the available serial console drivers, either
+ * one of the platform serial ports that have been probed earlier by
+ * find_legacy_serial_ports() or some more platform specific ones.
+ */
+static int __init check_legacy_serial_console(void)
+{
+	struct device_node *prom_stdout = NULL;
+	int speed = 0, offset = 0;
+	char *name;
+	u32 *spd;
+
+	DBG(" -> check_legacy_serial_console()\n");
+
+	/* The user has requested a console so this is already set up. */
+	if (strstr(saved_command_line, "console=")) {
+		DBG(" console was specified !\n");
+		return -EBUSY;
+	}
+
+	if (!of_chosen) {
+		DBG(" of_chosen is NULL !\n");
+		return -ENODEV;
+	}
+	/* We are getting a weird phandle from OF ... */
+	/* ... So use the full path instead */
+	name = (char *)get_property(of_chosen, "linux,stdout-path", NULL);
+	if (name == NULL) {
+		DBG(" no linux,stdout-path !\n");
+		return -ENODEV;
+	}
+	prom_stdout = of_find_node_by_path(name);
+	if (!prom_stdout) {
+		DBG(" can't find stdout package %s !\n", name);
+		return -ENODEV;
+	}
+	DBG("stdout is %s\n", prom_stdout->full_name);
+
+	name = (char *)get_property(prom_stdout, "name", NULL);
+	if (!name) {
+		DBG(" stdout package has no name !\n");
+		goto not_found;
+	}
+	spd = (u32 *)get_property(prom_stdout, "current-speed", NULL);
+	if (spd)
+		speed = *spd;
+
+	if (0)
+		;
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+	else if (strcmp(name, "serial") == 0) {
+		int i;
+		/* Look for it in probed array */
+		for (i = 0; i < legacy_serial_count; i++) {
+			if (prom_stdout != legacy_serial_infos[i].np)
+				continue;
+			offset = i;
+			speed = legacy_serial_infos[i].speed;
+			break;
+		}
+		if (i >= legacy_serial_count)
+			goto not_found;
+	}
+#endif /* CONFIG_SERIAL_8250_CONSOLE */
+#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE
+	else if (strcmp(name, "ch-a") == 0)
+		offset = 0;
+	else if (strcmp(name, "ch-b") == 0)
+		offset = 1;
+#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */
+	else
+		goto not_found;
+	of_node_put(prom_stdout);
+
+	DBG("Found serial console at ttyS%d\n", offset);
+
+	if (speed) {
+		static char __initdata opt[16];
+		sprintf(opt, "%d", speed);
+		return add_preferred_console("ttyS", offset, opt);
+	} else
+		return add_preferred_console("ttyS", offset, NULL);
+
+ not_found:
+	DBG("No preferred console found !\n");
+	of_node_put(prom_stdout);
+	return -ENODEV;
+}
+console_initcall(check_legacy_serial_console);
+
Index: linux-serialfix/arch/powerpc/kernel/Makefile
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/Makefile	2005-11-22 15:12:17.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/Makefile	2005-11-22 15:12:20.000000000 +1100
@@ -32,10 +32,6 @@
 obj-$(CONFIG_LPARCFG)		+= lparcfg.o
 obj-$(CONFIG_IBMVIO)		+= vio.o
 obj-$(CONFIG_GENERIC_TBSYNC)	+= smp-tbsync.o
-obj-$(CONFIG_PPC_PSERIES)	+= udbg_16550.o
-obj-$(CONFIG_PPC_MAPLE)		+= udbg_16550.o
-udbgscc-$(CONFIG_PPC64)		:= udbg_scc.o
-obj-$(CONFIG_PPC_PMAC)		+= $(udbgscc-y)
 obj64-$(CONFIG_PPC_MULTIPLATFORM) += nvram_64.o
 
 ifeq ($(CONFIG_PPC_MERGE),y)
@@ -58,14 +54,15 @@
 obj-$(CONFIG_6xx)		+= idle_6xx.o
 obj-$(CONFIG_SMP)		+= smp.o
 obj-$(CONFIG_KPROBES)		+= kprobes.o
-
+obj-$(CONFIG_PPC_MULTIPLATFORM) += legacy_serial.o
+obj64-$(CONFIG_PPC_MULTIPLATFORM) += udbg_16550.o
+obj64-$(CONFIG_PPC_PMAC)	+= udbg_scc.o
 module-$(CONFIG_PPC64)		+= module_64.o
 obj-$(CONFIG_MODULES)		+= $(module-y)
 
 pci64-$(CONFIG_PPC64)		+= pci_64.o pci_dn.o pci_iommu.o \
 				   pci_direct_iommu.o iomap.o
 obj-$(CONFIG_PCI)		+= $(pci64-y)
-
 kexec64-$(CONFIG_PPC64)		+= machine_kexec_64.o
 obj-$(CONFIG_KEXEC)		+= $(kexec64-y)
 
Index: linux-serialfix/arch/powerpc/kernel/setup-common.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/setup-common.c	2005-11-22 15:12:17.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/setup-common.c	2005-11-22 15:12:20.000000000 +1100
@@ -294,129 +294,6 @@
 	.show =	show_cpuinfo,
 };
 
-#ifdef CONFIG_PPC_MULTIPLATFORM
-static int __init set_preferred_console(void)
-{
-	struct device_node *prom_stdout = NULL;
-	char *name;
-	u32 *spd;
-	int offset = 0;
-
-	DBG(" -> set_preferred_console()\n");
-
-	/* The user has requested a console so this is already set up. */
-	if (strstr(saved_command_line, "console=")) {
-		DBG(" console was specified !\n");
-		return -EBUSY;
-	}
-
-	if (!of_chosen) {
-		DBG(" of_chosen is NULL !\n");
-		return -ENODEV;
-	}
-	/* We are getting a weird phandle from OF ... */
-	/* ... So use the full path instead */
-	name = (char *)get_property(of_chosen, "linux,stdout-path", NULL);
-	if (name == NULL) {
-		DBG(" no linux,stdout-path !\n");
-		return -ENODEV;
-	}
-	prom_stdout = of_find_node_by_path(name);
-	if (!prom_stdout) {
-		DBG(" can't find stdout package %s !\n", name);
-		return -ENODEV;
-	}	
-	DBG("stdout is %s\n", prom_stdout->full_name);
-
-	name = (char *)get_property(prom_stdout, "name", NULL);
-	if (!name) {
-		DBG(" stdout package has no name !\n");
-		goto not_found;
-	}
-	spd = (u32 *)get_property(prom_stdout, "current-speed", NULL);
-
-	if (0)
-		;
-#ifdef CONFIG_SERIAL_8250_CONSOLE
-	else if (strcmp(name, "serial") == 0) {
-		int i;
-		u32 *reg = (u32 *)get_property(prom_stdout, "reg", &i);
-		if (i > 8) {
-			switch (reg[1]) {
-				case 0x3f8:
-					offset = 0;
-					break;
-				case 0x2f8:
-					offset = 1;
-					break;
-				case 0x898:
-					offset = 2;
-					break;
-				case 0x890:
-					offset = 3;
-					break;
-				default:
-					/* We dont recognise the serial port */
-					goto not_found;
-			}
-		}
-	}
-#endif /* CONFIG_SERIAL_8250_CONSOLE */
-#ifdef CONFIG_PPC_PSERIES
-	else if (strcmp(name, "vty") == 0) {
- 		u32 *reg = (u32 *)get_property(prom_stdout, "reg", NULL);
- 		char *compat = (char *)get_property(prom_stdout, "compatible", NULL);
-
- 		if (reg && compat && (strcmp(compat, "hvterm-protocol") == 0)) {
- 			/* Host Virtual Serial Interface */
- 			switch (reg[0]) {
- 				case 0x30000000:
- 					offset = 0;
- 					break;
- 				case 0x30000001:
- 					offset = 1;
- 					break;
- 				default:
-					goto not_found;
- 			}
-			of_node_put(prom_stdout);
-			DBG("Found hvsi console at offset %d\n", offset);
- 			return add_preferred_console("hvsi", offset, NULL);
- 		} else {
- 			/* pSeries LPAR virtual console */
-			of_node_put(prom_stdout);
-			DBG("Found hvc console\n");
- 			return add_preferred_console("hvc", 0, NULL);
- 		}
-	}
-#endif /* CONFIG_PPC_PSERIES */
-#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE
-	else if (strcmp(name, "ch-a") == 0)
-		offset = 0;
-	else if (strcmp(name, "ch-b") == 0)
-		offset = 1;
-#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */
-	else
-		goto not_found;
-	of_node_put(prom_stdout);
-
-	DBG("Found serial console at ttyS%d\n", offset);
-
-	if (spd) {
-		static char __initdata opt[16];
-		sprintf(opt, "%d", *spd);
-		return add_preferred_console("ttyS", offset, opt);
-	} else
-		return add_preferred_console("ttyS", offset, NULL);
-
- not_found:
-	DBG("No preferred console found !\n");
-	of_node_put(prom_stdout);
-	return -ENODEV;
-}
-console_initcall(set_preferred_console);
-#endif /* CONFIG_PPC_MULTIPLATFORM */
-
 void __init check_for_initrd(void)
 {
 #ifdef CONFIG_BLK_DEV_INITRD
Index: linux-serialfix/arch/powerpc/kernel/setup_64.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/setup_64.c	2005-11-22 15:12:17.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/setup_64.c	2005-11-22 15:12:20.000000000 +1100
@@ -451,6 +451,15 @@
 	 */
 	ppc_md.init_early();
 
+ 	/*
+	 * We can discover serial ports now since the above did setup the
+	 * hash table management for us, thus ioremap works. We do that early
+	 * so that further code can be debugged
+	 */
+#ifdef CONFIG_PPC_MULTIPLATFORM
+	find_legacy_serial_ports();
+#endif
+
 	/*
 	 * "Finish" the device-tree, that is do the actual parsing of
 	 * some of the properties like the interrupt map
@@ -649,187 +658,6 @@
 	printk("[terminate]%04x %s\n", src, msg);
 }
 
-#ifndef CONFIG_PPC_ISERIES
-/*
- * This function can be used by platforms to "find" legacy serial ports.
- * It works for "serial" nodes under an "isa" node, and will try to
- * respect the "ibm,aix-loc" property if any. It works with up to 8
- * ports.
- */
-
-#define MAX_LEGACY_SERIAL_PORTS	8
-static struct plat_serial8250_port serial_ports[MAX_LEGACY_SERIAL_PORTS+1];
-static unsigned int old_serial_count;
-
-void __init generic_find_legacy_serial_ports(u64 *physport,
-		unsigned int *default_speed)
-{
-	struct device_node *np;
-	u32 *sizeprop;
-
-	struct isa_reg_property {
-		u32 space;
-		u32 address;
-		u32 size;
-	};
-	struct pci_reg_property {
-		struct pci_address addr;
-		u32 size_hi;
-		u32 size_lo;
-	};                                                                        
-
-	DBG(" -> generic_find_legacy_serial_port()\n");
-
-	*physport = 0;
-	if (default_speed)
-		*default_speed = 0;
-
-	np = of_find_node_by_path("/");
-	if (!np)
-		return;
-
-	/* First fill our array */
-	for (np = NULL; (np = of_find_node_by_type(np, "serial"));) {
-		struct device_node *isa, *pci;
-		struct isa_reg_property *reg;
-		unsigned long phys_size, addr_size, io_base;
-		u32 *rangesp;
-		u32 *interrupts, *clk, *spd;
-		char *typep;
-		int index, rlen, rentsize;
-
-		/* Ok, first check if it's under an "isa" parent */
-		isa = of_get_parent(np);
-		if (!isa || strcmp(isa->name, "isa")) {
-			DBG("%s: no isa parent found\n", np->full_name);
-			continue;
-		}
-		
-		/* Now look for an "ibm,aix-loc" property that gives us ordering
-		 * if any...
-		 */
-	 	typep = (char *)get_property(np, "ibm,aix-loc", NULL);
-
-		/* Get the ISA port number */
-		reg = (struct isa_reg_property *)get_property(np, "reg", NULL);	
-		if (reg == NULL)
-			goto next_port;
-		/* We assume the interrupt number isn't translated ... */
-		interrupts = (u32 *)get_property(np, "interrupts", NULL);
-		/* get clock freq. if present */
-		clk = (u32 *)get_property(np, "clock-frequency", NULL);
-		/* get default speed if present */
-		spd = (u32 *)get_property(np, "current-speed", NULL);
-		/* Default to locate at end of array */
-		index = old_serial_count; /* end of the array by default */
-
-		/* If we have a location index, then use it */
-		if (typep && *typep == 'S') {
-			index = simple_strtol(typep+1, NULL, 0) - 1;
-			/* if index is out of range, use end of array instead */
-			if (index >= MAX_LEGACY_SERIAL_PORTS)
-				index = old_serial_count;
-			/* if our index is still out of range, that mean that
-			 * array is full, we could scan for a free slot but that
-			 * make little sense to bother, just skip the port
-			 */
-			if (index >= MAX_LEGACY_SERIAL_PORTS)
-				goto next_port;
-			if (index >= old_serial_count)
-				old_serial_count = index + 1;
-			/* Check if there is a port who already claimed our slot */
-			if (serial_ports[index].iobase != 0) {
-				/* if we still have some room, move it, else override */
-				if (old_serial_count < MAX_LEGACY_SERIAL_PORTS) {
-					DBG("Moved legacy port %d -> %d\n", index,
-					    old_serial_count);
-					serial_ports[old_serial_count++] =
-						serial_ports[index];
-				} else {
-					DBG("Replacing legacy port %d\n", index);
-				}
-			}
-		}
-		if (index >= MAX_LEGACY_SERIAL_PORTS)
-			goto next_port;
-		if (index >= old_serial_count)
-			old_serial_count = index + 1;
-
-		/* Now fill the entry */
-		memset(&serial_ports[index], 0, sizeof(struct plat_serial8250_port));
-		serial_ports[index].uartclk = clk ? *clk : BASE_BAUD * 16;
-		serial_ports[index].iobase = reg->address;
-		serial_ports[index].irq = interrupts ? interrupts[0] : 0;
-		serial_ports[index].flags = ASYNC_BOOT_AUTOCONF;
-
-		DBG("Added legacy port, index: %d, port: %x, irq: %d, clk: %d\n",
-		    index,
-		    serial_ports[index].iobase,
-		    serial_ports[index].irq,
-		    serial_ports[index].uartclk);
-
-		/* Get phys address of IO reg for port 1 */
-		if (index != 0)
-			goto next_port;
-
-		pci = of_get_parent(isa);
-		if (!pci) {
-			DBG("%s: no pci parent found\n", np->full_name);
-			goto next_port;
-		}
-
-		rangesp = (u32 *)get_property(pci, "ranges", &rlen);
-		if (rangesp == NULL) {
-			of_node_put(pci);
-			goto next_port;
-		}
-		rlen /= 4;
-
-		/* we need the #size-cells of the PCI bridge node itself */
-		phys_size = 1;
-		sizeprop = (u32 *)get_property(pci, "#size-cells", NULL);
-		if (sizeprop != NULL)
-			phys_size = *sizeprop;
-		/* we need the parent #addr-cells */
-		addr_size = prom_n_addr_cells(pci);
-		rentsize = 3 + addr_size + phys_size;
-		io_base = 0;
-		for (;rlen >= rentsize; rlen -= rentsize,rangesp += rentsize) {
-			if (((rangesp[0] >> 24) & 0x3) != 1)
-				continue; /* not IO space */
-			io_base = rangesp[3];
-			if (addr_size == 2)
-				io_base = (io_base << 32) | rangesp[4];
-		}
-		if (io_base != 0) {
-			*physport = io_base + reg->address;
-			if (default_speed && spd)
-				*default_speed = *spd;
-		}
-		of_node_put(pci);
-	next_port:
-		of_node_put(isa);
-	}
-
-	DBG(" <- generic_find_legacy_serial_port()\n");
-}
-
-static struct platform_device serial_device = {
-	.name	= "serial8250",
-	.id	= PLAT8250_DEV_PLATFORM,
-	.dev	= {
-		.platform_data = serial_ports,
-	},
-};
-
-static int __init serial_dev_init(void)
-{
-	return platform_device_register(&serial_device);
-}
-arch_initcall(serial_dev_init);
-
-#endif /* CONFIG_PPC_ISERIES */
-
 int check_legacy_ioport(unsigned long base_port)
 {
 	if (ppc_md.check_legacy_ioport == NULL)
Index: linux-serialfix/arch/powerpc/kernel/udbg_16550.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/udbg_16550.c	2005-11-22 15:12:17.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/udbg_16550.c	2005-11-22 15:12:20.000000000 +1100
@@ -43,6 +43,8 @@
 #define LSR_TEMT 0x40  /* Xmitter empty */
 #define LSR_ERR  0x80  /* Error */
 
+#define LCR_DLAB 0x80
+
 static volatile struct NS16550 __iomem *udbg_comport;
 
 static void udbg_550_putc(unsigned char c)
@@ -77,29 +79,70 @@
 	return 0;
 }
 
-void udbg_init_uart(void __iomem *comport, unsigned int speed)
+void udbg_init_uart(void __iomem *comport, unsigned int speed,
+		    unsigned int clock)
 {
-	u16 dll = speed ? (115200 / speed) : 12;
+	unsigned int dll, base_bauds = clock / 16;
+
+	if (speed == 0)
+		speed = 9600;
+	dll = base_bauds / speed;
 
 	if (comport) {
 		udbg_comport = (struct NS16550 __iomem *)comport;
 		out_8(&udbg_comport->lcr, 0x00);
 		out_8(&udbg_comport->ier, 0xff);
 		out_8(&udbg_comport->ier, 0x00);
-		out_8(&udbg_comport->lcr, 0x80);	/* Access baud rate */
-		out_8(&udbg_comport->dll, dll & 0xff);	/* 1 = 115200,  2 = 57600,
-							   3 = 38400, 12 = 9600 baud */
-		out_8(&udbg_comport->dlm, dll >> 8);	/* dll >> 8 which should be zero
-							   for fast rates; */
-		out_8(&udbg_comport->lcr, 0x03);	/* 8 data, 1 stop, no parity */
-		out_8(&udbg_comport->mcr, 0x03);	/* RTS/DTR */
-		out_8(&udbg_comport->fcr ,0x07);	/* Clear & enable FIFOs */
+		out_8(&udbg_comport->lcr, LCR_DLAB);
+		out_8(&udbg_comport->dll, dll & 0xff);
+		out_8(&udbg_comport->dlm, dll >> 8);
+		/* 8 data, 1 stop, no parity */
+		out_8(&udbg_comport->lcr, 0x03);
+		/* RTS/DTR */
+		out_8(&udbg_comport->mcr, 0x03);
+		/* Clear & enable FIFOs */
+		out_8(&udbg_comport->fcr ,0x07);
 		udbg_putc = udbg_550_putc;
 		udbg_getc = udbg_550_getc;
 		udbg_getc_poll = udbg_550_getc_poll;
 	}
 }
 
+unsigned int udbg_probe_uart_speed(void __iomem *comport, unsigned int clock)
+{
+	unsigned int dll, dlm, divisor, prescaler, speed;
+	u8 old_lcr;
+	volatile struct NS16550 __iomem *port = comport;
+
+	old_lcr = in_8(&port->lcr);
+
+	/* select divisor latch registers.  */
+	out_8(&port->lcr, LCR_DLAB);
+
+	/* now, read the divisor */
+	dll = in_8(&port->dll);
+	dlm = in_8(&port->dlm);
+	divisor = dlm << 8 | dll;
+
+	/* check prescaling */
+	if (in_8(&port->mcr) & 0x80)
+		prescaler = 4;
+	else
+		prescaler = 1;
+
+	/* restore the LCR */
+	out_8(&port->lcr, old_lcr);
+
+	/* calculate speed */
+	speed = (clock / prescaler) / (divisor * 16);
+
+	/* sanity check */
+	if (speed < 9600 || speed > 115200)
+		speed = 9600;
+
+	return speed;
+}
+
 #ifdef CONFIG_PPC_MAPLE
 void udbg_maple_real_putc(unsigned char c)
 {
Index: linux-serialfix/arch/powerpc/platforms/maple/setup.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/platforms/maple/setup.c	2005-11-22 15:12:17.000000000 +1100
+++ linux-serialfix/arch/powerpc/platforms/maple/setup.c	2005-11-22 15:12:20.000000000 +1100
@@ -191,20 +191,6 @@
 	 */
 	hpte_init_native();
 
-	/* Find the serial port */
-	generic_find_legacy_serial_ports(&physport, &default_speed);
-
-	DBG("phys port addr: %lx\n", (long)physport);
-
-	if (physport) {
-		void *comport;
-		/* Map the uart for udbg. */
-		comport = (void *)ioremap(physport, 16);
-		udbg_init_uart(comport, default_speed);
-
-		DBG("Hello World !\n");
-	}
-
 	/* Setup interrupt mapping options */
 	ppc64_interrupt_controller = IC_OPEN_PIC;
 
Index: linux-serialfix/arch/powerpc/platforms/pseries/lpar.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/platforms/pseries/lpar.c	2005-11-22 15:12:17.000000000 +1100
+++ linux-serialfix/arch/powerpc/platforms/pseries/lpar.c	2005-11-22 15:12:20.000000000 +1100
@@ -24,6 +24,7 @@
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/dma-mapping.h>
+#include <linux/console.h>
 #include <asm/processor.h>
 #include <asm/mmu.h>
 #include <asm/page.h>
@@ -191,7 +192,7 @@
 /* call this from early_init() for a working debug console on
  * vterm capable LPAR machines
  */
-void udbg_init_debug_lpar(void)
+void __init udbg_init_debug_lpar(void)
 {
 	vtermno = 0;
 	udbg_putc = udbg_putcLP;
@@ -200,63 +201,54 @@
 }
 
 /* returns 0 if couldn't find or use /chosen/stdout as console */
-int find_udbg_vterm(void)
+void __init find_udbg_vterm(void)
 {
 	struct device_node *stdout_node;
 	u32 *termno;
 	char *name;
-	int found = 0;
+	int add_console;
 
 	/* find the boot console from /chosen/stdout */
 	if (!of_chosen)
-		return 0;
+		return;
 	name = (char *)get_property(of_chosen, "linux,stdout-path", NULL);
 	if (name == NULL)
-		return 0;
+		return;
 	stdout_node = of_find_node_by_path(name);
 	if (!stdout_node)
-		return 0;
-
-	/* now we have the stdout node; figure out what type of device it is. */
+		return;
 	name = (char *)get_property(stdout_node, "name", NULL);
 	if (!name) {
 		printk(KERN_WARNING "stdout node missing 'name' property!\n");
 		goto out;
 	}
+	/* The user has requested a console so this is already set up. */
+	add_console = !strstr(saved_command_line, "console=");
 
-	if (strncmp(name, "vty", 3) == 0) {
-		if (device_is_compatible(stdout_node, "hvterm1")) {
-			termno = (u32 *)get_property(stdout_node, "reg", NULL);
-			if (termno) {
-				vtermno = termno[0];
-				udbg_putc = udbg_putcLP;
-				udbg_getc = udbg_getcLP;
-				udbg_getc_poll = udbg_getc_pollLP;
-				found = 1;
-			}
-		} else if (device_is_compatible(stdout_node, "hvterm-protocol")) {
-			termno = (u32 *)get_property(stdout_node, "reg", NULL);
-			if (termno) {
-				vtermno = termno[0];
-				udbg_putc = udbg_hvsi_putc;
-				udbg_getc = udbg_hvsi_getc;
-				udbg_getc_poll = udbg_hvsi_getc_poll;
-				found = 1;
-			}
-		}
-	} else if (strncmp(name, "serial", 6)) {
-		/* XXX fix ISA serial console */
-		printk(KERN_WARNING "serial stdout on LPAR ('%s')! "
-				"can't print udbg messages\n",
-		       stdout_node->full_name);
-	} else {
-		printk(KERN_WARNING "don't know how to print to stdout '%s'\n",
-		       stdout_node->full_name);
-	}
+	/* Check if it's a virtual terminal */
+	if (strncmp(name, "vty", 3) != 0)
+		goto out;
+	termno = (u32 *)get_property(stdout_node, "reg", NULL);
+	if (termno == NULL)
+		goto out;
+	vtermno = termno[0];
 
+	if (device_is_compatible(stdout_node, "hvterm1")) {
+		udbg_putc = udbg_putcLP;
+		udbg_getc = udbg_getcLP;
+		udbg_getc_poll = udbg_getc_pollLP;
+		if (add_console)
+			add_preferred_console("hvc", termno[0] & 0xff, NULL);
+	} else if (device_is_compatible(stdout_node, "hvterm-protocol")) {
+		vtermno = termno[0];
+		udbg_putc = udbg_hvsi_putc;
+		udbg_getc = udbg_hvsi_getc;
+		udbg_getc_poll = udbg_hvsi_getc_poll;
+		if (add_console)
+			add_preferred_console("hvsi", termno[0] & 0xff, NULL);
+	}
 out:
 	of_node_put(stdout_node);
-	return found;
 }
 
 void vpa_init(int cpu)
Index: linux-serialfix/arch/powerpc/platforms/pseries/setup.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/platforms/pseries/setup.c	2005-11-22 15:12:17.000000000 +1100
+++ linux-serialfix/arch/powerpc/platforms/pseries/setup.c	2005-11-22 15:12:20.000000000 +1100
@@ -78,8 +78,6 @@
 extern void find_udbg_vterm(void);
 extern void system_reset_fwnmi(void);	/* from head.S */
 extern void machine_check_fwnmi(void);	/* from head.S */
-extern void generic_find_legacy_serial_ports(u64 *physport,
-		unsigned int *default_speed);
 
 int fwnmi_active;  /* TRUE if an FWNMI handler is present */
 
@@ -365,10 +363,7 @@
  */
 static void __init pSeries_init_early(void)
 {
-	void *comport;
 	int iommu_off = 0;
-	unsigned int default_speed;
-	u64 physport;
 
 	DBG(" -> pSeries_init_early()\n");
 
@@ -382,17 +377,8 @@
 			     get_property(of_chosen, "linux,iommu-off", NULL));
 	}
 
-	generic_find_legacy_serial_ports(&physport, &default_speed);
-
 	if (platform_is_lpar())
 		find_udbg_vterm();
-	else if (physport) {
-		/* Map the uart for udbg. */
-		comport = (void *)ioremap(physport, 16);
-		udbg_init_uart(comport, default_speed);
-
-		DBG("Hello World !\n");
-	}
 
 	if (firmware_has_feature(FW_FEATURE_DABR))
 		ppc_md.set_dabr = pseries_set_dabr;
Index: linux-serialfix/include/asm-powerpc/serial.h
===================================================================
--- linux-serialfix.orig/include/asm-powerpc/serial.h	2005-11-22 15:12:17.000000000 +1100
+++ linux-serialfix/include/asm-powerpc/serial.h	2005-11-22 15:12:20.000000000 +1100
@@ -15,4 +15,6 @@
 /* Default baud base if not found in device-tree */
 #define BASE_BAUD ( 1843200 / 16 )
 
+extern void find_legacy_serial_ports(void);
+
 #endif /* _PPC64_SERIAL_H */
Index: linux-serialfix/include/asm-powerpc/udbg.h
===================================================================
--- linux-serialfix.orig/include/asm-powerpc/udbg.h	2005-11-22 15:12:17.000000000 +1100
+++ linux-serialfix/include/asm-powerpc/udbg.h	2005-11-22 15:12:20.000000000 +1100
@@ -24,7 +24,10 @@
 extern void register_early_udbg_console(void);
 extern void udbg_printf(const char *fmt, ...);
 
-extern void udbg_init_uart(void __iomem *comport, unsigned int speed);
+extern void udbg_init_uart(void __iomem *comport, unsigned int speed,
+			   unsigned int clock);
+extern unsigned int udbg_probe_uart_speed(void __iomem *comport,
+					  unsigned int clock);
 
 struct device_node;
 extern void udbg_init_scc(struct device_node *np);
Index: linux-serialfix/arch/powerpc/kernel/setup_32.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/setup_32.c	2005-11-22 15:12:17.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/setup_32.c	2005-11-22 15:12:20.000000000 +1100
@@ -39,6 +39,7 @@
 #include <asm/nvram.h>
 #include <asm/xmon.h>
 #include <asm/time.h>
+#include <asm/serial.h>
 
 #include "setup.h"
 
@@ -282,6 +283,13 @@
 
 	unflatten_device_tree();
 	check_for_initrd();
+
+	if (ppc_md.init_early)
+		ppc_md.init_early();
+
+#ifdef CONFIG_PPC_MULTIPLATFORM
+	find_legacy_serial_ports();
+#endif
 	finish_device_tree();
 
 	smp_setup_cpu_maps();
Index: linux-serialfix/arch/powerpc/kernel/pci_64.c
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/pci_64.c	2005-11-21 11:53:14.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/pci_64.c	2005-11-22 15:34:40.000000000 +1100
@@ -1223,6 +1223,7 @@
 }
 EXPORT_SYMBOL(pcibios_fixup_device_resources);
 
+
 static void __devinit do_bus_setup(struct pci_bus *bus)
 {
 	struct pci_dev *dev;
@@ -1306,6 +1307,20 @@
 	*end = rsrc->end + offset;
 }
 
+struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node)
+{
+	if (!have_of)
+		return NULL;
+	while(node) {
+		struct pci_controller *hose, *tmp;
+		list_for_each_entry_safe(hose, tmp, &hose_list, list_node)
+			if (hose->arch_data == node)
+				return hose;
+		node = node->parent;
+	}
+	return NULL;
+}
+
 #endif /* CONFIG_PPC_MULTIPLATFORM */
 
 
Index: linux-serialfix/arch/ppc/kernel/pci.c
===================================================================
--- linux-serialfix.orig/arch/ppc/kernel/pci.c	2005-11-21 11:53:14.000000000 +1100
+++ linux-serialfix/arch/ppc/kernel/pci.c	2005-11-22 15:25:03.000000000 +1100
@@ -815,8 +815,7 @@
  * to set pci_assign_all_buses to 1 and still use RTAS for PCI
  * config cycles.
  */
-struct pci_controller*
-pci_find_hose_for_OF_device(struct device_node* node)
+struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node)
 {
 	if (!have_of)
 		return NULL;
Index: linux-serialfix/include/asm-powerpc/pci-bridge.h
===================================================================
--- linux-serialfix.orig/include/asm-powerpc/pci-bridge.h	2005-11-21 11:53:15.000000000 +1100
+++ linux-serialfix/include/asm-powerpc/pci-bridge.h	2005-11-22 15:28:24.000000000 +1100
@@ -140,6 +140,9 @@
 	return PCI_DN(busdn)->phb;
 }
 
+extern struct pci_controller*
+pci_find_hose_for_OF_device(struct device_node* node);
+
 extern struct pci_controller *
 pcibios_alloc_controller(struct device_node *dev);
 extern void pcibios_free_controller(struct pci_controller *phb);

^ permalink raw reply

* [PATCH] powerpc: Add OF address parsing code
From: Benjamin Herrenschmidt @ 2005-11-22  4:45 UTC (permalink / raw)
  To: linuxppc64-dev; +Cc: linuxppc-dev list

Parsing addresses extracted from Open Firmware isn't a simple matter. We
have various bits of code that try to do it in various place, including
some heuristics in prom.c that pre-parse addresses at boot and fill
device-nodes "addrs", but those are dodgy at best and I want to
deprecate them. So this patch introduces a new set of routines that
should be capable of parsing most types of addresses and translating
them into CPU physical addresses. It currently works for things on PCI
busses and ISA busses and should work on "standard" busses like the root
bus or the MacIO bus that don't put funky flags in addresses. If you
have other bus types that do use funky flags, you'll have to add new bus
type translators, which is fairly easy.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

This is not for 2.6.15. It's part of a series of patch which, when complete,
will unify serial port detection and udbg, and remove completely "addrs"
and the crappy heuristics from prom.c

Index: linux-serialfix/arch/powerpc/kernel/Makefile
===================================================================
--- linux-serialfix.orig/arch/powerpc/kernel/Makefile	2005-11-22 14:41:26.000000000 +1100
+++ linux-serialfix/arch/powerpc/kernel/Makefile	2005-11-22 15:43:43.000000000 +1100
@@ -12,7 +12,8 @@
 endif
 
 obj-y				:= semaphore.o cputable.o ptrace.o syscalls.o \
-				   irq.o align.o signal_32.o pmc.o vdso.o
+				   irq.o align.o signal_32.o pmc.o vdso.o \
+				   prom_parse.o
 obj-y				+= vdso32/
 obj-$(CONFIG_PPC64)		+= setup_64.o binfmt_elf32.o sys_ppc32.o \
 				   signal_64.o ptrace32.o systbl.o \
Index: linux-serialfix/arch/powerpc/kernel/prom_parse.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-serialfix/arch/powerpc/kernel/prom_parse.c	2005-11-22 15:11:59.000000000 +1100
@@ -0,0 +1,404 @@
+#undef DEBUG
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/pci_regs.h>
+#include <linux/module.h>
+#include <asm/prom.h>
+
+#ifdef DEBUG
+#define DBG(fmt...) do { printk(fmt); } while(0)
+#else
+#define DBG(fmt...) do { } while(0)
+#endif
+
+/* Max address size we deal with */
+#define OF_MAX_ADDR_CELLS	4
+#define OF_CHECK_COUNTS(na, ns)	((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \
+			(ns) > 0)
+
+/* Debug utility */
+#ifdef DEBUG
+static void of_dump_addr(const char *s, u32 *addr, int na)
+{
+	printk("%s", s);
+	while(na--)
+		printk(" %08x", *(addr++));
+	printk("\n");
+}
+#else
+static void of_dump_addr(const char *s, u32 *addr, int na) { }
+#endif
+
+/* Read a big address */
+static inline u64 of_read_addr(u32 *cell, int size)
+{
+	u64 r = 0;
+	while (size--)
+		r = (r << 32) | *(cell++);
+	return r;
+}
+
+/* Callbacks for bus specific translators */
+struct of_bus {
+	char		*name;
+	int		(*match)(struct device_node *parent);
+	void		(*count_cells)(struct device_node *child,
+				       int *addrc, int *sizec);
+	u64		(*map)(u32 *addr, u32 *range, int na, int ns, int pna);
+	int		(*translate)(u32 *addr, u64 offset, int na);
+};
+
+
+/*
+ * Default translator (generic bus)
+ */
+
+static void of_default_count_cells(struct device_node *dev,
+				   int *addrc, int *sizec)
+{
+	if (addrc)
+		*addrc = prom_n_addr_cells(dev);
+	if (sizec)
+		*sizec = prom_n_size_cells(dev);
+}
+
+static u64 of_default_map(u32 *addr, u32 *range, int na, int ns, int pna)
+{
+	u64 cp, s, da;
+
+	cp = of_read_addr(range, na);
+	s  = of_read_addr(range + na + pna, ns);
+	da = of_read_addr(addr, na);
+
+	DBG("OF: default map, cp=%lx, s=%lx, da=%lx\n", cp, s, da);
+
+	if (da < cp || da >= (cp + s))
+		return OF_BAD_ADDR;
+	return da - cp;
+}
+
+static int of_default_translate(u32 *addr, u64 offset, int na)
+{
+	u64 a = of_read_addr(addr, na);
+	memset(addr, 0, na * 4);
+	a += offset;
+	if (na > 1)
+		addr[na - 2] = a >> 32;
+	addr[na - 1] = a & 0xffffffffu;
+
+	return 0;
+}
+
+
+/*
+ * PCI bus specific translator
+ */
+
+static int of_bus_pci_match(struct device_node *np)
+{
+	return !strcmp(np->type, "pci");
+}
+
+static void of_bus_pci_count_cells(struct device_node *np,
+				   int *addrc, int *sizec)
+{
+	if (addrc)
+		*addrc = 3;
+	if (sizec)
+		*sizec = 2;
+}
+
+static u64 of_bus_pci_map(u32 *addr, u32 *range, int na, int ns, int pna)
+{
+	u64 cp, s, da;
+
+	/* Check address type match */
+	if ((addr[0] ^ range[0]) & 0x03000000)
+		return OF_BAD_ADDR;
+
+	/* Read address values, skipping high cell */
+	cp = of_read_addr(range + 1, na - 1);
+	s  = of_read_addr(range + na + pna, ns);
+	da = of_read_addr(addr + 1, na - 1);
+
+	DBG("OF: PCI map, cp=%lx, s=%lx, da=%lx\n", cp, s, da);
+
+	if (da < cp || da >= (cp + s))
+		return OF_BAD_ADDR;
+	return da - cp;
+}
+
+static int of_bus_pci_translate(u32 *addr, u64 offset, int na)
+{
+	return of_default_translate(addr + 1, offset, na - 1);
+}
+
+/*
+ * ISA bus specific translator
+ */
+
+static int of_bus_isa_match(struct device_node *np)
+{
+	return !strcmp(np->name, "isa");
+}
+
+static void of_bus_isa_count_cells(struct device_node *child,
+				   int *addrc, int *sizec)
+{
+	if (addrc)
+		*addrc = 2;
+	if (sizec)
+		*sizec = 1;
+}
+
+static u64 of_bus_isa_map(u32 *addr, u32 *range, int na, int ns, int pna)
+{
+	u64 cp, s, da;
+
+	/* Check address type match */
+	if ((addr[0] ^ range[0]) & 0x00000001)
+		return OF_BAD_ADDR;
+
+	/* Read address values, skipping high cell */
+	cp = of_read_addr(range + 1, na - 1);
+	s  = of_read_addr(range + na + pna, ns);
+	da = of_read_addr(addr + 1, na - 1);
+
+	DBG("OF: ISA map, cp=%lx, s=%lx, da=%lx\n", cp, s, da);
+
+	if (da < cp || da >= (cp + s))
+		return OF_BAD_ADDR;
+	return da - cp;
+}
+
+static int of_bus_isa_translate(u32 *addr, u64 offset, int na)
+{
+	return of_default_translate(addr + 1, offset, na - 1);
+}
+
+/*
+ * Array of bus specific translators
+ */
+
+static struct of_bus of_busses[] = {
+	/* PCI */
+	{
+		.name = "pci",
+		.match = of_bus_pci_match,
+		.count_cells = of_bus_pci_count_cells,
+		.map = of_bus_pci_map,
+		.translate = of_bus_pci_translate,
+	},
+	/* ISA */
+	{
+		.name = "isa",
+		.match = of_bus_isa_match,
+		.count_cells = of_bus_isa_count_cells,
+		.map = of_bus_isa_map,
+		.translate = of_bus_isa_translate,
+	},
+	/* Default */
+	{
+		.name = "default",
+		.match = NULL,
+		.count_cells = of_default_count_cells,
+		.map = of_default_map,
+		.translate = of_default_translate,
+	},
+};
+
+static struct of_bus *of_match_bus(struct device_node *np)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(of_busses); i ++)
+		if (!of_busses[i].match || of_busses[i].match(np))
+			return &of_busses[i];
+	BUG();
+	return NULL;
+}
+
+static int of_translate_one(struct device_node *parent, struct of_bus *bus,
+			    struct of_bus *pbus, u32 *addr,
+			    int na, int ns, int pna)
+{
+	u32 *ranges;
+	unsigned int rlen;
+	int rone;
+	u64 offset = OF_BAD_ADDR;
+
+	/* No "ranges" property is not an error */
+	ranges = (u32 *)get_property(parent, "ranges", &rlen);
+	if (ranges == NULL) {
+		offset = of_read_addr(addr, na);
+		memset(addr, 0, pna);
+		goto finish;
+	}
+
+	DBG("OF: walking ranges...\n");
+
+	/* Now walk through the ranges */
+	rlen /= 4;
+	rone = na + pna + ns;
+	for (; rlen >= rone; rlen -= rone, ranges += rone) {
+		offset = bus->map(addr, ranges, na, ns, pna);
+		if (offset != OF_BAD_ADDR)
+			break;
+	}
+	if (offset == OF_BAD_ADDR) {
+		DBG("OF: not found !\n");
+		return 1;
+	}
+	memcpy(addr, ranges + na, 4 * pna);
+
+ finish:
+	of_dump_addr("OF: parent translation for:", addr, pna);
+	DBG("OF: with offset: %lx\n", offset);
+
+	/* Translate it into parent bus space */
+	return pbus->translate(addr, offset, pna);
+}
+
+
+/*
+ * Translate an address from the device-tree into a CPU physical address,
+ * this walks up the tree and applies the various bus mappings on the
+ * way.
+ *
+ * Note: We consider that crossing any level with #size-cells == 0 to mean
+ * that translation is impossible (that is we are not dealing with a value
+ * that can be mapped to a cpu physical address). This is not really specified
+ * that way, but this is traditionally the way IBM at least do things
+ */
+u64 of_translate_address(struct device_node *dev, u32 *in_addr)
+{
+	struct device_node *parent = NULL;
+	struct of_bus *bus, *pbus;
+	u32 addr[OF_MAX_ADDR_CELLS];
+	int na, ns, pna, pns;
+	u64 result = OF_BAD_ADDR;
+
+	DBG("OF: ** translation for device %s **\n", dev->full_name);
+
+	/* Increase refcount at current level */
+	of_node_get(dev);
+
+	/* Get parent & match bus type */
+	parent = of_get_parent(dev);
+	if (parent == NULL)
+		goto bail;
+	bus = of_match_bus(parent);
+
+	/* Cound address cells & copy address locally */
+	bus->count_cells(dev, &na, &ns);
+	if (!OF_CHECK_COUNTS(na, ns)) {
+		printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
+		       dev->full_name);
+		goto bail;
+	}
+	memcpy(addr, in_addr, na * 4);
+
+	DBG("OF: bus is %s (na=%d, ns=%d) on %s\n",
+	    bus->name, na, ns, parent->full_name);
+	of_dump_addr("OF: translating address:", addr, na);
+
+	/* Translate */
+	for (;;) {
+		/* Switch to parent bus */
+		of_node_put(dev);
+		dev = parent;
+		parent = of_get_parent(dev);
+
+		/* If root, we have finished */
+		if (parent == NULL) {
+			DBG("OF: reached root node\n");
+			result = of_read_addr(addr, na);
+			break;
+		}
+
+		/* Get new parent bus and counts */
+		pbus = of_match_bus(parent);
+		pbus->count_cells(dev, &pna, &pns);
+		if (!OF_CHECK_COUNTS(pna, pns)) {
+			printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
+			       dev->full_name);
+			break;
+		}
+
+		DBG("OF: parent bus is %s (na=%d, ns=%d) on %s\n",
+		    pbus->name, pna, pns, parent->full_name);
+
+		/* Apply bus translation */
+		if (of_translate_one(dev, bus, pbus, addr, na, ns, pna))
+			break;
+
+		/* Complete the move up one level */
+		na = pna;
+		ns = pns;
+		bus = pbus;
+
+		of_dump_addr("OF: one level translation:", addr, na);
+	}
+ bail:
+	of_node_put(parent);
+	of_node_put(dev);
+
+	return result;
+}
+EXPORT_SYMBOL(of_translate_address);
+
+u32 *of_get_address(struct device_node *dev, int index, u64 *size)
+{
+	u32 *prop;
+	unsigned int psize;
+	struct device_node *parent;
+	struct of_bus *bus;
+	int onesize, i, na, ns;
+
+	/* Get "reg" property */
+	prop = (u32 *)get_property(dev, "reg", &psize);
+	if (prop == NULL)
+		return NULL;
+	psize /= 4;
+
+	/* Get parent & match bus type */
+	parent = of_get_parent(dev);
+	if (parent == NULL)
+		return NULL;
+	bus = of_match_bus(parent);
+	bus->count_cells(dev, &na, &ns);
+	of_node_put(parent);
+	if (!OF_CHECK_COUNTS(na, ns))
+		return NULL;
+
+	onesize = na + ns;
+	for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
+		if (i == index) {
+			if (size)
+				*size = of_read_addr(prop + na, ns);
+			return prop;
+		}
+	return NULL;
+}
+EXPORT_SYMBOL(of_get_address);
+
+u32 *of_get_pci_addr(struct device_node *pd, int bar, u64 *out_size)
+{
+	u32 *prop;
+	unsigned int psize;
+
+	prop = (u32 *)get_property(pd, "assigned-addresses", &psize);
+	if (prop == NULL)
+		return NULL;
+	psize /= 4;
+	for (; psize >= 5; psize -= 5, prop += 5) {
+		if (bar == (((prop[0] & 0xff) - PCI_BASE_ADDRESS_0) / 4)) {
+			if (out_size)
+				*out_size = of_read_addr(prop + 3, 2);
+			return prop;
+		}
+	}
+	return NULL;
+}
+EXPORT_SYMBOL(of_get_pci_addr);
Index: linux-serialfix/include/asm-powerpc/mmu.h
===================================================================
--- linux-serialfix.orig/include/asm-powerpc/mmu.h	2005-11-22 14:41:26.000000000 +1100
+++ linux-serialfix/include/asm-powerpc/mmu.h	2005-11-22 14:41:49.000000000 +1100
@@ -393,6 +393,10 @@
 #define VSID_SCRAMBLE(pvsid)	(((pvsid) * VSID_MULTIPLIER) % VSID_MODULUS)
 #define KERNEL_VSID(ea)		VSID_SCRAMBLE(GET_ESID(ea))
 
+/* Physical address used by some IO functions */
+typedef unsigned long phys_addr_t;
+
+
 #endif /* __ASSEMBLY */
 
 #endif /* CONFIG_PPC64 */
Index: linux-serialfix/include/asm-powerpc/prom.h
===================================================================
--- linux-serialfix.orig/include/asm-powerpc/prom.h	2005-11-22 14:41:26.000000000 +1100
+++ linux-serialfix/include/asm-powerpc/prom.h	2005-11-22 14:41:49.000000000 +1100
@@ -223,5 +223,14 @@
 				int index, const char* name_postfix);
 extern int release_OF_resource(struct device_node* node, int index);
 
+/*
+ * Address translation function(s)
+ */
+#define OF_BAD_ADDR	((u64)-1)
+extern u64 of_translate_address(struct device_node *np, u32 *addr);
+extern u32 *of_get_address(struct device_node *dev, int index, u64 *size);
+extern u32 *of_get_pci_addr(struct device_node *pd, int bar, u64 *out_size);
+
+
 #endif /* __KERNEL__ */
 #endif /* _POWERPC_PROM_H */
Index: linux-serialfix/include/asm-ppc/prom.h
===================================================================
--- linux-serialfix.orig/include/asm-ppc/prom.h	2005-11-22 14:41:26.000000000 +1100
+++ linux-serialfix/include/asm-ppc/prom.h	2005-11-22 14:41:49.000000000 +1100
@@ -136,5 +136,15 @@
 #define PTRRELOC(x)	((typeof(x))add_reloc_offset((unsigned long)(x)))
 #define PTRUNRELOC(x)	((typeof(x))sub_reloc_offset((unsigned long)(x)))
 
+
+/*
+ * Address translation function(s)
+ */
+#define OF_BAD_ADDR	((u64)-1)
+extern u64 of_translate_address(struct device_node *np, u32 *addr);
+extern u32 *of_get_address(struct device_node *dev, int index, u64 *size);
+extern u32 *of_get_pci_addr(struct device_node *pd, int bar, u64 *out_size);
+
+
 #endif /* _PPC_PROM_H */
 #endif /* __KERNEL__ */

^ permalink raw reply

* anyone used i2c-algo-8260 for the MPC8272 family?
From: Ron Kellam @ 2005-11-22  3:19 UTC (permalink / raw)
  To: linuxppc-embedded
In-Reply-To: <qpr2o1dhoelk8rt11jl3lbangfs0hj710a@4ax.com>

I'm posting this just to capture the solution.  An actual patch may
follow sometime down the track.

By looking over some code I got from Dave Bruce (thanks Dave!), I
tried replacing the use of __pa() with iopa().  With just this change,
the driver now "works for me".

I'm not sure of the details of the difference.  Anyone care to
elaborate?

By the way, as stated in another thread, I'm working with a
denx-2.4.25 kernel.

Regards,
Ron Kellam.

^ permalink raw reply

* Re: PowerBook5,8 - TrackPad update
From: Parag Warudkar @ 2005-11-22  0:08 UTC (permalink / raw)
  To: Parag Warudkar; +Cc: linuxppc-dev, debian-powerpc, linux-kernel
In-Reply-To: <70210ED5-37CA-40BC-8293-FF1DAA3E8BD5@comcast.net>

[-- Attachment #1: Type: text/plain, Size: 328 bytes --]


On Nov 21, 2005, at 6:57 PM, Parag Warudkar wrote:

> <appletouch.c>

Oops  - originally attached file has wrong comment, this one has  
fixed comments - other wise the code is same.
Also, one more change required was to add ATP_DEVICE(0x0214) into the  
usb_device_id table which I forgot
to mention in the last mail.

Parag


[-- Attachment #2: appletouch.c --]
[-- Type: application/octet-stream, Size: 14771 bytes --]

/*
 * Apple USB Touchpad (for post-February 2005 PowerBooks) driver
 *
 * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
 * Copyright (C) 2005      Johannes Berg (johannes@sipsolutions.net)
 * Copyright (C) 2005      Stelian Pop (stelian@popies.net)
 * Copyright (C) 2005      Frank Arnold (frank@scirocco-5v-turbo.de)
 * Copyright (C) 2005      Peter Osterlund (petero2@telia.com)
 * Copyright (C) 2005      Parag Warudkar (parag.warudkar@gmail.com)
 *
 * Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
 * Nov 2005 - Parag Warudkar 
 * 	o Add preliminary support for Oct 2005 Powerbooks 
 * 	  (Doesn't work fully yet)
 * 	o Converted to use input_device_allocate()
 * 	o Added ability to export data via relayfs
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/input.h>
#include <linux/usb_input.h>
#include <linux/debugfs.h>
#include <linux/relayfs_fs.h>

#if !defined(PPC) && !defined(POWERPC)
#error "Module intended for PowerPC based PowerBooks!"
#endif

/* Apple has powerbooks which have the keyboard with different Product IDs */
#define APPLE_VENDOR_ID		0x05AC

#define ATP_DEVICE(prod)					\
	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |   		\
		       USB_DEVICE_ID_MATCH_INT_CLASS |		\
		       USB_DEVICE_ID_MATCH_INT_PROTOCOL,	\
	.idVendor = APPLE_VENDOR_ID,				\
	.idProduct = (prod),					\
	.bInterfaceClass = 0x03,				\
	.bInterfaceProtocol = 0x02

/* table of devices that work with this driver */
static struct usb_device_id atp_table [] = {
	{ ATP_DEVICE(0x020E) },
	{ ATP_DEVICE(0x020F) },
	{ ATP_DEVICE(0x030A) },
	{ ATP_DEVICE(0x030B) },
	{ ATP_DEVICE(0x0214) },			/* PowerBooks Late Oct 2005 */
	{ }					/* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, atp_table);

struct rchan* rch=NULL;
struct rchan_callbacks* rcb =NULL;
/* Debugfs data */
struct atp_dbg_data {

};

/*
 * number of sensors. Note that only 16 instead of 26 X (horizontal)
 * sensors exist on 12" and 15" PowerBooks. All models have 16 Y
 * (vertical) sensors.
 */
#define ATP_MAX_XSENSORS	26
#define ATP_MAX_YSENSORS	16

/* amount of fuzz this touchpad generates */
#define ATP_FUZZ	16

/* maximum pressure this driver will report */
#define ATP_PRESSURE	300
/*
 * multiplication factor for the X and Y coordinates.
 * We try to keep the touchpad aspect ratio while still doing only simple
 * arithmetics.
 * The factors below give coordinates like:
 * 	0 <= x <  960 on 12" and 15" Powerbooks
 * 	0 <= x < 1600 on 17" Powerbooks
 * 	0 <= y <  646
 */
#define ATP_XFACT	64
#define ATP_YFACT	43

/*
 * Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
 * ignored.
 */
#define ATP_THRESHOLD	 5

typedef enum PB_MAC_TYPE {
	PowerBook54,
	PowerBook56,
	PowerBook58,
	PowerBook59,
	Unknown
}PB_MAC_TYPE;


/* Structure to hold all of our device specific stuff */
struct atp {
	struct usb_device *	udev;		/* usb device */
	struct urb *		urb;		/* usb request block */
	signed char *		data;		/* transferred data */
	int			open;		/* non-zero if opened */
	struct input_dev	*input;		/* input dev */
	int			valid;		/* are the sensors valid ? */
	int			x_old;		/* last reported x/y, */
	int			y_old;		/* used for smoothing */
						/* current value of the sensors */
	signed char		xy_cur[ATP_MAX_XSENSORS + ATP_MAX_YSENSORS];
						/* last value of the sensors */
	signed char		xy_old[ATP_MAX_XSENSORS + ATP_MAX_YSENSORS];
						/* accumulated sensors */
	int			xy_acc[ATP_MAX_XSENSORS + ATP_MAX_YSENSORS];
	PB_MAC_TYPE		mtype;
};

#define dbg_dump(msg, tab) \
	if (debug > 1) {						\
		int i;							\
		printk("appletouch: %s %lld", msg, (long long)jiffies); \
		for (i = 0; i < ATP_MAX_XSENSORS + ATP_MAX_YSENSORS; i++)	\
			printk(" %02x", tab[i]); 			\
		printk("\n"); 						\
	}

#define dprintk(format, a...) 						\
	do {								\
		if (debug) printk(format, ##a);				\
	} while (0)

MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold, Parag Warudkar");
MODULE_DESCRIPTION("Apple Al PowerBook USB touchpad driver");
MODULE_LICENSE("GPL");

static int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Activate debugging output");

/* USB URB buffer length - 81 works on Feb 05 models.
 * Oct 05 models fail with EOVERFLOW, detected and corrected 
 * separately.
 */
static int data_len = 81;
module_param(data_len, int, 0644);
MODULE_PARM_DESC(data_len, "Specify USB URB buffer length (auto-detected normally)");

PB_MAC_TYPE atp_machine_detect()
{
	if(machine_is_compatible("PowerBook5,9"))
		return PowerBook59;
	if(machine_is_compatible("PowerBook5,8"))
		return PowerBook58;
	if(machine_is_compatible("PowerBook5,6"))
		return PowerBook56;
	if (machine_is_compatible("PowerBook5,4"))
		return PowerBook54;
	return Unknown;
}

static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
			     int *z, int *fingers)
{
	int i;
	/* values to calculate mean */
	int pcum = 0, psum = 0;

	*fingers = 0;

	for (i = 0; i < nb_sensors; i++) {
		if (xy_sensors[i] < ATP_THRESHOLD)
			continue;
		if ((i - 1 < 0) || (xy_sensors[i - 1] < ATP_THRESHOLD))
			(*fingers)++;
		pcum += xy_sensors[i] * i;
		psum += xy_sensors[i];
	}

	if (psum > 0) {
		*z = psum;
		return pcum * fact / psum;
	}

	return 0;
}

static inline void atp_report_fingers(struct input_dev *input, int fingers)
{
	input_report_key(input, BTN_TOOL_FINGER, fingers == 1);
	input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2);
	input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
}

static void atp_complete(struct urb* urb, struct pt_regs* regs)
{
	int x, y, x_z, y_z, x_f, y_f;
	int retval, i;
	struct atp *dev = urb->context;
	static int ctr=1;
	switch (urb->status) {
	case 0:
		/* success */
		break;
	case -EOVERFLOW:
		if(ctr){
			printk("appletouch: OVERFLOW with data length %d\n", data_len);
			ctr=0;
		}
	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
		/* This urb is terminated, clean up */
		dbg("%s - urb shutting down with status: %d",
		    __FUNCTION__, urb->status);
		return;
	default:
		goto exit;
	}

	/* drop incomplete datasets */
	if (dev->urb->actual_length != data_len) {
		printk(KERN_WARNING "appletouch: incomplete data package length %d\n", dev->urb->actual_length);
		goto exit;
	}
	if ( dev->data ) {
		relay_write(rch, dev->data, dev->urb->actual_length);
	}

	/* reorder the sensors values */
	for (i = 0; i < 8; i++) {
		/* X values */
		dev->xy_cur[i     ] = dev->data[5 * i +  2];
		dev->xy_cur[i +  8] = dev->data[5 * i +  4];
		dev->xy_cur[i + 16] = dev->data[5 * i + 42];
		if (i < 2)
			dev->xy_cur[i + 24] = dev->data[5 * i + 44];

		/* Y values */
		dev->xy_cur[i + 26] = dev->data[5 * i +  1];
		dev->xy_cur[i + 34] = dev->data[5 * i +  3];
	}

	dbg_dump("sample", dev->xy_cur);

	if (!dev->valid) {
		/* first sample */
		dev->valid = 1;
		dev->x_old = dev->y_old = -1;
		memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));

		/* 17" Powerbooks have 10 extra X sensors */
		for (i = 16; i < ATP_MAX_XSENSORS; i++)
			if (dev->xy_cur[i]) {
				printk("appletouch: 17\" model detected.\n");
				input_set_abs_params(dev->input, ABS_X, 0,
				     		     (ATP_MAX_XSENSORS - 1) *
						     ATP_XFACT - 1,
						     ATP_FUZZ, 0);
				break;
			}

		goto exit;
	}

	for (i = 0; i < ATP_MAX_XSENSORS + ATP_MAX_YSENSORS; i++) {
		/* accumulate the change */
		signed char change = dev->xy_old[i] - dev->xy_cur[i];
		dev->xy_acc[i] -= change;

		/* prevent down drifting */
		if (dev->xy_acc[i] < 0)
			dev->xy_acc[i] = 0;
	}

	memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));

	dbg_dump("accumulator", dev->xy_acc);

	x = atp_calculate_abs(dev->xy_acc, ATP_MAX_XSENSORS,
			      ATP_XFACT, &x_z, &x_f);
	y = atp_calculate_abs(dev->xy_acc + ATP_MAX_XSENSORS, ATP_MAX_YSENSORS,
			      ATP_YFACT, &y_z, &y_f);

	if (x && y) {
		if (dev->x_old != -1) {
			x = (dev->x_old * 3 + x) >> 2;
			y = (dev->y_old * 3 + y) >> 2;
			dev->x_old = x;
			dev->y_old = y;

			if (debug > 1)
				printk("appletouch: X: %3d Y: %3d "
				       "Xz: %3d Yz: %3d\n",
				       x, y, x_z, y_z);

			input_report_key(dev->input, BTN_TOUCH, 1);
			input_report_abs(dev->input, ABS_X, x);
			input_report_abs(dev->input, ABS_Y, y);
			input_report_abs(dev->input, ABS_PRESSURE,
					 min(ATP_PRESSURE, x_z + y_z));
			atp_report_fingers(dev->input, max(x_f, y_f));
		}
		dev->x_old = x;
		dev->y_old = y;
	}
	else if (!x && !y) {

		dev->x_old = dev->y_old = -1;
		input_report_key(dev->input, BTN_TOUCH, 0);
		input_report_abs(dev->input, ABS_PRESSURE, 0);
		atp_report_fingers(dev->input, 0);

		/* reset the accumulator on release */
		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
	}

	input_report_key(dev->input, BTN_LEFT, !!dev->data[data_len-1]);

	input_sync(dev->input);

exit:
	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
	if (retval) {
		err("%s - usb_submit_urb failed with result %d",
		    __FUNCTION__, retval);
	}
}

static int atp_open(struct input_dev *input)
{
	struct atp *dev = input->private;
	if (usb_submit_urb(dev->urb, GFP_ATOMIC)) {
		printk("atp: usb_submit_urb Error\n");
		return -EIO;
	}

	dev->open = 1;
	return 0;
}

static void atp_close(struct input_dev *input)
{
	struct atp *dev = input->private;
	usb_kill_urb(dev->urb);
	dev->open = 0;
}

static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id)
{
	struct atp *dev = NULL;
	struct usb_host_interface *iface_desc;
	struct usb_endpoint_descriptor *endpoint;
	int int_in_endpointAddr = 0;
	int i, retval = -ENOMEM;
	int regret=0;
	printk(KERN_INFO "AppleTouch: Inside atp_probe\n");
	
	/* allocate memory for our device state and initialize it */
	dev = kmalloc(sizeof(struct atp), GFP_KERNEL);
	if (dev == NULL) {
		err("Out of memory");
		goto err_kmalloc;
	}
	
	memset(dev, 0, sizeof(struct atp));
	dev->mtype = atp_machine_detect();
	if(dev->mtype == Unknown)
	{
		printk(KERN_INFO "appletouch: Unknown machine type!\n");
		return -ENODEV;
	}
	if(dev->mtype > 1)
	{
		if(data_len < 256)
		{
			printk(KERN_INFO "appletouch: Setting URB buffer length to 256\n");
			data_len=256;
		}
	}

	dev->udev = interface_to_usbdev(iface);

	printk("appletouch: Machine type is: %d\n", dev->mtype);
	/* set up the endpoint information */
	/* use only the first interrupt-in endpoint */
	iface_desc = iface->cur_altsetting;
	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
		endpoint = &iface_desc->endpoint[i].desc;
		if (!int_in_endpointAddr &&
		    (endpoint->bEndpointAddress & USB_DIR_IN) &&
		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
					== USB_ENDPOINT_XFER_INT)) {
			/* we found an interrupt in endpoint */
			int_in_endpointAddr = endpoint->bEndpointAddress;
			printk(KERN_INFO "AppleTouch:atp_probe Found interrupt in endpoint: %d\n", int_in_endpointAddr);
			break;
		}
	}
	if (!int_in_endpointAddr) {
		retval = -EIO;
		err("Could not find int-in endpoint");
		goto err_endpoint;
	}

	/* save our data pointer in this interface device */
	usb_set_intfdata(iface, dev);

	dev->urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!dev->urb) {
		retval = -ENOMEM;
		goto err_usballoc;
	}
	dev->data = usb_buffer_alloc(dev->udev, data_len, GFP_KERNEL,
				     &dev->urb->transfer_dma);
	if (!dev->data) {
		retval = -ENOMEM;
		goto err_usbbufalloc;
	}
	usb_fill_int_urb(dev->urb, dev->udev,
			 usb_rcvintpipe(dev->udev, int_in_endpointAddr),
			 dev->data, data_len, atp_complete, dev, 1);

	dev->input = input_allocate_device();
	dev->input->name = "appletouch";
	dev->input->dev = &iface->dev;
	dev->input->private = dev;
	dev->input->open = atp_open;
	dev->input->close = atp_close;

	usb_to_input_id(dev->udev, &dev->input->id);

	set_bit(EV_ABS, dev->input->evbit);

	/*
	 * 12" and 15" Powerbooks only have 16 x sensors,
	 * 17" models are detected later.
	 */
	input_set_abs_params(dev->input, ABS_X, 0,
			     (16 - 1) * ATP_XFACT - 1, ATP_FUZZ, 0);
	input_set_abs_params(dev->input, ABS_Y, 0,
			     (ATP_MAX_YSENSORS - 1) * ATP_YFACT - 1, ATP_FUZZ, 0);
	input_set_abs_params(dev->input, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);

	set_bit(EV_KEY, dev->input->evbit);
	set_bit(BTN_TOUCH, dev->input->keybit);
	set_bit(BTN_TOOL_FINGER, dev->input->keybit);
	set_bit(BTN_TOOL_DOUBLETAP, dev->input->keybit);
	set_bit(BTN_TOOL_TRIPLETAP, dev->input->keybit);
	set_bit(BTN_LEFT, dev->input->keybit
			
			);

	regret = input_register_device(dev->input);

	printk(KERN_INFO "input: appletouch connected, Reg code :%d\n", regret);

	return 0;

err_usbbufalloc:
	usb_free_urb(dev->urb);
err_usballoc:
	usb_set_intfdata(iface, NULL);
err_endpoint:
	kfree(dev);
err_kmalloc:
	return retval;
}

static void atp_disconnect(struct usb_interface *iface)
{
	struct atp *dev = usb_get_intfdata(iface);

	usb_set_intfdata(iface, NULL);
	if (dev) {
		usb_kill_urb(dev->urb);
		input_unregister_device(dev->input);
		usb_free_urb(dev->urb);
		usb_buffer_free(dev->udev, data_len,
				dev->data, dev->urb->transfer_dma);
		kfree(dev);
	}
	printk(KERN_INFO "input: appletouch disconnected\n");
}

static int atp_suspend(struct usb_interface *iface, pm_message_t message)
{
	struct atp *dev = usb_get_intfdata(iface);
	usb_kill_urb(dev->urb);
	dev->valid = 0;
	return 0;
}

static int atp_resume(struct usb_interface *iface)
{
	struct atp *dev = usb_get_intfdata(iface);
	if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
		return -EIO;

	return 0;
}

static struct usb_driver atp_driver = {
	.owner		= THIS_MODULE,
	.name		= "appletouch",
	.probe		= atp_probe,
	.disconnect	= atp_disconnect,
	.suspend	= atp_suspend,
	.resume		= atp_resume,
	.id_table	= atp_table,
};


static int __init atp_init(void)
{
	rcb  = kmalloc(sizeof(struct rchan_callbacks), GFP_KERNEL);
	rcb->subbuf_start = NULL;
	rcb->buf_mapped = NULL;
	rcb->buf_unmapped = NULL;
	rch = relay_open("atpdata", NULL, 256, 256, NULL);
	if (!rch) return -ENOMEM;
	return usb_register(&atp_driver);
}

static void __exit atp_exit(void)
{
	relay_close(rch);
	kfree(rcb);
	usb_deregister(&atp_driver);
}

module_init(atp_init);
module_exit(atp_exit);

^ permalink raw reply

* Re: PowerBook5,8 - TrackPad update
From: Parag Warudkar @ 2005-11-21 23:57 UTC (permalink / raw)
  To: Parag Warudkar; +Cc: linuxppc-dev, debian-powerpc, linux-kernel
In-Reply-To: <111520052143.16540.437A5680000BE8A60000409C220076369200009A9B9CD3040A029D0A05@comcast.net>

[-- Attachment #1: Type: text/plain, Size: 1833 bytes --]


On Nov 15, 2005, at 4:43 PM, Parag Warudkar wrote:

> Just a heads up - After some lame hacking I finally have got the  
> trackpad on the PB5,8  (15" Late Oct 2005) to work.
>
> Ok, here is the current definition of working - mouse moves but for  
> that you have to roll over on the trackpad ;)!
>
> Currently negotiating with it so that it's happy with the fingers!! :D
>
> Will post the modified source once for appletouch once it starts  
> working acceptably.
>

Ok, the format interpretation of the trackpad data is taking up more  
of my time and brain than I was happy with! :)

If someone wants to take this up, here is something small to begin  
with -

So far the three simple changes that are required for the original  
appletouch.c in order to get it to produce _some_ wayward mouse  
movements and reliable button clicks at the least are

1) #define ATP_DATASIZE 81 - This needs to be made a module parameter  
and if not specified should be detected and set in code - to 81 if  
the machine_is_compatible() with the older powerbooks and 256 if the  
machine_is_compatible() with newer (Oct 05) PowerBooks

2) Convert the appletouch.c to use input_allocate_device()

3) Change the code to lookup either the 80th or 255th byte for button  
press depending upon it's old or new trackpad

Attached is the code which takes care of above 3 - tested but it  
should be considered half baked for obvious reasons and in addition I  
added some relayfs write calls so the received data can be sampled by  
writing a user space client for reading the relayfs file. The code  
might break the existing (old) trackpads as the detection might not  
be correct.

Needless to say the complete logic of interpreting the data and  
determining pressure and movement needs  to be changed such that it  
works for old as well as new.


[-- Attachment #2: appletouch.c --]
[-- Type: application/octet-stream, Size: 14729 bytes --]

/*
 * Apple USB Touchpad (for post-February 2005 PowerBooks) driver
 *
 * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
 * Copyright (C) 2005      Johannes Berg (johannes@sipsolutions.net)
 * Copyright (C) 2005      Stelian Pop (stelian@popies.net)
 * Copyright (C) 2005      Frank Arnold (frank@scirocco-5v-turbo.de)
 * Copyright (C) 2005      Peter Osterlund (petero2@telia.com)
 * Copyright (C) 2005      Parag Warudkar (parag.warudkar@gmail.com)
 *
 * Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
 * Nov 2005 - Parag Warudkar 
 * 	o Added support for Oct 2005 Powerbooks
 * 	o Converted to use input_device_allocate()
 * 	o Added ability to export data via debugfs
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/input.h>
#include <linux/usb_input.h>
#include <linux/debugfs.h>
#include <linux/relayfs_fs.h>

#if !defined(PPC) && !defined(POWERPC)
#error "Module intended for PowerPC based PowerBooks!"
#endif

/* Apple has powerbooks which have the keyboard with different Product IDs */
#define APPLE_VENDOR_ID		0x05AC

#define ATP_DEVICE(prod)					\
	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |   		\
		       USB_DEVICE_ID_MATCH_INT_CLASS |		\
		       USB_DEVICE_ID_MATCH_INT_PROTOCOL,	\
	.idVendor = APPLE_VENDOR_ID,				\
	.idProduct = (prod),					\
	.bInterfaceClass = 0x03,				\
	.bInterfaceProtocol = 0x02

/* table of devices that work with this driver */
static struct usb_device_id atp_table [] = {
	{ ATP_DEVICE(0x020E) },
	{ ATP_DEVICE(0x020F) },
	{ ATP_DEVICE(0x030A) },
	{ ATP_DEVICE(0x030B) },
	{ ATP_DEVICE(0x0214) },			/* PowerBooks Late Oct 2005 */
	{ }					/* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, atp_table);

struct rchan* rch=NULL;
struct rchan_callbacks* rcb =NULL;
/* Debugfs data */
struct atp_dbg_data {

};

/*
 * number of sensors. Note that only 16 instead of 26 X (horizontal)
 * sensors exist on 12" and 15" PowerBooks. All models have 16 Y
 * (vertical) sensors.
 */
#define ATP_MAX_XSENSORS	26
#define ATP_MAX_YSENSORS	16

/* amount of fuzz this touchpad generates */
#define ATP_FUZZ	16

/* maximum pressure this driver will report */
#define ATP_PRESSURE	300
/*
 * multiplication factor for the X and Y coordinates.
 * We try to keep the touchpad aspect ratio while still doing only simple
 * arithmetics.
 * The factors below give coordinates like:
 * 	0 <= x <  960 on 12" and 15" Powerbooks
 * 	0 <= x < 1600 on 17" Powerbooks
 * 	0 <= y <  646
 */
#define ATP_XFACT	64
#define ATP_YFACT	43

/*
 * Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
 * ignored.
 */
#define ATP_THRESHOLD	 5

typedef enum PB_MAC_TYPE {
	PowerBook54,
	PowerBook56,
	PowerBook58,
	PowerBook59,
	Unknown
}PB_MAC_TYPE;


/* Structure to hold all of our device specific stuff */
struct atp {
	struct usb_device *	udev;		/* usb device */
	struct urb *		urb;		/* usb request block */
	signed char *		data;		/* transferred data */
	int			open;		/* non-zero if opened */
	struct input_dev	*input;		/* input dev */
	int			valid;		/* are the sensors valid ? */
	int			x_old;		/* last reported x/y, */
	int			y_old;		/* used for smoothing */
						/* current value of the sensors */
	signed char		xy_cur[ATP_MAX_XSENSORS + ATP_MAX_YSENSORS];
						/* last value of the sensors */
	signed char		xy_old[ATP_MAX_XSENSORS + ATP_MAX_YSENSORS];
						/* accumulated sensors */
	int			xy_acc[ATP_MAX_XSENSORS + ATP_MAX_YSENSORS];
	PB_MAC_TYPE		mtype;
};

#define dbg_dump(msg, tab) \
	if (debug > 1) {						\
		int i;							\
		printk("appletouch: %s %lld", msg, (long long)jiffies); \
		for (i = 0; i < ATP_MAX_XSENSORS + ATP_MAX_YSENSORS; i++)	\
			printk(" %02x", tab[i]); 			\
		printk("\n"); 						\
	}

#define dprintk(format, a...) 						\
	do {								\
		if (debug) printk(format, ##a);				\
	} while (0)

MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold, Parag Warudkar");
MODULE_DESCRIPTION("Apple Al PowerBook USB touchpad driver");
MODULE_LICENSE("GPL");

static int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Activate debugging output");

/* USB URB buffer length - 81 works on Feb 05 models.
 * Oct 05 models fail with EOVERFLOW, detected and corrected 
 * separately.
 */
static int data_len = 81;
module_param(data_len, int, 0644);
MODULE_PARM_DESC(data_len, "Specify USB URB buffer length (auto-detected normally)");

PB_MAC_TYPE atp_machine_detect()
{
	if(machine_is_compatible("PowerBook5,9"))
		return PowerBook59;
	if(machine_is_compatible("PowerBook5,8"))
		return PowerBook58;
	if(machine_is_compatible("PowerBook5,6"))
		return PowerBook56;
	if (machine_is_compatible("PowerBook5,4"))
		return PowerBook54;
	return Unknown;
}

static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
			     int *z, int *fingers)
{
	int i;
	/* values to calculate mean */
	int pcum = 0, psum = 0;

	*fingers = 0;

	for (i = 0; i < nb_sensors; i++) {
		if (xy_sensors[i] < ATP_THRESHOLD)
			continue;
		if ((i - 1 < 0) || (xy_sensors[i - 1] < ATP_THRESHOLD))
			(*fingers)++;
		pcum += xy_sensors[i] * i;
		psum += xy_sensors[i];
	}

	if (psum > 0) {
		*z = psum;
		return pcum * fact / psum;
	}

	return 0;
}

static inline void atp_report_fingers(struct input_dev *input, int fingers)
{
	input_report_key(input, BTN_TOOL_FINGER, fingers == 1);
	input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2);
	input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
}

static void atp_complete(struct urb* urb, struct pt_regs* regs)
{
	int x, y, x_z, y_z, x_f, y_f;
	int retval, i;
	struct atp *dev = urb->context;
	static int ctr=1;
	switch (urb->status) {
	case 0:
		/* success */
		break;
	case -EOVERFLOW:
		if(ctr){
			printk("appletouch: OVERFLOW with data length %d\n", data_len);
			ctr=0;
		}
	case -ECONNRESET:
	case -ENOENT:
	case -ESHUTDOWN:
		/* This urb is terminated, clean up */
		dbg("%s - urb shutting down with status: %d",
		    __FUNCTION__, urb->status);
		return;
	default:
		goto exit;
	}

	/* drop incomplete datasets */
	if (dev->urb->actual_length != data_len) {
		printk(KERN_WARNING "appletouch: incomplete data package length %d\n", dev->urb->actual_length);
		goto exit;
	}
	if ( dev->data ) {
		relay_write(rch, dev->data, dev->urb->actual_length);
	}

	/* reorder the sensors values */
	for (i = 0; i < 8; i++) {
		/* X values */
		dev->xy_cur[i     ] = dev->data[5 * i +  2];
		dev->xy_cur[i +  8] = dev->data[5 * i +  4];
		dev->xy_cur[i + 16] = dev->data[5 * i + 42];
		if (i < 2)
			dev->xy_cur[i + 24] = dev->data[5 * i + 44];

		/* Y values */
		dev->xy_cur[i + 26] = dev->data[5 * i +  1];
		dev->xy_cur[i + 34] = dev->data[5 * i +  3];
	}

	dbg_dump("sample", dev->xy_cur);

	if (!dev->valid) {
		/* first sample */
		dev->valid = 1;
		dev->x_old = dev->y_old = -1;
		memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));

		/* 17" Powerbooks have 10 extra X sensors */
		for (i = 16; i < ATP_MAX_XSENSORS; i++)
			if (dev->xy_cur[i]) {
				printk("appletouch: 17\" model detected.\n");
				input_set_abs_params(dev->input, ABS_X, 0,
				     		     (ATP_MAX_XSENSORS - 1) *
						     ATP_XFACT - 1,
						     ATP_FUZZ, 0);
				break;
			}

		goto exit;
	}

	for (i = 0; i < ATP_MAX_XSENSORS + ATP_MAX_YSENSORS; i++) {
		/* accumulate the change */
		signed char change = dev->xy_old[i] - dev->xy_cur[i];
		dev->xy_acc[i] -= change;

		/* prevent down drifting */
		if (dev->xy_acc[i] < 0)
			dev->xy_acc[i] = 0;
	}

	memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));

	dbg_dump("accumulator", dev->xy_acc);

	x = atp_calculate_abs(dev->xy_acc, ATP_MAX_XSENSORS,
			      ATP_XFACT, &x_z, &x_f);
	y = atp_calculate_abs(dev->xy_acc + ATP_MAX_XSENSORS, ATP_MAX_YSENSORS,
			      ATP_YFACT, &y_z, &y_f);

	if (x && y) {
		if (dev->x_old != -1) {
			x = (dev->x_old * 3 + x) >> 2;
			y = (dev->y_old * 3 + y) >> 2;
			dev->x_old = x;
			dev->y_old = y;

			if (debug > 1)
				printk("appletouch: X: %3d Y: %3d "
				       "Xz: %3d Yz: %3d\n",
				       x, y, x_z, y_z);

			input_report_key(dev->input, BTN_TOUCH, 1);
			input_report_abs(dev->input, ABS_X, x);
			input_report_abs(dev->input, ABS_Y, y);
			input_report_abs(dev->input, ABS_PRESSURE,
					 min(ATP_PRESSURE, x_z + y_z));
			atp_report_fingers(dev->input, max(x_f, y_f));
		}
		dev->x_old = x;
		dev->y_old = y;
	}
	else if (!x && !y) {

		dev->x_old = dev->y_old = -1;
		input_report_key(dev->input, BTN_TOUCH, 0);
		input_report_abs(dev->input, ABS_PRESSURE, 0);
		atp_report_fingers(dev->input, 0);

		/* reset the accumulator on release */
		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
	}

	input_report_key(dev->input, BTN_LEFT, !!dev->data[data_len-1]);

	input_sync(dev->input);

exit:
	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
	if (retval) {
		err("%s - usb_submit_urb failed with result %d",
		    __FUNCTION__, retval);
	}
}

static int atp_open(struct input_dev *input)
{
	struct atp *dev = input->private;
	if (usb_submit_urb(dev->urb, GFP_ATOMIC)) {
		printk("atp: usb_submit_urb Error\n");
		return -EIO;
	}

	dev->open = 1;
	return 0;
}

static void atp_close(struct input_dev *input)
{
	struct atp *dev = input->private;
	usb_kill_urb(dev->urb);
	dev->open = 0;
}

static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id)
{
	struct atp *dev = NULL;
	struct usb_host_interface *iface_desc;
	struct usb_endpoint_descriptor *endpoint;
	int int_in_endpointAddr = 0;
	int i, retval = -ENOMEM;
	int regret=0;
	printk(KERN_INFO "AppleTouch: Inside atp_probe\n");
	
	/* allocate memory for our device state and initialize it */
	dev = kmalloc(sizeof(struct atp), GFP_KERNEL);
	if (dev == NULL) {
		err("Out of memory");
		goto err_kmalloc;
	}
	
	memset(dev, 0, sizeof(struct atp));
	dev->mtype = atp_machine_detect();
	if(dev->mtype == Unknown)
	{
		printk(KERN_INFO "appletouch: Unknown machine type!\n");
		return -ENODEV;
	}
	if(dev->mtype > 1)
	{
		if(data_len < 256)
		{
			printk(KERN_INFO "appletouch: Setting URB buffer length to 256\n");
			data_len=256;
		}
	}

	dev->udev = interface_to_usbdev(iface);

	printk("appletouch: Machine type is: %d\n", dev->mtype);
	/* set up the endpoint information */
	/* use only the first interrupt-in endpoint */
	iface_desc = iface->cur_altsetting;
	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
		endpoint = &iface_desc->endpoint[i].desc;
		if (!int_in_endpointAddr &&
		    (endpoint->bEndpointAddress & USB_DIR_IN) &&
		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
					== USB_ENDPOINT_XFER_INT)) {
			/* we found an interrupt in endpoint */
			int_in_endpointAddr = endpoint->bEndpointAddress;
			printk(KERN_INFO "AppleTouch:atp_probe Found interrupt in endpoint: %d\n", int_in_endpointAddr);
			break;
		}
	}
	if (!int_in_endpointAddr) {
		retval = -EIO;
		err("Could not find int-in endpoint");
		goto err_endpoint;
	}

	/* save our data pointer in this interface device */
	usb_set_intfdata(iface, dev);

	dev->urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!dev->urb) {
		retval = -ENOMEM;
		goto err_usballoc;
	}
	dev->data = usb_buffer_alloc(dev->udev, data_len, GFP_KERNEL,
				     &dev->urb->transfer_dma);
	if (!dev->data) {
		retval = -ENOMEM;
		goto err_usbbufalloc;
	}
	usb_fill_int_urb(dev->urb, dev->udev,
			 usb_rcvintpipe(dev->udev, int_in_endpointAddr),
			 dev->data, data_len, atp_complete, dev, 1);

	dev->input = input_allocate_device();
	dev->input->name = "appletouch";
	dev->input->dev = &iface->dev;
	dev->input->private = dev;
	dev->input->open = atp_open;
	dev->input->close = atp_close;

	usb_to_input_id(dev->udev, &dev->input->id);

	set_bit(EV_ABS, dev->input->evbit);

	/*
	 * 12" and 15" Powerbooks only have 16 x sensors,
	 * 17" models are detected later.
	 */
	input_set_abs_params(dev->input, ABS_X, 0,
			     (16 - 1) * ATP_XFACT - 1, ATP_FUZZ, 0);
	input_set_abs_params(dev->input, ABS_Y, 0,
			     (ATP_MAX_YSENSORS - 1) * ATP_YFACT - 1, ATP_FUZZ, 0);
	input_set_abs_params(dev->input, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);

	set_bit(EV_KEY, dev->input->evbit);
	set_bit(BTN_TOUCH, dev->input->keybit);
	set_bit(BTN_TOOL_FINGER, dev->input->keybit);
	set_bit(BTN_TOOL_DOUBLETAP, dev->input->keybit);
	set_bit(BTN_TOOL_TRIPLETAP, dev->input->keybit);
	set_bit(BTN_LEFT, dev->input->keybit
			
			);

	regret = input_register_device(dev->input);

	printk(KERN_INFO "input: appletouch connected, Reg code :%d\n", regret);

	return 0;

err_usbbufalloc:
	usb_free_urb(dev->urb);
err_usballoc:
	usb_set_intfdata(iface, NULL);
err_endpoint:
	kfree(dev);
err_kmalloc:
	return retval;
}

static void atp_disconnect(struct usb_interface *iface)
{
	struct atp *dev = usb_get_intfdata(iface);

	usb_set_intfdata(iface, NULL);
	if (dev) {
		usb_kill_urb(dev->urb);
		input_unregister_device(dev->input);
		usb_free_urb(dev->urb);
		usb_buffer_free(dev->udev, data_len,
				dev->data, dev->urb->transfer_dma);
		kfree(dev);
	}
	printk(KERN_INFO "input: appletouch disconnected\n");
}

static int atp_suspend(struct usb_interface *iface, pm_message_t message)
{
	struct atp *dev = usb_get_intfdata(iface);
	usb_kill_urb(dev->urb);
	dev->valid = 0;
	return 0;
}

static int atp_resume(struct usb_interface *iface)
{
	struct atp *dev = usb_get_intfdata(iface);
	if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
		return -EIO;

	return 0;
}

static struct usb_driver atp_driver = {
	.owner		= THIS_MODULE,
	.name		= "appletouch",
	.probe		= atp_probe,
	.disconnect	= atp_disconnect,
	.suspend	= atp_suspend,
	.resume		= atp_resume,
	.id_table	= atp_table,
};


static int __init atp_init(void)
{
	rcb  = kmalloc(sizeof(struct rchan_callbacks), GFP_KERNEL);
	rcb->subbuf_start = NULL;
	rcb->buf_mapped = NULL;
	rcb->buf_unmapped = NULL;
	rch = relay_open("atpdata", NULL, 256, 256, NULL);
	if (!rch) return -ENOMEM;
	return usb_register(&atp_driver);
}

static void __exit atp_exit(void)
{
	relay_close(rch);
	kfree(rcb);
	usb_deregister(&atp_driver);
}

module_init(atp_init);
module_exit(atp_exit);

[-- Attachment #3: Type: text/plain, Size: 10 bytes --]


Parag




^ permalink raw reply

* Re: [PATCH] MTD: Add support for the PM82x Boards.
From: David Woodhouse @ 2005-11-21 20:29 UTC (permalink / raw)
  To: hs; +Cc: linuxppc-dev, linux-mtd
In-Reply-To: <AHEILKONAKAEJPHNMOPNIELBCDAA.hs@denx.de>

On Mon, 2005-11-21 at 11:34 +0100, Heiko Schocher wrote:
> Hello,
> 
> the following Patch (against 2.6 kernel.org tree, COMMIT_ID:
> f093182d313edde9b1f86dbdaf40ba4da2dbd0e7) adds support for
> the NOR flashes and for the DiskOnChip on the PM82X Boards
> from Microsys.

Please use the new DiskOnChip driver in drivers/mtd/nand/ not the old
one.

-- 
dwmw2

^ permalink raw reply

* Re: [PATCH 1/3] ppc32: Fix a few issues in Yucca PCIe functionality
From: Dale Farnsworth @ 2005-11-21 17:47 UTC (permalink / raw)
  To: Ruslan V. Sushko, linuxppc-embedded
In-Reply-To: <1132583152.11785.41.camel@mephisto.spb.rtsoft.ru>

On Mon, Nov 21, 2005 at 02:25:52PM +0000, Ruslan V. Sushko wrote:
> This patch includes following changes:
> 
> 1) Fix wrong PCIe config space address calculation for slot #3 (Using an
> signed integer for port numbering will cause wrong address accessing)
> 2) Fix the PCI bus numbering assignment. This will be an issues if more
> than one PCI card is inserted.
> 3) Add verbose error checking.
> 4) Remove commented or unused lines.
> 
> Signed-off-by: Ruslan V. Sushko <rsushko@ru.mvista.com>

There are a couple of whitespace/style problems in these 3 patches.
Please clean them up and re-submit.

> diff --git a/arch/ppc/platforms/4xx/yucca.c b/arch/ppc/platforms/4xx/yucca.c
> --- a/arch/ppc/platforms/4xx/yucca.c
> +++ b/arch/ppc/platforms/4xx/yucca.c
> @@ -280,12 +276,14 @@ yucca_setup_hoses(void)
>  				  IORESOURCE_MEM,
>  				  name);
>  
> -		hose->first_busno = 0;
> -		hose->last_busno  = 15;
> +		hose->first_busno = bus_no;
> +		hose->last_busno  = 0xFF;
>  		hose_type[hose->index] = HOSE_PCIE0 + i;
>  
>  		ppc440spe_setup_pcie(hose, i);
>  		hose->last_busno = pciauto_bus_scan(hose, hose->first_busno);
> +                bus_no = hose->last_busno + 1;
> +                printk(KERN_INFO "%s: resources allocated\n", name);

The above two lines have leading spaces instead of tabs.  The same
problem exists in several other lines added by the patches.

diff --git a/arch/ppc/syslib/ppc440spe_pcie.c b/arch/ppc/syslib/ppc440spe_pcie.c
--- a/arch/ppc/syslib/ppc440spe_pcie.c
+++ b/arch/ppc/syslib/ppc440spe_pcie.c
> @@ -157,33 +162,37 @@ int ppc440spe_init_pcie(void)
>  	/* Set PLL clock receiver to LVPECL */
>  	SDR_WRITE(PESDR0_PLLLCT1, SDR_READ(PESDR0_PLLLCT1) | 1 << 28);
>  
> -	check_error();
> -
> -	printk(KERN_INFO "PCIE initialization OK\n");
> -
> -	if (!(SDR_READ(PESDR0_PLLLCT2) & 0x10000))
> -		printk(KERN_INFO "PESDR_PLLCT2 resistance calibration failed (0x%08x)\n",
> +	if (check_error()) {
> +                return -1;
> +        }

The above braces aren't needed.

> +
> +	if (!(SDR_READ(PESDR0_PLLLCT2) & 0x10000)) {
> +		printk(KERN_INFO "PCIE: PESDR_PLLCT2 resistance calibration "
> +                                "failed (0x%08x)\n",
>  		       SDR_READ(PESDR0_PLLLCT2));
> +                return -1;
> +        }
>  
>  	/* De-assert reset of PCIe PLL, wait for lock */
>  	SDR_WRITE(PESDR0_PLLLCT1, SDR_READ(PESDR0_PLLLCT1) & ~(1 << 24));
>  	udelay(3);
> +	printk(KERN_INFO "PCIE initialization OK\n");
>  
>  	return 0;
>  }
>  
> -int ppc440spe_init_pcie_rootport(int port)
> +int ppc440spe_init_pcie_rootport(u32 port)
>  {
>  	static int core_init;
>  	void __iomem *utl_base;
> +        int attempts;
>  	u32 val = 0;
> -	int i;
>  
>  	if (!core_init) {
> +		if(ppc440spe_init_pcie()) {
> +			return -1;
> +                }

Again, the above braces are unnecessary.

Thanks,
-Dale Farnsworth

^ permalink raw reply

* anyone used i2c-algo-8260 for the MPC8272 family?
From: Heiko Schocher @ 2005-11-21 16:57 UTC (permalink / raw)
  To: linuxppc-embedded

Hi Ron,

> There are some subtle differences b/t the 8260 family and 8272 family.
> The 8272 family only has 8KB DPRAM (c.f. 16KB), so I believe the
> definition of PROFF_I2C in include/linux/cpm_8260.h should be
> different.  I've changed it from:
> 
> 	#define PROFF_I2C	((16 * 1024) - 64)
> to 
> 	#define PROFF_I2C	((8 * 1024) - 64)
> 
> but still get no I2C activity, and a kernel hang.

I think your change above is right for a MPC8247.

> Anyone have this working, or any thoughts to contribute?

Look at my Patch, which I just posted at linuxppc-dev, which 
(I hope) will help you (!! it is for a MPC8260 based Board):

http://ozlabs.org/pipermail/linuxppc-dev/2005-November/020492.html

or have a look at the Git repository at Denx:

http://source.denx.net/git/linux-2.6-denx.git

where you can find the complete PM82x port for the 2.6.14 Kernel.

Best regards,
Heiko

^ permalink raw reply

* [PATCH] I2C: Added support for the PCF8563 chip.
From: Heiko Schocher @ 2005-11-21 16:00 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: lm-sensors

Hello,

the following Patch (against 2.6 kernel.org tree, COMMIT_ID:
f093182d313edde9b1f86dbdaf40ba4da2dbd0e7) adds support for
the PCF8563 RTC on the PM82X Boards from Microsys.

Signed-off-by: Heiko Schocher <hs@denx.de>

---

 drivers/i2c/chips/Kconfig   |    9 +
 drivers/i2c/chips/Makefile  |    1
 drivers/i2c/chips/pcf8563.c |  549 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/i2c-id.h      |    1
 4 files changed, 560 insertions(+), 0 deletions(-)

diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
index f9fae28..c470b0d 100644
--- a/drivers/i2c/chips/Kconfig
+++ b/drivers/i2c/chips/Kconfig
@@ -46,6 +46,15 @@ config SENSORS_PCF8574
 	  This driver can also be built as a module.  If so, the module
 	  will be called pcf8574.

+config SENSORS_PCF8563
+	tristate "Philips PCF8563 RTC Chip"
+	depends on I2C && EXPERIMENTAL
+	help
+	  If you say yes here you get support for Philips PCF8563 RTC chip.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-pcf8563.
+
 config SENSORS_PCA9539
 	tristate "Philips PCA9539 16-bit I/O port"
 	depends on I2C && EXPERIMENTAL
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
index 46178b5..e37835a 100644
--- a/drivers/i2c/chips/Makefile
+++ b/drivers/i2c/chips/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_SENSORS_MAX6875)	+= max6875
 obj-$(CONFIG_SENSORS_M41T00)	+= m41t00.o
 obj-$(CONFIG_SENSORS_PCA9539)	+= pca9539.o
 obj-$(CONFIG_SENSORS_PCF8574)	+= pcf8574.o
+obj-$(CONFIG_SENSORS_PCF8563)	+= pcf8563.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
 obj-$(CONFIG_SENSORS_RTC8564)	+= rtc8564.o
 obj-$(CONFIG_ISP1301_OMAP)	+= isp1301_omap.o
diff --git a/drivers/i2c/chips/pcf8563.c b/drivers/i2c/chips/pcf8563.c
new file mode 100644
index 0000000..8c4018b
--- /dev/null
+++ b/drivers/i2c/chips/pcf8563.c
@@ -0,0 +1,549 @@
+/*
+ *  linux/drivers/i2c/chips/pcf8563.c
+ *
+ *  Copyright (C) 2002-2004 Stefan Eletzhofer
+ *
+ *	based on linux/drivers/acron/char/pcf8583.c
+ *  Copyright (C) 2000 Russell King
+ *
+ * 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.
+ *
+ * Driver for system3's PHILIPS PCF 8563 chip
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/rtc.h>		/* get the user-level API */
+#include <linux/init.h>
+#include <linux/list.h>
+#include <asm/time.h>
+#include <asm/machdep.h>
+
+#include "rtc8564.h"
+
+#ifdef DEBUG
+# define _DBG(x, fmt, args...) do{ if (debug>=x) printk(KERN_DEBUG"%s: " fmt "\n", __FUNCTION__, ##args); } while(0);
+#else
+# define _DBG(x, fmt, args...) do { } while(0);
+#endif
+
+#define _DBGRTCTM(x, rtctm) if (debug>x) printk("%s: secs=%d, mins=%d, hours=%d, mday=%d, " \
+			"mon=%d, year=%d, wday=%d\n", __FUNCTION__, \
+			(rtctm).tm_sec, (rtctm).tm_min, (rtctm).tm_hour, (rtctm).tm_mday, \
+			(rtctm).tm_mon, (rtctm).tm_year, (rtctm).tm_wday);
+
+struct pcf8563_data {
+	struct i2c_client client;
+	struct list_head list;
+	u16 ctrl;
+};
+
+/*
+ * Internal variables
+ */
+static LIST_HEAD(pcf8563_clients);
+
+static 	struct i2c_client *myclient = NULL;
+
+static inline u8 _pcf8563_ctrl1(struct i2c_client *client)
+{
+	struct pcf8563_data *data = i2c_get_clientdata(client);
+	return data->ctrl & 0xff;
+}
+static inline u8 _pcf8563_ctrl2(struct i2c_client *client)
+{
+	struct pcf8563_data *data = i2c_get_clientdata(client);
+	return (data->ctrl & 0xff00) >> 8;
+}
+
+#define CTRL1(c) _pcf8563_ctrl1(c)
+#define CTRL2(c) _pcf8563_ctrl2(c)
+
+#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10)
+#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
+
+static int debug = 0;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+
+static struct i2c_driver pcf8563_driver;
+
+static int pcf8563_read_mem(struct i2c_client *client, struct mem *mem);
+static int pcf8563_write_mem(struct i2c_client *client, struct mem *mem);
+static unsigned long	pcf8563_get_rtc_time(void);
+static int		pcf8563_set_rtc_time(unsigned long now);
+
+/* save/restore old machdep pointers */
+int		(*save_set_rtc_time)(unsigned long);
+unsigned long	(*save_get_rtc_time)(void);
+
+static int pcf8563_read(struct i2c_client *client, unsigned char adr,
+			unsigned char *buf, unsigned char len)
+{
+	int ret = -EIO;
+	unsigned char addr[1] = { adr };
+	struct i2c_msg msgs[2] = {
+		{client->addr, 0, 1, addr},
+		{client->addr, I2C_M_RD, len, buf}
+	};
+
+	_DBG(1, "client=%p, adr=%d, buf=%p, len=%d", client, adr, buf, len);
+
+	if (!buf) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = i2c_transfer(client->adapter, msgs, 2);
+	if (ret == 2) {
+		ret = 0;
+	}
+
+done:
+	return ret;
+}
+
+static int pcf8563_write(struct i2c_client *client, unsigned char adr,
+			 unsigned char *data, unsigned char len)
+{
+	int ret = 0;
+	unsigned char _data[16];
+	struct i2c_msg wr;
+	int i;
+
+	if (!data || len > 15) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	_DBG(1, "client=%p, adr=%d, buf=%p, len=%d", client, adr, data, len);
+
+	_data[0] = adr;
+	for (i = 0; i < len; i++) {
+		_data[i + 1] = data[i];
+		_DBG(5, "data[%d] = 0x%02x (%d)", i, data[i], data[i]);
+	}
+
+	wr.addr = client->addr;
+	wr.flags = 0;
+	wr.len = len + 1;
+	wr.buf = _data;
+
+	ret = i2c_transfer(client->adapter, &wr, 1);
+	if (ret == 1) {
+		ret = 0;
+	}
+
+done:
+	return ret;
+}
+
+static void pcf8563_set_system_time (void)
+{
+	unsigned long now, flags;
+	extern time_t last_rtc_update;
+	extern seqlock_t xtime_lock;
+
+	/* Switching kernel RTC pointers */
+	_DBG (2,"RTC switching kernel pointers\n");
+
+	save_set_rtc_time   = ppc_md.set_rtc_time;
+	save_get_rtc_time   = ppc_md.get_rtc_time;
+
+	ppc_md.set_rtc_time = pcf8563_set_rtc_time;
+	ppc_md.get_rtc_time = pcf8563_get_rtc_time;
+
+	/*
+	 * Set system time
+	 * Code copied from arch/ppc/kernel/time.c
+	 */
+	write_seqlock_irqsave(&xtime_lock, flags);
+
+	now = pcf8563_get_rtc_time();
+	_DBG (2,"Set System Time from RTC Time: %lu\n", now);
+	xtime.tv_nsec = 0;
+	xtime.tv_sec  = now;
+
+	last_rtc_update = now - 658;
+
+	time_adjust   = 0;		/* stop active adjtime() */
+	time_status  |= STA_UNSYNC;
+	time_state    = TIME_ERROR;
+	time_maxerror = NTP_PHASE_LIMIT;
+	time_esterror = NTP_PHASE_LIMIT;
+
+	write_sequnlock_irqrestore(&xtime_lock, flags);
+
+#if 0
+	/*
+	 * Check for low voltage
+	 */
+	if (rtc_rd (0x02) & 0x80) {
+		printk (KERN_CRIT
+			"PCF8563 RTC Low Voltage - date/time not reliable\n");
+	}
+
+	/*
+	 * Reset any error conditions, alarms, etc.
+	 */
+	pcf8563_reset_rtc ();
+#endif
+}
+
+static int pcf8563_attach(struct i2c_adapter *adap, int addr, int kind)
+{
+	int ret;
+	struct i2c_client *new_client;
+	struct pcf8563_data *d;
+	unsigned char data[10];
+	unsigned char ad[1] = { 0 };
+	struct i2c_msg ctrl_wr[1] = {
+		{addr, 0, 2, data}
+	};
+	struct i2c_msg ctrl_rd[2] = {
+		{addr, 0, 1, ad},
+		{addr, I2C_M_RD, 2, data}
+	};
+
+	d = kmalloc(sizeof(struct pcf8563_data), GFP_KERNEL);
+	if (!d) {
+		ret = -ENOMEM;
+		goto done;
+	}
+	memset(d, 0, sizeof(struct pcf8563_data));
+	INIT_LIST_HEAD(&d->list);
+
+	new_client = &d->client;
+
+	strlcpy(new_client->name, "RTC8563", I2C_NAME_SIZE);
+	i2c_set_clientdata(new_client, d);
+	new_client->flags = I2C_CLIENT_ALLOW_USE | I2C_DF_NOTIFY;
+	new_client->addr = addr;
+	new_client->adapter = adap;
+	new_client->driver = &pcf8563_driver;
+
+	_DBG(1, "client=%p", new_client);
+
+	/* init ctrl1 reg */
+	data[0] = 0;
+	data[1] = 0;
+	ret = i2c_transfer(new_client->adapter, ctrl_wr, 1);
+	if (ret != 1) {
+		printk(KERN_INFO "pcf8563: cant init ctrl1\n");
+		ret = -ENODEV;
+		goto done;
+	}
+	/* read back ctrl1 and ctrl2 */
+	ret = i2c_transfer(new_client->adapter, ctrl_rd, 2);
+	if (ret != 2) {
+		printk(KERN_INFO "pcf8563: cant read ctrl\n");
+		ret = -ENODEV;
+		goto done;
+	}
+	d->ctrl = data[0] | (data[1] << 8);
+
+	_DBG(1, "RTC8564_REG_CTRL1=%02x, RTC8564_REG_CTRL2=%02x",
+	     data[0], data[1]);
+
+	ret = i2c_attach_client(new_client);
+
+	/* Add client to local list */
+	list_add(&d->list, &pcf8563_clients);
+
+done:
+	if (ret) {
+		kfree(d);
+	} else {
+		myclient = new_client;
+		pcf8563_set_system_time();
+	}
+	return ret;
+}
+
+static int pcf8563_probe(struct i2c_adapter *adap)
+{
+	/*
+	 * Probing seems to confuse the RTC on PM826 and CPU86 and UC100.
+	 * Not sure if it's true for other boards though - thus the
+	 * conditional.
+	 */
+#if defined(CONFIG_PM82X)
+	pcf8563_attach(adap, 0x51, 0);
+	return 0;
+#else
+	return i2c_probe(adap, &addr_data, pcf8563_attach);
+#endif
+}
+
+static int pcf8563_detach(struct i2c_client *client)
+{
+	struct pcf8563_data *data = i2c_get_clientdata(client);
+
+	i2c_detach_client(client);
+	kfree(i2c_get_clientdata(client));
+	list_del(&data->list);
+	return 0;
+}
+
+static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *dt)
+{
+	int ret = -EIO;
+	unsigned char buf[15];
+
+	_DBG(1, "client=%p, dt=%p", client, dt);
+
+	if (!dt)
+		return -EINVAL;
+
+	memset(buf, 0, sizeof(buf));
+
+	ret = pcf8563_read(client, 0, buf, 15);
+	if (ret)
+		return ret;
+
+	dt->tm_year = 1900 + BCD_TO_BIN(buf[RTC8564_REG_YEAR]);
+	if (buf[RTC8564_REG_MON_CENT] & 0x80)
+		dt->tm_year += 100;
+	dt->tm_mday = BCD_TO_BIN(buf[RTC8564_REG_DAY] & 0x3f);
+	dt->tm_wday = BCD_TO_BIN(buf[RTC8564_REG_WDAY] & 7);
+	dt->tm_mon = BCD_TO_BIN(buf[RTC8564_REG_MON_CENT] & 0x1f);
+
+	dt->tm_sec = BCD_TO_BIN(buf[RTC8564_REG_SEC] & 0x7f);
+/*	dt->vl = (buf[RTC8564_REG_SEC] & 0x80) == 0x80; */
+	dt->tm_min = BCD_TO_BIN(buf[RTC8564_REG_MIN] & 0x7f);
+	dt->tm_hour = BCD_TO_BIN(buf[RTC8564_REG_HR] & 0x3f);
+
+	_DBGRTCTM(2, *dt);
+	return 0;
+}
+
+static int
+pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *dt, int datetoo)
+{
+	int ret, len = 5;
+	unsigned char buf[15];
+	unsigned char val;
+
+	_DBG(1, "client=%p, dt=%p", client, dt);
+
+	if (!dt)
+		return -EINVAL;
+
+	_DBGRTCTM(2, *dt);
+
+	buf[RTC8564_REG_CTRL1] = CTRL1(client) | RTC8564_CTRL1_STOP;
+	buf[RTC8564_REG_CTRL2] = CTRL2(client);
+	buf[RTC8564_REG_SEC] = BIN_TO_BCD(dt->tm_sec);
+	buf[RTC8564_REG_MIN] = BIN_TO_BCD(dt->tm_min);
+	buf[RTC8564_REG_HR] = BIN_TO_BCD(dt->tm_hour);
+
+	if (datetoo) {
+		len += 5;
+		buf[RTC8564_REG_DAY] = BIN_TO_BCD(dt->tm_mday);
+		buf[RTC8564_REG_WDAY] = BIN_TO_BCD(dt->tm_wday);
+		buf[RTC8564_REG_MON_CENT] = BIN_TO_BCD(dt->tm_mon) & 0x1f;
+		if (dt->tm_year >= 2000) {
+			val = dt->tm_year - 2000;
+			buf[RTC8564_REG_MON_CENT] |= (1 << 7);
+		} else {
+			val = dt->tm_year - 1900;
+		}
+		buf[RTC8564_REG_YEAR] = BIN_TO_BCD(val);
+	}
+
+	ret = pcf8563_write(client, 0, buf, len);
+	if (ret) {
+		_DBG(1, "error writing data! %d", ret);
+	}
+
+	buf[RTC8564_REG_CTRL1] = CTRL1(client);
+	ret = pcf8563_write(client, 0, buf, 1);
+	if (ret) {
+		_DBG(1, "error writing data! %d", ret);
+	}
+
+	return ret;
+}
+
+static int pcf8563_get_ctrl(struct i2c_client *client, unsigned int *ctrl)
+{
+	struct pcf8563_data *data = i2c_get_clientdata(client);
+
+	if (!ctrl)
+		return -1;
+
+	*ctrl = data->ctrl;
+	return 0;
+}
+
+static int pcf8563_set_ctrl(struct i2c_client *client, unsigned int *ctrl)
+{
+	struct pcf8563_data *data = i2c_get_clientdata(client);
+	unsigned char buf[2];
+
+	if (!ctrl)
+		return -1;
+
+	buf[0] = *ctrl & 0xff;
+	buf[1] = (*ctrl & 0xff00) >> 8;
+	data->ctrl = *ctrl;
+
+	return pcf8563_write(client, 0, buf, 2);
+}
+
+static int pcf8563_read_mem(struct i2c_client *client, struct mem *mem)
+{
+
+	if (!mem)
+		return -EINVAL;
+
+	return pcf8563_read(client, mem->loc, mem->data, mem->nr);
+}
+
+static int pcf8563_write_mem(struct i2c_client *client, struct mem *mem)
+{
+
+	if (!mem)
+		return -EINVAL;
+
+	return pcf8563_write(client, mem->loc, mem->data, mem->nr);
+}
+
+static int
+pcf8563_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+
+	_DBG(1, "cmd=%d", cmd);
+
+	switch (cmd) {
+	case RTC_GETDATETIME:
+		return pcf8563_get_datetime(client, arg);
+
+	case RTC_SETTIME:
+		return pcf8563_set_datetime(client, arg, 0);
+
+	case RTC_SETDATETIME:
+		return pcf8563_set_datetime(client, arg, 1);
+
+	case RTC_GETCTRL:
+		return pcf8563_get_ctrl(client, arg);
+
+	case RTC_SETCTRL:
+		return pcf8563_set_ctrl(client, arg);
+
+	case MEM_READ:
+		return pcf8563_read_mem(client, arg);
+
+	case MEM_WRITE:
+		return pcf8563_write_mem(client, arg);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+/*
+ * Public API for access to specific device. Useful for low-level
+ * RTC access from kernel code.
+ */
+int pcf8563_do_command(int bus, int cmd, void *arg)
+{
+	struct list_head *walk;
+	struct list_head *tmp;
+	struct pcf8563_data *data;
+
+	list_for_each_safe(walk, tmp, &pcf8563_clients) {
+		data = list_entry(walk, struct pcf8563_data, list);
+		if (data->client.adapter->nr == bus) {
+			return pcf8563_command(&data->client, cmd, arg);
+		}
+	}
+
+	return -ENODEV;
+}
+
+extern spinlock_t rtc_lock;
+/***************************************************************************
+ *
+ * get RTC time:
+ */
+static unsigned long pcf8563_get_rtc_time(void)
+{
+	struct rtc_time tm;
+	int result;
+
+	spin_lock(&rtc_lock);
+	result = pcf8563_do_command(0, RTC_GETDATETIME, &tm);
+	spin_unlock(&rtc_lock);
+
+	if (result == 0)
+		result = mktime(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+	return result;
+}
+
+/***************************************************************************
+ *
+ * set RTC time:
+ */
+static int pcf8563_set_rtc_time(unsigned long now)
+{
+	struct rtc_time tm;
+	unsigned char century, year, mon, wday, mday, hour, min, sec;
+
+	to_tm (now, &tm);
+
+	_DBG (2, "Set RTC [dec] year=%d mon=%d day=%d hour=%d min=%d sec=%d\n",
+		tm.tm_year, tm.tm_mon, tm.tm_mday,
+		tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+	century = (tm.tm_year >= 2000) ? 0x80 : 0;
+	year = BIN_TO_BCD (tm.tm_year % 100);
+	mon  = BIN_TO_BCD (tm.tm_mon) | century;
+	wday = BIN_TO_BCD (tm.tm_wday);
+	mday = BIN_TO_BCD (tm.tm_mday);
+	hour = BIN_TO_BCD (tm.tm_hour);
+	min  = BIN_TO_BCD (tm.tm_min);
+	sec  = BIN_TO_BCD (tm.tm_sec);
+
+	_DBG (2, "Set RTC [bcd] year=%X mon=%X day=%X "
+		"hour=%X min=%X sec=%X wday=%X\n",
+		year, mon, mday, hour, min, sec, wday);
+
+	pcf8563_set_datetime(myclient, &tm, 1);
+
+	return (0);
+}
+
+static struct i2c_driver pcf8563_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "PCF8563",
+	.id		= I2C_DRIVERID_RTC8564,
+	.flags		= I2C_DF_NOTIFY,
+	.attach_adapter = pcf8563_probe,
+	.detach_client	= pcf8563_detach,
+	.command	= pcf8563_command
+};
+
+static __init int pcf8563_init(void)
+{
+	return i2c_add_driver(&pcf8563_driver);
+}
+
+static __exit void pcf8563_exit(void)
+{
+	ppc_md.set_rtc_time = save_set_rtc_time;
+	ppc_md.get_rtc_time = save_get_rtc_time;
+
+	i2c_del_driver(&pcf8563_driver);
+}
+
+MODULE_AUTHOR("Stefan Eletzhofer <Stefan.Eletzhofer@eletztrick.de>");
+MODULE_DESCRIPTION("EPSON RTC8563 Driver");
+MODULE_LICENSE("GPL");
+
+module_init(pcf8563_init);
+module_exit(pcf8563_exit);
diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
index 1ce4b54..b16c548 100644
--- a/include/linux/i2c-id.h
+++ b/include/linux/i2c-id.h
@@ -99,6 +99,7 @@
 #define I2C_DRIVERID_MAX6900	63	/* MAX6900 real-time clock	*/
 #define I2C_DRIVERID_SAA7114H	64	/* video decoder		*/
 #define I2C_DRIVERID_DS1374	65	/* DS1374 real time clock	*/
+#define I2C_DRIVERID_PCF8563	66	/* real time clock		*/


 #define I2C_DRIVERID_EXP0	0xF0	/* experimental use id's	*/


\f
!-------------------------------------------------------------flip-

^ permalink raw reply related

* Re: [PATCH 3/3] ppc32: MTD support for Yucca board
From: Matt Porter @ 2005-11-21 15:57 UTC (permalink / raw)
  To: Vitaly Bordug; +Cc: Ruslan V. Sushko, linuxppc-embedded
In-Reply-To: <4381E1A1.1060405@ru.mvista.com>

On Mon, Nov 21, 2005 at 06:02:57PM +0300, Vitaly Bordug wrote:
> Guess this should come through MTD community (linux-mtd@lists.infradead.org), rebased to 
> the MTD tree of course.

Indeed, however, it need to be split so the driver/mtd portion goes to
the MTD maintainers. The arch/ppc portion will go in through the powerpc
tree.

-Matt

^ permalink raw reply

* [PATCH] I2C: Add I2C support for the MPC8260
From: Heiko Schocher @ 2005-11-21 15:55 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: lm-sensors

Hello,

the following Patch (against 2.6 kernel.org tree, COMMIT_ID:
f093182d313edde9b1f86dbdaf40ba4da2dbd0e7) adds support for
the I2C on a MPC8260.
---
 drivers/i2c/busses/Kconfig       |   10 +
 drivers/i2c/busses/Makefile      |    1
 drivers/i2c/busses/i2c-mpc8260.c |  617 ++++++++++++++++++++++++++++++++++++++
 include/linux/i2c-algo-8260.h    |   26 ++
 include/linux/i2c-id.h           |    3
 5 files changed, 657 insertions(+), 0 deletions(-)

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 4010fe9..77f9c69 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -270,6 +270,16 @@ config I2C_MPC
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-mpc.

+config I2C_MPC8260
+	tristate "MPC 8260"
+	depends on I2C && PPC32
+	help
+	  If you say yes to this option, support will be included for the
+	  I2C interface on the MPC8260.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-m8260.
+
 config I2C_NFORCE2
 	tristate "Nvidia nForce2, nForce3 and nForce4"
 	depends on I2C && PCI
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index f1df00f..b4d8cea 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_I2C_VIAPRO)	+= i2c-viapro.o
 obj-$(CONFIG_I2C_VOODOO3)	+= i2c-voodoo3.o
 obj-$(CONFIG_SCx200_ACB)	+= scx200_acb.o
 obj-$(CONFIG_SCx200_I2C)	+= scx200_i2c.o
+obj-$(CONFIG_I2C_MPC8260)	+= i2c-mpc8260.o

 ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/busses/i2c-mpc8260.c b/drivers/i2c/busses/i2c-mpc8260.c
new file mode 100644
index 0000000..f1bfa51
--- /dev/null
+++ b/drivers/i2c/busses/i2c-mpc8260.c
@@ -0,0 +1,617 @@
+/*
+ * (C) Copyright 2005
+ * Heiko Schocher <hs@denx.de>
+ *
+ * This is a combined i2c adapter and algorithm driver for the
+ * MPC8260 Processor
+ *
+ * Release 0.1
+ *
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/fsl_devices.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/immap_cpm2.h>
+#include <asm/mpc8260.h>
+#include <asm/cpm2.h>
+
+#include <linux/i2c-algo-8260.h>
+#include <linux/platform_device.h>
+
+#define CPM_MAX_READ	513
+
+static wait_queue_head_t iic_wait;
+static ushort r_tbase, r_rbase;
+
+int cpm_scan = 0;
+int cpm_debug = 0;
+
+struct mpc_i2c {
+	u32 interrupt;
+	wait_queue_head_t queue;
+	struct i2c_adapter adap;
+	int irq;
+	u32 flags;
+	struct i2c_algo_8260_data *data;
+};
+
+static struct i2c_algo_8260_data pm82x_data;
+
+static void
+mpc8260_iic_init(struct i2c_algo_8260_data *data)
+{
+	volatile cpm_cpm2_t *cp;
+	volatile cpm2_map_t *immap;
+
+	cp = cpmp;	/* Get pointer to Communication Processor */
+	immap = (cpm2_map_t *)CPM_MAP_ADDR;	/* and to internal registers */
+
+	*(ushort *)(&immap->im_dprambase[PROFF_I2C_BASE]) = PROFF_I2C;
+	data->iip = (iic_t *)&immap->im_dprambase[PROFF_I2C];
+
+	data->i2c = (i2c_cpm2_t *)&(immap->im_i2c);
+	data->cp = cp;
+
+	/* Initialize Port D IIC pins.
+	*/
+	immap->im_ioport.iop_ppard |= 0x00030000;
+	immap->im_ioport.iop_pdird &= ~0x00030000;
+	immap->im_ioport.iop_podrd |= 0x00030000;
+	immap->im_ioport.iop_psord |= 0x00030000;
+
+	/* Allocate space for two transmit and two receive buffer
+	 * descriptors in the DP ram.
+	 */
+	data->dp_addr = cpm_dpalloc(sizeof(cbd_t) * 4, 8);
+
+	/* ptr to i2c area */
+	data->i2c = (i2c_cpm2_t *)&(((cpm2_map_t *)CPM_MAP_ADDR)->im_i2c);
+}
+
+static irqreturn_t mpc8260_i2c_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mpc_i2c *mpc_i2c = dev_id;
+	struct i2c_algo_8260_data *cpm_adap = mpc_i2c->data;
+	volatile i2c_cpm2_t *i2c = cpm_adap->i2c;
+
+	if (cpm_debug > 1)
+		printk(KERN_DEBUG "cpm_iic_interrupt(dev_id=%p)\n", dev_id);
+
+	/* Clear interrupt.
+	*/
+	i2c->i2c_i2cer = 0xff;
+
+	/* Get 'me going again.
+	*/
+	wake_up_interruptible(&iic_wait);
+	return IRQ_HANDLED;
+}
+
+
+static void
+cpm_iic_init(struct i2c_algo_8260_data *cpm_adap)
+{
+	volatile iic_t		*iip = cpm_adap->iip;
+	volatile i2c_cpm2_t	*i2c = cpm_adap->i2c;
+
+	if (cpm_debug) printk(KERN_DEBUG "cpm_iic_init() - iip=%p\n",iip);
+
+	/* Initialize the parameter ram.
+	 * We need to make sure many things are initialized to zero,
+	 * especially in the case of a microcode patch.
+	 */
+	iip->iic_rstate = 0;
+	iip->iic_rdp = 0;
+	iip->iic_rbptr = 0;
+	iip->iic_rbc = 0;
+	iip->iic_rxtmp = 0;
+	iip->iic_tstate = 0;
+	iip->iic_tdp = 0;
+	iip->iic_tbptr = 0;
+	iip->iic_tbc = 0;
+	iip->iic_txtmp = 0;
+
+	/* Set up the IIC parameters in the parameter ram.
+	*/
+	iip->iic_tbase = r_tbase = cpm_adap->dp_addr;
+	iip->iic_rbase = r_rbase = cpm_adap->dp_addr + sizeof(cbd_t)*2;
+
+	iip->iic_tfcr = CPMFCR_GBL | CPMFCR_EB;
+	iip->iic_rfcr = CPMFCR_GBL | CPMFCR_EB;
+
+	/* Set maximum receive size.
+	*/
+	iip->iic_mrblr = CPM_MAX_READ;
+
+		/* Initialize Tx/Rx parameters.
+		*/
+	{
+		volatile cpm_cpm2_t *cp = cpm_adap->cp;
+		cp->cp_cpcr =
+			mk_cr_cmd(CPM_CR_I2C_PAGE, CPM_CR_I2C_SBLOCK, 0x00, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+		while (cp->cp_cpcr & CPM_CR_FLG);
+	}
+
+	/* Select an arbitrary address.  Just make sure it is unique.
+	*/
+	i2c->i2c_i2add = 0x34;
+
+	/* Divider is 2 * ( 15 + 3 )
+	*/
+	i2c->i2c_i2brg = 0x0f;
+
+	/* Pre-divider is BRGCLK/4
+	 */
+	i2c->i2c_i2mod = 0x06;
+
+	/* Disable interrupts.
+	*/
+	i2c->i2c_i2cmr = 0;
+	i2c->i2c_i2cer = 0xff;
+
+	init_waitqueue_head(&iic_wait);
+}
+
+
+#if 0
+static int
+cpm_iic_shutdown(struct i2c_algo_8260_data *cpm_adap)
+{
+	volatile i2c_cpm2_t *i2c = cpm_adap->i2c;
+
+	/* Shut down IIC.
+	*/
+	i2c->i2c_i2mod = 0;
+	i2c->i2c_i2cmr = 0;
+	i2c->i2c_i2cer = 0xff;
+
+	return(0);
+}
+#endif
+
+static void
+cpm_reset_iic_params(volatile iic_t *iip)
+{
+	iip->iic_tbase = r_tbase;
+	iip->iic_rbase = r_rbase;
+
+	iip->iic_tfcr = CPMFCR_GBL | CPMFCR_EB;
+	iip->iic_rfcr = CPMFCR_GBL | CPMFCR_EB;
+
+	iip->iic_mrblr = CPM_MAX_READ;
+
+	iip->iic_rstate = 0;
+	iip->iic_rdp = 0;
+	iip->iic_rbptr = 0;
+	iip->iic_rbc = 0;
+	iip->iic_rxtmp = 0;
+	iip->iic_tstate = 0;
+	iip->iic_tdp = 0;
+	iip->iic_tbptr = 0;
+	iip->iic_tbc = 0;
+	iip->iic_txtmp = 0;
+}
+
+#define BD_SC_NAK		((ushort)0x0004) /* NAK - did not respond */
+#define CPM_CR_CLOSE_RXBD	((ushort)0x0007)
+
+static void force_close(struct i2c_algo_8260_data *cpm)
+{
+#if 0
+	volatile cpm_cpm2_t *cp = cpm->cp;
+
+	if (cpm_debug) printk(KERN_DEBUG "force_close()\n");
+	cp->cp_cpcr =
+		mk_cr_cmd(CPM_CR_CH_I2C, CPM_CR_CLOSE_RXBD) |
+		CPM_CR_FLG;
+
+	while (cp->cp_cpcr & CPM_CR_FLG);
+#endif
+}
+
+
+/* Read from IIC...
+ * abyte = address byte, with r/w flag already set
+ */
+static int
+cpm_iic_read(struct i2c_algo_8260_data *cpm, u_char abyte, char *buf, int count)
+{
+	volatile cpm2_map_t *immap = (cpm2_map_t *)CPM_MAP_ADDR;
+	volatile iic_t *iip = cpm->iip;
+	volatile i2c_cpm2_t *i2c = cpm->i2c;
+	volatile cbd_t	*tbdf, *rbdf;
+	u_char *tb;
+	unsigned long flags;
+
+	if (count >= CPM_MAX_READ)
+		return -EINVAL;
+
+	tbdf = (cbd_t *)&immap->im_dprambase[iip->iic_tbase];
+	rbdf = (cbd_t *)&immap->im_dprambase[iip->iic_rbase];
+
+	/* To read, we need an empty buffer of the proper length.
+	 * All that is used is the first byte for address, the remainder
+	 * is just used for timing (and doesn't really have to exist).
+	 */
+	if (/*cpm->reloc*/0) {
+		cpm_reset_iic_params(iip);
+	}
+	tb = cpm->temp;
+	tb = (u_char *)(((uint)tb + 15) & ~15);
+	tb[0] = abyte;		/* Device address byte w/rw flag */
+
+	dma_cache_wback_inv (tb, 1);
+
+	if (cpm_debug) printk(KERN_DEBUG "cpm_iic_read(abyte=0x%x)\n", abyte);
+
+	tbdf->cbd_bufaddr = __pa(tb);
+	tbdf->cbd_datlen = count + 1;
+	tbdf->cbd_sc =
+		BD_SC_READY | BD_SC_INTRPT | BD_SC_LAST |
+		BD_SC_WRAP | BD_IIC_START;
+
+	rbdf->cbd_datlen = 0;
+	rbdf->cbd_bufaddr = __pa(buf);
+	rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP;
+
+	if (count > 0 && count < CPM_MAX_READ)
+		iip->iic_mrblr = count;	/* prevent excessive read */
+
+	dma_cache_inv (buf, count);
+
+	/* Chip bug, set enable here */
+	local_irq_save(flags);
+	i2c->i2c_i2cmr = 0x13;	/* Enable some interupts */
+	i2c->i2c_i2cer = 0xff;
+	i2c->i2c_i2mod |= 1;	/* Enable */
+	i2c->i2c_i2com = 0x81;	/* Start master */
+
+	/* Wait for IIC transfer */
+	interruptible_sleep_on(&iic_wait);
+	local_irq_restore(flags);
+	if (signal_pending(current))
+		return -EIO;
+
+	if (cpm_debug) {
+		printk(KERN_DEBUG "tx sc %04x, rx sc %04x\n",
+		       tbdf->cbd_sc, rbdf->cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		printk(KERN_INFO "IIC read; no ack\n");
+		return 0;
+	}
+
+	if (rbdf->cbd_sc & BD_SC_EMPTY) {
+		printk(KERN_INFO "IIC read; complete but rbuf empty\n");
+		force_close(cpm);
+		printk(KERN_INFO "tx sc %04x, rx sc %04x\n",
+		       tbdf->cbd_sc, rbdf->cbd_sc);
+	}
+
+	if (cpm_debug) printk(KERN_DEBUG "read %d bytes\n", rbdf->cbd_datlen);
+
+	if (rbdf->cbd_datlen < count) {
+		printk(KERN_INFO "IIC read; short, wanted %d got %d\n",
+		       count, rbdf->cbd_datlen);
+		return 0;
+	}
+	if (cpm_debug) {
+			int u;
+			for (u = 0; u < count; u++) {
+				printk(KERN_DEBUG "buf[%d] = 0x%x\n", u, buf[u]);
+			}
+			}
+
+
+	return count;
+}
+
+/* Write to IIC...
+ * addr = address byte, with r/w flag already set
+ */
+static int
+cpm_iic_write(struct i2c_algo_8260_data *cpm, u_char abyte, char *buf,int count)
+{
+	volatile cpm2_map_t *immap = (cpm2_map_t *)CPM_MAP_ADDR;
+	volatile iic_t *iip = cpm->iip;
+	volatile i2c_cpm2_t *i2c = cpm->i2c;
+	volatile cbd_t	*tbdf;
+	u_char *tb;
+	unsigned long flags;
+
+	tb = cpm->temp;
+	tb = (u_char *)(((uint)tb + 15) & ~15);
+	*tb = abyte;		/* Device address byte w/rw flag */
+
+	dma_cache_wback_inv (tb, 1);
+	dma_cache_wback_inv (buf, count);
+
+	if (cpm_debug) printk(KERN_DEBUG "cpm_iic_write(abyte=0x%x)\n", abyte);
+	if (cpm_debug) printk(KERN_DEBUG "buf[0] = 0x%x, buf[1] = 0x%x\n", buf[0], buf[1]);
+
+	/* set up 2 descriptors */
+	tbdf = (cbd_t *)&immap->im_dprambase[iip->iic_tbase];
+
+	tbdf[0].cbd_bufaddr = __pa(tb);
+	tbdf[0].cbd_datlen = 1;
+	tbdf[0].cbd_sc = BD_SC_READY | BD_IIC_START;
+
+	tbdf[1].cbd_bufaddr = __pa(buf);
+	tbdf[1].cbd_datlen = count;
+	tbdf[1].cbd_sc = BD_SC_READY | BD_SC_INTRPT | BD_SC_LAST | BD_SC_WRAP;
+
+	/* Chip bug, set enable here */
+	local_irq_save(flags);;
+	i2c->i2c_i2cmr = 0x13;	/* Enable some interupts */
+	i2c->i2c_i2cer = 0xff;
+	i2c->i2c_i2mod |= 1;	/* Enable */
+	i2c->i2c_i2com = 0x81;	/* Start master */
+
+	/* Wait for IIC transfer */
+	interruptible_sleep_on(&iic_wait);
+	local_irq_restore(flags);
+	if (signal_pending(current))
+		return -EIO;
+
+	if (cpm_debug) {
+		printk(KERN_DEBUG "tx0 sc %04x, tx1 sc %04x\n",
+		       tbdf[0].cbd_sc, tbdf[1].cbd_sc);
+	}
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		printk(KERN_INFO "IIC write; no ack\n");
+		return 0;
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		printk(KERN_INFO "IIC write; complete but tbuf ready\n");
+		return 0;
+	}
+
+	return count;
+}
+
+#if 0
+/* See if an IIC address exists..
+ * addr = 7 bit address, unshifted
+ */
+static int
+cpm_iic_tryaddress(struct i2c_algo_8260_data *cpm, int addr)
+{
+	volatile cpm2_map_t *immap = (cpm2_map_t *)CPM_MAP_ADDR;
+	volatile iic_t *iip = cpm->iip;
+	volatile i2c_cpm2_t *i2c = cpm->i2c;
+	volatile cbd_t *tbdf, *rbdf;
+	u_char *tb;
+	unsigned long flags, len;
+
+	if (cpm_debug > 1)
+		printk(KERN_DEBUG "cpm_iic_tryaddress(cpm=%p,addr=%d)\n", cpm, addr);
+
+	if (cpm_debug && addr == 0) {
+		printk(KERN_DEBUG "iip %p, dp_addr 0x%x\n", cpm->iip, cpm->dp_addr);
+		printk(KERN_DEBUG "iic_tbase %d, r_tbase %d\n", iip->iic_tbase, r_tbase);
+	}
+
+	tbdf = (cbd_t *)&immap->im_dprambase[iip->iic_tbase];
+	rbdf = (cbd_t *)&immap->im_dprambase[iip->iic_rbase];
+
+	tb = cpm->temp;
+	tb = (u_char *)(((uint)tb + 15) & ~15);
+
+	/* do a simple read */
+	tb[0] = (addr << 1) | 1;	/* device address (+ read) */
+	len = 2;
+
+	dma_cache_wback_inv (tb, 1);
+
+	tbdf->cbd_bufaddr = __pa(tb);
+	tbdf->cbd_datlen = len;
+	tbdf->cbd_sc =
+		BD_SC_READY | BD_SC_INTRPT | BD_SC_LAST |
+		BD_SC_WRAP | BD_IIC_START;
+
+	rbdf->cbd_datlen = 0;
+	rbdf->cbd_bufaddr = __pa(tb+2);
+	rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP;
+
+	local_irq_save(flags);
+	i2c->i2c_i2cmr = 0x13;	/* Enable some interupts */
+	i2c->i2c_i2cer = 0xff;
+	i2c->i2c_i2mod |= 1;	/* Enable */
+	i2c->i2c_i2com = 0x81;	/* Start master */
+
+	if (cpm_debug > 1) printk(KERN_DEBUG "about to sleep\n");
+
+	/* wait for IIC transfer */
+	interruptible_sleep_on(&iic_wait);
+	local_irq_restore(flags);
+	if (signal_pending(current))
+		return -EIO;
+
+	if (cpm_debug > 1) printk(KERN_DEBUG "back from sleep\n");
+
+	if (tbdf->cbd_sc & BD_SC_NAK) {
+		if (cpm_debug > 1) printk(KERN_DEBUG "IIC try; no ack\n");
+		return 0;
+	}
+
+	if (tbdf->cbd_sc & BD_SC_READY) {
+		printk(KERN_INFO "IIC try; complete but tbuf ready\n");
+	}
+
+	return 1;
+}
+#endif
+
+static int mpc8260_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+	struct i2c_msg *pmsg;
+	int i;
+	int ret = 0;
+	struct mpc_i2c *mpc_i2c = i2c_get_adapdata(adap);
+	struct i2c_algo_8260_data *data = mpc_i2c->data;
+	u_char addr;
+
+	for (i = 0; i < num; i++) {
+			pmsg = &msgs[i];
+
+		if (cpm_debug)
+			printk(KERN_DEBUG "i2c-algo-8260.o: "
+			       "#%d addr=0x%x flags=0x%x len=%d\n",
+			       i, pmsg->addr, pmsg->flags, pmsg->len);
+
+		addr = pmsg->addr << 1;
+		if (pmsg->flags & I2C_M_RD )
+			addr |= 1;
+		if (pmsg->flags & I2C_M_REV_DIR_ADDR )
+			addr ^= 1;
+
+		if (!(pmsg->flags & I2C_M_NOSTART)) {
+		}
+		if (pmsg->flags & I2C_M_RD ) {
+			/* read bytes into buffer*/
+			ret = cpm_iic_read(data, addr, pmsg->buf, pmsg->len);
+			if (cpm_debug)
+				printk(KERN_DEBUG "i2c-algo-8260.o: read %d bytes\n", ret);
+			if (ret < pmsg->len ) {
+				return (ret<0)? ret : -EREMOTEIO;
+			}
+		} else {
+			/* write bytes from buffer */
+			ret = cpm_iic_write(data, addr, pmsg->buf, pmsg->len);
+			if (cpm_debug)
+				printk(KERN_DEBUG "i2c-algo-8260.o: wrote %d\n", ret);
+			if (ret < pmsg->len ) {
+				return (ret<0) ? ret : -EREMOTEIO;
+			}
+		}
+	}
+	return (ret < 0) ? ret : num;
+}
+
+static u32 mpc8260_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
+	       I2C_FUNC_PROTOCOL_MANGLING;
+}
+
+static struct i2c_algorithm mpc8260_algo = {
+	.master_xfer = mpc8260_xfer,
+	.functionality = mpc8260_functionality,
+};
+
+static struct i2c_adapter mpc8260_ops = {
+	.owner = THIS_MODULE,
+	.name = "MPC 8260 adapter",
+	.id = I2C_HW_MPC8260,
+	.algo = &mpc8260_algo,
+	.class = I2C_CLASS_HWMON,
+	.timeout = 1,
+	.retries = 1
+};
+
+static int fsl_i2c_probe(struct device *device)
+{
+	int result = 0;
+	struct mpc_i2c *i2c;
+	struct platform_device *pdev = to_platform_device(device);
+	struct fsl_i2c_platform_data *pdata;
+
+	pdata = (struct fsl_i2c_platform_data *) pdev->dev.platform_data;
+
+	if (!(i2c = kmalloc(sizeof(*i2c), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+	memset(i2c, 0, sizeof(*i2c));
+
+	i2c->data = &pm82x_data;
+	/* CPM Plattform initialisation */
+	mpc8260_iic_init (i2c->data);
+	/* CPM init */
+	cpm_iic_init (i2c->data);
+	i2c->irq = platform_get_irq(pdev, 0);
+
+	if (i2c->irq != 0) {
+		if ((result = request_irq(i2c->irq, mpc8260_i2c_isr,
+					  SA_SHIRQ, "i2c-mpc", i2c)) < 0) {
+			printk(KERN_ERR
+			       "i2c-mpc - failed to attach interrupt\n");
+			goto fail_irq;
+		}
+	}
+	dev_set_drvdata(device, i2c);
+
+	i2c->adap = mpc8260_ops;
+	i2c_set_adapdata(&i2c->adap, i2c);
+	i2c->adap.dev.parent = &pdev->dev;
+	if ((result = i2c_add_adapter(&i2c->adap)) < 0) {
+		printk(KERN_ERR "i2c-mpc - failed to add adapter\n");
+		goto fail_add;
+	}
+
+	return result;
+
+  fail_add:
+	if (i2c->irq != 0)
+		free_irq(i2c->irq, NULL);
+  fail_irq:
+	kfree(i2c);
+	return result;
+};
+
+static int fsl_i2c_remove(struct device *device)
+{
+	struct mpc_i2c *i2c = dev_get_drvdata(device);
+
+	/* hier noich irgendwo das dpalloc wieder freigeben */
+	cpm_dpfree (i2c->data->dp_addr);
+
+	i2c_del_adapter(&i2c->adap);
+	dev_set_drvdata(device, NULL);
+
+	if (i2c->irq != 0)
+		free_irq(i2c->irq, i2c);
+
+	kfree(i2c);
+	return 0;
+};
+
+/* Structure for a device driver */
+static struct device_driver fsl_i2c_driver = {
+	.name = "fsl-cpm-i2c",
+	.bus = &platform_bus_type,
+	.probe = fsl_i2c_probe,
+	.remove = fsl_i2c_remove,
+};
+
+static int __init fsl_i2c_init(void)
+{
+	return driver_register(&fsl_i2c_driver);
+}
+
+static void __exit fsl_i2c_exit(void)
+{
+	driver_unregister(&fsl_i2c_driver);
+}
+
+module_init(fsl_i2c_init);
+module_exit(fsl_i2c_exit);
+
+MODULE_AUTHOR("Heiko Schocher <hs@denx.de>");
+MODULE_DESCRIPTION
+    ("I2C-Bus adapter for MPC8260 processors");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/i2c-algo-8260.h b/include/linux/i2c-algo-8260.h
new file mode 100644
index 0000000..73b3e33
--- /dev/null
+++ b/include/linux/i2c-algo-8260.h
@@ -0,0 +1,26 @@
+/* ------------------------------------------------------------------------- */
+/* i2c-algo-8260.h i2c driver algorithms for MPX8260 CPM		     */
+/* ------------------------------------------------------------------------- */
+
+/* $Id$ */
+
+#ifndef I2C_ALGO_8260_H
+#define I2C_ALGO_8260_H 1
+
+#include <linux/i2c.h>
+
+struct i2c_algo_8260_data {
+	uint dp_addr;
+	int reloc;
+	volatile i2c_cpm2_t *i2c;
+	volatile iic_t	*iip;
+	volatile cpm_cpm2_t *cp;
+
+	int	(*setisr) (int irq,
+			   void (*func)(int, void (*)(void *), void *),
+			   void *data);
+
+	u_char	temp[513];
+};
+
+#endif /* I2C_ALGO_8260_H */
diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
index 1543daa..35b0a57 100644
--- a/include/linux/i2c-id.h
+++ b/include/linux/i2c-id.h
@@ -223,6 +223,9 @@
 /* --- PowerPC on-chip adapters						*/
 #define I2C_HW_OCP		0x120000 /* IBM on-chip I2C adapter */

+/* --- MPC8260 PowerPC adapters					*/
+#define I2C_HW_MPC8260    	0x100002 /* MPC8260 I2C adapter */
+
 /* --- Broadcom SiByte adapters						*/
 #define I2C_HW_SIBYTE		0x150000



\f
!-------------------------------------------------------------flip-



Signed-off-by: Heiko Schocher <hs@denx.de>

^ permalink raw reply related

* MPC8272 Fast Ethernet Controller drver
From: alexv @ 2005-11-21 15:08 UTC (permalink / raw)
  To: linuxppc-dev



	Hi,
	my new project uses a MPC8272. but i couldn't find any good tutorial or
things like that describing the registers and all
especially about  CPM,FCC.. Presently I am doing my assembly language
progamming using powerPC tutorial.
Can any one help me.

Thanks and Regards,

Alex




***************************************************************************
This message is proprietary to Future Software Limited (FSL)
and is intended solely for the use of the individual to whom it
is addressed. It may contain  privileged or confidential information
and should not be circulated or used for any purpose other than for
what it is intended.

If you have received this message in error, please notify the
originator immediately. If you are not the intended recipient,
you are notified that you are strictly prohibited from using,
copying, altering, or disclosing the contents of this message.
FSL accepts no responsibility for loss or damage arising from
the use of the information transmitted by this email including
damage from virus.
***************************************************************************

^ permalink raw reply

* Re: [PATCH 3/3] ppc32: MTD support for Yucca board
From: Vitaly Bordug @ 2005-11-21 15:02 UTC (permalink / raw)
  To: Ruslan V. Sushko; +Cc: linuxppc-embedded
In-Reply-To: <1132583169.11785.44.camel@mephisto.spb.rtsoft.ru>

Guess this should come through MTD community (linux-mtd@lists.infradead.org), rebased to 
the MTD tree of course.

Ruslan V. Sushko wrote:
> Following patch adds MTD support for Yucca embedded memories(Flashes,
> SRAM, FRAM).
> 
> 
> Signed-off-by: Ruslan V. Sushko <rsushko@ru.mvista.com>
> 
> 
> ------------------------------------------------------------------------
> 
> diff --git a/arch/ppc/platforms/4xx/yucca.h b/arch/ppc/platforms/4xx/yucca.h
> --- a/arch/ppc/platforms/4xx/yucca.h
> +++ b/arch/ppc/platforms/4xx/yucca.h
> @@ -32,12 +32,88 @@
>  /* External timer clock frequency */
>  #define YUCCA_TMR_CLK		25000000
>  
> +/* System Memory map (physical address, not effective address)
> + * Flash and other devices via EBC.
> + *
> + * This should be the same mapping as done by PIBS in the
> + * (not mandatory but ease code reading between two codes)
> + * xxx/boardlib/ebc.c
> + *
> + * depending of the switch setting off the board, the location of devices
> + * in the address map are not the same:
> + */
> +/* Common definitions */
> +
> +#define BOARD_SMALL_FLASH_SIZE                    0x100000      /* 1M */
> +#define BOARD_FRAM_SIZE			            0x8000
> +#define BOARD_SRAM_SIZE			          0x100000
> +#define BOARD_OPER_FLASH_SIZE                     0x400000      /* 4M */
> +
> +
> +/* Configuration 1:
> + * the small flash and sram are placed on top   (CS0)
> + * the Large flash is placed below              (CS2)
> + * The FPGA and FRAM at the end                 (CS1)
> + *                                        xxxx----xxxx----
> + */
> +#define BOARD_CONF1_SMALL_FLASH         0x00000004FFF00000ull
> +#define BOARD_CONF1_FRAM                0x00000004FF000000ull
> +#define BOARD_CONF1_OPER_FLASH          0x00000004E7C00000ull
> +#define BOARD_CONF1_SRAM                0x00000004E7000000ull
> +
> +/* Configuration 2:
> + * the small flash and sram are placed on top   (CS0)
> + * but now sram before smal flash
> + * the Large flash is placed below (no change)  (CS2)
> + * The FPGA and FRAM stay in same place.        (CS1)
> + *                                        xxxx----xxxx----
> + */
> +#define BOARD_CONF2_OPER_FLASH          0x00000004FFC00000ull
> +#define BOARD_CONF2_SRAM                0x00000004FF000000ull
> +#define BOARD_CONF2_SMALL_FLASH         0x00000004E7F00000ull
> +#define BOARD_CONF2_FRAM                0x00000004E7000000ull
> +
> +/* Configuration 3:
> + * the Large flash is placed on top,            (CS0)
> + * the small flash and sram are placed below.   (CS2)
> + * The FPGA and FRAM stay in same place.        (CS1)
> + *                                        xxxx----xxxx----
> + */
> +#define BOARD_CONF3_SRAM                0x00000004FFF00000ull
> +#define BOARD_CONF3_OPER_FLASH          0x00000004FF000000ull
> +#define BOARD_CONF3_SMALL_FLASH         0x00000004E7F00000ull
> +#define BOARD_CONF3_FRAM                0x00000004E7000000ull
> +
>  /*
>   * FPGA registers
>   */
>  #define YUCCA_FPGA_REG_BASE			0x00000004e2000000ULL
>  #define YUCCA_FPGA_REG_SIZE			0x24
>  
> +#define FPGA_REG10                              0x10 /* Ethernet/Reset/Boot
> +                                                        configs */
> +#define FPGA_REG10_ETH_MODE10                   0x8000
> +#define FPGA_REG10_ETH_MODE100                  0x4000
> +#define FPGA_REG10_ETH_MODE1000                 0x2000
> +#define FPGA_REG10_ETH_FORCE_DUPLEX             0x1000
> +#define FPGA_REG10_LOG_RESET_ETHERNET           0x0800
> +#define FPGA_REG10_ETH_AUTO_NEGO                0x0400
> +#define FPGA_REG10_LOG_INT_ETH                  0x0200
> +#define FPGA_REG10_LOG_RESET_HISR               0x0080
> +#define FPGA_REG10_LOG_RESET_DISPLAY            0x0040
> +#define FPGA_REG10_LOG_RESET_SDRAM              0x0020
> +#define FPGA_REG10_LOG_OPBOOT                   0x0010
> +#define FPGA_REG10_LOG_SRAM_BOOT                0x0008
> +#define FPGA_REG10_LOG_BTBOOT                   0x0004
> +
> +/*Boot configurations */
> +#define BOARD_CONFIG_MASK                       (0x001C)
> +#define BOARD_CONFIG_1                          (0x0018)
> +#define BOARD_CONFIG_2                          (0x000C)
> +#define BOARD_CONFIG_3                          (0x0014)
> +#define BOARD_BOOT_OPER_FLASH(x)       (((x) & BOARD_CONFIG_MASK) == \
> +                                                 BOARD_CONFIG_2)
> +
>  #define FPGA_REG1A				0x1a
>  
>  #define FPGA_REG1A_PE0_GLED			0x8000
> diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
> --- a/drivers/mtd/maps/Kconfig
> +++ b/drivers/mtd/maps/Kconfig
> @@ -339,6 +339,14 @@ config MTD_OCOTEA
>  	  Ocotea board. If you have one of these boards and would like to
>  	  use the flash chips on it, say 'Y'.
>  
> +config MTD_YUCCA
> +        tristate "Flash devices mapped on IBM 440SPe Yucca"
> +        depends on MTD_CFI && YUCCA
> +        help
> +          This enables access routines for the flash chips on the IBM 440SPe
> +          Yucca board. If you have one of these boards and would like to
> +          use the flash chips on it, say 'Y'.
> +
>  config MTD_REDWOOD
>  	tristate "CFI Flash devices mapped on IBM Redwood"
>  	depends on MTD_CFI && ( REDWOOD_4 || REDWOOD_5 || REDWOOD_6 )
> diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
> --- a/drivers/mtd/maps/Makefile
> +++ b/drivers/mtd/maps/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_MTD_NETtel)	+= nettel.o
>  obj-$(CONFIG_MTD_SCB2_FLASH)	+= scb2_flash.o
>  obj-$(CONFIG_MTD_EBONY)		+= ebony.o
>  obj-$(CONFIG_MTD_OCOTEA)	+= ocotea.o
> +obj-$(CONFIG_MTD_YUCCA)         += yucca_flash.o
>  obj-$(CONFIG_MTD_BEECH)		+= beech-mtd.o
>  obj-$(CONFIG_MTD_ARCTIC)	+= arctic-mtd.o
>  obj-$(CONFIG_MTD_WALNUT)        += walnut.o
> diff --git a/drivers/mtd/maps/yucca_flash.c b/drivers/mtd/maps/yucca_flash.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/mtd/maps/yucca_flash.c
> @@ -0,0 +1,297 @@
> +/*
> + * Mapping for Yucca user flash
> + *
> + * This was derived from the luan.c
> + *
> + * Ruslan Sushko <rsushko@ru.mvista.com>
> + * Matt Porter <mporter@mvista.com>
> + *
> + * Copyright (C) 2002-2005 MontaVista Software Inc.
> + * (C) Copyright IBM Corp. 2004
> + *
> + * 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/module.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/map.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/config.h>
> +#include <linux/version.h>
> +#include <asm/io.h>
> +
> +static struct mtd_info *oper_flash_mtd = NULL;
> +static struct mtd_info *small_flash_mtd = NULL;
> +static struct mtd_info *fram_mtd = NULL;
> +static struct mtd_info *sram_mtd = NULL;
> +
> +static struct map_info yucca_sram_map = {
> +        .name =         "Yucca SRAM",
> +        .size =         BOARD_SRAM_SIZE,
> +        .bankwidth = 2,
> +};
> +
> +static struct map_info yucca_fram_map = {
> +        .name =         "Yucca FRAM",
> +        .size =         BOARD_FRAM_SIZE,
> +        .bankwidth = 1,
> +};
> +
> +static struct map_info yucca_small_map = {
> +        .name =         "Yucca small flash",
> +        .size =         BOARD_SMALL_FLASH_SIZE,
> +        .bankwidth = 1,
> +};
> +
> +static struct map_info yucca_oper_map = {
> +        .name =         "Yucca Operational flash",
> +        .size =         BOARD_OPER_FLASH_SIZE,
> +        .bankwidth = 2,
> +};
> +
> +static struct mtd_partition yucca_sram_partitions[] = {
> +        {
> +                .name =   "SRAM",
> +                .offset = 0x0,
> +                .size =   0x100000,
> +        }
> +};
> +
> +static struct mtd_partition yucca_fram_partitions[] = {
> +        {
> +                .name =   "FRAM",
> +                .offset = 0x0,
> +                .size =   0x8000,
> +        }
> +};
> +
> +static struct mtd_partition yucca_small_partitions[] = {
> +        {
> +                .name =   "U-boot",
> +                .offset = 0x0,
> +                .size =   0x100000,
> +        }
> +};
> +
> +static struct mtd_partition yucca_oper_partitions[] = {
> +        {
> +                .name =   "Linux Kernel",
> +                .offset = 0,
> +                .size =   0x100000,
> +        },
> +        {
> +                .name =   "Free Area",
> +                .offset = 0x100000,
> +                .size =   0x300000,
> +        }
> +};
> +
> +#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
> +static void cleanup_yucca(void);
> +
> +
> +int __init init_yucca(void)
> +{
> +        u16 fpga10_reg;
> +        volatile u16 *fpga_adr;
> +        unsigned long long small_flash_base, oper_flash_base;
> +        unsigned long long sram_base, fram_base;
> +        int memconfig = 0;
> +        int err = 0;
> +
> +        fpga_adr = ioremap64(YUCCA_FPGA_REG_BASE, YUCCA_FPGA_REG_SIZE);
> +        if (!fpga_adr)
> +                return -ENOMEM;
> +
> +
> +        fpga10_reg = *((u16*)((u32)fpga_adr + FPGA_REG10));
> +        iounmap(fpga_adr);
> +        switch (fpga10_reg & BOARD_CONFIG_MASK) {
> +                default:
> +                        printk("Memory config switches are invalid: "
> +                               "fpga10_reg= %x. Using default configuration\n",
> +                                        fpga10_reg);
> +                case BOARD_CONFIG_1:
> +                        memconfig = 1;
> +                        small_flash_base =      BOARD_CONF1_SMALL_FLASH;
> +                        oper_flash_base =       BOARD_CONF1_OPER_FLASH;
> +		        sram_base =             BOARD_CONF1_SRAM;
> +		        fram_base =             BOARD_CONF1_FRAM;
> +                        break;
> +                case BOARD_CONFIG_2:
> +                        memconfig = 2;
> +                        small_flash_base =      BOARD_CONF2_SMALL_FLASH;
> +                        oper_flash_base =       BOARD_CONF2_OPER_FLASH;
> +		        sram_base =             BOARD_CONF2_SRAM;
> +		        fram_base =             BOARD_CONF3_FRAM;
> +                        break;
> +                case BOARD_CONFIG_3:
> +                        memconfig = 3;
> +                        small_flash_base =      BOARD_CONF3_SMALL_FLASH;
> +                        oper_flash_base =       BOARD_CONF3_OPER_FLASH;
> +		        sram_base =             BOARD_CONF3_SRAM;
> +		        fram_base =             BOARD_CONF3_FRAM;
> +                        break;
> +        }
> +
> +        printk("Using EBC memory configuration #%d\n", memconfig);
> +
> +        yucca_small_map.phys = small_flash_base;
> +        yucca_small_map.virt = ioremap64(small_flash_base,
> +                        yucca_small_map.size);
> +
> +        if (!yucca_small_map.virt) {
> +                printk("Failed to ioremap small flash\n");
> +                err = -EIO;
> +                goto ret_err;
> +        }
> +
> +        simple_map_init(&yucca_small_map);
> +
> +        small_flash_mtd = do_map_probe("map_rom", &yucca_small_map);
> +        if (small_flash_mtd) {
> +                small_flash_mtd->owner = THIS_MODULE;
> +                add_mtd_partitions(small_flash_mtd, yucca_small_partitions,
> +                                        NB_OF(yucca_small_partitions));
> +        } else {
> +                printk("map probe failed for small flash\n");
> +                err = -ENXIO;
> +                goto ret_err;
> +        }
> +
> +        yucca_oper_map.phys = oper_flash_base;
> +        yucca_oper_map.virt = ioremap64(oper_flash_base,
> +                        yucca_oper_map.size);
> +
> +        if (!yucca_oper_map.virt) {
> +                printk("Failed to ioremap oper flash\n");
> +                err = -EIO;
> +                goto ret_err;
> +        }
> +
> +        simple_map_init(&yucca_oper_map);
> +
> +        oper_flash_mtd = do_map_probe("cfi_probe", &yucca_oper_map);
> +        if (oper_flash_mtd) {
> +                oper_flash_mtd->owner = THIS_MODULE;
> +                add_mtd_partitions(oper_flash_mtd, yucca_oper_partitions,
> +                                        NB_OF(yucca_oper_partitions));
> +        } else {
> +                printk("map probe failed for oper flash\n");
> +                err = -ENXIO;
> +                goto ret_err;
> +        }
> +
> +        yucca_sram_map.phys = sram_base;
> +        yucca_sram_map.virt = ioremap64(sram_base,
> +                                 yucca_sram_map.size);
> +
> +        if (!yucca_sram_map.virt) {
> +                printk("Failed to ioremap SRAM\n");
> +                err = -EIO;
> +                goto ret_err;
> +        }
> +
> +        simple_map_init(&yucca_sram_map);
> +
> +        sram_mtd = do_map_probe("map_ram", &yucca_sram_map);
> +        if (sram_mtd) {
> +                sram_mtd->owner = THIS_MODULE;
> +                add_mtd_partitions(sram_mtd, yucca_sram_partitions,
> +                                        NB_OF(yucca_sram_partitions));
> +        } else {
> +                printk("map probe failed for SRAM\n");
> +                err = -ENXIO;
> +                goto ret_err;
> +        }
> +
> +        yucca_fram_map.phys = fram_base;
> +        yucca_fram_map.virt = ioremap64(fram_base,
> +                                 yucca_fram_map.size);
> +
> +        if (!yucca_fram_map.virt) {
> +                printk("Failed to ioremap FRAM\n");
> +                err = -EIO;
> +                goto ret_err;
> +        }
> +
> +        simple_map_init(&yucca_fram_map);
> +
> +        fram_mtd = do_map_probe("map_ram", &yucca_fram_map);
> +        if (fram_mtd) {
> +                fram_mtd->owner = THIS_MODULE;
> +                add_mtd_partitions(fram_mtd, yucca_fram_partitions,
> +                                        NB_OF(yucca_fram_partitions));
> +        } else {
> +                printk("map probe failed for FRAM\n");
> +                err = -ENXIO;
> +                goto ret_err;
> +        }
> +        return 0;
> +ret_err:
> +        cleanup_yucca();
> +        return err;
> +}
> +
> +static void cleanup_yucca(void)
> +{
> +        if (yucca_small_map.virt) {
> +                iounmap((void *)yucca_small_map.virt);
> +                yucca_small_map.virt = 0;
> +        }
> +
> +        if (yucca_oper_map.virt) {
> +                iounmap((void *)yucca_oper_map.virt);
> +                yucca_oper_map.virt = 0;
> +        }
> +
> +	if (yucca_fram_map.virt) {
> +                iounmap((void *)yucca_fram_map.virt);
> +                yucca_fram_map.virt = 0;
> +	}
> +
> +	if (yucca_sram_map.virt) {
> +                iounmap((void *)yucca_sram_map.virt);
> +                yucca_sram_map.virt = 0;
> +	}
> +
> +        if (oper_flash_mtd) {
> +                del_mtd_partitions(oper_flash_mtd);
> +                map_destroy(oper_flash_mtd);
> +        }
> +
> +        if (small_flash_mtd) {
> +                del_mtd_partitions(small_flash_mtd);
> +                map_destroy(small_flash_mtd);
> +        }
> +
> +        if (fram_mtd) {
> +                del_mtd_partitions(fram_mtd);
> +                map_destroy(fram_mtd);
> +        }
> +
> +        if (sram_mtd) {
> +                del_mtd_partitions(sram_mtd);
> +                map_destroy(sram_mtd);
> +        }
> +
> +}
> +
> +static void __exit rem_yucca(void)
> +{
> +        cleanup_yucca();
> +}
> +
> +module_init(init_yucca);
> +module_exit(rem_yucca);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Ruslan V. Sushko <rsushko@ru.mvista.com>");
> +MODULE_DESCRIPTION("MTD map and partitions for AMCC 440SPe Yucca board");
> +
> 
> 
> ------------------------------------------------------------------------
> 
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded


-- 
Sincerely,
Vitaly

^ permalink raw reply

* [PATCH 3/3] ppc32: MTD support for Yucca board
From: Ruslan V. Sushko @ 2005-11-21 14:26 UTC (permalink / raw)
  To: linuxppc-embedded

[-- Attachment #1: Type: text/plain, Size: 141 bytes --]

Following patch adds MTD support for Yucca embedded memories(Flashes,
SRAM, FRAM).


Signed-off-by: Ruslan V. Sushko <rsushko@ru.mvista.com>

[-- Attachment #2: yucca_mtd_support.patch --]
[-- Type: text/x-patch, Size: 14582 bytes --]

diff --git a/arch/ppc/platforms/4xx/yucca.h b/arch/ppc/platforms/4xx/yucca.h
--- a/arch/ppc/platforms/4xx/yucca.h
+++ b/arch/ppc/platforms/4xx/yucca.h
@@ -32,12 +32,88 @@
 /* External timer clock frequency */
 #define YUCCA_TMR_CLK		25000000
 
+/* System Memory map (physical address, not effective address)
+ * Flash and other devices via EBC.
+ *
+ * This should be the same mapping as done by PIBS in the
+ * (not mandatory but ease code reading between two codes)
+ * xxx/boardlib/ebc.c
+ *
+ * depending of the switch setting off the board, the location of devices
+ * in the address map are not the same:
+ */
+/* Common definitions */
+
+#define BOARD_SMALL_FLASH_SIZE                    0x100000      /* 1M */
+#define BOARD_FRAM_SIZE			            0x8000
+#define BOARD_SRAM_SIZE			          0x100000
+#define BOARD_OPER_FLASH_SIZE                     0x400000      /* 4M */
+
+
+/* Configuration 1:
+ * the small flash and sram are placed on top   (CS0)
+ * the Large flash is placed below              (CS2)
+ * The FPGA and FRAM at the end                 (CS1)
+ *                                        xxxx----xxxx----
+ */
+#define BOARD_CONF1_SMALL_FLASH         0x00000004FFF00000ull
+#define BOARD_CONF1_FRAM                0x00000004FF000000ull
+#define BOARD_CONF1_OPER_FLASH          0x00000004E7C00000ull
+#define BOARD_CONF1_SRAM                0x00000004E7000000ull
+
+/* Configuration 2:
+ * the small flash and sram are placed on top   (CS0)
+ * but now sram before smal flash
+ * the Large flash is placed below (no change)  (CS2)
+ * The FPGA and FRAM stay in same place.        (CS1)
+ *                                        xxxx----xxxx----
+ */
+#define BOARD_CONF2_OPER_FLASH          0x00000004FFC00000ull
+#define BOARD_CONF2_SRAM                0x00000004FF000000ull
+#define BOARD_CONF2_SMALL_FLASH         0x00000004E7F00000ull
+#define BOARD_CONF2_FRAM                0x00000004E7000000ull
+
+/* Configuration 3:
+ * the Large flash is placed on top,            (CS0)
+ * the small flash and sram are placed below.   (CS2)
+ * The FPGA and FRAM stay in same place.        (CS1)
+ *                                        xxxx----xxxx----
+ */
+#define BOARD_CONF3_SRAM                0x00000004FFF00000ull
+#define BOARD_CONF3_OPER_FLASH          0x00000004FF000000ull
+#define BOARD_CONF3_SMALL_FLASH         0x00000004E7F00000ull
+#define BOARD_CONF3_FRAM                0x00000004E7000000ull
+
 /*
  * FPGA registers
  */
 #define YUCCA_FPGA_REG_BASE			0x00000004e2000000ULL
 #define YUCCA_FPGA_REG_SIZE			0x24
 
+#define FPGA_REG10                              0x10 /* Ethernet/Reset/Boot
+                                                        configs */
+#define FPGA_REG10_ETH_MODE10                   0x8000
+#define FPGA_REG10_ETH_MODE100                  0x4000
+#define FPGA_REG10_ETH_MODE1000                 0x2000
+#define FPGA_REG10_ETH_FORCE_DUPLEX             0x1000
+#define FPGA_REG10_LOG_RESET_ETHERNET           0x0800
+#define FPGA_REG10_ETH_AUTO_NEGO                0x0400
+#define FPGA_REG10_LOG_INT_ETH                  0x0200
+#define FPGA_REG10_LOG_RESET_HISR               0x0080
+#define FPGA_REG10_LOG_RESET_DISPLAY            0x0040
+#define FPGA_REG10_LOG_RESET_SDRAM              0x0020
+#define FPGA_REG10_LOG_OPBOOT                   0x0010
+#define FPGA_REG10_LOG_SRAM_BOOT                0x0008
+#define FPGA_REG10_LOG_BTBOOT                   0x0004
+
+/*Boot configurations */
+#define BOARD_CONFIG_MASK                       (0x001C)
+#define BOARD_CONFIG_1                          (0x0018)
+#define BOARD_CONFIG_2                          (0x000C)
+#define BOARD_CONFIG_3                          (0x0014)
+#define BOARD_BOOT_OPER_FLASH(x)       (((x) & BOARD_CONFIG_MASK) == \
+                                                 BOARD_CONFIG_2)
+
 #define FPGA_REG1A				0x1a
 
 #define FPGA_REG1A_PE0_GLED			0x8000
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -339,6 +339,14 @@ config MTD_OCOTEA
 	  Ocotea board. If you have one of these boards and would like to
 	  use the flash chips on it, say 'Y'.
 
+config MTD_YUCCA
+        tristate "Flash devices mapped on IBM 440SPe Yucca"
+        depends on MTD_CFI && YUCCA
+        help
+          This enables access routines for the flash chips on the IBM 440SPe
+          Yucca board. If you have one of these boards and would like to
+          use the flash chips on it, say 'Y'.
+
 config MTD_REDWOOD
 	tristate "CFI Flash devices mapped on IBM Redwood"
 	depends on MTD_CFI && ( REDWOOD_4 || REDWOOD_5 || REDWOOD_6 )
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_MTD_NETtel)	+= nettel.o
 obj-$(CONFIG_MTD_SCB2_FLASH)	+= scb2_flash.o
 obj-$(CONFIG_MTD_EBONY)		+= ebony.o
 obj-$(CONFIG_MTD_OCOTEA)	+= ocotea.o
+obj-$(CONFIG_MTD_YUCCA)         += yucca_flash.o
 obj-$(CONFIG_MTD_BEECH)		+= beech-mtd.o
 obj-$(CONFIG_MTD_ARCTIC)	+= arctic-mtd.o
 obj-$(CONFIG_MTD_WALNUT)        += walnut.o
diff --git a/drivers/mtd/maps/yucca_flash.c b/drivers/mtd/maps/yucca_flash.c
new file mode 100644
--- /dev/null
+++ b/drivers/mtd/maps/yucca_flash.c
@@ -0,0 +1,297 @@
+/*
+ * Mapping for Yucca user flash
+ *
+ * This was derived from the luan.c
+ *
+ * Ruslan Sushko <rsushko@ru.mvista.com>
+ * Matt Porter <mporter@mvista.com>
+ *
+ * Copyright (C) 2002-2005 MontaVista Software Inc.
+ * (C) Copyright IBM Corp. 2004
+ *
+ * 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/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <asm/io.h>
+
+static struct mtd_info *oper_flash_mtd = NULL;
+static struct mtd_info *small_flash_mtd = NULL;
+static struct mtd_info *fram_mtd = NULL;
+static struct mtd_info *sram_mtd = NULL;
+
+static struct map_info yucca_sram_map = {
+        .name =         "Yucca SRAM",
+        .size =         BOARD_SRAM_SIZE,
+        .bankwidth = 2,
+};
+
+static struct map_info yucca_fram_map = {
+        .name =         "Yucca FRAM",
+        .size =         BOARD_FRAM_SIZE,
+        .bankwidth = 1,
+};
+
+static struct map_info yucca_small_map = {
+        .name =         "Yucca small flash",
+        .size =         BOARD_SMALL_FLASH_SIZE,
+        .bankwidth = 1,
+};
+
+static struct map_info yucca_oper_map = {
+        .name =         "Yucca Operational flash",
+        .size =         BOARD_OPER_FLASH_SIZE,
+        .bankwidth = 2,
+};
+
+static struct mtd_partition yucca_sram_partitions[] = {
+        {
+                .name =   "SRAM",
+                .offset = 0x0,
+                .size =   0x100000,
+        }
+};
+
+static struct mtd_partition yucca_fram_partitions[] = {
+        {
+                .name =   "FRAM",
+                .offset = 0x0,
+                .size =   0x8000,
+        }
+};
+
+static struct mtd_partition yucca_small_partitions[] = {
+        {
+                .name =   "U-boot",
+                .offset = 0x0,
+                .size =   0x100000,
+        }
+};
+
+static struct mtd_partition yucca_oper_partitions[] = {
+        {
+                .name =   "Linux Kernel",
+                .offset = 0,
+                .size =   0x100000,
+        },
+        {
+                .name =   "Free Area",
+                .offset = 0x100000,
+                .size =   0x300000,
+        }
+};
+
+#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
+static void cleanup_yucca(void);
+
+
+int __init init_yucca(void)
+{
+        u16 fpga10_reg;
+        volatile u16 *fpga_adr;
+        unsigned long long small_flash_base, oper_flash_base;
+        unsigned long long sram_base, fram_base;
+        int memconfig = 0;
+        int err = 0;
+
+        fpga_adr = ioremap64(YUCCA_FPGA_REG_BASE, YUCCA_FPGA_REG_SIZE);
+        if (!fpga_adr)
+                return -ENOMEM;
+
+
+        fpga10_reg = *((u16*)((u32)fpga_adr + FPGA_REG10));
+        iounmap(fpga_adr);
+        switch (fpga10_reg & BOARD_CONFIG_MASK) {
+                default:
+                        printk("Memory config switches are invalid: "
+                               "fpga10_reg= %x. Using default configuration\n",
+                                        fpga10_reg);
+                case BOARD_CONFIG_1:
+                        memconfig = 1;
+                        small_flash_base =      BOARD_CONF1_SMALL_FLASH;
+                        oper_flash_base =       BOARD_CONF1_OPER_FLASH;
+		        sram_base =             BOARD_CONF1_SRAM;
+		        fram_base =             BOARD_CONF1_FRAM;
+                        break;
+                case BOARD_CONFIG_2:
+                        memconfig = 2;
+                        small_flash_base =      BOARD_CONF2_SMALL_FLASH;
+                        oper_flash_base =       BOARD_CONF2_OPER_FLASH;
+		        sram_base =             BOARD_CONF2_SRAM;
+		        fram_base =             BOARD_CONF3_FRAM;
+                        break;
+                case BOARD_CONFIG_3:
+                        memconfig = 3;
+                        small_flash_base =      BOARD_CONF3_SMALL_FLASH;
+                        oper_flash_base =       BOARD_CONF3_OPER_FLASH;
+		        sram_base =             BOARD_CONF3_SRAM;
+		        fram_base =             BOARD_CONF3_FRAM;
+                        break;
+        }
+
+        printk("Using EBC memory configuration #%d\n", memconfig);
+
+        yucca_small_map.phys = small_flash_base;
+        yucca_small_map.virt = ioremap64(small_flash_base,
+                        yucca_small_map.size);
+
+        if (!yucca_small_map.virt) {
+                printk("Failed to ioremap small flash\n");
+                err = -EIO;
+                goto ret_err;
+        }
+
+        simple_map_init(&yucca_small_map);
+
+        small_flash_mtd = do_map_probe("map_rom", &yucca_small_map);
+        if (small_flash_mtd) {
+                small_flash_mtd->owner = THIS_MODULE;
+                add_mtd_partitions(small_flash_mtd, yucca_small_partitions,
+                                        NB_OF(yucca_small_partitions));
+        } else {
+                printk("map probe failed for small flash\n");
+                err = -ENXIO;
+                goto ret_err;
+        }
+
+        yucca_oper_map.phys = oper_flash_base;
+        yucca_oper_map.virt = ioremap64(oper_flash_base,
+                        yucca_oper_map.size);
+
+        if (!yucca_oper_map.virt) {
+                printk("Failed to ioremap oper flash\n");
+                err = -EIO;
+                goto ret_err;
+        }
+
+        simple_map_init(&yucca_oper_map);
+
+        oper_flash_mtd = do_map_probe("cfi_probe", &yucca_oper_map);
+        if (oper_flash_mtd) {
+                oper_flash_mtd->owner = THIS_MODULE;
+                add_mtd_partitions(oper_flash_mtd, yucca_oper_partitions,
+                                        NB_OF(yucca_oper_partitions));
+        } else {
+                printk("map probe failed for oper flash\n");
+                err = -ENXIO;
+                goto ret_err;
+        }
+
+        yucca_sram_map.phys = sram_base;
+        yucca_sram_map.virt = ioremap64(sram_base,
+                                 yucca_sram_map.size);
+
+        if (!yucca_sram_map.virt) {
+                printk("Failed to ioremap SRAM\n");
+                err = -EIO;
+                goto ret_err;
+        }
+
+        simple_map_init(&yucca_sram_map);
+
+        sram_mtd = do_map_probe("map_ram", &yucca_sram_map);
+        if (sram_mtd) {
+                sram_mtd->owner = THIS_MODULE;
+                add_mtd_partitions(sram_mtd, yucca_sram_partitions,
+                                        NB_OF(yucca_sram_partitions));
+        } else {
+                printk("map probe failed for SRAM\n");
+                err = -ENXIO;
+                goto ret_err;
+        }
+
+        yucca_fram_map.phys = fram_base;
+        yucca_fram_map.virt = ioremap64(fram_base,
+                                 yucca_fram_map.size);
+
+        if (!yucca_fram_map.virt) {
+                printk("Failed to ioremap FRAM\n");
+                err = -EIO;
+                goto ret_err;
+        }
+
+        simple_map_init(&yucca_fram_map);
+
+        fram_mtd = do_map_probe("map_ram", &yucca_fram_map);
+        if (fram_mtd) {
+                fram_mtd->owner = THIS_MODULE;
+                add_mtd_partitions(fram_mtd, yucca_fram_partitions,
+                                        NB_OF(yucca_fram_partitions));
+        } else {
+                printk("map probe failed for FRAM\n");
+                err = -ENXIO;
+                goto ret_err;
+        }
+        return 0;
+ret_err:
+        cleanup_yucca();
+        return err;
+}
+
+static void cleanup_yucca(void)
+{
+        if (yucca_small_map.virt) {
+                iounmap((void *)yucca_small_map.virt);
+                yucca_small_map.virt = 0;
+        }
+
+        if (yucca_oper_map.virt) {
+                iounmap((void *)yucca_oper_map.virt);
+                yucca_oper_map.virt = 0;
+        }
+
+	if (yucca_fram_map.virt) {
+                iounmap((void *)yucca_fram_map.virt);
+                yucca_fram_map.virt = 0;
+	}
+
+	if (yucca_sram_map.virt) {
+                iounmap((void *)yucca_sram_map.virt);
+                yucca_sram_map.virt = 0;
+	}
+
+        if (oper_flash_mtd) {
+                del_mtd_partitions(oper_flash_mtd);
+                map_destroy(oper_flash_mtd);
+        }
+
+        if (small_flash_mtd) {
+                del_mtd_partitions(small_flash_mtd);
+                map_destroy(small_flash_mtd);
+        }
+
+        if (fram_mtd) {
+                del_mtd_partitions(fram_mtd);
+                map_destroy(fram_mtd);
+        }
+
+        if (sram_mtd) {
+                del_mtd_partitions(sram_mtd);
+                map_destroy(sram_mtd);
+        }
+
+}
+
+static void __exit rem_yucca(void)
+{
+        cleanup_yucca();
+}
+
+module_init(init_yucca);
+module_exit(rem_yucca);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ruslan V. Sushko <rsushko@ru.mvista.com>");
+MODULE_DESCRIPTION("MTD map and partitions for AMCC 440SPe Yucca board");
+

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox