diff options
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 13 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/enclosure.c | 73 | ||||
-rw-r--r-- | drivers/misc/ep93xx_pwm.c | 384 | ||||
-rw-r--r-- | drivers/misc/hpilo.c | 290 | ||||
-rw-r--r-- | drivers/misc/hpilo.h | 8 | ||||
-rw-r--r-- | drivers/misc/sgi-xp/xpnet.c | 5 |
7 files changed, 661 insertions, 113 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 68ab39d7cb35..df1f86b5c83e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -233,6 +233,19 @@ config ISL29003 This driver can also be built as a module. If so, the module will be called isl29003. +config EP93XX_PWM + tristate "EP93xx PWM support" + depends on ARCH_EP93XX + help + This option enables device driver support for the PWM channels + on the Cirrus EP93xx processors. The EP9307 chip only has one + PWM channel all the others have two, the second channel is an + alternate function of the EGPIO14 pin. A sysfs interface is + provided to control the PWM channels. + + To compile this driver as a module, choose M here: the module will + be called ep93xx_pwm. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 36f733cd60e6..f982d2ecfde7 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_SGI_XP) += sgi-xp/ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_HP_ILO) += hpilo.o obj-$(CONFIG_ISL29003) += isl29003.o +obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o obj-$(CONFIG_C2PORT) += c2port/ obj-y += eeprom/ obj-y += cb710/ diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 348443bdb23b..e9eae4a78402 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -33,24 +33,44 @@ static DEFINE_MUTEX(container_list_lock); static struct class enclosure_class; /** - * enclosure_find - find an enclosure given a device - * @dev: the device to find for + * enclosure_find - find an enclosure given a parent device + * @dev: the parent to match against + * @start: Optional enclosure device to start from (NULL if none) * - * Looks through the list of registered enclosures to see - * if it can find a match for a device. Returns NULL if no - * enclosure is found. Obtains a reference to the enclosure class - * device which must be released with device_put(). + * Looks through the list of registered enclosures to find all those + * with @dev as a parent. Returns NULL if no enclosure is + * found. @start can be used as a starting point to obtain multiple + * enclosures per parent (should begin with NULL and then be set to + * each returned enclosure device). Obtains a reference to the + * enclosure class device which must be released with device_put(). + * If @start is not NULL, a reference must be taken on it which is + * released before returning (this allows a loop through all + * enclosures to exit with only the reference on the enclosure of + * interest held). Note that the @dev may correspond to the actual + * device housing the enclosure, in which case no iteration via @start + * is required. */ -struct enclosure_device *enclosure_find(struct device *dev) +struct enclosure_device *enclosure_find(struct device *dev, + struct enclosure_device *start) { struct enclosure_device *edev; mutex_lock(&container_list_lock); - list_for_each_entry(edev, &container_list, node) { - if (edev->edev.parent == dev) { - get_device(&edev->edev); - mutex_unlock(&container_list_lock); - return edev; + edev = list_prepare_entry(start, &container_list, node); + if (start) + put_device(&start->edev); + + list_for_each_entry_continue(edev, &container_list, node) { + struct device *parent = edev->edev.parent; + /* parent might not be immediate, so iterate up to + * the root of the tree if necessary */ + while (parent) { + if (parent == dev) { + get_device(&edev->edev); + mutex_unlock(&container_list_lock); + return edev; + } + parent = parent->parent; } } mutex_unlock(&container_list_lock); @@ -218,7 +238,7 @@ static void enclosure_component_release(struct device *dev) put_device(dev->parent); } -static struct attribute_group *enclosure_groups[]; +static const struct attribute_group *enclosure_groups[]; /** * enclosure_component_register - add a particular component to an enclosure @@ -295,6 +315,9 @@ int enclosure_add_device(struct enclosure_device *edev, int component, cdev = &edev->component[component]; + if (cdev->dev == dev) + return -EEXIST; + if (cdev->dev) enclosure_remove_links(cdev); @@ -312,19 +335,25 @@ EXPORT_SYMBOL_GPL(enclosure_add_device); * Returns zero on success or an error. * */ -int enclosure_remove_device(struct enclosure_device *edev, int component) +int enclosure_remove_device(struct enclosure_device *edev, struct device *dev) { struct enclosure_component *cdev; + int i; - if (!edev || component >= edev->components) + if (!edev || !dev) return -EINVAL; - cdev = &edev->component[component]; - - device_del(&cdev->cdev); - put_device(cdev->dev); - cdev->dev = NULL; - return device_add(&cdev->cdev); + for (i = 0; i < edev->components; i++) { + cdev = &edev->component[i]; + if (cdev->dev == dev) { + enclosure_remove_links(cdev); + device_del(&cdev->cdev); + put_device(dev); + cdev->dev = NULL; + return device_add(&cdev->cdev); + } + } + return -ENODEV; } EXPORT_SYMBOL_GPL(enclosure_remove_device); @@ -507,7 +536,7 @@ static struct attribute_group enclosure_group = { .attrs = enclosure_component_attrs, }; -static struct attribute_group *enclosure_groups[] = { +static const struct attribute_group *enclosure_groups[] = { &enclosure_group, NULL }; diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c new file mode 100644 index 000000000000..ba4694169d79 --- /dev/null +++ b/drivers/misc/ep93xx_pwm.c @@ -0,0 +1,384 @@ +/* + * Simple PWM driver for EP93XX + * + * (c) Copyright 2009 Matthieu Crapet <mcrapet@gmail.com> + * (c) Copyright 2009 H Hartley Sweeten <hsweeten@visionengravers.com> + * + * 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. + * + * EP9307 has only one channel: + * - PWMOUT + * + * EP9301/02/12/15 have two channels: + * - PWMOUT + * - PWMOUT1 (alternate function for EGPIO14) + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> + +#include <mach/platform.h> + +#define EP93XX_PWMx_TERM_COUNT 0x00 +#define EP93XX_PWMx_DUTY_CYCLE 0x04 +#define EP93XX_PWMx_ENABLE 0x08 +#define EP93XX_PWMx_INVERT 0x0C + +#define EP93XX_PWM_MAX_COUNT 0xFFFF + +struct ep93xx_pwm { + void __iomem *mmio_base; + struct clk *clk; + u32 duty_percent; +}; + +static inline void ep93xx_pwm_writel(struct ep93xx_pwm *pwm, + unsigned int val, unsigned int off) +{ + __raw_writel(val, pwm->mmio_base + off); +} + +static inline unsigned int ep93xx_pwm_readl(struct ep93xx_pwm *pwm, + unsigned int off) +{ + return __raw_readl(pwm->mmio_base + off); +} + +static inline void ep93xx_pwm_write_tc(struct ep93xx_pwm *pwm, u16 value) +{ + ep93xx_pwm_writel(pwm, value, EP93XX_PWMx_TERM_COUNT); +} + +static inline u16 ep93xx_pwm_read_tc(struct ep93xx_pwm *pwm) +{ + return ep93xx_pwm_readl(pwm, EP93XX_PWMx_TERM_COUNT); +} + +static inline void ep93xx_pwm_write_dc(struct ep93xx_pwm *pwm, u16 value) +{ + ep93xx_pwm_writel(pwm, value, EP93XX_PWMx_DUTY_CYCLE); +} + +static inline void ep93xx_pwm_enable(struct ep93xx_pwm *pwm) +{ + ep93xx_pwm_writel(pwm, 0x1, EP93XX_PWMx_ENABLE); +} + +static inline void ep93xx_pwm_disable(struct ep93xx_pwm *pwm) +{ + ep93xx_pwm_writel(pwm, 0x0, EP93XX_PWMx_ENABLE); +} + +static inline int ep93xx_pwm_is_enabled(struct ep93xx_pwm *pwm) +{ + return ep93xx_pwm_readl(pwm, EP93XX_PWMx_ENABLE) & 0x1; +} + +static inline void ep93xx_pwm_invert(struct ep93xx_pwm *pwm) +{ + ep93xx_pwm_writel(pwm, 0x1, EP93XX_PWMx_INVERT); +} + +static inline void ep93xx_pwm_normal(struct ep93xx_pwm *pwm) +{ + ep93xx_pwm_writel(pwm, 0x0, EP93XX_PWMx_INVERT); +} + +static inline int ep93xx_pwm_is_inverted(struct ep93xx_pwm *pwm) +{ + return ep93xx_pwm_readl(pwm, EP93XX_PWMx_INVERT) & 0x1; +} + +/* + * /sys/devices/platform/ep93xx-pwm.N + * /min_freq read-only minimum pwm output frequency + * /max_req read-only maximum pwm output frequency + * /freq read-write pwm output frequency (0 = disable output) + * /duty_percent read-write pwm duty cycle percent (1..99) + * /invert read-write invert pwm output + */ + +static ssize_t ep93xx_pwm_get_min_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + unsigned long rate = clk_get_rate(pwm->clk); + + return sprintf(buf, "%ld\n", rate / (EP93XX_PWM_MAX_COUNT + 1)); +} + +static ssize_t ep93xx_pwm_get_max_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + unsigned long rate = clk_get_rate(pwm->clk); + + return sprintf(buf, "%ld\n", rate / 2); +} + +static ssize_t ep93xx_pwm_get_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + + if (ep93xx_pwm_is_enabled(pwm)) { + unsigned long rate = clk_get_rate(pwm->clk); + u16 term = ep93xx_pwm_read_tc(pwm); + + return sprintf(buf, "%ld\n", rate / (term + 1)); + } else { + return sprintf(buf, "disabled\n"); + } +} + +static ssize_t ep93xx_pwm_set_freq(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + long val; + int err; + + err = strict_strtol(buf, 10, &val); + if (err) + return -EINVAL; + + if (val == 0) { + ep93xx_pwm_disable(pwm); + } else if (val <= (clk_get_rate(pwm->clk) / 2)) { + u32 term, duty; + + val = (clk_get_rate(pwm->clk) / val) - 1; + if (val > EP93XX_PWM_MAX_COUNT) + val = EP93XX_PWM_MAX_COUNT; + if (val < 1) + val = 1; + + term = ep93xx_pwm_read_tc(pwm); + duty = ((val + 1) * pwm->duty_percent / 100) - 1; + + /* If pwm is running, order is important */ + if (val > term) { + ep93xx_pwm_write_tc(pwm, val); + ep93xx_pwm_write_dc(pwm, duty); + } else { + ep93xx_pwm_write_dc(pwm, duty); + ep93xx_pwm_write_tc(pwm, val); + } + + if (!ep93xx_pwm_is_enabled(pwm)) + ep93xx_pwm_enable(pwm); + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t ep93xx_pwm_get_duty_percent(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", pwm->duty_percent); +} + +static ssize_t ep93xx_pwm_set_duty_percent(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + long val; + int err; + + err = strict_strtol(buf, 10, &val); + if (err) + return -EINVAL; + + if (val > 0 && val < 100) { + u32 term = ep93xx_pwm_read_tc(pwm); + ep93xx_pwm_write_dc(pwm, ((term + 1) * val / 100) - 1); + pwm->duty_percent = val; + return count; + } + + return -EINVAL; +} + +static ssize_t ep93xx_pwm_get_invert(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", ep93xx_pwm_is_inverted(pwm)); +} + +static ssize_t ep93xx_pwm_set_invert(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + long val; + int err; + + err = strict_strtol(buf, 10, &val); + if (err) + return -EINVAL; + + if (val == 0) + ep93xx_pwm_normal(pwm); + else if (val == 1) + ep93xx_pwm_invert(pwm); + else + return -EINVAL; + + return count; +} + +static DEVICE_ATTR(min_freq, S_IRUGO, ep93xx_pwm_get_min_freq, NULL); +static DEVICE_ATTR(max_freq, S_IRUGO, ep93xx_pwm_get_max_freq, NULL); +static DEVICE_ATTR(freq, S_IWUGO | S_IRUGO, + ep93xx_pwm_get_freq, ep93xx_pwm_set_freq); +static DEVICE_ATTR(duty_percent, S_IWUGO | S_IRUGO, + ep93xx_pwm_get_duty_percent, ep93xx_pwm_set_duty_percent); +static DEVICE_ATTR(invert, S_IWUGO | S_IRUGO, + ep93xx_pwm_get_invert, ep93xx_pwm_set_invert); + +static struct attribute *ep93xx_pwm_attrs[] = { + &dev_attr_min_freq.attr, + &dev_attr_max_freq.attr, + &dev_attr_freq.attr, + &dev_attr_duty_percent.attr, + &dev_attr_invert.attr, + NULL +}; + +static const struct attribute_group ep93xx_pwm_sysfs_files = { + .attrs = ep93xx_pwm_attrs, +}; + +static int __init ep93xx_pwm_probe(struct platform_device *pdev) +{ + struct ep93xx_pwm *pwm; + struct resource *res; + int err; + + err = ep93xx_pwm_acquire_gpio(pdev); + if (err) + return err; + + pwm = kzalloc(sizeof(struct ep93xx_pwm), GFP_KERNEL); + if (!pwm) { + err = -ENOMEM; + goto fail_no_mem; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + err = -ENXIO; + goto fail_no_mem_resource; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (res == NULL) { + err = -EBUSY; + goto fail_no_mem_resource; + } + + pwm->mmio_base = ioremap(res->start, resource_size(res)); + if (pwm->mmio_base == NULL) { + err = -ENXIO; + goto fail_no_ioremap; + } + + err = sysfs_create_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); + if (err) + goto fail_no_sysfs; + + pwm->clk = clk_get(&pdev->dev, "pwm_clk"); + if (IS_ERR(pwm->clk)) { + err = PTR_ERR(pwm->clk); + goto fail_no_clk; + } + + pwm->duty_percent = 50; + + platform_set_drvdata(pdev, pwm); + + /* disable pwm at startup. Avoids zero value. */ + ep93xx_pwm_disable(pwm); + ep93xx_pwm_write_tc(pwm, EP93XX_PWM_MAX_COUNT); + ep93xx_pwm_write_dc(pwm, EP93XX_PWM_MAX_COUNT / 2); + + clk_enable(pwm->clk); + + return 0; + +fail_no_clk: + sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); +fail_no_sysfs: + iounmap(pwm->mmio_base); +fail_no_ioremap: + release_mem_region(res->start, resource_size(res)); +fail_no_mem_resource: + kfree(pwm); +fail_no_mem: + ep93xx_pwm_release_gpio(pdev); + return err; +} + +static int __exit ep93xx_pwm_remove(struct platform_device *pdev) +{ + struct ep93xx_pwm *pwm = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + ep93xx_pwm_disable(pwm); + clk_disable(pwm->clk); + clk_put(pwm->clk); + platform_set_drvdata(pdev, NULL); + sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files); + iounmap(pwm->mmio_base); + release_mem_region(res->start, resource_size(res)); + kfree(pwm); + ep93xx_pwm_release_gpio(pdev); + + return 0; +} + +static struct platform_driver ep93xx_pwm_driver = { + .driver = { + .name = "ep93xx-pwm", + .owner = THIS_MODULE, + }, + .remove = __exit_p(ep93xx_pwm_remove), +}; + +static int __init ep93xx_pwm_init(void) +{ + return platform_driver_probe(&ep93xx_pwm_driver, ep93xx_pwm_probe); +} + +static void __exit ep93xx_pwm_exit(void) +{ + platform_driver_unregister(&ep93xx_pwm_driver); +} + +module_init(ep93xx_pwm_init); +module_exit(ep93xx_pwm_exit); + +MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, " + "H Hartley Sweeten <hsweeten@visionengravers.com>"); +MODULE_DESCRIPTION("EP93xx PWM driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ep93xx-pwm"); diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 880ccf39e23b..1ad27c6abcca 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/fs.h> #include <linux/pci.h> +#include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/device.h> #include <linux/file.h> @@ -21,6 +22,8 @@ #include <linux/delay.h> #include <linux/uaccess.h> #include <linux/io.h> +#include <linux/wait.h> +#include <linux/poll.h> #include "hpilo.h" static struct class *ilo_class; @@ -61,9 +64,10 @@ static inline int desc_mem_sz(int nr_entry) static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) { struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); + unsigned long flags; int ret = 0; - spin_lock(&hw->fifo_lock); + spin_lock_irqsave(&hw->fifo_lock, flags); if (!(fifo_q->fifobar[(fifo_q->tail + 1) & fifo_q->imask] & ENTRY_MASK_O)) { fifo_q->fifobar[fifo_q->tail & fifo_q->imask] |= @@ -71,7 +75,7 @@ static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) fifo_q->tail += 1; ret = 1; } - spin_unlock(&hw->fifo_lock); + spin_unlock_irqrestore(&hw->fifo_lock, flags); return ret; } @@ -79,10 +83,11 @@ static int fifo_enqueue(struct ilo_hwinfo *hw, char *fifobar, int entry) static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry) { struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); + unsigned long flags; int ret = 0; u64 c; - spin_lock(&hw->fifo_lock); + spin_lock_irqsave(&hw->fifo_lock, flags); c = fifo_q->fifobar[fifo_q->head & fifo_q->imask]; if (c & ENTRY_MASK_C) { if (entry) @@ -93,7 +98,23 @@ static int fifo_dequeue(struct ilo_hwinfo *hw, char *fifobar, int *entry) fifo_q->head += 1; ret = 1; } - spin_unlock(&hw->fifo_lock); + spin_unlock_irqrestore(&hw->fifo_lock, flags); + + return ret; +} + +static int fifo_check_recv(struct ilo_hwinfo *hw, char *fifobar) +{ + struct fifo *fifo_q = FIFOBARTOHANDLE(fifobar); + unsigned long flags; + int ret = 0; + u64 c; + + spin_lock_irqsave(&hw->fifo_lock, flags); + c = fifo_q->fifobar[fifo_q->head & fifo_q->imask]; + if (c & ENTRY_MASK_C) + ret = 1; + spin_unlock_irqrestore(&hw->fifo_lock, flags); return ret; } @@ -142,6 +163,13 @@ static int ilo_pkt_dequeue(struct ilo_hwinfo *hw, struct ccb *ccb, return ret; } +static int ilo_pkt_recv(struct ilo_hwinfo *hw, struct ccb *ccb) +{ + char *fifobar = ccb->ccb_u3.recv_fifobar; + + return fifo_check_recv(hw, fifobar); +} + static inline void doorbell_set(struct ccb *ccb) { iowrite8(1, ccb->ccb_u5.db_base); @@ -151,6 +179,7 @@ static inline void doorbell_clr(struct ccb *ccb) { iowrite8(2, ccb->ccb_u5.db_base); } + static inline int ctrl_set(int l2sz, int idxmask, int desclim) { int active = 0, go = 1; @@ -160,6 +189,7 @@ static inline int ctrl_set(int l2sz, int idxmask, int desclim) active << CTRL_BITPOS_A | go << CTRL_BITPOS_G; } + static void ctrl_setup(struct ccb *ccb, int nr_desc, int l2desc_sz) { /* for simplicity, use the same parameters for send and recv ctrls */ @@ -192,13 +222,10 @@ static void fifo_setup(void *base_addr, int nr_entry) static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data) { - struct ccb *driver_ccb; - struct ccb __iomem *device_ccb; + struct ccb *driver_ccb = &data->driver_ccb; + struct ccb __iomem *device_ccb = data->mapped_ccb; int retries; - driver_ccb = &data->driver_ccb; - device_ccb = data->mapped_ccb; - /* complicated dance to tell the hw we are stopping */ doorbell_clr(driver_ccb); iowrite32(ioread32(&device_ccb->send_ctrl) & ~(1 << CTRL_BITPOS_G), @@ -225,26 +252,22 @@ static void ilo_ccb_close(struct pci_dev *pdev, struct ccb_data *data) pci_free_consistent(pdev, data->dma_size, data->dma_va, data->dma_pa); } -static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) +static int ilo_ccb_setup(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) { char *dma_va, *dma_pa; - int pkt_id, pkt_sz, i, error; struct ccb *driver_ccb, *ilo_ccb; - struct pci_dev *pdev; driver_ccb = &data->driver_ccb; ilo_ccb = &data->ilo_ccb; - pdev = hw->ilo_dev; data->dma_size = 2 * fifo_sz(NR_QENTRY) + 2 * desc_mem_sz(NR_QENTRY) + ILO_START_ALIGN + ILO_CACHE_SZ; - error = -ENOMEM; - data->dma_va = pci_alloc_consistent(pdev, data->dma_size, + data->dma_va = pci_alloc_consistent(hw->ilo_dev, data->dma_size, &data->dma_pa); if (!data->dma_va) - goto out; + return -ENOMEM; dma_va = (char *)data->dma_va; dma_pa = (char *)data->dma_pa; @@ -290,10 +313,18 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) driver_ccb->ccb_u5.db_base = hw->db_vaddr + (slot << L2_DB_SIZE); ilo_ccb->ccb_u5.db_base = NULL; /* hw ccb's doorbell is not used */ + return 0; +} + +static void ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) +{ + int pkt_id, pkt_sz; + struct ccb *driver_ccb = &data->driver_ccb; + /* copy the ccb with physical addrs to device memory */ data->mapped_ccb = (struct ccb __iomem *) (hw->ram_vaddr + (slot * ILOHW_CCB_SZ)); - memcpy_toio(data->mapped_ccb, ilo_ccb, sizeof(struct ccb)); + memcpy_toio(data->mapped_ccb, &data->ilo_ccb, sizeof(struct ccb)); /* put packets on the send and receive queues */ pkt_sz = 0; @@ -306,7 +337,14 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) for (pkt_id = 0; pkt_id < NR_QENTRY; pkt_id++) ilo_pkt_enqueue(hw, driver_ccb, RECVQ, pkt_id, pkt_sz); + /* the ccb is ready to use */ doorbell_clr(driver_ccb); +} + +static int ilo_ccb_verify(struct ilo_hwinfo *hw, struct ccb_data *data) +{ + int pkt_id, i; + struct ccb *driver_ccb = &data->driver_ccb; /* make sure iLO is really handling requests */ for (i = MAX_WAIT; i > 0; i--) { @@ -315,20 +353,14 @@ static int ilo_ccb_open(struct ilo_hwinfo *hw, struct ccb_data *data, int slot) udelay(WAIT_TIME); } - if (i) { - ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0); - doorbell_set(driver_ccb); - } else { - dev_err(&pdev->dev, "Open could not dequeue a packet\n"); - error = -EBUSY; - goto free; + if (i == 0) { + dev_err(&hw->ilo_dev->dev, "Open could not dequeue a packet\n"); + return -EBUSY; } + ilo_pkt_enqueue(hw, driver_ccb, SENDQ, pkt_id, 0); + doorbell_set(driver_ccb); return 0; -free: - ilo_ccb_close(pdev, data); -out: - return error; } static inline int is_channel_reset(struct ccb *ccb) @@ -343,19 +375,45 @@ static inline void set_channel_reset(struct ccb *ccb) FIFOBARTOHANDLE(ccb->ccb_u1.send_fifobar)->reset = 1; } +static inline int get_device_outbound(struct ilo_hwinfo *hw) +{ + return ioread32(&hw->mmio_vaddr[DB_OUT]); +} + +static inline int is_db_reset(int db_out) +{ + return db_out & (1 << DB_RESET); +} + static inline int is_device_reset(struct ilo_hwinfo *hw) { /* check for global reset condition */ - return ioread32(&hw->mmio_vaddr[DB_OUT]) & (1 << DB_RESET); + return is_db_reset(get_device_outbound(hw)); +} + +static inline void clear_pending_db(struct ilo_hwinfo *hw, int clr) +{ + iowrite32(clr, &hw->mmio_vaddr[DB_OUT]); } static inline void clear_device(struct ilo_hwinfo *hw) { /* clear the device (reset bits, pending channel entries) */ - iowrite32(-1, &hw->mmio_vaddr[DB_OUT]); + clear_pending_db(hw, -1); +} + +static inline void ilo_enable_interrupts(struct ilo_hwinfo *hw) +{ + iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) | 1, &hw->mmio_vaddr[DB_IRQ]); } -static void ilo_locked_reset(struct ilo_hwinfo *hw) +static inline void ilo_disable_interrupts(struct ilo_hwinfo *hw) +{ + iowrite8(ioread8(&hw->mmio_vaddr[DB_IRQ]) & ~1, + &hw->mmio_vaddr[DB_IRQ]); +} + +static void ilo_set_reset(struct ilo_hwinfo *hw) { int slot; @@ -368,40 +426,22 @@ static void ilo_locked_reset(struct ilo_hwinfo *hw) continue; set_channel_reset(&hw->ccb_alloc[slot]->driver_ccb); } - - clear_device(hw); -} - -static void ilo_reset(struct ilo_hwinfo *hw) -{ - spin_lock(&hw->alloc_lock); - - /* reset might have been handled after lock was taken */ - if (is_device_reset(hw)) - ilo_locked_reset(hw); - - spin_unlock(&hw->alloc_lock); } static ssize_t ilo_read(struct file *fp, char __user *buf, size_t len, loff_t *off) { int err, found, cnt, pkt_id, pkt_len; - struct ccb_data *data; - struct ccb *driver_ccb; - struct ilo_hwinfo *hw; + struct ccb_data *data = fp->private_data; + struct ccb *driver_ccb = &data->driver_ccb; + struct ilo_hwinfo *hw = data->ilo_hw; void *pkt; - data = fp->private_data; - driver_ccb = &data->driver_ccb; - hw = data->ilo_hw; - - if (is_device_reset(hw) || is_channel_reset(driver_ccb)) { + if (is_channel_reset(driver_ccb)) { /* * If the device has been reset, applications * need to close and reopen all ccbs. */ - ilo_reset(hw); return -ENODEV; } @@ -442,23 +482,13 @@ static ssize_t ilo_write(struct file *fp, const char __user *buf, size_t len, loff_t *off) { int err, pkt_id, pkt_len; - struct ccb_data *data; - struct ccb *driver_ccb; - struct ilo_hwinfo *hw; + struct ccb_data *data = fp->private_data; + struct ccb *driver_ccb = &data->driver_ccb; + struct ilo_hwinfo *hw = data->ilo_hw; void *pkt; - data = fp->private_data; - driver_ccb = &data->driver_ccb; - hw = data->ilo_hw; - - if (is_device_reset(hw) || is_channel_reset(driver_ccb)) { - /* - * If the device has been reset, applications - * need to close and reopen all ccbs. - */ - ilo_reset(hw); + if (is_channel_reset(driver_ccb)) return -ENODEV; - } /* get a packet to send the user command */ if (!ilo_pkt_dequeue(hw, driver_ccb, SENDQ, &pkt_id, &pkt_len, &pkt)) @@ -480,32 +510,48 @@ static ssize_t ilo_write(struct file *fp, const char __user *buf, return err ? -EFAULT : len; } +static unsigned int ilo_poll(struct file *fp, poll_table *wait) +{ + struct ccb_data *data = fp->private_data; + struct ccb *driver_ccb = &data->driver_ccb; + + poll_wait(fp, &data->ccb_waitq, wait); + + if (is_channel_reset(driver_ccb)) + return POLLERR; + else if (ilo_pkt_recv(data->ilo_hw, driver_ccb)) + return POLLIN | POLLRDNORM; + + return 0; +} + static int ilo_close(struct inode *ip, struct file *fp) { int slot; struct ccb_data *data; struct ilo_hwinfo *hw; + unsigned long flags; slot = iminor(ip) % MAX_CCB; hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); - spin_lock(&hw->alloc_lock); - - if (is_device_reset(hw)) - ilo_locked_reset(hw); + spin_lock(&hw->open_lock); if (hw->ccb_alloc[slot]->ccb_cnt == 1) { data = fp->private_data; + spin_lock_irqsave(&hw->alloc_lock, flags); + hw->ccb_alloc[slot] = NULL; + spin_unlock_irqrestore(&hw->alloc_lock, flags); + ilo_ccb_close(hw->ilo_dev, data); kfree(data); - hw->ccb_alloc[slot] = NULL; } else hw->ccb_alloc[slot]->ccb_cnt--; - spin_unlock(&hw->alloc_lock); + spin_unlock(&hw->open_lock); return 0; } @@ -515,6 +561,7 @@ static int ilo_open(struct inode *ip, struct file *fp) int slot, error; struct ccb_data *data; struct ilo_hwinfo *hw; + unsigned long flags; slot = iminor(ip) % MAX_CCB; hw = container_of(ip->i_cdev, struct ilo_hwinfo, cdev); @@ -524,22 +571,42 @@ static int ilo_open(struct inode *ip, struct file *fp) if (!data) return -ENOMEM; - spin_lock(&hw->alloc_lock); - - if (is_device_reset(hw)) - ilo_locked_reset(hw); + spin_lock(&hw->open_lock); /* each fd private_data holds sw/hw view of ccb */ if (hw->ccb_alloc[slot] == NULL) { /* create a channel control block for this minor */ - error = ilo_ccb_open(hw, data, slot); - if (!error) { - hw->ccb_alloc[slot] = data; - hw->ccb_alloc[slot]->ccb_cnt = 1; - hw->ccb_alloc[slot]->ccb_excl = fp->f_flags & O_EXCL; - hw->ccb_alloc[slot]->ilo_hw = hw; - } else + error = ilo_ccb_setup(hw, data, slot); + if (error) { kfree(data); + goto out; + } + + data->ccb_cnt = 1; + data->ccb_excl = fp->f_flags & O_EXCL; + data->ilo_hw = hw; + init_waitqueue_head(&data->ccb_waitq); + + /* write the ccb to hw */ + spin_lock_irqsave(&hw->alloc_lock, flags); + ilo_ccb_open(hw, data, slot); + hw->ccb_alloc[slot] = data; + spin_unlock_irqrestore(&hw->alloc_lock, flags); + + /* make sure the channel is functional */ + error = ilo_ccb_verify(hw, data); + if (error) { + + spin_lock_irqsave(&hw->alloc_lock, flags); + hw->ccb_alloc[slot] = NULL; + spin_unlock_irqrestore(&hw->alloc_lock, flags); + + ilo_ccb_close(hw->ilo_dev, data); + + kfree(data); + goto out; + } + } else { kfree(data); if (fp->f_flags & O_EXCL || hw->ccb_alloc[slot]->ccb_excl) { @@ -554,7 +621,8 @@ static int ilo_open(struct inode *ip, struct file *fp) error = 0; } } - spin_unlock(&hw->alloc_lock); +out: + spin_unlock(&hw->open_lock); if (!error) fp->private_data = hw->ccb_alloc[slot]; @@ -566,10 +634,46 @@ static const struct file_operations ilo_fops = { .owner = THIS_MODULE, .read = ilo_read, .write = ilo_write, + .poll = ilo_poll, .open = ilo_open, .release = ilo_close, }; +static irqreturn_t ilo_isr(int irq, void *data) +{ + struct ilo_hwinfo *hw = data; + int pending, i; + + spin_lock(&hw->alloc_lock); + + /* check for ccbs which have data */ + pending = get_device_outbound(hw); + if (!pending) { + spin_unlock(&hw->alloc_lock); + return IRQ_NONE; + } + + if (is_db_reset(pending)) { + /* wake up all ccbs if the device was reset */ + pending = -1; + ilo_set_reset(hw); + } + + for (i = 0; i < MAX_CCB; i++) { + if (!hw->ccb_alloc[i]) + continue; + if (pending & (1 << i)) + wake_up_interruptible(&hw->ccb_alloc[i]->ccb_waitq); + } + + /* clear the device of the channels that have been handled */ + clear_pending_db(hw, pending); + + spin_unlock(&hw->alloc_lock); + + return IRQ_HANDLED; +} + static void ilo_unmap_device(struct pci_dev *pdev, struct ilo_hwinfo *hw) { pci_iounmap(pdev, hw->db_vaddr); @@ -623,6 +727,8 @@ static void ilo_remove(struct pci_dev *pdev) device_destroy(ilo_class, MKDEV(ilo_major, i)); cdev_del(&ilo_hw->cdev); + ilo_disable_interrupts(ilo_hw); + free_irq(pdev->irq, ilo_hw); ilo_unmap_device(pdev, ilo_hw); pci_release_regions(pdev); pci_disable_device(pdev); @@ -658,6 +764,7 @@ static int __devinit ilo_probe(struct pci_dev *pdev, ilo_hw->ilo_dev = pdev; spin_lock_init(&ilo_hw->alloc_lock); spin_lock_init(&ilo_hw->fifo_lock); + spin_lock_init(&ilo_hw->open_lock); error = pci_enable_device(pdev); if (error) @@ -676,13 +783,19 @@ static int __devinit ilo_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, ilo_hw); clear_device(ilo_hw); + error = request_irq(pdev->irq, ilo_isr, IRQF_SHARED, "hpilo", ilo_hw); + if (error) + goto unmap; + + ilo_enable_interrupts(ilo_hw); + cdev_init(&ilo_hw->cdev, &ilo_fops); ilo_hw->cdev.owner = THIS_MODULE; start = devnum * MAX_CCB; error = cdev_add(&ilo_hw->cdev, MKDEV(ilo_major, start), MAX_CCB); if (error) { dev_err(&pdev->dev, "Could not add cdev\n"); - goto unmap; + goto remove_isr; } for (minor = 0 ; minor < MAX_CCB; minor++) { @@ -695,6 +808,9 @@ static int __devinit ilo_probe(struct pci_dev *pdev, } return 0; +remove_isr: + ilo_disable_interrupts(ilo_hw); + free_irq(pdev->irq, ilo_hw); unmap: ilo_unmap_device(pdev, ilo_hw); free_regions: @@ -759,7 +875,7 @@ static void __exit ilo_exit(void) class_destroy(ilo_class); } -MODULE_VERSION("1.1"); +MODULE_VERSION("1.2"); MODULE_ALIAS(ILO_NAME); MODULE_DESCRIPTION(ILO_NAME); MODULE_AUTHOR("David Altobelli <david.altobelli@hp.com>"); diff --git a/drivers/misc/hpilo.h b/drivers/misc/hpilo.h index 03a14c82aad9..38576050776a 100644 --- a/drivers/misc/hpilo.h +++ b/drivers/misc/hpilo.h @@ -46,11 +46,14 @@ struct ilo_hwinfo { spinlock_t alloc_lock; spinlock_t fifo_lock; + spinlock_t open_lock; struct cdev cdev; }; -/* offset from mmio_vaddr */ +/* offset from mmio_vaddr for enabling doorbell interrupts */ +#define DB_IRQ 0xB2 +/* offset from mmio_vaddr for outbound communications */ #define DB_OUT 0xD4 /* DB_OUT reset bit */ #define DB_RESET 26 @@ -131,6 +134,9 @@ struct ccb_data { /* pointer to hardware device info */ struct ilo_hwinfo *ilo_hw; + /* queue for this ccb to wait for recv data */ + wait_queue_head_t ccb_waitq; + /* usage count, to allow for shared ccb's */ int ccb_cnt; diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c index 5d778ec8cdb2..16f0abda1423 100644 --- a/drivers/misc/sgi-xp/xpnet.c +++ b/drivers/misc/sgi-xp/xpnet.c @@ -240,7 +240,6 @@ xpnet_receive(short partid, int channel, struct xpnet_message *msg) (void *)skb->head, (void *)skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), skb->len); - xpnet_device->last_rx = jiffies; xpnet_device->stats.rx_packets++; xpnet_device->stats.rx_bytes += skb->len + ETH_HLEN; @@ -436,7 +435,7 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if (skb->data[0] == 0x33) { dev_kfree_skb(skb); - return 0; /* nothing needed to be done */ + return NETDEV_TX_OK; /* nothing needed to be done */ } /* @@ -503,7 +502,7 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; - return 0; + return NETDEV_TX_OK; } /* |