On 10/20/2011 10:19 PM, Thomas Bogendoerfer wrote: > SGI IP22/IP28 machines have GIO busses for adding graphics and other > extension cards. This patch adds support for GIO driver/device > handling and converts the newport console driver to a GIO driver. > > Signed-off-by: Thomas Bogendoerfer <tsbogend@xxxxxxxxxxxxxxxx> > --- > > Changes to last version: > - use EXPORT_SYMBOL_GPL > - ChallengeS has only 2 slots > - use subsys_initcall for gio detection > > Florian, the whole patch should go through the MIPS tree, if it's ok for > you. No problem. I'm not even sure whether I'm responsible for it as it is not a framebuffer but a console driver. Anyway I had a look at the console code so you may add an Acked-by: Florian Tobias Schandinat <FlorianSchandinat@xxxxxx> if you like. Best regards, Florian Tobias Schandinat > arch/mips/include/asm/gio_device.h | 56 +++++ > arch/mips/sgi-ip22/Makefile | 2 +- > arch/mips/sgi-ip22/ip22-gio.c | 414 +++++++++++++++++++++++++++++++++++ > arch/mips/sgi-ip22/ip22-mc.c | 10 +- > arch/mips/sgi-ip22/ip22-setup.c | 21 -- > drivers/video/console/newport_con.c | 61 ++++-- > 6 files changed, 518 insertions(+), 46 deletions(-) > > diff --git a/arch/mips/include/asm/gio_device.h b/arch/mips/include/asm/gio_device.h > new file mode 100644 > index 0000000..5437c84 > --- /dev/null > +++ b/arch/mips/include/asm/gio_device.h > @@ -0,0 +1,56 @@ > +#include <linux/device.h> > +#include <linux/mod_devicetable.h> > + > +struct gio_device_id { > + __u8 id; > +}; > + > +struct gio_device { > + struct device dev; > + struct resource resource; > + unsigned int irq; > + unsigned int slotno; > + > + const char *name; > + struct gio_device_id id; > + unsigned id32:1; > + unsigned gio64:1; > +}; > +#define to_gio_device(d) container_of(d, struct gio_device, dev) > + > +struct gio_driver { > + const char *name; > + struct module *owner; > + const struct gio_device_id *id_table; > + > + int (*probe)(struct gio_device *, const struct gio_device_id *); > + void (*remove)(struct gio_device *); > + int (*suspend)(struct gio_device *, pm_message_t); > + int (*resume)(struct gio_device *); > + void (*shutdown)(struct gio_device *); > + > + struct device_driver driver; > +}; > +#define to_gio_driver(drv) container_of(drv, struct gio_driver, driver) > + > +extern const struct gio_device_id *gio_match_device(const struct gio_device_id *, > + const struct gio_device *); > +extern struct gio_device *gio_dev_get(struct gio_device *); > +extern void gio_dev_put(struct gio_device *); > + > +extern int gio_device_register(struct gio_device *); > +extern void gio_device_unregister(struct gio_device *); > +extern void gio_release_dev(struct device *); > + > +static inline void gio_device_free(struct gio_device *dev) > +{ > + gio_release_dev(&dev->dev); > +} > + > +extern int gio_register_driver(struct gio_driver *); > +extern void gio_unregister_driver(struct gio_driver *); > + > +#define gio_get_drvdata(_dev) drv_get_drvdata(&(_dev)->dev) > +#define gio_set_drvdata(_dev, data) drv_set_drvdata(&(_dev)->dev, (data)) > + > +extern void gio_set_master(struct gio_device *); > diff --git a/arch/mips/sgi-ip22/Makefile b/arch/mips/sgi-ip22/Makefile > index cc53849..411cda9 100644 > --- a/arch/mips/sgi-ip22/Makefile > +++ b/arch/mips/sgi-ip22/Makefile > @@ -4,7 +4,7 @@ > # > > obj-y += ip22-mc.o ip22-hpc.o ip22-int.o ip22-time.o ip22-nvram.o \ > - ip22-platform.o ip22-reset.o ip22-setup.o > + ip22-platform.o ip22-reset.o ip22-setup.o ip22-gio.o > > obj-$(CONFIG_SGI_IP22) += ip22-berr.o > obj-$(CONFIG_SGI_IP28) += ip28-berr.o > diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c > new file mode 100644 > index 0000000..d841529 > --- /dev/null > +++ b/arch/mips/sgi-ip22/ip22-gio.c > @@ -0,0 +1,414 @@ > +#include <linux/kernel.h> > +#include <linux/init.h> > +#include <linux/slab.h> > + > +#include <asm/addrspace.h> > +#include <asm/paccess.h> > +#include <asm/gio_device.h> > +#include <asm/sgi/gio.h> > +#include <asm/sgi/hpc3.h> > +#include <asm/sgi/mc.h> > +#include <asm/sgi/ip22.h> > + > +static struct bus_type gio_bus; > + > +static struct { > + const char *name; > + __u8 id; > +} gio_name_table[] = { > + { .name = "SGI Impact", .id = 0x10 }, > + { .name = "Phobos G160", .id = 0x35 }, > + /* fake IDs */ > + { .name = "SGI Newport", .id = 0x7e }, > + { .name = "SGI GR2/GR3", .id = 0x7f }, > +}; > + > +/** > + * gio_match_device - Tell if an of_device structure has a matching > + * gio_match structure > + * @ids: array of of device match structures to search in > + * @dev: the of device structure to match against > + * > + * Used by a driver to check whether an of_device present in the > + * system is in its list of supported devices. > + */ > +const struct gio_device_id *gio_match_device(const struct gio_device_id *match, > + const struct gio_device *dev) > +{ > + const struct gio_device_id *ids; > + > + for (ids = match; ids->id != 0xff; ids++) > + if (ids->id == dev->id.id) > + return ids; > + > + return NULL; > +} > +EXPORT_SYMBOL_GPL(gio_match_device); > + > +struct gio_device *gio_dev_get(struct gio_device *dev) > +{ > + struct device *tmp; > + > + if (!dev) > + return NULL; > + tmp = get_device(&dev->dev); > + if (tmp) > + return to_gio_device(tmp); > + else > + return NULL; > +} > +EXPORT_SYMBOL_GPL(gio_dev_get); > + > +void gio_dev_put(struct gio_device *dev) > +{ > + if (dev) > + put_device(&dev->dev); > +} > +EXPORT_SYMBOL_GPL(gio_dev_put); > + > +static ssize_t dev_show_name(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct gio_device *giodev; > + > + giodev = to_gio_device(dev); > + return sprintf(buf, "%s", giodev->name); > +} > + > +static DEVICE_ATTR(name, S_IRUGO, dev_show_name, NULL); > + > +static ssize_t dev_show_id(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct gio_device *giodev; > + > + giodev = to_gio_device(dev); > + return sprintf(buf, "%x", giodev->id.id); > +} > + > +static DEVICE_ATTR(id, S_IRUGO, dev_show_id, NULL); > + > +/** > + * gio_release_dev - free an gio device structure when all users of it are finished. > + * @dev: device that's been disconnected > + * > + * Will be called only by the device core when all users of this gio device are > + * done. > + */ > +void gio_release_dev(struct device *dev) > +{ > + struct gio_device *giodev; > + > + giodev = to_gio_device(dev); > + kfree(giodev); > +} > +EXPORT_SYMBOL_GPL(gio_release_dev); > + > +int gio_device_register(struct gio_device *giodev) > +{ > + int rc; > + > + giodev->dev.bus = &gio_bus; > + rc = device_register(&giodev->dev); > + if (rc) > + return rc; > + > + rc = device_create_file(&giodev->dev, &dev_attr_name); > + if (rc) > + goto err; > + rc = device_create_file(&giodev->dev, &dev_attr_id); > + if (rc) > + goto err; > + > + return 0; > + > +err: > + device_unregister(&giodev->dev); > + return rc; > +} > +EXPORT_SYMBOL_GPL(gio_device_register); > + > +void gio_device_unregister(struct gio_device *giodev) > +{ > + device_remove_file(&giodev->dev, &dev_attr_id); > + device_remove_file(&giodev->dev, &dev_attr_name); > + device_unregister(&giodev->dev); > +} > +EXPORT_SYMBOL_GPL(gio_device_unregister); > + > +static int gio_bus_match(struct device *dev, struct device_driver *drv) > +{ > + struct gio_device *gio_dev = to_gio_device(dev); > + struct gio_driver *gio_drv = to_gio_driver(drv); > + > + return gio_match_device(gio_drv->id_table, gio_dev) != NULL; > +} > + > +static int gio_device_probe(struct device *dev) > +{ > + int error = -ENODEV; > + struct gio_driver *drv; > + struct gio_device *gio_dev; > + const struct gio_device_id *match; > + > + drv = to_gio_driver(dev->driver); > + gio_dev = to_gio_device(dev); > + > + if (!drv->probe) > + return error; > + > + gio_dev_get(gio_dev); > + > + match = gio_match_device(drv->id_table, gio_dev); > + if (match) > + error = drv->probe(gio_dev, match); > + if (error) > + gio_dev_put(gio_dev); > + > + return error; > +} > + > +static int gio_device_remove(struct device *dev) > +{ > + struct gio_device *gio_dev = to_gio_device(dev); > + struct gio_driver *drv = to_gio_driver(dev->driver); > + > + if (dev->driver && drv->remove) > + drv->remove(gio_dev); > + return 0; > +} > + > +static int gio_device_suspend(struct device *dev, pm_message_t state) > +{ > + struct gio_device *gio_dev = to_gio_device(dev); > + struct gio_driver *drv = to_gio_driver(dev->driver); > + int error = 0; > + > + if (dev->driver && drv->suspend) > + error = drv->suspend(gio_dev, state); > + return error; > +} > + > +static int gio_device_resume(struct device *dev) > +{ > + struct gio_device *gio_dev = to_gio_device(dev); > + struct gio_driver *drv = to_gio_driver(dev->driver); > + int error = 0; > + > + if (dev->driver && drv->resume) > + error = drv->resume(gio_dev); > + return error; > +} > + > +static void gio_device_shutdown(struct device *dev) > +{ > + struct gio_device *gio_dev = to_gio_device(dev); > + struct gio_driver *drv = to_gio_driver(dev->driver); > + > + if (dev->driver && drv->shutdown) > + drv->shutdown(gio_dev); > +} > + > +int gio_register_driver(struct gio_driver *drv) > +{ > + /* initialize common driver fields */ > + if (!drv->driver.name) > + drv->driver.name = drv->name; > + if (!drv->driver.owner) > + drv->driver.owner = drv->owner; > + drv->driver.bus = &gio_bus; > + > + /* register with core */ > + return driver_register(&drv->driver); > +} > +EXPORT_SYMBOL_GPL(gio_register_driver); > + > +void gio_unregister_driver(struct gio_driver *drv) > +{ > + driver_unregister(&drv->driver); > +} > +EXPORT_SYMBOL_GPL(gio_unregister_driver); > + > +void gio_set_master(struct gio_device *dev) > +{ > + u32 tmp = sgimc->giopar; > + > + switch (dev->slotno) { > + case 0: > + tmp |= SGIMC_GIOPAR_MASTERGFX; > + break; > + case 1: > + tmp |= SGIMC_GIOPAR_MASTEREXP0; > + break; > + case 2: > + tmp |= SGIMC_GIOPAR_MASTEREXP1; > + break; > + } > + sgimc->giopar = tmp; > +} > +EXPORT_SYMBOL_GPL(gio_set_master); > + > +void ip22_gio_set_64bit(int slotno) > +{ > + u32 tmp = sgimc->giopar; > + > + switch (slotno) { > + case 0: > + tmp |= SGIMC_GIOPAR_GFX64; > + break; > + case 1: > + tmp |= SGIMC_GIOPAR_EXP064; > + break; > + case 2: > + tmp |= SGIMC_GIOPAR_EXP164; > + break; > + } > + sgimc->giopar = tmp; > +} > + > +static int ip22_gio_id(unsigned long addr, u32 *res) > +{ > + u8 tmp8; > + u8 tmp16; > + u32 tmp32; > + u8 *ptr8; > + u16 *ptr16; > + u32 *ptr32; > + > + ptr32 = (void *)CKSEG1ADDR(addr); > + if (!get_dbe(tmp32, ptr32)) { > + /* > + * We got no DBE, but this doesn't mean anything. > + * If GIO is pipelined (which can't be disabled > + * for GFX slot) we don't get a DBE, but we see > + * the transfer size as data. So we do an 8bit > + * and a 16bit access and check whether the common > + * data matches > + */ > + ptr8 = (void *)CKSEG1ADDR(addr + 3); > + get_dbe(tmp8, ptr8); > + ptr16 = (void *)CKSEG1ADDR(addr + 2); > + get_dbe(tmp16, ptr16); > + if (tmp8 == (tmp16 & 0xff) && > + tmp8 == (tmp32 & 0xff) && > + tmp16 == (tmp32 & 0xffff)) { > + *res = tmp32; > + return 1; > + } > + } > + return 0; /* nothing here */ > +} > + > +#define HQ2_MYSTERY_OFFS 0x6A07C > +#define NEWPORT_USTATUS_OFFS 0xF133C > + > +static int ip22_is_gr2(unsigned long addr) > +{ > + u32 tmp; > + u32 *ptr; > + > + /* HQ2 only allows 32bit accesses */ > + ptr = (void *)CKSEG1ADDR(addr + HQ2_MYSTERY_OFFS); > + if (!get_dbe(tmp, ptr)) { > + if (tmp == 0xdeadbeef) > + return 1; > + } > + return 0; > +} > + > + > +static void ip22_check_gio(int slotno, unsigned long addr) > +{ > + const char *name = "Unknown"; > + struct gio_device *gio_dev; > + u32 tmp; > + __u8 id; > + int i; > + > + /* first look for GR2/GR3 by checking mystery register */ > + if (ip22_is_gr2(addr)) > + tmp = 0x7f; > + else { > + if (!ip22_gio_id(addr, &tmp)) { > + /* > + * no GIO signature at start address of slot, but > + * Newport doesn't have one, so let's check usea > + * status register > + */ > + if (ip22_gio_id(addr + NEWPORT_USTATUS_OFFS, &tmp)) > + tmp = 0x7e; > + else > + tmp = 0; > + } > + } > + if (tmp) { > + id = GIO_ID(tmp); > + if (tmp & GIO_32BIT_ID) { > + if (tmp & GIO_64BIT_IFACE) > + ip22_gio_set_64bit(slotno); > + } > + for (i = 0; i < ARRAY_SIZE(gio_name_table); i++) { > + if (id == gio_name_table[i].id) { > + name = gio_name_table[i].name; > + break; > + } > + } > + printk(KERN_INFO "GIO: slot %d : %s (id %x)\n", > + slotno, name, id); > + gio_dev = kzalloc(sizeof *gio_dev, GFP_KERNEL); > + gio_dev->name = name; > + gio_dev->slotno = slotno; > + gio_dev->id.id = id; > + gio_dev->resource.start = addr; > + gio_dev->resource.end = addr + 0x3fffff; > + gio_dev->resource.flags = IORESOURCE_MEM; > + dev_set_name(&gio_dev->dev, "gio%d", slotno); > + gio_device_register(gio_dev); > + } else > + printk(KERN_INFO "GIO: slot %d : Empty\n", slotno); > +} > + > +static struct bus_type gio_bus = { > + .name = "gio", > + .match = gio_bus_match, > + .probe = gio_device_probe, > + .remove = gio_device_remove, > + .suspend = gio_device_suspend, > + .resume = gio_device_resume, > + .shutdown = gio_device_shutdown, > +}; > + > +static struct resource gio_bus_resource = { > + .start = GIO_SLOT_GFX_BASE, > + .end = GIO_SLOT_GFX_BASE + 0x9fffff, > + .name = "GIO Bus", > + .flags = IORESOURCE_MEM, > +}; > + > +int __init ip22_gio_init(void) > +{ > + unsigned int pbdma __maybe_unused; > + int ret; > + > + ret = bus_register(&gio_bus); > + if (!ret) { > + request_resource(&iomem_resource, &gio_bus_resource); > + printk(KERN_INFO "GIO: Probing bus...\n"); > + > + if (ip22_is_fullhouse() || > + !get_dbe(pbdma, (unsigned int *)&hpc3c1->pbdma[1])) { > + /* Indigo2 and ChallengeS */ > + ip22_check_gio(0, GIO_SLOT_GFX_BASE); > + ip22_check_gio(1, GIO_SLOT_EXP0_BASE); > + } else { > + /* Indy */ > + ip22_check_gio(0, GIO_SLOT_GFX_BASE); > + ip22_check_gio(1, GIO_SLOT_EXP0_BASE); > + ip22_check_gio(2, GIO_SLOT_EXP1_BASE); > + } > + } > + return ret; > +} > + > +subsys_initcall(ip22_gio_init); > + > diff --git a/arch/mips/sgi-ip22/ip22-mc.c b/arch/mips/sgi-ip22/ip22-mc.c > index d22262e..75ada8a 100644 > --- a/arch/mips/sgi-ip22/ip22-mc.c > +++ b/arch/mips/sgi-ip22/ip22-mc.c > @@ -139,11 +139,11 @@ void __init sgimc_init(void) > * zero. > */ > /* don't touch parity settings for IP28 */ > -#ifndef CONFIG_SGI_IP28 > tmp = sgimc->cpuctrl0; > - tmp |= (SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM | > - SGIMC_CCTRL0_R4KNOCHKPARR); > +#ifndef CONFIG_SGI_IP28 > + tmp |= SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM; > #endif > + tmp |= SGIMC_CCTRL0_R4KNOCHKPARR; > sgimc->cpuctrl0 = tmp; > > /* Step 3: Setup the MC write buffer depth, this is controlled > @@ -178,7 +178,8 @@ void __init sgimc_init(void) > */ > > /* First the basic invariants across all GIO64 implementations. */ > - tmp = SGIMC_GIOPAR_HPC64; /* All 1st HPC's interface at 64bits */ > + tmp = sgimc->giopar & SGIMC_GIOPAR_GFX64; /* keep gfx 64bit settings */ > + tmp |= SGIMC_GIOPAR_HPC64; /* All 1st HPC's interface at 64bits */ > tmp |= SGIMC_GIOPAR_ONEBUS; /* Only one physical GIO bus exists */ > > if (ip22_is_fullhouse()) { > @@ -193,7 +194,6 @@ void __init sgimc_init(void) > tmp |= SGIMC_GIOPAR_PLINEEXP0; /* exp[01] pipelined */ > tmp |= SGIMC_GIOPAR_PLINEEXP1; > tmp |= SGIMC_GIOPAR_MASTEREISA; /* EISA masters */ > - tmp |= SGIMC_GIOPAR_GFX64; /* GFX at 64 bits */ > } > } else { > /* Guiness specific settings. */ > diff --git a/arch/mips/sgi-ip22/ip22-setup.c b/arch/mips/sgi-ip22/ip22-setup.c > index 5e66213..c7bdfe4 100644 > --- a/arch/mips/sgi-ip22/ip22-setup.c > +++ b/arch/mips/sgi-ip22/ip22-setup.c > @@ -26,9 +26,6 @@ > #include <asm/sgi/hpc3.h> > #include <asm/sgi/ip22.h> > > -unsigned long sgi_gfxaddr; > -EXPORT_SYMBOL_GPL(sgi_gfxaddr); > - > extern void ip22_be_init(void) __init; > > void __init plat_mem_setup(void) > @@ -78,22 +75,4 @@ void __init plat_mem_setup(void) > prom_flags |= PROM_FLAG_USE_AS_CONSOLE; > add_preferred_console("arc", 0, NULL); > } > - > -#if defined(CONFIG_VT) && defined(CONFIG_SGI_NEWPORT_CONSOLE) > - { > - ULONG *gfxinfo; > - ULONG * (*__vec)(void) = (void *) (long) > - *((_PULONG *)(long)((PROMBLOCK)->pvector + 0x20)); > - > - gfxinfo = __vec(); > - sgi_gfxaddr = ((gfxinfo[1] >= 0xa0000000 > - && gfxinfo[1] <= 0xc0000000) > - ? gfxinfo[1] - 0xa0000000 : 0); > - > - /* newport addresses? */ > - if (sgi_gfxaddr == 0x1f0f0000 || sgi_gfxaddr == 0x1f4f0000) { > - conswitchp = &newport_con; > - } > - } > -#endif > } > diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c > index 93317b5..628b7ec 100644 > --- a/drivers/video/console/newport_con.c > +++ b/drivers/video/console/newport_con.c > @@ -25,14 +25,13 @@ > #include <asm/system.h> > #include <asm/page.h> > #include <asm/pgtable.h> > +#include <asm/gio_device.h> > + > #include <video/newport.h> > > #include <linux/linux_logo.h> > #include <linux/font.h> > > - > -extern unsigned long sgi_gfxaddr; > - > #define FONT_DATA ((unsigned char *)font_vga_8x16.data) > > /* borrowed from fbcon.c */ > @@ -304,12 +303,6 @@ static const char *newport_startup(void) > { > int i; > > - if (!sgi_gfxaddr) > - return NULL; > - > - if (!npregs) > - npregs = (struct newport_regs *)/* ioremap cannot fail */ > - ioremap(sgi_gfxaddr, sizeof(struct newport_regs)); > npregs->cset.config = NPORT_CFG_GD0; > > if (newport_wait(npregs)) > @@ -743,26 +736,56 @@ const struct consw newport_con = { > .con_save_screen = DUMMY > }; > > -#ifdef MODULE > -static int __init newport_console_init(void) > +static int newport_probe(struct gio_device *dev, > + const struct gio_device_id *id) > { > - if (!sgi_gfxaddr) > - return 0; > + unsigned long newport_addr; > > - if (!npregs) > - npregs = (struct newport_regs *)/* ioremap cannot fail */ > - ioremap(sgi_gfxaddr, sizeof(struct newport_regs)); > + if (!dev->resource.start) > + return -EINVAL; > + > + if (npregs) > + return -EBUSY; /* we only support one Newport as console */ > + > + newport_addr = dev->resource.start + 0xF0000; > + if (!request_mem_region(newport_addr, 0x10000, "Newport")) > + return -ENODEV; > + > + npregs = (struct newport_regs *)/* ioremap cannot fail */ > + ioremap(newport_addr, sizeof(struct newport_regs)); > > return take_over_console(&newport_con, 0, MAX_NR_CONSOLES - 1, 1); > } > -module_init(newport_console_init); > > -static void __exit newport_console_exit(void) > +static void newport_remove(struct gio_device *dev) > { > give_up_console(&newport_con); > iounmap((void *)npregs); > } > + > +static struct gio_device_id newport_ids[] = { > + { .id = 0x7e }, > + { .id = 0xff } > +}; > + > +static struct gio_driver newport_driver = { > + .name = "newport", > + .id_table = newport_ids, > + .probe = newport_probe, > + .remove = newport_remove, > +}; > + > +int __init newport_console_init(void) > +{ > + return gio_register_driver(&newport_driver); > +} > + > +void __exit newport_console_exit(void) > +{ > + gio_unregister_driver(&newport_driver); > +} > + > +module_init(newport_console_init); > module_exit(newport_console_exit); > -#endif > > MODULE_LICENSE("GPL"); >