Commit 861fe906 authored by David S. Miller's avatar David S. Miller

[SPARC64]: SUN4U PCI-E controller support.

Some minor refactoring in the generic code was necessary for
this:

1) This controller requires 8-byte access to the interrupt map
   and clear register.  They are 64-bits on all the other
   SBUS and PCI controllers anyways, so this was easy to cure.

2) The IMAP register has a different layout and some bits that we
   need to preserve, so use a read/modify/write when making
   changes to the IMAP register in generic code.

3) Flushing the entire IOMMU TLB is best done with a single write
   to a register on this PCI controller, add a iommu->iommu_flushinv
   for this.

Still lacks MSI support, that will come later.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4cad6917
...@@ -17,7 +17,7 @@ obj-y := process.o setup.o cpu.o idprom.o \ ...@@ -17,7 +17,7 @@ obj-y := process.o setup.o cpu.o idprom.o \
obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \ obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \
pci_psycho.o pci_sabre.o pci_schizo.o \ pci_psycho.o pci_sabre.o pci_schizo.o \
pci_sun4v.o pci_sun4v_asm.o pci_sun4v.o pci_sun4v_asm.o pci_fire.o
obj-$(CONFIG_SMP) += smp.o trampoline.o obj-$(CONFIG_SMP) += smp.o trampoline.o
obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o
obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o
......
...@@ -279,7 +279,7 @@ static void sun4u_irq_enable(unsigned int virt_irq) ...@@ -279,7 +279,7 @@ static void sun4u_irq_enable(unsigned int virt_irq)
struct irq_handler_data *data = get_irq_chip_data(virt_irq); struct irq_handler_data *data = get_irq_chip_data(virt_irq);
if (likely(data)) { if (likely(data)) {
unsigned long cpuid, imap; unsigned long cpuid, imap, val;
unsigned int tid; unsigned int tid;
cpuid = irq_choose_cpu(virt_irq); cpuid = irq_choose_cpu(virt_irq);
...@@ -287,7 +287,11 @@ static void sun4u_irq_enable(unsigned int virt_irq) ...@@ -287,7 +287,11 @@ static void sun4u_irq_enable(unsigned int virt_irq)
tid = sun4u_compute_tid(imap, cpuid); tid = sun4u_compute_tid(imap, cpuid);
upa_writel(tid | IMAP_VALID, imap); val = upa_readq(imap);
val &= ~(IMAP_TID_UPA | IMAP_TID_JBUS |
IMAP_AID_SAFARI | IMAP_NID_SAFARI);
val |= tid | IMAP_VALID;
upa_writeq(val, imap);
} }
} }
...@@ -297,10 +301,10 @@ static void sun4u_irq_disable(unsigned int virt_irq) ...@@ -297,10 +301,10 @@ static void sun4u_irq_disable(unsigned int virt_irq)
if (likely(data)) { if (likely(data)) {
unsigned long imap = data->imap; unsigned long imap = data->imap;
u32 tmp = upa_readl(imap); u32 tmp = upa_readq(imap);
tmp &= ~IMAP_VALID; tmp &= ~IMAP_VALID;
upa_writel(tmp, imap); upa_writeq(tmp, imap);
} }
} }
...@@ -309,7 +313,7 @@ static void sun4u_irq_end(unsigned int virt_irq) ...@@ -309,7 +313,7 @@ static void sun4u_irq_end(unsigned int virt_irq)
struct irq_handler_data *data = get_irq_chip_data(virt_irq); struct irq_handler_data *data = get_irq_chip_data(virt_irq);
if (likely(data)) if (likely(data))
upa_writel(ICLR_IDLE, data->iclr); upa_writeq(ICLR_IDLE, data->iclr);
} }
static void sun4v_irq_enable(unsigned int virt_irq) static void sun4v_irq_enable(unsigned int virt_irq)
...@@ -465,7 +469,7 @@ unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap) ...@@ -465,7 +469,7 @@ unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap)
BUG_ON(tlb_type == hypervisor); BUG_ON(tlb_type == hypervisor);
ino = (upa_readl(imap) & (IMAP_IGN | IMAP_INO)) + inofixup; ino = (upa_readq(imap) & (IMAP_IGN | IMAP_INO)) + inofixup;
bucket = &ivector_table[ino]; bucket = &ivector_table[ino];
if (!bucket->virt_irq) { if (!bucket->virt_irq) {
bucket->virt_irq = virt_irq_alloc(__irq(bucket)); bucket->virt_irq = virt_irq_alloc(__irq(bucket));
......
...@@ -190,6 +190,7 @@ extern void schizo_init(struct device_node *, const char *); ...@@ -190,6 +190,7 @@ extern void schizo_init(struct device_node *, const char *);
extern void schizo_plus_init(struct device_node *, const char *); extern void schizo_plus_init(struct device_node *, const char *);
extern void tomatillo_init(struct device_node *, const char *); extern void tomatillo_init(struct device_node *, const char *);
extern void sun4v_pci_init(struct device_node *, const char *); extern void sun4v_pci_init(struct device_node *, const char *);
extern void fire_pci_init(struct device_node *, const char *);
static struct { static struct {
char *model_name; char *model_name;
...@@ -207,6 +208,7 @@ static struct { ...@@ -207,6 +208,7 @@ static struct {
{ "SUNW,tomatillo", tomatillo_init }, { "SUNW,tomatillo", tomatillo_init },
{ "pci108e,a801", tomatillo_init }, { "pci108e,a801", tomatillo_init },
{ "SUNW,sun4v-pci", sun4v_pci_init }, { "SUNW,sun4v-pci", sun4v_pci_init },
{ "pciex108e,80f0", fire_pci_init },
}; };
#define PCI_NUM_CONTROLLER_TYPES (sizeof(pci_controller_table) / \ #define PCI_NUM_CONTROLLER_TYPES (sizeof(pci_controller_table) / \
sizeof(pci_controller_table[0])) sizeof(pci_controller_table[0]))
...@@ -436,6 +438,13 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, ...@@ -436,6 +438,13 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
printk(" class: 0x%x device name: %s\n", printk(" class: 0x%x device name: %s\n",
dev->class, pci_name(dev)); dev->class, pci_name(dev));
/* I have seen IDE devices which will not respond to
* the bmdma simplex check reads if bus mastering is
* disabled.
*/
if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE)
pci_set_master(dev);
dev->current_state = 4; /* unknown power state */ dev->current_state = 4; /* unknown power state */
dev->error_state = pci_channel_io_normal; dev->error_state = pci_channel_io_normal;
......
This diff is collapsed.
...@@ -37,17 +37,21 @@ ...@@ -37,17 +37,21 @@
/* Must be invoked under the IOMMU lock. */ /* Must be invoked under the IOMMU lock. */
static void __iommu_flushall(struct iommu *iommu) static void __iommu_flushall(struct iommu *iommu)
{ {
unsigned long tag; if (iommu->iommu_flushinv) {
int entry; pci_iommu_write(iommu->iommu_flushinv, ~(u64)0);
} else {
unsigned long tag;
int entry;
tag = iommu->iommu_flush + (0xa580UL - 0x0210UL); tag = iommu->iommu_flush + (0xa580UL - 0x0210UL);
for (entry = 0; entry < 16; entry++) { for (entry = 0; entry < 16; entry++) {
pci_iommu_write(tag, 0); pci_iommu_write(tag, 0);
tag += 8; tag += 8;
} }
/* Ensure completion of previous PIO writes. */ /* Ensure completion of previous PIO writes. */
(void) pci_iommu_read(iommu->write_complete_reg); (void) pci_iommu_read(iommu->write_complete_reg);
}
} }
#define IOPTE_CONSISTENT(CTX) \ #define IOPTE_CONSISTENT(CTX) \
......
...@@ -386,11 +386,9 @@ static unsigned int psycho_irq_build(struct device_node *dp, ...@@ -386,11 +386,9 @@ static unsigned int psycho_irq_build(struct device_node *dp,
/* Now build the IRQ bucket. */ /* Now build the IRQ bucket. */
imap = controller_regs + imap_off; imap = controller_regs + imap_off;
imap += 4;
iclr_off = psycho_iclr_offset(ino); iclr_off = psycho_iclr_offset(ino);
iclr = controller_regs + iclr_off; iclr = controller_regs + iclr_off;
iclr += 4;
if ((ino & 0x20) == 0) if ((ino & 0x20) == 0)
inofixup = ino & 0x03; inofixup = ino & 0x03;
...@@ -613,11 +611,9 @@ static unsigned int sabre_irq_build(struct device_node *dp, ...@@ -613,11 +611,9 @@ static unsigned int sabre_irq_build(struct device_node *dp,
/* Now build the IRQ bucket. */ /* Now build the IRQ bucket. */
imap = controller_regs + imap_off; imap = controller_regs + imap_off;
imap += 4;
iclr_off = sabre_iclr_offset(ino); iclr_off = sabre_iclr_offset(ino);
iclr = controller_regs + iclr_off; iclr = controller_regs + iclr_off;
iclr += 4;
if ((ino & 0x20) == 0) if ((ino & 0x20) == 0)
inofixup = ino & 0x03; inofixup = ino & 0x03;
...@@ -679,13 +675,14 @@ static unsigned long schizo_iclr_offset(unsigned long ino) ...@@ -679,13 +675,14 @@ static unsigned long schizo_iclr_offset(unsigned long ino)
static unsigned long schizo_ino_to_iclr(unsigned long pbm_regs, static unsigned long schizo_ino_to_iclr(unsigned long pbm_regs,
unsigned int ino) unsigned int ino)
{ {
return pbm_regs + schizo_iclr_offset(ino) + 4;
return pbm_regs + schizo_iclr_offset(ino);
} }
static unsigned long schizo_ino_to_imap(unsigned long pbm_regs, static unsigned long schizo_ino_to_imap(unsigned long pbm_regs,
unsigned int ino) unsigned int ino)
{ {
return pbm_regs + schizo_imap_offset(ino) + 4; return pbm_regs + schizo_imap_offset(ino);
} }
#define schizo_read(__reg) \ #define schizo_read(__reg) \
...@@ -848,6 +845,85 @@ static void pci_sun4v_irq_trans_init(struct device_node *dp) ...@@ -848,6 +845,85 @@ static void pci_sun4v_irq_trans_init(struct device_node *dp)
dp->irq_trans->data = (void *) (unsigned long) dp->irq_trans->data = (void *) (unsigned long)
((regs->phys_addr >> 32UL) & 0x0fffffff); ((regs->phys_addr >> 32UL) & 0x0fffffff);
} }
struct fire_irq_data {
unsigned long pbm_regs;
u32 portid;
};
#define FIRE_IMAP_BASE 0x001000
#define FIRE_ICLR_BASE 0x001400
static unsigned long fire_imap_offset(unsigned long ino)
{
return FIRE_IMAP_BASE + (ino * 8UL);
}
static unsigned long fire_iclr_offset(unsigned long ino)
{
return FIRE_ICLR_BASE + (ino * 8UL);
}
static unsigned long fire_ino_to_iclr(unsigned long pbm_regs,
unsigned int ino)
{
return pbm_regs + fire_iclr_offset(ino);
}
static unsigned long fire_ino_to_imap(unsigned long pbm_regs,
unsigned int ino)
{
return pbm_regs + fire_imap_offset(ino);
}
static unsigned int fire_irq_build(struct device_node *dp,
unsigned int ino,
void *_data)
{
struct fire_irq_data *irq_data = _data;
unsigned long pbm_regs = irq_data->pbm_regs;
unsigned long imap, iclr;
unsigned long int_ctrlr;
ino &= 0x3f;
/* Now build the IRQ bucket. */
imap = fire_ino_to_imap(pbm_regs, ino);
iclr = fire_ino_to_iclr(pbm_regs, ino);
/* Set the interrupt controller number. */
int_ctrlr = 1 << 6;
upa_writeq(int_ctrlr, imap);
/* The interrupt map registers do not have an INO field
* like other chips do. They return zero in the INO
* field, and the interrupt controller number is controlled
* in bits 6 thru 9. So in order for build_irq() to get
* the INO right we pass it in as part of the fixup
* which will get added to the map register zero value
* read by build_irq().
*/
ino |= (irq_data->portid << 6);
ino -= int_ctrlr;
return build_irq(ino, iclr, imap);
}
static void fire_irq_trans_init(struct device_node *dp)
{
const struct linux_prom64_registers *regs;
struct fire_irq_data *irq_data;
dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
dp->irq_trans->irq_build = fire_irq_build;
irq_data = prom_early_alloc(sizeof(struct fire_irq_data));
regs = of_get_property(dp, "reg", NULL);
dp->irq_trans->data = irq_data;
irq_data->pbm_regs = regs[0].phys_addr;
irq_data->portid = of_getintprop_default(dp, "portid", 0);
}
#endif /* CONFIG_PCI */ #endif /* CONFIG_PCI */
#ifdef CONFIG_SBUS #ifdef CONFIG_SBUS
...@@ -1069,6 +1145,7 @@ static struct irq_trans pci_irq_trans_table[] = { ...@@ -1069,6 +1145,7 @@ static struct irq_trans pci_irq_trans_table[] = {
{ "SUNW,tomatillo", tomatillo_irq_trans_init }, { "SUNW,tomatillo", tomatillo_irq_trans_init },
{ "pci108e,a801", tomatillo_irq_trans_init }, { "pci108e,a801", tomatillo_irq_trans_init },
{ "SUNW,sun4v-pci", pci_sun4v_irq_trans_init }, { "SUNW,sun4v-pci", pci_sun4v_irq_trans_init },
{ "pciex108e,80f0", fire_irq_trans_init },
}; };
#endif #endif
......
...@@ -32,6 +32,7 @@ struct iommu { ...@@ -32,6 +32,7 @@ struct iommu {
unsigned long iommu_control; unsigned long iommu_control;
unsigned long iommu_tsbbase; unsigned long iommu_tsbbase;
unsigned long iommu_flush; unsigned long iommu_flush;
unsigned long iommu_flushinv;
unsigned long iommu_ctxflush; unsigned long iommu_ctxflush;
unsigned long write_complete_reg; unsigned long write_complete_reg;
unsigned long dummy_page; unsigned long dummy_page;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment