diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ata/pata_octeon_cf.c | 423 | ||||
-rw-r--r-- | drivers/bcma/Kconfig | 9 | ||||
-rw-r--r-- | drivers/bcma/Makefile | 1 | ||||
-rw-r--r-- | drivers/bcma/bcma_private.h | 10 | ||||
-rw-r--r-- | drivers/bcma/driver_chipcommon.c | 81 | ||||
-rw-r--r-- | drivers/bcma/driver_gpio.c | 98 | ||||
-rw-r--r-- | drivers/bcma/main.c | 5 | ||||
-rw-r--r-- | drivers/edac/Kconfig | 33 | ||||
-rw-r--r-- | drivers/edac/Makefile | 5 | ||||
-rw-r--r-- | drivers/edac/octeon_edac-l2c.c | 208 | ||||
-rw-r--r-- | drivers/edac/octeon_edac-lmc.c | 186 | ||||
-rw-r--r-- | drivers/edac/octeon_edac-pc.c | 143 | ||||
-rw-r--r-- | drivers/edac/octeon_edac-pci.c | 111 | ||||
-rw-r--r-- | drivers/ssb/Kconfig | 9 | ||||
-rw-r--r-- | drivers/ssb/Makefile | 1 | ||||
-rw-r--r-- | drivers/ssb/driver_chipcommon.c | 78 | ||||
-rw-r--r-- | drivers/ssb/driver_extif.c | 43 | ||||
-rw-r--r-- | drivers/ssb/driver_gpio.c | 176 | ||||
-rw-r--r-- | drivers/ssb/main.c | 7 | ||||
-rw-r--r-- | drivers/ssb/ssb_private.h | 17 | ||||
-rw-r--r-- | drivers/video/console/newport_con.c | 11 |
21 files changed, 1503 insertions, 152 deletions
diff --git a/drivers/ata/pata_octeon_cf.c b/drivers/ata/pata_octeon_cf.c index 1d61d5d278f..4e1194b4c27 100644 --- a/drivers/ata/pata_octeon_cf.c +++ b/drivers/ata/pata_octeon_cf.c @@ -5,19 +5,22 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2005 - 2009 Cavium Networks + * Copyright (C) 2005 - 2012 Cavium Inc. * Copyright (C) 2008 Wind River Systems */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/libata.h> -#include <linux/irq.h> +#include <linux/hrtimer.h> #include <linux/slab.h> +#include <linux/irq.h> +#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> -#include <linux/workqueue.h> #include <scsi/scsi_host.h> +#include <asm/byteorder.h> #include <asm/octeon/octeon.h> /* @@ -34,20 +37,36 @@ */ #define DRV_NAME "pata_octeon_cf" -#define DRV_VERSION "2.1" +#define DRV_VERSION "2.2" + +/* Poll interval in nS. */ +#define OCTEON_CF_BUSY_POLL_INTERVAL 500000 +#define DMA_CFG 0 +#define DMA_TIM 0x20 +#define DMA_INT 0x38 +#define DMA_INT_EN 0x50 struct octeon_cf_port { - struct workqueue_struct *wq; - struct delayed_work delayed_finish; + struct hrtimer delayed_finish; struct ata_port *ap; int dma_finished; + void *c0; + unsigned int cs0; + unsigned int cs1; + bool is_true_ide; + u64 dma_base; }; static struct scsi_host_template octeon_cf_sht = { ATA_PIO_SHT(DRV_NAME), }; +static int enable_dma; +module_param(enable_dma, int, 0444); +MODULE_PARM_DESC(enable_dma, + "Enable use of DMA on interfaces that support it (0=no dma [default], 1=use dma)"); + /** * Convert nanosecond based time to setting used in the * boot bus timing register, based on timing multiple @@ -66,12 +85,29 @@ static unsigned int ns_to_tim_reg(unsigned int tim_mult, unsigned int nsecs) return val; } -static void octeon_cf_set_boot_reg_cfg(int cs) +static void octeon_cf_set_boot_reg_cfg(int cs, unsigned int multiplier) { union cvmx_mio_boot_reg_cfgx reg_cfg; + unsigned int tim_mult; + + switch (multiplier) { + case 8: + tim_mult = 3; + break; + case 4: + tim_mult = 0; + break; + case 2: + tim_mult = 2; + break; + default: + tim_mult = 1; + break; + } + reg_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs)); reg_cfg.s.dmack = 0; /* Don't assert DMACK on access */ - reg_cfg.s.tim_mult = 2; /* Timing mutiplier 2x */ + reg_cfg.s.tim_mult = tim_mult; /* Timing mutiplier */ reg_cfg.s.rd_dly = 0; /* Sample on falling edge of BOOT_OE */ reg_cfg.s.sam = 0; /* Don't combine write and output enable */ reg_cfg.s.we_ext = 0; /* No write enable extension */ @@ -92,12 +128,12 @@ static void octeon_cf_set_boot_reg_cfg(int cs) */ static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev) { - struct octeon_cf_data *ocd = ap->dev->platform_data; + struct octeon_cf_port *cf_port = ap->private_data; union cvmx_mio_boot_reg_timx reg_tim; - int cs = ocd->base_region; int T; struct ata_timing timing; + unsigned int div; int use_iordy; int trh; int pause; @@ -106,7 +142,15 @@ static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev) int t2; int t2i; - T = (int)(2000000000000LL / octeon_get_clock_rate()); + /* + * A divisor value of four will overflow the timing fields at + * clock rates greater than 800MHz + */ + if (octeon_get_io_clock_rate() <= 800000000) + div = 4; + else + div = 8; + T = (int)((1000000000000LL * div) / octeon_get_io_clock_rate()); if (ata_timing_compute(dev, dev->pio_mode, &timing, T, T)) BUG(); @@ -121,23 +165,26 @@ static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev) if (t2i) t2i--; - trh = ns_to_tim_reg(2, 20); + trh = ns_to_tim_reg(div, 20); if (trh) trh--; - pause = timing.cycle - timing.active - timing.setup - trh; + pause = (int)timing.cycle - (int)timing.active - + (int)timing.setup - trh; + if (pause < 0) + pause = 0; if (pause) pause--; - octeon_cf_set_boot_reg_cfg(cs); - if (ocd->dma_engine >= 0) + octeon_cf_set_boot_reg_cfg(cf_port->cs0, div); + if (cf_port->is_true_ide) /* True IDE mode, program both chip selects. */ - octeon_cf_set_boot_reg_cfg(cs + 1); + octeon_cf_set_boot_reg_cfg(cf_port->cs1, div); use_iordy = ata_pio_need_iordy(dev); - reg_tim.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_TIMX(cs)); + reg_tim.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_TIMX(cf_port->cs0)); /* Disable page mode */ reg_tim.s.pagem = 0; /* Enable dynamic timing */ @@ -161,20 +208,22 @@ static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev) /* How long read enable is asserted */ reg_tim.s.oe = t2; /* Time after CE that read/write starts */ - reg_tim.s.ce = ns_to_tim_reg(2, 5); + reg_tim.s.ce = ns_to_tim_reg(div, 5); /* Time before CE that address is valid */ reg_tim.s.adr = 0; /* Program the bootbus region timing for the data port chip select. */ - cvmx_write_csr(CVMX_MIO_BOOT_REG_TIMX(cs), reg_tim.u64); - if (ocd->dma_engine >= 0) + cvmx_write_csr(CVMX_MIO_BOOT_REG_TIMX(cf_port->cs0), reg_tim.u64); + if (cf_port->is_true_ide) /* True IDE mode, program both chip selects. */ - cvmx_write_csr(CVMX_MIO_BOOT_REG_TIMX(cs + 1), reg_tim.u64); + cvmx_write_csr(CVMX_MIO_BOOT_REG_TIMX(cf_port->cs1), + reg_tim.u64); } static void octeon_cf_set_dmamode(struct ata_port *ap, struct ata_device *dev) { - struct octeon_cf_data *ocd = dev->link->ap->dev->platform_data; + struct octeon_cf_port *cf_port = ap->private_data; + union cvmx_mio_boot_pin_defs pin_defs; union cvmx_mio_boot_dma_timx dma_tim; unsigned int oe_a; unsigned int oe_n; @@ -183,6 +232,7 @@ static void octeon_cf_set_dmamode(struct ata_port *ap, struct ata_device *dev) unsigned int pause; unsigned int T0, Tkr, Td; unsigned int tim_mult; + int c; const struct ata_timing *timing; @@ -199,13 +249,19 @@ static void octeon_cf_set_dmamode(struct ata_port *ap, struct ata_device *dev) /* not spec'ed, value in eclocks, not affected by tim_mult */ dma_arq = 8; pause = 25 - dma_arq * 1000 / - (octeon_get_clock_rate() / 1000000); /* Tz */ + (octeon_get_io_clock_rate() / 1000000); /* Tz */ oe_a = Td; /* Tkr from cf spec, lengthened to meet T0 */ oe_n = max(T0 - oe_a, Tkr); - dma_tim.s.dmack_pi = 1; + pin_defs.u64 = cvmx_read_csr(CVMX_MIO_BOOT_PIN_DEFS); + + /* DMA channel number. */ + c = (cf_port->dma_base & 8) >> 3; + + /* Invert the polarity if the default is 0*/ + dma_tim.s.dmack_pi = (pin_defs.u64 & (1ull << (11 + c))) ? 0 : 1; dma_tim.s.oe_n = ns_to_tim_reg(tim_mult, oe_n); dma_tim.s.oe_a = ns_to_tim_reg(tim_mult, oe_a); @@ -228,14 +284,11 @@ static void octeon_cf_set_dmamode(struct ata_port *ap, struct ata_device *dev) pr_debug("ns to ticks (mult %d) of %d is: %d\n", tim_mult, 60, ns_to_tim_reg(tim_mult, 60)); - pr_debug("oe_n: %d, oe_a: %d, dmack_s: %d, dmack_h: " - "%d, dmarq: %d, pause: %d\n", + pr_debug("oe_n: %d, oe_a: %d, dmack_s: %d, dmack_h: %d, dmarq: %d, pause: %d\n", dma_tim.s.oe_n, dma_tim.s.oe_a, dma_tim.s.dmack_s, dma_tim.s.dmack_h, dma_tim.s.dmarq, dma_tim.s.pause); - cvmx_write_csr(CVMX_MIO_BOOT_DMA_TIMX(ocd->dma_engine), - dma_tim.u64); - + cvmx_write_csr(cf_port->dma_base + DMA_TIM, dma_tim.u64); } /** @@ -489,15 +542,10 @@ static void octeon_cf_exec_command16(struct ata_port *ap, ata_wait_idle(ap); } -static void octeon_cf_irq_on(struct ata_port *ap) +static void octeon_cf_ata_port_noaction(struct ata_port *ap) { } -static void octeon_cf_irq_clear(struct ata_port *ap) -{ - return; -} - static void octeon_cf_dma_setup(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; @@ -519,7 +567,7 @@ static void octeon_cf_dma_setup(struct ata_queued_cmd *qc) */ static void octeon_cf_dma_start(struct ata_queued_cmd *qc) { - struct octeon_cf_data *ocd = qc->ap->dev->platform_data; + struct octeon_cf_port *cf_port = qc->ap->private_data; union cvmx_mio_boot_dma_cfgx mio_boot_dma_cfg; union cvmx_mio_boot_dma_intx mio_boot_dma_int; struct scatterlist *sg; @@ -535,15 +583,16 @@ static void octeon_cf_dma_start(struct ata_queued_cmd *qc) */ mio_boot_dma_int.u64 = 0; mio_boot_dma_int.s.done = 1; - cvmx_write_csr(CVMX_MIO_BOOT_DMA_INTX(ocd->dma_engine), - mio_boot_dma_int.u64); + cvmx_write_csr(cf_port->dma_base + DMA_INT, mio_boot_dma_int.u64); /* Enable the interrupt. */ - cvmx_write_csr(CVMX_MIO_BOOT_DMA_INT_ENX(ocd->dma_engine), - mio_boot_dma_int.u64); + cvmx_write_csr(cf_port->dma_base + DMA_INT_EN, mio_boot_dma_int.u64); /* Set the direction of the DMA */ mio_boot_dma_cfg.u64 = 0; +#ifdef __LITTLE_ENDIAN + mio_boot_dma_cfg.s.endian = 1; +#endif mio_boot_dma_cfg.s.en = 1; mio_boot_dma_cfg.s.rw = ((qc->tf.flags & ATA_TFLAG_WRITE) != 0); @@ -569,8 +618,7 @@ static void octeon_cf_dma_start(struct ata_queued_cmd *qc) (mio_boot_dma_cfg.s.rw) ? "write" : "read", sg->length, (void *)(unsigned long)mio_boot_dma_cfg.s.adr); - cvmx_write_csr(CVMX_MIO_BOOT_DMA_CFGX(ocd->dma_engine), - mio_boot_dma_cfg.u64); + cvmx_write_csr(cf_port->dma_base + DMA_CFG, mio_boot_dma_cfg.u64); } /** @@ -583,10 +631,9 @@ static unsigned int octeon_cf_dma_finished(struct ata_port *ap, struct ata_queued_cmd *qc) { struct ata_eh_info *ehi = &ap->link.eh_info; - struct octeon_cf_data *ocd = ap->dev->platform_data; + struct octeon_cf_port *cf_port = ap->private_data; union cvmx_mio_boot_dma_cfgx dma_cfg; union cvmx_mio_boot_dma_intx dma_int; - struct octeon_cf_port *cf_port; u8 status; VPRINTK("ata%u: protocol %d task_state %d\n", @@ -596,9 +643,7 @@ static unsigned int octeon_cf_dma_finished(struct ata_port *ap, if (ap->hsm_task_state != HSM_ST_LAST) return 0; - cf_port = ap->private_data; - - dma_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_DMA_CFGX(ocd->dma_engine)); + dma_cfg.u64 = cvmx_read_csr(cf_port->dma_base + DMA_CFG); if (dma_cfg.s.size != 0xfffff) { /* Error, the transfer was not complete. */ qc->err_mask |= AC_ERR_HOST_BUS; @@ -608,15 +653,15 @@ static unsigned int octeon_cf_dma_finished(struct ata_port *ap, /* Stop and clear the dma engine. */ dma_cfg.u64 = 0; dma_cfg.s.size = -1; - cvmx_write_csr(CVMX_MIO_BOOT_DMA_CFGX(ocd->dma_engine), dma_cfg.u64); + cvmx_write_csr(cf_port->dma_base + DMA_CFG, dma_cfg.u64); /* Disable the interrupt. */ dma_int.u64 = 0; - cvmx_write_csr(CVMX_MIO_BOOT_DMA_INT_ENX(ocd->dma_engine), dma_int.u64); + cvmx_write_csr(cf_port->dma_base + DMA_INT_EN, dma_int.u64); /* Clear the DMA complete status */ dma_int.s.done = 1; - cvmx_write_csr(CVMX_MIO_BOOT_DMA_INTX(ocd->dma_engine), dma_int.u64); + cvmx_write_csr(cf_port->dma_base + DMA_INT, dma_int.u64); status = ap->ops->sff_check_status(ap); @@ -649,69 +694,68 @@ static irqreturn_t octeon_cf_interrupt(int irq, void *dev_instance) struct ata_queued_cmd *qc; union cvmx_mio_boot_dma_intx dma_int; union cvmx_mio_boot_dma_cfgx dma_cfg; - struct octeon_cf_data *ocd; ap = host->ports[i]; - ocd = ap->dev->platform_data; cf_port = ap->private_data; - dma_int.u64 = - cvmx_read_csr(CVMX_MIO_BOOT_DMA_INTX(ocd->dma_engine)); - dma_cfg.u64 = - cvmx_read_csr(CVMX_MIO_BOOT_DMA_CFGX(ocd->dma_engine)); + + dma_int.u64 = cvmx_read_csr(cf_port->dma_base + DMA_INT); + dma_cfg.u64 = cvmx_read_csr(cf_port->dma_base + DMA_CFG); qc = ata_qc_from_tag(ap, ap->link.active_tag); - if (qc && !(qc->tf.flags & ATA_TFLAG_POLLING)) { - if (dma_int.s.done && !dma_cfg.s.en) { - if (!sg_is_last(qc->cursg)) { - qc->cursg = sg_next(qc->cursg); - handled = 1; - octeon_cf_dma_start(qc); - continue; - } else { - cf_port->dma_finished = 1; - } - } - if (!cf_port->dma_finished) - continue; - status = ioread8(ap->ioaddr.altstatus_addr); - if (status & (ATA_BUSY | ATA_DRQ)) { - /* - * We are busy, try to handle it - * later. This is the DMA finished - * interrupt, and it could take a - * little while for the card to be - * ready for more commands. - */ - /* Clear DMA irq. */ - dma_int.u64 = 0; - dma_int.s.done = 1; - cvmx_write_csr(CVMX_MIO_BOOT_DMA_INTX(ocd->dma_engine), - dma_int.u64); - - queue_delayed_work(cf_port->wq, - &cf_port->delayed_finish, 1); + if (!qc || (qc->tf.flags & ATA_TFLAG_POLLING)) + continue; + + if (dma_int.s.done && !dma_cfg.s.en) { + if (!sg_is_last(qc->cursg)) { + qc->cursg = sg_next(qc->cursg); handled = 1; + octeon_cf_dma_start(qc); + continue; } else { - handled |= octeon_cf_dma_finished(ap, qc); + cf_port->dma_finished = 1; } } + if (!cf_port->dma_finished) + continue; + status = ioread8(ap->ioaddr.altstatus_addr); + if (status & (ATA_BUSY | ATA_DRQ)) { + /* + * We are busy, try to handle it later. This + * is the DMA finished interrupt, and it could + * take a little while for the card to be + * ready for more commands. + */ + /* Clear DMA irq. */ + dma_int.u64 = 0; + dma_int.s.done = 1; + cvmx_write_csr(cf_port->dma_base + DMA_INT, + dma_int.u64); + hrtimer_start_range_ns(&cf_port->delayed_finish, + ns_to_ktime(OCTEON_CF_BUSY_POLL_INTERVAL), + OCTEON_CF_BUSY_POLL_INTERVAL / 5, + HRTIMER_MODE_REL); + handled = 1; + } else { + handled |= octeon_cf_dma_finished(ap, qc); + } } spin_unlock_irqrestore(&host->lock, flags); DPRINTK("EXIT\n"); return IRQ_RETVAL(handled); } -static void octeon_cf_delayed_finish(struct work_struct *work) +static enum hrtimer_restart octeon_cf_delayed_finish(struct hrtimer *hrt) { - struct octeon_cf_port *cf_port = container_of(work, + struct octeon_cf_port *cf_port = container_of(hrt, struct octeon_cf_port, - delayed_finish.work); + delayed_finish); struct ata_port *ap = cf_port->ap; struct ata_host *host = ap->host; struct ata_queued_cmd *qc; unsigned long flags; u8 status; + enum hrtimer_restart rv = HRTIMER_NORESTART; spin_lock_irqsave(&host->lock, flags); @@ -726,15 +770,17 @@ static void octeon_cf_delayed_finish(struct work_struct *work) status = ioread8(ap->ioaddr.altstatus_addr); if (status & (ATA_BUSY | ATA_DRQ)) { /* Still busy, try again. */ - queue_delayed_work(cf_port->wq, - &cf_port->delayed_finish, 1); + hrtimer_forward_now(hrt, + ns_to_ktime(OCTEON_CF_BUSY_POLL_INTERVAL)); + rv = HRTIMER_RESTART; goto out; } qc = ata_qc_from_tag(ap, ap->link.active_tag); - if (qc && !(qc->tf.flags & ATA_TFLAG_POLLING)) + if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) octeon_cf_dma_finished(ap, qc); out: spin_unlock_irqrestore(&host->lock, flags); + return rv; } static void octeon_cf_dev_config(struct ata_device *dev) @@ -786,8 +832,8 @@ static struct ata_port_operations octeon_cf_ops = { .qc_prep = ata_noop_qc_prep, .qc_issue = octeon_cf_qc_issue, .sff_dev_select = octeon_cf_dev_select, - .sff_irq_on = octeon_cf_irq_on, - .sff_irq_clear = octeon_cf_irq_clear, + .sff_irq_on = octeon_cf_ata_port_noaction, + .sff_irq_clear = octeon_cf_ata_port_noaction, .cable_detect = ata_cable_40wire, .set_piomode = octeon_cf_set_piomode, .set_dmamode = octeon_cf_set_dmamode, @@ -798,46 +844,113 @@ static int __devinit octeon_cf_probe(struct platform_device *pdev) { struct resource *res_cs0, *res_cs1; + bool is_16bit; + const __be32 *cs_num; + struct property *reg_prop; + int n_addr, n_size, reg_len; + struct device_node *node; + const void *prop; void __iomem *cs0; void __iomem *cs1 = NULL; struct ata_host *host; struct ata_port *ap; - struct octeon_cf_data *ocd; int irq = 0; irq_handler_t irq_handler = NULL; void __iomem *base; struct octeon_cf_port *cf_port; - char version[32]; + int rv = -ENOMEM; - res_cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res_cs0) + node = pdev->dev.of_node; + if (node == NULL) return -EINVAL; - ocd = pdev->dev.platform_data; + cf_port = kzalloc(sizeof(*cf_port), GFP_KERNEL); + if (!cf_port) + return -ENOMEM; - cs0 = devm_ioremap_nocache(&pdev->dev, res_cs0->start, - resource_size(res_cs0)); + cf_port->is_true_ide = (of_find_property(node, "cavium,true-ide", NULL) != NULL); - if (!cs0) - return -ENOMEM; + prop = of_get_property(node, "cavium,bus-width", NULL); + if (prop) + is_16bit = (be32_to_cpup(prop) == 16); + else + is_16bit = false; - /* Determine from availability of DMA if True IDE mode or not */ - if (ocd->dma_engine >= 0) { - res_cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res_cs1) - return -EINVAL; + n_addr = of_n_addr_cells(node); + n_size = of_n_size_cells(node); + reg_prop = of_find_property(node, "reg", ®_len); + if (!reg_prop || reg_len < sizeof(__be32)) { + rv = -EINVAL; + goto free_cf_port; + } + cs_num = reg_prop->value; + cf_port->cs0 = be32_to_cpup(cs_num); + + if (cf_port->is_true_ide) { + struct device_node *dma_node; + dma_node = of_parse_phandle(node, + "cavium,dma-engine-handle", 0); + if (dma_node) { + struct platform_device *dma_dev; + dma_dev = of_find_device_by_node(dma_node); + if (dma_dev) { + struct resource *res_dma; + int i; + res_dma = platform_get_resource(dma_dev, IORESOURCE_MEM, 0); + if (!res_dma) { + of_node_put(dma_node); + rv = -EINVAL; + goto free_cf_port; + } + cf_port->dma_base = (u64)devm_ioremap_nocache(&pdev->dev, res_dma->start, + resource_size(res_dma)); + + if (!cf_port->dma_base) { + of_node_put(dma_node); + rv = -EINVAL; + goto free_cf_port; + } + + irq_handler = octeon_cf_interrupt; + i = platform_get_irq(dma_dev, 0); + if (i > 0) + irq = i; + } + of_node_put(dma_node); + } + res_cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res_cs1) { + rv = -EINVAL; + goto free_cf_port; + } cs1 = devm_ioremap_nocache(&pdev->dev, res_cs1->start, - resource_size(res_cs1)); + res_cs1->end - res_cs1->start + 1); if (!cs1) - return -ENOMEM; + goto free_cf_port; + + if (reg_len < (n_addr + n_size + 1) * sizeof(__be32)) { + rv = -EINVAL; + goto free_cf_port; + } + cs_num += n_addr + n_size; + cf_port->cs1 = be32_to_cpup(cs_num); } - cf_port = kzalloc(sizeof(*cf_port), GFP_KERNEL); - if (!cf_port) - return -ENOMEM; + res_cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res_cs0) { + rv = -EINVAL; + goto free_cf_port; + } + + cs0 = devm_ioremap_nocache(&pdev->dev, res_cs0->start, + resource_size(res_cs0)); + + if (!cs0) + goto free_cf_port; /* allocate host */ host = ata_host_alloc(&pdev->dev, 1); @@ -846,21 +959,22 @@ static int __devinit octeon_cf_probe(struct platform_device *pdev) ap = host->ports[0]; ap->private_data = cf_port; + pdev->dev.platform_data = cf_port; cf_port->ap = ap; ap->ops = &octeon_cf_ops; ap->pio_mask = ATA_PIO6; ap->flags |= ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING; - base = cs0 + ocd->base_region_bias; - if (!ocd->is16bit) { + if (!is_16bit) { + base = cs0 + 0x800; ap->ioaddr.cmd_addr = base; ata_sff_std_ports(&ap->ioaddr); ap->ioaddr.altstatus_addr = base + 0xe; ap->ioaddr.ctl_addr = base + 0xe; octeon_cf_ops.sff_data_xfer = octeon_cf_data_xfer8; - } else if (cs1) { - /* Presence of cs1 indicates True IDE mode. */ + } else if (cf_port->is_true_ide) { + base = cs0; ap->ioaddr.cmd_addr = base + (ATA_REG_CMD << 1) + 1; ap->ioaddr.data_addr = base + (ATA_REG_DATA << 1); ap->ioaddr.error_addr = base + (ATA_REG_ERR << 1) + 1; @@ -876,19 +990,15 @@ static int __devinit octeon_cf_probe(struct platform_device *pdev) ap->ioaddr.ctl_addr = cs1 + (6 << 1) + 1; octeon_cf_ops.sff_data_xfer = octeon_cf_data_xfer16; - ap->mwdma_mask = ATA_MWDMA4; - irq = platform_get_irq(pdev, 0); - irq_handler = octeon_cf_interrupt; - - /* True IDE mode needs delayed work to poll for not-busy. */ - cf_port->wq = create_singlethread_workqueue(DRV_NAME); - if (!cf_port->wq) - goto free_cf_port; - INIT_DELAYED_WORK(&cf_port->delayed_finish, - octeon_cf_delayed_finish); + ap->mwdma_mask = enable_dma ? ATA_MWDMA4 : 0; + /* True IDE mode needs a timer to poll for not-busy. */ + hrtimer_init(&cf_port->delayed_finish, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + cf_port->delayed_finish.function = octeon_cf_delayed_finish; } else { /* 16 bit but not True IDE */ + base = cs0 + 0x800; octeon_cf_ops.sff_data_xfer = octeon_cf_data_xfer16; octeon_cf_ops.softreset = octeon_cf_softreset16; octeon_cf_ops.sff_check_status = octeon_cf_check_status16; @@ -902,28 +1012,71 @@ static int __devinit octeon_cf_probe(struct platform_device *pdev) ap->ioaddr.ctl_addr = base + 0xe; ap->ioaddr.altstatus_addr = base + 0xe; } + cf_port->c0 = ap->ioaddr.ctl_addr; + + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64); + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; ata_port_desc(ap, "cmd %p ctl %p", base, ap->ioaddr.ctl_addr); - snprintf(version, sizeof(version), "%s %d bit%s", - DRV_VERSION, - (ocd->is16bit) ? 16 : 8, - (cs1) ? ", True IDE" : ""); - ata_print_version_once(&pdev->dev, version); + dev_info(&pdev->dev, "version " DRV_VERSION" %d bit%s.\n", + is_16bit ? 16 : 8, + cf_port->is_true_ide ? ", True IDE" : ""); - return ata_host_activate(host, irq, irq_handler, 0, &octeon_cf_sht); + return ata_host_activate(host, irq, irq_handler, + IRQF_SHARED, &octeon_cf_sht); free_cf_port: kfree(cf_port); - return -ENOMEM; + return rv; +} + +static void octeon_cf_shutdown(struct device *dev) +{ + union cvmx_mio_boot_dma_cfgx dma_cfg; + union cvmx_mio_boot_dma_intx dma_int; + + struct octeon_cf_port *cf_port = dev->platform_data; + + if (cf_port->dma_base) { + /* Stop and clear the dma engine. */ + dma_cfg.u64 = 0; + dma_cfg.s.size = -1; + cvmx_write_csr(cf_port->dma_base + DMA_CFG, dma_cfg.u64); + + /* Disable the interrupt. */ + dma_int.u64 = 0; + cvmx_write_csr(cf_port->dma_base + DMA_INT_EN, dma_int.u64); + + /* Clear the DMA complete status */ + dma_int.s.done = 1; + cvmx_write_csr(cf_port->dma_base + DMA_INT, dma_int.u64); + + __raw_writeb(0, cf_port->c0); + udelay(20); + __raw_writeb(ATA_SRST, cf_port->c0); + udelay(20); + __raw_writeb(0, cf_port->c0); + mdelay(100); + } } +static struct of_device_id octeon_cf_match[] = { + { + .compatible = "cavium,ebt3000-compact-flash", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, octeon_i2c_match); + static struct platform_driver octeon_cf_driver = { .probe = octeon_cf_probe, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, + .of_match_table = octeon_cf_match, + .shutdown = octeon_cf_shutdown }, }; diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig index a533af21836..d7b56a88c9f 100644 --- a/drivers/bcma/Kconfig +++ b/drivers/bcma/Kconfig @@ -65,6 +65,15 @@ config BCMA_DRIVER_GMAC_CMN If unsure, say N +config BCMA_DRIVER_GPIO + bool "BCMA GPIO driver" + depends on BCMA + select GPIOLIB + help + Driver to provide access to the GPIO pins of the bcma bus. + + If unsure, say N + config BCMA_DEBUG bool "BCMA debugging" depends on BCMA diff --git a/drivers/bcma/Makefile b/drivers/bcma/Makefile index 8ad42d41b2f..734b32f09c0 100644 --- a/drivers/bcma/Makefile +++ b/drivers/bcma/Makefile @@ -6,6 +6,7 @@ bcma-y += driver_pci.o bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN) += driver_gmac_cmn.o +bcma-$(CONFIG_BCMA_DRIVER_GPIO) += driver_gpio.o bcma-$(CONFIG_BCMA_HOST_PCI) += host_pci.o bcma-$(CONFIG_BCMA_HOST_SOC) += host_soc.o obj-$(CONFIG_BCMA) += bcma.o diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index 537ae53231c..4a2d72ec6d4 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -91,4 +91,14 @@ bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc); void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc); #endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */ +#ifdef CONFIG_BCMA_DRIVER_GPIO +/* driver_gpio.c */ +int bcma_gpio_init(struct bcma_drv_cc *cc); +#else +static inline int bcma_gpio_init(struct bcma_drv_cc *cc) +{ + return -ENOTSUPP; +} +#endif /* CONFIG_BCMA_DRIVER_GPIO */ + #endif diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c index dc96dd8ebff..e461ad25fda 100644 --- a/drivers/bcma/driver_chipcommon.c +++ b/drivers/bcma/driver_chipcommon.c @@ -114,6 +114,8 @@ void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc) if (cc->early_setup_done) return; + spin_lock_init(&cc->gpio_lock); + if (cc->core->id.rev >= 11) cc->status = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT); cc->capabilities = bcma_cc_read32(cc, BCMA_CC_CAP); @@ -202,28 +204,97 @@ u32 bcma_chipco_gpio_in(struct bcma_drv_cc *cc, u32 mask) u32 bcma_chipco_gpio_out(struct bcma_drv_cc *cc, u32 mask, u32 value) { - return bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUT, mask, value); + unsigned long flags; + u32 res; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUT, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } u32 bcma_chipco_gpio_outen(struct bcma_drv_cc *cc, u32 mask, u32 value) { - return bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUTEN, mask, value); + unsigned long flags; + u32 res; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOOUTEN, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } +/* + * If the bit is set to 0, chipcommon controlls this GPIO, + * if the bit is set to 1, it is used by some part of the chip and not our code. + */ u32 bcma_chipco_gpio_control(struct bcma_drv_cc *cc, u32 mask, u32 value) { - return bcma_cc_write32_masked(cc, BCMA_CC_GPIOCTL, mask, value); + unsigned long flags; + u32 res; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOCTL, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } EXPORT_SYMBOL_GPL(bcma_chipco_gpio_control); u32 bcma_chipco_gpio_intmask(struct bcma_drv_cc *cc, u32 mask, u32 value) { - return bcma_cc_write32_masked(cc, BCMA_CC_GPIOIRQ, mask, value); + unsigned long flags; + u32 res; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOIRQ, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value) { - return bcma_cc_write32_masked(cc, BCMA_CC_GPIOPOL, mask, value); + unsigned long flags; + u32 res; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOPOL, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; +} + +u32 bcma_chipco_gpio_pullup(struct bcma_drv_cc *cc, u32 mask, u32 value) +{ + unsigned long flags; + u32 res; + + if (cc->core->id.rev < 20) + return 0; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOPULLUP, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; +} + +u32 bcma_chipco_gpio_pulldown(struct bcma_drv_cc *cc, u32 mask, u32 value) +{ + unsigned long flags; + u32 res; + + if (cc->core->id.rev < 20) + return 0; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = bcma_cc_write32_masked(cc, BCMA_CC_GPIOPULLDOWN, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } #ifdef CONFIG_BCMA_DRIVER_MIPS diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c new file mode 100644 index 00000000000..9a6f585da2d --- /dev/null +++ b/drivers/bcma/driver_gpio.c @@ -0,0 +1,98 @@ +/* + * Broadcom specific AMBA + * GPIO driver + * + * Copyright 2011, Broadcom Corporation + * Copyright 2012, Hauke Mehrtens <hauke@hauke-m.de> + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include <linux/gpio.h> +#include <linux/export.h> +#include <linux/bcma/bcma.h> + +#include "bcma_private.h" + +static inline struct bcma_drv_cc *bcma_gpio_get_cc(struct gpio_chip *chip) +{ + return container_of(chip, struct bcma_drv_cc, gpio); +} + +static int bcma_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +{ + struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); + + return !!bcma_chipco_gpio_in(cc, 1 << gpio); +} + +static void bcma_gpio_set_value(struct gpio_chip *chip, unsigned gpio, + int value) +{ + struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); + + bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0); +} + +static int bcma_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); + + bcma_chipco_gpio_outen(cc, 1 << gpio, 0); + return 0; +} + +static int bcma_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, + int value) +{ + struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); + + bcma_chipco_gpio_outen(cc, 1 << gpio, 1 << gpio); + bcma_chipco_gpio_out(cc, 1 << gpio, value ? 1 << gpio : 0); + return 0; +} + +static int bcma_gpio_request(struct gpio_chip *chip, unsigned gpio) +{ + struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); + + bcma_chipco_gpio_control(cc, 1 << gpio, 0); + /* clear pulldown */ + bcma_chipco_gpio_pulldown(cc, 1 << gpio, 0); + /* Set pullup */ + bcma_chipco_gpio_pullup(cc, 1 << gpio, 1 << gpio); + + return 0; +} + +static void bcma_gpio_free(struct gpio_chip *chip, unsigned gpio) +{ + struct bcma_drv_cc *cc = bcma_gpio_get_cc(chip); + + /* clear pullup */ + bcma_chipco_gpio_pullup(cc, 1 << gpio, 0); +} + +int bcma_gpio_init(struct bcma_drv_cc *cc) +{ + struct gpio_chip *chip = &cc->gpio; + + chip->label = "bcma_gpio"; + chip->owner = THIS_MODULE; + chip->request = bcma_gpio_request; + chip->free = bcma_gpio_free; + chip->get = bcma_gpio_get_value; + chip->set = bcma_gpio_set_value; + chip->direction_input = bcma_gpio_direction_input; + chip->direction_output = bcma_gpio_direction_output; + chip->ngpio = 16; + /* There is just one SoC in one device and its GPIO addresses should be + * deterministic to address them more easily. The other buses could get + * a random base number. */ + if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC) + chip->base = 0; + else + chip->base = -1; + + return gpiochip_add(chip); +} diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index debd4f142f9..53ba20ca17e 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -164,6 +164,11 @@ static int bcma_register_cores(struct bcma_bus *bus) bcma_err(bus, "Error registering NAND flash\n"); } #endif + err = bcma_gpio_init(&bus->drv_cc); + if (err == -ENOTSUPP) + bcma_debug(bus, "GPIO driver not activated\n"); + else if (err) + bcma_err(bus, "Error registering GPIO driver: %i\n", err); if (bus->hosttype == BCMA_HOSTTYPE_SOC) { err = bcma_chipco_watchdog_register(&bus->drv_cc); diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index bb82d6be793..4c6c876d9dc 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -7,7 +7,7 @@ menuconfig EDAC bool "EDAC (Error Detection And Correction) reporting" depends on HAS_IOMEM - depends on X86 || PPC || TILE || ARM + depends on X86 || PPC || TILE || ARM || EDAC_SUPPORT help EDAC is designed to report errors in the core system. These are low-level errors that are reported in the CPU or @@ -27,6 +27,9 @@ menuconfig EDAC There is also a mailing list for the EDAC project, which can be found via the sourceforge page. +config EDAC_SUPPORT + bool + if EDAC comment "Reporting subsystems" @@ -316,4 +319,32 @@ config EDAC_HIGHBANK_L2 Support for error detection and correction on the Calxeda Highbank memory controller. +config EDAC_OCTEON_PC + tristate "Cavium Octeon Primary Caches" + depends on EDAC_MM_EDAC && CPU_CAVIUM_OCTEON + help + Support for error detection and correction on the primary caches of + the cnMIPS cores of Cavium Octeon family SOCs. + +config EDAC_OCTEON_L2C + tristate "Cavium Octeon Secondary Caches (L2C)" + depends on EDAC_MM_EDAC && CPU_CAVIUM_OCTEON + help + Support for error detection and correction on the + Cavium Octeon family of SOCs. + +config EDAC_OCTEON_LMC + tristate "Cavium Octeon DRAM Memory Controller (LMC)" + depends on EDAC_MM_EDAC && CPU_CAVIUM_OCTEON + help + Support for error detection and correction on the + Cavium Octeon family of SOCs. + +config EDAC_OCTEON_PCI + tristate "Cavium Octeon PCI Controller" + depends on EDAC_MM_EDAC && PCI && CPU_CAVIUM_OCTEON + help + Support for error detection and correction on the + Cavium Octeon family of SOCs. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 7e5129a733f..5608a9ba61b 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -58,3 +58,8 @@ obj-$(CONFIG_EDAC_TILE) += tile_edac.o obj-$(CONFIG_EDAC_HIGHBANK_MC) += highbank_mc_edac.o obj-$(CONFIG_EDAC_HIGHBANK_L2) += highbank_l2_edac.o + +obj-$(CONFIG_EDAC_OCTEON_PC) += octeon_edac-pc.o +obj-$(CONFIG_EDAC_OCTEON_L2C) += octeon_edac-l2c.o +obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o +obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o diff --git a/drivers/edac/octeon_edac-l2c.c b/drivers/edac/octeon_edac-l2c.c new file mode 100644 index 00000000000..40fde6a51ed --- /dev/null +++ b/drivers/edac/octeon_edac-l2c.c @@ -0,0 +1,208 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 Cavium, Inc. + * + * Copyright (C) 2009 Wind River Systems, + * written by Ralf Baechle <ralf@linux-mips.org> + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/edac.h> + +#include <asm/octeon/cvmx.h> + +#include "edac_core.h" +#include "edac_module.h" + +#define EDAC_MOD_STR "octeon-l2c" + +static void octeon_l2c_poll_oct1(struct edac_device_ctl_info *l2c) +{ + union cvmx_l2t_err l2t_err, l2t_err_reset; + union cvmx_l2d_err l2d_err, l2d_err_reset; + + l2t_err_reset.u64 = 0; + l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR); + if (l2t_err.s.sec_err) { + edac_device_handle_ce(l2c, 0, 0, + "Tag Single bit error (corrected)"); + l2t_err_reset.s.sec_err = 1; + } + if (l2t_err.s.ded_err) { + edac_device_handle_ue(l2c, 0, 0, + "Tag Double bit error (detected)"); + l2t_err_reset.s.ded_err = 1; + } + if (l2t_err_reset.u64) + cvmx_write_csr(CVMX_L2T_ERR, l2t_err_reset.u64); + + l2d_err_reset.u64 = 0; + l2d_err.u64 = cvmx_read_csr(CVMX_L2D_ERR); + if (l2d_err.s.sec_err) { + edac_device_handle_ce(l2c, 0, 1, + "Data Single bit error (corrected)"); + l2d_err_reset.s.sec_err = 1; + } + if (l2d_err.s.ded_err) { + edac_device_handle_ue(l2c, 0, 1, + "Data Double bit error (detected)"); + l2d_err_reset.s.ded_err = 1; + } + if (l2d_err_reset.u64) + cvmx_write_csr(CVMX_L2D_ERR, l2d_err_reset.u64); + +} + +static void _octeon_l2c_poll_oct2(struct edac_device_ctl_info *l2c, int tad) +{ + union cvmx_l2c_err_tdtx err_tdtx, err_tdtx_reset; + union cvmx_l2c_err_ttgx err_ttgx, err_ttgx_reset; + char buf1[64]; + char buf2[80]; + + err_tdtx_reset.u64 = 0; + err_tdtx.u64 = cvmx_read_csr(CVMX_L2C_ERR_TDTX(tad)); + if (err_tdtx.s.dbe || err_tdtx.s.sbe || + err_tdtx.s.vdbe || err_tdtx.s.vsbe) + snprintf(buf1, sizeof(buf1), + "type:%d, syn:0x%x, way:%d", + err_tdtx.s.type, err_tdtx.s.syn, err_tdtx.s.wayidx); + + if (err_tdtx.s.dbe) { + snprintf(buf2, sizeof(buf2), + "L2D Double bit error (detected):%s", buf1); + err_tdtx_reset.s.dbe = 1; + edac_device_handle_ue(l2c, tad, 1, buf2); + } + if (err_tdtx.s.sbe) { + snprintf(buf2, sizeof(buf2), + "L2D Single bit error (corrected):%s", buf1); + err_tdtx_reset.s.sbe = 1; + edac_device_handle_ce(l2c, tad, 1, buf2); + } + if (err_tdtx.s.vdbe) { + snprintf(buf2, sizeof(buf2), + "VBF Double bit error (detected):%s", buf1); + err_tdtx_reset.s.vdbe = 1; + edac_device_handle_ue(l2c, tad, 1, buf2); + } + if (err_tdtx.s.vsbe) { + snprintf(buf2, sizeof(buf2), + "VBF Single bit error (corrected):%s", buf1); + err_tdtx_reset.s.vsbe = 1; + edac_device_handle_ce(l2c, tad, 1, buf2); + } + if (err_tdtx_reset.u64) + cvmx_write_csr(CVMX_L2C_ERR_TDTX(tad), err_tdtx_reset.u64); + + err_ttgx_reset.u64 = 0; + err_ttgx.u64 = cvmx_read_csr(CVMX_L2C_ERR_TTGX(tad)); + + if (err_ttgx.s.dbe || err_ttgx.s.sbe) + snprintf(buf1, sizeof(buf1), + "type:%d, syn:0x%x, way:%d", + err_ttgx.s.type, err_ttgx.s.syn, err_ttgx.s.wayidx); + + if (err_ttgx.s.dbe) { + snprintf(buf2, sizeof(buf2), + "Tag Double bit error (detected):%s", buf1); + err_ttgx_reset.s.dbe = 1; + edac_device_handle_ue(l2c, tad, 0, buf2); + } + if (err_ttgx.s.sbe) { + snprintf(buf2, sizeof(buf2), + "Tag Single bit error (corrected):%s", buf1); + err_ttgx_reset.s.sbe = 1; + edac_device_handle_ce(l2c, tad, 0, buf2); + } + if (err_ttgx_reset.u64) + cvmx_write_csr(CVMX_L2C_ERR_TTGX(tad), err_ttgx_reset.u64); +} + +static void octeon_l2c_poll_oct2(struct edac_device_ctl_info *l2c) +{ + int i; + for (i = 0; i < l2c->nr_instances; i++) + _octeon_l2c_poll_oct2(l2c, i); +} + +static int __devinit octeon_l2c_probe(struct platform_device *pdev) +{ + struct edac_device_ctl_info *l2c; + + int num_tads = OCTEON_IS_MODEL(OCTEON_CN68XX) ? 4 : 1; + + /* 'Tags' are block 0, 'Data' is block 1*/ + l2c = edac_device_alloc_ctl_info(0, "l2c", num_tads, "l2c", 2, 0, + NULL, 0, edac_device_alloc_index()); + if (!l2c) + return -ENOMEM; + + l2c->dev = &pdev->dev; + platform_set_drvdata(pdev, l2c); + l2c->dev_name = dev_name(&pdev->dev); + + l2c->mod_name = "octeon-l2c"; + l2c->ctl_name = "octeon_l2c_err"; + + + if (OCTEON_IS_MODEL(OCTEON_FAM_1_PLUS)) { + union cvmx_l2t_err l2t_err; + union cvmx_l2d_err l2d_err; + + l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR); + l2t_err.s.sec_intena = 0; /* We poll */ + l2t_err.s.ded_intena = 0; + cvmx_write_csr(CVMX_L2T_ERR, l2t_err.u64); + + l2d_err.u64 = cvmx_read_csr(CVMX_L2D_ERR); + l2d_err.s.sec_intena = 0; /* We poll */ + l2d_err.s.ded_intena = 0; + cvmx_write_csr(CVMX_L2T_ERR, l2d_err.u64); + + l2c->edac_check = octeon_l2c_poll_oct1; + } else { + /* OCTEON II */ + l2c->edac_check = octeon_l2c_poll_oct2; + } + + if (edac_device_add_device(l2c) > 0) { + pr_err("%s: edac_device_add_device() failed\n", __func__); + goto err; + } + + + return 0; + +err: + edac_device_free_ctl_info(l2c); + + return -ENXIO; +} + +static int octeon_l2c_remove(struct platform_device *pdev) +{ + struct edac_device_ctl_info *l2c = platform_get_drvdata(pdev); + + edac_device_del_device(&pdev->dev); + edac_device_free_ctl_info(l2c); + + return 0; +} + +static struct platform_driver octeon_l2c_driver = { + .probe = octeon_l2c_probe, + .remove = octeon_l2c_remove, + .driver = { + .name = "octeon_l2c_edac", + } +}; +module_platform_driver(octeon_l2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); diff --git a/drivers/edac/octeon_edac-lmc.c b/drivers/edac/octeon_edac-lmc.c new file mode 100644 index 00000000000..33bca766e37 --- /dev/null +++ b/drivers/edac/octeon_edac-lmc.c @@ -0,0 +1,186 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2009 Wind River Systems, + * written by Ralf Baechle <ralf@linux-mips.org> + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/edac.h> + +#include <asm/octeon/octeon.h> +#include <asm/octeon/cvmx-lmcx-defs.h> + +#include "edac_core.h" +#include "edac_module.h" + +#define OCTEON_MAX_MC 4 + +static void octeon_lmc_edac_poll(struct mem_ctl_info *mci) +{ + union cvmx_lmcx_mem_cfg0 cfg0; + bool do_clear = false; + char msg[64]; + + cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mci->mc_idx)); + if (cfg0.s.sec_err || cfg0.s.ded_err) { + union cvmx_lmcx_fadr fadr; + fadr.u64 = cvmx_read_csr(CVMX_LMCX_FADR(mci->mc_idx)); + snprintf(msg, sizeof(msg), + "DIMM %d rank %d bank %d row %d col %d", + fadr.cn30xx.fdimm, fadr.cn30xx.fbunk, + fadr.cn30xx.fbank, fadr.cn30xx.frow, fadr.cn30xx.fcol); + } + + if (cfg0.s.sec_err) { + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, + -1, -1, -1, msg, ""); + cfg0.s.sec_err = -1; /* Done, re-arm */ + do_clear = true; + } + + if (cfg0.s.ded_err) { + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, + -1, -1, -1, msg, ""); + cfg0.s.ded_err = -1; /* Done, re-arm */ + do_clear = true; + } + if (do_clear) + cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mci->mc_idx), cfg0.u64); +} + +static void octeon_lmc_edac_poll_o2(struct mem_ctl_info *mci) +{ + union cvmx_lmcx_int int_reg; + bool do_clear = false; + char msg[64]; + + int_reg.u64 = cvmx_read_csr(CVMX_LMCX_INT(mci->mc_idx)); + if (int_reg.s.sec_err || int_reg.s.ded_err) { + union cvmx_lmcx_fadr fadr; + fadr.u64 = cvmx_read_csr(CVMX_LMCX_FADR(mci->mc_idx)); + snprintf(msg, sizeof(msg), + "DIMM %d rank %d bank %d row %d col %d", + fadr.cn61xx.fdimm, fadr.cn61xx.fbunk, + fadr.cn61xx.fbank, fadr.cn61xx.frow, fadr.cn61xx.fcol); + } + + if (int_reg.s.sec_err) { + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, + -1, -1, -1, msg, ""); + int_reg.s.sec_err = -1; /* Done, re-arm */ + do_clear = true; + } + + if (int_reg.s.ded_err) { + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, + -1, -1, -1, msg, ""); + int_reg.s.ded_err = -1; /* Done, re-arm */ + do_clear = true; + } + if (do_clear) + cvmx_write_csr(CVMX_LMCX_INT(mci->mc_idx), int_reg.u64); +} + +static int __devinit octeon_lmc_edac_probe(struct platform_device *pdev) +{ + struct mem_ctl_info *mci; + struct edac_mc_layer layers[1]; + int mc = pdev->id; + + layers[0].type = EDAC_MC_LAYER_CHANNEL; + layers[0].size = 1; + layers[0].is_virt_csrow = false; + + if (OCTEON_IS_MODEL(OCTEON_FAM_1_PLUS)) { + union cvmx_lmcx_mem_cfg0 cfg0; + + cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(0)); + if (!cfg0.s.ecc_ena) { + dev_info(&pdev->dev, "Disabled (ECC not enabled)\n"); + return 0; + } + + mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, 0); + if (!mci) + return -ENXIO; + + mci->pdev = &pdev->dev; + mci->dev_name = dev_name(&pdev->dev); + + mci->mod_name = "octeon-lmc"; + mci->ctl_name = "octeon-lmc-err"; + mci->edac_check = octeon_lmc_edac_poll; + + if (edac_mc_add_mc(mci)) { + dev_err(&pdev->dev, "edac_mc_add_mc() failed\n"); + edac_mc_free(mci); + return -ENXIO; + } + + cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc)); + cfg0.s.intr_ded_ena = 0; /* We poll */ + cfg0.s.intr_sec_ena = 0; + cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mc), cfg0.u64); + } else { + /* OCTEON II */ + union cvmx_lmcx_int_en en; + union cvmx_lmcx_config config; + + config.u64 = cvmx_read_csr(CVMX_LMCX_CONFIG(0)); + if (!config.s.ecc_ena) { + dev_info(&pdev->dev, "Disabled (ECC not enabled)\n"); + return 0; + } + + mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, 0); + if (!mci) + return -ENXIO; + + mci->pdev = &pdev->dev; + mci->dev_name = dev_name(&pdev->dev); + + mci->mod_name = "octeon-lmc"; + mci->ctl_name = "co_lmc_err"; + mci->edac_check = octeon_lmc_edac_poll_o2; + + if (edac_mc_add_mc(mci)) { + dev_err(&pdev->dev, "edac_mc_add_mc() failed\n"); + edac_mc_free(mci); + return -ENXIO; + } + + en.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc)); + en.s.intr_ded_ena = 0; /* We poll */ + en.s.intr_sec_ena = 0; + cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mc), en.u64); + } + platform_set_drvdata(pdev, mci); + + return 0; +} + +static int octeon_lmc_edac_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci = platform_get_drvdata(pdev); + + edac_mc_del_mc(&pdev->dev); + edac_mc_free(mci); + return 0; +} + +static struct platform_driver octeon_lmc_edac_driver = { + .probe = octeon_lmc_edac_probe, + .remove = octeon_lmc_edac_remove, + .driver = { + .name = "octeon_lmc_edac", + } +}; +module_platform_driver(octeon_lmc_edac_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); diff --git a/drivers/edac/octeon_edac-pc.c b/drivers/edac/octeon_edac-pc.c new file mode 100644 index 00000000000..14a5e57f2b3 --- /dev/null +++ b/drivers/edac/octeon_edac-pc.c @@ -0,0 +1,143 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 Cavium, Inc. + * + * Copyright (C) 2009 Wind River Systems, + * written by Ralf Baechle <ralf@linux-mips.org> + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/edac.h> + +#include "edac_core.h" +#include "edac_module.h" + +#include <asm/octeon/cvmx.h> +#include <asm/mipsregs.h> + +extern int register_co_cache_error_notifier(struct notifier_block *nb); +extern int unregister_co_cache_error_notifier(struct notifier_block *nb); + +extern unsigned long long cache_err_dcache[NR_CPUS]; + +struct co_cache_error { + struct notifier_block notifier; + struct edac_device_ctl_info *ed; +}; + +/** + * EDAC CPU cache error callback + * + * @event: non-zero if unrecoverable. + */ +static int co_cache_error_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct co_cache_error *p = container_of(this, struct co_cache_error, + notifier); + + unsigned int core = cvmx_get_core_num(); + unsigned int cpu = smp_processor_id(); + u64 icache_err = read_octeon_c0_icacheerr(); + u64 dcache_err; + + if (event) { + dcache_err = cache_err_dcache[core]; + cache_err_dcache[core] = 0; + } else { + dcache_err = read_octeon_c0_dcacheerr(); + } + + if (icache_err & 1) { + edac_device_printk(p->ed, KERN_ERR, + "CacheErr (Icache):%llx, core %d/cpu %d, cp0_errorepc == %lx\n", + (unsigned long long)icache_err, core, cpu, + read_c0_errorepc()); + write_octeon_c0_icacheerr(0); + edac_device_handle_ce(p->ed, cpu, 1, "icache"); + } + if (dcache_err & 1) { + edac_device_printk(p->ed, KERN_ERR, + "CacheErr (Dcache):%llx, core %d/cpu %d, cp0_errorepc == %lx\n", + (unsigned long long)dcache_err, core, cpu, + read_c0_errorepc()); + if (event) + edac_device_handle_ue(p->ed, cpu, 0, "dcache"); + else + edac_device_handle_ce(p->ed, cpu, 0, "dcache"); + + /* Clear the error indication */ + if (OCTEON_IS_MODEL(OCTEON_FAM_2)) + write_octeon_c0_dcacheerr(1); + else + write_octeon_c0_dcacheerr(0); + } + + return NOTIFY_STOP; +} + +static int __devinit co_cache_error_probe(struct platform_device *pdev) +{ + struct co_cache_error *p = devm_kzalloc(&pdev->dev, sizeof(*p), + GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->notifier.notifier_call = co_cache_error_event; + platform_set_drvdata(pdev, p); + + p->ed = edac_device_alloc_ctl_info(0, "cpu", num_possible_cpus(), + "cache", 2, 0, NULL, 0, + edac_device_alloc_index()); + if (!p->ed) + goto err; + + p->ed->dev = &pdev->dev; + + p->ed->dev_name = dev_name(&pdev->dev); + + p->ed->mod_name = "octeon-cpu"; + p->ed->ctl_name = "cache"; + + if (edac_device_add_device(p->ed)) { + pr_err("%s: edac_device_add_device() failed\n", __func__); + goto err1; + } + + register_co_cache_error_notifier(&p->notifier); + + return 0; + +err1: + edac_device_free_ctl_info(p->ed); +err: + return -ENXIO; +} + +static int co_cache_error_remove(struct platform_device *pdev) +{ + struct co_cache_error *p = platform_get_drvdata(pdev); + + unregister_co_cache_error_notifier(&p->notifier); + edac_device_del_device(&pdev->dev); + edac_device_free_ctl_info(p->ed); + return 0; +} + +static struct platform_driver co_cache_error_driver = { + .probe = co_cache_error_probe, + .remove = co_cache_error_remove, + .driver = { + .name = "octeon_pc_edac", + } +}; +module_platform_driver(co_cache_error_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); diff --git a/drivers/edac/octeon_edac-pci.c b/drivers/edac/octeon_edac-pci.c new file mode 100644 index 00000000000..758c1ef5fc9 --- /dev/null +++ b/drivers/edac/octeon_edac-pci.c @@ -0,0 +1,111 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 Cavium, Inc. + * Copyright (C) 2009 Wind River Systems, + * written by Ralf Baechle <ralf@linux-mips.org> + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/edac.h> + +#include <asm/octeon/cvmx.h> +#include <asm/octeon/cvmx-npi-defs.h> +#include <asm/octeon/cvmx-pci-defs.h> +#include <asm/octeon/octeon.h> + +#include "edac_core.h" +#include "edac_module.h" + +static void octeon_pci_poll(struct edac_pci_ctl_info *pci) +{ + union cvmx_pci_cfg01 cfg01; + + cfg01.u32 = octeon_npi_read32(CVMX_NPI_PCI_CFG01); + if (cfg01.s.dpe) { /* Detected parity error */ + edac_pci_handle_pe(pci, pci->ctl_name); + cfg01.s.dpe = 1; /* Reset */ + octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32); + } + if (cfg01.s.sse) { + edac_pci_handle_npe(pci, "Signaled System Error"); + cfg01.s.sse = 1; /* Reset */ + octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32); + } + if (cfg01.s.rma) { + edac_pci_handle_npe(pci, "Received Master Abort"); + cfg01.s.rma = 1; /* Reset */ + octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32); + } + if (cfg01.s.rta) { + edac_pci_handle_npe(pci, "Received Target Abort"); + cfg01.s.rta = 1; /* Reset */ + octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32); + } + if (cfg01.s.sta) { + edac_pci_handle_npe(pci, "Signaled Target Abort"); + cfg01.s.sta = 1; /* Reset */ + octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32); + } + if (cfg01.s.mdpe) { + edac_pci_handle_npe(pci, "Master Data Parity Error"); + cfg01.s.mdpe = 1; /* Reset */ + octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32); + } +} + +static int __devinit octeon_pci_probe(struct platform_device *pdev) +{ + struct edac_pci_ctl_info *pci; + int res = 0; + + pci = edac_pci_alloc_ctl_info(0, "octeon_pci_err"); + if (!pci) + return -ENOMEM; + + pci->dev = &pdev->dev; + platform_set_drvdata(pdev, pci); + pci->dev_name = dev_name(&pdev->dev); + + pci->mod_name = "octeon-pci"; + pci->ctl_name = "octeon_pci_err"; + pci->edac_check = octeon_pci_poll; + + if (edac_pci_add_device(pci, 0) > 0) { + pr_err("%s: edac_pci_add_device() failed\n", __func__); + goto err; + } + + return 0; + +err: + edac_pci_free_ctl_info(pci); + + return res; +} + +static int octeon_pci_remove(struct platform_device *pdev) +{ + struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev); + + edac_pci_del_device(&pdev->dev); + edac_pci_free_ctl_info(pci); + + return 0; +} + +static struct platform_driver octeon_pci_driver = { + .probe = octeon_pci_probe, + .remove = octeon_pci_remove, + .driver = { + .name = "octeon_pci_edac", + } +}; +module_platform_driver(octeon_pci_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig index 42cdaa9a4d8..ff3c8a21f10 100644 --- a/drivers/ssb/Kconfig +++ b/drivers/ssb/Kconfig @@ -160,4 +160,13 @@ config SSB_DRIVER_GIGE If unsure, say N +config SSB_DRIVER_GPIO + bool "SSB GPIO driver" + depends on SSB + select GPIOLIB + help + Driver to provide access to the GPIO pins on the bus. + + If unsure, say N + endmenu diff --git a/drivers/ssb/Makefile b/drivers/ssb/Makefile index 656e58b9261..9159ba77c38 100644 --- a/drivers/ssb/Makefile +++ b/drivers/ssb/Makefile @@ -15,6 +15,7 @@ ssb-$(CONFIG_SSB_DRIVER_MIPS) += driver_mipscore.o ssb-$(CONFIG_SSB_DRIVER_EXTIF) += driver_extif.o ssb-$(CONFIG_SSB_DRIVER_PCICORE) += driver_pcicore.o ssb-$(CONFIG_SSB_DRIVER_GIGE) += driver_gige.o +ssb-$(CONFIG_SSB_DRIVER_GPIO) += driver_gpio.o # b43 pci-ssb-bridge driver # Not strictly a part of SSB, but kept here for convenience diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c index 95c33a05f43..71098a7b5fe 100644 --- a/drivers/ssb/driver_chipcommon.c +++ b/drivers/ssb/driver_chipcommon.c @@ -349,6 +349,9 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc) { if (!cc->dev) return; /* We don't have a ChipCommon */ + + spin_lock_init(&cc->gpio_lock); + if (cc->dev->id.revision >= 11) cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT); ssb_dprintk(KERN_INFO PFX "chipcommon status is 0x%x\n", cc->status); @@ -505,28 +508,93 @@ u32 ssb_chipco_gpio_in(struct ssb_chipcommon *cc, u32 mask) u32 ssb_chipco_gpio_out(struct ssb_chipcommon *cc, u32 mask, u32 value) { - return chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUT, mask, value); + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUT, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } u32 ssb_chipco_gpio_outen(struct ssb_chipcommon *cc, u32 mask, u32 value) { - return chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUTEN, mask, value); + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUTEN, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } u32 ssb_chipco_gpio_control(struct ssb_chipcommon *cc, u32 mask, u32 value) { - return chipco_write32_masked(cc, SSB_CHIPCO_GPIOCTL, mask, value); + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOCTL, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } EXPORT_SYMBOL(ssb_chipco_gpio_control); u32 ssb_chipco_gpio_intmask(struct ssb_chipcommon *cc, u32 mask, u32 value) { - return chipco_write32_masked(cc, SSB_CHIPCO_GPIOIRQ, mask, value); + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOIRQ, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } u32 ssb_chipco_gpio_polarity(struct ssb_chipcommon *cc, u32 mask, u32 value) { - return chipco_write32_masked(cc, SSB_CHIPCO_GPIOPOL, mask, value); + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPOL, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; +} + +u32 ssb_chipco_gpio_pullup(struct ssb_chipcommon *cc, u32 mask, u32 value) +{ + unsigned long flags; + u32 res = 0; + + if (cc->dev->id.revision < 20) + return 0xffffffff; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPULLUP, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; +} + +u32 ssb_chipco_gpio_pulldown(struct ssb_chipcommon *cc, u32 mask, u32 value) +{ + unsigned long flags; + u32 res = 0; + + if (cc->dev->id.revision < 20) + return 0xffffffff; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPULLDOWN, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } #ifdef CONFIG_SSB_SERIAL diff --git a/drivers/ssb/driver_extif.c b/drivers/ssb/driver_extif.c index 553227a3062..59385fdab5b 100644 --- a/drivers/ssb/driver_extif.c +++ b/drivers/ssb/driver_extif.c @@ -138,6 +138,13 @@ u32 ssb_extif_watchdog_timer_set(struct ssb_extif *extif, u32 ticks) return ticks; } +void ssb_extif_init(struct ssb_extif *extif) +{ + if (!extif->dev) + return; /* We don't have a Extif core */ + spin_lock_init(&extif->gpio_lock); +} + u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask) { return extif_read32(extif, SSB_EXTIF_GPIO_IN) & mask; @@ -145,22 +152,50 @@ u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask) u32 ssb_extif_gpio_out(struct ssb_extif *extif, u32 mask, u32 value) { - return extif_write32_masked(extif, SSB_EXTIF_GPIO_OUT(0), + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&extif->gpio_lock, flags); + res = extif_write32_masked(extif, SSB_EXTIF_GPIO_OUT(0), mask, value); + spin_unlock_irqrestore(&extif->gpio_lock, flags); + + return res; } u32 ssb_extif_gpio_outen(struct ssb_extif *extif, u32 mask, u32 value) { - return extif_write32_masked(extif, SSB_EXTIF_GPIO_OUTEN(0), + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&extif->gpio_lock, flags); + res = extif_write32_masked(extif, SSB_EXTIF_GPIO_OUTEN(0), mask, value); + spin_unlock_irqrestore(&extif->gpio_lock, flags); + + return res; } u32 ssb_extif_gpio_polarity(struct ssb_extif *extif, u32 mask, u32 value) { - return extif_write32_masked(extif, SSB_EXTIF_GPIO_INTPOL, mask, value); + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&extif->gpio_lock, flags); + res = extif_write32_masked(extif, SSB_EXTIF_GPIO_INTPOL, mask, value); + spin_unlock_irqrestore(&extif->gpio_lock, flags); + + return res; } u32 ssb_extif_gpio_intmask(struct ssb_extif *extif, u32 mask, u32 value) { - return extif_write32_masked(extif, SSB_EXTIF_GPIO_INTMASK, mask, value); + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&extif->gpio_lock, flags); + res = extif_write32_masked(extif, SSB_EXTIF_GPIO_INTMASK, mask, value); + spin_unlock_irqrestore(&extif->gpio_lock, flags); + + return res; } diff --git a/drivers/ssb/driver_gpio.c b/drivers/ssb/driver_gpio.c new file mode 100644 index 00000000000..97ac0a38e3d --- /dev/null +++ b/drivers/ssb/driver_gpio.c @@ -0,0 +1,176 @@ +/* + * Sonics Silicon Backplane + * GPIO driver + * + * Copyright 2011, Broadcom Corporation + * Copyright 2012, Hauke Mehrtens <hauke@hauke-m.de> + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include <linux/gpio.h> +#include <linux/export.h> +#include <linux/ssb/ssb.h> + +#include "ssb_private.h" + +static struct ssb_bus *ssb_gpio_get_bus(struct gpio_chip *chip) +{ + return container_of(chip, struct ssb_bus, gpio); +} + +static int ssb_gpio_chipco_get_value(struct gpio_chip *chip, unsigned gpio) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + return !!ssb_chipco_gpio_in(&bus->chipco, 1 << gpio); +} + +static void ssb_gpio_chipco_set_value(struct gpio_chip *chip, unsigned gpio, + int value) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + ssb_chipco_gpio_out(&bus->chipco, 1 << gpio, value ? 1 << gpio : 0); +} + +static int ssb_gpio_chipco_direction_input(struct gpio_chip *chip, + unsigned gpio) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + ssb_chipco_gpio_outen(&bus->chipco, 1 << gpio, 0); + return 0; +} + +static int ssb_gpio_chipco_direction_output(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + ssb_chipco_gpio_outen(&bus->chipco, 1 << gpio, 1 << gpio); + ssb_chipco_gpio_out(&bus->chipco, 1 << gpio, value ? 1 << gpio : 0); + return 0; +} + +static int ssb_gpio_chipco_request(struct gpio_chip *chip, unsigned gpio) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + ssb_chipco_gpio_control(&bus->chipco, 1 << gpio, 0); + /* clear pulldown */ + ssb_chipco_gpio_pulldown(&bus->chipco, 1 << gpio, 0); + /* Set pullup */ + ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 1 << gpio); + + return 0; +} + +static void ssb_gpio_chipco_free(struct gpio_chip *chip, unsigned gpio) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + /* clear pullup */ + ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 0); +} + +static int ssb_gpio_chipco_init(struct ssb_bus *bus) +{ + struct gpio_chip *chip = &bus->gpio; + + chip->label = "ssb_chipco_gpio"; + chip->owner = THIS_MODULE; + chip->request = ssb_gpio_chipco_request; + chip->free = ssb_gpio_chipco_free; + chip->get = ssb_gpio_chipco_get_value; + chip->set = ssb_gpio_chipco_set_value; + chip->direction_input = ssb_gpio_chipco_direction_input; + chip->direction_output = ssb_gpio_chipco_direction_output; + chip->ngpio = 16; + /* There is just one SoC in one device and its GPIO addresses should be + * deterministic to address them more easily. The other buses could get + * a random base number. */ + if (bus->bustype == SSB_BUSTYPE_SSB) + chip->base = 0; + else + chip->base = -1; + + return gpiochip_add(chip); +} + +#ifdef CONFIG_SSB_DRIVER_EXTIF + +static int ssb_gpio_extif_get_value(struct gpio_chip *chip, unsigned gpio) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + return !!ssb_extif_gpio_in(&bus->extif, 1 << gpio); +} + +static void ssb_gpio_extif_set_value(struct gpio_chip *chip, unsigned gpio, + int value) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + ssb_extif_gpio_out(&bus->extif, 1 << gpio, value ? 1 << gpio : 0); +} + +static int ssb_gpio_extif_direction_input(struct gpio_chip *chip, + unsigned gpio) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + ssb_extif_gpio_outen(&bus->extif, 1 << gpio, 0); + return 0; +} + +static int ssb_gpio_extif_direction_output(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + ssb_extif_gpio_outen(&bus->extif, 1 << gpio, 1 << gpio); + ssb_extif_gpio_out(&bus->extif, 1 << gpio, value ? 1 << gpio : 0); + return 0; +} + +static int ssb_gpio_extif_init(struct ssb_bus *bus) +{ + struct gpio_chip *chip = &bus->gpio; + + chip->label = "ssb_extif_gpio"; + chip->owner = THIS_MODULE; + chip->get = ssb_gpio_extif_get_value; + chip->set = ssb_gpio_extif_set_value; + chip->direction_input = ssb_gpio_extif_direction_input; + chip->direction_output = ssb_gpio_extif_direction_output; + chip->ngpio = 5; + /* There is just one SoC in one device and its GPIO addresses should be + * deterministic to address them more easily. The other buses could get + * a random base number. */ + if (bus->bustype == SSB_BUSTYPE_SSB) + chip->base = 0; + else + chip->base = -1; + + return gpiochip_add(chip); +} + +#else +static int ssb_gpio_extif_init(struct ssb_bus *bus) +{ + return -ENOTSUPP; +} +#endif + +int ssb_gpio_init(struct ssb_bus *bus) +{ + if (ssb_chipco_available(&bus->chipco)) + return ssb_gpio_chipco_init(bus); + else if (ssb_extif_available(&bus->extif)) + return ssb_gpio_extif_init(bus); + else + SSB_WARN_ON(1); + + return -1; +} diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 6e0daaa0e04..c82c5c95fe8 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -804,7 +804,14 @@ static int __devinit ssb_bus_register(struct ssb_bus *bus, if (err) goto err_pcmcia_exit; ssb_chipcommon_init(&bus->chipco); + ssb_extif_init(&bus->extif); ssb_mipscore_init(&bus->mipscore); + err = ssb_gpio_init(bus); + if (err == -ENOTSUPP) + ssb_dprintk(KERN_DEBUG PFX "GPIO driver not activated\n"); + else if (err) + ssb_dprintk(KERN_ERR PFX + "Error registering GPIO driver: %i\n", err); err = ssb_fetch_invariants(bus, get_invariants); if (err) { ssb_bus_may_powerdown(bus); diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h index 8942db1d855..6c10b66c796 100644 --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h @@ -242,4 +242,21 @@ static inline int ssb_watchdog_register(struct ssb_bus *bus) } #endif /* CONFIG_SSB_EMBEDDED */ +#ifdef CONFIG_SSB_DRIVER_EXTIF +extern void ssb_extif_init(struct ssb_extif *extif); +#else +static inline void ssb_extif_init(struct ssb_extif *extif) +{ +} +#endif + +#ifdef CONFIG_SSB_DRIVER_GPIO +extern int ssb_gpio_init(struct ssb_bus *bus); +#else /* CONFIG_SSB_DRIVER_GPIO */ +static inline int ssb_gpio_init(struct ssb_bus *bus) +{ + return -ENOTSUPP; +} +#endif /* CONFIG_SSB_DRIVER_GPIO */ + #endif /* LINUX_SSB_PRIVATE_H_ */ diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index 6d159662904..b05afd03729 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -327,9 +327,16 @@ out_unmap: static void newport_init(struct vc_data *vc, int init) { - vc->vc_cols = newport_xsize / 8; - vc->vc_rows = newport_ysize / 16; + int cols, rows; + + cols = newport_xsize / 8; + rows = newport_ysize / 16; vc->vc_can_do_color = 1; + if (init) { + vc->vc_cols = cols; + vc->vc_rows = rows; + } else + vc_resize(vc, cols, rows); } static void newport_deinit(struct vc_data *c) |