aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/pasemi
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/pasemi')
-rw-r--r--arch/powerpc/platforms/pasemi/Kconfig32
-rw-r--r--arch/powerpc/platforms/pasemi/Makefile4
-rw-r--r--arch/powerpc/platforms/pasemi/dma_lib.c633
-rw-r--r--arch/powerpc/platforms/pasemi/gpio_mdio.c343
-rw-r--r--arch/powerpc/platforms/pasemi/idle.c18
-rw-r--r--arch/powerpc/platforms/pasemi/iommu.c94
-rw-r--r--arch/powerpc/platforms/pasemi/misc.c94
-rw-r--r--arch/powerpc/platforms/pasemi/pasemi.h23
-rw-r--r--arch/powerpc/platforms/pasemi/pci.c128
-rw-r--r--arch/powerpc/platforms/pasemi/powersave.S11
-rw-r--r--arch/powerpc/platforms/pasemi/setup.c272
11 files changed, 1509 insertions, 143 deletions
diff --git a/arch/powerpc/platforms/pasemi/Kconfig b/arch/powerpc/platforms/pasemi/Kconfig
index 68dc529dfd2..a2aeb327d18 100644
--- a/arch/powerpc/platforms/pasemi/Kconfig
+++ b/arch/powerpc/platforms/pasemi/Kconfig
@@ -1,3 +1,16 @@
+config PPC_PASEMI
+ depends on PPC64 && PPC_BOOK3S
+ bool "PA Semi SoC-based platforms"
+ default n
+ select MPIC
+ select PCI
+ select PPC_UDBG_16550
+ select PPC_NATIVE
+ select MPIC_BROKEN_REGREAD
+ help
+ This option enables support for PA Semi's PWRficient line
+ of SoC processors, including PA6T-1682M
+
menu "PA Semi PWRficient options"
depends on PPC_PASEMI
@@ -5,6 +18,23 @@ config PPC_PASEMI_IOMMU
bool "PA Semi IOMMU support"
depends on PPC_PASEMI
help
- IOMMU support for PA6T-1682M
+ IOMMU support for PA Semi PWRficient
+
+config PPC_PASEMI_IOMMU_DMA_FORCE
+ bool "Force DMA engine to use IOMMU"
+ depends on PPC_PASEMI_IOMMU
+ help
+ This option forces the use of the IOMMU also for the
+ DMA engine. Otherwise the kernel will use it only when
+ running under a hypervisor.
+
+ If in doubt, say "N".
+
+config PPC_PASEMI_MDIO
+ depends on PHYLIB
+ tristate "MDIO support via GPIO"
+ default y
+ help
+ Driver for MDIO via GPIO on PWRficient platforms
endmenu
diff --git a/arch/powerpc/platforms/pasemi/Makefile b/arch/powerpc/platforms/pasemi/Makefile
index e657ccae90a..8e8d4cae5eb 100644
--- a/arch/powerpc/platforms/pasemi/Makefile
+++ b/arch/powerpc/platforms/pasemi/Makefile
@@ -1,2 +1,2 @@
-obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o
-
+obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o dma_lib.o misc.o
+obj-$(CONFIG_PPC_PASEMI_MDIO) += gpio_mdio.o
diff --git a/arch/powerpc/platforms/pasemi/dma_lib.c b/arch/powerpc/platforms/pasemi/dma_lib.c
new file mode 100644
index 00000000000..aafa01ba062
--- /dev/null
+++ b/arch/powerpc/platforms/pasemi/dma_lib.c
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2006-2007 PA Semi, Inc
+ *
+ * Common functions for DMA access on PA Semi PWRficient
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/sched.h>
+
+#include <asm/pasemi_dma.h>
+
+#define MAX_TXCH 64
+#define MAX_RXCH 64
+#define MAX_FLAGS 64
+#define MAX_FUN 8
+
+static struct pasdma_status *dma_status;
+
+static void __iomem *iob_regs;
+static void __iomem *mac_regs[6];
+static void __iomem *dma_regs;
+
+static int base_hw_irq;
+
+static int num_txch, num_rxch;
+
+static struct pci_dev *dma_pdev;
+
+/* Bitmaps to handle allocation of channels */
+
+static DECLARE_BITMAP(txch_free, MAX_TXCH);
+static DECLARE_BITMAP(rxch_free, MAX_RXCH);
+static DECLARE_BITMAP(flags_free, MAX_FLAGS);
+static DECLARE_BITMAP(fun_free, MAX_FUN);
+
+/* pasemi_read_iob_reg - read IOB register
+ * @reg: Register to read (offset into PCI CFG space)
+ */
+unsigned int pasemi_read_iob_reg(unsigned int reg)
+{
+ return in_le32(iob_regs+reg);
+}
+EXPORT_SYMBOL(pasemi_read_iob_reg);
+
+/* pasemi_write_iob_reg - write IOB register
+ * @reg: Register to write to (offset into PCI CFG space)
+ * @val: Value to write
+ */
+void pasemi_write_iob_reg(unsigned int reg, unsigned int val)
+{
+ out_le32(iob_regs+reg, val);
+}
+EXPORT_SYMBOL(pasemi_write_iob_reg);
+
+/* pasemi_read_mac_reg - read MAC register
+ * @intf: MAC interface
+ * @reg: Register to read (offset into PCI CFG space)
+ */
+unsigned int pasemi_read_mac_reg(int intf, unsigned int reg)
+{
+ return in_le32(mac_regs[intf]+reg);
+}
+EXPORT_SYMBOL(pasemi_read_mac_reg);
+
+/* pasemi_write_mac_reg - write MAC register
+ * @intf: MAC interface
+ * @reg: Register to write to (offset into PCI CFG space)
+ * @val: Value to write
+ */
+void pasemi_write_mac_reg(int intf, unsigned int reg, unsigned int val)
+{
+ out_le32(mac_regs[intf]+reg, val);
+}
+EXPORT_SYMBOL(pasemi_write_mac_reg);
+
+/* pasemi_read_dma_reg - read DMA register
+ * @reg: Register to read (offset into PCI CFG space)
+ */
+unsigned int pasemi_read_dma_reg(unsigned int reg)
+{
+ return in_le32(dma_regs+reg);
+}
+EXPORT_SYMBOL(pasemi_read_dma_reg);
+
+/* pasemi_write_dma_reg - write DMA register
+ * @reg: Register to write to (offset into PCI CFG space)
+ * @val: Value to write
+ */
+void pasemi_write_dma_reg(unsigned int reg, unsigned int val)
+{
+ out_le32(dma_regs+reg, val);
+}
+EXPORT_SYMBOL(pasemi_write_dma_reg);
+
+static int pasemi_alloc_tx_chan(enum pasemi_dmachan_type type)
+{
+ int bit;
+ int start, limit;
+
+ switch (type & (TXCHAN_EVT0|TXCHAN_EVT1)) {
+ case TXCHAN_EVT0:
+ start = 0;
+ limit = 10;
+ break;
+ case TXCHAN_EVT1:
+ start = 10;
+ limit = MAX_TXCH;
+ break;
+ default:
+ start = 0;
+ limit = MAX_TXCH;
+ break;
+ }
+retry:
+ bit = find_next_bit(txch_free, MAX_TXCH, start);
+ if (bit >= limit)
+ return -ENOSPC;
+ if (!test_and_clear_bit(bit, txch_free))
+ goto retry;
+
+ return bit;
+}
+
+static void pasemi_free_tx_chan(int chan)
+{
+ BUG_ON(test_bit(chan, txch_free));
+ set_bit(chan, txch_free);
+}
+
+static int pasemi_alloc_rx_chan(void)
+{
+ int bit;
+retry:
+ bit = find_first_bit(rxch_free, MAX_RXCH);
+ if (bit >= MAX_TXCH)
+ return -ENOSPC;
+ if (!test_and_clear_bit(bit, rxch_free))
+ goto retry;
+
+ return bit;
+}
+
+static void pasemi_free_rx_chan(int chan)
+{
+ BUG_ON(test_bit(chan, rxch_free));
+ set_bit(chan, rxch_free);
+}
+
+/* pasemi_dma_alloc_chan - Allocate a DMA channel
+ * @type: Type of channel to allocate
+ * @total_size: Total size of structure to allocate (to allow for more
+ * room behind the structure to be used by the client)
+ * @offset: Offset in bytes from start of the total structure to the beginning
+ * of struct pasemi_dmachan. Needed when struct pasemi_dmachan is
+ * not the first member of the client structure.
+ *
+ * pasemi_dma_alloc_chan allocates a DMA channel for use by a client. The
+ * type argument specifies whether it's a RX or TX channel, and in the case
+ * of TX channels which group it needs to belong to (if any).
+ *
+ * Returns a pointer to the total structure allocated on success, NULL
+ * on failure.
+ */
+void *pasemi_dma_alloc_chan(enum pasemi_dmachan_type type,
+ int total_size, int offset)
+{
+ void *buf;
+ struct pasemi_dmachan *chan;
+ int chno;
+
+ BUG_ON(total_size < sizeof(struct pasemi_dmachan));
+
+ buf = kzalloc(total_size, GFP_KERNEL);
+
+ if (!buf)
+ return NULL;
+ chan = buf + offset;
+
+ chan->priv = buf;
+
+ switch (type & (TXCHAN|RXCHAN)) {
+ case RXCHAN:
+ chno = pasemi_alloc_rx_chan();
+ chan->chno = chno;
+ chan->irq = irq_create_mapping(NULL,
+ base_hw_irq + num_txch + chno);
+ chan->status = &dma_status->rx_sta[chno];
+ break;
+ case TXCHAN:
+ chno = pasemi_alloc_tx_chan(type);
+ chan->chno = chno;
+ chan->irq = irq_create_mapping(NULL, base_hw_irq + chno);
+ chan->status = &dma_status->tx_sta[chno];
+ break;
+ }
+
+ chan->chan_type = type;
+
+ return chan;
+}
+EXPORT_SYMBOL(pasemi_dma_alloc_chan);
+
+/* pasemi_dma_free_chan - Free a previously allocated channel
+ * @chan: Channel to free
+ *
+ * Frees a previously allocated channel. It will also deallocate any
+ * descriptor ring associated with the channel, if allocated.
+ */
+void pasemi_dma_free_chan(struct pasemi_dmachan *chan)
+{
+ if (chan->ring_virt)
+ pasemi_dma_free_ring(chan);
+
+ switch (chan->chan_type & (RXCHAN|TXCHAN)) {
+ case RXCHAN:
+ pasemi_free_rx_chan(chan->chno);
+ break;
+ case TXCHAN:
+ pasemi_free_tx_chan(chan->chno);
+ break;
+ }
+
+ kfree(chan->priv);
+}
+EXPORT_SYMBOL(pasemi_dma_free_chan);
+
+/* pasemi_dma_alloc_ring - Allocate descriptor ring for a channel
+ * @chan: Channel for which to allocate
+ * @ring_size: Ring size in 64-bit (8-byte) words
+ *
+ * Allocate a descriptor ring for a channel. Returns 0 on success, errno
+ * on failure. The passed in struct pasemi_dmachan is updated with the
+ * virtual and DMA addresses of the ring.
+ */
+int pasemi_dma_alloc_ring(struct pasemi_dmachan *chan, int ring_size)
+{
+ BUG_ON(chan->ring_virt);
+
+ chan->ring_size = ring_size;
+
+ chan->ring_virt = dma_alloc_coherent(&dma_pdev->dev,
+ ring_size * sizeof(u64),
+ &chan->ring_dma, GFP_KERNEL);
+
+ if (!chan->ring_virt)
+ return -ENOMEM;
+
+ memset(chan->ring_virt, 0, ring_size * sizeof(u64));
+
+ return 0;
+}
+EXPORT_SYMBOL(pasemi_dma_alloc_ring);
+
+/* pasemi_dma_free_ring - Free an allocated descriptor ring for a channel
+ * @chan: Channel for which to free the descriptor ring
+ *
+ * Frees a previously allocated descriptor ring for a channel.
+ */
+void pasemi_dma_free_ring(struct pasemi_dmachan *chan)
+{
+ BUG_ON(!chan->ring_virt);
+
+ dma_free_coherent(&dma_pdev->dev, chan->ring_size * sizeof(u64),
+ chan->ring_virt, chan->ring_dma);
+ chan->ring_virt = NULL;
+ chan->ring_size = 0;
+ chan->ring_dma = 0;
+}
+EXPORT_SYMBOL(pasemi_dma_free_ring);
+
+/* pasemi_dma_start_chan - Start a DMA channel
+ * @chan: Channel to start
+ * @cmdsta: Additional CCMDSTA/TCMDSTA bits to write
+ *
+ * Enables (starts) a DMA channel with optional additional arguments.
+ */
+void pasemi_dma_start_chan(const struct pasemi_dmachan *chan, const u32 cmdsta)
+{
+ if (chan->chan_type == RXCHAN)
+ pasemi_write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(chan->chno),
+ cmdsta | PAS_DMA_RXCHAN_CCMDSTA_EN);
+ else
+ pasemi_write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(chan->chno),
+ cmdsta | PAS_DMA_TXCHAN_TCMDSTA_EN);
+}
+EXPORT_SYMBOL(pasemi_dma_start_chan);
+
+/* pasemi_dma_stop_chan - Stop a DMA channel
+ * @chan: Channel to stop
+ *
+ * Stops (disables) a DMA channel. This is done by setting the ST bit in the
+ * CMDSTA register and waiting on the ACT (active) bit to clear, then
+ * finally disabling the whole channel.
+ *
+ * This function will only try for a short while for the channel to stop, if
+ * it doesn't it will return failure.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+#define MAX_RETRIES 5000
+int pasemi_dma_stop_chan(const struct pasemi_dmachan *chan)
+{
+ int reg, retries;
+ u32 sta;
+
+ if (chan->chan_type == RXCHAN) {
+ reg = PAS_DMA_RXCHAN_CCMDSTA(chan->chno);
+ pasemi_write_dma_reg(reg, PAS_DMA_RXCHAN_CCMDSTA_ST);
+ for (retries = 0; retries < MAX_RETRIES; retries++) {
+ sta = pasemi_read_dma_reg(reg);
+ if (!(sta & PAS_DMA_RXCHAN_CCMDSTA_ACT)) {
+ pasemi_write_dma_reg(reg, 0);
+ return 1;
+ }
+ cond_resched();
+ }
+ } else {
+ reg = PAS_DMA_TXCHAN_TCMDSTA(chan->chno);
+ pasemi_write_dma_reg(reg, PAS_DMA_TXCHAN_TCMDSTA_ST);
+ for (retries = 0; retries < MAX_RETRIES; retries++) {
+ sta = pasemi_read_dma_reg(reg);
+ if (!(sta & PAS_DMA_TXCHAN_TCMDSTA_ACT)) {
+ pasemi_write_dma_reg(reg, 0);
+ return 1;
+ }
+ cond_resched();
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pasemi_dma_stop_chan);
+
+/* pasemi_dma_alloc_buf - Allocate a buffer to use for DMA
+ * @chan: Channel to allocate for
+ * @size: Size of buffer in bytes
+ * @handle: DMA handle
+ *
+ * Allocate a buffer to be used by the DMA engine for read/write,
+ * similar to dma_alloc_coherent().
+ *
+ * Returns the virtual address of the buffer, or NULL in case of failure.
+ */
+void *pasemi_dma_alloc_buf(struct pasemi_dmachan *chan, int size,
+ dma_addr_t *handle)
+{
+ return dma_alloc_coherent(&dma_pdev->dev, size, handle, GFP_KERNEL);
+}
+EXPORT_SYMBOL(pasemi_dma_alloc_buf);
+
+/* pasemi_dma_free_buf - Free a buffer used for DMA
+ * @chan: Channel the buffer was allocated for
+ * @size: Size of buffer in bytes
+ * @handle: DMA handle
+ *
+ * Frees a previously allocated buffer.
+ */
+void pasemi_dma_free_buf(struct pasemi_dmachan *chan, int size,
+ dma_addr_t *handle)
+{
+ dma_free_coherent(&dma_pdev->dev, size, handle, GFP_KERNEL);
+}
+EXPORT_SYMBOL(pasemi_dma_free_buf);
+
+/* pasemi_dma_alloc_flag - Allocate a flag (event) for channel synchronization
+ *
+ * Allocates a flag for use with channel synchronization (event descriptors).
+ * Returns allocated flag (0-63), < 0 on error.
+ */
+int pasemi_dma_alloc_flag(void)
+{
+ int bit;
+
+retry:
+ bit = find_next_bit(flags_free, MAX_FLAGS, 0);
+ if (bit >= MAX_FLAGS)
+ return -ENOSPC;
+ if (!test_and_clear_bit(bit, flags_free))
+ goto retry;
+
+ return bit;
+}
+EXPORT_SYMBOL(pasemi_dma_alloc_flag);
+
+
+/* pasemi_dma_free_flag - Deallocates a flag (event)
+ * @flag: Flag number to deallocate
+ *
+ * Frees up a flag so it can be reused for other purposes.
+ */
+void pasemi_dma_free_flag(int flag)
+{
+ BUG_ON(test_bit(flag, flags_free));
+ BUG_ON(flag >= MAX_FLAGS);
+ set_bit(flag, flags_free);
+}
+EXPORT_SYMBOL(pasemi_dma_free_flag);
+
+
+/* pasemi_dma_set_flag - Sets a flag (event) to 1
+ * @flag: Flag number to set active
+ *
+ * Sets the flag provided to 1.
+ */
+void pasemi_dma_set_flag(int flag)
+{
+ BUG_ON(flag >= MAX_FLAGS);
+ if (flag < 32)
+ pasemi_write_dma_reg(PAS_DMA_TXF_SFLG0, 1 << flag);
+ else
+ pasemi_write_dma_reg(PAS_DMA_TXF_SFLG1, 1 << flag);
+}
+EXPORT_SYMBOL(pasemi_dma_set_flag);
+
+/* pasemi_dma_clear_flag - Sets a flag (event) to 0
+ * @flag: Flag number to set inactive
+ *
+ * Sets the flag provided to 0.
+ */
+void pasemi_dma_clear_flag(int flag)
+{
+ BUG_ON(flag >= MAX_FLAGS);
+ if (flag < 32)
+ pasemi_write_dma_reg(PAS_DMA_TXF_CFLG0, 1 << flag);
+ else
+ pasemi_write_dma_reg(PAS_DMA_TXF_CFLG1, 1 << flag);
+}
+EXPORT_SYMBOL(pasemi_dma_clear_flag);
+
+/* pasemi_dma_alloc_fun - Allocate a function engine
+ *
+ * Allocates a function engine to use for crypto/checksum offload
+ * Returns allocated engine (0-8), < 0 on error.
+ */
+int pasemi_dma_alloc_fun(void)
+{
+ int bit;
+
+retry:
+ bit = find_next_bit(fun_free, MAX_FLAGS, 0);
+ if (bit >= MAX_FLAGS)
+ return -ENOSPC;
+ if (!test_and_clear_bit(bit, fun_free))
+ goto retry;
+
+ return bit;
+}
+EXPORT_SYMBOL(pasemi_dma_alloc_fun);
+
+
+/* pasemi_dma_free_fun - Deallocates a function engine
+ * @flag: Engine number to deallocate
+ *
+ * Frees up a function engine so it can be used for other purposes.
+ */
+void pasemi_dma_free_fun(int fun)
+{
+ BUG_ON(test_bit(fun, fun_free));
+ BUG_ON(fun >= MAX_FLAGS);
+ set_bit(fun, fun_free);
+}
+EXPORT_SYMBOL(pasemi_dma_free_fun);
+
+
+static void *map_onedev(struct pci_dev *p, int index)
+{
+ struct device_node *dn;
+ void __iomem *ret;
+
+ dn = pci_device_to_OF_node(p);
+ if (!dn)
+ goto fallback;
+
+ ret = of_iomap(dn, index);
+ if (!ret)
+ goto fallback;
+
+ return ret;
+fallback:
+ /* This is hardcoded and ugly, but we have some firmware versions
+ * that don't provide the register space in the device tree. Luckily
+ * they are at well-known locations so we can just do the math here.
+ */
+ return ioremap(0xe0000000 + (p->devfn << 12), 0x2000);
+}
+
+/* pasemi_dma_init - Initialize the PA Semi DMA library
+ *
+ * This function initializes the DMA library. It must be called before
+ * any other function in the library.
+ *
+ * Returns 0 on success, errno on failure.
+ */
+int pasemi_dma_init(void)
+{
+ static DEFINE_SPINLOCK(init_lock);
+ struct pci_dev *iob_pdev;
+ struct pci_dev *pdev;
+ struct resource res;
+ struct device_node *dn;
+ int i, intf, err = 0;
+ unsigned long timeout;
+ u32 tmp;
+
+ if (!machine_is(pasemi))
+ return -ENODEV;
+
+ spin_lock(&init_lock);
+
+ /* Make sure we haven't already initialized */
+ if (dma_pdev)
+ goto out;
+
+ iob_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa001, NULL);
+ if (!iob_pdev) {
+ BUG();
+ printk(KERN_WARNING "Can't find I/O Bridge\n");
+ err = -ENODEV;
+ goto out;
+ }
+ iob_regs = map_onedev(iob_pdev, 0);
+
+ dma_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa007, NULL);
+ if (!dma_pdev) {
+ BUG();
+ printk(KERN_WARNING "Can't find DMA controller\n");
+ err = -ENODEV;
+ goto out;
+ }
+ dma_regs = map_onedev(dma_pdev, 0);
+ base_hw_irq = virq_to_hw(dma_pdev->irq);
+
+ pci_read_config_dword(dma_pdev, PAS_DMA_CAP_TXCH, &tmp);
+ num_txch = (tmp & PAS_DMA_CAP_TXCH_TCHN_M) >> PAS_DMA_CAP_TXCH_TCHN_S;
+
+ pci_read_config_dword(dma_pdev, PAS_DMA_CAP_RXCH, &tmp);
+ num_rxch = (tmp & PAS_DMA_CAP_RXCH_RCHN_M) >> PAS_DMA_CAP_RXCH_RCHN_S;
+
+ intf = 0;
+ for (pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa006, NULL);
+ pdev;
+ pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa006, pdev))
+ mac_regs[intf++] = map_onedev(pdev, 0);
+
+ pci_dev_put(pdev);
+
+ for (pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa005, NULL);
+ pdev;
+ pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa005, pdev))
+ mac_regs[intf++] = map_onedev(pdev, 0);
+
+ pci_dev_put(pdev);
+
+ dn = pci_device_to_OF_node(iob_pdev);
+ if (dn)
+ err = of_address_to_resource(dn, 1, &res);
+ if (!dn || err) {
+ /* Fallback for old firmware */
+ res.start = 0xfd800000;
+ res.end = res.start + 0x1000;
+ }
+ dma_status = __ioremap(res.start, resource_size(&res), 0);
+ pci_dev_put(iob_pdev);
+
+ for (i = 0; i < MAX_TXCH; i++)
+ __set_bit(i, txch_free);
+
+ for (i = 0; i < MAX_RXCH; i++)
+ __set_bit(i, rxch_free);
+
+ timeout = jiffies + HZ;
+ pasemi_write_dma_reg(PAS_DMA_COM_RXCMD, 0);
+ while (pasemi_read_dma_reg(PAS_DMA_COM_RXSTA) & 1) {
+ if (time_after(jiffies, timeout)) {
+ pr_warning("Warning: Could not disable RX section\n");
+ break;
+ }
+ }
+
+ timeout = jiffies + HZ;
+ pasemi_write_dma_reg(PAS_DMA_COM_TXCMD, 0);
+ while (pasemi_read_dma_reg(PAS_DMA_COM_TXSTA) & 1) {
+ if (time_after(jiffies, timeout)) {
+ pr_warning("Warning: Could not disable TX section\n");
+ break;
+ }
+ }
+
+ /* setup resource allocations for the different DMA sections */
+ tmp = pasemi_read_dma_reg(PAS_DMA_COM_CFG);
+ pasemi_write_dma_reg(PAS_DMA_COM_CFG, tmp | 0x18000000);
+
+ /* enable tx section */
+ pasemi_write_dma_reg(PAS_DMA_COM_TXCMD, PAS_DMA_COM_TXCMD_EN);
+
+ /* enable rx section */
+ pasemi_write_dma_reg(PAS_DMA_COM_RXCMD, PAS_DMA_COM_RXCMD_EN);
+
+ for (i = 0; i < MAX_FLAGS; i++)
+ __set_bit(i, flags_free);
+
+ for (i = 0; i < MAX_FUN; i++)
+ __set_bit(i, fun_free);
+
+ /* clear all status flags */
+ pasemi_write_dma_reg(PAS_DMA_TXF_CFLG0, 0xffffffff);
+ pasemi_write_dma_reg(PAS_DMA_TXF_CFLG1, 0xffffffff);
+
+ printk(KERN_INFO "PA Semi PWRficient DMA library initialized "
+ "(%d tx, %d rx channels)\n", num_txch, num_rxch);
+
+out:
+ spin_unlock(&init_lock);
+ return err;
+}
+EXPORT_SYMBOL(pasemi_dma_init);
diff --git a/arch/powerpc/platforms/pasemi/gpio_mdio.c b/arch/powerpc/platforms/pasemi/gpio_mdio.c
new file mode 100644
index 00000000000..15adee54463
--- /dev/null
+++ b/arch/powerpc/platforms/pasemi/gpio_mdio.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2006-2007 PA Semi, Inc
+ *
+ * Author: Olof Johansson, PA Semi
+ *
+ * Maintained by: Olof Johansson <olof@lixom.net>
+ *
+ * Based on drivers/net/fs_enet/mii-bitbang.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/phy.h>
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/of_platform.h>
+
+#define DELAY 1
+
+static void __iomem *gpio_regs;
+
+struct gpio_priv {
+ int mdc_pin;
+ int mdio_pin;
+ int mdio_irqs[PHY_MAX_ADDR];
+};
+
+#define MDC_PIN(bus) (((struct gpio_priv *)bus->priv)->mdc_pin)
+#define MDIO_PIN(bus) (((struct gpio_priv *)bus->priv)->mdio_pin)
+
+static inline void mdio_lo(struct mii_bus *bus)
+{
+ out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus));
+}
+
+static inline void mdio_hi(struct mii_bus *bus)
+{
+ out_le32(gpio_regs, 1 << MDIO_PIN(bus));
+}
+
+static inline void mdc_lo(struct mii_bus *bus)
+{
+ out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus));
+}
+
+static inline void mdc_hi(struct mii_bus *bus)
+{
+ out_le32(gpio_regs, 1 << MDC_PIN(bus));
+}
+
+static inline void mdio_active(struct mii_bus *bus)
+{
+ out_le32(gpio_regs+0x20, (1 << MDC_PIN(bus)) | (1 << MDIO_PIN(bus)));
+}
+
+static inline void mdio_tristate(struct mii_bus *bus)
+{
+ out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus)));
+}
+
+static inline int mdio_read(struct mii_bus *bus)
+{
+ return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus)));
+}
+
+static void clock_out(struct mii_bus *bus, int bit)
+{
+ if (bit)
+ mdio_hi(bus);
+ else
+ mdio_lo(bus);
+ udelay(DELAY);
+ mdc_hi(bus);
+ udelay(DELAY);
+ mdc_lo(bus);
+}
+
+/* Utility to send the preamble, address, and register (common to read and write). */
+static void bitbang_pre(struct mii_bus *bus, int read, u8 addr, u8 reg)
+{
+ int i;
+
+ /* CFE uses a really long preamble (40 bits). We'll do the same. */
+ mdio_active(bus);
+ for (i = 0; i < 40; i++) {
+ clock_out(bus, 1);
+ }
+
+ /* send the start bit (01) and the read opcode (10) or write (10) */
+ clock_out(bus, 0);
+ clock_out(bus, 1);
+
+ clock_out(bus, read);
+ clock_out(bus, !read);
+
+ /* send the PHY address */
+ for (i = 0; i < 5; i++) {
+ clock_out(bus, (addr & 0x10) != 0);
+ addr <<= 1;
+ }
+
+ /* send the register address */
+ for (i = 0; i < 5; i++) {
+ clock_out(bus, (reg & 0x10) != 0);
+ reg <<= 1;
+ }
+}
+
+static int gpio_mdio_read(struct mii_bus *bus, int phy_id, int location)
+{
+ u16 rdreg;
+ int ret, i;
+ u8 addr = phy_id & 0xff;
+ u8 reg = location & 0xff;
+
+ bitbang_pre(bus, 1, addr, reg);
+
+ /* tri-state our MDIO I/O pin so we can read */
+ mdio_tristate(bus);
+ udelay(DELAY);
+ mdc_hi(bus);
+ udelay(DELAY);
+ mdc_lo(bus);
+
+ /* read 16 bits of register data, MSB first */
+ rdreg = 0;
+ for (i = 0; i < 16; i++) {
+ mdc_lo(bus);
+ udelay(DELAY);
+ mdc_hi(bus);
+ udelay(DELAY);
+ mdc_lo(bus);
+ udelay(DELAY);
+ rdreg <<= 1;
+ rdreg |= mdio_read(bus);
+ }
+
+ mdc_hi(bus);
+ udelay(DELAY);
+ mdc_lo(bus);
+ udelay(DELAY);
+
+ ret = rdreg;
+
+ return ret;
+}
+
+static int gpio_mdio_write(struct mii_bus *bus, int phy_id, int location, u16 val)
+{
+ int i;
+
+ u8 addr = phy_id & 0xff;
+ u8 reg = location & 0xff;
+ u16 value = val & 0xffff;
+
+ bitbang_pre(bus, 0, addr, reg);
+
+ /* send the turnaround (10) */
+ mdc_lo(bus);
+ mdio_hi(bus);
+ udelay(DELAY);
+ mdc_hi(bus);
+ udelay(DELAY);
+ mdc_lo(bus);
+ mdio_lo(bus);
+ udelay(DELAY);
+ mdc_hi(bus);
+ udelay(DELAY);
+
+ /* write 16 bits of register data, MSB first */
+ for (i = 0; i < 16; i++) {
+ mdc_lo(bus);
+ if (value & 0x8000)
+ mdio_hi(bus);
+ else
+ mdio_lo(bus);
+ udelay(DELAY);
+ mdc_hi(bus);
+ udelay(DELAY);
+ value <<= 1;
+ }
+
+ /*
+ * Tri-state the MDIO line.
+ */
+ mdio_tristate(bus);
+ mdc_lo(bus);
+ udelay(DELAY);
+ mdc_hi(bus);
+ udelay(DELAY);
+ return 0;
+}
+
+static int gpio_mdio_reset(struct mii_bus *bus)
+{
+ /*nothing here - dunno how to reset it*/
+ return 0;
+}
+
+
+static int gpio_mdio_probe(struct platform_device *ofdev)
+{
+ struct device *dev = &ofdev->dev;
+ struct device_node *np = ofdev->dev.of_node;
+ struct mii_bus *new_bus;
+ struct gpio_priv *priv;
+ const unsigned int *prop;
+ int err;
+
+ err = -ENOMEM;
+ priv = kzalloc(sizeof(struct gpio_priv), GFP_KERNEL);
+ if (!priv)
+ goto out;
+
+ new_bus = mdiobus_alloc();
+
+ if (!new_bus)
+ goto out_free_priv;
+
+ new_bus->name = "pasemi gpio mdio bus";
+ new_bus->read = &gpio_mdio_read;
+ new_bus->write = &gpio_mdio_write;
+ new_bus->reset = &gpio_mdio_reset;
+
+ prop = of_get_property(np, "reg", NULL);
+ snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", *prop);
+ new_bus->priv = priv;
+
+ new_bus->irq = priv->mdio_irqs;
+
+ prop = of_get_property(np, "mdc-pin", NULL);
+ priv->mdc_pin = *prop;
+
+ prop = of_get_property(np, "mdio-pin", NULL);
+ priv->mdio_pin = *prop;
+
+ new_bus->parent = dev;
+ dev_set_drvdata(dev, new_bus);
+
+ err = of_mdiobus_register(new_bus, np);
+
+ if (err != 0) {
+ printk(KERN_ERR "%s: Cannot register as MDIO bus, err %d\n",
+ new_bus->name, err);
+ goto out_free_irq;
+ }
+
+ return 0;
+
+out_free_irq:
+ kfree(new_bus);
+out_free_priv:
+ kfree(priv);
+out:
+ return err;
+}
+
+
+static int gpio_mdio_remove(struct platform_device *dev)
+{
+ struct mii_bus *bus = dev_get_drvdata(&dev->dev);
+
+ mdiobus_unregister(bus);
+
+ dev_set_drvdata(&dev->dev, NULL);
+
+ kfree(bus->priv);
+ bus->priv = NULL;
+ mdiobus_free(bus);
+
+ return 0;
+}
+
+static struct of_device_id gpio_mdio_match[] =
+{
+ {
+ .compatible = "gpio-mdio",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, gpio_mdio_match);
+
+static struct platform_driver gpio_mdio_driver =
+{
+ .probe = gpio_mdio_probe,
+ .remove = gpio_mdio_remove,
+ .driver = {
+ .name = "gpio-mdio-bitbang",
+ .owner = THIS_MODULE,
+ .of_match_table = gpio_mdio_match,
+ },
+};
+
+int gpio_mdio_init(void)
+{
+ struct device_node *np;
+
+ np = of_find_compatible_node(NULL, NULL, "1682m-gpio");
+ if (!np)
+ np = of_find_compatible_node(NULL, NULL,
+ "pasemi,pwrficient-gpio");
+ if (!np)
+ return -ENODEV;
+ gpio_regs = of_iomap(np, 0);
+ of_node_put(np);
+
+ if (!gpio_regs)
+ return -ENODEV;
+
+ return platform_driver_register(&gpio_mdio_driver);
+}
+module_init(gpio_mdio_init);
+
+void gpio_mdio_exit(void)
+{
+ platform_driver_unregister(&gpio_mdio_driver);
+ if (gpio_regs)
+ iounmap(gpio_regs);
+}
+module_exit(gpio_mdio_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");
+MODULE_DESCRIPTION("Driver for MDIO over GPIO on PA Semi PWRficient-based boards");
diff --git a/arch/powerpc/platforms/pasemi/idle.c b/arch/powerpc/platforms/pasemi/idle.c
index 1ca3ff38159..75b296bc51a 100644
--- a/arch/powerpc/platforms/pasemi/idle.c
+++ b/arch/powerpc/platforms/pasemi/idle.c
@@ -22,9 +22,11 @@
#include <linux/kernel.h>
#include <linux/string.h>
+#include <linux/irq.h>
#include <asm/machdep.h>
#include <asm/reg.h>
+#include <asm/smp.h>
#include "pasemi.h"
@@ -61,22 +63,34 @@ static int pasemi_system_reset_exception(struct pt_regs *regs)
/* do system reset */
return 0;
}
+
+ /* Set higher astate since we come out of power savings at 0 */
+ restore_astate(hard_smp_processor_id());
+
/* everything handled */
regs->msr |= MSR_RI;
return 1;
}
-void __init pasemi_idle_init(void)
+static int __init pasemi_idle_init(void)
{
+#ifndef CONFIG_PPC_PASEMI_CPUFREQ
+ printk(KERN_WARNING "No cpufreq driver, powersavings modes disabled\n");
+ current_mode = 0;
+#endif
+
ppc_md.system_reset_exception = pasemi_system_reset_exception;
ppc_md.power_save = modes[current_mode].entry;
printk(KERN_INFO "Using PA6T idle loop (%s)\n", modes[current_mode].name);
+
+ return 0;
}
+machine_late_initcall(pasemi, pasemi_idle_init);
static int __init idle_param(char *p)
{
int i;
- for (i = 0; i < sizeof(modes)/sizeof(struct sleep_mode); i++) {
+ for (i = 0; i < ARRAY_SIZE(modes); i++) {
if (!strcmp(modes[i].name, p)) {
current_mode = i;
break;
diff --git a/arch/powerpc/platforms/pasemi/iommu.c b/arch/powerpc/platforms/pasemi/iommu.c
index 459a53b7d24..2e576f2ae44 100644
--- a/arch/powerpc/platforms/pasemi/iommu.c
+++ b/arch/powerpc/platforms/pasemi/iommu.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2007, PA Semi, Inc
+ * Copyright (C) 2005-2008, PA Semi, Inc
*
* Maintained by: Olof Johansson <olof@lixom.net>
*
@@ -19,30 +19,28 @@
#undef DEBUG
+#include <linux/memblock.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
#include <asm/iommu.h>
#include <asm/machdep.h>
-#include <asm/abs_addr.h>
-
+#include <asm/firmware.h>
#define IOBMAP_PAGE_SHIFT 12
#define IOBMAP_PAGE_SIZE (1 << IOBMAP_PAGE_SHIFT)
#define IOBMAP_PAGE_MASK (IOBMAP_PAGE_SIZE - 1)
-#define IOBMAP_PAGE_FACTOR (PAGE_SHIFT - IOBMAP_PAGE_SHIFT)
-
#define IOB_BASE 0xe0000000
#define IOB_SIZE 0x3000
/* Configuration registers */
-#define IOBCAP_REG 0x10
-#define IOBCOM_REG 0x40
+#define IOBCAP_REG 0x40
+#define IOBCOM_REG 0x100
/* Enable IOB address translation */
#define IOBCOM_ATEN 0x00000100
/* Address decode configuration register */
-#define IOB_AD_REG 0x53
+#define IOB_AD_REG 0x14c
/* IOBCOM_AD_REG fields */
#define IOB_AD_VGPRT 0x00000e00
#define IOB_AD_VGAEN 0x00000100
@@ -57,13 +55,13 @@
#define IOB_AD_TRNG_2G 0x00000001
#define IOB_AD_TRNG_128G 0x00000003
-#define IOB_TABLEBASE_REG 0x55
+#define IOB_TABLEBASE_REG 0x154
/* Base of the 64 4-byte L1 registers */
-#define IOB_XLT_L1_REGBASE 0xac0
+#define IOB_XLT_L1_REGBASE 0x2b00
/* Register to invalidate TLB entries */
-#define IOB_AT_INVAL_TLB_REG 0xb40
+#define IOB_AT_INVAL_TLB_REG 0x2d00
/* The top two bits of the level 1 entry contains valid and type flags */
#define IOBMAP_L1E_V 0x40000000
@@ -77,7 +75,7 @@
#define IOBMAP_L2E_V 0x80000000
#define IOBMAP_L2E_V_CACHED 0xc0000000
-static u32 *iob;
+static void __iomem *iob;
static u32 iob_l1_emptyval;
static u32 iob_l2_emptyval;
static u32 *iob_l2_base;
@@ -85,9 +83,10 @@ static u32 *iob_l2_base;
static struct iommu_table iommu_table_iobmap;
static int iommu_table_iobmap_inited;
-static void iobmap_build(struct iommu_table *tbl, long index,
+static int iobmap_build(struct iommu_table *tbl, long index,
long npages, unsigned long uaddr,
- enum dma_data_direction direction)
+ enum dma_data_direction direction,
+ struct dma_attrs *attrs)
{
u32 *ip;
u32 rpn;
@@ -95,15 +94,12 @@ static void iobmap_build(struct iommu_table *tbl, long index,
pr_debug("iobmap: build at: %lx, %lx, addr: %lx\n", index, npages, uaddr);
- bus_addr = (tbl->it_offset + index) << PAGE_SHIFT;
-
- npages <<= IOBMAP_PAGE_FACTOR;
- index <<= IOBMAP_PAGE_FACTOR;
+ bus_addr = (tbl->it_offset + index) << IOBMAP_PAGE_SHIFT;
ip = ((u32 *)tbl->it_base) + index;
while (npages--) {
- rpn = virt_to_abs(uaddr) >> IOBMAP_PAGE_SHIFT;
+ rpn = __pa(uaddr) >> IOBMAP_PAGE_SHIFT;
*(ip++) = IOBMAP_L2E_V | rpn;
/* invalidate tlb, can be optimized more */
@@ -112,6 +108,7 @@ static void iobmap_build(struct iommu_table *tbl, long index,
uaddr += IOBMAP_PAGE_SIZE;
bus_addr += IOBMAP_PAGE_SIZE;
}
+ return 0;
}
@@ -123,10 +120,7 @@ static void iobmap_free(struct iommu_table *tbl, long index,
pr_debug("iobmap: free at: %lx, %lx\n", index, npages);
- bus_addr = (tbl->it_offset + index) << PAGE_SHIFT;
-
- npages <<= IOBMAP_PAGE_FACTOR;
- index <<= IOBMAP_PAGE_FACTOR;
+ bus_addr = (tbl->it_offset + index) << IOBMAP_PAGE_SHIFT;
ip = ((u32 *)tbl->it_base) + index;
@@ -144,8 +138,11 @@ static void iommu_table_iobmap_setup(void)
pr_debug(" -> %s\n", __func__);
iommu_table_iobmap.it_busno = 0;
iommu_table_iobmap.it_offset = 0;
+ iommu_table_iobmap.it_page_shift = IOBMAP_PAGE_SHIFT;
+
/* it_size is in number of entries */
- iommu_table_iobmap.it_size = 0x80000000 >> PAGE_SHIFT;
+ iommu_table_iobmap.it_size =
+ 0x80000000 >> iommu_table_iobmap.it_page_shift;
/* Initialize the common IOMMU code */
iommu_table_iobmap.it_base = (unsigned long)iob_l2_base;
@@ -162,20 +159,12 @@ static void iommu_table_iobmap_setup(void)
static void pci_dma_bus_setup_pasemi(struct pci_bus *bus)
{
- struct device_node *dn;
-
pr_debug("pci_dma_bus_setup, bus %p, bus->self %p\n", bus, bus->self);
if (!iommu_table_iobmap_inited) {
iommu_table_iobmap_inited = 1;
iommu_table_iobmap_setup();
}
-
- dn = pci_bus_to_OF_node(bus);
-
- if (dn)
- PCI_DN(dn)->iommu_table = &iommu_table_iobmap;
-
}
@@ -183,19 +172,22 @@ static void pci_dma_dev_setup_pasemi(struct pci_dev *dev)
{
pr_debug("pci_dma_dev_setup, dev %p (%s)\n", dev, pci_name(dev));
- /* DMA device is untranslated, but all other PCI-e goes through
- * the IOMMU
+#if !defined(CONFIG_PPC_PASEMI_IOMMU_DMA_FORCE)
+ /* For non-LPAR environment, don't translate anything for the DMA
+ * engine. The exception to this is if the user has enabled
+ * CONFIG_PPC_PASEMI_IOMMU_DMA_FORCE at build time.
*/
- if (dev->vendor == 0x1959 && dev->device == 0xa007)
+ if (dev->vendor == 0x1959 && dev->device == 0xa007 &&
+ !firmware_has_feature(FW_FEATURE_LPAR)) {
dev->dev.archdata.dma_ops = &dma_direct_ops;
- else
- dev->dev.archdata.dma_data = &iommu_table_iobmap;
-}
+ return;
+ }
+#endif
-static void pci_dma_bus_setup_null(struct pci_bus *b) { }
-static void pci_dma_dev_setup_null(struct pci_dev *d) { }
+ set_iommu_table_base(&dev->dev, &iommu_table_iobmap);
+}
-int iob_init(struct device_node *dn)
+int __init iob_init(struct device_node *dn)
{
unsigned long tmp;
u32 regword;
@@ -204,7 +196,7 @@ int iob_init(struct device_node *dn)
pr_debug(" -> %s\n", __func__);
/* Allocate a spare page to map all invalid IOTLB pages. */
- tmp = lmb_alloc(IOBMAP_PAGE_SIZE, IOBMAP_PAGE_SIZE);
+ tmp = memblock_alloc(IOBMAP_PAGE_SIZE, IOBMAP_PAGE_SIZE);
if (!tmp)
panic("IOBMAP: Cannot allocate spare page!");
/* Empty l1 is marked invalid */
@@ -220,7 +212,7 @@ int iob_init(struct device_node *dn)
for (i = 0; i < 64; i++) {
/* Each L1 covers 32MB, i.e. 8K entries = 32K of ram */
regword = IOBMAP_L1E_V | (__pa(iob_l2_base + i*0x2000) >> 12);
- out_le32(iob+IOB_XLT_L1_REGBASE+i, regword);
+ out_le32(iob+IOB_XLT_L1_REGBASE+i*4, regword);
}
/* set 2GB translation window, based at 0 */
@@ -241,7 +233,7 @@ int iob_init(struct device_node *dn)
/* These are called very early. */
-void iommu_init_early_pasemi(void)
+void __init iommu_init_early_pasemi(void)
{
int iommu_off;
@@ -249,16 +241,10 @@ void iommu_init_early_pasemi(void)
iommu_off = 1;
#else
iommu_off = of_chosen &&
- get_property(of_chosen, "linux,iommu-off", NULL);
+ of_get_property(of_chosen, "linux,iommu-off", NULL);
#endif
- if (iommu_off) {
- /* Direct I/O, IOMMU off */
- ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_null;
- ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_null;
- pci_dma_ops = &dma_direct_ops;
-
+ if (iommu_off)
return;
- }
iob_init(NULL);
@@ -266,7 +252,7 @@ void iommu_init_early_pasemi(void)
ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_pasemi;
ppc_md.tce_build = iobmap_build;
ppc_md.tce_free = iobmap_free;
- pci_dma_ops = &dma_iommu_ops;
+ set_pci_dma_ops(&dma_iommu_ops);
}
void __init alloc_iobmap_l2(void)
@@ -275,7 +261,7 @@ void __init alloc_iobmap_l2(void)
return;
#endif
/* For 2G space, 8x64 pages (2^21 bytes) is max total l2 size */
- iob_l2_base = (u32 *)abs_to_virt(lmb_alloc_base(1UL<<21, 1UL<<21, 0x80000000));
+ iob_l2_base = (u32 *)__va(memblock_alloc_base(1UL<<21, 1UL<<21, 0x80000000));
printk(KERN_INFO "IOBMAP L2 allocated at: %p\n", iob_l2_base);
}
diff --git a/arch/powerpc/platforms/pasemi/misc.c b/arch/powerpc/platforms/pasemi/misc.c
new file mode 100644
index 00000000000..e0ab299763c
--- /dev/null
+++ b/arch/powerpc/platforms/pasemi/misc.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2007 PA Semi, Inc
+ *
+ * Parts based on arch/powerpc/sysdev/fsl_soc.c:
+ *
+ * 2006 (c) MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+
+#ifdef CONFIG_I2C_BOARDINFO
+/* The below is from fsl_soc.c. It's copied because since there are no
+ * official bus bindings at this time it doesn't make sense to share across
+ * the platforms, even though they happen to be common.
+ */
+struct i2c_driver_device {
+ char *of_device;
+ char *i2c_type;
+};
+
+static struct i2c_driver_device i2c_devices[] __initdata = {
+ {"dallas,ds1338", "ds1338"},
+};
+
+static int __init find_i2c_driver(struct device_node *node,
+ struct i2c_board_info *info)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(i2c_devices); i++) {
+ if (!of_device_is_compatible(node, i2c_devices[i].of_device))
+ continue;
+ if (strlcpy(info->type, i2c_devices[i].i2c_type,
+ I2C_NAME_SIZE) >= I2C_NAME_SIZE)
+ return -ENOMEM;
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static int __init pasemi_register_i2c_devices(void)
+{
+ struct pci_dev *pdev;
+ struct device_node *adap_node;
+ struct device_node *node;
+
+ pdev = NULL;
+ while ((pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa003, pdev))) {
+ adap_node = pci_device_to_OF_node(pdev);
+
+ if (!adap_node)
+ continue;
+
+ node = NULL;
+ while ((node = of_get_next_child(adap_node, node))) {
+ struct i2c_board_info info = {};
+ const u32 *addr;
+ int len;
+
+ addr = of_get_property(node, "reg", &len);
+ if (!addr || len < sizeof(int) ||
+ *addr > (1 << 10) - 1) {
+ printk(KERN_WARNING
+ "pasemi_register_i2c_devices: "
+ "invalid i2c device entry\n");
+ continue;
+ }
+
+ info.irq = irq_of_parse_and_map(node, 0);
+ if (info.irq == NO_IRQ)
+ info.irq = -1;
+
+ if (find_i2c_driver(node, &info) < 0)
+ continue;
+
+ info.addr = *addr;
+
+ i2c_register_board_info(PCI_FUNC(pdev->devfn), &info,
+ 1);
+ }
+ }
+ return 0;
+}
+device_initcall(pasemi_register_i2c_devices);
+#endif
diff --git a/arch/powerpc/platforms/pasemi/pasemi.h b/arch/powerpc/platforms/pasemi/pasemi.h
index 2d3927e6edb..ea65bf0eb89 100644
--- a/arch/powerpc/platforms/pasemi/pasemi.h
+++ b/arch/powerpc/platforms/pasemi/pasemi.h
@@ -3,17 +3,32 @@
extern unsigned long pas_get_boot_time(void);
extern void pas_pci_init(void);
-extern void __devinit pas_pci_irq_fixup(struct pci_dev *dev);
-extern void __devinit pas_pci_dma_dev_setup(struct pci_dev *dev);
+extern void pas_pci_irq_fixup(struct pci_dev *dev);
+extern void pas_pci_dma_dev_setup(struct pci_dev *dev);
-extern void __init alloc_iobmap_l2(void);
+extern void __iomem *pasemi_pci_getcfgaddr(struct pci_dev *dev, int offset);
-extern void __init pasemi_idle_init(void);
+extern void __init alloc_iobmap_l2(void);
+extern void __init pasemi_map_registers(void);
/* Power savings modes, implemented in asm */
extern void idle_spin(void);
extern void idle_doze(void);
+/* Restore astate to last set */
+#ifdef CONFIG_PPC_PASEMI_CPUFREQ
+extern int check_astate(void);
+extern void restore_astate(int cpu);
+#else
+static inline int check_astate(void)
+{
+ /* Always return >0 so we never power save */
+ return 1;
+}
+static inline void restore_astate(int cpu)
+{
+}
+#endif
#endif /* _PASEMI_PASEMI_H */
diff --git a/arch/powerpc/platforms/pasemi/pci.c b/arch/powerpc/platforms/pasemi/pci.c
index 7ecb2ba24db..aa862713258 100644
--- a/arch/powerpc/platforms/pasemi/pci.c
+++ b/arch/powerpc/platforms/pasemi/pci.c
@@ -33,7 +33,17 @@
#define PA_PXP_CFA(bus, devfn, off) (((bus) << 20) | ((devfn) << 12) | (off))
-#define CONFIG_OFFSET_VALID(off) ((off) < 4096)
+static inline int pa_pxp_offset_valid(u8 bus, u8 devfn, int offset)
+{
+ /* Device 0 Function 0 is special: It's config space spans function 1 as
+ * well, so allow larger offset. It's really a two-function device but the
+ * second function does not probe.
+ */
+ if (bus == 0 && devfn == 0)
+ return offset < 8192;
+ else
+ return offset < 4096;
+}
static void volatile __iomem *pa_pxp_cfg_addr(struct pci_controller *hose,
u8 bus, u8 devfn, int offset)
@@ -41,6 +51,61 @@ static void volatile __iomem *pa_pxp_cfg_addr(struct pci_controller *hose,
return hose->cfg_data + PA_PXP_CFA(bus, devfn, offset);
}
+static inline int is_root_port(int busno, int devfn)
+{
+ return ((busno == 0) && (PCI_FUNC(devfn) < 4) &&
+ ((PCI_SLOT(devfn) == 16) || (PCI_SLOT(devfn) == 17)));
+}
+
+static inline int is_5945_reg(int reg)
+{
+ return (((reg >= 0x18) && (reg < 0x34)) ||
+ ((reg >= 0x158) && (reg < 0x178)));
+}
+
+static int workaround_5945(struct pci_bus *bus, unsigned int devfn,
+ int offset, int len, u32 *val)
+{
+ struct pci_controller *hose;
+ void volatile __iomem *addr, *dummy;
+ int byte;
+ u32 tmp;
+
+ if (!is_root_port(bus->number, devfn) || !is_5945_reg(offset))
+ return 0;
+
+ hose = pci_bus_to_host(bus);
+
+ addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset & ~0x3);
+ byte = offset & 0x3;
+
+ /* Workaround bug 5945: write 0 to a dummy register before reading,
+ * and write back what we read. We must read/write the full 32-bit
+ * contents so we need to shift and mask by hand.
+ */
+ dummy = pa_pxp_cfg_addr(hose, bus->number, devfn, 0x10);
+ out_le32(dummy, 0);
+ tmp = in_le32(addr);
+ out_le32(addr, tmp);
+
+ switch (len) {
+ case 1:
+ *val = (tmp >> (8*byte)) & 0xff;
+ break;
+ case 2:
+ if (byte == 0)
+ *val = tmp & 0xffff;
+ else
+ *val = (tmp >> 16) & 0xffff;
+ break;
+ default:
+ *val = tmp;
+ break;
+ }
+
+ return 1;
+}
+
static int pa_pxp_read_config(struct pci_bus *bus, unsigned int devfn,
int offset, int len, u32 *val)
{
@@ -51,9 +116,12 @@ static int pa_pxp_read_config(struct pci_bus *bus, unsigned int devfn,
if (!hose)
return PCIBIOS_DEVICE_NOT_FOUND;
- if (!CONFIG_OFFSET_VALID(offset))
+ if (!pa_pxp_offset_valid(bus->number, devfn, offset))
return PCIBIOS_BAD_REGISTER_NUMBER;
+ if (workaround_5945(bus, devfn, offset, len, val))
+ return PCIBIOS_SUCCESSFUL;
+
addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset);
/*
@@ -85,7 +153,7 @@ static int pa_pxp_write_config(struct pci_bus *bus, unsigned int devfn,
if (!hose)
return PCIBIOS_DEVICE_NOT_FOUND;
- if (!CONFIG_OFFSET_VALID(offset))
+ if (!pa_pxp_offset_valid(bus->number, devfn, offset))
return PCIBIOS_BAD_REGISTER_NUMBER;
addr = pa_pxp_cfg_addr(hose, bus->number, devfn, offset);
@@ -97,23 +165,20 @@ static int pa_pxp_write_config(struct pci_bus *bus, unsigned int devfn,
switch (len) {
case 1:
out_8(addr, val);
- (void) in_8(addr);
break;
case 2:
out_le16(addr, val);
- (void) in_le16(addr);
break;
default:
out_le32(addr, val);
- (void) in_le32(addr);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static struct pci_ops pa_pxp_ops = {
- pa_pxp_read_config,
- pa_pxp_write_config,
+ .read = pa_pxp_read_config,
+ .write = pa_pxp_write_config,
};
static void __init setup_pa_pxp(struct pci_controller *hose)
@@ -122,7 +187,7 @@ static void __init setup_pa_pxp(struct pci_controller *hose)
hose->cfg_data = ioremap(0xe0000000, 0x10000000);
}
-static int __init add_bridge(struct device_node *dev)
+static int __init pas_add_bridge(struct device_node *dev)
{
struct pci_controller *hose;
@@ -140,42 +205,11 @@ static int __init add_bridge(struct device_node *dev)
printk(KERN_INFO "Found PA-PXP PCI host bridge.\n");
/* Interpret the "ranges" property */
- /* This also maps the I/O region and sets isa_io/mem_base */
pci_process_bridge_OF_ranges(hose, dev, 1);
- pci_setup_phb_io(hose, 1);
return 0;
}
-
-static void __init pas_fixup_phb_resources(void)
-{
- struct pci_controller *hose, *tmp;
-
- list_for_each_entry_safe(hose, tmp, &hose_list, list_node) {
- unsigned long offset = (unsigned long)hose->io_base_virt - pci_io_base;
- hose->io_resource.start += offset;
- hose->io_resource.end += offset;
- printk(KERN_INFO "PCI Host %d, io start: %lx; io end: %lx\n",
- hose->global_number,
- hose->io_resource.start, hose->io_resource.end);
- }
-}
-
-
-void __devinit pas_pci_irq_fixup(struct pci_dev *dev)
-{
- /* DMA is special, 84 interrupts (128 -> 211), all but 128
- * need to be mapped by hand here.
- */
- if (dev->vendor == 0x1959 && dev->device == 0xa007) {
- int i;
- for (i = 129; i < 212; i++)
- irq_create_mapping(NULL, i);
- }
-}
-
-
void __init pas_pci_init(void)
{
struct device_node *np, *root;
@@ -188,16 +222,20 @@ void __init pas_pci_init(void)
}
for (np = NULL; (np = of_get_next_child(root, np)) != NULL;)
- if (np->name && !strcmp(np->name, "pxp") && !add_bridge(np))
+ if (np->name && !strcmp(np->name, "pxp") && !pas_add_bridge(np))
of_node_get(np);
of_node_put(root);
- pas_fixup_phb_resources();
-
/* Setup the linkage between OF nodes and PHBs */
pci_devs_phb_init();
+}
+
+void __iomem *pasemi_pci_getcfgaddr(struct pci_dev *dev, int offset)
+{
+ struct pci_controller *hose;
+
+ hose = pci_bus_to_host(dev->bus);
- /* Use the common resource allocation mechanism */
- pci_probe_only = 1;
+ return (void __iomem *)pa_pxp_cfg_addr(hose, dev->bus->number, dev->devfn, offset);
}
diff --git a/arch/powerpc/platforms/pasemi/powersave.S b/arch/powerpc/platforms/pasemi/powersave.S
index 6d0fba6aab1..81ab555aa49 100644
--- a/arch/powerpc/platforms/pasemi/powersave.S
+++ b/arch/powerpc/platforms/pasemi/powersave.S
@@ -62,7 +62,16 @@ sleep_common:
mflr r0
std r0, 16(r1)
stdu r1,-64(r1)
+#ifdef CONFIG_PPC_PASEMI_CPUFREQ
+ std r3, 48(r1)
+ /* Only do power savings when in astate 0 */
+ bl check_astate
+ cmpwi r3,0
+ bne 1f
+
+ ld r3, 48(r1)
+#endif
LOAD_REG_IMMEDIATE(r6,MSR_DR|MSR_IR|MSR_ME|MSR_EE)
mfmsr r4
andc r5,r4,r6
@@ -73,7 +82,7 @@ sleep_common:
mtmsrd r4,0
- addi r1,r1,64
+1: addi r1,r1,64
ld r0,16(r1)
mtlr r0
blr
diff --git a/arch/powerpc/platforms/pasemi/setup.c b/arch/powerpc/platforms/pasemi/setup.c
index 449cf1a0829..8c54de6d8ec 100644
--- a/arch/powerpc/platforms/pasemi/setup.c
+++ b/arch/powerpc/platforms/pasemi/setup.c
@@ -26,49 +26,83 @@
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/console.h>
+#include <linux/export.h>
#include <linux/pci.h>
+#include <linux/of_platform.h>
+#include <linux/gfp.h>
#include <asm/prom.h>
-#include <asm/system.h>
#include <asm/iommu.h>
#include <asm/machdep.h>
#include <asm/mpic.h>
#include <asm/smp.h>
#include <asm/time.h>
+#include <asm/mmu.h>
+#include <asm/debug.h>
+
+#include <pcmcia/ss.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
#include "pasemi.h"
+/* SDC reset register, must be pre-mapped at reset time */
static void __iomem *reset_reg;
+/* Various error status registers, must be pre-mapped at MCE time */
+
+#define MAX_MCE_REGS 32
+struct mce_regs {
+ char *name;
+ void __iomem *addr;
+};
+
+static struct mce_regs mce_regs[MAX_MCE_REGS];
+static int num_mce_regs;
+static int nmi_virq = NO_IRQ;
+
+
static void pas_restart(char *cmd)
{
+ /* Need to put others cpu in hold loop so they're not sleeping */
+ smp_send_stop();
+ udelay(10000);
printk("Restarting...\n");
while (1)
out_le32(reset_reg, 0x6000000);
}
#ifdef CONFIG_SMP
-static DEFINE_SPINLOCK(timebase_lock);
+static arch_spinlock_t timebase_lock;
+static unsigned long timebase;
-static void __devinit pas_give_timebase(void)
+static void pas_give_timebase(void)
{
- unsigned long tb;
+ unsigned long flags;
- spin_lock(&timebase_lock);
+ local_irq_save(flags);
+ hard_irq_disable();
+ arch_spin_lock(&timebase_lock);
mtspr(SPRN_TBCTL, TBCTL_FREEZE);
- tb = mftb();
- mtspr(SPRN_TBCTL, TBCTL_UPDATE_LOWER | (tb & 0xffffffff));
- mtspr(SPRN_TBCTL, TBCTL_UPDATE_UPPER | (tb >> 32));
+ isync();
+ timebase = get_tb();
+ arch_spin_unlock(&timebase_lock);
+
+ while (timebase)
+ barrier();
mtspr(SPRN_TBCTL, TBCTL_RESTART);
- spin_unlock(&timebase_lock);
- pr_debug("pas_give_timebase: cpu %d gave tb %lx\n",
- smp_processor_id(), tb);
+ local_irq_restore(flags);
}
-static void __devinit pas_take_timebase(void)
+static void pas_take_timebase(void)
{
- pr_debug("pas_take_timebase: cpu %d has tb %lx\n",
- smp_processor_id(), mftb());
+ while (!timebase)
+ smp_rmb();
+
+ arch_spin_lock(&timebase_lock);
+ set_tb(timebase >> 32, timebase & 0xffffffff);
+ timebase = 0;
+ arch_spin_unlock(&timebase_lock);
}
struct smp_ops_t pas_smp_ops = {
@@ -97,15 +131,57 @@ void __init pas_setup_arch(void)
/* Remap SDC register for doing reset */
/* XXXOJN This should maybe come out of the device tree */
reset_reg = ioremap(0xfc101100, 4);
-
- pasemi_idle_init();
}
-/* No legacy IO on our parts */
-static int pas_check_legacy_ioport(unsigned int baseport)
+static int __init pas_setup_mce_regs(void)
{
- return -ENODEV;
+ struct pci_dev *dev;
+ int reg;
+
+ /* Remap various SoC status registers for use by the MCE handler */
+
+ reg = 0;
+
+ dev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa00a, NULL);
+ while (dev && reg < MAX_MCE_REGS) {
+ mce_regs[reg].name = kasprintf(GFP_KERNEL,
+ "mc%d_mcdebug_errsta", reg);
+ mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0x730);
+ dev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa00a, dev);
+ reg++;
+ }
+
+ dev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa001, NULL);
+ if (dev && reg+4 < MAX_MCE_REGS) {
+ mce_regs[reg].name = "iobdbg_IntStatus1";
+ mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0x438);
+ reg++;
+ mce_regs[reg].name = "iobdbg_IOCTbusIntDbgReg";
+ mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0x454);
+ reg++;
+ mce_regs[reg].name = "iobiom_IntStatus";
+ mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0xc10);
+ reg++;
+ mce_regs[reg].name = "iobiom_IntDbgReg";
+ mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0xc1c);
+ reg++;
+ }
+
+ dev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa009, NULL);
+ if (dev && reg+2 < MAX_MCE_REGS) {
+ mce_regs[reg].name = "l2csts_IntStatus";
+ mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0x200);
+ reg++;
+ mce_regs[reg].name = "l2csts_Cnt";
+ mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0x214);
+ reg++;
+ }
+
+ num_mce_regs = reg;
+
+ return 0;
}
+machine_device_initcall(pasemi, pas_setup_mce_regs);
static __init void pas_init_IRQ(void)
{
@@ -114,12 +190,14 @@ static __init void pas_init_IRQ(void)
unsigned long openpic_addr;
const unsigned int *opprop;
int naddr, opplen;
+ int mpic_flags;
+ const unsigned int *nmiprop;
struct mpic *mpic;
mpic_node = NULL;
for_each_node_by_type(np, "interrupt-controller")
- if (device_is_compatible(np, "open-pic")) {
+ if (of_device_is_compatible(np, "open-pic")) {
mpic_node = np;
break;
}
@@ -136,8 +214,8 @@ static __init void pas_init_IRQ(void)
/* Find address list in /platform-open-pic */
root = of_find_node_by_path("/");
- naddr = prom_n_addr_cells(root);
- opprop = get_property(root, "platform-open-pic", &opplen);
+ naddr = of_n_addr_cells(root);
+ opprop = of_get_property(root, "platform-open-pic", &opplen);
if (!opprop) {
printk(KERN_ERR "No platform-open-pic property.\n");
of_node_put(root);
@@ -146,13 +224,26 @@ static __init void pas_init_IRQ(void)
openpic_addr = of_read_number(opprop, naddr);
printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic_addr);
+ mpic_flags = MPIC_LARGE_VECTORS | MPIC_NO_BIAS | MPIC_NO_RESET;
+
+ nmiprop = of_get_property(mpic_node, "nmi-source", NULL);
+ if (nmiprop)
+ mpic_flags |= MPIC_ENABLE_MCK;
+
mpic = mpic_alloc(mpic_node, openpic_addr,
- MPIC_PRIMARY|MPIC_LARGE_VECTORS,
- 0, 0, " PAS-OPIC ");
+ mpic_flags, 0, 0, "PASEMI-OPIC");
BUG_ON(!mpic);
- mpic_assign_isu(mpic, 0, openpic_addr + 0x10000);
+ mpic_assign_isu(mpic, 0, mpic->paddr + 0x10000);
mpic_init(mpic);
+ /* The NMI/MCK source needs to be prio 15 */
+ if (nmiprop) {
+ nmi_virq = irq_create_mapping(NULL, *nmiprop);
+ mpic_irq_set_priority(nmi_virq, 15);
+ irq_set_irq_type(nmi_virq, IRQ_TYPE_EDGE_RISING);
+ mpic_unmask_irq(irq_get_irq_data(nmi_virq));
+ }
+
of_node_put(mpic_node);
of_node_put(root);
}
@@ -167,25 +258,42 @@ static int pas_machine_check_handler(struct pt_regs *regs)
{
int cpu = smp_processor_id();
unsigned long srr0, srr1, dsisr;
+ int dump_slb = 0;
+ int i;
srr0 = regs->nip;
srr1 = regs->msr;
+
+ if (nmi_virq != NO_IRQ && mpic_get_mcirq() == nmi_virq) {
+ printk(KERN_ERR "NMI delivered\n");
+ debugger(regs);
+ mpic_end_irq(irq_get_irq_data(nmi_virq));
+ goto out;
+ }
+
dsisr = mfspr(SPRN_DSISR);
printk(KERN_ERR "Machine Check on CPU %d\n", cpu);
- printk(KERN_ERR "SRR0 0x%016lx SRR1 0x%016lx\n", srr0, srr1);
- printk(KERN_ERR "DSISR 0x%016lx DAR 0x%016lx\n", dsisr, regs->dar);
+ printk(KERN_ERR "SRR0 0x%016lx SRR1 0x%016lx\n", srr0, srr1);
+ printk(KERN_ERR "DSISR 0x%016lx DAR 0x%016lx\n", dsisr, regs->dar);
+ printk(KERN_ERR "BER 0x%016lx MER 0x%016lx\n", mfspr(SPRN_PA6T_BER),
+ mfspr(SPRN_PA6T_MER));
+ printk(KERN_ERR "IER 0x%016lx DER 0x%016lx\n", mfspr(SPRN_PA6T_IER),
+ mfspr(SPRN_PA6T_DER));
printk(KERN_ERR "Cause:\n");
if (srr1 & 0x200000)
printk(KERN_ERR "Signalled by SDC\n");
+
if (srr1 & 0x100000) {
printk(KERN_ERR "Load/Store detected error:\n");
if (dsisr & 0x8000)
printk(KERN_ERR "D-cache ECC double-bit error or bus error\n");
if (dsisr & 0x4000)
printk(KERN_ERR "LSU snoop response error\n");
- if (dsisr & 0x2000)
+ if (dsisr & 0x2000) {
printk(KERN_ERR "MMU SLB multi-hit or invalid B field\n");
+ dump_slb = 1;
+ }
if (dsisr & 0x1000)
printk(KERN_ERR "Recoverable Duptags\n");
if (dsisr & 0x800)
@@ -193,13 +301,40 @@ static int pas_machine_check_handler(struct pt_regs *regs)
if (dsisr & 0x400)
printk(KERN_ERR "TLB parity error count overflow\n");
}
+
if (srr1 & 0x80000)
printk(KERN_ERR "Bus Error\n");
- if (srr1 & 0x40000)
+
+ if (srr1 & 0x40000) {
printk(KERN_ERR "I-side SLB multiple hit\n");
+ dump_slb = 1;
+ }
+
if (srr1 & 0x20000)
printk(KERN_ERR "I-cache parity error hit\n");
+ if (num_mce_regs == 0)
+ printk(KERN_ERR "No MCE registers mapped yet, can't dump\n");
+ else
+ printk(KERN_ERR "SoC debug registers:\n");
+
+ for (i = 0; i < num_mce_regs; i++)
+ printk(KERN_ERR "%s: 0x%08x\n", mce_regs[i].name,
+ in_le32(mce_regs[i].addr));
+
+ if (dump_slb) {
+ unsigned long e, v;
+ int i;
+
+ printk(KERN_ERR "slb contents:\n");
+ for (i = 0; i < mmu_slb_size; i++) {
+ asm volatile("slbmfee %0,%1" : "=r" (e) : "r" (i));
+ asm volatile("slbmfev %0,%1" : "=r" (v) : "r" (i));
+ printk(KERN_ERR "%02d %016lx %016lx\n", i, e, v);
+ }
+ }
+
+out:
/* SRR1[62] is from MSR[62] if recoverable, so pass that back */
return !!(srr1 & 0x2);
}
@@ -209,6 +344,76 @@ static void __init pas_init_early(void)
iommu_init_early_pasemi();
}
+#ifdef CONFIG_PCMCIA
+static int pcmcia_notify(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct device *dev = data;
+ struct device *parent;
+ struct pcmcia_device *pdev = to_pcmcia_dev(dev);
+
+ /* We are only intereted in device addition */
+ if (action != BUS_NOTIFY_ADD_DEVICE)
+ return 0;
+
+ parent = pdev->socket->dev.parent;
+
+ /* We know electra_cf devices will always have of_node set, since
+ * electra_cf is an of_platform driver.
+ */
+ if (!parent->of_node)
+ return 0;
+
+ if (!of_device_is_compatible(parent->of_node, "electra-cf"))
+ return 0;
+
+ /* We use the direct ops for localbus */
+ dev->archdata.dma_ops = &dma_direct_ops;
+
+ return 0;
+}
+
+static struct notifier_block pcmcia_notifier = {
+ .notifier_call = pcmcia_notify,
+};
+
+static inline void pasemi_pcmcia_init(void)
+{
+ extern struct bus_type pcmcia_bus_type;
+
+ bus_register_notifier(&pcmcia_bus_type, &pcmcia_notifier);
+}
+
+#else
+
+static inline void pasemi_pcmcia_init(void)
+{
+}
+
+#endif
+
+
+static struct of_device_id pasemi_bus_ids[] = {
+ /* Unfortunately needed for legacy firmwares */
+ { .type = "localbus", },
+ { .type = "sdc", },
+ /* These are the proper entries, which newer firmware uses */
+ { .compatible = "pasemi,localbus", },
+ { .compatible = "pasemi,sdc", },
+ {},
+};
+
+static int __init pasemi_publish_devices(void)
+{
+ pasemi_pcmcia_init();
+
+ /* Publish OF platform devices for SDC and other non-PCI devices */
+ of_platform_bus_probe(NULL, pasemi_bus_ids, NULL);
+
+ return 0;
+}
+machine_device_initcall(pasemi, pasemi_publish_devices);
+
/*
* Called very early, MMU is off, device-tree isn't unflattened
@@ -217,7 +422,8 @@ static int __init pas_probe(void)
{
unsigned long root = of_get_flat_dt_root();
- if (!of_flat_dt_is_compatible(root, "PA6T-1682M"))
+ if (!of_flat_dt_is_compatible(root, "PA6T-1682M") &&
+ !of_flat_dt_is_compatible(root, "pasemi,pwrficient"))
return 0;
hpte_init_native();
@@ -227,8 +433,8 @@ static int __init pas_probe(void)
return 1;
}
-define_machine(pas) {
- .name = "PA Semi PA6T-1682M",
+define_machine(pasemi) {
+ .name = "PA Semi PWRficient",
.probe = pas_probe,
.setup_arch = pas_setup_arch,
.init_early = pas_init_early,
@@ -237,8 +443,6 @@ define_machine(pas) {
.restart = pas_restart,
.get_boot_time = pas_get_boot_time,
.calibrate_decr = generic_calibrate_decr,
- .check_legacy_ioport = pas_check_legacy_ioport,
.progress = pas_progress,
.machine_check_exception = pas_machine_check_handler,
- .pci_irq_fixup = pas_pci_irq_fixup,
};