/* * $Id$ * * HWDD NVRAM module: Initialization file * Copyright (c) 2011 Network Appliance, Inc. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hwdd_nvram.h" #define DRV_VERSION NVRAM_VERSION #define NVRAM9_BAR0_SIZE (64 * 1024) /*TODO: Detect this dynamically */ static struct mri_devhdr devhdr; static void fill_mri_devhdr(void); #ifdef DISPLAY_MRI_HEADER static void display_mri_devhdr(void); #endif static int nvram_open(struct inode *inode, struct file *fp); static int nvram_close(struct inode *inode, struct file *fp); static long nvram_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); static int __devinit nvram_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent); static void __devexit nvram_pci_remove(struct pci_dev *pdev); static void __iomem *addr0 ; /* holds the remapped address of NVRAM BAR0 */ static dev_t nvram_dev; static int nvram_major; static struct cdev *nv_cdev ; static const struct pci_device_id nvram_pci_dev_id[] = { { PCI_DEVICE(VENDOR_ID_NVRAM9, DEVICE_ID_NVRAM9) }, { } /* end of table */ }; MODULE_DEVICE_TABLE(pci, nvram_pci_dev_id); static struct pci_driver nvram_pci_driver = { .name = "NVRAM9", .id_table = nvram_pci_dev_id, .probe = nvram_pci_probe, .remove = __devexit_p(nvram_pci_remove) }; struct file_operations nvram_fops = { .owner = THIS_MODULE, .unlocked_ioctl = nvram_ioctl, .open = nvram_open, .release = nvram_close, }; /* * read from NVRAM BAR0 space * @offset : Register offset to read from * @value : buffer that holds the value read * returns -1 on failure, 0 on success */ int read_reg(int offset, u32 *value) { if ( !value || (offset % 4) || (offset > NVRAM9_BAR0_SIZE)) return -1; *value = readl(addr0 + offset); return 0; } /* * write to NVRAM BAR0 space * @offset : Register offset to read from * @value : value to write * returns -1 on failure, 0 on success */ int write_reg(int offset, u32 value) { if ( (offset % 4) || (offset > NVRAM9_BAR0_SIZE)) return -1; writel(value, addr0 + offset ); return 0; } /* * @id: MRI Element id to track * @instance: which instance (there can be multiple MRI elements of the same type * returns -1 if no id found */ int get_mri_base(u16 id, int instance) { u32 val; int offset = 0; int cnt = 0; int max_loop = 100; /* For now considering there can be a max of 100 MRI elements */ while (offset < NVRAM9_BAR0_SIZE) { val = readl(addr0 + offset); if ( (val & 0xffff) == id) { if (cnt++ == instance) return offset; } offset = (val >> 16) & 0xffff; if (!max_loop--) break; } return -1; } /* * Fill the NVRAM MRI Device Header */ static void fill_mri_devhdr(void) { struct mri_devhdr *hdr = addr0; devhdr.element = readl(&hdr->element); devhdr.flist = readl(&hdr->flist); devhdr.rev = readl(&hdr->rev); devhdr.tstamp = readl(&hdr->tstamp); devhdr.intr_stat = readl(&hdr->intr_stat); devhdr.intr_mask = readl(&hdr->intr_mask); } #ifdef DISPLAY_MRI_HEADER static void display_mri_devhdr(void) { printk(KERN_CRIT "Element ID :%x\n", devhdr.element & 0xffff); printk(KERN_CRIT "Rev Micro :%x\n", devhdr.rev & 0xffff); printk(KERN_CRIT "Rev Minor :%x\n", (devhdr.rev >> 16) & 0xff); printk(KERN_CRIT "Rev Major :%x\n", (devhdr.rev >> 24) & 0xff); } #endif /* * nvram_open */ static int nvram_open(struct inode *inode, struct file *fp) { return 0; } /* * nvram_close */ static int nvram_close(struct inode *inode, struct file *fp) { return 0; } /* * nvram_ioctl :- ioctl interface */ static long nvram_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { u8 __user *p = (u8 __user *)arg; if (_IOC_TYPE(cmd) != HWDD_NVRAM_MAGIC) { printk(KERN_ERR "NVRAM: bad ioctl !\n"); return -EINVAL; } switch (cmd) { int ret; u8 val; case HWDD_NVRAM_VERSION: { nv_version version; version.major = (u8)((devhdr.rev >> 24) & 0xff); version.minor = (u8)((devhdr.rev >> 16) & 0xff);; version.build_id = (u16) (devhdr.rev & 0xff) ; if(copy_to_user( (nv_version*)arg, &version, sizeof(nv_version)) ) { printk(KERN_ERR "ioctl: copy_to_user failed !\n"); return -EFAULT; } printk(KERN_CRIT " Val:%x \n", readl(addr0 + 3)); break; } case HWDD_NVRAM_DIP_DUMP: /* display the DIP switch contents */ if (get_user(val, p)) { printk(KERN_ERR "ioctl HWDD_NVRAM_DIP_DUMP, get_user failed \n"); return -EFAULT; } if ( (ret = dip_access(val)) < 0) return -EFAULT; if (put_user(ret, p)) return -EFAULT; printk(KERN_CRIT "Makeing non 4byte data access access \n"); printk(KERN_CRIT " Val:%x \n", readw(addr0)); break; default: printk(KERN_ERR "NVRAM: Unsupported ioctl !\n"); return -EINVAL; } return 0; } /* * probe routine, invoked when NVRAM device is detected on the PCI bus */ static int __devinit nvram_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int retval, irq; retval = pci_enable_device(pdev); if (retval) { dev_err(&pdev->dev, "cannot enable device\n"); goto err; } /* read PCI configuration area, presently we are not using interrupts */ irq = pdev->irq; retval = pci_request_regions(pdev, "nvram"); if (retval) { printk(KERN_ERR "NVRAM: failed to reserve resources\n"); goto err_dis; } /* FPGA control registers are mapped to a 64KB region in BAR0 */ addr0 = ioremap_nocache(pci_resource_start(pdev, 0), NVRAM9_BAR0_SIZE); if (!addr0) { printk(KERN_ERR "NVRAM: ioremap failed \n"); retval = 1; goto err_rel; } /* read the MRI header of NVRAM */ fill_mri_devhdr(); #ifdef DISPLAY_MRI_HEADER display_mri_devhdr(); #endif /* TWI initialization */ twi_init(); return 0; err_rel: pci_release_regions(pdev); err_dis: pci_disable_device(pdev); err: return retval; } /* * time for cleanup */ static void __devexit nvram_pci_remove(struct pci_dev *pdev) { iounmap(addr0); pci_release_regions(pdev); pci_disable_device(pdev); } /* * nvram_init */ static int __init nvram_init(void) { int ret = -ENODEV; /* standard char device stuff below */ nvram_dev = MKDEV(nvram_major, 0); ret = alloc_chrdev_region(&nvram_dev, 0, 1, HWDD_DEVNAME); if (ret != 0) { printk(KERN_ERR "NVRAM, alloc_chrdev_region failed !!\n"); return ret; } nv_cdev = cdev_alloc(); if (!nv_cdev) { printk(KERN_ERR "NVRAM, cdev_alloc failed !\n"); goto fail1; } nv_cdev->owner = THIS_MODULE; nv_cdev->ops = &nvram_fops; if ( cdev_add(nv_cdev, nvram_dev, 1) < 0) { printk(KERN_ERR "NVRAM, cdev_add failed \n"); goto fail2; } /* look for NVRAM */ ret = pci_register_driver(&nvram_pci_driver); if (!ret) return ret; fail2: cdev_del(nv_cdev); fail1: unregister_chrdev_region(nvram_dev, 1); return ret; } /* * nvram_cleanup */ static void __exit nvram_cleanup(void) { pci_unregister_driver(&nvram_pci_driver); cdev_del(nv_cdev); unregister_chrdev_region(nvram_dev, 1); } module_init(nvram_init); module_exit(nvram_cleanup); MODULE_AUTHOR("NetApp, "); MODULE_DESCRIPTION("NVRAM Hardware Diagnostics Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION);