* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define DEBUG
+
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/dma-mapping.h>
#include <asm/naca.h>
#include <asm/tlbflush.h>
#include <asm/tlb.h>
-#include <asm/hvcall.h>
#include <asm/prom.h>
#include <asm/abs_addr.h>
#include <asm/cputable.h>
+#include <asm/plpar_wrappers.h>
+
+#ifdef DEBUG
+#define DBG(fmt...) udbg_printf(fmt)
+#else
+#define DBG(fmt...)
+#endif
/* in pSeries_hvCall.S */
EXPORT_SYMBOL(plpar_hcall);
EXPORT_SYMBOL(plpar_hcall_norets);
EXPORT_SYMBOL(plpar_hcall_8arg_2ret);
-long poll_pending(void)
-{
- unsigned long dummy;
- return plpar_hcall(H_POLL_PENDING, 0, 0, 0, 0,
- &dummy, &dummy, &dummy);
-}
-
-long prod_processor(void)
-{
- plpar_hcall_norets(H_PROD);
- return(0);
-}
-
-long cede_processor(void)
-{
- plpar_hcall_norets(H_CEDE);
- return(0);
-}
-
-long register_vpa(unsigned long flags, unsigned long proc, unsigned long vpa)
-{
- plpar_hcall_norets(H_REGISTER_VPA, flags, proc, vpa);
- return(0);
-}
-
-long plpar_pte_remove(unsigned long flags,
- unsigned long ptex,
- unsigned long avpn,
- unsigned long *old_pteh_ret, unsigned long *old_ptel_ret)
-{
- unsigned long dummy;
- return plpar_hcall(H_REMOVE, flags, ptex, avpn, 0,
- old_pteh_ret, old_ptel_ret, &dummy);
-}
-
-long plpar_pte_read(unsigned long flags,
- unsigned long ptex,
- unsigned long *old_pteh_ret, unsigned long *old_ptel_ret)
-{
- unsigned long dummy;
- return plpar_hcall(H_READ, flags, ptex, 0, 0,
- old_pteh_ret, old_ptel_ret, &dummy);
-}
-
-long plpar_pte_protect(unsigned long flags,
- unsigned long ptex,
- unsigned long avpn)
-{
- return plpar_hcall_norets(H_PROTECT, flags, ptex, avpn);
-}
-
-long plpar_tce_get(unsigned long liobn,
- unsigned long ioba,
- unsigned long *tce_ret)
-{
- unsigned long dummy;
- return plpar_hcall(H_GET_TCE, liobn, ioba, 0, 0,
- tce_ret, &dummy, &dummy);
-}
-
-long plpar_tce_put(unsigned long liobn,
- unsigned long ioba,
- unsigned long tceval)
-{
- return plpar_hcall_norets(H_PUT_TCE, liobn, ioba, tceval);
-}
+extern void fw_feature_init(void);
+extern void pSeries_find_serial_port(void);
-long plpar_get_term_char(unsigned long termno,
- unsigned long *len_ret,
- char *buf_ret)
-{
- unsigned long *lbuf = (unsigned long *)buf_ret; /* ToDo: alignment? */
- return plpar_hcall(H_GET_TERM_CHAR, termno, 0, 0, 0,
- len_ret, lbuf+0, lbuf+1);
-}
-
-long plpar_put_term_char(unsigned long termno,
- unsigned long len,
- const char *buffer)
-{
- unsigned long *lbuf = (unsigned long *)buffer; /* ToDo: alignment? */
- return plpar_hcall_norets(H_PUT_TERM_CHAR, termno, len, lbuf[0],
- lbuf[1]);
-}
-
-static void tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum,
- long npages, unsigned long uaddr,
- enum dma_data_direction direction)
-{
- u64 rc;
- union tce_entry tce;
-
- tce.te_word = 0;
- tce.te_rpn = (virt_to_abs(uaddr)) >> PAGE_SHIFT;
- tce.te_rdwr = 1;
- if (direction != DMA_TO_DEVICE)
- tce.te_pciwr = 1;
-
- while (npages--) {
- rc = plpar_tce_put((u64)tbl->it_index,
- (u64)tcenum << 12,
- tce.te_word );
-
- if (rc && printk_ratelimit()) {
- printk("tce_build_pSeriesLP: plpar_tce_put failed. rc=%ld\n", rc);
- printk("\tindex = 0x%lx\n", (u64)tbl->it_index);
- printk("\ttcenum = 0x%lx\n", (u64)tcenum);
- printk("\ttce val = 0x%lx\n", tce.te_word );
- show_stack(current, (unsigned long *)__get_SP());
- }
-
- tcenum++;
- tce.te_rpn++;
- }
-}
-
-static void tce_free_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages)
-{
- u64 rc;
- union tce_entry tce;
-
- tce.te_word = 0;
-
- while (npages--) {
- rc = plpar_tce_put((u64)tbl->it_index,
- (u64)tcenum << 12,
- tce.te_word );
-
- if (rc && printk_ratelimit()) {
- printk("tce_free_pSeriesLP: plpar_tce_put failed\n");
- printk("\trc = %ld\n", rc);
- printk("\tindex = 0x%lx\n", (u64)tbl->it_index);
- printk("\ttcenum = 0x%lx\n", (u64)tcenum);
- printk("\ttce val = 0x%lx\n", tce.te_word );
- show_stack(current, (unsigned long *)__get_SP());
- }
-
- tcenum++;
- }
-}
int vtermno; /* virtual terminal# for udbg */
}
}
+/* call this from early_init() for a working debug console on
+ * vterm capable LPAR machines
+ */
+void udbg_init_debug_lpar(void)
+{
+ vtermno = 0;
+ ppc_md.udbg_putc = udbg_putcLP;
+ ppc_md.udbg_getc = udbg_getcLP;
+ ppc_md.udbg_getc_poll = udbg_getc_pollLP;
+}
+
/* returns 0 if couldn't find or use /chosen/stdout as console */
-static int find_udbg_vterm(void)
+int find_udbg_vterm(void)
{
struct device_node *stdout_node;
u32 *termno;
int found = 0;
/* find the boot console from /chosen/stdout */
- if (!of_stdout_device) {
- printk(KERN_WARNING "couldn't get path from /chosen/stdout!\n");
- return found;
- }
- stdout_node = of_find_node_by_path(of_stdout_device);
- if (!stdout_node) {
- printk(KERN_WARNING "couldn't find node from /chosen/stdout\n");
- return found;
- }
+ if (!of_chosen)
+ return 0;
+ name = (char *)get_property(of_chosen, "linux,stdout-path", NULL);
+ if (name == NULL)
+ return 0;
+ 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. */
name = (char *)get_property(stdout_node, "name", NULL);
} else {
/* XXX implement udbg_putcLP_vtty for hvterm-protocol1 case */
printk(KERN_WARNING "%s doesn't speak hvterm1; "
- "can't print udbg messages\n", of_stdout_device);
+ "can't print udbg messages\n",
+ stdout_node->full_name);
}
} 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", of_stdout_device);
+ "can't print udbg messages\n",
+ stdout_node->full_name);
} else {
printk(KERN_WARNING "don't know how to print to stdout '%s'\n",
- of_stdout_device);
+ stdout_node->full_name);
}
out:
return found;
}
-void pSeries_lpar_mm_init(void);
-
-/* This is called early in setup.c.
- * Use it to setup page table ppc_md stuff as well as udbg.
- */
-void pSeriesLP_init_early(void)
-{
- pSeries_lpar_mm_init();
-
- tce_init_pSeries();
-
- ppc_md.tce_build = tce_build_pSeriesLP;
- ppc_md.tce_free = tce_free_pSeriesLP;
-
- pci_iommu_init();
-
-#ifdef CONFIG_SMP
- smp_init_pSeries();
-#endif
-
- /* The keyboard is not useful in the LPAR environment.
- * Leave all the ppc_md keyboard interfaces NULL.
- */
-
- if (0 == find_udbg_vterm()) {
- printk(KERN_WARNING
- "can't use stdout; can't print early debug messages.\n");
- }
-}
long pSeries_lpar_hpte_insert(unsigned long hpte_group,
unsigned long va, unsigned long prpn,
lpar_rc = plpar_hcall(H_ENTER, flags, hpte_group, lhpte.dw0.dword0,
lhpte.dw1.dword1, &slot, &dummy0, &dummy1);
- if (lpar_rc == H_PTEG_Full)
+ if (unlikely(lpar_rc == H_PTEG_Full))
return -1;
/*
* will fail. However we must catch the failure in hash_page
* or we will loop forever, so return -2 in this case.
*/
- if (lpar_rc != H_Success)
+ if (unlikely(lpar_rc != H_Success))
return -2;
/* Because of iSeries, we have to pass down the secondary
if (lpar_rc == H_Success)
return i;
- if (lpar_rc != H_Not_Found)
- panic("Bad return code from pte remove rc = %lx\n",
- lpar_rc);
+ BUG_ON(lpar_rc != H_Not_Found);
slot_offset++;
slot_offset &= 0x7;
return -1;
}
+static void pSeries_lpar_hptab_clear(void)
+{
+ unsigned long size_bytes = 1UL << naca->pftSize;
+ unsigned long hpte_count = size_bytes >> 4;
+ unsigned long dummy1, dummy2;
+ int i;
+
+ /* TODO: Use bulk call */
+ for (i = 0; i < hpte_count; i++)
+ plpar_pte_remove(0, i, 0, &dummy1, &dummy2);
+}
+
/*
* NOTE: for updatepp ops we are fortunate that the linux "newpp" bits and
* the low 3 bits of flags happen to line up. So no transform is needed.
if (lpar_rc == H_Not_Found)
return -1;
- if (lpar_rc != H_Success)
- panic("bad return code from pte protect rc = %lx\n", lpar_rc);
+ BUG_ON(lpar_rc != H_Success);
return 0;
}
/* Do not need RPN to logical page translation */
/* No cross CEC PFT access */
flags = 0;
-
+
lpar_rc = plpar_pte_read(flags, slot, &dword0, &dummy_word1);
- if (lpar_rc != H_Success)
- panic("Error on pte read in get_hpte0 rc = %lx\n", lpar_rc);
+ BUG_ON(lpar_rc != H_Success);
return dword0;
}
vpn = va >> PAGE_SHIFT;
slot = pSeries_lpar_hpte_find(vpn);
- if (slot == -1)
- panic("updateboltedpp: Could not find page to bolt\n");
+ BUG_ON(slot == -1);
flags = newpp & 3;
lpar_rc = plpar_pte_protect(flags, slot, 0);
- if (lpar_rc != H_Success)
- panic("Bad return code from pte bolted protect rc = %lx\n",
- lpar_rc);
+ BUG_ON(lpar_rc != H_Success);
}
static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va,
if (lpar_rc == H_Not_Found)
return;
- if (lpar_rc != H_Success)
- panic("Bad return code from invalidate rc = %lx\n", lpar_rc);
+ BUG_ON(lpar_rc != H_Success);
}
/*
int i;
unsigned long flags;
struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch);
+ int lock_tlbie = !(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE);
- if (!(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE))
+ if (lock_tlbie)
spin_lock_irqsave(&pSeries_lpar_tlbie_lock, flags);
for (i = 0; i < number; i++)
flush_hash_page(context, batch->addr[i], batch->pte[i], local);
- if (!(cur_cpu_spec->cpu_features & CPU_FTR_LOCKLESS_TLBIE))
+ if (lock_tlbie)
spin_unlock_irqrestore(&pSeries_lpar_tlbie_lock, flags);
}
-void pSeries_lpar_mm_init(void)
+void hpte_init_lpar(void)
{
ppc_md.hpte_invalidate = pSeries_lpar_hpte_invalidate;
ppc_md.hpte_updatepp = pSeries_lpar_hpte_updatepp;
ppc_md.hpte_insert = pSeries_lpar_hpte_insert;
ppc_md.hpte_remove = pSeries_lpar_hpte_remove;
ppc_md.flush_hash_range = pSeries_lpar_flush_hash_range;
+ ppc_md.hpte_clear_all = pSeries_lpar_hptab_clear;
+
+ htab_finish_init();
}